diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/cstrike/buy_presets | |
| download | archived-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.cpp | 1195 | ||||
| -rw-r--r-- | game/client/cstrike/buy_presets/buy_preset_debug.cpp | 65 | ||||
| -rw-r--r-- | game/client/cstrike/buy_presets/buy_preset_debug.h | 25 | ||||
| -rw-r--r-- | game/client/cstrike/buy_presets/buy_preset_weapon_info.cpp | 364 | ||||
| -rw-r--r-- | game/client/cstrike/buy_presets/buy_presets.cpp | 454 | ||||
| -rw-r--r-- | game/client/cstrike/buy_presets/buy_presets.h | 276 |
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 |