//====== Copyright © 1996-2005, 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 "c_cs_player.h" #include "bot/shared_util.h" #include "vgui/ILocalize.h" #include // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" extern ConVar cl_rebuy; extern CSWeaponID GetClientWeaponID( bool primary ); //-------------------------------------------------------------------------------------------------------------- /** * 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->GetPrimaryAmmoType():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->GetPrimaryAmmoType():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->GetPrimaryAmmoType():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->GetPrimaryAmmoType()]; int buySize = GetCSAmmoDef()->GetBuySize( pInfo->GetPrimaryAmmoType() ); 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->GetPrimaryAmmoType()]; int buySize = GetCSAmmoDef()->GetBuySize( pInfo->GetPrimaryAmmoType() ); 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 = ITEM_PRICE_HELMET; int iKevlarPrice = ITEM_PRICE_KEVLAR; int iNVGPrice = ITEM_PRICE_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; const CCSWeaponInfo *pInfo; //------------------------------------------------------------------------- // Smoke grenade pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_SMOKEGRENADE )); pInfo = GetWeaponInfo( WEAPON_SMOKEGRENADE ); int ammoType = (pInfo)?pInfo->GetPrimaryAmmoType():0; bool hasSmokeGrenade = (pWeapon && player->GetAmmoCount( ammoType )); if ( m_smokeGrenade && !hasSmokeGrenade ) { cost += pWeapon->GetWeaponPrice(); ws.m_smokeGrenade = true; hasSmokeGrenade = true; } //------------------------------------------------------------------------- // HE grenade pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_HEGRENADE )); pInfo = GetWeaponInfo( WEAPON_HEGRENADE ); ammoType = (pInfo)?pInfo->GetPrimaryAmmoType():0; bool hasHEGrenade = (pWeapon && player->GetAmmoCount( ammoType )); if ( m_HEGrenade && !hasHEGrenade ) { cost += pWeapon->GetWeaponPrice(); ws.m_HEGrenade = true; hasHEGrenade = true; } //------------------------------------------------------------------------- // Flashbang grenades pWeapon = dynamic_cast< CWeaponCSBase * >(player->GetCSWeapon( WEAPON_FLASHBANG )); pInfo = GetWeaponInfo( WEAPON_FLASHBANG ); ammoType = (pInfo)?pInfo->GetPrimaryAmmoType():0; int numFlashbangs = (!pWeapon) ? 0 : player->GetAmmoCount( ammoType ); if ( m_flashbangs && numFlashbangs < m_flashbangs ) { int count = m_flashbangs - numFlashbangs; cost += pWeapon->GetWeaponPrice() * count; ws.m_flashbangs = count; numFlashbangs += count; } //------------------------------------------------------------------------- // defuser if ( m_defuser && player->GetTeamNumber() == TEAM_CT && !player->HasDefuser() && CanBuyDefuser() ) { cost += ITEM_PRICE_DEFUSEKIT; 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; iGetCSWeaponID(); if ( weaponID == WEAPON_NONE ) weaponID = primaryID; const 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->GetPrimaryAmmoType() ); primaryAmmoBuySize = GetCSAmmoDef()->GetBuySize( primaryInfo->GetPrimaryAmmoType() ); currentCost += primaryWeaponCost; currentCost += primaryAmmoCost * primaryClips; ammo[primaryInfo->GetPrimaryAmmoType()] += 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; jGetWeaponPrice() : 0; currentCost += primaryAmmoCost * primaryClips; ammo[primaryInfo->GetPrimaryAmmoType() ] += primaryClips * primaryAmmoBuySize; } int currentCostWithoutSecondary = currentCost; CSWeaponID weaponID = secondaryWeapon->GetCSWeaponID(); if ( weaponID == WEAPON_NONE ) weaponID = secondaryID; const 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->GetPrimaryAmmoType() ); int secondaryAmmoBuySize = GetCSAmmoDef()->GetBuySize( secondaryInfo->GetPrimaryAmmoType() ); currentCost += secondaryWeaponCost; currentCost += secondaryAmmoCost * secondaryClips; ammo[secondaryInfo->GetPrimaryAmmoType() ] += 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; iGetCSWeaponID(); if ( id == WEAPON_NONE ) id = primaryID; if ( primaryWeaponToBuy->GetFillAmmo() ) { const CCSWeaponInfo *info = GetWeaponInfo( id ); if ( info ) { int maxRounds = GetCSAmmoDef()->MaxCarry( info->GetPrimaryAmmoType(), C_CSPlayer::GetLocalCSPlayer() ); int ammoCost = GetCSAmmoDef()->GetCost( info->GetPrimaryAmmoType() ); int ammoBuySize = GetCSAmmoDef()->GetBuySize( info->GetPrimaryAmmoType() ); int roundsLeft = maxRounds - ammo[info->GetPrimaryAmmoType()]; 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->GetPrimaryAmmoType()] += MIN(maxRounds, ammoBuySize); cost += ammoCost; } } } } } else if ( !stricmp( token, "SecondaryAmmo" ) ) { if ( secondaryWeaponToBuy ) { if ( secondaryWeaponToBuy->GetFillAmmo() ) { CSWeaponID weaponID = secondaryWeaponToBuy->GetCSWeaponID(); if ( weaponID == WEAPON_NONE ) weaponID = secondaryID; const CCSWeaponInfo *info = GetWeaponInfo( weaponID ); if ( info ) { int maxRounds = GetCSAmmoDef()->MaxCarry( info->GetPrimaryAmmoType(), C_CSPlayer::GetLocalCSPlayer() ); int ammoCost = GetCSAmmoDef()->GetCost( info->GetPrimaryAmmoType() ); int ammoBuySize = GetCSAmmoDef()->GetBuySize( info->GetPrimaryAmmoType() ); int roundsLeft = maxRounds - ammo[info->GetPrimaryAmmoType() ]; 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->GetPrimaryAmmoType() ] += 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 = ITEM_PRICE_HELMET; int iKevlarPrice = ITEM_PRICE_KEVLAR; int iNVGPrice = ITEM_PRICE_NVG; //------------------------------------------------------------------------- // Primary weapon const CCSWeaponInfo *primaryInfo = GetWeaponInfo( m_primaryWeapon.GetCSWeaponID() ); if ( primaryInfo ) { int primaryClips = CalcClipsNeeded( &m_primaryWeapon, primaryInfo, ammo ); int primaryWeaponCost = primaryInfo->GetWeaponPrice(); int primaryAmmoCost = GetCSAmmoDef()->GetCost( primaryInfo->GetPrimaryAmmoType() ); int primaryAmmoBuySize = GetCSAmmoDef()->GetBuySize( primaryInfo->GetPrimaryAmmoType() ); cost += primaryWeaponCost; cost += primaryAmmoCost * primaryClips; ammo[primaryInfo->GetPrimaryAmmoType()] += 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 const CCSWeaponInfo *secondaryInfo = GetWeaponInfo( m_secondaryWeapon.GetCSWeaponID() ); if ( secondaryInfo ) { int secondaryClips = CalcClipsNeeded( &m_secondaryWeapon, secondaryInfo, ammo ); int secondaryWeaponCost = secondaryInfo->GetWeaponPrice(); int secondaryAmmoCost = GetCSAmmoDef()->GetCost( secondaryInfo->GetPrimaryAmmoType() ); int secondaryAmmoBuySize = GetCSAmmoDef()->GetBuySize( secondaryInfo->GetPrimaryAmmoType() ); cost += secondaryWeaponCost; cost += secondaryAmmoCost * secondaryClips; ammo[ secondaryInfo->GetPrimaryAmmoType() ] += 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 ) { const CCSWeaponInfo *pInfo = GetWeaponInfo( WEAPON_SMOKEGRENADE ); cost += ( pInfo ) ? pInfo->GetWeaponPrice() : 0; ws.m_smokeGrenade = m_smokeGrenade; } if ( m_HEGrenade ) { const CCSWeaponInfo *pInfo = GetWeaponInfo( WEAPON_HEGRENADE ); cost += ( pInfo ) ? pInfo->GetWeaponPrice() : 0; ws.m_HEGrenade = m_HEGrenade; } const CCSWeaponInfo *pInfo = GetWeaponInfo( WEAPON_FLASHBANG ); cost += ( pInfo ) ? pInfo->GetWeaponPrice() * m_flashbangs : 0; ws.m_flashbangs = m_flashbangs; if ( m_defuser ) { cost += ITEM_PRICE_DEFUSEKIT; 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.GetCSWeaponID(); 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; iFind( "#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]; char weaponBuf[BufLen]; char clipModifier; int numClips; 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->GetPrimaryAmmoType(), C_CSPlayer::GetLocalCSPlayer() ); int buySize = GetCSAmmoDef()->GetBuySize( info->GetPrimaryAmmoType() ); 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.GetCSWeaponID() ), 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; } } //--------------------------------------------------------------------------------------------------------------