summaryrefslogtreecommitdiff
path: root/game/client/cstrike/buy_presets
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/cstrike/buy_presets
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/client/cstrike/buy_presets')
-rw-r--r--game/client/cstrike/buy_presets/buy_preset.cpp1195
-rw-r--r--game/client/cstrike/buy_presets/buy_preset_debug.cpp65
-rw-r--r--game/client/cstrike/buy_presets/buy_preset_debug.h25
-rw-r--r--game/client/cstrike/buy_presets/buy_preset_weapon_info.cpp364
-rw-r--r--game/client/cstrike/buy_presets/buy_presets.cpp454
-rw-r--r--game/client/cstrike/buy_presets/buy_presets.h276
6 files changed, 2379 insertions, 0 deletions
diff --git a/game/client/cstrike/buy_presets/buy_preset.cpp b/game/client/cstrike/buy_presets/buy_preset.cpp
new file mode 100644
index 0000000..369fccc
--- /dev/null
+++ b/game/client/cstrike/buy_presets/buy_preset.cpp
@@ -0,0 +1,1195 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+
+#include "buy_preset_debug.h"
+#include "buy_presets.h"
+#include "weapon_csbase.h"
+#include "cs_ammodef.h"
+#include "cs_gamerules.h"
+#include "cstrike/bot/shared_util.h"
+#include "vgui/ILocalize.h"
+#include <vgui_controls/Controls.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+extern ConVar cl_rebuy;
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns true if the local player is a CT and the map is a defuse map.
+ */
+static bool CanBuyDefuser()
+{
+ C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
+ return ( pPlayer && pPlayer->GetTeamNumber() == TEAM_CT && CSGameRules()->IsBombDefuseMap() );
+}
+
+
+void BuyPresetManager::GetCurrentLoadout( WeaponSet *weaponSet )
+{
+ if ( !weaponSet )
+ return;
+
+ C_CSPlayer *player = C_CSPlayer::GetLocalCSPlayer();
+ if ( !player )
+ return;
+
+ CWeaponCSBase *pWeapon;
+ const CCSWeaponInfo *pInfo;
+
+ int ammo[MAX_AMMO_TYPES];
+ memset( ammo, 0, sizeof(ammo) );
+ FillClientAmmo( ammo );
+
+ weaponSet->Reset();
+
+ // Grab current armor values
+ weaponSet->m_armor = ( player->ArmorValue() > 0 ) ? 100 : 0;
+ weaponSet->m_helmet = player->HasHelmet();
+
+ // Grab current smoke grenade
+ pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_SMOKEGRENADE ));
+ pInfo = GetWeaponInfo( WEAPON_SMOKEGRENADE );
+ int ammoType = (pInfo)?pInfo->iAmmoType:0;
+ weaponSet->m_smokeGrenade = (pWeapon && player->GetAmmoCount( ammoType ));
+
+ // Grab current HE grenade
+ pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_HEGRENADE ));
+ pInfo = GetWeaponInfo( WEAPON_HEGRENADE );
+ ammoType = (pInfo)?pInfo->iAmmoType:0;
+ weaponSet->m_HEGrenade = (pWeapon && player->GetAmmoCount( ammoType ));
+
+ // Grab current flashbangs
+ pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_FLASHBANG ));
+ pInfo = GetWeaponInfo( WEAPON_FLASHBANG );
+ ammoType = (pInfo)?pInfo->iAmmoType:0;
+ weaponSet->m_flashbangs = (!pWeapon) ? 0 : player->GetAmmoCount( ammoType );
+
+ // Grab current equipment
+ weaponSet->m_defuser = player->HasDefuser();
+ weaponSet->m_nightvision = player->HasNightVision();
+
+ // Grab current primary weapon
+ CSWeaponID primaryID = GetClientWeaponID( true ); ///< The local player's current primary weapon
+ pInfo = GetWeaponInfo( primaryID );
+ if ( pInfo )
+ {
+ int roundsNeeded = ammo[pInfo->iAmmoType];
+ int buySize = GetCSAmmoDef()->GetBuySize( pInfo->iAmmoType );
+ int numClips = ceil(roundsNeeded/(float)buySize);
+
+ weaponSet->m_primaryWeapon.SetWeaponID( primaryID );
+ weaponSet->m_primaryWeapon.SetAmmoType( AMMO_CLIPS );
+ weaponSet->m_primaryWeapon.SetAmmoAmount( numClips );
+ weaponSet->m_primaryWeapon.SetFillAmmo( false );
+ }
+
+ // Grab current pistol
+ CSWeaponID secondaryID = GetClientWeaponID( false ); ///< The local player's current pistol
+ pInfo = GetWeaponInfo( secondaryID );
+ if ( pInfo )
+ {
+ int roundsNeeded = ammo[pInfo->iAmmoType];
+ int buySize = GetCSAmmoDef()->GetBuySize( pInfo->iAmmoType );
+ int numClips = ceil(roundsNeeded/(float)buySize);
+
+ weaponSet->m_secondaryWeapon.SetWeaponID( secondaryID );
+ weaponSet->m_secondaryWeapon.SetAmmoType( AMMO_CLIPS );
+ weaponSet->m_secondaryWeapon.SetAmmoAmount( numClips );
+ weaponSet->m_secondaryWeapon.SetFillAmmo( false );
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------------------------
+WeaponSet::WeaponSet()
+{
+ Reset();
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Sets reasonable defaults for an empty weapon set: no primary/secondary weapons, no equipment, and
+ * everything optional.
+ */
+void WeaponSet::Reset()
+{
+ BuyPresetWeapon blank;
+ m_primaryWeapon = blank;
+ m_secondaryWeapon = blank;
+
+ m_armor = 0;
+ m_helmet = false;
+ m_smokeGrenade = false;
+ m_HEGrenade = false;
+ m_flashbangs = 0;
+ m_defuser = false;
+ m_nightvision = false;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Constructs a WeaponSet containing everything that could be purchased right now. The cost is also
+ * returned, and is -1 if the set is not purchasable (as opposed to completely purchased already, in
+ * which case cost is 0.)
+ */
+void WeaponSet::GetCurrent( int& cost, WeaponSet& ws ) const
+{
+ cost = -1;
+ ws.Reset();
+
+ if ( !engine->IsConnected() )
+ return;
+
+ C_CSPlayer *player = CCSPlayer::GetLocalCSPlayer();
+ if ( !player )
+ return;
+
+ if ( player->GetTeamNumber() != TEAM_CT && player->GetTeamNumber() != TEAM_TERRORIST )
+ return;
+
+ // we're connected, and a valid team, so we can purchase
+ cost = 0;
+
+ if ( player->IsVIP() )
+ return; // can't buy anything as the VIP
+
+ int iHelmetPrice = HELMET_PRICE;
+ int iKevlarPrice = KEVLAR_PRICE;
+ int iNVGPrice = NVG_PRICE;
+
+ if ( CSGameRules()->IsBlackMarket() )
+ {
+ iHelmetPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_ASSAULTSUIT ) - CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_KEVLAR );
+ iKevlarPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_KEVLAR );
+ iNVGPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_NVG );
+ }
+
+ //-------------------------------------------------------------------------
+ //-------------------------------------------------------------------------
+ // Buy any required items
+
+ //-------------------------------------------------------------------------
+ // Armor
+ if ( m_armor > player->ArmorValue() )
+ {
+ cost += iKevlarPrice;
+ ws.m_armor = 100;
+ }
+
+ if ( (m_helmet && m_armor > 0) && !player->HasHelmet() )
+ {
+ cost += iHelmetPrice;
+ ws.m_armor = 100;
+ ws.m_helmet = true;
+ }
+
+ CWeaponCSBase *pWeapon;
+ CCSWeaponInfo *pInfo;
+
+ //-------------------------------------------------------------------------
+ // Smoke grenade
+ pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_SMOKEGRENADE ));
+ pInfo = GetWeaponInfo( WEAPON_SMOKEGRENADE );
+ int ammoType = (pInfo)?pInfo->iAmmoType:0;
+
+ bool hasSmokeGrenade = (pWeapon && player->GetAmmoCount( ammoType ));
+ if ( m_smokeGrenade && !hasSmokeGrenade )
+ {
+ cost += pInfo->GetWeaponPrice();
+ ws.m_smokeGrenade = true;
+ hasSmokeGrenade = true;
+ }
+
+ //-------------------------------------------------------------------------
+ // HE grenade
+ pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_HEGRENADE ));
+ pInfo = GetWeaponInfo( WEAPON_HEGRENADE );
+ ammoType = (pInfo)?pInfo->iAmmoType:0;
+
+ bool hasHEGrenade = (pWeapon && player->GetAmmoCount( ammoType ));
+ if ( m_HEGrenade && !hasHEGrenade )
+ {
+ cost += pInfo->GetWeaponPrice();
+ ws.m_HEGrenade = true;
+ hasHEGrenade = true;
+ }
+
+ //-------------------------------------------------------------------------
+ // Flashbang grenades
+ pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_FLASHBANG ));
+ pInfo = GetWeaponInfo( WEAPON_FLASHBANG );
+ ammoType = (pInfo)?pInfo->iAmmoType:0;
+
+ int numFlashbangs = (!pWeapon) ? 0 : player->GetAmmoCount( ammoType );
+ if ( m_flashbangs && numFlashbangs < m_flashbangs )
+ {
+ int count = m_flashbangs - numFlashbangs;
+ cost += pInfo->GetWeaponPrice() * count;
+ ws.m_flashbangs = count;
+ numFlashbangs += count;
+ }
+
+ //-------------------------------------------------------------------------
+ // defuser
+ if ( m_defuser && player->GetTeamNumber() == TEAM_CT && !player->HasDefuser() && CanBuyDefuser() )
+ {
+ cost += DEFUSEKIT_PRICE;
+ ws.m_defuser = true;
+ }
+
+ //-------------------------------------------------------------------------
+ // nightvision goggles
+ if ( m_nightvision && !player->HasNightVision() )
+ {
+ cost += iNVGPrice;
+ ws.m_nightvision = true;
+ }
+
+ //-------------------------------------------------------------------------
+ // Construct a list of weapons to buy from.
+ CSWeaponID primaryID = GetClientWeaponID( true ); ///< The local player's current primary weapon
+ CSWeaponID secondaryID = GetClientWeaponID( false ); ///< The local player's current pistol
+
+ BuyPresetWeaponList primaryWeapons;
+ primaryWeapons.AddToTail( m_primaryWeapon );
+
+ BuyPresetWeaponList secondaryWeapons;
+ secondaryWeapons.AddToTail( m_secondaryWeapon );
+
+ /**
+ * Determine best weapons & ammo to purchase
+ *
+ * For each primary weapon in the list, do the following until one satisfies the current money situation:
+ * 1. buy primary weapon and its ammo (NEED TO WATCH FOR WEAPONS PLAYER CANNOT BUY!!!)
+ * 2. for each non-optional secondary weapon (if any),
+ * try to buy it and its ammo
+ * 3. when a set can be purchased, add it to the total cost and stop processing
+ * If we can't purchase the primary weapon, or a required secondary weapon, the WeaponSet can't be bought,
+ * so we return -1.
+ *
+ * To find if a weapon is owned, we can compare it's weaponID to either primaryID or secondaryID from above.
+ */
+
+ int currentCost = cost;
+ int ammo[MAX_AMMO_TYPES];
+ memset( ammo, 0, sizeof(ammo) );
+ FillClientAmmo( ammo );
+ const BuyPresetWeapon *primaryWeaponToBuy = NULL;
+ const BuyPresetWeapon *secondaryWeaponToBuy = NULL;
+ int primaryClipsToBuy = 0;
+ int secondaryClipsToBuy = 0;
+ int currentNonWeaponCost = currentCost;
+ bool doneBuyingWeapons = false;
+
+ int currentCash = player->GetAccount();
+
+ //-------------------------------------------------------------------------
+ // Try to buy the primary weapon
+ int i;
+ for ( i=0; i<primaryWeapons.Count() && !doneBuyingWeapons; ++i )
+ {
+ const BuyPresetWeapon *primaryWeapon = &primaryWeapons[i];
+ primaryWeaponToBuy = NULL;
+ int primaryClips = 0;
+ primaryClipsToBuy = 0;
+ currentCost = currentNonWeaponCost;
+ FillClientAmmo( ammo );
+
+ CSWeaponID weaponID = primaryWeapon->GetWeaponID();
+ if ( weaponID == WEAPON_NONE )
+ weaponID = primaryID;
+
+ CCSWeaponInfo *primaryInfo = GetWeaponInfo( weaponID );
+ int primaryAmmoCost = 0;
+ int primaryAmmoBuySize = 0;
+ if ( primaryInfo )
+ {
+ primaryClips = CalcClipsNeeded( primaryWeapon, primaryInfo, ammo );
+ int primaryWeaponCost = ( weaponID != primaryID ) ? primaryInfo->GetWeaponPrice() : 0;
+ primaryAmmoCost = GetCSAmmoDef()->GetCost( primaryInfo->iAmmoType );
+ primaryAmmoBuySize = GetCSAmmoDef()->GetBuySize( primaryInfo->iAmmoType );
+ currentCost += primaryWeaponCost;
+ currentCost += primaryAmmoCost * primaryClips;
+ ammo[primaryInfo->iAmmoType] += primaryClips * primaryAmmoBuySize;
+
+ CURRENTCOST_DEBUG("\t%5.5d (%s)\n", primaryWeaponCost, WeaponIDToAlias( weaponID ));
+ CURRENTCOST_DEBUG("\t%5.5d (ammo x %d)\n", primaryAmmoCost * primaryClips, primaryClips);
+ if ( currentCash < currentCost )
+ {
+ currentCost = currentNonWeaponCost; // can't afford it, so try the next weapon
+ continue;
+ }
+
+ // check for as_* maps, and CTs buying AK47s, etc
+ if ( !CanBuyWeapon(primaryID, secondaryID, weaponID) && weaponID != primaryID )
+ {
+ currentCost = currentNonWeaponCost; // can't buy it, so try the next weapon
+ continue;
+ }
+
+ primaryWeaponToBuy = primaryWeapon;
+ primaryClipsToBuy = primaryClips;
+ }
+
+ if ( !secondaryWeapons.Count() )
+ {
+ CURRENTCOST_DEBUG("\t\t\tDone buying weapons (no secondary)\n");
+ break;
+ }
+
+ //-------------------------------------------------------------------------
+ // Try to buy a (required) secondary weapon to go with primaryWeaponToBuy.
+ // If not, reset cost to not reflect either weapon, so we can look at buying
+ // the next primary weapon in the list.
+ int j;
+ for ( j=0; j<secondaryWeapons.Count(); ++j )
+ {
+ const BuyPresetWeapon *secondaryWeapon = &secondaryWeapons[j];
+
+ // reset ammo counts and cost
+ secondaryWeaponToBuy = NULL;
+ secondaryClipsToBuy = 0;
+ currentCost = currentNonWeaponCost;
+ FillClientAmmo( ammo );
+
+ // add in ammo we're already buying for the primary weapon to the client's actual ammo
+ if ( primaryInfo )
+ {
+ currentCost += ( weaponID != primaryID ) ? primaryInfo->GetWeaponPrice() : 0;
+ currentCost += primaryAmmoCost * primaryClips;
+ ammo[primaryInfo->iAmmoType] += primaryClips * primaryAmmoBuySize;
+ }
+
+ int currentCostWithoutSecondary = currentCost;
+
+ CSWeaponID weaponID = secondaryWeapon->GetWeaponID();
+ if ( weaponID == WEAPON_NONE )
+ weaponID = secondaryID;
+
+ CCSWeaponInfo *secondaryInfo = GetWeaponInfo( weaponID );
+ if ( !secondaryInfo )
+ {
+ currentCost = currentCostWithoutSecondary;
+ continue;
+ }
+
+ int secondaryClips = CalcClipsNeeded( secondaryWeapon, secondaryInfo, ammo );
+ int secondaryWeaponCost = ( weaponID != secondaryID ) ? secondaryInfo->GetWeaponPrice() : 0;
+ int secondaryAmmoCost = GetCSAmmoDef()->GetCost( secondaryInfo->iAmmoType );
+ int secondaryAmmoBuySize = GetCSAmmoDef()->GetBuySize( secondaryInfo->iAmmoType );
+ currentCost += secondaryWeaponCost;
+ currentCost += secondaryAmmoCost * secondaryClips;
+ ammo[secondaryInfo->iAmmoType] += secondaryClips * secondaryAmmoBuySize;
+
+ CURRENTCOST_DEBUG("\t%5.5d (%s)\n", secondaryWeaponCost, WeaponIDToAlias( weaponID ));
+ CURRENTCOST_DEBUG("\t%5.5d (ammo x %d)\n", secondaryAmmoCost * secondaryClips, secondaryClips);
+ if ( currentCash < currentCost )
+ {
+ currentCost = currentCostWithoutSecondary; // can't afford it, so skip
+ continue;
+ }
+
+ // check for as_* maps, and CTs buying elites, etc
+ if ( !CanBuyWeapon(primaryID, secondaryID, weaponID) && weaponID != secondaryID )
+ {
+ currentCost = currentCostWithoutSecondary; // can't buy it, so skip
+ continue;
+ }
+
+ // We found both a primary and secondary weapon to buy. Stop looking for more.
+ secondaryWeaponToBuy = secondaryWeapon;
+ secondaryClipsToBuy = secondaryClips;
+ doneBuyingWeapons = true;
+ CURRENTCOST_DEBUG("\t\t\tDone buying weapons (primary && secondary)\n");
+ break;
+ }
+ }
+
+ // Update our cost to reflect the weapons and ammo purchased
+ cost = currentCost;
+
+ if ( primaryWeaponToBuy )
+ {
+ BuyPresetWeapon weapon = *primaryWeaponToBuy;
+ weapon.SetAmmoAmount( primaryClipsToBuy );
+ weapon.SetAmmoType( AMMO_CLIPS );
+ ws.m_primaryWeapon = weapon;
+ }
+ else
+ {
+ // If we failed to buy a primary weapon, and one was in the list, bail - the player can't afford this WeaponSet.
+ for ( int i=0; i<primaryWeapons.Count(); ++i )
+ {
+ int weaponID = primaryWeapons[i].GetWeaponID();
+ if ( weaponID != WEAPON_NONE )
+ {
+ cost = -1;
+ ws.Reset();
+ return;
+ }
+ }
+ }
+
+ if ( secondaryWeaponToBuy )
+ {
+ BuyPresetWeapon weapon = *secondaryWeaponToBuy;
+ weapon.SetAmmoAmount( secondaryClipsToBuy );
+ weapon.SetAmmoType( AMMO_CLIPS );
+ ws.m_secondaryWeapon = weapon;
+ }
+ else
+ {
+ // If we failed to buy a required secondary weapon, and one was in the list, bail - the player can't afford this WeaponSet.
+ for ( int i=0; i<secondaryWeapons.Count(); ++i )
+ {
+ int weaponID = secondaryWeapons[i].GetWeaponID();
+ if ( weaponID != WEAPON_NONE )
+ {
+ cost = -1;
+ ws.Reset();
+ return;
+ }
+ }
+ }
+
+ //-------------------------------------------------------------------------
+ // now any extra ammo, in rebuy order
+ const char *remainder = SharedParse( cl_rebuy.GetString() );
+ const char *token;
+
+ while ( remainder )
+ {
+ token = SharedGetToken();
+ if ( !token || !*token )
+ return;
+
+ if ( !stricmp( token, "PrimaryAmmo" ) )
+ {
+ if ( primaryWeaponToBuy )
+ {
+ CSWeaponID id = primaryWeaponToBuy->GetWeaponID();
+ if ( id == WEAPON_NONE )
+ id = primaryID;
+
+ if ( primaryWeaponToBuy->GetFillAmmo() )
+ {
+ const CCSWeaponInfo *info = GetWeaponInfo( id );
+ if ( info )
+ {
+ int maxRounds = GetCSAmmoDef()->MaxCarry( info->iAmmoType );
+ int ammoCost = GetCSAmmoDef()->GetCost( info->iAmmoType );
+ int ammoBuySize = GetCSAmmoDef()->GetBuySize( info->iAmmoType );
+ int roundsLeft = maxRounds - ammo[info->iAmmoType];
+ int clipsLeft = (ammoBuySize > 0) ? ceil(roundsLeft / (float)ammoBuySize) : 0;
+ while ( clipsLeft > 0 && cost + ammoCost <= currentCash )
+ {
+ ws.m_primaryWeapon.SetAmmoAmount( ws.m_primaryWeapon.GetAmmoAmount() + 1 );
+ --clipsLeft;
+ ammo[info->iAmmoType] += MIN(maxRounds, ammoBuySize);
+ cost += ammoCost;
+ }
+ }
+ }
+ }
+ }
+ else if ( !stricmp( token, "SecondaryAmmo" ) )
+ {
+ if ( secondaryWeaponToBuy )
+ {
+ if ( secondaryWeaponToBuy->GetFillAmmo() )
+ {
+ CSWeaponID weaponID = secondaryWeaponToBuy->GetWeaponID();
+ if ( weaponID == WEAPON_NONE )
+ weaponID = secondaryID;
+
+ const CCSWeaponInfo *info = GetWeaponInfo( weaponID );
+ if ( info )
+ {
+ int maxRounds = GetCSAmmoDef()->MaxCarry( info->iAmmoType );
+ int ammoCost = GetCSAmmoDef()->GetCost( info->iAmmoType );
+ int ammoBuySize = GetCSAmmoDef()->GetBuySize( info->iAmmoType );
+ int roundsLeft = maxRounds - ammo[info->iAmmoType];
+ int clipsLeft = (ammoBuySize > 0) ? ceil(roundsLeft / (float)ammoBuySize) : 0;
+ while ( clipsLeft > 0 && cost + ammoCost <= currentCash )
+ {
+ ws.m_secondaryWeapon.SetAmmoAmount( ws.m_secondaryWeapon.GetAmmoAmount() + 1 );
+ --clipsLeft;
+ ammo[info->iAmmoType] += MIN(maxRounds, ammoBuySize);
+ cost += ammoCost;
+ }
+ }
+ }
+ }
+ }
+
+ remainder = SharedParse( remainder );
+ }
+
+ if ( cost > currentCash )
+ {
+ cost = -1; // can't buy it
+ ws.Reset();
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Constructs a WeaponSet containing everything that can be purchased.
+ * The cost (including extra cash) is also calculated and returned.
+ */
+void WeaponSet::GetFromScratch( int& cost, WeaponSet& ws ) const
+{
+ cost = 0;
+ ws.Reset();
+
+ int ammo[MAX_AMMO_TYPES];
+ memset( ammo, 0, sizeof(ammo) );
+
+ int iHelmetPrice = HELMET_PRICE;
+ int iKevlarPrice = KEVLAR_PRICE;
+ int iNVGPrice = NVG_PRICE;
+
+ if ( CSGameRules()->IsBlackMarket() )
+ {
+ iHelmetPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_ASSAULTSUIT ) - CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_KEVLAR );
+ iKevlarPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_KEVLAR );
+ iNVGPrice = CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_NVG );
+ }
+
+ //-------------------------------------------------------------------------
+ // Primary weapon
+ CCSWeaponInfo *primaryInfo = GetWeaponInfo( m_primaryWeapon.GetWeaponID() );
+ if ( primaryInfo )
+ {
+ int primaryClips = CalcClipsNeeded( &m_primaryWeapon, primaryInfo, ammo );
+ int primaryWeaponCost = primaryInfo->GetWeaponPrice();
+ int primaryAmmoCost = GetCSAmmoDef()->GetCost( primaryInfo->iAmmoType );
+ int primaryAmmoBuySize = GetCSAmmoDef()->GetBuySize( primaryInfo->iAmmoType );
+ cost += primaryWeaponCost;
+ cost += primaryAmmoCost * primaryClips;
+ ammo[primaryInfo->iAmmoType] += primaryClips * primaryAmmoBuySize;
+
+ ws.m_primaryWeapon = m_primaryWeapon;
+ ws.m_primaryWeapon.SetAmmoAmount( primaryClips );
+ ws.m_primaryWeapon.SetAmmoType( AMMO_CLIPS );
+ ws.m_primaryWeapon.SetFillAmmo( false );
+ }
+
+ //-------------------------------------------------------------------------
+ // secondary weapon
+ CCSWeaponInfo *secondaryInfo = GetWeaponInfo( m_secondaryWeapon.GetWeaponID() );
+ if ( secondaryInfo )
+ {
+ int secondaryClips = CalcClipsNeeded( &m_secondaryWeapon, secondaryInfo, ammo );
+ int secondaryWeaponCost = secondaryInfo->GetWeaponPrice();
+ int secondaryAmmoCost = GetCSAmmoDef()->GetCost( secondaryInfo->iAmmoType );
+ int secondaryAmmoBuySize = GetCSAmmoDef()->GetBuySize( secondaryInfo->iAmmoType );
+ cost += secondaryWeaponCost;
+ cost += secondaryAmmoCost * secondaryClips;
+ ammo[secondaryInfo->iAmmoType] += secondaryClips * secondaryAmmoBuySize;
+
+ ws.m_secondaryWeapon = m_secondaryWeapon;
+ ws.m_secondaryWeapon.SetAmmoAmount( secondaryClips );
+ ws.m_secondaryWeapon.SetAmmoType( AMMO_CLIPS );
+ ws.m_secondaryWeapon.SetFillAmmo( false );
+ }
+
+ //-------------------------------------------------------------------------
+ // equipment
+ if ( m_armor )
+ {
+ cost += (m_helmet) ? (iKevlarPrice + iHelmetPrice) : iKevlarPrice;
+ ws.m_armor = m_armor;
+ ws.m_helmet = m_helmet;
+ }
+
+ if ( m_smokeGrenade )
+ {
+ CCSWeaponInfo *pInfo = GetWeaponInfo( WEAPON_SMOKEGRENADE );
+ cost += ( pInfo ) ? pInfo->GetWeaponPrice() : 0;
+ ws.m_smokeGrenade = m_smokeGrenade;
+ }
+
+ if ( m_HEGrenade )
+ {
+ CCSWeaponInfo *pInfo = GetWeaponInfo( WEAPON_HEGRENADE );
+ cost += ( pInfo ) ? pInfo->GetWeaponPrice() : 0;
+ ws.m_HEGrenade = m_HEGrenade;
+ }
+
+ CCSWeaponInfo *pInfo = GetWeaponInfo( WEAPON_FLASHBANG );
+ cost += ( pInfo ) ? pInfo->GetWeaponPrice() * m_flashbangs : 0;
+ ws.m_flashbangs = m_flashbangs;
+
+ if ( m_defuser )
+ {
+ cost += DEFUSEKIT_PRICE;
+ ws.m_defuser = m_defuser;
+ }
+
+ if ( m_nightvision )
+ {
+ cost += iNVGPrice;
+ ws.m_nightvision = m_nightvision;
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Convenience function that wraps GetFromScratch() and discards the generated WeaponSet. Returns the cost
+ * to buy the full WeaponSet from scratch.
+ */
+int WeaponSet::FullCost() const
+{
+ WeaponSet fullSet;
+ int cost = 0;
+ GetFromScratch( cost, fullSet );
+ return cost;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Generates a list of buy commands that will buy the current WeaponSet.
+ */
+void WeaponSet::GenerateBuyCommands( char command[BUY_PRESET_COMMAND_LEN] ) const
+{
+ command[0] = 0;
+ char *tmp = command;
+ int remainder = BUY_PRESET_COMMAND_LEN;
+ int i;
+
+ CSWeaponID primaryID = GetClientWeaponID( true ); ///< The local player's current primary weapon
+ CSWeaponID secondaryID = GetClientWeaponID( false ); ///< The local player's current pistol
+
+ //-------------------------------------------------------------------------
+ // Primary weapon
+ CSWeaponID weaponID = m_primaryWeapon.GetWeaponID();
+ if ( weaponID == WEAPON_NONE )
+ weaponID = primaryID;
+ const CCSWeaponInfo *primaryInfo = GetWeaponInfo( weaponID );
+ if ( primaryInfo )
+ {
+ if ( weaponID != primaryID )
+ {
+ tmp = BufPrintf( tmp, remainder, "buy %s\n", WeaponIDToAlias(weaponID) );
+ }
+ for ( i=0; i<m_primaryWeapon.GetAmmoAmount(); ++i )
+ {
+ tmp = BufPrintf( tmp, remainder, "buyammo1\n" );
+ }
+ }
+
+ //-------------------------------------------------------------------------
+ // secondary weapon
+ weaponID = m_secondaryWeapon.GetWeaponID();
+ if ( weaponID == WEAPON_NONE )
+ weaponID = secondaryID;
+ const CCSWeaponInfo *secondaryInfo = GetWeaponInfo( weaponID );
+ if ( secondaryInfo )
+ {
+ if ( weaponID != secondaryID )
+ {
+ tmp = BufPrintf( tmp, remainder, "buy %s\n", WeaponIDToAlias(weaponID) );
+ }
+ for ( i=0; i<m_secondaryWeapon.GetAmmoAmount(); ++i )
+ {
+ tmp = BufPrintf( tmp, remainder, "buyammo2\n" );
+ }
+ }
+
+ //-------------------------------------------------------------------------
+ // equipment
+ if ( m_armor )
+ {
+ if ( m_helmet )
+ {
+ tmp = BufPrintf( tmp, remainder, "buy vesthelm\n" );
+ }
+ else
+ {
+ tmp = BufPrintf( tmp, remainder, "buy vest\n" );
+ }
+ }
+
+ if ( m_smokeGrenade )
+ {
+ tmp = BufPrintf( tmp, remainder, "buy smokegrenade\n" );
+ }
+
+ if ( m_HEGrenade )
+ {
+ tmp = BufPrintf( tmp, remainder, "buy hegrenade\n" );
+ }
+
+ for ( i=0; i<m_flashbangs; ++i )
+ {
+ tmp = BufPrintf( tmp, remainder, "buy flashbang\n" );
+ }
+
+ if ( m_defuser )
+ {
+ tmp = BufPrintf( tmp, remainder, "buy defuser\n" );
+ }
+
+ if ( m_nightvision )
+ {
+ tmp = BufPrintf( tmp, remainder, "buy nvgs\n" );
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return images out of a subdir, so when the buy preset system resizes the images, they don't resize in the
+ * main buy menu.
+ */
+const char *ImageFnameFromWeaponID( CSWeaponID weaponID, bool isPrimary )
+{
+ switch (weaponID)
+ {
+ case WEAPON_NONE:
+ return "gfx/vgui/defaultweapon";
+ case WEAPON_SCOUT:
+ return "gfx/vgui/scout";
+ case WEAPON_XM1014:
+ return "gfx/vgui/xm1014";
+ case WEAPON_MAC10:
+ return "gfx/vgui/mac10";
+ case WEAPON_AUG:
+ return "gfx/vgui/aug";
+ case WEAPON_UMP45:
+ return "gfx/vgui/ump45";
+ case WEAPON_SG550:
+ return "gfx/vgui/sg550";
+ case WEAPON_GALIL:
+ return "gfx/vgui/galil";
+ case WEAPON_FAMAS:
+ return "gfx/vgui/famas";
+ case WEAPON_AWP:
+ return "gfx/vgui/awp";
+ case WEAPON_MP5NAVY:
+ return "gfx/vgui/mp5";
+ case WEAPON_M249:
+ return "gfx/vgui/m249";
+ case WEAPON_M3:
+ return "gfx/vgui/m3";
+ case WEAPON_M4A1:
+ return "gfx/vgui/m4a1";
+ case WEAPON_TMP:
+ return "gfx/vgui/tmp";
+ case WEAPON_G3SG1:
+ return "gfx/vgui/g3sg1";
+ case WEAPON_SG552:
+ return "gfx/vgui/sg552";
+ case WEAPON_AK47:
+ return "gfx/vgui/ak47";
+ case WEAPON_P90:
+ return "gfx/vgui/p90";
+ case WEAPON_SHIELDGUN:
+ return "gfx/vgui/shield";
+
+ case WEAPON_USP:
+ return "gfx/vgui/usp45";
+ case WEAPON_GLOCK:
+ return "gfx/vgui/glock18";
+ case WEAPON_DEAGLE:
+ return "gfx/vgui/deserteagle";
+ case WEAPON_ELITE:
+ return "gfx/vgui/elites";
+ case WEAPON_P228:
+ return "gfx/vgui/p228";
+ case WEAPON_FIVESEVEN:
+ return "gfx/vgui/fiveseven";
+
+ default:
+ return "";
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------------------------
+BuyPreset::BuyPreset()
+{
+ SetName( L"" );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+BuyPreset::BuyPreset( const BuyPreset& other )
+{
+ *this = other;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+BuyPreset::~BuyPreset()
+{
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+void BuyPreset::SetName( const wchar_t *name )
+{
+ wcsncpy( m_name, name, MaxBuyPresetName );
+ if ( m_name[0] == 0 )
+ {
+ const wchar_t * defaultName = g_pVGuiLocalize->Find( "#Cstrike_BuyPresetBlank" );
+ if ( defaultName )
+ {
+ wcsncpy( m_name, defaultName, MaxBuyPresetName );
+ }
+ }
+ m_name[MaxBuyPresetName-1] = 0;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+// Parse KeyValues string into a BuyPresetWeapon vector
+static void ParseWeaponString( const char *str, BuyPresetWeaponList& weapons, bool isPrimary )
+{
+ weapons.RemoveAll();
+
+ if ( !str )
+ return;
+
+ const char *remainder = SharedParse( str );
+ const char *token;
+
+ const int BufLen = 32;
+ char tmpBuf[BufLen];
+ tmpBuf[0] = '\0';
+ char weaponBuf[BufLen];
+ weaponBuf[0] = '\0';
+ char clipModifier = 0;
+ int numClips = 0;
+
+ while ( remainder )
+ {
+ token = SharedGetToken();
+ if ( !token || strlen(token) >= BufLen )
+ return;
+
+ Q_strncpy( tmpBuf, token, BufLen );
+ tmpBuf[BufLen - 1] = 0;
+ char *tmp = tmpBuf;
+ while ( *tmp )
+ {
+ if ( *tmp == '/' )
+ {
+ *tmp = ' ';
+ }
+ ++tmp;
+ }
+ // sscanf is safe, since the size of the string being scanned is at least as small as any individual destination buffer
+ if ( sscanf( tmpBuf, "%s %d%c", weaponBuf, &numClips, &clipModifier ) != 3 )
+ return;
+
+ CSWeaponID weaponID = AliasToWeaponID( weaponBuf );
+ if ( weaponID != WEAPON_NONE )
+ {
+ const CCSWeaponInfo *info = GetWeaponInfo( weaponID );
+ if ( info )
+ {
+ int maxRounds = GetCSAmmoDef()->MaxCarry( info->iAmmoType );
+ int buySize = GetCSAmmoDef()->GetBuySize( info->iAmmoType );
+ numClips = MIN(ceil(maxRounds/(float)buySize), MAX(0, numClips));
+ if ( ((!isPrimary) ^ IsPrimaryWeapon( weaponID )) == 0 )
+ return;
+ }
+ else
+ {
+ numClips = MIN(NUM_CLIPS_FOR_CURRENT, MAX(0, numClips));
+ }
+ }
+ else
+ {
+ numClips = MIN(NUM_CLIPS_FOR_CURRENT, MAX(0, numClips));
+ }
+
+ BuyPresetWeapon weapon( weaponID );
+ weapon.SetAmmoType( AMMO_CLIPS );
+ weapon.SetAmmoAmount( numClips );
+ weapon.SetFillAmmo( (clipModifier == '+') );
+ weapons.AddToTail( weapon );
+
+ remainder = SharedParse( remainder );
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Populates data from a KeyValues structure, for loading
+ */
+void BuyPreset::Parse( KeyValues *data )
+{
+ m_name[0] = 0;
+ m_weaponList.RemoveAll();
+
+ if (!data)
+ return;
+
+ //-------------------------------------------------------------------------
+ // Read name
+ wcsncpy( m_name, data->GetWString( "PresetName", L""), MaxBuyPresetName );
+ m_name[ MaxBuyPresetName - 1 ] = 0;
+ PRESET_DEBUG( "Parsing Buy Preset %ls\n", m_name );
+
+ int version = data->GetInt( "Version", 0 );
+ if ( version < 4 || version > 4 )
+ {
+ PRESET_DEBUG( "Invalid preset version %d\n", version );
+ return;
+ }
+
+ const char *primaryString = data->GetString( "Primary", NULL );
+ const char *secondaryString = data->GetString( "Secondary", NULL );
+ const char *equipmentString = data->GetString( "Equipment", NULL );
+
+ CUtlVector< BuyPresetWeapon > weapons;
+ ParseWeaponString( primaryString, weapons, true );
+
+ WeaponSet ws;
+ if ( weapons.Count() )
+ ws.m_primaryWeapon = weapons[0];
+
+ weapons.RemoveAll();
+ ParseWeaponString( secondaryString, weapons, false );
+ if ( weapons.Count() )
+ ws.m_secondaryWeapon = weapons[0];
+
+ // parse equipment
+ if ( equipmentString )
+ {
+ const char *remainder = SharedParse( equipmentString );
+ const char *token;
+
+ const int BufLen = 32;
+ char tmpBuf[BufLen];
+ char itemBuf[BufLen];
+ int intVal;
+
+ while ( remainder )
+ {
+ token = SharedGetToken();
+ if ( !token || Q_strlen(token) >= BufLen )
+ break;
+
+ Q_strncpy( tmpBuf, token, BufLen );
+ tmpBuf[BufLen - 1] = 0;
+ char *tmp = tmpBuf;
+ while ( *tmp )
+ {
+ if ( *tmp == '/' )
+ {
+ *tmp = ' ';
+ }
+ ++tmp;
+ }
+ // sscanf is safe, since the size of the string being scanned is at least as small as any individual destination buffer
+ if ( sscanf( tmpBuf, "%s %d", itemBuf, &intVal ) != 2 )
+ break;
+
+ if ( !strcmp( itemBuf, "vest" ) )
+ {
+ ws.m_armor = (intVal > 0) ? 100 : 0;
+ ws.m_helmet = false;
+ }
+ else if ( !strcmp( itemBuf, "vesthelm" ) )
+ {
+ ws.m_armor = (intVal > 0) ? 100 : 0;
+ ws.m_helmet = true;
+ }
+ else if ( !strcmp( itemBuf, "defuser" ) )
+ {
+ ws.m_defuser = (intVal > 0);
+ }
+ else if ( !strcmp( itemBuf, "nvgs" ) )
+ {
+ ws.m_nightvision = (intVal > 0);
+ }
+ else if ( !strcmp( itemBuf, "sgren" ) )
+ {
+ ws.m_smokeGrenade = (intVal > 0);
+ }
+ else if ( !strcmp( itemBuf, "hegren" ) )
+ {
+ ws.m_HEGrenade = (intVal > 0);
+ }
+ else if ( !strcmp( itemBuf, "flash" ) )
+ {
+ ws.m_flashbangs = MIN( 2, MAX( 0, intVal ) );
+ }
+
+ remainder = SharedParse( remainder );
+ }
+
+ m_weaponList.AddToTail( ws );
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+// Build a string of weapons+ammo for KeyValues saving/loading
+static const char* ConstructWeaponString( const BuyPresetWeapon& weapon )
+{
+ const int WeaponLen = 1024;
+ static char weaponString[WeaponLen];
+ weaponString[0] = 0;
+ int remainder = WeaponLen;
+ char *tmp = weaponString;
+
+ tmp = BufPrintf( tmp, remainder, "%s/%d%c",
+ WeaponIDToAlias( weapon.GetWeaponID() ),
+ weapon.GetAmmoAmount(),
+ (weapon.GetFillAmmo()) ? '+' : '=' );
+ return weaponString;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Fills in a KeyValues structure with data, for saving
+ */
+void BuyPreset::Save( KeyValues *data )
+{
+ //-------------------------------------------------------------------------
+ // Save name and version
+ KeyValues *presetKey = data->CreateNewKey();
+ presetKey->SetWString( "PresetName", m_name );
+
+ /**
+ * Version History
+ * ---------------
+ *
+ * PRE-RELEASE
+ * 1. 2/2004
+ * First-pass implementation. Allowed for multiple weapons per fallback.
+ * Individual items could be marked optional. Cash reserve could be specified.
+ * 2. 3/2004
+ * Second-pass implementation. Removed multiple weapons per fallback. The
+ * pistol could be marked optional. A preset could be marked as 'use for
+ * Autobuy'.
+ *
+ * CZ RELEASE
+ * 3. 4/22/2004
+ * The first released version. Removed optional/required status. Also
+ * removed the cash reserve and autobuy status. Corresponds to streamlined
+ * editing interface mocked up by Greg Coomer.
+ *
+ * CS:S RELEASE
+ * 4. 3/10/2004
+ * Removed fallbacks to correspond to new UI.
+ */
+ presetKey->SetInt( "Version", 4 );
+ if ( m_weaponList.Count() > 0 )
+ {
+ //-------------------------------------------------------------------------
+ // Save a fallback WeaponSet
+ const WeaponSet& ws = m_weaponList[0];
+
+ presetKey->SetString( "Primary", ConstructWeaponString( ws.m_primaryWeapon ) );
+ presetKey->SetString( "Secondary", ConstructWeaponString( ws.m_secondaryWeapon ) );
+
+ presetKey->SetString( "Equipment",
+ SharedVarArgs("vest%s/%d flash/%d sgren/%d hegren/%d defuser/%d nvgs/%d",
+ (ws.m_helmet)?"helm":"", ws.m_armor,
+ ws.m_flashbangs,
+ ws.m_smokeGrenade,
+ ws.m_HEGrenade,
+ ws.m_defuser,
+ ws.m_nightvision
+ ) );
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Calculates the full cost of the preset, which is exactly the full cost of the first fallback WeaponSet
+ */
+int BuyPreset::FullCost() const
+{
+ if ( m_weaponList.Count() == 0 )
+ return 0;
+
+ return m_weaponList[0].FullCost();
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns the specified fallback WeaponSet, or NULL if it doesn't exist
+ */
+const WeaponSet * BuyPreset::GetSet( int index ) const
+{
+ if ( index < 0 || index >= m_weaponList.Count() )
+ return NULL;
+
+ return &(m_weaponList[index]);
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Deletes a fallback
+ */
+void BuyPreset::DeleteSet( int index )
+{
+ if ( index < 0 || index >= m_weaponList.Count() )
+ return;
+
+ m_weaponList.Remove( index );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Switches the order of two fallbacks
+ */
+void BuyPreset::SwapSet( int firstIndex, int secondIndex )
+{
+ if ( firstIndex < 0 || firstIndex >= m_weaponList.Count() )
+ return;
+
+ if ( secondIndex < 0 || secondIndex >= m_weaponList.Count() )
+ return;
+
+ WeaponSet tmpSet = m_weaponList[firstIndex];
+ m_weaponList[firstIndex] = m_weaponList[secondIndex];
+ m_weaponList[secondIndex] = tmpSet;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Replaces a fallback with the supplied WeaponSet
+ */
+void BuyPreset::ReplaceSet( int index, const WeaponSet& weaponSet )
+{
+ if ( index < 0 || index > m_weaponList.Count() )
+ return;
+
+ if ( index == m_weaponList.Count() )
+ {
+ m_weaponList.AddToTail( weaponSet );
+ }
+ else
+ {
+ m_weaponList[index] = weaponSet;
+ }
+}
+
+//--------------------------------------------------------------------------------------------------------------
diff --git a/game/client/cstrike/buy_presets/buy_preset_debug.cpp b/game/client/cstrike/buy_presets/buy_preset_debug.cpp
new file mode 100644
index 0000000..afe3e23
--- /dev/null
+++ b/game/client/cstrike/buy_presets/buy_preset_debug.cpp
@@ -0,0 +1,65 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+// Author: Matthew D. Campbell ([email protected]), 2004
+
+#include "cbase.h"
+#include "buy_preset_debug.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#ifdef _DEBUG
+#define BUY_PRESET_DEBUGGING 1
+#else
+#define BUY_PRESET_DEBUGGING 0
+#endif
+
+#if BUY_PRESET_DEBUGGING
+
+static ConVar cl_preset_debug( "cl_preset_debug", "0", 0, "Controls debug information about buy presets" );
+
+//--------------------------------------------------------------------------------------------------------------
+bool IsPresetDebuggingEnabled()
+{
+ return cl_preset_debug.GetInt() >= 1.0f;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+bool IsPresetFullCostDebuggingEnabled()
+{
+ return cl_preset_debug.GetInt() == 2.0f;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+bool IsPresetCurrentCostDebuggingEnabled()
+{
+ return cl_preset_debug.GetInt() == 3.0f;
+}
+
+#else // ! BUY_PRESET_DEBUGGING
+
+//--------------------------------------------------------------------------------------------------------------
+bool IsPresetDebuggingEnabled()
+{
+ return false;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+bool IsPresetFullCostDebuggingEnabled()
+{
+ return false;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+bool IsPresetCurrentCostDebuggingEnabled()
+{
+ return false;
+}
+
+#endif // ! BUY_PRESET_DEBUGGING
+
+//--------------------------------------------------------------------------------------------------------------
diff --git a/game/client/cstrike/buy_presets/buy_preset_debug.h b/game/client/cstrike/buy_presets/buy_preset_debug.h
new file mode 100644
index 0000000..ded580b
--- /dev/null
+++ b/game/client/cstrike/buy_presets/buy_preset_debug.h
@@ -0,0 +1,25 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef BUY_PRESET_DEBUG_H
+#define BUY_PRESET_DEBUG_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+//--------------------------------------------------------------------------------------------------------------
+// Utility functions for the debugging macros below
+bool IsPresetDebuggingEnabled(); ///< cl_preset_debug >= 1.0f
+bool IsPresetFullCostDebuggingEnabled(); ///< cl_preset_debug == 2.0f
+bool IsPresetCurrentCostDebuggingEnabled(); ///< cl_preset_debug == 3.0f
+
+//--------------------------------------------------------------------------------------------------------------
+// Macros for conditional debugging of buy presets
+#define PRESET_DEBUG if (IsPresetDebuggingEnabled()) DevMsg
+#define FULLCOST_DEBUG if (IsPresetFullCostDebuggingEnabled()) DevMsg
+#define CURRENTCOST_DEBUG if (IsPresetCurrentCostDebuggingEnabled()) DevMsg
+
+#endif // BUY_PRESET_DEBUG_H
diff --git a/game/client/cstrike/buy_presets/buy_preset_weapon_info.cpp b/game/client/cstrike/buy_presets/buy_preset_weapon_info.cpp
new file mode 100644
index 0000000..24c4751
--- /dev/null
+++ b/game/client/cstrike/buy_presets/buy_preset_weapon_info.cpp
@@ -0,0 +1,364 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: BuyPresetWeapon implementation, and misc knowledge relating to weapons
+//
+//=============================================================================
+
+#include "cbase.h"
+
+#include "buy_preset_debug.h"
+#include "buy_presets.h"
+#include "weapon_csbase.h"
+#include "cs_ammodef.h"
+#include "cs_gamerules.h"
+#include "cstrike/bot/shared_util.h"
+#include <vgui/ILocalize.h>
+#include <vgui_controls/Controls.h>
+#include "c_cs_player.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//--------------------------------------------------------------------------------------------------------------
+struct WeaponDisplayNameInfo
+{
+ CSWeaponID id;
+ const char *displayName;
+};
+
+
+//--------------------------------------------------------------------------------------------------------------
+// NOTE: Array must be NULL-terminated
+static WeaponDisplayNameInfo weaponDisplayNameInfo[] =
+{
+ { WEAPON_P228, "#Cstrike_TitlesTXT_P228" },
+ { WEAPON_GLOCK, "#Cstrike_TitlesTXT_Glock18" },
+ { WEAPON_SCOUT, "#Cstrike_TitlesTXT_Scout" },
+ { WEAPON_XM1014, "#Cstrike_TitlesTXT_AutoShotgun" },
+ { WEAPON_MAC10, "#Cstrike_TitlesTXT_Mac10_Short" },
+ { WEAPON_AUG, "#Cstrike_TitlesTXT_Aug" },
+ { WEAPON_ELITE, "#Cstrike_TitlesTXT_Beretta96G" },
+ { WEAPON_FIVESEVEN, "#Cstrike_TitlesTXT_ESFiveSeven" },
+ { WEAPON_UMP45, "#Cstrike_TitlesTXT_KMUMP45" },
+ { WEAPON_SG550, "#Cstrike_TitlesTXT_SG550" },
+ { WEAPON_GALIL, "#Cstrike_TitlesTXT_Galil" },
+ { WEAPON_FAMAS, "#Cstrike_TitlesTXT_Famas" },
+ { WEAPON_USP, "#Cstrike_TitlesTXT_USP45" },
+ { WEAPON_AWP, "#Cstrike_TitlesTXT_ArcticWarfareMagnum" },
+ { WEAPON_MP5NAVY, "#Cstrike_TitlesTXT_mp5navy" },
+ { WEAPON_M249, "#Cstrike_TitlesTXT_ESM249" },
+ { WEAPON_M3, "#Cstrike_TitlesTXT_Leone12" },
+ { WEAPON_M4A1, "#Cstrike_TitlesTXT_M4A1_Short" },
+ { WEAPON_TMP, "#Cstrike_TitlesTXT_tmp" },
+ { WEAPON_G3SG1, "#Cstrike_TitlesTXT_G3SG1" },
+ { WEAPON_DEAGLE, "#Cstrike_TitlesTXT_DesertEagle" },
+ { WEAPON_SG552, "#Cstrike_TitlesTXT_SG552" },
+ { WEAPON_AK47, "#Cstrike_TitlesTXT_AK47" },
+ { WEAPON_P90, "#Cstrike_TitlesTXT_ESC90" },
+ { WEAPON_SHIELDGUN, "#Cstrike_TitlesTXT_TactShield" },
+
+ { WEAPON_NONE, "#Cstrike_CurrentWeapon" }
+};
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns the display name for a weapon, based on it's weaponID
+ */
+const wchar_t* WeaponIDToDisplayName( CSWeaponID weaponID )
+{
+ for( int i=0; weaponDisplayNameInfo[i].displayName; ++i )
+ if ( weaponDisplayNameInfo[i].id == weaponID )
+ return g_pVGuiLocalize->Find( weaponDisplayNameInfo[i].displayName );
+
+ return NULL;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------------------------
+BuyPresetWeapon::BuyPresetWeapon()
+{
+ m_name = NULL;
+ m_weaponID = WEAPON_NONE;
+ m_ammoType = AMMO_CLIPS;
+ m_ammoAmount = 0;
+ m_fillAmmo = true;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+BuyPresetWeapon::BuyPresetWeapon( CSWeaponID weaponID )
+{
+ m_name = WeaponIDToDisplayName( weaponID );
+ m_weaponID = weaponID;
+ m_ammoType = AMMO_CLIPS;
+ m_ammoAmount = (weaponID == WEAPON_NONE) ? 0 : 1;
+ m_fillAmmo = true;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+BuyPresetWeapon& BuyPresetWeapon::operator= (const BuyPresetWeapon& other)
+{
+ m_name = other.m_name;
+ m_weaponID = other.m_weaponID;
+ m_ammoType = other.m_ammoType;
+ m_ammoAmount = other.m_ammoAmount;
+ m_fillAmmo = other.m_fillAmmo;
+
+ return *this;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Sets the WEAPON_* weapon ID (and resets ammo, etc)
+ */
+void BuyPresetWeapon::SetWeaponID( CSWeaponID weaponID )
+{
+ m_name = WeaponIDToDisplayName( weaponID );
+ m_weaponID = weaponID;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns the number of clips that will have to be bought for the specified BuyPresetWeapon
+ */
+int CalcClipsNeeded( const BuyPresetWeapon *pWeapon, const CCSWeaponInfo *pInfo, const int ammo[MAX_AMMO_TYPES] )
+{
+ if ( !pWeapon || !pInfo )
+ return 0;
+
+ int maxRounds = GetCSAmmoDef()->MaxCarry( pInfo->iAmmoType );
+ int buySize = GetCSAmmoDef()->GetBuySize( pInfo->iAmmoType );
+
+ int numClips = 0;
+ if ( buySize && pInfo->iAmmoType >= 0 )
+ {
+ switch ( pWeapon->GetAmmoType() )
+ {
+ case AMMO_CLIPS:
+ numClips = pWeapon->GetAmmoAmount();
+ if ( pWeapon->GetWeaponID() == WEAPON_NONE && numClips >= 4 )
+ {
+ numClips = ceil(maxRounds/(float)buySize);
+ }
+ numClips = MIN( ceil(maxRounds/(float)buySize), numClips );
+
+ numClips -= ammo[pInfo->iAmmoType]/buySize;
+ if ( numClips < 0 || ammo[pInfo->iAmmoType] == maxRounds )
+ {
+ numClips = 0;
+ }
+ break;
+ case AMMO_ROUNDS:
+ {
+ int roundsNeeded = pWeapon->GetAmmoAmount() - ammo[pInfo->iAmmoType];
+ if ( roundsNeeded > 0 )
+ {
+ numClips = ceil(roundsNeeded/(float)buySize);
+ }
+ else
+ {
+ numClips = 0;
+ }
+ }
+ break;
+ case AMMO_PERCENT:
+ {
+ int roundsNeeded = maxRounds*pWeapon->GetAmmoAmount() - ammo[pInfo->iAmmoType];
+ roundsNeeded *= 0.01f;
+ if ( roundsNeeded > 0 )
+ {
+ numClips = ceil(roundsNeeded/(float)buySize);
+ }
+ else
+ {
+ numClips = 0;
+ }
+ }
+ break;
+ }
+ }
+ return numClips;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns true if the client can currently buy the weapon according to class/gamemode restrictions. Returns
+ * true also if the player owns the weapon already.
+ */
+bool CanBuyWeapon( CSWeaponID currentPrimaryID, CSWeaponID currentSecondaryID, CSWeaponID weaponID )
+{
+ if ( currentPrimaryID == WEAPON_SHIELDGUN && weaponID == WEAPON_ELITE )
+ {
+ return false;
+ }
+
+ if ( currentSecondaryID == WEAPON_ELITE && weaponID == WEAPON_SHIELDGUN )
+ {
+ return false;
+ }
+
+ C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
+ if ( !pPlayer )
+ return false;
+
+ CCSWeaponInfo *info = GetWeaponInfo( weaponID );
+ if ( !info )
+ return false;
+
+ /// @TODO: assasination maps have a specific set of weapons that can be used in them.
+ if ( info->m_iTeam != TEAM_UNASSIGNED && pPlayer->GetTeamNumber() != info->m_iTeam )
+ return false;
+
+ return true;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns true if the CSWeaponType is a primary class
+ */
+static bool IsPrimaryWeaponClassID( CSWeaponType classId )
+{
+ switch (classId)
+ {
+ case WEAPONTYPE_SUBMACHINEGUN:
+ case WEAPONTYPE_SHOTGUN:
+ case WEAPONTYPE_MACHINEGUN:
+ case WEAPONTYPE_RIFLE:
+ case WEAPONTYPE_SNIPER_RIFLE:
+ return true;
+ }
+
+ return false;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns true if the weapon ID is for a primary weapon
+ */
+static bool IsPrimaryWeaponID( CSWeaponID id )
+{
+ if ( id == WEAPON_SHIELDGUN )
+ return true;
+
+ CCSWeaponInfo *info = GetWeaponInfo( id );
+ if ( !info )
+ return false;
+
+ return IsPrimaryWeaponClassID( info->m_WeaponType );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns true if the CSWeaponType is a secondary class
+ */
+static bool IsSecondaryWeaponClassID( CSWeaponType classId )
+{
+ return (classId == WEAPONTYPE_PISTOL);
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns true if the weapon ID is for a secondary weapon
+ */
+static bool IsSecondaryWeaponID( CSWeaponID id )
+{
+ if ( id == WEAPON_SHIELDGUN )
+ return false;
+
+ CCSWeaponInfo *info = GetWeaponInfo( id );
+ if ( !info )
+ return false;
+
+ return IsSecondaryWeaponClassID( info->m_WeaponType );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Fills the ammo array with the ammo currently owned by the local player
+ */
+void FillClientAmmo( int ammo[MAX_AMMO_TYPES] )
+{
+ int i;
+ for ( i=0; i<MAX_AMMO_TYPES; ++i )
+ {
+ ammo[i] = 0;
+ }
+
+ C_CSPlayer *localPlayer = CCSPlayer::GetLocalCSPlayer();
+ if ( !localPlayer )
+ return;
+
+ for ( i=0; i<WEAPON_MAX; ++i )
+ {
+ CSWeaponID gameWeaponID = (CSWeaponID)i;
+ if ( gameWeaponID == WEAPON_NONE || gameWeaponID >= WEAPON_SHIELDGUN )
+ continue;
+
+ const CCSWeaponInfo *info = GetWeaponInfo( gameWeaponID );
+ if ( !info )
+ continue;
+
+ int clientAmmoType = info->iAmmoType;
+ int clientAmmoCount = localPlayer->GetAmmoCount( clientAmmoType );
+ if ( clientAmmoCount > 0 )
+ {
+ ammo[ clientAmmoType ] = MAX( ammo[ clientAmmoType ], clientAmmoCount );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the weapon in the specified slot
+//-----------------------------------------------------------------------------
+CWeaponCSBase *GetWeaponInSlot( int iSlot, int iSlotPos )
+{
+ C_CSPlayer *player = C_CSPlayer::GetLocalCSPlayer();
+ if ( !player )
+ return NULL;
+
+ for ( int i = 0; i < MAX_WEAPONS; i++ )
+ {
+ CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetWeapon(i));
+
+ if ( pWeapon == NULL )
+ continue;
+
+ if ( pWeapon->GetSlot() == iSlot && pWeapon->GetPosition() == iSlotPos )
+ return pWeapon;
+ }
+
+ return NULL;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns the client's WEAPON_* value for the currently owned weapon, or WEAPON_NONE if no weapon is owned
+ */
+CSWeaponID GetClientWeaponID( bool primary )
+{
+ C_CSPlayer *localPlayer = CCSPlayer::GetLocalCSPlayer();
+ if ( !localPlayer )
+ return WEAPON_NONE;
+
+ int slot = (primary)?0:1;
+ CWeaponCSBase *pWeapon = GetWeaponInSlot( slot, slot );
+ if ( !pWeapon )
+ return WEAPON_NONE;
+
+ return pWeapon->GetWeaponID();
+}
+
+//--------------------------------------------------------------------------------------------------------------
diff --git a/game/client/cstrike/buy_presets/buy_presets.cpp b/game/client/cstrike/buy_presets/buy_presets.cpp
new file mode 100644
index 0000000..7badf8f
--- /dev/null
+++ b/game/client/cstrike/buy_presets/buy_presets.cpp
@@ -0,0 +1,454 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+
+#include "buy_preset_debug.h"
+#include "buy_presets.h"
+#include "weapon_csbase.h"
+#include "game/client/iviewport.h"
+#include "filesystem.h"
+#include <vgui/ILocalize.h>
+#include <vgui_controls/Controls.h>
+#include "c_cs_player.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+BuyPresetManager *TheBuyPresets = NULL;
+
+#if USE_BUY_PRESETS
+//--------------------------------------------------------------------------------------------------------------
+ConVar cl_buy_favorite_quiet( "cl_buy_favorite_quiet", "0", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Skips the prompt when saving a buy favorite in the buy menu" );
+ConVar cl_buy_favorite_nowarn( "cl_buy_favorite_nowarn", "0", FCVAR_ARCHIVE | FCVAR_CLIENTCMD_CAN_EXECUTE, "Skips the error prompt when saving an invalid buy favorite" );
+
+//--------------------------------------------------------------------------------------------------------------
+static void PrintBuyPresetUsage( void )
+{
+ if ( TheBuyPresets->GetNumPresets() )
+ {
+ Msg( "usage: cl_buy_favorite <1...%d>\n", TheBuyPresets->GetNumPresets() );
+ for ( int i=0; i<TheBuyPresets->GetNumPresets(); ++i )
+ {
+ const BuyPreset *preset = TheBuyPresets->GetPreset( i );
+ if ( preset && preset->GetName() && preset->GetName()[0] )
+ {
+ char buffer[64];
+ g_pVGuiLocalize->ConvertUnicodeToANSI( preset->GetName(), buffer, sizeof( buffer ) );
+ Msg( " %d. %s\n", i+1, buffer );
+ }
+ }
+ }
+ else
+ {
+ Msg( "cl_buy_favorite: no favorites are defined\n" );
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Callback function for handling the "cl_buy_favorite" command
+ */
+CON_COMMAND_F( cl_buy_favorite, "Purchase a favorite weapon/equipment loadout", FCVAR_CLIENTCMD_CAN_EXECUTE )
+{
+ if ( !engine->IsConnected() )
+ return;
+
+ if ( !TheBuyPresets )
+ TheBuyPresets = new BuyPresetManager();
+
+ if ( args.ArgC() != 2 )
+ {
+ PRESET_DEBUG( "cl_buy_favorite: no favorite specified\n" );
+ PrintBuyPresetUsage();
+ return;
+ }
+
+ int presetIndex = atoi( args[1] ) - 1;
+ if ( presetIndex < 0 || presetIndex >= TheBuyPresets->GetNumPresets() )
+ {
+ PRESET_DEBUG( "cl_buy_favorite: favorite %d doesn't exist\n", presetIndex );
+ PrintBuyPresetUsage();
+ return;
+ }
+
+ TheBuyPresets->PurchasePreset( presetIndex );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Callback function for handling the "cl_buy_favorite_set" command
+ */
+CON_COMMAND_F( cl_buy_favorite_set, "Saves the current loadout as a favorite", FCVAR_CLIENTCMD_CAN_EXECUTE )
+{
+ if ( !engine->IsConnected() )
+ return;
+
+ if ( !TheBuyPresets )
+ TheBuyPresets = new BuyPresetManager();
+
+ if ( args.ArgC() != 2 )
+ {
+ PRESET_DEBUG( "cl_buy_favorite_set: no favorite specified\n" );
+ PrintBuyPresetUsage();
+ return;
+ }
+
+ int presetIndex = atoi( args[ 1 ] ) - 1;
+ if ( presetIndex < 0 || presetIndex >= TheBuyPresets->GetNumPresets() )
+ {
+ PRESET_DEBUG( "cl_buy_favorite_set: favorite %d doesn't exist\n", presetIndex );
+ PrintBuyPresetUsage();
+ return;
+ }
+
+ const BuyPreset *preset = TheBuyPresets->GetPreset( presetIndex );
+ if ( !preset )
+ {
+ return;
+ }
+
+ WeaponSet ws;
+ TheBuyPresets->GetCurrentLoadout( &ws );
+ BuyPreset newPreset( *preset );
+ newPreset.ReplaceSet( 0, ws );
+
+ TheBuyPresets->SetPreset( presetIndex, &newPreset );
+ TheBuyPresets->Save();
+
+ C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
+ if ( pPlayer )
+ {
+ pPlayer->EmitSound( "BuyPreset.Updated" );
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Callback function for handling the "cl_buy_favorite_reset" command
+ */
+void __CmdFunc_BuyPresetsReset(void)
+{
+ if ( !engine->IsConnected() )
+ return;
+
+ if ( !TheBuyPresets )
+ TheBuyPresets = new BuyPresetManager();
+
+ C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
+ if ( !pPlayer || ( pPlayer->GetTeamNumber() != TEAM_CT && pPlayer->GetTeamNumber() != TEAM_TERRORIST ) )
+ {
+ return;
+ }
+
+ TheBuyPresets->ResetEditToDefaults();
+ TheBuyPresets->SetPresets( TheBuyPresets->GetEditPresets() );
+ TheBuyPresets->Save();
+}
+ConCommand cl_buy_favorite_reset( "cl_buy_favorite_reset", __CmdFunc_BuyPresetsReset, "Reset favorite loadouts to the default", FCVAR_CLIENTCMD_CAN_EXECUTE );
+#endif // USE_BUY_PRESETS
+
+
+//--------------------------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Creates the BuyPresetManager singleton
+ */
+BuyPresetManager::BuyPresetManager()
+{
+ m_loadedTeam = TEAM_UNASSIGNED;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Resets the BuyPresetManager, loading BuyPresets from disk. If no presets are defined, it loads the
+ * default presets instead.
+ */
+void BuyPresetManager::VerifyLoadedTeam( void )
+{
+#if USE_BUY_PRESETS
+ C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
+ if ( !pPlayer )
+ return;
+
+ int playerTeam = pPlayer->GetTeamNumber();
+
+ if ( playerTeam == m_loadedTeam )
+ return;
+
+ if ( playerTeam != TEAM_CT && playerTeam != TEAM_TERRORIST )
+ return;
+
+ m_presets.RemoveAll();
+
+ const char *filename = "cfg/BuyPresets_TER.vdf";
+ if ( playerTeam == TEAM_CT )
+ {
+ filename = "cfg/BuyPresets_CT.vdf";
+ }
+
+ KeyValues *data;
+ KeyValues *presetKey;
+ data = new KeyValues( "Presets" );
+ bool fileExists = data->LoadFromFile( filesystem, filename, NULL );
+
+ presetKey = data->GetFirstSubKey();
+ while ( presetKey )
+ {
+ BuyPreset preset;
+ preset.Parse( presetKey );
+ m_presets.AddToTail(preset);
+
+ presetKey = presetKey->GetNextKey();
+ }
+
+ if ( !m_presets.Count() )
+ {
+ const char *filename = "cfg/BuyPresetsDefault_TER.vdf";
+ if ( playerTeam == TEAM_CT )
+ {
+ filename = "cfg/BuyPresetsDefault_CT.vdf";
+ }
+
+ KeyValues *data;
+ KeyValues *presetKey;
+ data = new KeyValues( "Presets" );
+ data->LoadFromFile( filesystem, filename, NULL );
+
+ presetKey = data->GetFirstSubKey();
+ while ( presetKey )
+ {
+ BuyPreset preset;
+ preset.Parse( presetKey );
+ m_presets.AddToTail(preset);
+
+ presetKey = presetKey->GetNextKey();
+ }
+
+ data->deleteThis();
+ }
+
+ // Guarantee we have at least this many presets, even if they are blank
+ while ( m_presets.Count() < NUM_PRESETS )
+ {
+ BuyPreset preset;
+ m_presets.AddToTail(preset);
+ }
+
+ data->deleteThis();
+ m_editPresets = m_presets;
+
+ if ( !fileExists )
+ Save();
+#endif // USE_BUY_PRESETS
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Loads the default presets into the editing presets (e.g. when hitting the "Use Defaults" button)
+ */
+void BuyPresetManager::ResetEditToDefaults( void )
+{
+#if USE_BUY_PRESETS
+ VerifyLoadedTeam();
+
+ C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
+ if ( !pPlayer )
+ return;
+
+ int playerTeam = pPlayer->GetTeamNumber();
+
+ if ( playerTeam != TEAM_CT && playerTeam != TEAM_TERRORIST )
+ return;
+
+ m_editPresets.RemoveAll();
+
+ const char *filename = "cfg/BuyPresetsDefault_TER.vdf";
+ if ( playerTeam == TEAM_CT )
+ {
+ filename = "cfg/BuyPresetsDefault_CT.vdf";
+ }
+
+ KeyValues *data;
+ KeyValues *presetKey;
+ data = new KeyValues( "Presets" );
+ data->LoadFromFile( filesystem, filename, NULL );
+
+ presetKey = data->GetFirstSubKey();
+ while ( presetKey )
+ {
+ BuyPreset preset;
+ preset.Parse( presetKey );
+ m_editPresets.AddToTail(preset);
+
+ presetKey = presetKey->GetNextKey();
+ }
+
+ data->deleteThis();
+#endif // USE_BUY_PRESETS
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Saves the current BuyPresets to disk
+ */
+void BuyPresetManager::Save()
+{
+#if USE_BUY_PRESETS
+ C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
+ if ( !pPlayer )
+ return;
+
+ const char *filename = "cfg/BuyPresets_TER.vdf";
+ switch ( pPlayer->GetTeamNumber() )
+ {
+ case TEAM_CT:
+ filename = "cfg/BuyPresets_CT.vdf";
+ break;
+ case TEAM_TERRORIST:
+ filename = "cfg/BuyPresets_TER.vdf";
+ break;
+ default:
+ return; // don't bother saving presets unless we're on a team
+ }
+
+ KeyValues *data = new KeyValues( "Presets" );
+ for( int i=0; i<m_presets.Count(); ++i )
+ {
+ m_presets[i].Save( data );
+ }
+ data->SaveToFile( filesystem, filename, NULL );
+ data->deleteThis();
+#endif // USE_BUY_PRESETS
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns the specified "live" preset, or NULL if it doesn't exist
+ */
+const BuyPreset * BuyPresetManager::GetPreset( int index ) const
+{
+ if ( index < 0 || index >= m_presets.Count() )
+ {
+ return NULL;
+ }
+
+ return &(m_presets[index]);
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+void BuyPresetManager::SetPreset( int index, const BuyPreset *preset )
+{
+ if ( index < 0 || index >= m_presets.Count() || !preset )
+ {
+ return;
+ }
+
+ m_presets[index] = *preset;
+}
+
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns the specified editing preset, or NULL if it doesn't exist
+ */
+BuyPreset * BuyPresetManager::GetEditPreset( int index )
+{
+ if ( index < 0 || index >= m_editPresets.Count() )
+ {
+ return NULL;
+ }
+
+ return &(m_editPresets[index]);
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Generates and sends buy commands to buy a specific preset
+ */
+void BuyPresetManager::PurchasePreset( int presetIndex )
+{
+ if ( presetIndex >= 0 && presetIndex < m_presets.Count() )
+ {
+ const BuyPreset *preset = &(m_presets[presetIndex]);
+
+ int setIndex;
+ for ( setIndex = 0; setIndex < preset->GetNumSets(); ++setIndex )
+ {
+ // Try to buy this weapon set.
+ const WeaponSet *itemSet = preset->GetSet( setIndex );
+ if ( itemSet )
+ {
+ int currentCost;
+ WeaponSet currentSet;
+ itemSet->GetCurrent( currentCost, currentSet );
+ if ( currentCost > 0 )
+ {
+ PRESET_DEBUG( "cl_buy_favorite: buying %ls for a total of $%d.\n",
+ preset->GetName(),
+ currentCost );
+ char buf[BUY_PRESET_COMMAND_LEN];
+ currentSet.GenerateBuyCommands( buf );
+
+ // Send completed string
+ PRESET_DEBUG( "%s\n", buf );
+ engine->ClientCmd( buf );
+ return;
+ }
+ else if ( currentCost == 0 )
+ {
+ C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
+ if ( pPlayer )
+ {
+ pPlayer->EmitSound( "BuyPreset.AlreadyBought" );
+ }
+
+ // We have everything already. Let the player know.
+ if ( setIndex == 0 )
+ {
+ PRESET_DEBUG( "cl_buy_favorite: already have a complete %ls set.\n", preset->GetName() );
+ }
+ else
+ {
+ PRESET_DEBUG( "cl_buy_favorite: can't afford anything better from %ls.\n", preset->GetName() );
+ }
+ return;
+ }
+ }
+ }
+
+ C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
+ if ( pPlayer )
+ {
+ pPlayer->EmitSound( "BuyPreset.CantBuy" );
+ }
+
+ PRESET_DEBUG( "cl_buy_favorite: can't afford anything better from %ls.\n", preset->GetName() );
+ return;
+ }
+
+ // We failed to buy anything. Let the player know.
+ C_CSPlayer *pPlayer = C_CSPlayer::GetLocalCSPlayer();
+ if ( pPlayer )
+ {
+ pPlayer->EmitSound( "BuyPreset.CantBuy" );
+ }
+
+ PRESET_DEBUG( "cl_buy_favorite: preset %d doesn't exist.\n", presetIndex );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
diff --git a/game/client/cstrike/buy_presets/buy_presets.h b/game/client/cstrike/buy_presets/buy_presets.h
new file mode 100644
index 0000000..086e6ba
--- /dev/null
+++ b/game/client/cstrike/buy_presets/buy_presets.h
@@ -0,0 +1,276 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef BUY_PRESETS_H
+#define BUY_PRESETS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#define USE_BUY_PRESETS 1
+
+/**
+ * CLASS STRUCTURE:
+ * Buy presets are managed by TheBuyPresetManager, which has a list of BuyPreset objects.
+ * Each BuyPreset has a list of fallback WeaponSets. Each WeaponSet is a complete set of
+ * weapons and equipment that could be purchased by a buy preset.
+ *
+ * BUYING:
+ * When purchasing a buy preset, the fallback WeaponSets are considered in order from first
+ * to last. When one is deemed to be purchasable, the buy commands are generated and
+ * processing stops.
+ *
+ * EDITING:
+ * When editing buy presets, TheBuyPresetManager maintains a second list of BuyPresets, to
+ * allow an all-or-nothing undo system. The editing is done on two main VGUI menus: a
+ * list of the four main buy presets (CBuyPresetEditMainMenu), and a menu showing the
+ * fallbacks for a single preset (CBuyPresetEditOutfitListMenu).
+ *
+ * Presets and fallbacks can be copied, created, deleted, etc from these pages. From the
+ * fallback menu, a wizard can edit the contents of a fallback, specifying primary weapon,
+ * pistol, and equipment.
+ */
+
+#include "weapon_csbase.h"
+#include <KeyValues.h>
+#include <utlvector.h>
+
+class BuyPreset;
+class CCSWeaponInfo;
+
+//--------------------------------------------------------------------------------------------------------------
+enum BuyPresetStringSizes
+{
+ BUY_PRESET_COMMAND_LEN = 256,
+ MaxBuyPresetName = 64,
+ MaxBuyPresetImageFname = 64,
+};
+
+enum AmmoSizeType
+{
+ AMMO_PERCENT,
+ AMMO_CLIPS,
+ AMMO_ROUNDS
+};
+
+enum { NUM_PRESETS = 4 };
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * A BuyPresetWeapon stores the mp.dll version of the WeaponID, the increment for buying ammo (clips, rounds, etc),
+ * the number of increments to buy, etc. This is the basic info for buying a weapon that is present in an
+ * item set.
+ */
+class BuyPresetWeapon
+{
+public:
+ BuyPresetWeapon();
+ BuyPresetWeapon( CSWeaponID weaponID );
+ BuyPresetWeapon& operator= (const BuyPresetWeapon& other);
+
+ const wchar_t* GetName() const { return m_name; } ///< Returns the display name corresponding to the weapon ID
+ CSWeaponID GetWeaponID() const { return m_weaponID; } ///< Returns the WEAPON_* weapon ID
+ void SetWeaponID( CSWeaponID weaponID ); ///< Sets the WEAPON_* weapon ID (and resets ammo, etc)
+
+ void SetAmmoType( AmmoSizeType ammoType ) { m_ammoType = ammoType; } ///< Sets the ammo type - percent (unused), clips, or rounds (unused)
+ void SetAmmoAmount( int ammoAmount ) { m_ammoAmount = ammoAmount; } ///< Sets the amount of ammo, in units relevant to the ammo type
+ void SetFillAmmo( bool fill ) { m_fillAmmo = fill; } ///< Sets whether the weapon's ammo should be topped off
+
+ AmmoSizeType GetAmmoType() const { return m_ammoType; } ///< Returns ammo type - percent (unused), clips, or rounds (unused)
+ int GetAmmoAmount() const { return m_ammoAmount; } ///< Returns the amount of ammo, in units relevant to the ammo type
+ bool GetFillAmmo() const { return m_fillAmmo; } ///< Returns true if the weapon's ammo should be topped off
+
+protected:
+ const wchar_t *m_name;
+ CSWeaponID m_weaponID;
+ AmmoSizeType m_ammoType;
+ int m_ammoAmount;
+ bool m_fillAmmo;
+};
+
+typedef CUtlVector< BuyPresetWeapon > BuyPresetWeaponList;
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * A WeaponSet is part of a buy preset. Each buy preset is composed of a series of (fallback) WeaponSets.
+ * The WeaponSet contains a snapshot of a set of weapons and equipment that should be bought.
+ */
+class WeaponSet
+{
+public:
+ WeaponSet();
+
+ void GetCurrent( int& cost, WeaponSet& ws ) const; ///< Generates a WeaponSet containing only items that would be bought right now
+ void GetFromScratch( int& cost, WeaponSet& ws ) const; ///< Generates a WeaponSet containing everything that would be bought from scratch
+
+ void GenerateBuyCommands( char command[BUY_PRESET_COMMAND_LEN] ) const; ///< Generates a list of commands to buy the current WeaponSet.
+
+ int FullCost() const; ///< Calculates the full cost of the WeaponSet, including all optional items
+
+ void Reset( void );
+
+ const BuyPresetWeapon& GetPrimaryWeapon() const { return m_primaryWeapon; }
+ const BuyPresetWeapon& GetSecondaryWeapon() const { return m_secondaryWeapon; }
+
+ BuyPresetWeapon m_primaryWeapon;
+ BuyPresetWeapon m_secondaryWeapon;
+
+ // Equipment --------------------------------
+ int m_armor;
+ bool m_helmet;
+ bool m_smokeGrenade;
+ bool m_HEGrenade;
+ int m_flashbangs;
+ bool m_defuser;
+ bool m_nightvision;
+};
+
+typedef CUtlVector< WeaponSet > WeaponSetList;
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * The BuyPreset is a complete representation of a buy preset. Essentially, it consists of the preset name,
+ * whether or not the preset is used for autobuy, and a list of fallback WeaponSets.
+ */
+class BuyPreset
+{
+public:
+ BuyPreset();
+ ~BuyPreset();
+ BuyPreset(const BuyPreset& other);
+
+ void SetName( const wchar_t *name );
+ const wchar_t * GetName() const { return m_name; }
+
+ void Parse( KeyValues *data ); ///< Populates data from a KeyValues structure, for loading
+ void Save( KeyValues *data ); ///< Fills in a KeyValues structure with data, for saving
+
+ int GetNumSets() const { return m_weaponList.Count(); } ///< Returns the number of fallback WeaponSets
+ const WeaponSet * GetSet( int index ) const; ///< Returns the specified fallback WeaponSet, or NULL if it doesn't exist
+
+ int FullCost() const; ///< Calculates the full cost of the preset, which is exactly the full cost of the first fallback WeaponSet
+
+ // editing functions --------------------
+ void DeleteSet( int index ); ///< Deletes a fallback
+ void SwapSet( int firstIndex, int secondIndex ); ///< Switches the order of two fallbacks
+ void ReplaceSet( int index, const WeaponSet& weaponSet ); ///< Replaces a fallback with the supplied WeaponSet
+
+private:
+ wchar_t m_name[MaxBuyPresetName];
+
+ WeaponSetList m_weaponList;
+};
+
+typedef CUtlVector< BuyPreset > BuyPresetList;
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * The BuyPresetManager singleton manages saving/loading/buying the individual BuyPresets. It also tracks the
+ * ownership of some items that aren't explicitly known by the client (defuser, nightvision).
+ *
+ * Two sets of BuyPresets are maintained - the live set used for purchasing, and a set that is acted upon for
+ * editing. This provides the basic save changes/abandon changes ability when editing presets.
+ */
+class BuyPresetManager
+{
+public:
+ BuyPresetManager();
+
+ void Save();
+
+ void PurchasePreset( int presetIndex ); ///< Generates and sends buy commands to buy a specific preset
+
+ int GetNumPresets() { VerifyLoadedTeam(); return m_presets.Count(); }
+ const BuyPreset * GetPreset( int index ) const; ///< Returns the specified "live" preset, or NULL if it doesn't exist
+ void SetPreset( int index, const BuyPreset *preset );
+
+ void SetPresets( const BuyPresetList& presets ) { m_presets = presets; }
+
+ void SetEditPresets( const BuyPresetList& presets ) { m_editPresets = presets; }
+ int GetNumEditPresets() const { return m_editPresets.Count(); }
+
+ BuyPreset * GetEditPreset( int index ); ///< Returns the specified editing preset, or NULL if it doesn't exist
+ const CUtlVector< BuyPreset >& GetEditPresets() const { return m_editPresets; }
+
+ void ResetEditPresets() { m_editPresets = m_presets; } ///< Resets the editing presets to the live presets (e.g. when starting editing)
+ void ResetEditToDefaults( void ); ///< Loads the default presets into the editing presets (e.g. when hitting the "Use Defaults" button)
+
+ void GetCurrentLoadout( WeaponSet *weaponSet );
+
+private:
+ BuyPresetList m_presets; ///< Current (live) presets
+ BuyPresetList m_editPresets; ///< BuyPresets used when editing
+
+ int m_loadedTeam;
+ void VerifyLoadedTeam( void );
+};
+
+//--------------------------------------------------------------------------------------------------------------
+extern BuyPresetManager *TheBuyPresets;
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Functions for controlling the display of the various buy preset editing menus. Opening one closes any
+ * others open.
+ */
+enum { REOPEN_YES, REOPEN_NO, REOPEN_DONTCHANGE }; ///< Determines if we need to re-open the buy menu when done editing.
+void ShowBuyPresetMainMenu( bool runUpdate, int reopenBuyMenu ); ///< Open the main preset editing menu
+void ShowBuyPresetEditMenu( int presetIndex ); ///< Open the menu for editing the list of fallbacks for an individual preset
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns the image filename for a given WEAPON_* weapon ID. Primary is specified because WEAPON_NONE returns
+ * separate images for primary and pistol, to facilitate showing an outline version for "Current Weapon".
+ */
+const char *ImageFnameFromWeaponID( CSWeaponID weaponID, bool isPrimary );
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Constructs a list of weapons that will satisfy the any unfinished weapon-specific career tasks:
+ * 1. If there are no unfinished career tasks, the source list is returned
+ * 2. If there are unfinished career tasks, all weapons that can be used for the tasks are added
+ * to the front of the list. This list of career weapons is sorted according to the order of
+ * weapons in the source list, so that if any weapons in the source list can be used for career
+ * tasks, they will be.
+ */
+BuyPresetWeaponList CareerWeaponList( const BuyPresetWeaponList& source, bool isPrimary, CSWeaponID currentClientID );
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns the number of clips that will have to be bought for the specified BuyPresetWeapon
+ */
+class CCSWeaponInfo;
+int CalcClipsNeeded( const BuyPresetWeapon *pWeapon, const CCSWeaponInfo *pInfo, const int ammo[MAX_AMMO_TYPES] );
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Fills the ammo array with the ammo currently owned by the local player
+ */
+void FillClientAmmo( int ammo[MAX_AMMO_TYPES] );
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns true if the client can currently buy the weapon according to class/gamemode restrictions. Returns
+ * true also if the player owns the weapon already.
+ */
+bool CanBuyWeapon( CSWeaponID currentPrimaryID, CSWeaponID currentSecondaryID, CSWeaponID weaponID );
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns the display name for a weapon, based on it's weaponID
+ */
+const wchar_t* WeaponIDToDisplayName( CSWeaponID weaponID );
+
+//--------------------------------------------------------------------------------------------------------------
+// If our weapon is NONE, we want to be able to buy up to this many clips for the current gun we're holding
+enum { NUM_CLIPS_FOR_CURRENT = 4 };
+
+#if USE_BUY_PRESETS
+extern ConVar cl_buy_favorite_quiet;
+extern ConVar cl_buy_favorite_nowarn;
+#endif // USE_BUY_PRESETS
+
+#endif // BUY_PRESETS_H