1- /* *
1+ /* *
22 * ============================================================================= 
33 * CS2Fixes 
44 * Copyright (C) 2023-2025 Source2ZE 
@@ -59,70 +59,27 @@ bool g_bEnableAdminCommands;
5959FAKE_BOOL_CVAR (cs2f_commands_enable, " Whether to enable chat commands" false , 0 )
6060FAKE_BOOL_CVAR(cs2f_admin_commands_enable, " Whether to enable admin chat commands" false , 0 )
6161
62- //  clang-format off
63- WeaponMapEntry_t WeaponMap[] = {
64- 	{{" bizon" " weapon_bizon" " PP-Bizon" 1400 , 26 , GEAR_SLOT_RIFLE},
65- 	{{" mac10" " mac" " weapon_mac10" " MAC-10" 1050 , 27 , GEAR_SLOT_RIFLE},
66- 	{{" mp5sd" " mp5" " weapon_mp5sd" " MP5-SD" 1500 , 23 , GEAR_SLOT_RIFLE},
67- 	{{" mp7" " weapon_mp7" " MP7" 1500 , 23 , GEAR_SLOT_RIFLE},
68- 	{{" mp9" " weapon_mp9" " MP9" 1250 , 34 , GEAR_SLOT_RIFLE},
69- 	{{" p90" " weapon_p90" " P90" 2350 , 19 , GEAR_SLOT_RIFLE},
70- 	{{" ump45" " ump" " weapon_ump45" " UMP-45" 1200 , 24 , GEAR_SLOT_RIFLE},
71- 	{{" ak47" " ak" " weapon_ak47" " AK-47" 2700 , 7 , GEAR_SLOT_RIFLE},
72- 	{{" aug" " weapon_aug" " AUG" 3300 , 8 , GEAR_SLOT_RIFLE},
73- 	{{" famas" " weapon_famas" " FAMAS" 2050 , 10 , GEAR_SLOT_RIFLE},
74- 	{{" galilar" " galil" " weapon_galilar" " Galil AR" 1800 , 13 , GEAR_SLOT_RIFLE},
75- 	{{" m4a4" " weapon_m4a1" " M4A4" 3100 , 16 , GEAR_SLOT_RIFLE},
76- 	{{" m4a1-s" " m4a1" " weapon_m4a1_silencer" " M4A1-S" 2900 , 60 , GEAR_SLOT_RIFLE},
77- 	{{" sg553" " weapon_sg556" " SG 553" 3000 , 39 , GEAR_SLOT_RIFLE},
78- 	{{" awp" " weapon_awp" " AWP" 4750 , 9 , GEAR_SLOT_RIFLE},
79- 	{{" g3sg1" " weapon_g3sg1" " G3SG1" 5000 , 11 , GEAR_SLOT_RIFLE},
80- 	{{" scar20" " scar" " weapon_scar20" " SCAR-20" 5000 , 38 , GEAR_SLOT_RIFLE},
81- 	{{" ssg08" " ssg" " weapon_ssg08" " SSG 08" 1700 , 40 , GEAR_SLOT_RIFLE},
82- 	{{" mag7" " mag" " weapon_mag7" " MAG-7" 1300 , 29 , GEAR_SLOT_RIFLE},
83- 	{{" nova" " weapon_nova" " Nova" 1050 , 35 , GEAR_SLOT_RIFLE},
84- 	{{" sawedoff" " weapon_sawedoff" " Sawed-Off" 1100 , 29 , GEAR_SLOT_RIFLE},
85- 	{{" xm1014" " xm" " weapon_xm1014" " XM1014" 2000 , 25 , GEAR_SLOT_RIFLE},
86- 	{{" m249" " weapon_m249" " M249" 5200 , 14 , GEAR_SLOT_RIFLE},
87- 	{{" negev" " weapon_negev" " Negev" 1700 , 28 , GEAR_SLOT_RIFLE},
88- 	{{" deagle" " weapon_deagle" " Desert Eagle" 700 , 1 , GEAR_SLOT_PISTOL},
89- 	{{" dualberettas" " elite" " weapon_elite" " Dual Berettas" 300 , 2 , GEAR_SLOT_PISTOL},
90- 	{{" fiveseven" " weapon_fiveseven" " Five-SeveN" 500 , 3 , GEAR_SLOT_PISTOL},
91- 	{{" glock18" " glock" " weapon_glock" " Glock-18" 200 , 4 , GEAR_SLOT_PISTOL},
92- 	{{" p2000" " weapon_hkp2000" " P2000" 200 , 32 , GEAR_SLOT_PISTOL},
93- 	{{" p250" " weapon_p250" " P250" 300 , 36 , GEAR_SLOT_PISTOL},
94- 	{{" tec9" " weapon_tec9" " Tec-9" 500 , 30 , GEAR_SLOT_PISTOL},
95- 	{{" usp-s" " usp" " weapon_usp_silencer" " USP-S" 200 , 61 , GEAR_SLOT_PISTOL},
96- 	{{" cz75-auto" " cs75a" " cz" " weapon_cz75a" " CZ75-Auto" 500 , 63 , GEAR_SLOT_PISTOL},
97- 	{{" r8revolver" " revolver" " r8" " weapon_revolver" " R8 Revolver" 600 , 64 , GEAR_SLOT_PISTOL},
98- 	{{" hegrenade" " he" " weapon_hegrenade" " HE Grenade" 300 , 44 , GEAR_SLOT_GRENADES, 1 },
99- 	{{" molotov" " weapon_molotov" " Molotov" 400 , 46 , GEAR_SLOT_GRENADES, 1 },
100- 	{{" kevlar" " item_kevlar" " Kevlar Vest" 650 , 50 , GEAR_SLOT_UTILITY},
101- };
102- //  clang-format on
103- 
10462bool g_bEnableWeapons = false;
10563
10664FAKE_BOOL_CVAR (cs2f_weapons_enable, " Whether to enable weapon commands" false , false )
10765
108- int GetGrenadeAmmo(CCSPlayer_WeaponServices* pWeaponServices, WeaponMapEntry_t weaponEntry )
66+ int GetGrenadeAmmo(CCSPlayer_WeaponServices* pWeaponServices, const  WeaponInfo_t* pWeaponInfo )
10967{
110- 	if  (!pWeaponServices || weaponEntry. iGearSlot  != GEAR_SLOT_GRENADES)
68+ 	if  (!pWeaponServices || pWeaponInfo-> m_eSlot  != GEAR_SLOT_GRENADES)
11169		return  -1 ;
11270
11371	//  TODO: look into molotov vs inc interaction
114- 	if  (strcmp (weaponEntry. szClassName , " weapon_hegrenade" 0 )
72+ 	if  (strcmp (pWeaponInfo-> m_pClass , " weapon_hegrenade" 0 )
11573		return  pWeaponServices->m_iAmmo [AMMO_OFFSET_HEGRENADE];
116- 	else   if  (strcmp (weaponEntry. szClassName , " weapon_molotov" 0  || strcmp (weaponEntry. szClassName , " weapon_incgrenade" 0 )
74+ 	if  (strcmp (pWeaponInfo-> m_pClass , " weapon_molotov" 0  || strcmp (pWeaponInfo-> m_pClass , " weapon_incgrenade" 0 )
11775		return  pWeaponServices->m_iAmmo [AMMO_OFFSET_MOLOTOV];
118- 	else   if  (strcmp (weaponEntry. szClassName , " weapon_decoy" 0 )
76+ 	if  (strcmp (pWeaponInfo-> m_pClass , " weapon_decoy" 0 )
11977		return  pWeaponServices->m_iAmmo [AMMO_OFFSET_DECOY];
120- 	else   if  (strcmp (weaponEntry. szClassName , " weapon_flashbang" 0 )
78+ 	if  (strcmp (pWeaponInfo-> m_pClass , " weapon_flashbang" 0 )
12179		return  pWeaponServices->m_iAmmo [AMMO_OFFSET_FLASHBANG];
122- 	else   if  (strcmp (weaponEntry. szClassName , " weapon_smokegrenade" 0 )
80+ 	if  (strcmp (pWeaponInfo-> m_pClass , " weapon_smokegrenade" 0 )
12381		return  pWeaponServices->m_iAmmo [AMMO_OFFSET_SMOKEGRENADE];
124- 	else 
125- 		return  -1 ;
82+ 	return  -1 ;
12683}
12784
12885int  GetGrenadeAmmoTotal (CCSPlayer_WeaponServices* pWeaponServices)
@@ -152,32 +109,15 @@ void ParseWeaponCommand(const CCommand& args, CCSPlayerController* player)
152109
153110	VPROF (" ParseWeaponCommand" 
154111
155- 	CCSPlayerPawn* pPawn = (CCSPlayerPawn*)player->GetPawn ();
156- 	WeaponMapEntry_t weaponEntry;
157- 	bool  foundWeapon = false ;
158- 
159- 	for  (int  i = 0 ; i < sizeof (WeaponMap) / sizeof (*WeaponMap); i++)
160- 	{
161- 		if  (foundWeapon)
162- 			break ;
163- 
164- 		weaponEntry = WeaponMap[i];
165- 		const  char * command = args[0 ];
112+ 	const  auto  pPawn = reinterpret_cast <CCSPlayerPawn*>(player->GetPawn ());
166113
167- 		if  (!V_strncmp (" c_" 2 ))
168- 			command = command + 2 ;
114+ 	const  char * command = args[0 ];
115+ 	if  (!V_strncmp (" c_" 2 ))
116+ 		command = command + 2 ;
169117
170- 		for  (std::string alias : weaponEntry.aliases )
171- 		{
172- 			if  (!V_stricmp (command, alias.c_str ()))
173- 			{
174- 				foundWeapon = true ;
175- 				break ;
176- 			}
177- 		}
178- 	}
118+ 	const  auto  pWeaponInfo = FindWeaponInfoByAlias (command);
179119
180- 	if  (!foundWeapon )
120+ 	if  (!pWeaponInfo || pWeaponInfo-> m_nPrice  ==  0 )
181121		return ;
182122
183123	if  (pPawn->m_iHealth () <= 0  || pPawn->m_iTeamNum  != CS_TEAM_CT)
@@ -195,29 +135,27 @@ void ParseWeaponCommand(const CCommand& args, CCSPlayerController* player)
195135
196136	int  money = player->m_pInGameMoneyServices ->m_iAccount ;
197137
198- 	if  (money < weaponEntry. iPrice )
138+ 	if  (money < pWeaponInfo-> m_nPrice )
199139	{
200- 		ClientPrint (player, HUD_PRINTTALK, CHAT_PREFIX " You can't afford %s! It costs $%i, you only have $%i" weaponEntry. szWeaponName , weaponEntry. iPrice , money);
140+ 		ClientPrint (player, HUD_PRINTTALK, CHAT_PREFIX " You can't afford %s! It costs $%i, you only have $%i" pWeaponInfo-> m_pName , pWeaponInfo-> m_nPrice , money);
201141		return ;
202142	}
203143
204- 	if  (weaponEntry. iGearSlot  == GEAR_SLOT_GRENADES)
144+ 	if  (pWeaponInfo-> m_eSlot  == GEAR_SLOT_GRENADES)
205145	{
206- 		CUtlVector<CHandle<CBasePlayerWeapon>>* weapons = pWeaponServices->m_hMyWeapons ();
207- 
208146		//  CONVAR_TODO
209147		ConVar* cvar = g_pCVar->GetConVar (g_pCVar->FindConVar (" ammo_grenade_limit_default" 
210148		//  HACK: values is actually the cvar value itself, hence this ugly cast.
211149		int  iGrenadeLimitDefault = *(int *)&cvar->values ;
212150		cvar = g_pCVar->GetConVar (g_pCVar->FindConVar (" ammo_grenade_limit_total" 
213151		int  iGrenadeLimitTotal = *(int *)&cvar->values ;
214152
215- 		int  iMatchingGrenades = GetGrenadeAmmo (pWeaponServices, weaponEntry );
153+ 		int  iMatchingGrenades = GetGrenadeAmmo (pWeaponServices, pWeaponInfo );
216154		int  iTotalGrenades = GetGrenadeAmmoTotal (pWeaponServices);
217155
218156		if  (iMatchingGrenades >= iGrenadeLimitDefault)
219157		{
220- 			ClientPrint (player, HUD_PRINTTALK, CHAT_PREFIX " You cannot carry any more %ss (Max %i)" weaponEntry. szWeaponName , iGrenadeLimitDefault);
158+ 			ClientPrint (player, HUD_PRINTTALK, CHAT_PREFIX " You cannot carry any more %ss (Max %i)" pWeaponInfo-> m_pName , iGrenadeLimitDefault);
221159			return ;
222160		}
223161
@@ -228,18 +166,18 @@ void ParseWeaponCommand(const CCommand& args, CCSPlayerController* player)
228166		}
229167	}
230168
231- 	if  (weaponEntry. maxAmount )
169+ 	if  (pWeaponInfo-> m_nMaxAmount )
232170	{
233171		CUtlVector<WeaponPurchaseCount_t>* weaponPurchases = pPawn->m_pActionTrackingServices ->m_weaponPurchasesThisRound ().m_weaponPurchases ;
234172		bool  found = false ;
235173		FOR_EACH_VEC (*weaponPurchases, i)
236174		{
237175			WeaponPurchaseCount_t& purchase = (*weaponPurchases)[i];
238- 			if  (purchase.m_nItemDefIndex  == weaponEntry. iItemDefIndex )
176+ 			if  (purchase.m_nItemDefIndex  == pWeaponInfo-> m_iItemDefinitionIndex )
239177			{
240- 				if  (purchase.m_nCount  >= weaponEntry. maxAmount )
178+ 				if  (purchase.m_nCount  >= pWeaponInfo-> m_nMaxAmount )
241179				{
242- 					ClientPrint (player, HUD_PRINTTALK, CHAT_PREFIX " You cannot buy any more %s (Max %i)" weaponEntry. szWeaponName , weaponEntry. maxAmount );
180+ 					ClientPrint (player, HUD_PRINTTALK, CHAT_PREFIX " You cannot buy any more %s (Max %i)" pWeaponInfo-> m_pName , pWeaponInfo-> m_nMaxAmount );
243181					return ;
244182				}
245183				purchase.m_nCount  += 1 ;
@@ -253,13 +191,13 @@ void ParseWeaponCommand(const CCommand& args, CCSPlayerController* player)
253191			WeaponPurchaseCount_t purchase = {};
254192
255193			purchase.m_nCount  = 1 ;
256- 			purchase.m_nItemDefIndex  = weaponEntry. iItemDefIndex ;
194+ 			purchase.m_nItemDefIndex  = pWeaponInfo-> m_iItemDefinitionIndex ;
257195
258196			weaponPurchases->AddToTail (purchase);
259197		}
260198	}
261199
262- 	if  (weaponEntry. iGearSlot  == GEAR_SLOT_RIFLE || weaponEntry. iGearSlot  == GEAR_SLOT_PISTOL)
200+ 	if  (pWeaponInfo-> m_eSlot  == GEAR_SLOT_RIFLE || pWeaponInfo-> m_eSlot  == GEAR_SLOT_PISTOL)
263201	{
264202		CUtlVector<CHandle<CBasePlayerWeapon>>* weapons = pWeaponServices->m_hMyWeapons ();
265203
@@ -270,17 +208,46 @@ void ParseWeaponCommand(const CCommand& args, CCSPlayerController* player)
270208			if  (!weapon)
271209				continue ;
272210
273- 			if  (weapon->GetWeaponVData ()->m_GearSlot () == weaponEntry. iGearSlot )
211+ 			if  (weapon->GetWeaponVData ()->m_GearSlot () == pWeaponInfo-> m_eSlot )
274212			{
275213				pWeaponServices->DropWeapon (weapon);
276214				break ;
277215			}
278216		}
279217	}
280218
281- 	player->m_pInGameMoneyServices ->m_iAccount  = money - weaponEntry.iPrice ;
282- 	pItemServices->GiveNamedItem (weaponEntry.szClassName );
283- 	ClientPrint (player, HUD_PRINTTALK, CHAT_PREFIX " You have purchased %s for $%i" szWeaponName , weaponEntry.iPrice );
219+ 	CBasePlayerWeapon* pWeapon = pItemServices->GiveNamedItemAws (pWeaponInfo->m_pClass );
220+ 
221+ 	//  Normally shouldn't be possible, but avoid crashes in some edge cases
222+ 	if  (!pWeapon)
223+ 		return ;
224+ 
225+ 	player->m_pInGameMoneyServices ->m_iAccount  = money - pWeaponInfo->m_nPrice ;
226+ 
227+ 	//  If the weapon spawn goes through AWS, it needs to be reselected with a 1 tick delay (some fuckery with team change?)
228+ 	if  (pWeaponInfo->m_eSlot  == GEAR_SLOT_RIFLE || pWeaponInfo->m_eSlot  == GEAR_SLOT_PISTOL)
229+ 	{
230+ 		CHandle<CBasePlayerWeapon> hWeapon = pWeapon->GetHandle ();
231+ 		CHandle<CCSPlayerPawn> hPawn = pPawn->GetHandle ();
232+ 
233+ 		new  CTimer (0 .0f , false , false , [hWeapon, hPawn]() {
234+ 			CBasePlayerWeapon* pWeapon = hWeapon.Get ();
235+ 			CCSPlayerPawn* pPawn = hPawn.Get ();
236+ 
237+ 			if  (!pWeapon || !pPawn)
238+ 				return  -1 .0f ;
239+ 
240+ 			CCSPlayer_WeaponServices* pWeaponServices = pPawn->m_pWeaponServices ;
241+ 
242+ 			if  (!pWeaponServices)
243+ 				return  -1 .0f ;
244+ 
245+ 			pWeaponServices->SelectItem (pWeapon);
246+ 			return  -1 .0f ;
247+ 		});
248+ 	}
249+ 
250+ 	ClientPrint (player, HUD_PRINTTALK, CHAT_PREFIX " You have purchased %s for $%i" m_pName , pWeaponInfo->m_nPrice );
284251}
285252
286253void  WeaponCommandCallback (const  CCommandContext& context, const  CCommand& args)
@@ -298,11 +265,11 @@ void WeaponCommandCallback(const CCommandContext& context, const CCommand& args)
298265
299266void  RegisterWeaponCommands ()
300267{
301- 	for  (int  i = 0 ; i < sizeof (WeaponMap) / sizeof (*WeaponMap); i++)
302- 	{
303- 		WeaponMapEntry_t weaponEntry = WeaponMap[i];
268+ 	const  auto & weapons = GenerateWeaponCommands ();
304269
305- 		for  (std::string alias : weaponEntry.aliases )
270+ 	for  (const  auto & aliases : weapons | std::views::values)
271+ 	{
272+ 		for  (const  auto & alias : aliases)
306273		{
307274			new  CChatCommand (alias.c_str (), ParseWeaponCommand, " - Buys this weapon" 
308275			ConCommandRefAbstract ref;
0 commit comments