Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1112 lines
33 KiB

  1. //====== Copyright � 1996-2005, Valve Corporation, All rights reserved. =======
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "buy_preset_debug.h"
  8. #include "buy_presets.h"
  9. #include "weapon_csbase.h"
  10. #include "cs_ammodef.h"
  11. #include "cs_gamerules.h"
  12. #include "c_cs_player.h"
  13. #include "bot/shared_util.h"
  14. #include "vgui/ILocalize.h"
  15. #include <vgui_controls/Controls.h>
  16. // memdbgon must be the last include file in a .cpp file!!!
  17. #include "tier0/memdbgon.h"
  18. extern ConVar cl_rebuy;
  19. extern CSWeaponID GetClientWeaponID( bool primary );
  20. //--------------------------------------------------------------------------------------------------------------
  21. /**
  22. * Returns true if the local player is a CT and the map is a defuse map.
  23. */
  24. static bool CanBuyDefuser()
  25. {
  26. C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
  27. return ( pPlayer && pPlayer->GetTeamNumber() == TEAM_CT && CSGameRules()->IsBombDefuseMap() );
  28. }
  29. void BuyPresetManager::GetCurrentLoadout( WeaponSet *weaponSet )
  30. {
  31. if ( !weaponSet )
  32. return;
  33. C_CSPlayer *player = C_CSPlayer::GetLocalCSPlayer();
  34. if ( !player )
  35. return;
  36. CWeaponCSBase *pWeapon;
  37. const CCSWeaponInfo *pInfo;
  38. int ammo[MAX_AMMO_TYPES];
  39. memset( ammo, 0, sizeof(ammo) );
  40. FillClientAmmo( ammo );
  41. weaponSet->Reset();
  42. // Grab current armor values
  43. weaponSet->m_armor = ( player->ArmorValue() > 0 ) ? 100 : 0;
  44. weaponSet->m_helmet = player->HasHelmet();
  45. // Grab current smoke grenade
  46. pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_SMOKEGRENADE ));
  47. pInfo = GetWeaponInfo( WEAPON_SMOKEGRENADE );
  48. int ammoType = (pInfo)?pInfo->GetPrimaryAmmoType():0;
  49. weaponSet->m_smokeGrenade = (pWeapon && player->GetAmmoCount( ammoType ));
  50. // Grab current HE grenade
  51. pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_HEGRENADE ));
  52. pInfo = GetWeaponInfo( WEAPON_HEGRENADE );
  53. ammoType = (pInfo)?pInfo->GetPrimaryAmmoType():0;
  54. weaponSet->m_HEGrenade = (pWeapon && player->GetAmmoCount( ammoType ));
  55. // Grab current flashbangs
  56. pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_FLASHBANG ));
  57. pInfo = GetWeaponInfo( WEAPON_FLASHBANG );
  58. ammoType = (pInfo)?pInfo->GetPrimaryAmmoType():0;
  59. weaponSet->m_flashbangs = (!pWeapon) ? 0 : player->GetAmmoCount( ammoType );
  60. // Grab current equipment
  61. weaponSet->m_defuser = player->HasDefuser();
  62. weaponSet->m_nightvision = player->HasNightVision();
  63. // Grab current primary weapon
  64. CSWeaponID primaryID = GetClientWeaponID( true ); ///< The local player's current primary weapon
  65. pInfo = GetWeaponInfo( primaryID );
  66. if ( pInfo )
  67. {
  68. int roundsNeeded = ammo[pInfo->GetPrimaryAmmoType()];
  69. int buySize = GetCSAmmoDef()->GetBuySize( pInfo->GetPrimaryAmmoType() );
  70. int numClips = ceil(roundsNeeded/(float)buySize);
  71. weaponSet->m_primaryWeapon.SetWeaponID( primaryID );
  72. weaponSet->m_primaryWeapon.SetAmmoType( AMMO_CLIPS );
  73. weaponSet->m_primaryWeapon.SetAmmoAmount( numClips );
  74. weaponSet->m_primaryWeapon.SetFillAmmo( false );
  75. }
  76. // Grab current pistol
  77. CSWeaponID secondaryID = GetClientWeaponID( false ); ///< The local player's current pistol
  78. pInfo = GetWeaponInfo( secondaryID );
  79. if ( pInfo )
  80. {
  81. int roundsNeeded = ammo[pInfo->GetPrimaryAmmoType()];
  82. int buySize = GetCSAmmoDef()->GetBuySize( pInfo->GetPrimaryAmmoType() );
  83. int numClips = ceil(roundsNeeded/(float)buySize);
  84. weaponSet->m_secondaryWeapon.SetWeaponID( secondaryID );
  85. weaponSet->m_secondaryWeapon.SetAmmoType( AMMO_CLIPS );
  86. weaponSet->m_secondaryWeapon.SetAmmoAmount( numClips );
  87. weaponSet->m_secondaryWeapon.SetFillAmmo( false );
  88. }
  89. }
  90. //--------------------------------------------------------------------------------------------------------------
  91. //--------------------------------------------------------------------------------------------------------------
  92. WeaponSet::WeaponSet()
  93. {
  94. Reset();
  95. }
  96. //--------------------------------------------------------------------------------------------------------------
  97. /**
  98. * Sets reasonable defaults for an empty weapon set: no primary/secondary weapons, no equipment, and
  99. * everything optional.
  100. */
  101. void WeaponSet::Reset()
  102. {
  103. BuyPresetWeapon blank;
  104. m_primaryWeapon = blank;
  105. m_secondaryWeapon = blank;
  106. m_armor = 0;
  107. m_helmet = false;
  108. m_smokeGrenade = false;
  109. m_HEGrenade = false;
  110. m_flashbangs = 0;
  111. m_defuser = false;
  112. m_nightvision = false;
  113. }
  114. //--------------------------------------------------------------------------------------------------------------
  115. /**
  116. * Constructs a WeaponSet containing everything that could be purchased right now. The cost is also
  117. * returned, and is -1 if the set is not purchasable (as opposed to completely purchased already, in
  118. * which case cost is 0.)
  119. */
  120. void WeaponSet::GetCurrent( int& cost, WeaponSet& ws ) const
  121. {
  122. cost = -1;
  123. ws.Reset();
  124. if ( !engine->IsConnected() )
  125. return;
  126. C_CSPlayer *player = CCSPlayer::GetLocalCSPlayer();
  127. if ( !player )
  128. return;
  129. if ( player->GetTeamNumber() != TEAM_CT && player->GetTeamNumber() != TEAM_TERRORIST )
  130. return;
  131. // we're connected, and a valid team, so we can purchase
  132. cost = 0;
  133. if ( player->IsVIP() )
  134. return; // can't buy anything as the VIP
  135. int iHelmetPrice = ITEM_PRICE_HELMET;
  136. int iKevlarPrice = ITEM_PRICE_KEVLAR;
  137. int iNVGPrice = ITEM_PRICE_NVG;
  138. //-------------------------------------------------------------------------
  139. //-------------------------------------------------------------------------
  140. // Buy any required items
  141. //-------------------------------------------------------------------------
  142. // Armor
  143. if ( m_armor > player->ArmorValue() )
  144. {
  145. cost += iKevlarPrice;
  146. ws.m_armor = 100;
  147. }
  148. if ( (m_helmet && m_armor > 0) && !player->HasHelmet() )
  149. {
  150. cost += iHelmetPrice;
  151. ws.m_armor = 100;
  152. ws.m_helmet = true;
  153. }
  154. CWeaponCSBase *pWeapon;
  155. const CCSWeaponInfo *pInfo;
  156. //-------------------------------------------------------------------------
  157. // Smoke grenade
  158. pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_SMOKEGRENADE ));
  159. pInfo = GetWeaponInfo( WEAPON_SMOKEGRENADE );
  160. int ammoType = (pInfo)?pInfo->GetPrimaryAmmoType():0;
  161. bool hasSmokeGrenade = (pWeapon && player->GetAmmoCount( ammoType ));
  162. if ( m_smokeGrenade && !hasSmokeGrenade )
  163. {
  164. cost += pWeapon->GetWeaponPrice();
  165. ws.m_smokeGrenade = true;
  166. hasSmokeGrenade = true;
  167. }
  168. //-------------------------------------------------------------------------
  169. // HE grenade
  170. pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_HEGRENADE ));
  171. pInfo = GetWeaponInfo( WEAPON_HEGRENADE );
  172. ammoType = (pInfo)?pInfo->GetPrimaryAmmoType():0;
  173. bool hasHEGrenade = (pWeapon && player->GetAmmoCount( ammoType ));
  174. if ( m_HEGrenade && !hasHEGrenade )
  175. {
  176. cost += pWeapon->GetWeaponPrice();
  177. ws.m_HEGrenade = true;
  178. hasHEGrenade = true;
  179. }
  180. //-------------------------------------------------------------------------
  181. // Flashbang grenades
  182. pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_FLASHBANG ));
  183. pInfo = GetWeaponInfo( WEAPON_FLASHBANG );
  184. ammoType = (pInfo)?pInfo->GetPrimaryAmmoType():0;
  185. int numFlashbangs = (!pWeapon) ? 0 : player->GetAmmoCount( ammoType );
  186. if ( m_flashbangs && numFlashbangs < m_flashbangs )
  187. {
  188. int count = m_flashbangs - numFlashbangs;
  189. cost += pWeapon->GetWeaponPrice() * count;
  190. ws.m_flashbangs = count;
  191. numFlashbangs += count;
  192. }
  193. //-------------------------------------------------------------------------
  194. // defuser
  195. if ( m_defuser && player->GetTeamNumber() == TEAM_CT && !player->HasDefuser() && CanBuyDefuser() )
  196. {
  197. cost += ITEM_PRICE_DEFUSEKIT;
  198. ws.m_defuser = true;
  199. }
  200. //-------------------------------------------------------------------------
  201. // nightvision goggles
  202. if ( m_nightvision && !player->HasNightVision() )
  203. {
  204. cost += iNVGPrice;
  205. ws.m_nightvision = true;
  206. }
  207. //-------------------------------------------------------------------------
  208. // Construct a list of weapons to buy from.
  209. CSWeaponID primaryID = GetClientWeaponID( true ); ///< The local player's current primary weapon
  210. CSWeaponID secondaryID = GetClientWeaponID( false ); ///< The local player's current pistol
  211. BuyPresetWeaponList primaryWeapons;
  212. primaryWeapons.AddToTail( m_primaryWeapon );
  213. BuyPresetWeaponList secondaryWeapons;
  214. secondaryWeapons.AddToTail( m_secondaryWeapon );
  215. /**
  216. * Determine best weapons & ammo to purchase
  217. *
  218. * For each primary weapon in the list, do the following until one satisfies the current money situation:
  219. * 1. buy primary weapon and its ammo (NEED TO WATCH FOR WEAPONS PLAYER CANNOT BUY!!!)
  220. * 2. for each non-optional secondary weapon (if any),
  221. * try to buy it and its ammo
  222. * 3. when a set can be purchased, add it to the total cost and stop processing
  223. * If we can't purchase the primary weapon, or a required secondary weapon, the WeaponSet can't be bought,
  224. * so we return -1.
  225. *
  226. * To find if a weapon is owned, we can compare it's weaponID to either primaryID or secondaryID from above.
  227. */
  228. int currentCost = cost;
  229. int ammo[MAX_AMMO_TYPES];
  230. memset( ammo, 0, sizeof(ammo) );
  231. FillClientAmmo( ammo );
  232. const BuyPresetWeapon *primaryWeaponToBuy = NULL;
  233. const BuyPresetWeapon *secondaryWeaponToBuy = NULL;
  234. int primaryClipsToBuy = 0;
  235. int secondaryClipsToBuy = 0;
  236. int currentNonWeaponCost = currentCost;
  237. bool doneBuyingWeapons = false;
  238. int currentCash = player->GetAccount();
  239. //-------------------------------------------------------------------------
  240. // Try to buy the primary weapon
  241. int i;
  242. for ( i=0; i<primaryWeapons.Count() && !doneBuyingWeapons; ++i )
  243. {
  244. const BuyPresetWeapon *primaryWeapon = &primaryWeapons[i];
  245. primaryWeaponToBuy = NULL;
  246. int primaryClips = 0;
  247. primaryClipsToBuy = 0;
  248. currentCost = currentNonWeaponCost;
  249. FillClientAmmo( ammo );
  250. CSWeaponID weaponID = primaryWeapon->GetCSWeaponID();
  251. if ( weaponID == WEAPON_NONE )
  252. weaponID = primaryID;
  253. const CCSWeaponInfo *primaryInfo = GetWeaponInfo( weaponID );
  254. int primaryAmmoCost = 0;
  255. int primaryAmmoBuySize = 0;
  256. if ( primaryInfo )
  257. {
  258. primaryClips = CalcClipsNeeded( primaryWeapon, primaryInfo, ammo );
  259. int primaryWeaponCost = ( weaponID != primaryID ) ? primaryInfo->GetWeaponPrice() : 0;
  260. primaryAmmoCost = GetCSAmmoDef()->GetCost( primaryInfo->GetPrimaryAmmoType() );
  261. primaryAmmoBuySize = GetCSAmmoDef()->GetBuySize( primaryInfo->GetPrimaryAmmoType() );
  262. currentCost += primaryWeaponCost;
  263. currentCost += primaryAmmoCost * primaryClips;
  264. ammo[primaryInfo->GetPrimaryAmmoType()] += primaryClips * primaryAmmoBuySize;
  265. CURRENTCOST_DEBUG("\t%5.5d (%s)\n", primaryWeaponCost, WeaponIDToAlias( weaponID ));
  266. CURRENTCOST_DEBUG("\t%5.5d (ammo x %d)\n", primaryAmmoCost * primaryClips, primaryClips);
  267. if ( currentCash < currentCost )
  268. {
  269. currentCost = currentNonWeaponCost; // can't afford it, so try the next weapon
  270. continue;
  271. }
  272. // check for as_* maps, and CTs buying AK47s, etc
  273. if ( !CanBuyWeapon(primaryID, secondaryID, weaponID) && weaponID != primaryID )
  274. {
  275. currentCost = currentNonWeaponCost; // can't buy it, so try the next weapon
  276. continue;
  277. }
  278. primaryWeaponToBuy = primaryWeapon;
  279. primaryClipsToBuy = primaryClips;
  280. }
  281. if ( !secondaryWeapons.Count() )
  282. {
  283. CURRENTCOST_DEBUG("\t\t\tDone buying weapons (no secondary)\n");
  284. break;
  285. }
  286. //-------------------------------------------------------------------------
  287. // Try to buy a (required) secondary weapon to go with primaryWeaponToBuy.
  288. // If not, reset cost to not reflect either weapon, so we can look at buying
  289. // the next primary weapon in the list.
  290. int j;
  291. for ( j=0; j<secondaryWeapons.Count(); ++j )
  292. {
  293. const BuyPresetWeapon *secondaryWeapon = &secondaryWeapons[j];
  294. // reset ammo counts and cost
  295. secondaryWeaponToBuy = NULL;
  296. secondaryClipsToBuy = 0;
  297. currentCost = currentNonWeaponCost;
  298. FillClientAmmo( ammo );
  299. // add in ammo we're already buying for the primary weapon to the client's actual ammo
  300. if ( primaryInfo )
  301. {
  302. currentCost += ( weaponID != primaryID ) ? primaryInfo->GetWeaponPrice() : 0;
  303. currentCost += primaryAmmoCost * primaryClips;
  304. ammo[primaryInfo->GetPrimaryAmmoType() ] += primaryClips * primaryAmmoBuySize;
  305. }
  306. int currentCostWithoutSecondary = currentCost;
  307. CSWeaponID weaponID = secondaryWeapon->GetCSWeaponID();
  308. if ( weaponID == WEAPON_NONE )
  309. weaponID = secondaryID;
  310. const CCSWeaponInfo *secondaryInfo = GetWeaponInfo( weaponID );
  311. if ( !secondaryInfo )
  312. {
  313. currentCost = currentCostWithoutSecondary;
  314. continue;
  315. }
  316. int secondaryClips = CalcClipsNeeded( secondaryWeapon, secondaryInfo, ammo );
  317. int secondaryWeaponCost = ( weaponID != secondaryID ) ? secondaryInfo->GetWeaponPrice() : 0;
  318. int secondaryAmmoCost = GetCSAmmoDef()->GetCost( secondaryInfo->GetPrimaryAmmoType() );
  319. int secondaryAmmoBuySize = GetCSAmmoDef()->GetBuySize( secondaryInfo->GetPrimaryAmmoType() );
  320. currentCost += secondaryWeaponCost;
  321. currentCost += secondaryAmmoCost * secondaryClips;
  322. ammo[secondaryInfo->GetPrimaryAmmoType() ] += secondaryClips * secondaryAmmoBuySize;
  323. CURRENTCOST_DEBUG("\t%5.5d (%s)\n", secondaryWeaponCost, WeaponIDToAlias( weaponID ));
  324. CURRENTCOST_DEBUG("\t%5.5d (ammo x %d)\n", secondaryAmmoCost * secondaryClips, secondaryClips);
  325. if ( currentCash < currentCost )
  326. {
  327. currentCost = currentCostWithoutSecondary; // can't afford it, so skip
  328. continue;
  329. }
  330. // check for as_* maps, and CTs buying elites, etc
  331. if ( !CanBuyWeapon(primaryID, secondaryID, weaponID) && weaponID != secondaryID )
  332. {
  333. currentCost = currentCostWithoutSecondary; // can't buy it, so skip
  334. continue;
  335. }
  336. // We found both a primary and secondary weapon to buy. Stop looking for more.
  337. secondaryWeaponToBuy = secondaryWeapon;
  338. secondaryClipsToBuy = secondaryClips;
  339. doneBuyingWeapons = true;
  340. CURRENTCOST_DEBUG("\t\t\tDone buying weapons (primary && secondary)\n");
  341. break;
  342. }
  343. }
  344. // Update our cost to reflect the weapons and ammo purchased
  345. cost = currentCost;
  346. if ( primaryWeaponToBuy )
  347. {
  348. BuyPresetWeapon weapon = *primaryWeaponToBuy;
  349. weapon.SetAmmoAmount( primaryClipsToBuy );
  350. weapon.SetAmmoType( AMMO_CLIPS );
  351. ws.m_primaryWeapon = weapon;
  352. }
  353. else
  354. {
  355. // If we failed to buy a primary weapon, and one was in the list, bail - the player can't afford this WeaponSet.
  356. for ( int i=0; i<primaryWeapons.Count(); ++i )
  357. {
  358. int weaponID = primaryWeapons[i].GetCSWeaponID();
  359. if ( weaponID != WEAPON_NONE )
  360. {
  361. cost = -1;
  362. ws.Reset();
  363. return;
  364. }
  365. }
  366. }
  367. if ( secondaryWeaponToBuy )
  368. {
  369. BuyPresetWeapon weapon = *secondaryWeaponToBuy;
  370. weapon.SetAmmoAmount( secondaryClipsToBuy );
  371. weapon.SetAmmoType( AMMO_CLIPS );
  372. ws.m_secondaryWeapon = weapon;
  373. }
  374. else
  375. {
  376. // If we failed to buy a required secondary weapon, and one was in the list, bail - the player can't afford this WeaponSet.
  377. for ( int i=0; i<secondaryWeapons.Count(); ++i )
  378. {
  379. int weaponID = secondaryWeapons[i].GetCSWeaponID();
  380. if ( weaponID != WEAPON_NONE )
  381. {
  382. cost = -1;
  383. ws.Reset();
  384. return;
  385. }
  386. }
  387. }
  388. //-------------------------------------------------------------------------
  389. // now any extra ammo, in rebuy order
  390. const char *remainder = SharedParse( cl_rebuy.GetString() );
  391. const char *token;
  392. while ( remainder )
  393. {
  394. token = SharedGetToken();
  395. if ( !token || !*token )
  396. return;
  397. if ( !stricmp( token, "PrimaryAmmo" ) )
  398. {
  399. if ( primaryWeaponToBuy )
  400. {
  401. CSWeaponID id = primaryWeaponToBuy->GetCSWeaponID();
  402. if ( id == WEAPON_NONE )
  403. id = primaryID;
  404. if ( primaryWeaponToBuy->GetFillAmmo() )
  405. {
  406. const CCSWeaponInfo *info = GetWeaponInfo( id );
  407. if ( info )
  408. {
  409. int maxRounds = GetCSAmmoDef()->MaxCarry( info->GetPrimaryAmmoType(), C_CSPlayer::GetLocalCSPlayer() );
  410. int ammoCost = GetCSAmmoDef()->GetCost( info->GetPrimaryAmmoType() );
  411. int ammoBuySize = GetCSAmmoDef()->GetBuySize( info->GetPrimaryAmmoType() );
  412. int roundsLeft = maxRounds - ammo[info->GetPrimaryAmmoType()];
  413. int clipsLeft = (ammoBuySize > 0) ? ceil(roundsLeft / (float)ammoBuySize) : 0;
  414. while ( clipsLeft > 0 && cost + ammoCost <= currentCash )
  415. {
  416. ws.m_primaryWeapon.SetAmmoAmount( ws.m_primaryWeapon.GetAmmoAmount() + 1 );
  417. --clipsLeft;
  418. ammo[info->GetPrimaryAmmoType()] += MIN(maxRounds, ammoBuySize);
  419. cost += ammoCost;
  420. }
  421. }
  422. }
  423. }
  424. }
  425. else if ( !stricmp( token, "SecondaryAmmo" ) )
  426. {
  427. if ( secondaryWeaponToBuy )
  428. {
  429. if ( secondaryWeaponToBuy->GetFillAmmo() )
  430. {
  431. CSWeaponID weaponID = secondaryWeaponToBuy->GetCSWeaponID();
  432. if ( weaponID == WEAPON_NONE )
  433. weaponID = secondaryID;
  434. const CCSWeaponInfo *info = GetWeaponInfo( weaponID );
  435. if ( info )
  436. {
  437. int maxRounds = GetCSAmmoDef()->MaxCarry( info->GetPrimaryAmmoType(), C_CSPlayer::GetLocalCSPlayer() );
  438. int ammoCost = GetCSAmmoDef()->GetCost( info->GetPrimaryAmmoType() );
  439. int ammoBuySize = GetCSAmmoDef()->GetBuySize( info->GetPrimaryAmmoType() );
  440. int roundsLeft = maxRounds - ammo[info->GetPrimaryAmmoType() ];
  441. int clipsLeft = (ammoBuySize > 0) ? ceil(roundsLeft / (float)ammoBuySize) : 0;
  442. while ( clipsLeft > 0 && cost + ammoCost <= currentCash )
  443. {
  444. ws.m_secondaryWeapon.SetAmmoAmount( ws.m_secondaryWeapon.GetAmmoAmount() + 1 );
  445. --clipsLeft;
  446. ammo[info->GetPrimaryAmmoType() ] += MIN(maxRounds, ammoBuySize);
  447. cost += ammoCost;
  448. }
  449. }
  450. }
  451. }
  452. }
  453. remainder = SharedParse( remainder );
  454. }
  455. if ( cost > currentCash )
  456. {
  457. cost = -1; // can't buy it
  458. ws.Reset();
  459. }
  460. }
  461. //--------------------------------------------------------------------------------------------------------------
  462. /**
  463. * Constructs a WeaponSet containing everything that can be purchased.
  464. * The cost (including extra cash) is also calculated and returned.
  465. */
  466. void WeaponSet::GetFromScratch( int& cost, WeaponSet& ws ) const
  467. {
  468. cost = 0;
  469. ws.Reset();
  470. int ammo[MAX_AMMO_TYPES];
  471. memset( ammo, 0, sizeof(ammo) );
  472. int iHelmetPrice = ITEM_PRICE_HELMET;
  473. int iKevlarPrice = ITEM_PRICE_KEVLAR;
  474. int iNVGPrice = ITEM_PRICE_NVG;
  475. //-------------------------------------------------------------------------
  476. // Primary weapon
  477. const CCSWeaponInfo *primaryInfo = GetWeaponInfo( m_primaryWeapon.GetCSWeaponID() );
  478. if ( primaryInfo )
  479. {
  480. int primaryClips = CalcClipsNeeded( &m_primaryWeapon, primaryInfo, ammo );
  481. int primaryWeaponCost = primaryInfo->GetWeaponPrice();
  482. int primaryAmmoCost = GetCSAmmoDef()->GetCost( primaryInfo->GetPrimaryAmmoType() );
  483. int primaryAmmoBuySize = GetCSAmmoDef()->GetBuySize( primaryInfo->GetPrimaryAmmoType() );
  484. cost += primaryWeaponCost;
  485. cost += primaryAmmoCost * primaryClips;
  486. ammo[primaryInfo->GetPrimaryAmmoType()] += primaryClips * primaryAmmoBuySize;
  487. ws.m_primaryWeapon = m_primaryWeapon;
  488. ws.m_primaryWeapon.SetAmmoAmount( primaryClips );
  489. ws.m_primaryWeapon.SetAmmoType( AMMO_CLIPS );
  490. ws.m_primaryWeapon.SetFillAmmo( false );
  491. }
  492. //-------------------------------------------------------------------------
  493. // secondary weapon
  494. const CCSWeaponInfo *secondaryInfo = GetWeaponInfo( m_secondaryWeapon.GetCSWeaponID() );
  495. if ( secondaryInfo )
  496. {
  497. int secondaryClips = CalcClipsNeeded( &m_secondaryWeapon, secondaryInfo, ammo );
  498. int secondaryWeaponCost = secondaryInfo->GetWeaponPrice();
  499. int secondaryAmmoCost = GetCSAmmoDef()->GetCost( secondaryInfo->GetPrimaryAmmoType() );
  500. int secondaryAmmoBuySize = GetCSAmmoDef()->GetBuySize( secondaryInfo->GetPrimaryAmmoType() );
  501. cost += secondaryWeaponCost;
  502. cost += secondaryAmmoCost * secondaryClips;
  503. ammo[ secondaryInfo->GetPrimaryAmmoType() ] += secondaryClips * secondaryAmmoBuySize;
  504. ws.m_secondaryWeapon = m_secondaryWeapon;
  505. ws.m_secondaryWeapon.SetAmmoAmount( secondaryClips );
  506. ws.m_secondaryWeapon.SetAmmoType( AMMO_CLIPS );
  507. ws.m_secondaryWeapon.SetFillAmmo( false );
  508. }
  509. //-------------------------------------------------------------------------
  510. // equipment
  511. if ( m_armor )
  512. {
  513. cost += (m_helmet) ? (iKevlarPrice + iHelmetPrice) : iKevlarPrice;
  514. ws.m_armor = m_armor;
  515. ws.m_helmet = m_helmet;
  516. }
  517. if ( m_smokeGrenade )
  518. {
  519. const CCSWeaponInfo *pInfo = GetWeaponInfo( WEAPON_SMOKEGRENADE );
  520. cost += ( pInfo ) ? pInfo->GetWeaponPrice() : 0;
  521. ws.m_smokeGrenade = m_smokeGrenade;
  522. }
  523. if ( m_HEGrenade )
  524. {
  525. const CCSWeaponInfo *pInfo = GetWeaponInfo( WEAPON_HEGRENADE );
  526. cost += ( pInfo ) ? pInfo->GetWeaponPrice() : 0;
  527. ws.m_HEGrenade = m_HEGrenade;
  528. }
  529. const CCSWeaponInfo *pInfo = GetWeaponInfo( WEAPON_FLASHBANG );
  530. cost += ( pInfo ) ? pInfo->GetWeaponPrice() * m_flashbangs : 0;
  531. ws.m_flashbangs = m_flashbangs;
  532. if ( m_defuser )
  533. {
  534. cost += ITEM_PRICE_DEFUSEKIT;
  535. ws.m_defuser = m_defuser;
  536. }
  537. if ( m_nightvision )
  538. {
  539. cost += iNVGPrice;
  540. ws.m_nightvision = m_nightvision;
  541. }
  542. }
  543. //--------------------------------------------------------------------------------------------------------------
  544. /**
  545. * Convenience function that wraps GetFromScratch() and discards the generated WeaponSet. Returns the cost
  546. * to buy the full WeaponSet from scratch.
  547. */
  548. int WeaponSet::FullCost() const
  549. {
  550. WeaponSet fullSet;
  551. int cost = 0;
  552. GetFromScratch( cost, fullSet );
  553. return cost;
  554. }
  555. //--------------------------------------------------------------------------------------------------------------
  556. /**
  557. * Generates a list of buy commands that will buy the current WeaponSet.
  558. */
  559. void WeaponSet::GenerateBuyCommands( char command[BUY_PRESET_COMMAND_LEN] ) const
  560. {
  561. command[0] = 0;
  562. char *tmp = command;
  563. int remainder = BUY_PRESET_COMMAND_LEN;
  564. int i;
  565. CSWeaponID primaryID = GetClientWeaponID( true ); ///< The local player's current primary weapon
  566. CSWeaponID secondaryID = GetClientWeaponID( false ); ///< The local player's current pistol
  567. //-------------------------------------------------------------------------
  568. // Primary weapon
  569. CSWeaponID weaponID = m_primaryWeapon.GetCSWeaponID();
  570. if ( weaponID == WEAPON_NONE )
  571. weaponID = primaryID;
  572. const CCSWeaponInfo *primaryInfo = GetWeaponInfo( weaponID );
  573. if ( primaryInfo )
  574. {
  575. if ( weaponID != primaryID )
  576. {
  577. tmp = BufPrintf( tmp, remainder, "buy %s\n", WeaponIDToAlias(weaponID) );
  578. }
  579. for ( i=0; i<m_primaryWeapon.GetAmmoAmount(); ++i )
  580. {
  581. tmp = BufPrintf( tmp, remainder, "buyammo1\n" );
  582. }
  583. }
  584. //-------------------------------------------------------------------------
  585. // secondary weapon
  586. weaponID = m_secondaryWeapon.GetCSWeaponID();
  587. if ( weaponID == WEAPON_NONE )
  588. weaponID = secondaryID;
  589. const CCSWeaponInfo *secondaryInfo = GetWeaponInfo( weaponID );
  590. if ( secondaryInfo )
  591. {
  592. if ( weaponID != secondaryID )
  593. {
  594. tmp = BufPrintf( tmp, remainder, "buy %s\n", WeaponIDToAlias(weaponID) );
  595. }
  596. for ( i=0; i<m_secondaryWeapon.GetAmmoAmount(); ++i )
  597. {
  598. tmp = BufPrintf( tmp, remainder, "buyammo2\n" );
  599. }
  600. }
  601. //-------------------------------------------------------------------------
  602. // equipment
  603. if ( m_armor )
  604. {
  605. if ( m_helmet )
  606. {
  607. tmp = BufPrintf( tmp, remainder, "buy vesthelm\n" );
  608. }
  609. else
  610. {
  611. tmp = BufPrintf( tmp, remainder, "buy vest\n" );
  612. }
  613. }
  614. if ( m_smokeGrenade )
  615. {
  616. tmp = BufPrintf( tmp, remainder, "buy smokegrenade\n" );
  617. }
  618. if ( m_HEGrenade )
  619. {
  620. tmp = BufPrintf( tmp, remainder, "buy hegrenade\n" );
  621. }
  622. for ( i=0; i<m_flashbangs; ++i )
  623. {
  624. tmp = BufPrintf( tmp, remainder, "buy flashbang\n" );
  625. }
  626. if ( m_defuser )
  627. {
  628. tmp = BufPrintf( tmp, remainder, "buy defuser\n" );
  629. }
  630. if ( m_nightvision )
  631. {
  632. tmp = BufPrintf( tmp, remainder, "buy nvgs\n" );
  633. }
  634. }
  635. //--------------------------------------------------------------------------------------------------------------
  636. //--------------------------------------------------------------------------------------------------------------
  637. BuyPreset::BuyPreset()
  638. {
  639. SetName( L"" );
  640. }
  641. //--------------------------------------------------------------------------------------------------------------
  642. BuyPreset::BuyPreset( const BuyPreset& other )
  643. {
  644. *this = other;
  645. }
  646. //--------------------------------------------------------------------------------------------------------------
  647. BuyPreset::~BuyPreset()
  648. {
  649. }
  650. //--------------------------------------------------------------------------------------------------------------
  651. void BuyPreset::SetName( const wchar_t *name )
  652. {
  653. wcsncpy( m_name, name, MaxBuyPresetName );
  654. if ( m_name[0] == 0 )
  655. {
  656. const wchar_t * defaultName = g_pVGuiLocalize->Find( "#Cstrike_BuyPresetBlank" );
  657. if ( defaultName )
  658. {
  659. wcsncpy( m_name, defaultName, MaxBuyPresetName );
  660. }
  661. }
  662. m_name[MaxBuyPresetName-1] = 0;
  663. }
  664. //--------------------------------------------------------------------------------------------------------------
  665. // Parse KeyValues string into a BuyPresetWeapon vector
  666. static void ParseWeaponString( const char *str, BuyPresetWeaponList& weapons, bool isPrimary )
  667. {
  668. weapons.RemoveAll();
  669. if ( !str )
  670. return;
  671. const char *remainder = SharedParse( str );
  672. const char *token;
  673. const int BufLen = 32;
  674. char tmpBuf[BufLen];
  675. char weaponBuf[BufLen];
  676. char clipModifier;
  677. int numClips;
  678. while ( remainder )
  679. {
  680. token = SharedGetToken();
  681. if ( !token || strlen(token) >= BufLen )
  682. return;
  683. Q_strncpy( tmpBuf, token, BufLen );
  684. tmpBuf[BufLen - 1] = 0;
  685. char *tmp = tmpBuf;
  686. while ( *tmp )
  687. {
  688. if ( *tmp == '/' )
  689. {
  690. *tmp = ' ';
  691. }
  692. ++tmp;
  693. }
  694. // sscanf is safe, since the size of the string being scanned is at least as small as any individual destination buffer
  695. if ( sscanf( tmpBuf, "%s %d%c", weaponBuf, &numClips, &clipModifier ) != 3 )
  696. return;
  697. CSWeaponID weaponID = AliasToWeaponID( weaponBuf );
  698. if ( weaponID != WEAPON_NONE )
  699. {
  700. const CCSWeaponInfo *info = GetWeaponInfo( weaponID );
  701. if ( info )
  702. {
  703. int maxRounds = GetCSAmmoDef()->MaxCarry( info->GetPrimaryAmmoType(), C_CSPlayer::GetLocalCSPlayer() );
  704. int buySize = GetCSAmmoDef()->GetBuySize( info->GetPrimaryAmmoType() );
  705. numClips = MIN(ceil(maxRounds/(float)buySize), MAX(0, numClips));
  706. if ( ((!isPrimary) ^ IsPrimaryWeapon( weaponID )) == 0 )
  707. return;
  708. }
  709. else
  710. {
  711. numClips = MIN(NUM_CLIPS_FOR_CURRENT, MAX(0, numClips));
  712. }
  713. }
  714. else
  715. {
  716. numClips = MIN(NUM_CLIPS_FOR_CURRENT, MAX(0, numClips));
  717. }
  718. BuyPresetWeapon weapon( weaponID );
  719. weapon.SetAmmoType( AMMO_CLIPS );
  720. weapon.SetAmmoAmount( numClips );
  721. weapon.SetFillAmmo( (clipModifier == '+') );
  722. weapons.AddToTail( weapon );
  723. remainder = SharedParse( remainder );
  724. }
  725. }
  726. //--------------------------------------------------------------------------------------------------------------
  727. /**
  728. * Populates data from a KeyValues structure, for loading
  729. */
  730. void BuyPreset::Parse( KeyValues *data )
  731. {
  732. m_name[0] = 0;
  733. m_weaponList.RemoveAll();
  734. if (!data)
  735. return;
  736. //-------------------------------------------------------------------------
  737. // Read name
  738. wcsncpy( m_name, data->GetWString( "PresetName", L""), MaxBuyPresetName );
  739. m_name[ MaxBuyPresetName - 1 ] = 0;
  740. PRESET_DEBUG( "Parsing Buy Preset %ls\n", m_name );
  741. int version = data->GetInt( "Version", 0 );
  742. if ( version < 4 || version > 4 )
  743. {
  744. PRESET_DEBUG( "Invalid preset version %d\n", version );
  745. return;
  746. }
  747. const char *primaryString = data->GetString( "Primary", NULL );
  748. const char *secondaryString = data->GetString( "Secondary", NULL );
  749. const char *equipmentString = data->GetString( "Equipment", NULL );
  750. CUtlVector< BuyPresetWeapon > weapons;
  751. ParseWeaponString( primaryString, weapons, true );
  752. WeaponSet ws;
  753. if ( weapons.Count() )
  754. ws.m_primaryWeapon = weapons[0];
  755. weapons.RemoveAll();
  756. ParseWeaponString( secondaryString, weapons, false );
  757. if ( weapons.Count() )
  758. ws.m_secondaryWeapon = weapons[0];
  759. // parse equipment
  760. if ( equipmentString )
  761. {
  762. const char *remainder = SharedParse( equipmentString );
  763. const char *token;
  764. const int BufLen = 32;
  765. char tmpBuf[BufLen];
  766. char itemBuf[BufLen];
  767. int intVal;
  768. while ( remainder )
  769. {
  770. token = SharedGetToken();
  771. if ( !token || Q_strlen(token) >= BufLen )
  772. break;
  773. Q_strncpy( tmpBuf, token, BufLen );
  774. tmpBuf[BufLen - 1] = 0;
  775. char *tmp = tmpBuf;
  776. while ( *tmp )
  777. {
  778. if ( *tmp == '/' )
  779. {
  780. *tmp = ' ';
  781. }
  782. ++tmp;
  783. }
  784. // sscanf is safe, since the size of the string being scanned is at least as small as any individual destination buffer
  785. if ( sscanf( tmpBuf, "%s %d", itemBuf, &intVal ) != 2 )
  786. break;
  787. if ( !strcmp( itemBuf, "vest" ) )
  788. {
  789. ws.m_armor = (intVal > 0) ? 100 : 0;
  790. ws.m_helmet = false;
  791. }
  792. else if ( !strcmp( itemBuf, "vesthelm" ) )
  793. {
  794. ws.m_armor = (intVal > 0) ? 100 : 0;
  795. ws.m_helmet = true;
  796. }
  797. else if ( !strcmp( itemBuf, "defuser" ) )
  798. {
  799. ws.m_defuser = (intVal > 0);
  800. }
  801. else if ( !strcmp( itemBuf, "nvgs" ) )
  802. {
  803. ws.m_nightvision = (intVal > 0);
  804. }
  805. else if ( !strcmp( itemBuf, "sgren" ) )
  806. {
  807. ws.m_smokeGrenade = (intVal > 0);
  808. }
  809. else if ( !strcmp( itemBuf, "hegren" ) )
  810. {
  811. ws.m_HEGrenade = (intVal > 0);
  812. }
  813. else if ( !strcmp( itemBuf, "flash" ) )
  814. {
  815. ws.m_flashbangs = MIN( 2, MAX( 0, intVal ) );
  816. }
  817. remainder = SharedParse( remainder );
  818. }
  819. m_weaponList.AddToTail( ws );
  820. }
  821. }
  822. //--------------------------------------------------------------------------------------------------------------
  823. // Build a string of weapons+ammo for KeyValues saving/loading
  824. static const char* ConstructWeaponString( const BuyPresetWeapon& weapon )
  825. {
  826. const int WeaponLen = 1024;
  827. static char weaponString[WeaponLen];
  828. weaponString[0] = 0;
  829. int remainder = WeaponLen;
  830. char *tmp = weaponString;
  831. tmp = BufPrintf( tmp, remainder, "%s/%d%c",
  832. WeaponIDToAlias( weapon.GetCSWeaponID() ),
  833. weapon.GetAmmoAmount(),
  834. (weapon.GetFillAmmo()) ? '+' : '=' );
  835. return weaponString;
  836. }
  837. //--------------------------------------------------------------------------------------------------------------
  838. /**
  839. * Fills in a KeyValues structure with data, for saving
  840. */
  841. void BuyPreset::Save( KeyValues *data )
  842. {
  843. //-------------------------------------------------------------------------
  844. // Save name and version
  845. KeyValues *presetKey = data->CreateNewKey();
  846. presetKey->SetWString( "PresetName", m_name );
  847. /**
  848. * Version History
  849. * ---------------
  850. *
  851. * PRE-RELEASE
  852. * 1. 2/2004
  853. * First-pass implementation. Allowed for multiple weapons per fallback.
  854. * Individual items could be marked optional. Cash reserve could be specified.
  855. * 2. 3/2004
  856. * Second-pass implementation. Removed multiple weapons per fallback. The
  857. * pistol could be marked optional. A preset could be marked as 'use for
  858. * Autobuy'.
  859. *
  860. * CZ RELEASE
  861. * 3. 4/22/2004
  862. * The first released version. Removed optional/required status. Also
  863. * removed the cash reserve and autobuy status. Corresponds to streamlined
  864. * editing interface mocked up by Greg Coomer.
  865. *
  866. * CS:S RELEASE
  867. * 4. 3/10/2004
  868. * Removed fallbacks to correspond to new UI.
  869. */
  870. presetKey->SetInt( "Version", 4 );
  871. if ( m_weaponList.Count() > 0 )
  872. {
  873. //-------------------------------------------------------------------------
  874. // Save a fallback WeaponSet
  875. const WeaponSet& ws = m_weaponList[0];
  876. presetKey->SetString( "Primary", ConstructWeaponString( ws.m_primaryWeapon ) );
  877. presetKey->SetString( "Secondary", ConstructWeaponString( ws.m_secondaryWeapon ) );
  878. presetKey->SetString( "Equipment",
  879. SharedVarArgs("vest%s/%d flash/%d sgren/%d hegren/%d defuser/%d nvgs/%d",
  880. (ws.m_helmet)?"helm":"", ws.m_armor,
  881. ws.m_flashbangs,
  882. ws.m_smokeGrenade,
  883. ws.m_HEGrenade,
  884. ws.m_defuser,
  885. ws.m_nightvision
  886. ) );
  887. }
  888. }
  889. //--------------------------------------------------------------------------------------------------------------
  890. /**
  891. * Calculates the full cost of the preset, which is exactly the full cost of the first fallback WeaponSet
  892. */
  893. int BuyPreset::FullCost() const
  894. {
  895. if ( m_weaponList.Count() == 0 )
  896. return 0;
  897. return m_weaponList[0].FullCost();
  898. }
  899. //--------------------------------------------------------------------------------------------------------------
  900. /**
  901. * Returns the specified fallback WeaponSet, or NULL if it doesn't exist
  902. */
  903. const WeaponSet * BuyPreset::GetSet( int index ) const
  904. {
  905. if ( index < 0 || index >= m_weaponList.Count() )
  906. return NULL;
  907. return &(m_weaponList[index]);
  908. }
  909. //--------------------------------------------------------------------------------------------------------------
  910. /**
  911. * Deletes a fallback
  912. */
  913. void BuyPreset::DeleteSet( int index )
  914. {
  915. if ( index < 0 || index >= m_weaponList.Count() )
  916. return;
  917. m_weaponList.Remove( index );
  918. }
  919. //--------------------------------------------------------------------------------------------------------------
  920. /**
  921. * Switches the order of two fallbacks
  922. */
  923. void BuyPreset::SwapSet( int firstIndex, int secondIndex )
  924. {
  925. if ( firstIndex < 0 || firstIndex >= m_weaponList.Count() )
  926. return;
  927. if ( secondIndex < 0 || secondIndex >= m_weaponList.Count() )
  928. return;
  929. WeaponSet tmpSet = m_weaponList[firstIndex];
  930. m_weaponList[firstIndex] = m_weaponList[secondIndex];
  931. m_weaponList[secondIndex] = tmpSet;
  932. }
  933. //--------------------------------------------------------------------------------------------------------------
  934. /**
  935. * Replaces a fallback with the supplied WeaponSet
  936. */
  937. void BuyPreset::ReplaceSet( int index, const WeaponSet& weaponSet )
  938. {
  939. if ( index < 0 || index > m_weaponList.Count() )
  940. return;
  941. if ( index == m_weaponList.Count() )
  942. {
  943. m_weaponList.AddToTail( weaponSet );
  944. }
  945. else
  946. {
  947. m_weaponList[index] = weaponSet;
  948. }
  949. }
  950. //--------------------------------------------------------------------------------------------------------------