Skip to content

Commit

Permalink
Prevent grenade purchase if player cannot carry it (#251)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
m-arcuri and Vauff authored Oct 31, 2024
1 parent a8b95f8 commit a98514e
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 10 deletions.
92 changes: 82 additions & 10 deletions src/commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -159,6 +201,33 @@ void ParseWeaponCommand(const CCommand& args, CCSPlayerController* player)
return;
}

if (weaponEntry.iGearSlot == GEAR_SLOT_GRENADES)
{
CUtlVector<CHandle<CBasePlayerWeapon>>* 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<WeaponPurchaseCount_t>* weaponPurchases = pPawn->m_pActionTrackingServices->m_weaponPurchasesThisRound().m_weaponPurchases;
Expand Down Expand Up @@ -190,19 +259,22 @@ void ParseWeaponCommand(const CCommand& args, CCSPlayerController* player)
}
}

CUtlVector<CHandle<CBasePlayerWeapon>>* 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<CHandle<CBasePlayerWeapon>>* 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;
}
}
}

Expand Down
7 changes: 7 additions & 0 deletions src/cs2_sdk/entity/services.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
#include <entity/ccsweaponbase.h>
#include <entity/ccsplayerpawn.h>

#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
Expand Down Expand Up @@ -110,6 +116,7 @@ class CPlayer_WeaponServices : public CPlayerPawnComponent

SCHEMA_FIELD_POINTER(CUtlVector<CHandle<CBasePlayerWeapon>>, m_hMyWeapons)
SCHEMA_FIELD(CHandle<CBasePlayerWeapon>, m_hActiveWeapon)
SCHEMA_FIELD_POINTER(uint16_t, m_iAmmo)
};

class CCSPlayer_WeaponServices : public CPlayer_WeaponServices
Expand Down

0 comments on commit a98514e

Please sign in to comment.