From a98514e6c9f9ed3dc01d33db943e596d2dc6bd62 Mon Sep 17 00:00:00 2001 From: m-arcuri <124469923+m-arcuri@users.noreply.github.com> Date: Wed, 30 Oct 2024 20:09:54 -0400 Subject: [PATCH] Prevent grenade purchase if player cannot carry it (#251) * Prevent grenade purchase if already carrying that type Only run weapon drop logic if purchased weapon is primary or secondary * Add cvar checks for grenade inventory limit when buying * Rewrite logic for grenade ammo cvar checks when buying --------- Co-authored-by: Alex --- src/commands.cpp | 92 +++++++++++++++++++++++++++++++---- src/cs2_sdk/entity/services.h | 7 +++ 2 files changed, 89 insertions(+), 10 deletions(-) diff --git a/src/commands.cpp b/src/commands.cpp index b2b74dc6..cb02c3d0 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -103,6 +103,48 @@ bool g_bEnableWeapons = false; FAKE_BOOL_CVAR(cs2f_weapons_enable, "Whether to enable weapon commands", g_bEnableWeapons, false, false) +int GetGrenadeAmmo(CCSPlayer_WeaponServices* pWeaponServices, WeaponMapEntry_t weaponEntry) +{ + if (!pWeaponServices || weaponEntry.iGearSlot != GEAR_SLOT_GRENADES) + return -1; + + // TODO: look into molotov vs inc interaction + if (strcmp(weaponEntry.szClassName, "weapon_hegrenade") == 0) + return pWeaponServices->m_iAmmo[AMMO_OFFSET_HEGRENADE]; + else if (strcmp(weaponEntry.szClassName, "weapon_molotov") == 0 || strcmp(weaponEntry.szClassName, "weapon_incgrenade") == 0) + return pWeaponServices->m_iAmmo[AMMO_OFFSET_MOLOTOV]; + else if (strcmp(weaponEntry.szClassName, "weapon_decoy") == 0) + return pWeaponServices->m_iAmmo[AMMO_OFFSET_DECOY]; + else if (strcmp(weaponEntry.szClassName, "weapon_flashbang") == 0) + return pWeaponServices->m_iAmmo[AMMO_OFFSET_FLASHBANG]; + else if (strcmp(weaponEntry.szClassName, "weapon_smokegrenade") == 0) + return pWeaponServices->m_iAmmo[AMMO_OFFSET_SMOKEGRENADE]; + else + return -1; +} + +int GetGrenadeAmmoTotal(CCSPlayer_WeaponServices* pWeaponServices) +{ + if(!pWeaponServices) + return -1; + + int grenadeAmmoOffsets[] = { + AMMO_OFFSET_HEGRENADE, + AMMO_OFFSET_FLASHBANG, + AMMO_OFFSET_SMOKEGRENADE, + AMMO_OFFSET_DECOY, + AMMO_OFFSET_MOLOTOV, + }; + + int totalGrenades = 0; + for (int i = 0; i < (sizeof(grenadeAmmoOffsets) / sizeof(int)); i++) + { + totalGrenades += pWeaponServices->m_iAmmo[grenadeAmmoOffsets[i]]; + } + + return totalGrenades; +} + void ParseWeaponCommand(const CCommand& args, CCSPlayerController* player) { if (!g_bEnableWeapons || !player || !player->m_hPawn()) @@ -159,6 +201,33 @@ void ParseWeaponCommand(const CCommand& args, CCSPlayerController* player) return; } + if (weaponEntry.iGearSlot == GEAR_SLOT_GRENADES) + { + CUtlVector>* weapons = pWeaponServices->m_hMyWeapons(); + + // CONVAR_TODO + ConVar* cvar = g_pCVar->GetConVar(g_pCVar->FindConVar("ammo_grenade_limit_default")); + // HACK: values is actually the cvar value itself, hence this ugly cast. + int iGrenadeLimitDefault = *(int*)&cvar->values; + cvar = g_pCVar->GetConVar(g_pCVar->FindConVar("ammo_grenade_limit_total")); + int iGrenadeLimitTotal = *(int*)&cvar->values; + + int iMatchingGrenades = GetGrenadeAmmo(pWeaponServices, weaponEntry); + int iTotalGrenades = GetGrenadeAmmoTotal(pWeaponServices); + + if (iMatchingGrenades >= iGrenadeLimitDefault) + { + ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX"You cannot carry any more %ss (Max %i)", weaponEntry.szWeaponName, iGrenadeLimitDefault); + return; + } + + if (iTotalGrenades >= iGrenadeLimitTotal) + { + ClientPrint(player, HUD_PRINTTALK, CHAT_PREFIX"You cannot carry any more grenades (Max %i)", iGrenadeLimitTotal); + return; + } + } + if (weaponEntry.maxAmount) { CUtlVector* weaponPurchases = pPawn->m_pActionTrackingServices->m_weaponPurchasesThisRound().m_weaponPurchases; @@ -190,19 +259,22 @@ void ParseWeaponCommand(const CCommand& args, CCSPlayerController* player) } } - CUtlVector>* weapons = pWeaponServices->m_hMyWeapons(); - - FOR_EACH_VEC(*weapons, i) + if (weaponEntry.iGearSlot == GEAR_SLOT_RIFLE || weaponEntry.iGearSlot == GEAR_SLOT_PISTOL) { - CBasePlayerWeapon* weapon = (*weapons)[i].Get(); + CUtlVector>* weapons = pWeaponServices->m_hMyWeapons(); - if (!weapon) - continue; - - if (weapon->GetWeaponVData()->m_GearSlot() == weaponEntry.iGearSlot && (weaponEntry.iGearSlot == GEAR_SLOT_RIFLE || weaponEntry.iGearSlot == GEAR_SLOT_PISTOL)) + FOR_EACH_VEC(*weapons, i) { - pWeaponServices->DropWeapon(weapon); - break; + CBasePlayerWeapon* weapon = (*weapons)[i].Get(); + + if (!weapon) + continue; + + if (weapon->GetWeaponVData()->m_GearSlot() == weaponEntry.iGearSlot) + { + pWeaponServices->DropWeapon(weapon); + break; + } } } diff --git a/src/cs2_sdk/entity/services.h b/src/cs2_sdk/entity/services.h index 6c0ea436..74825e61 100644 --- a/src/cs2_sdk/entity/services.h +++ b/src/cs2_sdk/entity/services.h @@ -23,6 +23,12 @@ #include #include +#define AMMO_OFFSET_HEGRENADE 13 +#define AMMO_OFFSET_FLASHBANG 14 +#define AMMO_OFFSET_SMOKEGRENADE 15 +#define AMMO_OFFSET_MOLOTOV 16 +#define AMMO_OFFSET_DECOY 17 + class CBaseEntity; struct CSPerRoundStats_t @@ -110,6 +116,7 @@ class CPlayer_WeaponServices : public CPlayerPawnComponent SCHEMA_FIELD_POINTER(CUtlVector>, m_hMyWeapons) SCHEMA_FIELD(CHandle, m_hActiveWeapon) + SCHEMA_FIELD_POINTER(uint16_t, m_iAmmo) }; class CCSPlayer_WeaponServices : public CPlayer_WeaponServices