Team Fortress 2 Source Code as on 22/4/2020
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.

1195 lines
34 KiB

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