Skip to content

Commit aa46f8d

Browse files
KiIoOnlineThisAMJ
andauthored
feat: promo items (#347)
Cheat command that force-enables promotion items (pre-order roll cage "helmet", skin, antenna) --------- Co-authored-by: ThisAMJ <69196954+ThisAMJ@users.noreply.github.com>
1 parent 68d5667 commit aa46f8d

7 files changed

Lines changed: 61 additions & 0 deletions

File tree

docs/cvars.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,6 +514,7 @@
514514
|sar_scrollspeed_y|210|Scroll speed HUD y offset.|
515515
|sar_seamshot_finder|0|Enables or disables seamshot finder overlay.|
516516
|sar_session|cmd|sar_session - prints the current tick of the server since it has loaded|
517+
|sar_set_promo_items_state|cmd|sar_set_promo_items_state \<off\|all\|skins\|helmet\|antenna>... - enables coop promotional items on spawn.|
517518
|sar_show_entinp|0|Print all entity inputs to console.|
518519
|sar_skiptodemo|cmd|sar_skiptodemo \<demoname> - skip demos in demo queue to this demo|
519520
|sar_speedrun_autoreset_clear|cmd|sar_speedrun_autoreset_clear - stop using the autoreset file|

src/Cheats.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ Memory::Patch *g_floorReportalPatch;
331331
Memory::Patch *g_coopLoadingDotsPatch;
332332
Memory::Patch *g_autoGrabPatchServer;
333333
Memory::Patch *g_autoGrabPatchClient;
334+
Memory::Patch *g_promoFlagsPatch;
334335

335336
void Cheats::Init() {
336337
sv_laser_cube_autoaim = Variable("sv_laser_cube_autoaim");
@@ -399,6 +400,14 @@ void Cheats::Init() {
399400
g_autoGrabPatchClient->Restore();
400401
}
401402

403+
g_promoFlagsPatch = new Memory::Patch();
404+
auto portal2PromoFlags = Memory::Scan(MODULE("server"), Offsets::Portal2PromoFlagsSig, Offsets::Portal2PromoFlagsOff);
405+
if (portal2PromoFlags) {
406+
unsigned char promoFlagsByte = 0x00;
407+
g_promoFlagsPatch->Execute(Memory::Deref<uintptr_t>(portal2PromoFlags), &promoFlagsByte, 1); // Note: Has to be active before map loads.
408+
g_promoFlagsPatch->Restore();
409+
}
410+
402411
Variable::RegisterAll();
403412
Command::RegisterAll();
404413
}
@@ -422,6 +431,8 @@ void Cheats::Shutdown() {
422431
SAFE_DELETE(g_autoGrabPatchServer);
423432
g_autoGrabPatchClient->Restore();
424433
SAFE_DELETE(g_autoGrabPatchClient);
434+
g_promoFlagsPatch->Restore();
435+
SAFE_DELETE(g_promoFlagsPatch);
425436
}
426437

427438

@@ -589,3 +600,37 @@ void Cheats::CheckAutoGrab() {
589600
g_autoGrabPatchClient->Restore();
590601
}
591602
}
603+
604+
DECL_AUTO_COMMAND_COMPLETION(sar_set_promo_items_state, ({"skins", "helmet", "antenna"})) // TODO: Add support for autofilling multiple args.
605+
CON_COMMAND_F_COMPLETION(sar_set_promo_items_state, "sar_set_promo_items_state <off|all|skins|helmet|antenna>... - enables coop promotional items on spawn.\n", FCVAR_CHEAT, AUTOCOMPLETION_FUNCTION(sar_set_promo_items_state)) {
606+
if (!g_promoFlagsPatch || !g_promoFlagsPatch->IsInit()) {
607+
return console->Print("sar_set_promo_items_state is not available.\n");
608+
}
609+
610+
if (args.ArgC() < 2) {
611+
return console->Print(sar_set_promo_items_state.ThisPtr()->m_pszHelpString);
612+
}
613+
614+
unsigned char targetFlags = 0;
615+
for (int i = 1; i < args.ArgC(); i++) {
616+
if (strcasecmp(args[i], "off") == 0) {
617+
g_promoFlagsPatch->Restore();
618+
return;
619+
}
620+
if (strcasecmp(args[i], "all") == 0) {
621+
targetFlags = 0b111;
622+
break;
623+
}
624+
if (strcasecmp(args[i], "skins") == 0) {;
625+
targetFlags |= 0b001;
626+
} else if (strcasecmp(args[i], "helmet") == 0) {
627+
targetFlags |= 0b010;
628+
} else if (strcasecmp(args[i], "antenna") == 0) {
629+
targetFlags |= 0b100;
630+
} else {
631+
return console->Print(sar_set_promo_items_state.ThisPtr()->m_pszHelpString);
632+
}
633+
}
634+
g_promoFlagsPatch->Restore();
635+
g_promoFlagsPatch->Execute(&targetFlags, 1);
636+
}

src/Cheats.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,5 @@ extern Variable hide_gun_when_holding;
5050
extern Variable r_flashlightbrightness;
5151

5252
extern Command sar_togglewait;
53+
54+
extern Memory::Patch *g_promoFlagsPatch;

src/Modules/Server.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ DETOUR(Server::PlayerRunCommand, CUserCmd *cmd, void *moveHelper) {
285285

286286
Cheats::AutoStrafe(slot, thisptr, cmd);
287287
Cheats::CheckFloorReportals();
288+
if (!sv_cheats.GetBool()) g_promoFlagsPatch->Restore(); // We only want to check this once per map load, to preserve the intended behavior.
288289

289290
inputHud.SetInputInfo(slot, cmd->buttons, {cmd->sidemove, cmd->forwardmove, cmd->upmove});
290291

src/Offsets/Portal 2 9568.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,10 @@ SIGSCAN_DEFAULT(FloorReportalBranch, "75 7D 8B 8E C0 04 00 00",
537537
SIGSCAN_DEFAULT(CPortal_Player__PollForUseEntity_CheckMP, "74 ? ? ? 8B 82 ? ? ? ? FF D0 84 C0 74 ? 8B CE",
538538
"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
539539

540+
SIGSCAN_DEFAULT(Portal2PromoFlagsSig, "F6 05 ? ? ? ? 02 74 ? 8B CE",
541+
"A1 ? ? ? ? A8 02") // "#P2_WearableType_Flag" xref -> CPortal_Player::GiveDefaultItems -> bitwise & -> portal2 promo flag
542+
OFFSET_DEFAULT(Portal2PromoFlagsOff, 2, 1)
543+
540544
// VPhysics
541545
OFFSET_EMPTY(DestroyEnvironment)
542546
OFFSET_EMPTY(GetActiveEnvironmentByIndex)

src/Utils/Memory.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ Memory::Patch::~Patch() {
245245
}
246246
bool Memory::Patch::Execute() {
247247
if (this->isPatched) return true; // already executed
248+
if (!this->IsInit()) return false;
248249
unsigned char *tmpPatch = new unsigned char[this->size];
249250
// We create another patch, because this->patch is gonna be deleted
250251
memcpy(tmpPatch, this->patch, this->size);

src/Utils/Memory.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ namespace Memory {
5050
bool Execute(uintptr_t location, unsigned char (&bytes)[size]) {
5151
return Execute(location, bytes, size);
5252
}
53+
bool Execute(unsigned char *bytes, size_t size) {
54+
return Execute(location, bytes, size);
55+
}
56+
template <size_t size>
57+
bool Execute(unsigned char (&bytes)[size]) {
58+
return Execute(location, bytes, size);
59+
}
5360
bool Restore();
5461
bool IsPatched();
5562
bool IsInit();

0 commit comments

Comments
 (0)