summaryrefslogtreecommitdiff
path: root/game/client/cstrike/buy_presets/buy_preset.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/cstrike/buy_presets/buy_preset.cpp')
-rw-r--r--game/client/cstrike/buy_presets/buy_preset.cpp1195
1 files changed, 1195 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;
+ }
+}
+
+//--------------------------------------------------------------------------------------------------------------