diff --git a/amxmodx/msvc12/amxmodx_mm.vcxproj b/amxmodx/msvc12/amxmodx_mm.vcxproj index c02bfdef..6ae2689b 100644 --- a/amxmodx/msvc12/amxmodx_mm.vcxproj +++ b/amxmodx/msvc12/amxmodx_mm.vcxproj @@ -382,6 +382,7 @@ + diff --git a/amxmodx/msvc12/amxmodx_mm.vcxproj.filters b/amxmodx/msvc12/amxmodx_mm.vcxproj.filters index 86499225..07545eb8 100644 --- a/amxmodx/msvc12/amxmodx_mm.vcxproj.filters +++ b/amxmodx/msvc12/amxmodx_mm.vcxproj.filters @@ -600,6 +600,9 @@ Pawn Includes + + Pawn Includes + diff --git a/gamedata/modules.games/game.cstrike.txt b/gamedata/modules.games/game.cstrike.txt index d1a0f7c0..dbf6578c 100644 --- a/gamedata/modules.games/game.cstrike.txt +++ b/gamedata/modules.games/game.cstrike.txt @@ -15,6 +15,22 @@ { "Signatures" { + "CanPlayerBuy" // bool CBasePlayer::CanPlayerBuy(bool display) + { + "library" "server" + "windows" "\x51\x53\x55\x56\x57\x8B\x2A\x8B\x2A\x2A\x2A\x2A\x2A\x8B\x2A\xFF" + "linux" "@_ZN11CBasePlayer12CanPlayerBuyEb" + "mac" "@_ZN11CBasePlayer12CanPlayerBuyEb" + } + + "CanBuyThis" // bool CanBuyThis(CBasePlayer *pPlayer, int weaponId) + { + "library" "server" + "windows" "\x53\x8B\x2A\x2A\x2A\x2A\x2A\x56\x8B\x2A\x2A\x2A\x57\x8B" + "linux" "@_Z10CanBuyThisP11CBasePlayeri" + "mac" "@_Z10CanBuyThisP11CBasePlayeri" + } + "AddAccount" // void CBasePlayer::AddAccount(int amount, bool bTrackChange) { "library" "server" @@ -62,6 +78,30 @@ "linux" "@_Z23UTIL_FindEntityByStringP11CBaseEntityPKcS2_" "mac" "@_Z23UTIL_FindEntityByStringP11CBaseEntityPKcS2_" } + + "GetWeaponInfo" // WeaponInfoStruct *GetWeaponInfo(int id); + { + "library" "server" + "windows" "\x8B\x2A\x2A\x2A\x2A\x2A\x33\x2A\x85\x2A\x56\x74\x2A\x8B" + "linux" "@_Z13GetWeaponInfoi" + "mac" "@_Z13GetWeaponInfoi" + } + + "GetAmmoIndex" // int CBasePlayer::GetAmmoIndex(const char *psz) + { + "library" "server" + "windows" "\x56\x57\x8B\x2A\x2A\x2A\x85\x2A\x74\x2A\xBE" + "linux" "@_ZN11CBasePlayer12GetAmmoIndexEPKc" + "mac" "@_ZN11CBasePlayer12GetAmmoIndexEPKc" + } + + "BuyGunAmmo" // bool BuyGunAmmo(CBasePlayer *player, CBasePlayerItem *weapon, bool bBlinkMoney) + { + "library" "server" + "windows" "\x56\x57\x8B\x2A\x2A\x2A\x6A\x2A\x8B\x2A\xE8\x2A\x2A\x2A\x2A\x84\x2A\x0F" + "linux" "@_Z10BuyGunAmmoR11CBasePlayerR15CBasePlayerItemb" + "mac" "@_Z10BuyGunAmmoR11CBasePlayerR15CBasePlayerItemb" + } } } @@ -99,5 +139,344 @@ } } } + + "#default" + { + "ItemInfos" + { + "CommonAlias" + { + "p228" + { + "itemid" "1" // CSW/I_P228 + "classid" "2" // CS_WEAPONCLASS_PISTOL + } + "scout" + { + "itemid" "3" // CSW/I_SCOUT + "classid" "8" // CS_WEAPONCLASS_SNIPERRIFLE + } + "xm1014" + { + "itemid" "5" // CSW/I_XM1014 + "classid" "5" // CS_WEAPONCLASS_SHOTGUN + } + "mac10" + { + "itemid" "7" // CSW/I_MAC10 + "classid" "4" // CS_WEAPONCLASS_SUBMACHINEGUN + } + "aug" + { + "itemid" "8" // CSW/I_AUG + "classid" "7" // CS_WEAPONCLASS_RIFLE + } + "elites" + { + "itemid" "10" // CSW/I_ELITE + "classid" "2" // CS_WEAPONCLASS_PISTOL + } + "fn57" + { + "itemid" "11" // CSW/I_FIVESEVEN + "classid" "2" // CS_WEAPONCLASS_PISTOL + } + "ump45" + { + "itemid" "12" // CSW/I_UMP45 + "classid" "4" // CS_WEAPONCLASS_SUBMACHINEGUN + } + "sg550" + { + "itemid" "13" // CSW/I_SG550 + "classid" "8" // CS_WEAPONCLASS_SNIPERRIFLE + } + "galil" + { + "itemid" "14" // CSW/I_GALIL + "classid" "7" // CS_WEAPONCLASS_RIFLE + } + "famas" + { + "itemid" "15" // CSW/I_FAMAS + "classid" "7" // CS_WEAPONCLASS_RIFLE + } + "usp" + { + "itemid" "16" // CSW/I_USP + "classid" "2" // CS_WEAPONCLASS_PISTOL + } + "glock" + { + "itemid" "17" // CSW/I_GLOCK18 + "classid" "2" // CS_WEAPONCLASS_PISTOL + } + "awp" + { + "itemid" "18" // CSW/I_AWP + "classid" "8" // CS_WEAPONCLASS_SNIPERRIFLE + } + "mp5" + { + "itemid" "19" // CSW/I_MP5NAVY + "classid" "4" // CS_WEAPONCLASS_SUBMACHINEGUN + } + "m249" + { + "itemid" "20" // CSW/I_M249 + "classid" "6" // CS_WEAPONCLASS_MACHINEGUN + } + "m3" + { + "itemid" "21" // CSW/I_M3 + "classid" "5" // CS_WEAPONCLASS_SHOTGUN + } + "m4a1" + { + "itemid" "22" // CSW/I_M4A1 + "classid" "7" // CS_WEAPONCLASS_RIFLE + } + "tmp" + { + "itemid" "23" // CSW/I_TMP + "classid" "4" // CS_WEAPONCLASS_SUBMACHINEGUN + } + "g3sg1" + { + "itemid" "24" // CSW/I_G3SG1 + "classid" "8" // CS_WEAPONCLASS_SNIPERRIFLE + } + "deagle" + { + "itemid" "26" // CSW/I_DEAGLE + "classid" "2" // CS_WEAPONCLASS_PISTOL + } + "sg552" + { + "itemid" "27" // CSW/I_SG552 + "classid" "7" // CS_WEAPONCLASS_RIFLE + } + "ak47" + { + "itemid" "28" // CSW/I_AK47 + "classid" "7" // CS_WEAPONCLASS_RIFLE + } + "p90" + { + "itemid" "30" // CSW/I_P90 + "classid" "4" // CS_WEAPONCLASS_SUBMACHINEGUN + } + "fiveseven" + { + "itemid" "11" // CSW/I_FIVESEVEN + "classid" "2" // CS_WEAPONCLASS_PISTOL + } + } + + "WeaponAlias" + { + "grenade" + { + "itemid" "4" // CSW/I_HEGRENADE + "classid" "3" // CS_WEAPONCLASS_GRENADE + } + "hegrenade" + { + "itemid" "4" // CSW/I_HEGRENADE + "classid" "3" // CS_WEAPONCLASS_GRENADE + } + "c4" + { + "itemid" "6" // CSW/I_C4 + "classid" "3" // CS_WEAPONCLASS_GRENADE + } + "elite" + { + "itemid" "10" // CSW/I_ELITE + "classid" "2" // CS_WEAPONCLASS_PISTOL + } + "glock18" + { + "itemid" "17" // CSW/I_GLOCK18 + "classid" "2" // CS_WEAPONCLASS_PISTOL + } + "mp5navy" + { + "itemid" "19" // CSW/I_MP5NAVY + "classid" "4" // CS_WEAPONCLASS_SUBMACHINEGUN + } + "knife" + { + "itemid" "29" // CSW/I_KNIFE + "classid" "1" // CS_WEAPONCLASS_KNIFE + } + } + + "BuyAlias" + { + "228compact" + { + "itemid" "1" // CSW/I_P228 + "classid" "2" // CS_WEAPONCLASS_PISTOL + } + "autoshotgun" + { + "itemid" "5" // CSW/I_XM1014 + "classid" "5" // CS_WEAPONCLASS_SHOTGUN + } + "bullpup" + { + "itemid" "8" // CSW/I_AUG + "classid" "7" // CS_WEAPONCLASS_RIFLE + } + "sg550" + { + "itemid" "13" // CSW/I_SG550 + "classid" "7" // CS_WEAPONCLASS_RIFLE + } + "krieg550" + { + "itemid" "13" // CSW/I_SG550 + "classid" "7" // CS_WEAPONCLASS_RIFLE + } + "defender" + { + "itemid" "14" // CSW/I_GALIL + "classid" "7" // CS_WEAPONCLASS_RIFLE + } + "clarion" + { + "itemid" "15" // CSW/I_FAMAS + "classid" "7" // CS_WEAPONCLASS_RIFLE + } + "km45" + { + "itemid" "16" // CSW/I_USP + "classid" "2" // CS_WEAPONCLASS_PISTOL + } + "9x19mm" + { + "itemid" "17" // CSW/I_GLOCK18 + "classid" "2" // CS_WEAPONCLASS_PISTOL + } + "magnum" + { + "itemid" "18" // CSW/I_AWP + "classid" "8" // CS_WEAPONCLASS_SNIPERRIFLE + } + "smg" + { + "itemid" "19" // CSW/I_MP5NAVY + "classid" "4" // CS_WEAPONCLASS_SUBMACHINEGUN + } + "12gauge" + { + "itemid" "21" // CSW/I_M3 + "classid" "5" // CS_WEAPONCLASS_SHOTGUN + } + "mp" + { + "itemid" "23" // CSW/I_TMP + "classid" "4" // CS_WEAPONCLASS_SUBMACHINEGUN + } + "d3au1" + { + "itemid" "24" // CSW/I_G3SG1 + "classid" "8" // CS_WEAPONCLASS_SNIPERRIFLE + } + "nighthawk" + { + "itemid" "26" // CSW/I_DEAGLE + "classid" "2" // CS_WEAPONCLASS_PISTOL + } + "krieg552" + { + "itemid" "27" // CSW/I_SG552 + "classid" "7" // CS_WEAPONCLASS_RIFLE + } + "cv47" + { + "itemid" "28" // CSW/I_AK47 + "classid" "7" // CS_WEAPONCLASS_RIFLE + } + "c90" + { + "itemid" "30" // CSW/I_P90 + "classid" "4" // CS_WEAPONCLASS_SUBMACHINEGUN + } + } + + "BuyEquipAlias" + { + "hegren" + { + "itemid" "4" // CSW/I_HEGRENADE + "classid" "3" // CS_WEAPONCLASS_GRENADE + "price" "300" + } + "sgren" + { + "itemid" "9" // CSW/I_SMOKEGRENADE + "classid" "3" // CS_WEAPONCLASS_GRENADE + "price" "300" + } + "flash" + { + "itemid" "25" // CSW/I_FLASHBANG + "classid" "3" // CS_WEAPONCLASS_GRENADE + "price" "200" + } + "vest" + { + "itemid" "31" // CSW/I_VEST + "classname" "item_kevlar" + "price" "650" + } + "vesthelm" + { + "itemid" "32" // CSW/I_VESTHELM + "classname" "item_assaultsuit" + "price" "1000" + } + "defuser" + { + "itemid" "33" // CSI_DEFUSER + "classname" "item_thighpack" + "price" "200" + } + "nvgs" + { + "itemid" "34" // CSI_NVGS + "price" "1250" + } + "shield" + { + "itemid" "35" // CSI_SHIELD + "classid" "2" // CS_WEAPONCLASS_PISTOL + "classname" "weapon_shield" + } + } + + "BuyAmmoAlias" + { + "primammo" + { + "itemid" "36" // CSI_PRIAMMO + } + "secammo" + { + "itemid" "37" // CSI_SECAMMO + } + "buyammo1" + { + "itemid" "36" // CSI_PRIAMMO + } + "buyammo2" + { + "itemid" "37" // CSI_SECAMMO + } + } + } + } } diff --git a/modules/cstrike/cstrike/AMBuilder b/modules/cstrike/cstrike/AMBuilder index 0db59424..3c86c911 100644 --- a/modules/cstrike/cstrike/AMBuilder +++ b/modules/cstrike/cstrike/AMBuilder @@ -15,6 +15,7 @@ binary.sources = [ 'CstrikeHacks.cpp', 'CstrikeUtils.cpp', 'CstrikeUserMessages.cpp', + 'CstrikeItemsInfos.cpp', '../../../public/memtools/MemoryUtils.cpp', '../../../public/memtools/CDetour/detours.cpp', '../../../public/memtools/CDetour/asm/asm.c', diff --git a/modules/cstrike/cstrike/CstrikeDatas.h b/modules/cstrike/cstrike/CstrikeDatas.h index 6167f7f3..607d7895 100644 --- a/modules/cstrike/cstrike/CstrikeDatas.h +++ b/modules/cstrike/cstrike/CstrikeDatas.h @@ -17,6 +17,7 @@ /** * Weapon Ids for use with CS_OnBuyAttempt(), CS_OnBuy(). */ +#define CSI_NONE CSW_NONE #define CSI_P228 CSW_P228 #define CSI_SCOUT CSW_SCOUT #define CSI_HEGRENADE CSW_HEGRENADE @@ -50,15 +51,27 @@ #define CSI_VESTHELM CSW_VESTHELM // Custom #define CSI_DEFUSER 33 // Custom #define CSI_NVGS 34 // Custom +#define CSI_SHIELD 35 // Custom - The value passed by the forward, more convenient for plugins. #define CSI_PRIAMMO 36 // Custom #define CSI_SECAMMO 37 // Custom -#define CSI_SHIELD 38 // Custom - The value passed by the forward, more convenient for plugins. +#define CSI_MAX_COUNT 38 +#define CSI_LAST_WEAPON CSW_LAST_WEAPON -#define BITS_PISTOLS (1< - -void CtrlDetours_ClientCommand(bool set); -void CtrlDetours_BuyCommands(bool set); -void CtrlDetours_Natives(bool set); +#include "CstrikeItemsInfos.h" int ForwardInternalCommand = -1; int ForwardOnBuy = -1; int ForwardOnBuyAttempt = -1; +bool HasInternalCommandForward; +bool HasOnBuyAttemptForward; +bool HasOnBuyForward; + int *UseBotArgs; const char **BotArgs; @@ -31,25 +31,47 @@ CDetour *ClientCommandDetour; CDetour *GiveShieldDetour; CDetour *GiveNamedItemDetour; CDetour *AddAccountDetour; +CDetour *CanPlayerBuyDetour; +CDetour *CanBuyThisDetour; CDetour *GiveDefaultItemsDetour; +CDetour *BuyGunAmmoDetour; CreateNamedEntityFunc CS_CreateNamedEntity; UTIL_FindEntityByStringFunc CS_UTIL_FindEntityByString; +GetWeaponInfoFunc GetWeaponInfo; int CurrentItemId; -StringHashMap ItemAliasList; +bool TriggeredFromCommand; +bool BlockMoneyUpdate; +bool BlockAmmosUpdate; + +// CBasePlayer members. TypeDescription TeamDesc; TypeDescription MenuDesc; +TypeDescription NvgsDesc; +TypeDescription DefuserDesc; +TypeDescription SignalsDesc; +TypeDescription MoneyDesc; +// GameRules members. +TypeDescription BombTargetDesc; + +// Engine global variables. server_static_t *ServerStatic; server_t *Server; +// Mod global variable +void **GameRules; + + void InitializeHacks() { CtrlDetours_ClientCommand(true); CtrlDetours_BuyCommands(true); CtrlDetours_Natives(true); + InitFuncsAddresses(); + InitClassMembers(); InitGlobalVars(); } @@ -77,37 +99,26 @@ const char *CMD_ARGV(int i) return g_engfuncs.pfnCmd_Argv(i); } -void OnEmitSound(edict_t *entity, int channel, const char *sample, float volume, float attenuation, int fFlags, int pitch) -{ - // If shield is blocked with CS_OnBuy, we need to block the pickup sound ("items/gunpickup2.wav") - // as well played right after. Why this sound is not contained in GiveShield()? - - g_pengfuncsTable->pfnEmitSound = nullptr; - - RETURN_META(MRES_SUPERCEDE); -} - DETOUR_DECL_STATIC1(C_ClientCommand, void, edict_t*, pEdict) // void ClientCommand(edict_t *pEntity) { - const char *command = CMD_ARGV(0); - - // A new command is triggered, reset variable, always. - CurrentItemId = 0; + auto command = CMD_ARGV(0); + + CurrentItemId = CSI_NONE; // Purpose is to retrieve an item id based on alias name or selected item from menu, - // to be used in OnBuy* forwards. - if ((ForwardOnBuyAttempt != -1 || ForwardOnBuy != -1) && command && *command) + // to be used in CS_OnBuy* forwards. + if ((HasOnBuyAttemptForward || HasOnBuyForward) && command && *command) { - int itemId = 0; - + int itemId = CSI_NONE; + // Handling buy via menu. - if (!strcmp(command, "menuselect")) + if (!strcmp(command, "menuselect")) { int slot = atoi(CMD_ARGV(1)); if (slot > 0 && slot < 9) { - static const int menuItemsTe[][9] = + static const int menuItemsTe[][9] = { /* Menu_Buy */ { 0, 0, 0, 0, 0, 0, CSI_PRIAMMO, CSI_SECAMMO, 0 }, /* Menu_BuyPistol */ { 0, CSI_GLOCK18, CSI_USP, CSI_P228, CSI_DEAGLE, CSI_ELITE, 0, 0, 0 }, @@ -118,7 +129,7 @@ DETOUR_DECL_STATIC1(C_ClientCommand, void, edict_t*, pEdict) // void ClientComma /* Menu_BuyItem */ { 0, CSI_VEST, CSI_VESTHELM, CSI_FLASHBANG, CSI_HEGRENADE, CSI_SMOKEGRENADE, CSI_NVGS, 0, 0 } }; - static const int menuItemsCt[][9] = + static const int menuItemsCt[][9] = { /* Menu_Buy */ { 0, 0, 0, 0, 0, 0, CSI_PRIAMMO, CSI_SECAMMO, 0 }, /* Menu_BuyPistol */ { 0, CSI_GLOCK18, CSI_USP, CSI_P228, CSI_DEAGLE, CSI_FIVESEVEN, 0, 0, 0 }, @@ -126,7 +137,7 @@ DETOUR_DECL_STATIC1(C_ClientCommand, void, edict_t*, pEdict) // void ClientComma /* Menu_BuyMachineGun */ { 0, CSI_M249, 0, 0, 0, 0, 0, 0, 0 }, /* Menu_BuyShotgun */ { 0, CSI_M3, CSI_XM1014, 0, 0, 0, 0, 0, 0 }, /* Menu_BuySubMachineGun */ { 0, CSI_TMP, CSI_MP5NAVY, CSI_UMP45, CSI_P90, 0, 0, 0, 0 }, - /* Menu_BuyItem */ { 0, CSI_VEST, CSI_VESTHELM, CSI_FLASHBANG, CSI_HEGRENADE, CSI_SMOKEGRENADE, CSI_NVGS, CSI_DEFUSER, CSI_SHIELDGUN } + /* Menu_BuyItem */ { 0, CSI_VEST, CSI_VESTHELM, CSI_FLASHBANG, CSI_HEGRENADE, CSI_SMOKEGRENADE, CSI_NVGS, CSI_DEFUSER, CSI_SHIELD } }; int menuId = get_pdata(pEdict, MenuDesc.fieldOffset); @@ -148,39 +159,40 @@ DETOUR_DECL_STATIC1(C_ClientCommand, void, edict_t*, pEdict) // void ClientComma } else // Handling buy via alias { - if (ItemAliasList.retrieve(command, &itemId)) + if (get_pdata(pEdict, SignalsDesc.fieldOffset).GetState() & SIGNAL_BUY) // Are we inside the buy zone? { - CurrentItemId = itemId; + AliasInfo info; + + if (ItemsManager.GetAliasInfosFromBuy(command, &info)) + { + CurrentItemId = info.itemid; + } } } } - int client = ENTINDEX(pEdict); + auto client = TypeConversion.edict_to_id(pEdict); - if (ForwardInternalCommand != -1 && *UseBotArgs) - { - const char *args = *BotArgs; - - if (MF_ExecuteForward(ForwardInternalCommand, static_cast(client), args) > 0) - { - return; - } - } - - if (ForwardOnBuyAttempt != -1 && - CurrentItemId && - MF_IsPlayerAlive(client) && - MF_ExecuteForward(ForwardOnBuyAttempt, static_cast(client), static_cast(CurrentItemId)) > 0) + if (HasInternalCommandForward && *UseBotArgs && MF_ExecuteForward(ForwardInternalCommand, client, *BotArgs) > 0) { return; } + if (HasOnBuyAttemptForward && CurrentItemId && MF_IsPlayerAlive(client) && MF_ExecuteForward(ForwardOnBuyAttempt, client, CurrentItemId) > 0) + { + return; + } + + TriggeredFromCommand = CurrentItemId != CSI_NONE; + DETOUR_STATIC_CALL(C_ClientCommand)(pEdict); + + TriggeredFromCommand = BlockMoneyUpdate = BlockAmmosUpdate = false; } edict_s* OnCreateNamedEntity(int classname) { - if (NoKifesMode) + if (NoKnivesMode) { if (!strcmp(STRING(classname), "weapon_knife")) { @@ -192,12 +204,12 @@ edict_s* OnCreateNamedEntity(int classname) g_pengfuncsTable->pfnCreateNamedEntity = nullptr; } - RETURN_META_VALUE(MRES_IGNORED, 0); + RETURN_META_VALUE(MRES_IGNORED, nullptr); } DETOUR_DECL_MEMBER0(GiveDefaultItems, void) // void CBasePlayer::GiveDefaultItems(void) { - if (NoKifesMode) + if (NoKnivesMode) { g_pengfuncsTable->pfnCreateNamedEntity = OnCreateNamedEntity; } @@ -207,65 +219,143 @@ DETOUR_DECL_MEMBER0(GiveDefaultItems, void) // void CBasePlayer::GiveDefaultIte g_pengfuncsTable->pfnCreateNamedEntity = nullptr; } -DETOUR_DECL_MEMBER1(GiveNamedItem, void, const char*, pszName) // void CBasePlayer::GiveNamedItem(const char *pszName) +DETOUR_DECL_MEMBER1(CanPlayerBuy, bool, bool, display) // bool CBasePlayer::CanPlayerBuy(bool display) { - // If the current item id is not null, this means player has triggers a buy command. - if (CurrentItemId) - { - int client = TypeConversion.cbase_to_id(this); + auto canBuy = DETOUR_MEMBER_CALL(CanPlayerBuy)(display); - if (MF_IsPlayerAlive(client) && MF_ExecuteForward(ForwardOnBuy, static_cast(client), static_cast(CurrentItemId)) > 0) + if (!canBuy || !TriggeredFromCommand || !(CurrentItemId == CSI_NVGS || CurrentItemId == CSI_DEFUSER)) + { + return canBuy; + } + + auto pPlayer = TypeConversion.cbase_to_edict(this); + auto playerId = TypeConversion.edict_to_id(pPlayer); + + if (!MF_IsPlayerAlive(playerId)) + { + return canBuy; + } + + auto allowedToBuy = false; + auto itemPrice = ItemsManager.GetItemPrice(CurrentItemId); + + switch (CurrentItemId) + { + case CSI_NVGS: { - return; + allowedToBuy = !get_pdata(pPlayer, NvgsDesc.fieldOffset) && + get_pdata(pPlayer, MoneyDesc.fieldOffset) >= itemPrice; + break; + } + case CSI_DEFUSER: + { + allowedToBuy = !get_pdata(pPlayer, DefuserDesc.fieldOffset) && + get_pdata(pPlayer, TeamDesc.fieldOffset) == TEAM_CT && + get_pdata(*GameRules, BombTargetDesc.fieldOffset) && + get_pdata(pPlayer, MoneyDesc.fieldOffset) >= itemPrice; + break; } } - // From here, forward is not blocked, resetting this - // to ignore code in AddAccount which is called right after. - CurrentItemId = 0; + if (allowedToBuy && MF_ExecuteForward(ForwardOnBuy, playerId, CurrentItemId) > 0) + { + canBuy = false; + } - // Give me my item! - DETOUR_MEMBER_CALL(GiveNamedItem)(pszName); + return canBuy; } -DETOUR_DECL_MEMBER1(GiveShield, void, bool, bRetire) // void CBasePlayer::GiveShield(bool bRetire) +DETOUR_DECL_STATIC2(CanBuyThis, bool, void*, pvPlayer, int, weaponId) // bool CanBuyThis(CBasePlayer *pPlayer, int weaponId) { - // Special case for shield. Game doesn't use GiveNamedItem() to give a shield. - if (CurrentItemId == CSI_SHIELDGUN) - { - int client = TypeConversion.cbase_to_id(this); + auto canBuy = DETOUR_STATIC_CALL(CanBuyThis)(pvPlayer, weaponId); - if (MF_IsPlayerAlive(client) && MF_ExecuteForward(ForwardOnBuy, static_cast(client), CSI_SHIELDGUN) > 0) + if (!canBuy || !TriggeredFromCommand || !((1 << CurrentItemId & CSI_ALL_GUNS) || CurrentItemId == CSI_SHIELD)) + { + return canBuy; + } + + auto playerId = TypeConversion.cbase_to_id(pvPlayer); + + if (MF_IsPlayerAlive(playerId) && get_pdata(pvPlayer, MoneyDesc.fieldOffset) >= ItemsManager.GetItemPrice(CurrentItemId)) + { + if (MF_ExecuteForward(ForwardOnBuy, playerId, CurrentItemId) > 0) { - return; + canBuy = false; } } - // From here, forward is not blocked, resetting this - // to ignore code in AddAccount which is called right after. - CurrentItemId = 0; + return canBuy; +} - // Give me my shield! - DETOUR_MEMBER_CALL(GiveShield)(bRetire); +DETOUR_DECL_STATIC3(BuyGunAmmo, bool, void*, player, int, nSlot, bool, bBlinkMoney) // bool BuyGunAmmo(CBasePlayer *player, int nSlot, bool bBlinkMoney) +{ + auto result = DETOUR_STATIC_CALL(BuyGunAmmo)(player, nSlot, bBlinkMoney); + + if (result && BlockAmmosUpdate) + { + BlockAmmosUpdate = false; + return false; + } + + return result; +} + +DETOUR_DECL_MEMBER1(GiveNamedItem, void, const char*, pszName) // void CBasePlayer::GiveNamedItem(const char *pszName) +{ + if (TriggeredFromCommand) + { + switch (CurrentItemId) + { + case CSI_VEST: + case CSI_VESTHELM: + case CSI_FLASHBANG: + case CSI_HEGRENADE: + case CSI_SMOKEGRENADE: + case CSI_PRIAMMO: + case CSI_SECAMMO: + { + auto playerId = TypeConversion.cbase_to_id(this); + + if (MF_IsPlayerAlive(playerId) && MF_ExecuteForward(ForwardOnBuy, playerId, CurrentItemId) > 0) + { + BlockAmmosUpdate = CurrentItemId == CSI_PRIAMMO || CurrentItemId == CSI_SECAMMO; + BlockMoneyUpdate = true; + return; + } + } + } + } + + DETOUR_MEMBER_CALL(GiveNamedItem)(pszName); } DETOUR_DECL_MEMBER2(AddAccount, void, int, amount, bool, bTrackChange) // void CBasePlayer::AddAccount(int amount, bool bTrackChange) { - // No buy command or forward not blocked. - // Resuming game flow. - if (!CurrentItemId) + if (BlockMoneyUpdate) { - DETOUR_MEMBER_CALL(AddAccount)(amount, bTrackChange); - } - // Shield is blocked. - // We need to hook EmitSound to block pickup sound played right after. - else if (CurrentItemId == CSI_SHIELDGUN) - { - g_pengfuncsTable->pfnEmitSound = OnEmitSound; + BlockMoneyUpdate = false; + return; } - // Let's reset this right away to avoid issues. - CurrentItemId = 0; + DETOUR_MEMBER_CALL(AddAccount)(amount, bTrackChange); +} + + +void ToggleDetour(CDetour *detour, bool enable) +{ + if (detour) + { + (enable) ? detour->EnableDetour() : detour->DisableDetour(); + } +} + +void DestroyDetour(CDetour *&detour) +{ + if (detour) + { + detour->Destroy(); + detour = nullptr; + } } @@ -273,116 +363,55 @@ void CtrlDetours_ClientCommand(bool set) { if (set) { - void *base = reinterpret_cast(MDLL_ClientCommand); + auto base = reinterpret_cast(MDLL_ClientCommand); -#if defined(WIN32) +#if defined(KE_WINDOWS) TypeDescription type; if (MainConfig->GetOffset("UseBotArgs", &type)) { - UseBotArgs = get_pdata(base, type.fieldOffset); + UseBotArgs = get_pdata(base, type.fieldOffset); } if (MainConfig->GetOffset("BotArgs", &type)) { - BotArgs = get_pdata(base, type.fieldOffset); + BotArgs = get_pdata(base, type.fieldOffset); } - -#elif defined(__linux__) || defined(__APPLE__) - +#else void *address = nullptr; if (MainConfig->GetMemSig("UseBotArgs", &address)) { - UseBotArgs = reinterpret_cast(address); + UseBotArgs = reinterpret_cast(address); } if (MainConfig->GetMemSig("BotArgs", &address)) { - BotArgs = reinterpret_cast(address); + BotArgs = reinterpret_cast(address); } #endif ClientCommandDetour = DETOUR_CREATE_STATIC_FIXED(C_ClientCommand, base); - CommonConfig->GetOffsetByClass("CBasePlayer", "m_iTeam", &TeamDesc); - CommonConfig->GetOffsetByClass("CBasePlayer", "m_iMenu", &MenuDesc); - - if (!ClientCommandDetour || !UseBotArgs || !BotArgs || !TeamDesc.fieldOffset || !MenuDesc.fieldOffset) + if (!ClientCommandDetour) { - MF_Log("ClientCommand is not available - forward client_command has been disabled"); + MF_Log("ClientCommand is not available - forwards CS_InternalCommand and CS_OnBuy[Attempt] have been disabled"); + CtrlDetours_ClientCommand(false); + } + else if (!UseBotArgs || !BotArgs) + { + MF_Log("UseBotArgs or BotArgs is not available - forward CS_InternalCommand has been disabled"); } } else { - if (ClientCommandDetour) - { - ClientCommandDetour->Destroy(); - } - - ItemAliasList.clear(); + DestroyDetour(ClientCommandDetour); } } void ToggleDetour_ClientCommands(bool enable) { - if (ClientCommandDetour) - { - (enable) ? ClientCommandDetour->EnableDetour() : ClientCommandDetour->DisableDetour(); - } - - if (enable) - { - // Build the item alias list. - // Used in ClientCommand to check and get fastly item id from alias name. - typedef struct - { - const char *alias; - int id; - - } itemBuyAliasInfo; - - itemBuyAliasInfo aliasToId[] = - { - { "p228" , CSI_P228 }, { "228compact" , CSI_P228 }, - { "scout" , CSI_SCOUT }, { "hegren" , CSI_HEGRENADE }, - { "xm1014" , CSI_XM1014 }, { "autoshotgun", CSI_XM1014 }, - { "mac10" , CSI_MAC10 }, { "aug" , CSI_AUG }, - { "bullpup" , CSI_AUG }, { "sgren" , CSI_SMOKEGRENADE }, - { "elites" , CSI_ELITE }, { "fn57" , CSI_FIVESEVEN }, - { "fiveseven" , CSI_FIVESEVEN }, { "ump45" , CSI_UMP45 }, - { "sg550" , CSI_SG550 }, { "krieg550" , CSI_SG550 }, - { "galil" , CSI_GALIL }, { "defender" , CSI_GALIL }, - { "famas" , CSI_FAMAS }, { "clarion" , CSI_FAMAS }, - { "usp" , CSI_USP }, { "km45" , CSI_USP }, - { "glock" , CSI_GLOCK18 }, { "9x19mm" , CSI_GLOCK18 }, - { "awp" , CSI_AWP }, { "magnum" , CSI_AWP }, - { "mp5" , CSI_MP5NAVY }, { "smg" , CSI_MP5NAVY }, - { "m249" , CSI_M249 }, { "m3" , CSI_M3 }, - { "12gauge" , CSI_M3 }, { "m4a1" , CSI_M4A1 }, - { "tmp" , CSI_TMP }, { "mp" , CSI_TMP }, - { "g3sg1" , CSI_G3SG1 }, { "d3au1" , CSI_G3SG1 }, - { "flash" , CSI_FLASHBANG }, { "deagle" , CSI_DEAGLE }, - { "nighthawk" , CSI_DEAGLE }, { "sg552" , CSI_SG552 }, - { "krieg552" , CSI_SG552 }, { "ak47" , CSI_AK47 }, - { "cv47" , CSI_AK47 }, { "p90" , CSI_P90 }, - { "c90" , CSI_P90 }, { "vest" , CSI_VEST }, - { "vesthelm" , CSI_VESTHELM }, { "defuser" , CSI_DEFUSER }, - { "nvgs" , CSI_NVGS }, { "shield" , CSI_SHIELDGUN }, - { "buyammo1" , CSI_PRIAMMO }, { "primammo" , CSI_PRIAMMO }, - { "buyammo2" , CSI_SECAMMO }, { "secammo" , CSI_SECAMMO }, - { nullptr , 0 } - }; - - for (size_t i = 0; aliasToId[i].alias != nullptr; ++i) - { - ItemAliasList.insert(aliasToId[i].alias, aliasToId[i].id); - } - } - else - { - ItemAliasList.clear(); - } + ToggleDetour(ClientCommandDetour, enable); } @@ -392,9 +421,9 @@ void CtrlDetours_BuyCommands(bool set) { void *address = nullptr; - if (MainConfig->GetMemSig("GiveShield", &address)) + if (MainConfig->GetMemSig("BuyGunAmmo", &address)) { - GiveShieldDetour = DETOUR_CREATE_MEMBER_FIXED(GiveShield, address); + BuyGunAmmoDetour = DETOUR_CREATE_STATIC_FIXED(BuyGunAmmo, address); } if (MainConfig->GetMemSig("GiveNamedItem", &address)) @@ -406,12 +435,22 @@ void CtrlDetours_BuyCommands(bool set) { AddAccountDetour = DETOUR_CREATE_MEMBER_FIXED(AddAccount, address); } - - if (!GiveShieldDetour || !GiveNamedItemDetour || !AddAccountDetour) + + if (MainConfig->GetMemSig("CanPlayerBuy", &address)) { - if (!GiveShieldDetour) + CanPlayerBuyDetour = DETOUR_CREATE_MEMBER_FIXED(CanPlayerBuy, address); + } + + if (MainConfig->GetMemSig("CanBuyThis", &address)) + { + CanBuyThisDetour = DETOUR_CREATE_STATIC_FIXED(CanBuyThis, address); + } + + if (!BuyGunAmmoDetour || !GiveNamedItemDetour || !AddAccountDetour || !CanPlayerBuyDetour || !CanBuyThisDetour) + { + if (!BuyGunAmmoDetour) { - MF_Log("GiveShield is not available"); + MF_Log("BuyGunAmmo is not available"); } if (!GiveNamedItemDetour) @@ -424,74 +463,46 @@ void CtrlDetours_BuyCommands(bool set) MF_Log("AddAccount is not available"); } - MF_Log("Some functions are not available - forward CS_OnBuyAttempt and CS_OnBuy have been disabled"); + if (!CanPlayerBuyDetour) + { + MF_Log("CanPlayerBuy is not available"); + } + + if (!CanBuyThisDetour) + { + MF_Log("CanBuyThis is not available"); + } + + MF_Log("Some functions are not available - forwards CS_OnBuy[Attempt] have been disabled"); + ToggleDetour_BuyCommands(false); } } else { - if (GiveShieldDetour) - { - GiveShieldDetour->Destroy(); - } - - if (GiveNamedItemDetour) - { - GiveNamedItemDetour->Destroy(); - } - - if (AddAccountDetour) - { - AddAccountDetour->Destroy(); - } - - ItemAliasList.clear(); + DestroyDetour(BuyGunAmmoDetour); + DestroyDetour(GiveNamedItemDetour); + DestroyDetour(AddAccountDetour); + DestroyDetour(CanPlayerBuyDetour); + DestroyDetour(CanBuyThisDetour); } } void ToggleDetour_BuyCommands(bool enable) { - if (GiveShieldDetour) - { - (enable) ? GiveShieldDetour->EnableDetour() : GiveShieldDetour->DisableDetour(); - } - - if (GiveNamedItemDetour) - { - (enable) ? GiveNamedItemDetour->EnableDetour() : GiveNamedItemDetour->DisableDetour(); - } - - if (AddAccountDetour) - { - (enable) ? AddAccountDetour->EnableDetour() : AddAccountDetour->DisableDetour(); - } + ToggleDetour(BuyGunAmmoDetour, enable); + ToggleDetour(GiveNamedItemDetour, enable); + ToggleDetour(AddAccountDetour, enable); + ToggleDetour(CanPlayerBuyDetour, enable); + ToggleDetour(CanBuyThisDetour, enable); } + void CtrlDetours_Natives(bool set) { if (set) { void *address = nullptr; - if (MainConfig->GetMemSig("CreateNamedEntity", &address)) // cs_create_entity() - { - CS_CreateNamedEntity = reinterpret_cast(address); - } - - if (MainConfig->GetMemSig("FindEntityByString", &address)) // cs_find_ent_by_class() - { - CS_UTIL_FindEntityByString = reinterpret_cast(address); - } - - if (!CS_CreateNamedEntity) - { - MF_Log("CREATE_NAMED_ENITTY is not available - native cs_create_entity() has been disabled"); - } - - if (!CS_UTIL_FindEntityByString) - { - MF_Log("UTIL_FindEntByString is not available - native cs_find_ent_by_class() has been disabled"); - } - if (MainConfig->GetMemSig("GiveDefaultItems", &address)) { GiveDefaultItemsDetour = DETOUR_CREATE_MEMBER_FIXED(GiveDefaultItems, address); @@ -504,10 +515,66 @@ void CtrlDetours_Natives(bool set) } else { - if (GiveDefaultItemsDetour) - { - GiveDefaultItemsDetour->Destroy(); - } + DestroyDetour(GiveDefaultItemsDetour); + } +} + + +void InitFuncsAddresses() +{ + void *address = nullptr; + + if (MainConfig->GetMemSig("CreateNamedEntity", &address)) // cs_create_entity() + { + CS_CreateNamedEntity = reinterpret_cast(address); + } + + if (MainConfig->GetMemSig("FindEntityByString", &address)) // cs_find_ent_by_class() + { + CS_UTIL_FindEntityByString = reinterpret_cast(address); + } + + if (MainConfig->GetMemSig("GetWeaponInfo", &address)) // cs_get_weapon_info() + { + GetWeaponInfo = reinterpret_cast(address); + } + + + if (!CS_CreateNamedEntity) + { + MF_Log("CREATE_NAMED_ENITTY is not available - native cs_create_entity() has been disabled"); + } + + if (!CS_UTIL_FindEntityByString) + { + MF_Log("UTIL_FindEntByString is not available - native cs_find_ent_by_class() has been disabled"); + } + + if (!GetWeaponInfo) + { + MF_Log("GetWeaponInfo is not available - native cs_get_weapon_info() and forward CS_OnBuy have been disabled"); + CtrlDetours_BuyCommands(false); + } +} + +void InitClassMembers() +{ + CommonConfig->GetOffsetByClass("CBasePlayer", "m_iTeam" , &TeamDesc ); + CommonConfig->GetOffsetByClass("CBasePlayer", "m_iMenu" , &MenuDesc ); + CommonConfig->GetOffsetByClass("CBasePlayer", "m_bHasNightVision", &NvgsDesc ); + CommonConfig->GetOffsetByClass("CBasePlayer", "m_bHasDefuser" , &DefuserDesc); + CommonConfig->GetOffsetByClass("CBasePlayer", "m_signals" , &SignalsDesc); + CommonConfig->GetOffsetByClass("CBasePlayer", "m_iAccount" , &MoneyDesc ); + + if (!TeamDesc.fieldOffset || + !MenuDesc.fieldOffset || + !NvgsDesc.fieldOffset || + !DefuserDesc.fieldOffset || + !SignalsDesc.fieldOffset || + !MoneyDesc.fieldOffset) + { + MF_Log("Invalid or missing entity gamedata files - forwards CS_OnBuy[Attempt] have been disabled"); + CtrlDetours_BuyCommands(false); } } @@ -515,30 +582,43 @@ void InitGlobalVars() { void *address = nullptr; -#if defined(WIN32) +#if defined(KE_WINDOWS) TypeDescription typeDesc; if (CommonConfig->GetOffset("svs", &typeDesc)) { uintptr_t base = *reinterpret_cast(reinterpret_cast(g_engfuncs.pfnGetCurrentPlayer) + typeDesc.fieldOffset); - ServerStatic = reinterpret_cast(base - 4); + ServerStatic = reinterpret_cast(base - 4); } if (CommonConfig->GetAddress("sv", &address)) { - Server = *reinterpret_cast(address); + Server = *reinterpret_cast(address); } + + if (CommonConfig->GetAddress("g_pGameRules", &address)) + { + GameRules = *reinterpret_cast(address); + } + #else + if (CommonConfig->GetMemSig("svs", &address)) { - ServerStatic = reinterpret_cast(address); + ServerStatic = reinterpret_cast(address); } if (CommonConfig->GetMemSig("sv", &address)) { - Server = reinterpret_cast(address); + Server = reinterpret_cast(address); } + + if (CommonConfig->GetMemSig("g_pGameRules", &address)) + { + GameRules = reinterpret_cast(address); + } + #endif if (!ServerStatic) @@ -550,4 +630,10 @@ void InitGlobalVars() { MF_Log("sv global variable is not available"); } -} \ No newline at end of file + + if (!GameRules) + { + MF_Log("g_pGameRules is not available - Forward CS_OnBuy has been disabled"); + CtrlDetours_BuyCommands(false); + } +} diff --git a/modules/cstrike/cstrike/CstrikeHacks.h b/modules/cstrike/cstrike/CstrikeHacks.h index c7d91474..743a7cb9 100644 --- a/modules/cstrike/cstrike/CstrikeHacks.h +++ b/modules/cstrike/cstrike/CstrikeHacks.h @@ -18,10 +18,18 @@ #include #include #include +#include "CstrikeDatas.h" void InitializeHacks(); +void InitFuncsAddresses(); +void InitClassMembers(); void InitGlobalVars(); void ShutdownHacks(); + +void CtrlDetours_ClientCommand(bool set); +void CtrlDetours_BuyCommands(bool set); +void CtrlDetours_Natives(bool set); + void ToggleDetour_ClientCommands(bool enable); void ToggleDetour_BuyCommands(bool enable); @@ -34,18 +42,36 @@ extern int ForwardInternalCommand; extern int ForwardOnBuy; extern int ForwardOnBuyAttempt; +extern bool HasInternalCommandForward; +extern bool HasOnBuyAttemptForward; +extern bool HasOnBuyForward; + +extern CDetour *ClientCommandDetour; +extern CDetour *GiveNamedItemDetour; +extern CDetour *AddAccountDetour; +extern CDetour *CanPlayerBuyDetour; +extern CDetour *CanBuyThisDetour; +extern CDetour *GiveDefaultItemsDetour; +extern CDetour *BuyGunAmmoDetour; + typedef edict_t* (*CreateNamedEntityFunc)(string_t iszClassname); typedef void* (*UTIL_FindEntityByStringFunc)(void* pStartEntity, const char *szKeyword, const char *szValue); +typedef WeaponInfoStruct* (*GetWeaponInfoFunc)(int id); extern CreateNamedEntityFunc CS_CreateNamedEntity; extern UTIL_FindEntityByStringFunc CS_UTIL_FindEntityByString; +extern GetWeaponInfoFunc GetWeaponInfo; extern CDetour *GiveDefaultItemsDetour; extern enginefuncs_t *g_pengfuncsTable; extern DLL_FUNCTIONS *g_pFunctionTable; -extern bool NoKifesMode; +extern bool NoKnivesMode; extern server_static_t *ServerStatic; extern server_t *Server; +extern void **GameRules; + +extern int *UseBotArgs; +extern const char **BotArgs; #endif // CSTRIKE_HACKS_H diff --git a/modules/cstrike/cstrike/CstrikeItemsInfos.cpp b/modules/cstrike/cstrike/CstrikeItemsInfos.cpp new file mode 100644 index 00000000..40b83b41 --- /dev/null +++ b/modules/cstrike/cstrike/CstrikeItemsInfos.cpp @@ -0,0 +1,277 @@ +// vim: set ts=4 sw=4 tw=99 noet: +// +// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). +// Copyright (C) The AMX Mod X Development Team. +// +// This software is licensed under the GNU General Public License, version 3 or higher. +// Additional exceptions apply. For full license details, see LICENSE.txt or visit: +// https://alliedmods.net/amxmodx-license + +// +// Counter-Strike Module +// + +#include "CstrikeItemsInfos.h" +#include "CstrikeHacks.h" + +CsItemInfo ItemsManager; +char WeaponNameList[MAX_WEAPONS][64]; + +#define PSTATE_ALIASES_TYPE 0 +#define PSTATE_ALIASES_ALIAS 1 +#define PSTATE_ALIASES_ALIAS_DEFS 2 + +CsItemInfo::CsItemInfo() + : + m_ParseState(PSTATE_ALIASES_TYPE), + m_List(nullptr), + m_ListsRetrievedFromConfig(false) +{} + +CsItemInfo::~CsItemInfo() +{ + Clear(); +} + +void CsItemInfo::Clear() +{ + m_CommonAliasesList.clear(); + m_WeaponAliasesList.clear(); + m_BuyAliasesList.clear(); +} + +bool CsItemInfo::HasConfigError() +{ + return !m_ListsRetrievedFromConfig; +} + +SMCResult CsItemInfo::ReadSMC_NewSection(const SMCStates *states, const char *name) +{ + switch (m_ParseState) + { + case PSTATE_ALIASES_TYPE: + { + m_List = nullptr; + + if (!strcmp(name, "CommonAlias")) + { + m_List = &m_CommonAliasesList; + } + else if (!strcmp(name, "WeaponAlias")) + { + m_List = &m_WeaponAliasesList; + } + else if (!strcmp(name, "BuyAlias") || !strcmp(name, "BuyEquipAlias") || !strcmp(name, "BuyAmmoAlias")) + { + m_List = &m_BuyAliasesList; + } + + if (m_List) + { + m_ParseState = PSTATE_ALIASES_ALIAS; + } + + break; + } + case PSTATE_ALIASES_ALIAS: + { + m_AliasInfo.clear(); + m_Alias = name; + + m_ParseState = PSTATE_ALIASES_ALIAS_DEFS; + break; + } + } + + return SMCResult_Continue; +} + +SMCResult CsItemInfo::ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value) +{ + switch (m_ParseState) + { + case PSTATE_ALIASES_ALIAS_DEFS: + { + if (!strcmp(key, "itemid")) + { + m_AliasInfo.itemid = atoi(value); + + if (m_AliasInfo.itemid < CSI_NONE || m_AliasInfo.itemid >= CSI_MAX_COUNT) + { + m_AliasInfo.itemid = CSI_NONE; + } + } + else if (!strcmp(key, "classid")) + { + m_AliasInfo.classid = atoi(value); + + if (m_AliasInfo.classid < CS_WEAPONCLASS_NONE || m_AliasInfo.classid >= CS_WEAPONCLASS_MAX_COUNT) + { + m_AliasInfo.classid = CS_WEAPONCLASS_NONE; + } + } + else if (!strcmp(key, "classname")) + { + m_AliasInfo.classname = value; + } + else if (!strcmp(key, "price")) + { + static int equipmentsList[static_cast(Equipments::Count)] = + { + CSI_NONE, CSI_VEST, CSI_VESTHELM, CSI_FLASHBANG, CSI_HEGRENADE, CSI_SMOKEGRENADE, CSI_NVGS, CSI_DEFUSER + }; + + for (int i = 0; i < ARRAY_LENGTH(equipmentsList); ++i) + { + if (m_AliasInfo.itemid == equipmentsList[i]) + { + m_EquipmentsPrice[i] = atoi(value); + } + } + } + break; + } + } + + return SMCResult_Continue; +} + +SMCResult CsItemInfo::ReadSMC_LeavingSection(const SMCStates *states) +{ + switch (m_ParseState) + { + case PSTATE_ALIASES_ALIAS: + { + m_ParseState = PSTATE_ALIASES_TYPE; + break; + } + case PSTATE_ALIASES_ALIAS_DEFS: + { + m_List->replace(m_Alias.chars(), m_AliasInfo); + m_WeaponIdToClass[m_AliasInfo.itemid] = static_cast(m_AliasInfo.classid); + + m_AliasInfo.clear(); + + m_ParseState = PSTATE_ALIASES_ALIAS; + break; + } + } + + return SMCResult_Continue; +} + +void CsItemInfo::ReadSMC_ParseEnd(bool halted, bool failed) +{ + if (!halted && !failed) + { + m_ListsRetrievedFromConfig = true; + } +} + +bool CsItemInfo::GetAliasInfos(const char *alias, AliasInfo *info) +{ + if (GetAliasInfosFromBuy(alias, info) || m_WeaponAliasesList.retrieve(alias, info)) + { + return true; + } + + return false; +} + +bool CsItemInfo::GetAliasInfosFromBuy(const char *alias, AliasInfo *info) +{ + if (m_CommonAliasesList.retrieve(alias, info) || m_BuyAliasesList.retrieve(alias, info)) + { + return true; + } + + return false; +} + +bool CsItemInfo::GetAliasInfosFromName(const char *name, AliasInfo *info) +{ + static const char prefix_weapon[] = "weapon_"; + static const char prefix_item[] = "item_"; + + const char *alias = name; + + if (strstr(name, prefix_weapon) && strcmp(name + sizeof(prefix_weapon) - 1, "shield")) + { + for (size_t id = 0; id < ARRAYSIZE(WeaponNameList); ++id) + { + if (!strcmp(name, WeaponNameList[id])) + { + info->classname = name; + info->itemid = id; + info->classid = WeaponIdToClass(id); + + return true; + } + } + + alias = name + sizeof(prefix_weapon) - 1; + } + else if (strstr(name, prefix_item)) + { + for (auto iter = m_BuyAliasesList.iter(); !iter.empty(); iter.next()) + { + if (iter->value.classname.length() && !iter->value.classname.compare(name)) + { + *info = iter->value; + return true; + } + } + + alias = name + sizeof(prefix_item) - 1; + } + + if (GetAliasInfos(alias, info)) + { + return true; + } + + return false; +} + +CsWeaponClassType CsItemInfo::WeaponIdToClass(int id) +{ + if ((id > CSI_NONE && id <= CSI_P90) || id == CSI_SHIELD || id == CSI_SHIELDGUN) + { + if (id == CSI_SHIELDGUN) + { + id = CSI_SHIELD; + } + + return m_WeaponIdToClass[id]; + } + + return CS_WEAPONCLASS_NONE; +} + +int CsItemInfo::GetItemPrice(int itemId) +{ + if (itemId <= CSI_NONE || itemId > CSI_SHIELD) + { + return 0; + } + + Equipments id = Equipments::None; + + switch (itemId) + { + case CSI_VEST: id = Equipments::Vest; break; + case CSI_VESTHELM: id = Equipments::Vesthelm; break; + case CSI_HEGRENADE: id = Equipments::HEGrenade; break; + case CSI_SMOKEGRENADE: id = Equipments::SmokeGrenade; break; + case CSI_FLASHBANG: id = Equipments::Flashbang; break; + case CSI_NVGS: id = Equipments::Nvg; break; + case CSI_DEFUSER: id = Equipments::Defuser; break; + } + + if (id != Equipments::None) + { + return m_EquipmentsPrice[static_cast(id)]; + } + + return GetWeaponInfo(itemId == CSI_SHIELD ? CSI_SHIELDGUN : itemId)->cost; +} diff --git a/modules/cstrike/cstrike/CstrikeItemsInfos.h b/modules/cstrike/cstrike/CstrikeItemsInfos.h new file mode 100644 index 00000000..2729f19f --- /dev/null +++ b/modules/cstrike/cstrike/CstrikeItemsInfos.h @@ -0,0 +1,106 @@ +// vim: set ts=4 sw=4 tw=99 noet: +// +// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). +// Copyright (C) The AMX Mod X Development Team. +// +// This software is licensed under the GNU General Public License, version 3 or higher. +// Additional exceptions apply. For full license details, see LICENSE.txt or visit: +// https://alliedmods.net/amxmodx-license + +// +// Counter-Strike Module +// + +#ifndef _CSTRIKE_WEAPONS_INFOS_H_ +#define _CSTRIKE_WEAPONS_INFOS_H_ + +#include +#include "CstrikeDatas.h" +#include +#include +#include + +struct AliasInfo +{ + AliasInfo() + { + clear(); + } + + void clear() + { + itemid = CSI_NONE; + classid = CS_WEAPONCLASS_NONE; + } + + int itemid; + int classid; + ke::AString classname; +}; + +enum class Equipments +{ + None, + Vest, + Vesthelm, + Flashbang, + HEGrenade, + SmokeGrenade, + Nvg, + Defuser, + Count +}; + +class CsItemInfo : public ITextListener_SMC +{ + public: + + CsItemInfo(); + virtual ~CsItemInfo(); + + public: + + void Clear(); + bool HasConfigError(); + + public: + + SMCResult ReadSMC_NewSection(const SMCStates *states, const char *name) override; + SMCResult ReadSMC_KeyValue(const SMCStates *states, const char *key, const char *value) override; + SMCResult ReadSMC_LeavingSection(const SMCStates *states) override; + void ReadSMC_ParseEnd(bool halted, bool failed) override; + + public: + + bool GetAliasInfos(const char *alias, AliasInfo *info); + bool GetAliasInfosFromBuy(const char *alias, AliasInfo *info); + bool GetAliasInfosFromName(const char *classname, AliasInfo *info); + + CsWeaponClassType WeaponIdToClass(int id); + + int GetItemPrice(int id); + + private: // Retrieved datas + + typedef StringHashMap AliasMap; + + AliasMap m_CommonAliasesList; + AliasMap m_WeaponAliasesList; + AliasMap m_BuyAliasesList; + + CsWeaponClassType m_WeaponIdToClass[CSI_MAX_COUNT]; + + private: // Config parsing + + int m_ParseState; + AliasMap* m_List; + ke::AString m_Alias; + AliasInfo m_AliasInfo; + bool m_ListsRetrievedFromConfig; + int m_EquipmentsPrice[static_cast(Equipments::Count)]; +}; + +extern char WeaponNameList[MAX_WEAPONS][64]; +extern CsItemInfo ItemsManager; + +#endif // _CSTRIKE_WEAPONS_INFOS_H_ diff --git a/modules/cstrike/cstrike/CstrikeMain.cpp b/modules/cstrike/cstrike/CstrikeMain.cpp index 1313bc36..55a80286 100644 --- a/modules/cstrike/cstrike/CstrikeMain.cpp +++ b/modules/cstrike/cstrike/CstrikeMain.cpp @@ -14,6 +14,8 @@ #include "amxxmodule.h" #include "CstrikeUtils.h" #include "CstrikeHacks.h" +#include "CstrikeItemsInfos.h" +#include "CstrikeUserMessages.h" #include IGameConfig *MainConfig; @@ -38,18 +40,18 @@ void OnAmxxAttach() ConfigManager = MF_GetConfigManager(); - char error[256]; - error[0] = '\0'; + char error[256] = ""; + ConfigManager->AddUserConfigHook("ItemInfos", &ItemsManager); - if (!ConfigManager->LoadGameConfigFile("modules.games", &MainConfig, error, sizeof(error)) && error[0] != '\0') + if (!ConfigManager->LoadGameConfigFile("modules.games", &MainConfig, error, sizeof(error)) && *error) { MF_Log("Could not read module.games gamedata: %s", error); return; } - error[0] = '\0'; + *error = '\0'; - if (!ConfigManager->LoadGameConfigFile("common.games", &CommonConfig, error, sizeof(error)) && error[0] != '\0') + if (!ConfigManager->LoadGameConfigFile("common.games", &CommonConfig, error, sizeof(error)) && *error) { MF_Log("Could not read common.games gamedata: %s", error); return; @@ -60,31 +62,57 @@ void OnAmxxAttach() void OnPluginsLoaded() { + TypeConversion.init(); + ForwardInternalCommand = MF_RegisterForward("CS_InternalCommand", ET_STOP, FP_CELL, FP_STRING, FP_DONE); ForwardOnBuy = MF_RegisterForward("CS_OnBuy" , ET_STOP, FP_CELL, FP_CELL, FP_DONE); ForwardOnBuyAttempt = MF_RegisterForward("CS_OnBuyAttempt" , ET_STOP, FP_CELL, FP_CELL, FP_DONE); - // Checking whether such public forwards are used in plugins. - // Resetting variable to -1 to avoid running unnecessary code in ClientCommand. - if (!UTIL_CheckForPublic("CS_InternalCommand")) { ForwardInternalCommand = -1; } - if (!UTIL_CheckForPublic("CS_OnBuy")) { ForwardOnBuy = -1; } - if (!UTIL_CheckForPublic("CS_OnBuyAttempt")) { ForwardOnBuyAttempt = -1; } + if (!ClientCommandDetour) // All CS_* forwards requires ClientCommand. Unlikely to fail. + { + ToggleDetour_ClientCommands(false); + ToggleDetour_BuyCommands(false); - // And enable/disable detours when necessary. - ToggleDetour_ClientCommands(ForwardInternalCommand != -1 || ForwardOnBuy != -1 || ForwardOnBuyAttempt != -1); - ToggleDetour_BuyCommands(ForwardOnBuy != -1); + return; + } - // Search pev/vtable offset automatically. - TypeConversion.init(); + auto haveBotDetours = UseBotArgs && BotArgs; + auto haveBuyDetours = BuyGunAmmoDetour && GiveNamedItemDetour && AddAccountDetour && CanPlayerBuyDetour && CanBuyThisDetour; + HasInternalCommandForward = haveBotDetours && UTIL_CheckForPublic("CS_InternalCommand"); + HasOnBuyAttemptForward = haveBuyDetours && UTIL_CheckForPublic("CS_OnBuyAttempt"); + HasOnBuyForward = haveBuyDetours && UTIL_CheckForPublic("CS_OnBuy"); + + ToggleDetour_ClientCommands(HasInternalCommandForward || HasOnBuyAttemptForward || HasOnBuyForward); + ToggleDetour_BuyCommands(HasOnBuyForward); +} + +void OnServerActivate(edict_t *pEdictList, int edictCount, int clientMax) +{ + // Used to catch WeaponList message at map change. + EnableMessageHooks(); +} + +void OnServerActivate_Post(edict_t *pEdictList, int edictCount, int clientMax) +{ + DisableMessageHooks(); +} + +void OnPluginsUnloaded() +{ // Used with model natives, enabled on demand. g_pengfuncsTable->pfnSetClientKeyValue = nullptr; g_pFunctionTable->pfnClientUserInfoChanged = nullptr; g_pFunctionTable->pfnStartFrame = nullptr; + + // Force to disable all event hooks at map change. + DisableMessageHooks(true); } void OnAmxxDetach() { + ConfigManager->RemoveUserConfigHook("ItemInfos", &ItemsManager); + ConfigManager->CloseGameConfigFile(MainConfig); ConfigManager->CloseGameConfigFile(CommonConfig); diff --git a/modules/cstrike/cstrike/CstrikeNatives.cpp b/modules/cstrike/cstrike/CstrikeNatives.cpp index e1419a98..143ec92d 100644 --- a/modules/cstrike/cstrike/CstrikeNatives.cpp +++ b/modules/cstrike/cstrike/CstrikeNatives.cpp @@ -16,10 +16,11 @@ #include "CstrikeUtils.h" #include "CstrikeHacks.h" #include "CstrikeUserMessages.h" +#include "CstrikeItemsInfos.h" #include #include -bool NoKifesMode = false; +bool NoKnivesMode = false; // native cs_set_user_money(index, money, flash = 1); static cell AMX_NATIVE_CALL cs_set_user_money(AMX *amx, cell *params) @@ -257,7 +258,7 @@ static cell AMX_NATIVE_CALL cs_set_weapon_silenced(AMX *amx, cell *params) GET_OFFSET("CBasePlayerWeapon", m_flNextPrimaryAttack); char animExt[12]; - float time; + float time = 0.0f; switch (weaponType) { @@ -405,7 +406,7 @@ static cell AMX_NATIVE_CALL cs_set_user_armor(AMX *amx, cell *params) pPlayer->v.armorvalue = armor; set_pdata(pPlayer, m_iKevlar, type); - + if (type == CS_ARMOR_KEVLAR || type == CS_ARMOR_ASSAULTSUIT) { MESSAGE_BEGIN(MSG_ONE, MessageIdArmorType, nullptr, pPlayer); @@ -488,7 +489,7 @@ static cell AMX_NATIVE_CALL cs_set_user_vip(AMX *amx, cell *params) WRITE_BYTE(index); WRITE_BYTE(scoreattrib); MESSAGE_END(); - } + } return 1; } @@ -500,7 +501,7 @@ static cell AMX_NATIVE_CALL cs_get_user_team(AMX *amx, cell *params) GET_OFFSET("CBasePlayer", m_iTeam); int index = params[1]; - + CHECK_PLAYER(index); edict_t *pPlayer = MF_GetPlayerEdict(index); @@ -537,7 +538,7 @@ static cell AMX_NATIVE_CALL cs_set_user_team(AMX *amx, cell *params) { Players[index].ResetModel(pPlayer); } - + bool sendTeamInfo = true; if (*params / sizeof(cell) >= 4) @@ -578,7 +579,7 @@ static cell AMX_NATIVE_CALL cs_get_user_inside_buyzone(AMX *amx, cell *params) CHECK_PLAYER(index); edict_t *pPlayer = MF_GetPlayerEdict(index); - + if (get_pdata(pPlayer, m_signals).GetState() & SIGNAL_BUY) { return 1; @@ -634,7 +635,7 @@ static cell AMX_NATIVE_CALL cs_set_user_plant(AMX *amx, cell *params) if (plant) { - if (icon) + if (icon) { MESSAGE_BEGIN(MSG_ONE, MessageIdStatusIcon, nullptr, pPlayer); WRITE_BYTE(1); @@ -645,7 +646,7 @@ static cell AMX_NATIVE_CALL cs_set_user_plant(AMX *amx, cell *params) MESSAGE_END(); } } - else + else { MESSAGE_BEGIN(MSG_ONE, MessageIdStatusIcon, nullptr, pPlayer); WRITE_BYTE(0); @@ -684,14 +685,14 @@ static cell AMX_NATIVE_CALL cs_set_user_defusekit(AMX *amx, cell *params) CHECK_PLAYER(index); edict_t *pPlayer = MF_GetPlayerEdict(index); - + set_pdata(pPlayer, m_bHasDefuser, kit); pPlayer->v.body = kit ? 1 : 0; if (kit) { int colour[3] = {DEFUSER_COLOUR_R, DEFUSER_COLOUR_G, DEFUSER_COLOUR_B}; - + for (int i = 0; i < 3; i++) { if (params[i + 3] != -1) @@ -707,7 +708,7 @@ static cell AMX_NATIVE_CALL cs_set_user_defusekit(AMX *amx, cell *params) int length; icon = MF_GetAmxString(amx, params[6], 1, &length); } - + MESSAGE_BEGIN(MSG_ONE, MessageIdStatusIcon, nullptr, pPlayer); WRITE_BYTE(params[7] == 1 ? 2 : 1); WRITE_STRING(icon); @@ -716,7 +717,7 @@ static cell AMX_NATIVE_CALL cs_set_user_defusekit(AMX *amx, cell *params) WRITE_BYTE(colour[2]); MESSAGE_END(); } - else + else { MESSAGE_BEGIN(MSG_ONE, MessageIdStatusIcon, nullptr, pPlayer); WRITE_BYTE(0); @@ -762,7 +763,7 @@ static cell AMX_NATIVE_CALL cs_get_user_backpackammo(AMX *amx, cell *params) pItem = get_pdata(pItem, m_pNext); } } - + return 0; } @@ -803,7 +804,7 @@ static cell AMX_NATIVE_CALL cs_set_user_backpackammo(AMX *amx, cell *params) pItem = get_pdata(pItem, m_pNext); } } - + return 0; } @@ -868,7 +869,7 @@ static cell AMX_NATIVE_CALL cs_set_user_model(AMX *amx, cell *params) CHECK_PLAYER(index); edict_t *pPlayer = MF_GetPlayerEdict(index); - if (model == -1) + if (model == -1) { MF_LogError(amx, AMX_ERR_NATIVE, "Invalid model %d", params[2]); return 0; @@ -1072,7 +1073,7 @@ static cell AMX_NATIVE_CALL cs_get_user_hasprimary(AMX *amx, cell *params) // native cs_get_no_knives(); static cell AMX_NATIVE_CALL cs_get_no_knives(AMX *amx, cell *params) { - return NoKifesMode ? 1 : 0; + return NoKnivesMode ? 1 : 0; } // native cs_set_no_knives(noknives = 0); @@ -1080,13 +1081,13 @@ static cell AMX_NATIVE_CALL cs_set_no_knives(AMX *amx, cell *params) { if (!GiveDefaultItemsDetour) { - MF_LogError(amx, AMX_ERR_NATIVE, "Native cs_set_no_knives() is disabled"); + MF_LogError(amx, AMX_ERR_NATIVE, "Native cs_set_no_knives() is disabled. Check your amxx logs."); return 0; } - NoKifesMode = params[1] != 0; + NoKnivesMode = params[1] != 0; - if (NoKifesMode) + if (NoKnivesMode) { GiveDefaultItemsDetour->EnableDetour(); } @@ -1100,7 +1101,7 @@ static cell AMX_NATIVE_CALL cs_set_no_knives(AMX *amx, cell *params) // native cs_get_user_tked(index); static cell AMX_NATIVE_CALL cs_get_user_tked(AMX *amx, cell *params) -{ +{ GET_OFFSET("CBasePlayer", m_bJustKilledTeammate); int index = params[1]; @@ -1118,7 +1119,7 @@ static cell AMX_NATIVE_CALL cs_get_user_tked(AMX *amx, cell *params) // native cs_set_user_tked(index, tk = 1, subtract = 1); static cell AMX_NATIVE_CALL cs_set_user_tked(AMX *amx, cell *params) -{ +{ GET_OFFSET("CBasePlayer", m_bJustKilledTeammate); GET_OFFSET("CBasePlayer", m_iTeam); GET_OFFSET("CBasePlayer", m_iDeaths); @@ -1133,7 +1134,7 @@ static cell AMX_NATIVE_CALL cs_set_user_tked(AMX *amx, cell *params) set_pdata(pPlayer, m_bJustKilledTeammate, tk != 0); if (subtract > 0) - { + { pPlayer->v.frags -= subtract; MESSAGE_BEGIN(MSG_ALL, MessageIdScoreInfo); @@ -1142,15 +1143,15 @@ static cell AMX_NATIVE_CALL cs_set_user_tked(AMX *amx, cell *params) WRITE_SHORT(get_pdata(pPlayer, m_iDeaths)); WRITE_SHORT(0); WRITE_SHORT(get_pdata(pPlayer, m_iTeam)); - MESSAGE_END(); - } + MESSAGE_END(); + } - return 1; + return 1; } // native cs_get_user_driving(index); static cell AMX_NATIVE_CALL cs_get_user_driving(AMX *amx, cell *params) -{ +{ GET_OFFSET("CBasePlayer", m_iTrain); int index = params[1]; @@ -1162,8 +1163,8 @@ static cell AMX_NATIVE_CALL cs_get_user_driving(AMX *amx, cell *params) } // native cs_get_user_stationary(index); -static cell AMX_NATIVE_CALL cs_get_user_stationary(AMX *amx, cell *params) -{ +static cell AMX_NATIVE_CALL cs_get_user_stationary(AMX *amx, cell *params) +{ GET_OFFSET("CBasePlayer", m_iClientHideHUD); int index = params[1]; @@ -1183,13 +1184,13 @@ static cell AMX_NATIVE_CALL cs_get_user_shield(AMX *amx, cell *params) CHECK_PLAYER(index); edict_t *pPlayer = MF_GetPlayerEdict(index); - + if (get_pdata(pPlayer, m_bOwnsShield)) { return 1; } - return 0; + return 0; } // native cs_user_spawn(player); @@ -1203,7 +1204,7 @@ static cell AMX_NATIVE_CALL cs_user_spawn(AMX *amx, cell *params) pPlayer->v.deadflag = DEAD_RESPAWNABLE; MDLL_Think(pPlayer); - if (MF_IsPlayerBot(index) && pPlayer->v.deadflag == DEAD_RESPAWNABLE) + if (MF_IsPlayerBot(index) && pPlayer->v.deadflag == DEAD_RESPAWNABLE) { MDLL_Spawn(pPlayer); } @@ -1221,7 +1222,7 @@ static cell AMX_NATIVE_CALL cs_get_armoury_type(AMX *amx, cell *params) CHECK_NONPLAYER(index); edict_t *pArmoury = INDEXENT(index); - if (strcmp(STRING(pArmoury->v.classname), "armoury_entity")) + if (strcmp(STRING(pArmoury->v.classname), "armoury_entity")) { MF_LogError(amx, AMX_ERR_NATIVE, "Not an armoury_entity! (%d)", index); return 0; @@ -1230,7 +1231,7 @@ static cell AMX_NATIVE_CALL cs_get_armoury_type(AMX *amx, cell *params) int weapontype = get_pdata(pArmoury, m_iItem);; int weapontype_out = 0; - switch (weapontype) + switch (weapontype) { case CSA_MP5NAVY: weapontype_out = CSW_MP5NAVY; break; case CSA_TMP: weapontype_out = CSW_TMP; break; @@ -1265,7 +1266,7 @@ static cell AMX_NATIVE_CALL cs_get_armoury_type(AMX *amx, cell *params) *MF_GetAmxAddr(amx, params[2]) = get_pdata(pArmoury, m_iCount); } - return weapontype_out; + return weapontype_out; } // native cs_set_armoury_type(index, type, count = -1); @@ -1356,8 +1357,14 @@ static cell AMX_NATIVE_CALL cs_set_user_zoom(AMX *amx, cell *params) int type = params[2]; int mode = params[3]; int weapon = *static_cast(MF_PlayerPropAddr(index, Player_CurrentWeapon)); - - Players[index].ResetZoom(); + + CPlayer& player = Players[index]; + + if (player.GetZoom()) + { + DisableMessageHooks(); + player.ResetZoom(); + } if (type == CS_RESET_ZOOM) { @@ -1405,7 +1412,7 @@ static cell AMX_NATIVE_CALL cs_set_user_zoom(AMX *amx, cell *params) if (!mode) { - Players[index].SetZoom(value); + player.SetZoom(value); EnableMessageHooks(); } @@ -1481,7 +1488,7 @@ static cell AMX_NATIVE_CALL cs_get_user_lastactivity(AMX *amx, cell *params) CHECK_PLAYER(index); edict_t *pPlayer = MF_GetPlayerEdict(index); - + return amx_ftoc(get_pdata(pPlayer, m_fLastMovement)); } @@ -1494,7 +1501,7 @@ static cell AMX_NATIVE_CALL cs_set_user_lastactivity(AMX *amx, cell *params) CHECK_PLAYER(index); edict_t *pPlayer = MF_GetPlayerEdict(index); - + set_pdata(pPlayer, m_fLastMovement, amx_ctof(params[2])); return 1; @@ -1535,12 +1542,12 @@ static cell AMX_NATIVE_CALL cs_get_hostage_lastuse(AMX *amx, cell *params) GET_OFFSET("CHostage", m_improv); int index = params[1]; - + CHECK_NONPLAYER(index); edict_t *pHostage = INDEXENT(index); CHECK_HOSTAGE(pHostage); - + void *pImprov = get_pdata(pHostage, m_improv); if (pImprov) // Specific to CZ @@ -1566,7 +1573,7 @@ static cell AMX_NATIVE_CALL cs_set_hostage_lastuse(AMX *amx, cell *params) edict_t *pHostage = INDEXENT(index); CHECK_HOSTAGE(pHostage); - + void *pImprov = get_pdata(pHostage, m_improv); if (pImprov) // Specific to CZ @@ -1593,7 +1600,7 @@ static cell AMX_NATIVE_CALL cs_get_hostage_nextuse(AMX* amx, cell* params) edict_t *pHostage = INDEXENT(index); CHECK_HOSTAGE(pHostage); - + return amx_ftoc(get_pdata(pHostage, m_flNextChange)); } @@ -1699,7 +1706,7 @@ static cell AMX_NATIVE_CALL cs_create_entity(AMX* amx, cell* params) { if (CS_CreateNamedEntity <= 0) { - MF_LogError(amx, AMX_ERR_NATIVE, "Native cs_create_entity() is disabled"); + MF_LogError(amx, AMX_ERR_NATIVE, "Native cs_create_entity() is disabled. Check your amxx logs."); return 0; } @@ -1721,7 +1728,7 @@ static cell AMX_NATIVE_CALL cs_find_ent_by_class(AMX* amx, cell* params) { if (CS_UTIL_FindEntityByString <= 0) { - MF_LogError(amx, AMX_ERR_NATIVE, "Native cs_find_ent_by_class() is disabled"); + MF_LogError(amx, AMX_ERR_NATIVE, "Native cs_find_ent_by_class() is disabled. Check your amxx logs."); return 0; } @@ -1739,7 +1746,165 @@ static cell AMX_NATIVE_CALL cs_find_ent_by_class(AMX* amx, cell* params) return 0; } -AMX_NATIVE_INFO CstrikeNatives[] = +// cs_find_ent_by_owner(start_index, const classname[], owner) +static cell AMX_NATIVE_CALL cs_find_ent_by_owner(AMX* amx, cell* params) +{ + if (CS_UTIL_FindEntityByString <= 0) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Native cs_find_ent_by_owner() is disabled. Check your amxx logs."); + return 0; + } + + int owner = params[3]; + CHECK_ENTITY_SIMPLE(owner); + + int length; + void* pEntity = TypeConversion.id_to_cbase(params[1]); + const char* value = MF_GetAmxString(amx, params[2], 0, &length); + + edict_t *pOwner = TypeConversion.id_to_edict(owner); + + while ((pEntity = CS_UTIL_FindEntityByString(pEntity, "classname", value))) + { + edict_t *pev = TypeConversion.cbase_to_edict(pEntity); + + if (!FNullEnt(pev) && pev->v.owner == pOwner) + { + int index = TypeConversion.edict_to_id(pev); + + if (index != -1) + { + return index; + } + } + } + + return 0; +} + +// native any:cs_get_item_id(const name[], &CsWeaponClassType:classid = CS_WEAPONCLASS_NONE); +static cell AMX_NATIVE_CALL cs_get_item_id(AMX* amx, cell* params) +{ + if (ItemsManager.HasConfigError()) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Native cs_get_item_id() is disabled because of corrupted or missing gamedata"); + return 0; + } + + int length; + char *name = MF_GetAmxString(amx, params[1], 0, &length); + cell *classid = MF_GetAmxAddr(amx, params[2]); + + if (length) + { + AliasInfo info; + + if (ItemsManager.GetAliasInfosFromName(name, &info)) + { + *classid = info.classid; + return info.itemid; + } + } + + return CSI_NONE; +} + +// native bool:cs_get_translated_item_alias(const alias[], itemname[], maxlength); +static cell AMX_NATIVE_CALL cs_get_translated_item_alias(AMX* amx, cell* params) +{ + if (ItemsManager.HasConfigError()) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Native cs_get_translated_item_alias() is disabled because of corrupted or missing gamedata"); + return 0; + } + + int length; + const char *alias = MF_GetAmxString(amx, params[1], 0, &length); + const char *name = alias; + AliasInfo info; + + if (length && ItemsManager.GetAliasInfos(alias, &info) && info.itemid != CSI_NONE) + { + switch (info.itemid) + { + case CSI_VEST: + case CSI_VESTHELM: + case CSI_DEFUSER: + case CSI_SHIELD: + { + // Special item_* defined in gamdata file as game + // doesn't give us really a way to know about their classname + // and I don't want to hard code them in module. + name = info.classname.chars(); + break; + } + default: + { + // weapon_* retrieved from WeaponList messages at map change. + name = WeaponNameList[info.itemid]; + break; + } + } + } + + MF_SetAmxString(amx, params[2], alias, params[3]); + + return info.itemid != CSI_NONE; +} + +// native cs_get_weapon_info(weapon_id, CsWeaponInfo:type); +static cell AMX_NATIVE_CALL cs_get_weapon_info(AMX* amx, cell* params) +{ + if (GetWeaponInfo <= 0) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Native cs_get_weapon_info() is disabled. Check your amxx logs."); + return 0; + } + + int weapon_id = params[1]; + + if (weapon_id <= CSW_NONE || weapon_id == CSW_C4 || weapon_id == CSW_KNIFE || weapon_id > CSW_LAST_WEAPON) + { + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid weapon id: %d", weapon_id); + return 0; + } + + int info_type = params[2]; + + switch (info_type) + { + case CS_WEAPONINFO_COST: + { + return GetWeaponInfo(weapon_id)->cost; + } + case CS_WEAPONINFO_CLIP_COST: + { + return GetWeaponInfo(weapon_id)->clipCost; + } + case CS_WEAPONINFO_BUY_CLIP_SIZE: + { + return GetWeaponInfo(weapon_id)->buyClipSize; + } + case CS_WEAPONINFO_GUN_CLIP_SIZE: + { + return GetWeaponInfo(weapon_id)->gunClipSize; + } + case CS_WEAPONINFO_MAX_ROUNDS: + { + return GetWeaponInfo(weapon_id)->maxRounds; + } + case CS_WEAPONINFO_AMMO_TYPE: + { + return GetWeaponInfo(weapon_id)->ammoType; + } + } + + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid info type: %d", info_type); + return 0; +} + + +AMX_NATIVE_INFO CstrikeNatives[] = { {"cs_set_user_money", cs_set_user_money}, {"cs_get_user_money", cs_get_user_money}, @@ -1801,8 +1966,11 @@ AMX_NATIVE_INFO CstrikeNatives[] = {"cs_set_c4_explode_time", cs_set_c4_explode_time}, {"cs_get_c4_defusing", cs_get_c4_defusing}, {"cs_set_c4_defusing", cs_set_c4_defusing}, - {"cs_create_entity", cs_create_entity }, - {"cs_find_ent_by_class", cs_find_ent_by_class}, - + {"cs_create_entity", cs_create_entity }, + {"cs_find_ent_by_class", cs_find_ent_by_class}, + {"cs_find_ent_by_owner", cs_find_ent_by_owner}, + {"cs_get_item_id", cs_get_item_id}, + {"cs_get_translated_item_alias",cs_get_translated_item_alias}, + {"cs_get_weapon_info", cs_get_weapon_info}, {nullptr, nullptr} }; diff --git a/modules/cstrike/cstrike/CstrikeUserMessages.cpp b/modules/cstrike/cstrike/CstrikeUserMessages.cpp index 00c368f6..4d646765 100644 --- a/modules/cstrike/cstrike/CstrikeUserMessages.cpp +++ b/modules/cstrike/cstrike/CstrikeUserMessages.cpp @@ -11,16 +11,22 @@ // Counter-Strike Module // -#include #include "CstrikeUserMessages.h" #include "CstrikeUtils.h" #include "CstrikeHacks.h" #include "CstrikePlayer.h" +#include "CstrikeItemsInfos.h" +#include bool ShouldBlock; bool ShouldBlockHLTV; +bool ShouldDisableHooks; +bool RetrieveWeaponName; +ke::AString CurrentWeaponName; +int ArgPosition; int MessageIdArmorType; +int MessageIdItemStatus; int MessageIdHLTV; int MessageIdMoney; int MessageIdResetHUD; @@ -30,6 +36,10 @@ int MessageIdSetFOV; int MessageIdStatusIcon; int MessageIdTeamInfo; int MessageIdTextMsg; +int MessageIdWeaponList; + +extern bool OnMessageStatusIcon(edict_t *pPlayer); +extern bool OnMessageItemStatus(edict_t *pPlayer); struct UserMsg { @@ -40,6 +50,7 @@ struct UserMsg UserMsg MessagesList[] = { { "ArmorType" , &MessageIdArmorType }, + { "ItemStatus" , &MessageIdItemStatus }, { "HLTV" , &MessageIdHLTV }, { "Money" , &MessageIdMoney }, { "ResetHUD" , &MessageIdResetHUD }, @@ -49,6 +60,7 @@ UserMsg MessagesList[] = { "StatusIcon" , &MessageIdStatusIcon }, { "TeamInfo" , &MessageIdTeamInfo }, { "TextMsg" , &MessageIdTextMsg }, + { "WeaponList" , &MessageIdWeaponList }, { nullptr , nullptr } }; @@ -98,7 +110,10 @@ void OnMessageBegin(int msg_dest, int msg_type, const float *pOrigin, edict_t *p int index = ENTINDEX(pEntity); if (Players[index].GetZoom()) + { Players[index].ResetZoom(); + DisableMessageHooks(); + } } break; } @@ -113,6 +128,13 @@ void OnMessageBegin(int msg_dest, int msg_type, const float *pOrigin, edict_t *p } break; } + case MSG_INIT: + { + if (msg_type == MessageIdWeaponList) + { + RetrieveWeaponName = true; + } + } } if (ShouldBlockHLTV) @@ -123,13 +145,33 @@ void OnMessageBegin(int msg_dest, int msg_type, const float *pOrigin, edict_t *p RETURN_META(MRES_IGNORED); } -void OnWriteByte(int iValue) +void OnWriteByte(int value) { - if (ShouldBlock) + if (ShouldBlock) { RETURN_META(MRES_SUPERCEDE); } + if (RetrieveWeaponName && ++ArgPosition == 7 && value >= 0 && value < MAX_WEAPONS) + { + strncopy(WeaponNameList[value], CurrentWeaponName.chars(), sizeof(WeaponNameList[value])); + } + + RETURN_META(MRES_IGNORED); +} + +void OnWriteString(const char *value) +{ + if (ShouldBlock) + { + RETURN_META(MRES_SUPERCEDE); + } + + if (RetrieveWeaponName) + { + CurrentWeaponName = value; + } + RETURN_META(MRES_IGNORED); } @@ -138,18 +180,52 @@ void OnMessageEnd(void) if (ShouldBlock) { ShouldBlock = false; + + if (ShouldDisableHooks) + { + ShouldDisableHooks = false; + DisableMessageHooks(); + } + RETURN_META(MRES_SUPERCEDE); } + if (RetrieveWeaponName) + { + RetrieveWeaponName = false; + ArgPosition = 0; + } + RETURN_META(MRES_IGNORED); } +size_t RefCount; + void EnableMessageHooks() { + ++RefCount; + if (!g_pengfuncsTable->pfnMessageBegin) { g_pengfuncsTable->pfnMessageBegin = OnMessageBegin; g_pengfuncsTable->pfnWriteByte = OnWriteByte; + g_pengfuncsTable->pfnWriteString = OnWriteString; g_pengfuncsTable->pfnMessageEnd = OnMessageEnd; } } + +void DisableMessageHooks(bool force) +{ + if (force) + { + RefCount = 1; + } + + if (--RefCount == 0) + { + g_pengfuncsTable->pfnMessageBegin = nullptr; + g_pengfuncsTable->pfnWriteByte = nullptr; + g_pengfuncsTable->pfnWriteString = nullptr; + g_pengfuncsTable->pfnMessageEnd = nullptr; + } +} diff --git a/modules/cstrike/cstrike/CstrikeUserMessages.h b/modules/cstrike/cstrike/CstrikeUserMessages.h index 7c1a66ac..eae0ed15 100644 --- a/modules/cstrike/cstrike/CstrikeUserMessages.h +++ b/modules/cstrike/cstrike/CstrikeUserMessages.h @@ -26,5 +26,6 @@ extern int MessageIdTeamInfo; extern int MessageIdTextMsg; void EnableMessageHooks(); +void DisableMessageHooks(bool force = false); #endif // CSTRIKE_USER_MESSAGES_H diff --git a/modules/cstrike/cstrike/CstrikeUtils.h b/modules/cstrike/cstrike/CstrikeUtils.h index ac94a113..7f6c635f 100644 --- a/modules/cstrike/cstrike/CstrikeUtils.h +++ b/modules/cstrike/cstrike/CstrikeUtils.h @@ -27,6 +27,17 @@ bool UTIL_CheckForPublic(const char *publicname); #define GETCLIENTKEYVALUE (*g_engfuncs.pfnInfoKeyValue) #define CREATENAMEDENTITY (*g_engfuncs.pfnCreateNamedEntity) +#define CHECK_ENTITY_SIMPLE(x) \ + if (x < 0 || x > gpGlobals->maxEntities) { \ + MF_LogError(amx, AMX_ERR_NATIVE, "Entity out of range (%d)", x); \ + return 0; \ + } else { \ + if (x != 0 && FNullEnt(INDEXENT(x))) { \ + MF_LogError(amx, AMX_ERR_NATIVE, "Invalid entity %d", x); \ + return 0; \ + } \ + } + #define CHECK_ENTITY(x) \ if (x < 0 || x > gpGlobals->maxEntities) { \ MF_LogError(amx, AMX_ERR_NATIVE, "Entity out of range (%d)", x); \ @@ -99,6 +110,7 @@ bool UTIL_CheckForPublic(const char *publicname); member = type.fieldOffset; \ } + class CUnifiedSignals { public: @@ -109,17 +121,17 @@ class CUnifiedSignals m_flSignal = 0; } - void Signal(int flags) + void Signal(int flags) { m_flSignal |= flags; } - int GetSignal(void) + int GetSignal(void) { return m_flSignal; } - int GetState(void) + int GetState(void) { return m_flState; } diff --git a/modules/cstrike/cstrike/moduleconfig.h b/modules/cstrike/cstrike/moduleconfig.h index 79b9eccd..adeeea39 100644 --- a/modules/cstrike/cstrike/moduleconfig.h +++ b/modules/cstrike/cstrike/moduleconfig.h @@ -80,7 +80,7 @@ //#define FN_AMXX_PLUGINSUNLOADING OnPluginsUnloading /** All plugins are now unloaded */ -//#define FN_AMXX_PLUGINSUNLOADED OnPluginsUnloaded +#define FN_AMXX_PLUGINSUNLOADED OnPluginsUnloaded /**** METAMOD ****/ @@ -123,7 +123,7 @@ // #define FN_ClientPutInServer ClientPutInServer /* pfnClientPutInServer() (wd) Client is entering the game */ // #define FN_ClientCommand ClientCommand /* pfnClientCommand() (wd) Player has sent a command (typed or from a bind) */ // #define FN_ClientUserInfoChanged ClientUserInfoChanged /* pfnClientUserInfoChanged() (wd) Client has updated their setinfo structure */ -// #define FN_ServerActivate ServerActivate /* pfnServerActivate() (wd) Server is starting a new map */ +#define FN_ServerActivate OnServerActivate /* pfnServerActivate() (wd) Server is starting a new map */ // #define FN_ServerDeactivate ServerDeactivate /* pfnServerDeactivate() (wd) Server is leaving the map (shutdown or changelevel); SDK2 */ // #define FN_PlayerPreThink PlayerPreThink /* pfnPlayerPreThink() */ // #define FN_PlayerPostThink PlayerPostThink /* pfnPlayerPostThink() */ @@ -175,7 +175,7 @@ // #define FN_ClientPutInServer_Post ClientPutInServer_Post // #define FN_ClientCommand_Post ClientCommand_Post // #define FN_ClientUserInfoChanged_Post ClientUserInfoChanged_Post -// #define FN_ServerActivate_Post ServerActivate_Post +#define FN_ServerActivate_Post OnServerActivate_Post // #define FN_ServerDeactivate_Post ServerDeactivate_Post // #define FN_PlayerPreThink_Post PlayerPreThink_Post // #define FN_PlayerPostThink_Post PlayerPostThink_Post diff --git a/modules/cstrike/cstrike/msvc12/cstrike.vcxproj b/modules/cstrike/cstrike/msvc12/cstrike.vcxproj index e50b7f14..e15df551 100644 --- a/modules/cstrike/cstrike/msvc12/cstrike.vcxproj +++ b/modules/cstrike/cstrike/msvc12/cstrike.vcxproj @@ -151,12 +151,14 @@ + + @@ -167,6 +169,7 @@ + diff --git a/modules/cstrike/cstrike/msvc12/cstrike.vcxproj.filters b/modules/cstrike/cstrike/msvc12/cstrike.vcxproj.filters index 33e30dbe..89db5505 100644 --- a/modules/cstrike/cstrike/msvc12/cstrike.vcxproj.filters +++ b/modules/cstrike/cstrike/msvc12/cstrike.vcxproj.filters @@ -59,6 +59,9 @@ Source Files + + Source Files + @@ -94,10 +97,16 @@ Header Files + + Header Files + Pawn Includes + + Pawn Includes + \ No newline at end of file diff --git a/plugins/include/amxconst.inc b/plugins/include/amxconst.inc index 2512f09f..96dfcd21 100755 --- a/plugins/include/amxconst.inc +++ b/plugins/include/amxconst.inc @@ -118,43 +118,6 @@ public stock const Float:NULL_VECTOR[3]; #define PLUGIN_HANDLED 1 /* stop other plugins */ #define PLUGIN_HANDLED_MAIN 2 /* to use in client_command(), continue all plugins but stop the command */ -/** - * IDs of weapons in CS - */ -#define CSW_P228 1 -#define CSW_SCOUT 3 -#define CSW_HEGRENADE 4 -#define CSW_XM1014 5 -#define CSW_C4 6 -#define CSW_MAC10 7 -#define CSW_AUG 8 -#define CSW_SMOKEGRENADE 9 -#define CSW_ELITE 10 -#define CSW_FIVESEVEN 11 -#define CSW_UMP45 12 -#define CSW_SG550 13 -#define CSW_GALI 14 -#define CSW_GALIL 14 -#define CSW_FAMAS 15 -#define CSW_USP 16 -#define CSW_GLOCK18 17 -#define CSW_AWP 18 -#define CSW_MP5NAVY 19 -#define CSW_M249 20 -#define CSW_M3 21 -#define CSW_M4A1 22 -#define CSW_TMP 23 -#define CSW_G3SG1 24 -#define CSW_FLASHBANG 25 -#define CSW_DEAGLE 26 -#define CSW_SG552 27 -#define CSW_AK47 28 -#define CSW_KNIFE 29 -#define CSW_P90 30 -#define CSW_VEST 31 // Custom -#define CSW_VESTHELM 32 // Custom -#define CSW_SHIELDGUN 99 - /** * HI weapon constants */ @@ -187,6 +150,7 @@ public stock const Float:NULL_VECTOR[3]; #define HIT_RIGHTARM 5 #define HIT_LEFTLEG 6 #define HIT_RIGHTLEG 7 +#define HIT_SHIELD 8 // CS only /** * @section emit_sound() constants @@ -485,3 +449,5 @@ enum HashType Hash_Keccak_384, // Provides Keccak 384 bit hashing Hash_Keccak_512 // Provides Keccak 512 bit hashing }; + +#include // To keep backward compatibility \ No newline at end of file diff --git a/plugins/include/cstrike.inc b/plugins/include/cstrike.inc index 041d35d9..70e3ade5 100755 --- a/plugins/include/cstrike.inc +++ b/plugins/include/cstrike.inc @@ -21,121 +21,7 @@ #pragma loadlib cstrike #endif -/** - * @section Team and team model constants, used by cs_[get|set]_user_team(). - */ - -/** - * Internal Counter-Strike model id constants. - * - * @note Model ids starting with CZ_ are only valid in Condition Zero. - */ -enum CsInternalModel -{ - CS_NORESET = -1, - CS_DONTCHANGE = 0, - CS_CT_URBAN = 1, - CS_T_TERROR = 2, - CS_T_LEET = 3, - CS_T_ARCTIC = 4, - CS_CT_GSG9 = 5, - CS_CT_GIGN = 6, - CS_CT_SAS = 7, - CS_T_GUERILLA = 8, - CS_CT_VIP = 9, - CZ_T_MILITIA = 10, - CZ_CT_SPETSNAZ = 11, -}; - -/** - * Counter-Strike team id constants. - */ -enum CsTeams -{ - CS_TEAM_UNASSIGNED = 0, - CS_TEAM_T = 1, - CS_TEAM_CT = 2, - CS_TEAM_SPECTATOR = 3, -}; - -/** - * @endsection - */ - -/** - * Counter-Strike armor types for use with cs_[get|set]_user_armor(). - */ -enum CsArmorType -{ - CS_ARMOR_NONE = 0, // no armor - CS_ARMOR_KEVLAR = 1, // body vest only - CS_ARMOR_VESTHELM = 2, // vest and helmet -}; - -/** - * Map zone flags returned by cs_get_user_mapzones(). - */ -#define CS_MAPZONE_BUY (1<<0) // Buyzone -#define CS_MAPZONE_BOMBTARGET (1<<1) // Bomb target zone -#define CS_MAPZONE_HOSTAGE_RESCUE (1<<2) // Hostage rescue zone -#define CS_MAPZONE_ESCAPE (1<<3) // Terrorist escape zone -#define CS_MAPZONE_VIP_SAFETY (1<<4) // VIP escape zone - -/** - * Constants used for cs_[get|set]_user_zoom(). - */ -enum -{ - CS_RESET_ZOOM = 0, // Reset any zoom blocking (mode has no effect) - CS_SET_NO_ZOOM, // Disable any sort of zoom - CS_SET_FIRST_ZOOM, // Set first zoom level (AWP style) - CS_SET_SECOND_ZOOM, // Set second zoom level (AWP style) - CS_SET_AUGSG552_ZOOM, // Set AUG/SG552 zoom style -}; - -/** - * Constants used for the CS_OnBuy() and CS_OnBuyAttempt() forwards. - * - * @note While these mostly overlap with the CSW_* constants the CSI_* constants - * contain custom AMXX values that do not correspond to any real value in - * the game. The CSI_* constants should therefore be used for consistency. - */ -#define CSI_P228 CSW_P228 -#define CSI_SCOUT CSW_SCOUT -#define CSI_HEGRENADE CSW_HEGRENADE -#define CSI_XM1014 CSW_XM1014 -#define CSI_C4 CSW_C4 -#define CSI_MAC10 CSW_MAC10 -#define CSI_AUG CSW_AUG -#define CSI_SMOKEGRENADE CSW_SMOKEGRENADE -#define CSI_ELITE CSW_ELITE -#define CSI_FIVESEVEN CSW_FIVESEVEN -#define CSI_UMP45 CSW_UMP45 -#define CSI_SG550 CSW_SG550 -#define CSI_GALIL CSW_GALIL -#define CSI_FAMAS CSW_FAMAS -#define CSI_USP CSW_USP -#define CSI_GLOCK18 CSW_GLOCK18 -#define CSI_AWP CSW_AWP -#define CSI_MP5NAVY CSW_MP5NAVY -#define CSI_M249 CSW_M249 -#define CSI_M3 CSW_M3 -#define CSI_M4A1 CSW_M4A1 -#define CSI_TMP CSW_TMP -#define CSI_G3SG1 CSW_G3SG1 -#define CSI_FLASHBANG CSW_FLASHBANG -#define CSI_DEAGLE CSW_DEAGLE -#define CSI_SG552 CSW_SG552 -#define CSI_AK47 CSW_AK47 -#define CSI_P90 CSW_P90 -#define CSI_SHIELDGUN CSW_SHIELDGUN // The real CS value, use CSI_SHELD instead. -#define CSI_VEST CSW_VEST // Custom -#define CSI_VESTHELM CSW_VESTHELM // Custom -#define CSI_DEFUSER 33 // Custom -#define CSI_NVGS 34 // Custom -#define CSI_PRIAMMO 36 // Custom -#define CSI_SECAMMO 37 // Custom -#define CSI_SHIELD 38 // Custom - The value passed by the forward, more convenient for plugins. +#include /** * Returns client's deaths. @@ -1139,6 +1025,157 @@ native cs_create_entity(const classname[]); */ native cs_find_ent_by_class(start_index, const classname[]); +/** + * Finds an entity in the world using Counter-Strike's custom FindEntityByString + * wrapper, matching by owner. + * + * @note Unlike other mods CS keeps track of entities using a custom hashtable. + * This function utilizes the hasthable and allows for considerably faster + * classname lookup compared to the default FindEntityByString (used by + * find_ent_by_owner() for example). + * @note This exclusively considers entities in the hashtable, created by the + * game itself or using cs_create_entity(). + * + * @param start_index Entity index to start searching from. -1 to start from + * the first entity + * @param classname Classname to search for + * @param owner Entity index to search for entity's owner + * + * @return Entity index > 0 if found, 0 otherwise + */ +native cs_find_ent_by_owner(start_index, const classname[], owner); + +/** + * Returns the item id associated with an item name and its aliases. + * + * @note The item name is case sensitive an can be with or without + * weapon_ and item_ prefixes. This can be a command alias as well. + * Values examples: ak47, weapon_ak47, kevlar, item_kevlar, vest, bullpup, ... + * + * @param name Alias or classname + * @param classid If item is a weapon, variable to store the associated + * weapon class id in (CS_WEAPONCLASS_* constants) + * + * @return Item id (CSI_* constants) + */ +native any:cs_get_item_id(const name[], &CsWeaponClassType:classid = CS_WEAPONCLASS_NONE); + +/** + * Returns an item name associated with a command alias. + * + * @note The alias is case sensitive. + * @note If not an alias to a weapon, buffer will be set with the original alias. + * + * @param alias Alias name + * @param itemname Buffer to store item name to + * @param maxlength Maximum buffer size + * + * @return True if alias is translated, false otherwise + */ +native bool:cs_get_translated_item_alias(const alias[], itemname[], maxlength); + +/** + * Returns some information about a weapon. + * + * @param weapon_id Weapon id, see CSW_* constants + * @param type Info type, see CS_WEAPONINFO_* constants + * + * @return Weapon information value + * @error If weapon_id and type are out of bound, an error will be thrown. + */ +native any:cs_get_weapon_info(weapon_id, CsWeaponInfo:type); + +/** + * Returns a weapon class id associated with a weapon id. + * + * @param weapon_id Weapon id (CSI_* constants) + * + * @return Weapon class id (CS_WEAPONCLASS_* constants) + */ +stock CsWeaponClassType:cs_get_weapon_class(weapon_id) +{ + new CsWeaponClassType:type = CS_WEAPONCLASS_NONE; + + if (cs_is_valid_itemid(weapon_id, .weapon_only = true) || weapon_id == CSI_SHIELD) + { + switch (weapon_id) + { + case CSI_SHIELDGUN, CSI_SHIELD: + { + type = CS_WEAPONCLASS_PISTOL; + } + case CSI_KNIFE: + { + type = CS_WEAPONCLASS_KNIFE; + } + default: + { + new const bits = (1 << weapon_id); + + if(bits & CSI_ALL_PISTOLS) + { + type = CS_WEAPONCLASS_PISTOL; + } + else if(bits & CSI_ALL_GRENADES) + { + type = CS_WEAPONCLASS_GRENADE; + } + else if(bits & CSI_ALL_SMGS) + { + type = CS_WEAPONCLASS_SUBMACHINEGUN; + } + else if(bits & CSI_ALL_SHOTGUNS) + { + type = CS_WEAPONCLASS_SHOTGUN; + } + else if(bits & CSI_ALL_MACHINEGUNS) + { + type = CS_WEAPONCLASS_MACHINEGUN; + } + else if(bits & CSI_ALL_RIFLES) + { + type = CS_WEAPONCLASS_RIFLE; + } + else if(bits & CSI_ALL_SNIPERRIFLES) + { + type = CS_WEAPONCLASS_SNIPERRIFLE; + } + } + } + } + + return type; +} + +/** + * Checks whether an item id is not out of bounds. + * + * @param id Item id (CSI_* constants) + * @param weapon_only If true, only the real weapon ids will be checked, + * including shield as well + * + * @return True if item id is valid, false otherwise + */ +stock bool:cs_is_valid_itemid(id, bool:weapon_only = false) +{ + if (id <= CSI_NONE) + { + return false; + } + + if (id > CSI_LAST_WEAPON && id != CSI_SHIELDGUN && weapon_only) + { + return false; + } + + if (id >= CSI_MAX_COUNT) + { + return false; + } + + return true; +} + /** * Called when CS internally fires a command to a player. * diff --git a/plugins/include/cstrike_const.inc b/plugins/include/cstrike_const.inc new file mode 100644 index 00000000..4478b9fb --- /dev/null +++ b/plugins/include/cstrike_const.inc @@ -0,0 +1,487 @@ +// vim: set ts=4 sw=4 tw=99 noet: +// +// AMX Mod X, based on AMX Mod by Aleksander Naszko ("OLO"). +// Copyright (C) The AMX Mod X Development Team. +// +// This software is licensed under the GNU General Public License, version 3 or higher. +// Additional exceptions apply. For full license details, see LICENSE.txt or visit: +// https://alliedmods.net/amxmodx-license + +// +// Counter-Strike Functions +// + +#if defined _cstrike_const_included + #endinput +#endif +#define _cstrike_const_included + +/** + * IDs of weapons in CS + */ +#define CSW_NONE 0 +#define CSW_P228 1 +#define CSW_SCOUT 3 +#define CSW_HEGRENADE 4 +#define CSW_XM1014 5 +#define CSW_C4 6 +#define CSW_MAC10 7 +#define CSW_AUG 8 +#define CSW_SMOKEGRENADE 9 +#define CSW_ELITE 10 +#define CSW_FIVESEVEN 11 +#define CSW_UMP45 12 +#define CSW_SG550 13 +#define CSW_GALI 14 +#define CSW_GALIL 14 +#define CSW_FAMAS 15 +#define CSW_USP 16 +#define CSW_GLOCK18 17 +#define CSW_AWP 18 +#define CSW_MP5NAVY 19 +#define CSW_M249 20 +#define CSW_M3 21 +#define CSW_M4A1 22 +#define CSW_TMP 23 +#define CSW_G3SG1 24 +#define CSW_FLASHBANG 25 +#define CSW_DEAGLE 26 +#define CSW_SG552 27 +#define CSW_AK47 28 +#define CSW_KNIFE 29 +#define CSW_P90 30 +#define CSW_VEST 31 // Custom +#define CSW_VESTHELM 32 // Custom +#define CSW_SHIELDGUN 99 +#define CSW_LAST_WEAPON CSW_P90 + +const CSW_ALL_WEAPONS = (~(1<(cbase, m_PevOffset); } + edict_t* cbase_to_edict(void *cbase) + { + return entvar_to_edict(cbase_to_entvar(cbase)); + } + int cbase_to_id(void *cbase) { return entvars_to_id(cbase_to_entvar(cbase)); diff --git a/support/PackageScript b/support/PackageScript index 1d42f07e..314a861f 100644 --- a/support/PackageScript +++ b/support/PackageScript @@ -273,6 +273,7 @@ scripting_files = [ 'include/core.inc', 'include/csstats.inc', 'include/cstrike.inc', + 'include/cstrike_const.inc', 'include/csx.inc', 'include/cvars.inc', 'include/datapack.inc',