summaryrefslogtreecommitdiff
path: root/game/shared/cstrike
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/shared/cstrike
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/shared/cstrike')
-rw-r--r--game/shared/cstrike/achievements_cs.cpp727
-rw-r--r--game/shared/cstrike/achievements_cs.h75
-rw-r--r--game/shared/cstrike/basecsgrenade_projectile.cpp346
-rw-r--r--game/shared/cstrike/basecsgrenade_projectile.h88
-rw-r--r--game/shared/cstrike/bot/bot.cpp109
-rw-r--r--game/shared/cstrike/bot/bot.h1053
-rw-r--r--game/shared/cstrike/bot/bot_constants.h40
-rw-r--r--game/shared/cstrike/bot/bot_hide.cpp490
-rw-r--r--game/shared/cstrike/bot/bot_manager.cpp402
-rw-r--r--game/shared/cstrike/bot/bot_manager.h195
-rw-r--r--game/shared/cstrike/bot/bot_profile.cpp704
-rw-r--r--game/shared/cstrike/bot/bot_profile.h251
-rw-r--r--game/shared/cstrike/bot/bot_util.cpp604
-rw-r--r--game/shared/cstrike/bot/bot_util.h167
-rw-r--r--game/shared/cstrike/bot/improv_locomotor.h57
-rw-r--r--game/shared/cstrike/bot/nav_path.cpp1208
-rw-r--r--game/shared/cstrike/bot/nav_path.h246
-rw-r--r--game/shared/cstrike/bot/shared_util.cpp207
-rw-r--r--game/shared/cstrike/bot/shared_util.h83
-rw-r--r--game/shared/cstrike/cs_achievement_constants.h54
-rw-r--r--game/shared/cstrike/cs_achievementdefs.h210
-rw-r--r--game/shared/cstrike/cs_ammodef.cpp57
-rw-r--r--game/shared/cstrike/cs_ammodef.h55
-rw-r--r--game/shared/cstrike/cs_blackmarket.cpp147
-rw-r--r--game/shared/cstrike/cs_blackmarket.h89
-rw-r--r--game/shared/cstrike/cs_gamemovement.cpp1144
-rw-r--r--game/shared/cstrike/cs_gamerules.cpp5859
-rw-r--r--game/shared/cstrike/cs_gamerules.h537
-rw-r--r--game/shared/cstrike/cs_gamestats_shared.cpp410
-rw-r--r--game/shared/cstrike/cs_gamestats_shared.h824
-rw-r--r--game/shared/cstrike/cs_player_shared.cpp943
-rw-r--r--game/shared/cstrike/cs_playeranimstate.cpp1057
-rw-r--r--game/shared/cstrike/cs_playeranimstate.h82
-rw-r--r--game/shared/cstrike/cs_shareddefs.cpp67
-rw-r--r--game/shared/cstrike/cs_shareddefs.h261
-rw-r--r--game/shared/cstrike/cs_urlretrieveprices.cpp237
-rw-r--r--game/shared/cstrike/cs_urlretrieveprices.h29
-rw-r--r--game/shared/cstrike/cs_usermessages.cpp80
-rw-r--r--game/shared/cstrike/cs_weapon_parse.cpp455
-rw-r--r--game/shared/cstrike/cs_weapon_parse.h192
-rw-r--r--game/shared/cstrike/flashbang_projectile.cpp316
-rw-r--r--game/shared/cstrike/flashbang_projectile.h39
-rw-r--r--game/shared/cstrike/fx_cs_shared.cpp345
-rw-r--r--game/shared/cstrike/fx_cs_shared.h47
-rw-r--r--game/shared/cstrike/hegrenade_projectile.cpp94
-rw-r--r--game/shared/cstrike/hegrenade_projectile.h46
-rw-r--r--game/shared/cstrike/weapon_ak47.cpp103
-rw-r--r--game/shared/cstrike/weapon_aug.cpp160
-rw-r--r--game/shared/cstrike/weapon_awp.cpp306
-rw-r--r--game/shared/cstrike/weapon_basecsgrenade.cpp438
-rw-r--r--game/shared/cstrike/weapon_basecsgrenade.h82
-rw-r--r--game/shared/cstrike/weapon_c4.cpp1353
-rw-r--r--game/shared/cstrike/weapon_c4.h221
-rw-r--r--game/shared/cstrike/weapon_csbase.cpp1917
-rw-r--r--game/shared/cstrike/weapon_csbase.h289
-rw-r--r--game/shared/cstrike/weapon_csbasegun.cpp218
-rw-r--r--game/shared/cstrike/weapon_csbasegun.h58
-rw-r--r--game/shared/cstrike/weapon_deagle.cpp235
-rw-r--r--game/shared/cstrike/weapon_elite.cpp317
-rw-r--r--game/shared/cstrike/weapon_famas.cpp243
-rw-r--r--game/shared/cstrike/weapon_fiveseven.cpp202
-rw-r--r--game/shared/cstrike/weapon_flashbang.cpp60
-rw-r--r--game/shared/cstrike/weapon_flashbang.h49
-rw-r--r--game/shared/cstrike/weapon_g3sg1.cpp213
-rw-r--r--game/shared/cstrike/weapon_galil.cpp114
-rw-r--r--game/shared/cstrike/weapon_glock.cpp369
-rw-r--r--game/shared/cstrike/weapon_hegrenade.cpp67
-rw-r--r--game/shared/cstrike/weapon_hegrenade.h50
-rw-r--r--game/shared/cstrike/weapon_knife.cpp517
-rw-r--r--game/shared/cstrike/weapon_knife.h81
-rw-r--r--game/shared/cstrike/weapon_m249.cpp105
-rw-r--r--game/shared/cstrike/weapon_m3.cpp293
-rw-r--r--game/shared/cstrike/weapon_m4a1.cpp351
-rw-r--r--game/shared/cstrike/weapon_mac10.cpp130
-rw-r--r--game/shared/cstrike/weapon_mp5navy.cpp129
-rw-r--r--game/shared/cstrike/weapon_p228.cpp204
-rw-r--r--game/shared/cstrike/weapon_p90.cpp97
-rw-r--r--game/shared/cstrike/weapon_scout.cpp220
-rw-r--r--game/shared/cstrike/weapon_sg550.cpp214
-rw-r--r--game/shared/cstrike/weapon_sg552.cpp170
-rw-r--r--game/shared/cstrike/weapon_smokegrenade.cpp49
-rw-r--r--game/shared/cstrike/weapon_smokegrenade.h52
-rw-r--r--game/shared/cstrike/weapon_tmp.cpp106
-rw-r--r--game/shared/cstrike/weapon_ump45.cpp132
-rw-r--r--game/shared/cstrike/weapon_usp.cpp395
-rw-r--r--game/shared/cstrike/weapon_xm1014.cpp304
86 files changed, 31341 insertions, 0 deletions
diff --git a/game/shared/cstrike/achievements_cs.cpp b/game/shared/cstrike/achievements_cs.cpp
new file mode 100644
index 0000000..9996589
--- /dev/null
+++ b/game/shared/cstrike/achievements_cs.cpp
@@ -0,0 +1,727 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "cbase.h"
+#include <time.h>
+
+#ifdef CLIENT_DLL
+
+#include "achievementmgr.h"
+#include "baseachievement.h"
+#include "cs_achievement_constants.h"
+#include "c_cs_team.h"
+#include "c_cs_player.h"
+#include "c_cs_playerresource.h"
+#include "cs_gamerules.h"
+#include "achievements_cs.h"
+#include "cs_gamestats_shared.h"
+#include "cs_client_gamestats.h"
+
+
+// [dwenger] Necessary for stats display
+#include "cs_achievements_and_stats_interface.h"
+
+// [dwenger] Necessary for sorting achievements by award time
+#include <vgui/ISystem.h>
+#include "../../src/public/vgui_controls/Controls.h"
+
+
+ConVar achievements_easymode( "achievement_easymode", "0", FCVAR_CLIENTDLL | FCVAR_DEVELOPMENTONLY,
+ "Enables all stat-based achievements to be earned at 10% of goals" );
+
+// global achievement mgr for CS
+CAchievementMgr g_AchievementMgrCS;
+
+// [dwenger] Necessary for achievement / stats panel
+CSAchievementsAndStatsInterface g_AchievementsAndStatsInterfaceCS;
+
+
+CCSBaseAchievement::CCSBaseAchievement()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Determines the timestamp when the achievement is awarded
+//-----------------------------------------------------------------------------
+void CCSBaseAchievement::OnAchieved()
+{
+// __time32_t unlockTime;
+// _time32(&unlockTime);
+// SetUnlockTime(unlockTime);
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the time values when the achievement was awarded.
+//-----------------------------------------------------------------------------
+bool CCSBaseAchievement::GetAwardTime( int& year, int& month, int& day, int& hour, int& minute, int& second )
+{
+ time_t t = GetUnlockTime();
+
+ if ( t != 0 )
+ {
+ struct tm structuredTime;
+
+ Plat_localtime(&t, &structuredTime);
+
+ year = structuredTime.tm_year + 1900;
+ month = structuredTime.tm_mon + 1; // 0..11
+ day = structuredTime.tm_mday;
+ hour = structuredTime.tm_hour;
+ minute = structuredTime.tm_min;
+ second = structuredTime.tm_sec;
+
+ return true;
+ }
+
+ return false;
+}
+
+void CCSBaseAchievement::GetSettings( KeyValues* pNodeOut )
+{
+ BaseClass::GetSettings(pNodeOut);
+
+ pNodeOut->SetInt("unlockTime", GetUnlockTime());
+}
+
+void CCSBaseAchievement::ApplySettings( /* const */ KeyValues* pNodeIn )
+{
+ BaseClass::ApplySettings(pNodeIn);
+
+ SetUnlockTime(pNodeIn->GetInt("unlockTime"));
+}
+
+
+/**
+* Meta Achievement base class methods
+*/
+CAchievement_Meta::CAchievement_Meta() :
+ m_CallbackUserAchievement( this, &CAchievement_Meta::Steam_OnUserAchievementStored )
+{
+}
+
+void CAchievement_Meta::Init()
+{
+ SetFlags( ACH_SAVE_GLOBAL );
+ SetGoal( 1 );
+}
+
+void CAchievement_Meta::Steam_OnUserAchievementStored( UserAchievementStored_t *pUserAchievementStored )
+{
+ if ( IsAchieved() )
+ return;
+
+ int iAchieved = 0;
+
+ FOR_EACH_VEC(m_requirements, i)
+ {
+ IAchievement* pAchievement = (IAchievement*)m_pAchievementMgr->GetAchievementByID(m_requirements[i]);
+ Assert ( pAchievement );
+
+ if ( pAchievement->IsAchieved() )
+ iAchieved++;
+ else
+ break;
+ }
+
+ if ( iAchieved == m_requirements.Count() )
+ {
+ AwardAchievement();
+ }
+}
+
+void CAchievement_Meta::AddRequirement( int nAchievementId )
+{
+ m_requirements.AddToTail(nAchievementId);
+}
+
+#if 0
+bool CheckWinNoEnemyCaps( IGameEvent *event, int iRole );
+
+// Grace period that we allow a player to start after level init and still consider them to be participating for the full round. This is fairly generous
+// because it can in some cases take a client several minutes to connect with respect to when the server considers the game underway
+#define CS_FULL_ROUND_GRACE_PERIOD ( 4 * 60.0f )
+
+bool IsLocalCSPlayerClass( int iClass );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCSBaseAchievementFullRound::Init()
+{
+ m_iFlags |= ACH_FILTER_FULL_ROUND_ONLY;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCSBaseAchievementFullRound::ListenForEvents()
+{
+ ListenForGameEvent( "teamplay_round_win" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCSBaseAchievementFullRound::FireGameEvent_Internal( IGameEvent *event )
+{
+ if ( 0 == Q_strcmp( event->GetName(), "teamplay_round_win" ) )
+ {
+ C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( pLocalPlayer )
+ {
+ // is the player currently on a game team?
+ int iTeam = pLocalPlayer->GetTeamNumber();
+ if ( iTeam >= FIRST_GAME_TEAM )
+ {
+ float flRoundTime = event->GetFloat( "round_time", 0 );
+ if ( flRoundTime > 0 )
+ {
+ Event_OnRoundComplete( flRoundTime, event );
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CCSBaseAchievementFullRound::PlayerWasInEntireRound( float flRoundTime )
+{
+ float flTeamplayStartTime = m_pAchievementMgr->GetTeamplayStartTime();
+ if ( flTeamplayStartTime > 0 )
+ {
+ // has the player been present and on a game team since the start of this round (minus a grace period)?
+ if ( flTeamplayStartTime < ( gpGlobals->curtime - flRoundTime ) + CS_FULL_ROUND_GRACE_PERIOD )
+ return true;
+ }
+ return false;
+}
+#endif
+
+//Base class for all achievements to kill x players with a given weapon
+class CAchievement_StatGoal: public CCSBaseAchievement
+{
+public:
+ void SetStatId(CSStatType_t stat)
+ {
+ m_StatId = stat;
+ }
+private:
+ virtual void Init()
+ {
+ SetFlags( ACH_SAVE_GLOBAL );
+ }
+
+ void OnPlayerStatsUpdate()
+ {
+ // when stats are updated by server, use most recent stat value
+ const StatsCollection_t& roundStats = g_CSClientGameStats.GetLifetimeStats();
+
+ int iOldCount = GetCount();
+ SetCount(roundStats[m_StatId]);
+ if ( GetCount() != iOldCount )
+ {
+ m_pAchievementMgr->SetDirty(true);
+ }
+
+ int iGoal = GetGoal();
+ if (!IsAchieved() && iGoal > 0 )
+ {
+ if (achievements_easymode.GetBool())
+ {
+ iGoal /= 10;
+ if ( iGoal == 0 )
+ iGoal = 1;
+ }
+
+ if ( GetCount() >= iGoal )
+ {
+ AwardAchievement();
+ }
+ }
+ }
+ CSStatType_t m_StatId;
+};
+
+#define DECLARE_ACHIEVEMENT_STATGOAL( achievementID, achievementName, iPointValue, iStatId, iGoal ) \
+ static CBaseAchievement *Create_##achievementID( void ) \
+{ \
+ CAchievement_StatGoal *pAchievement = new CAchievement_StatGoal(); \
+ pAchievement->SetAchievementID( achievementID ); \
+ pAchievement->SetName( achievementName ); \
+ pAchievement->SetPointValue( iPointValue ); \
+ pAchievement->SetHideUntilAchieved( false ); \
+ pAchievement->SetStatId(iStatId); \
+ pAchievement->SetGoal( iGoal ); \
+ return pAchievement; \
+}; \
+static CBaseAchievementHelper g_##achievementID##_Helper( Create_##achievementID );
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsLow, "KILL_ENEMY_LOW", 10, CSSTAT_KILLS, 25); //25
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsMed, "KILL_ENEMY_MED", 10, CSSTAT_KILLS, 500); //500
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsHigh, "KILL_ENEMY_HIGH", 10, CSSTAT_KILLS, 10000); //10000
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinRoundsLow, "WIN_ROUNDS_LOW", 10, CSSTAT_ROUNDS_WON, 10); //10
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinRoundsMed, "WIN_ROUNDS_MED", 10, CSSTAT_ROUNDS_WON, 200); //200
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinRoundsHigh, "WIN_ROUNDS_HIGH", 10, CSSTAT_ROUNDS_WON, 5000); //5000
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinPistolRoundsLow, "WIN_PISTOLROUNDS_LOW", 10, CSSTAT_PISTOLROUNDS_WON, 5); //5
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinPistolRoundsMed, "WIN_PISTOLROUNDS_MED", 10, CSSTAT_PISTOLROUNDS_WON, 25); //25
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinPistolRoundsHigh, "WIN_PISTOLROUNDS_HIGH", 10, CSSTAT_PISTOLROUNDS_WON, 250); //250
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSMoneyEarnedLow, "EARN_MONEY_LOW", 10, CSSTAT_MONEY_EARNED, 125000); //125000
+DECLARE_ACHIEVEMENT_STATGOAL(CSMoneyEarnedMed, "EARN_MONEY_MED", 10, CSSTAT_MONEY_EARNED, 2500000); //2500000
+DECLARE_ACHIEVEMENT_STATGOAL(CSMoneyEarnedHigh, "EARN_MONEY_HIGH", 10, CSSTAT_MONEY_EARNED, 50000000); //50000000
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSGiveDamageLow, "GIVE_DAMAGE_LOW", 10, CSSTAT_DAMAGE, 2500); //2500
+DECLARE_ACHIEVEMENT_STATGOAL(CSGiveDamageMed, "GIVE_DAMAGE_MED", 10, CSSTAT_DAMAGE, 50000); //50000
+DECLARE_ACHIEVEMENT_STATGOAL(CSGiveDamageHigh, "GIVE_DAMAGE_HIGH", 10, CSSTAT_DAMAGE, 1000000); //1000000
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsDeagle, "KILL_ENEMY_DEAGLE", 5, CSSTAT_KILLS_DEAGLE, 200); //200
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsUSP, "KILL_ENEMY_USP", 5, CSSTAT_KILLS_USP, 200); //200
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsGlock, "KILL_ENEMY_GLOCK", 5, CSSTAT_KILLS_GLOCK, 200); //200
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsP228, "KILL_ENEMY_P228", 5, CSSTAT_KILLS_P228, 200); //200
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsElite, "KILL_ENEMY_ELITE", 5, CSSTAT_KILLS_ELITE, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsFiveSeven, "KILL_ENEMY_FIVESEVEN", 5, CSSTAT_KILLS_FIVESEVEN, 100); //100
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsAWP, "KILL_ENEMY_AWP", 5, CSSTAT_KILLS_AWP, 1000); //1000
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsAK47, "KILL_ENEMY_AK47", 5, CSSTAT_KILLS_AK47, 1000); //1000
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsM4A1, "KILL_ENEMY_M4A1", 5, CSSTAT_KILLS_M4A1, 1000); //1000
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsAUG, "KILL_ENEMY_AUG", 5, CSSTAT_KILLS_AUG, 500); //500
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsSG552, "KILL_ENEMY_SG552", 5, CSSTAT_KILLS_SG552, 500); //500
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsSG550, "KILL_ENEMY_SG550", 5, CSSTAT_KILLS_SG550, 500); //500
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsGALIL, "KILL_ENEMY_GALIL", 5, CSSTAT_KILLS_GALIL, 500); //500
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsFAMAS, "KILL_ENEMY_FAMAS", 5, CSSTAT_KILLS_FAMAS, 500); //500
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsScout, "KILL_ENEMY_SCOUT", 5, CSSTAT_KILLS_SCOUT, 1000); //1000
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsG3SG1, "KILL_ENEMY_G3SG1", 5, CSSTAT_KILLS_G3SG1, 500); //500
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsP90, "KILL_ENEMY_P90", 5, CSSTAT_KILLS_P90, 1000); //1000
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsMP5NAVY, "KILL_ENEMY_MP5NAVY", 5, CSSTAT_KILLS_MP5NAVY, 1000); //1000
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsTMP, "KILL_ENEMY_TMP", 5, CSSTAT_KILLS_TMP, 500); //500
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsMAC10, "KILL_ENEMY_MAC10", 5, CSSTAT_KILLS_MAC10, 500); //500
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsUMP45, "KILL_ENEMY_UMP45", 5, CSSTAT_KILLS_UMP45, 1000); //1000
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsM3, "KILL_ENEMY_M3", 5, CSSTAT_KILLS_M3, 200); //200
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsXM1014, "KILL_ENEMY_XM1014", 5, CSSTAT_KILLS_XM1014, 200); //200
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsM249, "KILL_ENEMY_M249", 5, CSSTAT_KILLS_M249, 500); //500
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsKnife, "KILL_ENEMY_KNIFE", 5, CSSTAT_KILLS_KNIFE, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSEnemyKillsHEGrenade, "KILL_ENEMY_HEGRENADE", 5, CSSTAT_KILLS_HEGRENADE, 500); //500
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSHeadshots, "HEADSHOTS", 5, CSSTAT_KILLS_HEADSHOT, 250); //250
+DECLARE_ACHIEVEMENT_STATGOAL(CSKillsEnemyWeapon, "KILLS_ENEMY_WEAPON", 5, CSSTAT_KILLS_ENEMY_WEAPON, 100); //100
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSKillEnemyBlinded, "KILL_ENEMY_BLINDED", 5, CSSTAT_KILLS_ENEMY_BLINDED, 25); //25
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSDefuseBombsLow, "BOMB_DEFUSE_LOW", 5, CSSTAT_NUM_BOMBS_DEFUSED, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSPlantBombsLow, "BOMB_PLANT_LOW", 5, CSSTAT_NUM_BOMBS_PLANTED, 100); //100
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSRescueHostagesLow, "RESCUE_HOSTAGES_LOW", 5, CSSTAT_NUM_HOSTAGES_RESCUED, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSRescueHostagesMid, "RESCUE_HOSTAGES_MED", 5, CSSTAT_NUM_HOSTAGES_RESCUED, 500); //500
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinKnifeFightsLow, "WIN_KNIFE_FIGHTS_LOW", 5, CSSTAT_KILLS_KNIFE_FIGHT, 1); //1
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinKnifeFightsHigh, "WIN_KNIFE_FIGHTS_HIGH", 5, CSSTAT_KILLS_KNIFE_FIGHT, 100); //100
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSDecalSprays, "DECAL_SPRAYS", 5, CSSTAT_DECAL_SPRAYS, 100); //100
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSNightvisionDamage, "NIGHTVISION_DAMAGE", 5, CSSTAT_NIGHTVISION_DAMAGE, 5000); //5000
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSKillSnipers, "KILL_SNIPERS", 5, CSSTAT_KILLS_AGAINST_ZOOMED_SNIPER, 100); //100
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinMapCS_ASSAULT, "WIN_MAP_CS_ASSAULT", 5, CSSTAT_MAP_WINS_CS_ASSAULT, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinMapCS_COMPOUND, "WIN_MAP_CS_COMPOUND", 5, CSSTAT_MAP_WINS_CS_COMPOUND, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinMapCS_HAVANA, "WIN_MAP_CS_HAVANA", 5, CSSTAT_MAP_WINS_CS_HAVANA, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinMapCS_ITALY, "WIN_MAP_CS_ITALY", 5, CSSTAT_MAP_WINS_CS_ITALY, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinMapCS_MILITIA, "WIN_MAP_CS_MILITIA", 5, CSSTAT_MAP_WINS_CS_MILITIA, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinMapCS_OFFICE, "WIN_MAP_CS_OFFICE", 5, CSSTAT_MAP_WINS_CS_OFFICE, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinMapDE_AZTEC, "WIN_MAP_DE_AZTEC", 5, CSSTAT_MAP_WINS_DE_AZTEC, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinMapDE_CBBLE, "WIN_MAP_DE_CBBLE", 5, CSSTAT_MAP_WINS_DE_CBBLE, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinMapDE_CHATEAU, "WIN_MAP_DE_CHATEAU", 5, CSSTAT_MAP_WINS_DE_CHATEAU, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinMapDE_DUST2, "WIN_MAP_DE_DUST2", 5, CSSTAT_MAP_WINS_DE_DUST2, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinMapDE_DUST, "WIN_MAP_DE_DUST", 5, CSSTAT_MAP_WINS_DE_DUST, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinMapDE_INFERNO, "WIN_MAP_DE_INFERNO", 5, CSSTAT_MAP_WINS_DE_INFERNO, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinMapDE_NUKE, "WIN_MAP_DE_NUKE", 5, CSSTAT_MAP_WINS_DE_NUKE, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinMapDE_PIRANESI, "WIN_MAP_DE_PIRANESI", 5, CSSTAT_MAP_WINS_DE_PIRANESI, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinMapDE_PORT, "WIN_MAP_DE_PORT", 5, CSSTAT_MAP_WINS_DE_PORT, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinMapDE_PRODIGY, "WIN_MAP_DE_PRODIGY", 5, CSSTAT_MAP_WINS_DE_PRODIGY, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinMapDE_TIDES, "WIN_MAP_DE_TIDES", 5, CSSTAT_MAP_WINS_DE_TIDES, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSWinMapDE_TRAIN, "WIN_MAP_DE_TRAIN", 5, CSSTAT_MAP_WINS_DE_TRAIN, 100); //100
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSDonateWeapons, "DONATE_WEAPONS", 5, CSSTAT_WEAPONS_DONATED, 100); //100
+
+DECLARE_ACHIEVEMENT_STATGOAL(CSDominationsLow, "DOMINATIONS_LOW", 5, CSSTAT_DOMINATIONS, 1); //1
+DECLARE_ACHIEVEMENT_STATGOAL(CSDominationsHigh, "DOMINATIONS_HIGH", 5, CSSTAT_DOMINATIONS, 10); //10
+DECLARE_ACHIEVEMENT_STATGOAL(CSDominationOverkillsLow, "DOMINATION_OVERKILLS_LOW", 5, CSSTAT_DOMINATION_OVERKILLS, 1); //1
+DECLARE_ACHIEVEMENT_STATGOAL(CSDominationOverkillsHigh, "DOMINATION_OVERKILLS_HIGH",5, CSSTAT_DOMINATION_OVERKILLS, 100); //100
+DECLARE_ACHIEVEMENT_STATGOAL(CSRevengesLow, "REVENGES_LOW", 5, CSSTAT_REVENGES, 1); //1
+DECLARE_ACHIEVEMENT_STATGOAL(CSRevengesHigh, "REVENGES_HIGH", 5, CSSTAT_REVENGES, 20); //20
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Generic server awarded achievement
+//-----------------------------------------------------------------------------
+class CAchievementCS_ServerAwarded : public CCSBaseAchievement
+{
+ virtual void Init()
+ {
+ SetGoal(1);
+ SetFlags( ACH_SAVE_GLOBAL );
+ }
+
+ // server fires an event for this achievement, no other code within achievement necessary
+};
+
+#define DECLARE_ACHIEVEMENT_SERVERAWARDED(achievementID, achievementName, iPointValue) \
+ static CBaseAchievement *Create_##achievementID( void ) \
+{ \
+ CAchievementCS_ServerAwarded *pAchievement = new CAchievementCS_ServerAwarded( ); \
+ pAchievement->SetAchievementID( achievementID ); \
+ pAchievement->SetName( achievementName ); \
+ pAchievement->SetPointValue( iPointValue ); \
+ return pAchievement; \
+}; \
+static CBaseAchievementHelper g_##achievementID##_Helper( Create_##achievementID );
+
+
+
+// server triggered achievements
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSBombDefuseCloseCall, "BOMB_DEFUSE_CLOSE_CALL", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSDefuseAndNeededKit, "BOMB_DEFUSE_NEEDED_KIT", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKilledDefuser, "KILL_BOMB_DEFUSER", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSWinBombPlant, "WIN_BOMB_PLANT", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSWinBombDefuse, "WIN_BOMB_DEFUSE", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSPlantBombWithin25Seconds, "BOMB_PLANT_IN_25_SECONDS", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSRescueAllHostagesInARound, "RESCUE_ALL_HOSTAGES", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKillEnemyWithFormerGun, "KILL_WITH_OWN_GUN", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKillingSpree, "KILLING_SPREE", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKillTwoWithOneShot, "KILL_TWO_WITH_ONE_SHOT", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKillEnemyReloading, "KILL_ENEMY_RELOADING", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKillsWithMultipleGuns, "KILLS_WITH_MULTIPLE_GUNS", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSPosthumousGrenadeKill, "DEAD_GRENADE_KILL", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKillEnemyTeam, "KILL_ENEMY_TEAM", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSLastPlayerAlive, "LAST_PLAYER_ALIVE", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKillEnemyLastBullet, "KILL_ENEMY_LAST_BULLET", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKillingSpreeEnder, "KILLING_SPREE_ENDER", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKillEnemiesWhileBlind, "KILL_ENEMIES_WHILE_BLIND", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKillEnemiesWhileBlindHard, "KILL_ENEMIES_WHILE_BLIND_HARD", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSDamageNoKill, "DAMAGE_NO_KILL", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKillLowDamage, "KILL_LOW_DAMAGE", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKilledRescuer, "KILL_HOSTAGE_RESCUER", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSSurviveGrenade, "SURVIVE_GRENADE", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKilledDefuserWithGrenade, "KILLED_DEFUSER_WITH_GRENADE", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSSurvivedHeadshotDueToHelmet, "SURVIVED_HEADSHOT_DUE_TO_HELMET", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKillSniperWithSniper, "KILL_SNIPER_WITH_SNIPER", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKillSniperWithKnife, "KILL_SNIPER_WITH_KNIFE", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSHipShot, "HIP_SHOT", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKillWhenAtLowHealth, "KILL_WHEN_AT_LOW_HEALTH", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSGrenadeMultikill, "GRENADE_MULTIKILL", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSBombMultikill, "BOMB_MULTIKILL", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSPistolRoundKnifeKill, "PISTOL_ROUND_KNIFE_KILL", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSFastRoundWin, "FAST_ROUND_WIN", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSSurviveManyAttacks, "SURVIVE_MANY_ATTACKS", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSGooseChase, "GOOSE_CHASE", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSWinBombPlantAfterRecovery, "WIN_BOMB_PLANT_AFTER_RECOVERY", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSLosslessExtermination, "LOSSLESS_EXTERMINATION", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSFlawlessVictory, "FLAWLESS_VICTORY", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSWinDualDuel, "WIN_DUAL_DUEL", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSFastHostageRescue, "FAST_HOSTAGE_RESCUE", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSBreakWindows, "BREAK_WINDOWS", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSBreakProps, "BREAK_PROPS", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSUnstoppableForce, "UNSTOPPABLE_FORCE", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSImmovableObject, "IMMOVABLE_OBJECT", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSHeadshotsInRound, "HEADSHOTS_IN_ROUND", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKillWhileInAir, "KILL_WHILE_IN_AIR", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKillEnemyInAir, "KILL_ENEMY_IN_AIR", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKillerAndEnemyInAir, "KILLER_AND_ENEMY_IN_AIR", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSSilentWin, "SILENT_WIN", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSBloodlessVictory, "BLOODLESS_VICTORY", 5);
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSWinRoundsWithoutBuying, "WIN_ROUNDS_WITHOUT_BUYING", 5)
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSDefuseDefense, "DEFUSE_DEFENSE", 5)
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSKillBombPickup, "KILL_BOMB_PICKUP", 5)
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSSameUniform, "SAME_UNIFORM", 5)
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSConcurrentDominations, "CONCURRENT_DOMINATIONS", 5)
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSDominationOverkillsMatch, "DOMINATION_OVERKILLS_MATCH", 5)
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSExtendedDomination, "EXTENDED_DOMINATION", 5)
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSCauseFriendlyFireWithFlashbang, "CAUSE_FRIENDLY_FIRE_WITH_FLASHBANG", 5)
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSWinClanMatch, "WIN_CLAN_MATCH", 5)
+DECLARE_ACHIEVEMENT_SERVERAWARDED(CSSnipeTwoFromSameSpot, "SNIPE_TWO_FROM_SAME_SPOT", 5 )
+
+
+
+
+//-----------------------------------------------------------------------------
+// Meta Achievements
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Purpose: Get all the pistol achievements
+//-----------------------------------------------------------------------------
+class CAchievementCS_PistolMaster : public CAchievement_Meta
+{
+ DECLARE_CLASS( CAchievementCS_PistolMaster, CAchievement_Meta );
+ virtual void Init()
+ {
+ BaseClass::Init();
+ AddRequirement(CSEnemyKillsDeagle);
+ AddRequirement(CSEnemyKillsUSP);
+ AddRequirement(CSEnemyKillsGlock);
+ AddRequirement(CSEnemyKillsP228);
+ AddRequirement(CSEnemyKillsElite);
+ AddRequirement(CSEnemyKillsFiveSeven);
+ }
+};
+DECLARE_ACHIEVEMENT(CAchievementCS_PistolMaster, CSMetaPistol, "META_PISTOL", 10);
+
+//-----------------------------------------------------------------------------
+// Purpose: Get all the rifle achievements
+//-----------------------------------------------------------------------------
+class CAchievementCS_RifleMaster : public CAchievement_Meta
+{
+ DECLARE_CLASS( CAchievementCS_RifleMaster, CAchievement_Meta );
+ virtual void Init()
+ {
+ BaseClass::Init();
+ AddRequirement(CSEnemyKillsAWP);
+ AddRequirement(CSEnemyKillsAK47);
+ AddRequirement(CSEnemyKillsM4A1);
+ AddRequirement(CSEnemyKillsAUG);
+ AddRequirement(CSEnemyKillsSG552);
+ AddRequirement(CSEnemyKillsSG550);
+ AddRequirement(CSEnemyKillsGALIL);
+ AddRequirement(CSEnemyKillsFAMAS);
+ AddRequirement(CSEnemyKillsScout);
+ AddRequirement(CSEnemyKillsG3SG1);
+ }
+};
+DECLARE_ACHIEVEMENT(CAchievementCS_RifleMaster, CSMetaRifle, "META_RIFLE", 10);
+
+//-----------------------------------------------------------------------------
+// Purpose: Get all the SMG achievements
+//-----------------------------------------------------------------------------
+class CAchievementCS_SubMachineGunMaster : public CAchievement_Meta
+{
+ DECLARE_CLASS( CAchievementCS_SubMachineGunMaster, CAchievement_Meta );
+ virtual void Init()
+ {
+ BaseClass::Init();
+ AddRequirement(CSEnemyKillsP90);
+ AddRequirement(CSEnemyKillsMP5NAVY);
+ AddRequirement(CSEnemyKillsTMP);
+ AddRequirement(CSEnemyKillsMAC10);
+ AddRequirement(CSEnemyKillsUMP45);
+ }
+};
+DECLARE_ACHIEVEMENT(CAchievementCS_SubMachineGunMaster, CSMetaSMG, "META_SMG", 10);
+
+//-----------------------------------------------------------------------------
+// Purpose: Get all the Shotgun achievements
+//-----------------------------------------------------------------------------
+class CAchievementCS_ShotgunMaster : public CAchievement_Meta
+{
+ DECLARE_CLASS( CAchievementCS_ShotgunMaster, CAchievement_Meta );
+ virtual void Init()
+ {
+ BaseClass::Init();
+ AddRequirement(CSEnemyKillsM3);
+ AddRequirement(CSEnemyKillsXM1014);
+ }
+};
+DECLARE_ACHIEVEMENT(CAchievementCS_ShotgunMaster, CSMetaShotgun, "META_SHOTGUN", 10);
+
+//-----------------------------------------------------------------------------
+// Purpose: Get every weapon achievement
+//-----------------------------------------------------------------------------
+class CAchievementCS_WeaponMaster : public CAchievement_Meta
+{
+ DECLARE_CLASS( CAchievementCS_WeaponMaster, CAchievement_Meta );
+ virtual void Init()
+ {
+ BaseClass::Init();
+ AddRequirement(CSMetaPistol);
+ AddRequirement(CSMetaRifle);
+ AddRequirement(CSMetaSMG);
+ AddRequirement(CSMetaShotgun);
+ AddRequirement(CSEnemyKillsM249);
+ AddRequirement(CSEnemyKillsKnife);
+ AddRequirement(CSEnemyKillsHEGrenade);
+ }
+};
+DECLARE_ACHIEVEMENT(CAchievementCS_WeaponMaster, CSMetaWeaponMaster, "META_WEAPONMASTER", 50);
+
+
+
+class CAchievementCS_KillWithAllWeapons : public CCSBaseAchievement
+{
+ void Init()
+ {
+ SetFlags( ACH_SAVE_GLOBAL );
+ SetGoal( 1 );
+ }
+
+ void OnPlayerStatsUpdate()
+ {
+ const StatsCollection_t& roundStats = g_CSClientGameStats.GetLifetimeStats();
+
+ //Loop through all weapons we care about and make sure we got a kill with each.
+ for (int i = 0; WeaponName_StatId_Table[i].killStatId != CSSTAT_UNDEFINED; ++i)
+ {
+ CSStatType_t statId = WeaponName_StatId_Table[i].killStatId;
+
+ if ( roundStats[statId] == 0)
+ {
+ return;
+ }
+ }
+
+ //If we haven't bailed yet, award the achievement.
+ IncrementCount();
+ }
+};
+DECLARE_ACHIEVEMENT( CAchievementCS_KillWithAllWeapons, CSKillWithEveryWeapon, "KILL_WITH_EVERY_WEAPON", 5 );
+
+class CAchievementCS_FriendsSameUniform : public CCSBaseAchievement
+{
+ void Init()
+ {
+ SetFlags(ACH_SAVE_GLOBAL);
+ SetGoal(1);
+ }
+
+ void ListenForEvents()
+ {
+ ListenForGameEvent( "round_start" );
+ }
+
+ void FireGameEvent_Internal( IGameEvent *event )
+ {
+ if ( Q_strcmp( event->GetName(), "round_start" ) == 0 )
+ {
+ int localPlayerIndex = GetLocalPlayerIndex();
+ C_CSPlayer* pLocalPlayer = ToCSPlayer(UTIL_PlayerByIndex(localPlayerIndex));
+
+ // Initialize all to 1, since the local player doesn't get counted as we loop.
+ int numPlayersOnTeam = 1;
+ int numFriendsOnTeam = 1;
+ int numMatchingFriendsOnTeam = 1;
+
+ if (pLocalPlayer)
+ {
+ int localPlayerClass = pLocalPlayer->PlayerClass();
+ int localPlayerTeam = pLocalPlayer->GetTeamNumber();
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ if ( i != localPlayerIndex)
+ {
+ CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
+
+ if (pPlayer)
+ {
+ if (pPlayer->GetTeamNumber() == localPlayerTeam)
+ {
+ ++numPlayersOnTeam;
+ if (pLocalPlayer->HasPlayerAsFriend(pPlayer) )
+ {
+ ++numFriendsOnTeam;
+ if ( pPlayer->PlayerClass() == localPlayerClass )
+ ++numMatchingFriendsOnTeam;
+ }
+ }
+ }
+ }
+ }
+
+ if (numMatchingFriendsOnTeam >= AchievementConsts::FriendsSameUniform_MinPlayers )
+ {
+ AwardAchievement();
+ }
+ }
+ }
+ }
+};
+DECLARE_ACHIEVEMENT( CAchievementCS_FriendsSameUniform, CSFriendsSameUniform, "FRIENDS_SAME_UNIFORM", 5 );
+
+
+
+class CAchievementCS_AvengeFriend : public CCSBaseAchievement
+{
+ void Init()
+ {
+ SetFlags(ACH_SAVE_GLOBAL);
+ SetGoal(1);
+ }
+
+ void ListenForEvents()
+ {
+ ListenForGameEvent( "player_avenged_teammate" );
+ }
+
+ void FireGameEvent_Internal( IGameEvent *event )
+ {
+ if ( Q_strcmp( event->GetName(), "player_avenged_teammate" ) == 0 )
+ {
+ int localPlayerIndex = GetLocalPlayerIndex();
+ C_CSPlayer* pLocalPlayer = ToCSPlayer(UTIL_PlayerByIndex(localPlayerIndex));
+
+ //for debugging
+ //int eventId = event->GetInt( "avenger_id" );
+ //int localUserId = pLocalPlayer->GetUserID();
+
+ if (pLocalPlayer && pLocalPlayer->GetUserID() == event->GetInt( "avenger_id" ))
+ {
+ int avengedPlayerIndex = engine->GetPlayerForUserID( event->GetInt( "avenged_player_id" ) );
+
+ if ( avengedPlayerIndex > 0 )
+ {
+ C_CSPlayer* pAvengedPlayer = ToCSPlayer(UTIL_PlayerByIndex(avengedPlayerIndex));
+ if (pAvengedPlayer && pLocalPlayer->HasPlayerAsFriend(pAvengedPlayer))
+ {
+ AwardAchievement();
+ }
+ }
+ }
+ }
+ }
+};
+DECLARE_ACHIEVEMENT( CAchievementCS_AvengeFriend, CSAvengeFriend, "AVENGE_FRIEND", 5 );
+
+
+
+class CAchievementCS_CollectHolidayGifts : public CCSBaseAchievement
+{
+ void Init()
+ {
+ SetFlags( ACH_SAVE_GLOBAL );
+ SetGoal( 3 );
+ SetStoreProgressInSteam( true );
+ }
+
+ virtual void ListenForEvents()
+ {
+ ListenForGameEvent( "christmas_gift_grab" );
+ }
+
+ void FireGameEvent_Internal( IGameEvent *event )
+ {
+ if ( !UTIL_IsHolidayActive( 3 /*kHoliday_Christmas*/ ) )
+ return;
+
+ if ( Q_strcmp( event->GetName(), "christmas_gift_grab" ) == 0 )
+ {
+ int iPlayer = engine->GetPlayerForUserID( event->GetInt( "userid" ) );
+ CBaseEntity *pPlayer = UTIL_PlayerByIndex( iPlayer );
+
+ if ( pPlayer && pPlayer == C_CSPlayer::GetLocalCSPlayer() )
+ {
+ IncrementCount();
+ }
+ }
+ }
+};
+DECLARE_ACHIEVEMENT( CAchievementCS_CollectHolidayGifts, CSCollectHolidayGifts, "COLLECT_GIFTS", 5 );
+
+#endif // CLIENT_DLL
+
diff --git a/game/shared/cstrike/achievements_cs.h b/game/shared/cstrike/achievements_cs.h
new file mode 100644
index 0000000..817a73e
--- /dev/null
+++ b/game/shared/cstrike/achievements_cs.h
@@ -0,0 +1,75 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+
+#include "cbase.h"
+
+#ifdef CLIENT_DLL
+
+bool CheckWinNoEnemyCaps( IGameEvent *event, int iRole );
+bool IsLocalCSPlayerClass( int iClass );
+bool GameRulesAllowsAchievements( void );
+
+//----------------------------------------------------------------------------------------------------------------
+// Base class for all CS achievements
+class CCSBaseAchievement : public CBaseAchievement
+{
+ DECLARE_CLASS( CCSBaseAchievement, CBaseAchievement );
+public:
+
+ CCSBaseAchievement();
+
+ virtual void GetSettings( KeyValues* pNodeOut ); // serialize
+ virtual void ApplySettings( /* const */ KeyValues* pNodeIn ); // unserialize
+
+ // [dwenger] Necessary for sorting achievements by award time
+ virtual void OnAchieved();
+ bool GetAwardTime( int& year, int& month, int& day, int& hour, int& minute, int& second );
+
+ int64 GetSortKey() const { return GetUnlockTime(); }
+};
+
+
+//----------------------------------------------------------------------------------------------------------------
+// Helper class for achievements that check that the player was playing on a game team for the full round
+class CCSBaseAchievementFullRound : public CCSBaseAchievement
+{
+ DECLARE_CLASS( CCSBaseAchievementFullRound, CCSBaseAchievement );
+public:
+ virtual void Init() ;
+ virtual void ListenForEvents();
+ void FireGameEvent_Internal( IGameEvent *event );
+ bool PlayerWasInEntireRound( float flRoundTime );
+
+ virtual void Event_OnRoundComplete( float flRoundTime, IGameEvent *event ) = 0 ;
+};
+
+
+//----------------------------------------------------------------------------------------------------------------
+// Helper class for achievements based on other achievements
+class CAchievement_Meta : public CCSBaseAchievement
+{
+ DECLARE_CLASS( CAchievement_Meta, CCSBaseAchievement );
+public:
+ CAchievement_Meta();
+ virtual void Init();
+
+#if !defined(NO_STEAM)
+ STEAM_CALLBACK( CAchievement_Meta, Steam_OnUserAchievementStored, UserAchievementStored_t, m_CallbackUserAchievement );
+#endif
+
+protected:
+ void AddRequirement( int nAchievementId );
+
+private:
+ CUtlVector<int> m_requirements;
+};
+
+
+
+extern CAchievementMgr g_AchievementMgrCS; // global achievement mgr for CS
+
+#endif // CLIENT_DLL \ No newline at end of file
diff --git a/game/shared/cstrike/basecsgrenade_projectile.cpp b/game/shared/cstrike/basecsgrenade_projectile.cpp
new file mode 100644
index 0000000..5d6641b
--- /dev/null
+++ b/game/shared/cstrike/basecsgrenade_projectile.cpp
@@ -0,0 +1,346 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "basecsgrenade_projectile.h"
+
+float GetCurrentGravity( void );
+
+
+#ifdef CLIENT_DLL
+
+ #include "c_cs_player.h"
+
+#else
+
+ #include "bot_manager.h"
+ #include "cs_player.h"
+ #include "soundent.h"
+ #include "te_effect_dispatch.h"
+ #include "KeyValues.h"
+
+ BEGIN_DATADESC( CBaseCSGrenadeProjectile )
+ DEFINE_THINKFUNC( DangerSoundThink ),
+ END_DATADESC()
+
+#endif
+
+
+IMPLEMENT_NETWORKCLASS_ALIASED( BaseCSGrenadeProjectile, DT_BaseCSGrenadeProjectile )
+
+BEGIN_NETWORK_TABLE( CBaseCSGrenadeProjectile, DT_BaseCSGrenadeProjectile )
+ #ifdef CLIENT_DLL
+ RecvPropVector( RECVINFO( m_vInitialVelocity ) )
+ #else
+ SendPropVector( SENDINFO( m_vInitialVelocity ),
+ 20, // nbits
+ 0, // flags
+ -3000, // low value
+ 3000 // high value
+ )
+ #endif
+END_NETWORK_TABLE()
+
+
+#ifdef CLIENT_DLL
+
+
+ void CBaseCSGrenadeProjectile::PostDataUpdate( DataUpdateType_t type )
+ {
+ BaseClass::PostDataUpdate( type );
+
+ if ( type == DATA_UPDATE_CREATED )
+ {
+ // Now stick our initial velocity into the interpolation history
+ CInterpolatedVar< Vector > &interpolator = GetOriginInterpolator();
+
+ interpolator.ClearHistory();
+ float changeTime = GetLastChangeTime( LATCH_SIMULATION_VAR );
+
+ // Add a sample 1 second back.
+ Vector vCurOrigin = GetLocalOrigin() - m_vInitialVelocity;
+ interpolator.AddToHead( changeTime - 1.0, &vCurOrigin, false );
+
+ // Add the current sample.
+ vCurOrigin = GetLocalOrigin();
+ interpolator.AddToHead( changeTime, &vCurOrigin, false );
+ }
+ }
+
+ int CBaseCSGrenadeProjectile::DrawModel( int flags )
+ {
+ // During the first half-second of our life, don't draw ourselves if he's
+ // still playing his throw animation.
+ // (better yet, we could draw ourselves in his hand).
+ if ( GetThrower() != C_BasePlayer::GetLocalPlayer() )
+ {
+ if ( gpGlobals->curtime - m_flSpawnTime < 0.5 )
+ {
+ C_CSPlayer *pPlayer = dynamic_cast<C_CSPlayer*>( GetThrower() );
+ if ( pPlayer && pPlayer->m_PlayerAnimState->IsThrowingGrenade() )
+ {
+ return 0;
+ }
+ }
+ }
+
+ return BaseClass::DrawModel( flags );
+ }
+
+ void CBaseCSGrenadeProjectile::Spawn()
+ {
+ m_flSpawnTime = gpGlobals->curtime;
+ BaseClass::Spawn();
+ }
+
+#else
+
+ void CBaseCSGrenadeProjectile::PostConstructor( const char *className )
+ {
+ BaseClass::PostConstructor( className );
+ TheBots->AddGrenade( this );
+ }
+
+ CBaseCSGrenadeProjectile::~CBaseCSGrenadeProjectile()
+ {
+ TheBots->RemoveGrenade( this );
+ }
+
+ void CBaseCSGrenadeProjectile::Spawn( void )
+ {
+ BaseClass::Spawn();
+
+ SetSolidFlags( FSOLID_NOT_STANDABLE );
+ SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
+ SetSolid( SOLID_BBOX ); // So it will collide with physics props!
+
+ // smaller, cube bounding box so we rest on the ground
+ SetSize( Vector ( -2, -2, -2 ), Vector ( 2, 2, 2 ) );
+ }
+
+ void CBaseCSGrenadeProjectile::DangerSoundThink( void )
+ {
+ if (!IsInWorld())
+ {
+ Remove( );
+ return;
+ }
+
+ if( gpGlobals->curtime > m_flDetonateTime )
+ {
+ Detonate();
+ return;
+ }
+
+ CSoundEnt::InsertSound ( SOUND_DANGER, GetAbsOrigin() + GetAbsVelocity() * 0.5, GetAbsVelocity().Length( ), 0.2 );
+
+ SetNextThink( gpGlobals->curtime + 0.2 );
+
+ if (GetWaterLevel() != 0)
+ {
+ SetAbsVelocity( GetAbsVelocity() * 0.5 );
+ }
+ }
+
+ //Sets the time at which the grenade will explode
+ void CBaseCSGrenadeProjectile::SetDetonateTimerLength( float timer )
+ {
+ m_flDetonateTime = gpGlobals->curtime + timer;
+ }
+
+ void CBaseCSGrenadeProjectile::ResolveFlyCollisionCustom( trace_t &trace, Vector &vecVelocity )
+ {
+ //Assume all surfaces have the same elasticity
+ float flSurfaceElasticity = 1.0;
+
+ //Don't bounce off of players with perfect elasticity
+ if( trace.m_pEnt && trace.m_pEnt->IsPlayer() )
+ {
+ flSurfaceElasticity = 0.3;
+ }
+
+ // if its breakable glass and we kill it, don't bounce.
+ // give some damage to the glass, and if it breaks, pass
+ // through it.
+ bool breakthrough = false;
+
+ if( trace.m_pEnt && FClassnameIs( trace.m_pEnt, "func_breakable" ) )
+ {
+ breakthrough = true;
+ }
+
+ if( trace.m_pEnt && FClassnameIs( trace.m_pEnt, "func_breakable_surf" ) )
+ {
+ breakthrough = true;
+ }
+
+ if (breakthrough)
+ {
+ CTakeDamageInfo info( this, this, 10, DMG_CLUB );
+ trace.m_pEnt->DispatchTraceAttack( info, GetAbsVelocity(), &trace );
+
+ ApplyMultiDamage();
+
+ if( trace.m_pEnt->m_iHealth <= 0 )
+ {
+ // slow our flight a little bit
+ Vector vel = GetAbsVelocity();
+
+ vel *= 0.4;
+
+ SetAbsVelocity( vel );
+ return;
+ }
+ }
+
+ float flTotalElasticity = GetElasticity() * flSurfaceElasticity;
+ flTotalElasticity = clamp( flTotalElasticity, 0.0f, 0.9f );
+
+ // NOTE: A backoff of 2.0f is a reflection
+ Vector vecAbsVelocity;
+ PhysicsClipVelocity( GetAbsVelocity(), trace.plane.normal, vecAbsVelocity, 2.0f );
+ vecAbsVelocity *= flTotalElasticity;
+
+ // Get the total velocity (player + conveyors, etc.)
+ VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
+ float flSpeedSqr = DotProduct( vecVelocity, vecVelocity );
+
+ // Stop if on ground.
+ if ( trace.plane.normal.z > 0.7f ) // Floor
+ {
+ // Verify that we have an entity.
+ CBaseEntity *pEntity = trace.m_pEnt;
+ Assert( pEntity );
+
+ SetAbsVelocity( vecAbsVelocity );
+
+ if ( flSpeedSqr < ( 30 * 30 ) )
+ {
+ if ( pEntity->IsStandable() )
+ {
+ SetGroundEntity( pEntity );
+ }
+
+ // Reset velocities.
+ SetAbsVelocity( vec3_origin );
+ SetLocalAngularVelocity( vec3_angle );
+
+ //align to the ground so we're not standing on end
+ QAngle angle;
+ VectorAngles( trace.plane.normal, angle );
+
+ // rotate randomly in yaw
+ angle[1] = random->RandomFloat( 0, 360 );
+
+ // TODO: rotate around trace.plane.normal
+
+ SetAbsAngles( angle );
+ }
+ else
+ {
+ Vector vecDelta = GetBaseVelocity() - vecAbsVelocity;
+ Vector vecBaseDir = GetBaseVelocity();
+ VectorNormalize( vecBaseDir );
+ float flScale = vecDelta.Dot( vecBaseDir );
+
+ VectorScale( vecAbsVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, vecVelocity );
+ VectorMA( vecVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, GetBaseVelocity() * flScale, vecVelocity );
+ PhysicsPushEntity( vecVelocity, &trace );
+ }
+ }
+ else
+ {
+ // If we get *too* slow, we'll stick without ever coming to rest because
+ // we'll get pushed down by gravity faster than we can escape from the wall.
+ if ( flSpeedSqr < ( 30 * 30 ) )
+ {
+ // Reset velocities.
+ SetAbsVelocity( vec3_origin );
+ SetLocalAngularVelocity( vec3_angle );
+ }
+ else
+ {
+ SetAbsVelocity( vecAbsVelocity );
+ }
+ }
+
+ BounceSound();
+
+ // tell the bots a grenade has bounced
+ CCSPlayer *player = ToCSPlayer(GetThrower());
+ if ( player )
+ {
+ IGameEvent * event = gameeventmanager->CreateEvent( "grenade_bounce" );
+ if ( event )
+ {
+ event->SetInt( "userid", player->GetUserID() );
+ event->SetFloat( "x", GetAbsOrigin().x );
+ event->SetFloat( "y", GetAbsOrigin().y );
+ event->SetFloat( "z", GetAbsOrigin().z );
+ gameeventmanager->FireEvent( event );
+ }
+ }
+ }
+
+ void CBaseCSGrenadeProjectile::SetupInitialTransmittedGrenadeVelocity( const Vector &velocity )
+ {
+ m_vInitialVelocity = velocity;
+ }
+
+ #define MAX_WATER_SURFACE_DISTANCE 512
+
+ void CBaseCSGrenadeProjectile::Splash()
+ {
+ Vector centerPoint = GetAbsOrigin();
+ Vector normal( 0, 0, 1 );
+
+ // Find our water surface by tracing up till we're out of the water
+ trace_t tr;
+ Vector vecTrace( 0, 0, MAX_WATER_SURFACE_DISTANCE );
+ UTIL_TraceLine( centerPoint, centerPoint + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr );
+
+ // If we didn't start in water, we're above it
+ if ( tr.startsolid == false )
+ {
+ // Look downward to find the surface
+ vecTrace.Init( 0, 0, -MAX_WATER_SURFACE_DISTANCE );
+ UTIL_TraceLine( centerPoint, centerPoint + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr );
+
+ // If we hit it, setup the explosion
+ if ( tr.fraction < 1.0f )
+ {
+ centerPoint = tr.endpos;
+ }
+ else
+ {
+ //NOTENOTE: We somehow got into a splash without being near water?
+ Assert( 0 );
+ }
+ }
+ else if ( tr.fractionleftsolid )
+ {
+ // Otherwise we came out of the water at this point
+ centerPoint = centerPoint + (vecTrace * tr.fractionleftsolid);
+ }
+ else
+ {
+ // Use default values, we're really deep
+ }
+
+ CEffectData data;
+ data.m_vOrigin = centerPoint;
+ data.m_vNormal = normal;
+ data.m_flScale = random->RandomFloat( 1.0f, 2.0f );
+
+ if ( GetWaterType() & CONTENTS_SLIME )
+ {
+ data.m_fFlags |= FX_WATER_IN_SLIME;
+ }
+
+ DispatchEffect( "gunshotsplash", data );
+ }
+
+#endif // !CLIENT_DLL
diff --git a/game/shared/cstrike/basecsgrenade_projectile.h b/game/shared/cstrike/basecsgrenade_projectile.h
new file mode 100644
index 0000000..56ebd47
--- /dev/null
+++ b/game/shared/cstrike/basecsgrenade_projectile.h
@@ -0,0 +1,88 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef BASECSGRENADE_PROJECTILE_H
+#define BASECSGRENADE_PROJECTILE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "basegrenade_shared.h"
+
+
+#ifdef CLIENT_DLL
+ #define CBaseCSGrenadeProjectile C_BaseCSGrenadeProjectile
+#else
+ class CCSWeaponInfo;
+#endif
+
+
+class CBaseCSGrenadeProjectile : public CBaseGrenade
+{
+public:
+ DECLARE_CLASS( CBaseCSGrenadeProjectile, CBaseGrenade );
+ DECLARE_NETWORKCLASS();
+
+ virtual void Spawn();
+
+
+public:
+
+ // This gets sent to the client and placed in the client's interpolation history
+ // so the projectile starts out moving right off the bat.
+ CNetworkVector( m_vInitialVelocity );
+
+
+#ifdef CLIENT_DLL
+ CBaseCSGrenadeProjectile() {}
+ CBaseCSGrenadeProjectile( const CBaseCSGrenadeProjectile& ) {}
+ virtual int DrawModel( int flags );
+ virtual void PostDataUpdate( DataUpdateType_t type );
+
+ float m_flSpawnTime;
+#else
+ DECLARE_DATADESC();
+
+ virtual void PostConstructor( const char *className );
+ virtual ~CBaseCSGrenadeProjectile();
+
+ //Constants for all CS Grenades
+ static inline float GetGrenadeGravity() { return 0.4f; }
+ static inline const float GetGrenadeFriction() { return 0.2f; }
+ static inline const float GetGrenadeElasticity() { return 0.45f; }
+
+ //Think function to emit danger sounds for the AI
+ void DangerSoundThink( void );
+
+ virtual float GetShakeAmplitude( void ) { return 0.0f; }
+ virtual void Splash();
+
+ // Specify what velocity we want the grenade to have on the client immediately.
+ // Without this, the entity wouldn't have an interpolation history initially, so it would
+ // sit still until it had gotten a few updates from the server.
+ void SetupInitialTransmittedGrenadeVelocity( const Vector &velocity );
+
+ // [jpaquin] give grenade projectiles a link back to the type
+ // of weapon they are
+ CCSWeaponInfo *m_pWeaponInfo;
+
+protected:
+
+ //Set the time to detonate ( now + timer )
+ void SetDetonateTimerLength( float timer );
+
+private:
+
+ //Custom collision to allow for constant elasticity on hit surfaces
+ virtual void ResolveFlyCollisionCustom( trace_t &trace, Vector &vecVelocity );
+
+ float m_flDetonateTime;
+#endif
+};
+
+
+#endif // BASECSGRENADE_PROJECTILE_H
diff --git a/game/shared/cstrike/bot/bot.cpp b/game/shared/cstrike/bot/bot.cpp
new file mode 100644
index 0000000..4b17ddf
--- /dev/null
+++ b/game/shared/cstrike/bot/bot.cpp
@@ -0,0 +1,109 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+// Author: Michael S. Booth ([email protected]), Leon Hartwig, 2003
+
+#include "cbase.h"
+#include "basegrenade_shared.h"
+
+#include "bot.h"
+#include "bot_util.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+/// @todo Remove this nasty hack - CreateFakeClient() calls CBot::Spawn, which needs the profile and team
+const BotProfile *g_botInitProfile = NULL;
+int g_botInitTeam = 0;
+
+//
+// NOTE: Because CBot had to be templatized, the code was moved into bot.h
+//
+
+
+//--------------------------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------------------------
+
+ActiveGrenade::ActiveGrenade( CBaseGrenade *grenadeEntity )
+{
+ m_entity = grenadeEntity;
+ m_detonationPosition = grenadeEntity->GetAbsOrigin();
+ m_dieTimestamp = 0.0f;
+ m_radius = HEGrenadeRadius;
+
+ m_isSmoke = FStrEq( grenadeEntity->GetClassname(), "smokegrenade_projectile" );
+ if ( m_isSmoke )
+ {
+ m_radius = SmokeGrenadeRadius;
+ }
+
+ m_isFlashbang = FStrEq( grenadeEntity->GetClassname(), "flashbang_projectile" );
+ if ( m_isFlashbang )
+ {
+ m_radius = FlashbangGrenadeRadius;
+ }
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Called when the grenade in the world goes away
+ */
+void ActiveGrenade::OnEntityGone( void )
+{
+ if (m_isSmoke)
+ {
+ // smoke lingers after grenade is gone
+ const float smokeLingerTime = 4.0f;
+ m_dieTimestamp = gpGlobals->curtime + smokeLingerTime;
+ }
+
+ m_entity = NULL;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+void ActiveGrenade::Update( void )
+{
+ if (m_entity != NULL)
+ {
+ m_detonationPosition = m_entity->GetAbsOrigin();
+ }
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if this grenade is valid
+ */
+bool ActiveGrenade::IsValid( void ) const
+{
+ if ( m_isSmoke )
+ {
+ if ( m_entity == NULL && gpGlobals->curtime > m_dieTimestamp )
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if ( m_entity == NULL )
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+const Vector &ActiveGrenade::GetPosition( void ) const
+{
+ // smoke grenades can vanish before the smoke itself does - refer to the detonation position
+ if (m_entity == NULL)
+ return GetDetonationPosition();
+
+ return m_entity->GetAbsOrigin();
+}
+
diff --git a/game/shared/cstrike/bot/bot.h b/game/shared/cstrike/bot/bot.h
new file mode 100644
index 0000000..3067c2c
--- /dev/null
+++ b/game/shared/cstrike/bot/bot.h
@@ -0,0 +1,1053 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+//
+// Author: Michael S. Booth ([email protected]), 2003
+//
+// NOTE: The CS Bot code uses Doxygen-style comments. If you run Doxygen over this code, it will
+// auto-generate documentation. Visit www.doxygen.org to download the system for free.
+//
+
+#ifndef BOT_H
+#define BOT_H
+
+#include "cbase.h"
+#include "in_buttons.h"
+#include "movehelper_server.h"
+#include "mathlib/mathlib.h"
+
+#include "bot_manager.h"
+#include "bot_util.h"
+#include "bot_constants.h"
+#include "nav_mesh.h"
+#include "gameinterface.h"
+#include "weapon_csbase.h"
+#include "shared_util.h"
+#include "util.h"
+#include "shareddefs.h"
+
+#include "tier0/vprof.h"
+
+class BotProfile;
+
+
+extern bool AreBotsAllowed();
+
+
+//--------------------------------------------------------------------------------------------------------
+// BOTPORT: Convert everything to assume "origin" means "feet"
+
+//
+// Utility function to get "centroid" or center of player or player equivalent
+//
+inline Vector GetCentroid( const CBaseEntity *player )
+{
+ Vector centroid = player->GetAbsOrigin();
+
+ const Vector &mins = player->WorldAlignMins();
+ const Vector &maxs = player->WorldAlignMaxs();
+
+ centroid.z += (maxs.z - mins.z)/2.0f;
+
+ //centroid.z += HalfHumanHeight;
+
+ return centroid;
+}
+
+
+CBasePlayer* ClientPutInServerOverride_Bot( edict_t *pEdict, const char *playername );
+
+/// @todo Remove this nasty hack - CreateFakeClient() calls CBot::Spawn, which needs the profile
+extern const BotProfile *g_botInitProfile;
+extern int g_botInitTeam;
+extern int g_nClientPutInServerOverrides;
+
+//--------------------------------------------------------------------------------------------------------
+template < class T > T * CreateBot( const BotProfile *profile, int team )
+{
+ if ( !AreBotsAllowed() )
+ return NULL;
+
+ if ( UTIL_ClientsInGame() >= gpGlobals->maxClients )
+ {
+ CONSOLE_ECHO( "Unable to create bot: Server is full (%d/%d clients).\n", UTIL_ClientsInGame(), gpGlobals->maxClients );
+ return NULL;
+ }
+
+ // set the bot's name
+ char botName[64];
+ UTIL_ConstructBotNetName( botName, 64, profile );
+
+ // This is a backdoor we use so when the engine calls ClientPutInServer (from CreateFakeClient),
+ // expecting the game to make an entity for the fake client, we can make our special bot class
+ // instead of a CCSPlayer.
+ g_nClientPutInServerOverrides = 0;
+ ClientPutInServerOverride( ClientPutInServerOverride_Bot );
+
+ // get an edict for the bot
+ // NOTE: This will ultimately invoke CBot::Spawn(), so set the profile now
+ g_botInitProfile = profile;
+ g_botInitTeam = team;
+ edict_t *botEdict = engine->CreateFakeClient( botName );
+
+ ClientPutInServerOverride( NULL );
+ Assert( g_nClientPutInServerOverrides == 1 );
+
+
+ if ( botEdict == NULL )
+ {
+ CONSOLE_ECHO( "Unable to create bot: CreateFakeClient() returned null.\n" );
+ return NULL;
+ }
+
+
+ // create an instance of the bot's class and bind it to the edict
+ T *bot = dynamic_cast< T * >( CBaseEntity::Instance( botEdict ) );
+
+ if ( bot == NULL )
+ {
+ Assert( false );
+ Error( "Could not allocate and bind entity to bot edict.\n" );
+ return NULL;
+ }
+
+ bot->ClearFlags();
+ bot->AddFlag( FL_CLIENT | FL_FAKECLIENT );
+
+ return bot;
+}
+
+//----------------------------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------------------------
+/**
+ * The base bot class from which bots for specific games are derived
+ * A template is needed here because the CBot class must be derived from CBasePlayer,
+ * but also may need to be derived from a more specific player class, such as CCSPlayer
+ */
+template < class PlayerType >
+class CBot : public PlayerType
+{
+public:
+ DECLARE_CLASS( CBot, PlayerType );
+
+ CBot( void ); ///< constructor initializes all values to zero
+ virtual ~CBot();
+ virtual bool Initialize( const BotProfile *profile, int team ); ///< (EXTEND) prepare bot for action
+
+ unsigned int GetID( void ) const { return m_id; } ///< return bot's unique ID
+
+ virtual bool IsBot( void ) const { return true; }
+ virtual bool IsNetClient( void ) const { return false; } // Bots should return FALSE for this, they can't receive NET messages
+
+ virtual void Spawn( void ); ///< (EXTEND) spawn the bot into the game
+
+ virtual void Upkeep( void ) = 0; ///< lightweight maintenance, invoked frequently
+ virtual void Update( void ) = 0; ///< heavyweight algorithms, invoked less often
+
+
+ virtual void Run( void );
+ virtual void Walk( void );
+ virtual bool IsRunning( void ) const { return m_isRunning; }
+
+ virtual void Crouch( void );
+ virtual void StandUp( void );
+ bool IsCrouching( void ) const { return m_isCrouching; }
+
+ void PushPostureContext( void ); ///< push the current posture context onto the top of the stack
+ void PopPostureContext( void ); ///< restore the posture context to the next context on the stack
+
+ virtual void MoveForward( void );
+ virtual void MoveBackward( void );
+ virtual void StrafeLeft( void );
+ virtual void StrafeRight( void );
+
+ #define MUST_JUMP true
+ virtual bool Jump( bool mustJump = false ); ///< returns true if jump was started
+ bool IsJumping( void ); ///< returns true if we are in the midst of a jump
+ float GetJumpTimestamp( void ) const { return m_jumpTimestamp; } ///< return time last jump began
+
+ virtual void ClearMovement( void ); ///< zero any MoveForward(), Jump(), etc
+
+ const Vector &GetViewVector( void ); ///< return the actual view direction
+
+
+ //------------------------------------------------------------------------------------
+ // Weapon interface
+ //
+ virtual void UseEnvironment( void );
+ virtual void PrimaryAttack( void );
+ virtual void ClearPrimaryAttack( void );
+ virtual void TogglePrimaryAttack( void );
+ virtual void SecondaryAttack( void );
+ virtual void Reload( void );
+
+ float GetActiveWeaponAmmoRatio( void ) const; ///< returns ratio of ammo left to max ammo (1 = full clip, 0 = empty)
+ bool IsActiveWeaponClipEmpty( void ) const; ///< return true if active weapon has any empty clip
+ bool IsActiveWeaponOutOfAmmo( void ) const; ///< return true if active weapon has no ammo at all
+ bool IsActiveWeaponRecoilHigh( void ) const; ///< return true if active weapon's bullet spray has become large and inaccurate
+ bool IsUsingScope( void ); ///< return true if looking thru weapon's scope
+
+
+ //------------------------------------------------------------------------------------
+ // Event hooks
+ //
+
+ /// invoked when injured by something (EXTEND) - returns the amount of damage inflicted
+ virtual int OnTakeDamage( const CTakeDamageInfo &info )
+ {
+ return PlayerType::OnTakeDamage( info );
+ }
+
+ /// invoked when killed (EXTEND)
+ virtual void Event_Killed( const CTakeDamageInfo &info )
+ {
+ PlayerType::Event_Killed( info );
+ }
+
+ bool IsEnemy( CBaseEntity *ent ) const; ///< returns TRUE if given entity is our enemy
+ int GetEnemiesRemaining( void ) const; ///< return number of enemies left alive
+ int GetFriendsRemaining( void ) const; ///< return number of friends left alive
+
+ bool IsPlayerFacingMe( CBasePlayer *enemy ) const; ///< return true if player is facing towards us
+ bool IsPlayerLookingAtMe( CBasePlayer *enemy, float cosTolerance = 0.9f ) const; ///< returns true if other player is pointing right at us
+ bool IsLookingAtPosition( const Vector &pos, float angleTolerance = 20.0f ) const; ///< returns true if looking (roughly) at given position
+
+ bool IsLocalPlayerWatchingMe( void ) const; ///< return true if local player is observing this bot
+
+ void PrintIfWatched( PRINTF_FORMAT_STRING const char *format, ... ) const; ///< output message to console if we are being watched by the local player
+
+ virtual void UpdatePlayer( void ); ///< update player physics, movement, weapon firing commands, etc
+ virtual void BuildUserCmd( CUserCmd& cmd, const QAngle& viewangles, float forwardmove, float sidemove, float upmove, int buttons, byte impulse );
+ virtual void SetModel( const char *modelName );
+
+ int Save( CSave &save ) const { return 0; }
+ int Restore( CRestore &restore ) const { return 0; }
+ virtual void Think( void ) { }
+
+ const BotProfile *GetProfile( void ) const { return m_profile; } ///< return our personality profile
+
+ virtual bool ClientCommand( const CCommand &args ); ///< Do a "client command" - useful for invoking menu choices, etc.
+ virtual int Cmd_Argc( void ); ///< Returns the number of tokens in the command string
+ virtual char *Cmd_Argv( int argc ); ///< Retrieves a specified token
+
+private:
+ CUtlVector< char * > m_args;
+
+protected:
+ const BotProfile *m_profile; ///< the "personality" profile of this bot
+
+private:
+ friend class CBotManager;
+
+ unsigned int m_id; ///< unique bot ID
+
+ CUserCmd m_userCmd;
+ bool m_isRunning; ///< run/walk mode
+ bool m_isCrouching; ///< true if crouching (ducking)
+ float m_forwardSpeed;
+ float m_strafeSpeed;
+ float m_verticalSpeed;
+ int m_buttonFlags; ///< bitfield of movement buttons
+
+ float m_jumpTimestamp; ///< time when we last began a jump
+
+ Vector m_viewForward; ///< forward view direction (only valid when GetViewVector() is used)
+
+ /// the PostureContext represents the current settings of walking and crouching
+ struct PostureContext
+ {
+ bool isRunning;
+ bool isCrouching;
+ };
+ enum { MAX_POSTURE_STACK = 8 };
+ PostureContext m_postureStack[ MAX_POSTURE_STACK ];
+ int m_postureStackIndex; ///< index of top of stack
+
+ void ResetCommand( void );
+ //byte ThrottledMsec( void ) const;
+
+protected:
+ virtual float GetMoveSpeed( void ); ///< returns current movement speed (for walk/run)
+};
+
+
+//-----------------------------------------------------------------------------------------------------------
+//-----------------------------------------------------------------------------------------------------------
+//
+// Inlines
+//
+
+//--------------------------------------------------------------------------------------------------------------
+template < class T >
+inline void CBot<T>::SetModel( const char *modelName )
+{
+ BaseClass::SetModel( modelName );
+}
+
+//-----------------------------------------------------------------------------------------------------------
+template < class T >
+inline float CBot<T>::GetMoveSpeed( void )
+{
+ return this->MaxSpeed();
+}
+
+//-----------------------------------------------------------------------------------------------------------
+template < class T >
+inline void CBot<T>::Run( void )
+{
+ m_isRunning = true;
+}
+
+//-----------------------------------------------------------------------------------------------------------
+template < class T >
+inline void CBot<T>::Walk( void )
+{
+ m_isRunning = false;
+}
+
+//-----------------------------------------------------------------------------------------------------------
+template < class T >
+inline bool CBot<T>::IsActiveWeaponRecoilHigh( void ) const
+{
+ const QAngle &angles = const_cast< CBot<T> * >( this )->GetPunchAngle();
+ const float highRecoil = -1.5f;
+ return (angles.x < highRecoil);
+}
+
+//-----------------------------------------------------------------------------------------------------------
+template < class T >
+inline void CBot<T>::PushPostureContext( void )
+{
+ if (m_postureStackIndex == MAX_POSTURE_STACK)
+ {
+ PrintIfWatched( "PushPostureContext() overflow error!\n" );
+ return;
+ }
+
+ m_postureStack[ m_postureStackIndex ].isRunning = m_isRunning;
+ m_postureStack[ m_postureStackIndex ].isCrouching = m_isCrouching;
+ ++m_postureStackIndex;
+}
+
+//-----------------------------------------------------------------------------------------------------------
+template < class T >
+inline void CBot<T>::PopPostureContext( void )
+{
+ if (m_postureStackIndex == 0)
+ {
+ PrintIfWatched( "PopPostureContext() underflow error!\n" );
+ m_isRunning = true;
+ m_isCrouching = false;
+ return;
+ }
+
+ --m_postureStackIndex;
+ m_isRunning = m_postureStack[ m_postureStackIndex ].isRunning;
+ m_isCrouching = m_postureStack[ m_postureStackIndex ].isCrouching;
+}
+
+//-----------------------------------------------------------------------------------------------------------
+template < class T >
+inline bool CBot<T>::IsPlayerFacingMe( CBasePlayer *other ) const
+{
+ Vector toOther = other->GetAbsOrigin() - this->GetAbsOrigin();
+
+ Vector otherForward;
+ AngleVectors( other->EyeAngles() + other->GetPunchAngle(), &otherForward );
+
+ if (DotProduct( otherForward, toOther ) < 0.0f)
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------------------------------------
+template < class T >
+inline bool CBot<T>::IsPlayerLookingAtMe( CBasePlayer *other, float cosTolerance ) const
+{
+ Vector toOther = other->GetAbsOrigin() - this->GetAbsOrigin();
+ toOther.NormalizeInPlace();
+
+ Vector otherForward;
+ AngleVectors( other->EyeAngles() + other->GetPunchAngle(), &otherForward );
+
+ // other player must be pointing nearly right at us to be "looking at" us
+ if (DotProduct( otherForward, toOther ) < -cosTolerance)
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------------------------------------
+template < class T >
+inline const Vector &CBot<T>::GetViewVector( void )
+{
+ AngleVectors( this->EyeAngles() + this->GetPunchAngle(), &m_viewForward );
+ return m_viewForward;
+}
+
+//-----------------------------------------------------------------------------------------------------------
+template < class T >
+inline bool CBot<T>::IsLookingAtPosition( const Vector &pos, float angleTolerance ) const
+{
+ // forced to do this since many methods in CBaseEntity are not const, but should be
+ CBot< T > *me = const_cast< CBot< T > * >( this );
+
+ Vector to = pos - me->EyePosition();
+
+ QAngle idealAngles;
+ VectorAngles( to, idealAngles );
+
+ QAngle viewAngles = me->EyeAngles();
+
+ float deltaYaw = AngleNormalize( idealAngles.y - viewAngles.y );
+ float deltaPitch = AngleNormalize( idealAngles.x - viewAngles.x );
+
+ if (fabs( deltaYaw ) < angleTolerance && abs( deltaPitch ) < angleTolerance)
+ return true;
+
+ return false;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+template < class PlayerType >
+inline CBot< PlayerType >::CBot( void )
+{
+ // the profile will be attached after this instance is constructed
+ m_profile = NULL;
+
+ // assign this bot a unique ID
+ static unsigned int nextID = 1;
+
+ // wraparound (highly unlikely)
+ if (nextID == 0)
+ ++nextID;
+
+ m_id = nextID;
+ ++nextID;
+
+ m_postureStackIndex = 0;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+template < class PlayerType >
+inline CBot< PlayerType >::~CBot( void )
+{
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Prepare bot for action
+ */
+template < class PlayerType >
+inline bool CBot< PlayerType >::Initialize( const BotProfile *profile, int team )
+{
+ m_profile = profile;
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+template < class PlayerType >
+inline void CBot< PlayerType >::Spawn( void )
+{
+ // initialize the bot (thus setting its profile)
+ if (m_profile == NULL)
+ Initialize( g_botInitProfile, g_botInitTeam );
+
+ // let the base class set some things up
+ PlayerType::Spawn();
+
+ // Make sure everyone knows we are a bot
+ this->AddFlag( FL_CLIENT | FL_FAKECLIENT );
+
+ // Bots use their own thinking mechanism
+ this->SetThink( NULL );
+
+ m_isRunning = true;
+ m_isCrouching = false;
+ m_postureStackIndex = 0;
+
+ m_jumpTimestamp = 0.0f;
+
+ // Command interface variable initialization
+ ResetCommand();
+}
+
+
+/*
+//--------------------------------------------------------------------------------------------------------------
+template < class PlayerType >
+inline void CBot< PlayerType >::BotThink( void )
+{
+float g_flBotFullThinkInterval = 1.0 / 15.0; // full AI at lower frequency (was 10 in GoldSrc)
+
+
+ Upkeep();
+
+ if (gpGlobals->curtime >= m_flNextFullBotThink)
+ {
+ m_flNextFullBotThink = gpGlobals->curtime + g_flBotFullThinkInterval;
+
+ ResetCommand();
+ Update();
+ }
+
+ UpdatePlayer();
+}
+*/
+
+
+//--------------------------------------------------------------------------------------------------------------
+template < class PlayerType >
+inline void CBot< PlayerType >::MoveForward( void )
+{
+ m_forwardSpeed = GetMoveSpeed();
+ SETBITS( m_buttonFlags, IN_FORWARD );
+
+ // make mutually exclusive
+ CLEARBITS( m_buttonFlags, IN_BACK );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+template < class PlayerType >
+inline void CBot< PlayerType >::MoveBackward( void )
+{
+ m_forwardSpeed = -GetMoveSpeed();
+ SETBITS( m_buttonFlags, IN_BACK );
+
+ // make mutually exclusive
+ CLEARBITS( m_buttonFlags, IN_FORWARD );
+}
+
+//--------------------------------------------------------------------------------------------------------------
+template < class PlayerType >
+inline void CBot< PlayerType >::StrafeLeft( void )
+{
+ m_strafeSpeed = -GetMoveSpeed();
+ SETBITS( m_buttonFlags, IN_MOVELEFT );
+
+ // make mutually exclusive
+ CLEARBITS( m_buttonFlags, IN_MOVERIGHT );
+}
+
+//--------------------------------------------------------------------------------------------------------------
+template < class PlayerType >
+inline void CBot< PlayerType >::StrafeRight( void )
+{
+ m_strafeSpeed = GetMoveSpeed();
+ SETBITS( m_buttonFlags, IN_MOVERIGHT );
+
+ // make mutually exclusive
+ CLEARBITS( m_buttonFlags, IN_MOVELEFT );
+}
+
+//--------------------------------------------------------------------------------------------------------------
+template < class PlayerType >
+inline bool CBot< PlayerType >::Jump( bool mustJump )
+{
+ if (IsJumping() || IsCrouching())
+ return false;
+
+ if (!mustJump)
+ {
+ const float minJumpInterval = 0.9f; // 1.5f;
+ if (gpGlobals->curtime - m_jumpTimestamp < minJumpInterval)
+ return false;
+ }
+
+ // still need sanity check for jumping frequency
+ const float sanityInterval = 0.3f;
+ if (gpGlobals->curtime - m_jumpTimestamp < sanityInterval)
+ return false;
+
+ // jump
+ SETBITS( m_buttonFlags, IN_JUMP );
+ m_jumpTimestamp = gpGlobals->curtime;
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Zero any MoveForward(), Jump(), etc
+ */
+template < class PlayerType >
+void CBot< PlayerType >::ClearMovement( void )
+{
+ m_forwardSpeed = 0.0;
+ m_strafeSpeed = 0.0;
+ m_verticalSpeed = 100.0; // stay at the top of water, so we don't drown. TODO: swim logic
+ m_buttonFlags &= ~(IN_FORWARD | IN_BACK | IN_LEFT | IN_RIGHT | IN_JUMP);
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns true if we are in the midst of a jump
+ */
+template < class PlayerType >
+inline bool CBot< PlayerType >::IsJumping( void )
+{
+ // if long time after last jump, we can't be jumping
+ if (gpGlobals->curtime - m_jumpTimestamp > 3.0f)
+ return false;
+
+ // if we just jumped, we're still jumping
+ if (gpGlobals->curtime - m_jumpTimestamp < 0.9f) // 1.0f
+ return true;
+
+ // a little after our jump, we're jumping until we hit the ground
+ if (FBitSet( this->GetFlags(), FL_ONGROUND ))
+ return false;
+
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+template < class PlayerType >
+inline void CBot< PlayerType >::Crouch( void )
+{
+ m_isCrouching = true;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+template < class PlayerType >
+inline void CBot< PlayerType >::StandUp( void )
+{
+ m_isCrouching = false;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+template < class PlayerType >
+inline void CBot< PlayerType >::UseEnvironment( void )
+{
+ SETBITS( m_buttonFlags, IN_USE );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+template < class PlayerType >
+inline void CBot< PlayerType >::PrimaryAttack( void )
+{
+ SETBITS( m_buttonFlags, IN_ATTACK );
+}
+
+//--------------------------------------------------------------------------------------------------------------
+template < class PlayerType >
+inline void CBot< PlayerType >::ClearPrimaryAttack( void )
+{
+ CLEARBITS( m_buttonFlags, IN_ATTACK );
+}
+
+//--------------------------------------------------------------------------------------------------------------
+template < class PlayerType >
+inline void CBot< PlayerType >::TogglePrimaryAttack( void )
+{
+ if (FBitSet( m_buttonFlags, IN_ATTACK ))
+ {
+ CLEARBITS( m_buttonFlags, IN_ATTACK );
+ }
+ else
+ {
+ SETBITS( m_buttonFlags, IN_ATTACK );
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+template < class PlayerType >
+inline void CBot< PlayerType >::SecondaryAttack( void )
+{
+ SETBITS( m_buttonFlags, IN_ATTACK2 );
+}
+
+//--------------------------------------------------------------------------------------------------------------
+template < class PlayerType >
+inline void CBot< PlayerType >::Reload( void )
+{
+ SETBITS( m_buttonFlags, IN_RELOAD );
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns ratio of ammo left to max ammo (1 = full clip, 0 = empty)
+ */
+template < class PlayerType >
+inline float CBot< PlayerType >::GetActiveWeaponAmmoRatio( void ) const
+{
+ CWeaponCSBase *weapon = this->GetActiveCSWeapon();
+
+ if (weapon == NULL)
+ return 0.0f;
+
+ // weapons with no ammo are always full
+ if (weapon->Clip1() < 0)
+ return 1.0f;
+
+ return (float)weapon->Clip1() / (float)weapon->GetMaxClip1();
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if active weapon has an empty clip
+ */
+template < class PlayerType >
+inline bool CBot< PlayerType >::IsActiveWeaponClipEmpty( void ) const
+{
+ CWeaponCSBase *gun = this->GetActiveCSWeapon();
+
+ if (gun && gun->Clip1() == 0)
+ return true;
+
+ return false;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if active weapon has no ammo at all
+ */
+template < class PlayerType >
+inline bool CBot< PlayerType >::IsActiveWeaponOutOfAmmo( void ) const
+{
+ CWeaponCSBase *weapon = this->GetActiveCSWeapon();
+
+ if (weapon == NULL)
+ return true;
+
+ return !weapon->HasAnyAmmo();
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if looking thru weapon's scope
+ */
+template < class PlayerType >
+inline bool CBot< PlayerType >::IsUsingScope( void )
+{
+ // if our field of view is less than 90, we're looking thru a scope (maybe only true for CS...)
+ if (this->GetFOV() < this->GetDefaultFOV())
+ return true;
+
+ return false;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Fill in a CUserCmd with our data
+ */
+template < class PlayerType >
+inline void CBot< PlayerType >::BuildUserCmd( CUserCmd& cmd, const QAngle& viewangles, float forwardmove, float sidemove, float upmove, int buttons, byte impulse )
+{
+ Q_memset( &cmd, 0, sizeof( cmd ) );
+ cmd.command_number = gpGlobals->tickcount;
+ cmd.forwardmove = forwardmove;
+ cmd.sidemove = sidemove;
+ cmd.upmove = upmove;
+ cmd.buttons = buttons;
+ cmd.impulse = impulse;
+
+ VectorCopy( viewangles, cmd.viewangles );
+ cmd.random_seed = random->RandomInt( 0, 0x7fffffff );
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Update player physics, movement, weapon firing commands, etc
+ */
+template < class PlayerType >
+inline void CBot< PlayerType >::UpdatePlayer( void )
+{
+ if (m_isCrouching)
+ {
+ SETBITS( m_buttonFlags, IN_DUCK );
+ }
+ else if (!m_isRunning)
+ {
+ SETBITS( m_buttonFlags, IN_SPEED );
+ }
+
+ if ( this->IsEFlagSet(EFL_BOT_FROZEN) )
+ {
+ m_buttonFlags = 0; // Freeze.
+ m_forwardSpeed = 0;
+ m_strafeSpeed = 0;
+ m_verticalSpeed = 0;
+ }
+
+ // Fill in a CUserCmd with our data
+ this->BuildUserCmd( m_userCmd, this->EyeAngles(), m_forwardSpeed, m_strafeSpeed, m_verticalSpeed, m_buttonFlags, 0 );
+
+ // Save off the CUserCmd to execute later
+ this->ProcessUsercmds( &m_userCmd, 1, 1, 0, false );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+template < class PlayerType >
+inline void CBot< PlayerType >::ResetCommand( void )
+{
+ m_forwardSpeed = 0.0;
+ m_strafeSpeed = 0.0;
+ m_verticalSpeed = 100.0; // stay at the top of water, so we don't drown. TODO: swim logic
+ m_buttonFlags = 0;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/*
+template < class PlayerType >
+inline byte CBot< PlayerType >::ThrottledMsec( void ) const
+{
+ int iNewMsec;
+
+ // Estimate Msec to use for this command based on time passed from the previous command
+ iNewMsec = (int)( (gpGlobals->curtime - m_flPreviousCommandTime) * 1000 );
+ if (iNewMsec > 255) // Doh, bots are going to be slower than they should if this happens.
+ iNewMsec = 255; // Upgrade that CPU or use less bots!
+
+ return (byte)iNewMsec;
+}
+*/
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Do a "client command" - useful for invoking menu choices, etc.
+ */
+template < class PlayerType >
+inline bool CBot< PlayerType >::ClientCommand( const CCommand &args )
+{
+ // Remove old args
+ int i;
+ for ( i=0; i<m_args.Count(); ++i )
+ {
+ delete[] m_args[i];
+ }
+ m_args.RemoveAll();
+
+ // parse individual args
+ const char *cmd = args.GetCommandString();
+ while (1)
+ {
+ // skip whitespace up to a /n
+ while (*cmd && *cmd <= ' ' && *cmd != '\n')
+ {
+ cmd++;
+ }
+
+ if (*cmd == '\n')
+ { // a newline seperates commands in the buffer
+ cmd++;
+ break;
+ }
+
+ if (!*cmd)
+ break;
+
+ cmd = SharedParse (cmd);
+ if (!cmd)
+ break;
+
+ m_args.AddToTail( CloneString( SharedGetToken() ) );
+ }
+
+ // and pass to the base class
+ return PlayerType::ClientCommand( args );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns the number of tokens in the command string
+ */
+template < class PlayerType >
+inline int CBot< PlayerType >::Cmd_Argc()
+{
+ return m_args.Count();
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Retrieves a specified token
+ */
+template < class PlayerType >
+inline char * CBot< PlayerType >::Cmd_Argv( int argc )
+{
+ if ( argc < 0 || argc >= m_args.Count() )
+ return NULL;
+ return m_args[argc];
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Returns TRUE if given entity is our enemy
+ */
+template < class PlayerType >
+inline bool CBot< PlayerType >::IsEnemy( CBaseEntity *ent ) const
+{
+ // only Players (real and AI) can be enemies
+ if (!ent->IsPlayer())
+ return false;
+
+ // corpses are no threat
+ if (!ent->IsAlive())
+ return false;
+
+ CBasePlayer *player = static_cast<CBasePlayer *>( ent );
+
+ // if they are on our team, they are our friends
+ if (player->GetTeamNumber() == this->GetTeamNumber())
+ return false;
+
+ // yep, we hate 'em
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return number of enemies left alive
+ */
+template < class PlayerType >
+inline int CBot< PlayerType >::GetEnemiesRemaining( void ) const
+{
+ int count = 0;
+
+ for ( int i = 1; i <= gpGlobals->maxClients; ++i )
+ {
+ CBaseEntity *player = UTIL_PlayerByIndex( i );
+
+ if (player == NULL)
+ continue;
+
+ if (!IsEnemy( player ))
+ continue;
+
+ if (!player->IsAlive())
+ continue;
+
+ count++;
+ }
+
+ return count;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return number of friends left alive
+ */
+template < class PlayerType >
+inline int CBot< PlayerType >::GetFriendsRemaining( void ) const
+{
+ int count = 0;
+
+ for ( int i = 1; i <= gpGlobals->maxClients; ++i )
+ {
+ CBaseEntity *player = UTIL_PlayerByIndex( i );
+
+ if (player == NULL)
+ continue;
+
+ if (IsEnemy( player ))
+ continue;
+
+ if (!player->IsAlive())
+ continue;
+
+ if (player == static_cast<CBaseEntity *>( const_cast<CBot *>( this ) ))
+ continue;
+
+ count++;
+ }
+
+ return count;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if the local player is currently in observer mode watching this bot.
+ */
+template < class PlayerType >
+inline bool CBot< PlayerType >::IsLocalPlayerWatchingMe( void ) const
+{
+ if ( engine->IsDedicatedServer() )
+ return false;
+
+ CBasePlayer *player = UTIL_GetListenServerHost();
+ if ( player == NULL )
+ return false;
+
+ if ( cv_bot_debug_target.GetInt() > 0 )
+ {
+ return this->entindex() == cv_bot_debug_target.GetInt();
+ }
+
+ if ( player->IsObserver() || !player->IsAlive() )
+ {
+ if ( const_cast< CBot< PlayerType > * >(this) == player->GetObserverTarget() )
+ {
+ switch( player->GetObserverMode() )
+ {
+ case OBS_MODE_IN_EYE:
+ case OBS_MODE_CHASE:
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Output message to console if we are being watched by the local player
+ */
+template < class PlayerType >
+inline void CBot< PlayerType >::PrintIfWatched( PRINTF_FORMAT_STRING const char *format, ... ) const
+{
+ if (cv_bot_debug.GetInt() == 0)
+ {
+ return;
+ }
+
+ if ((IsLocalPlayerWatchingMe() && (cv_bot_debug.GetInt() == 1 || cv_bot_debug.GetInt() == 3)) ||
+ (cv_bot_debug.GetInt() == 2 || cv_bot_debug.GetInt() == 4))
+ {
+ va_list varg;
+ char buffer[ CBotManager::MAX_DBG_MSG_SIZE ];
+ const char *name = const_cast< CBot< PlayerType > * >( this )->GetPlayerName();
+
+ va_start( varg, format );
+ vsprintf( buffer, format, varg );
+ va_end( varg );
+
+ // prefix the console message with the bot's name (this can be NULL if bot was just added)
+ ClientPrint( UTIL_GetListenServerHost(),
+ HUD_PRINTCONSOLE,
+ UTIL_VarArgs( "%s: %s",
+ (name) ? name : "(NULL netname)", buffer ) );
+
+ TheBots->AddDebugMessage( buffer );
+ }
+}
+
+//-----------------------------------------------------------------------------------------------------------
+//-----------------------------------------------------------------------------------------------------------
+
+extern void InstallBotControl( void );
+extern void RemoveBotControl( void );
+extern void Bot_ServerCommand( void );
+extern void Bot_RegisterCvars( void );
+
+extern bool IsSpotOccupied( CBaseEntity *me, const Vector &pos ); // if a player is at the given spot, return true
+extern const Vector *FindNearbyHidingSpot( CBaseEntity *me, const Vector &pos, float maxRange = 1000.0f, bool isSniper = false, bool useNearest = false );
+extern const Vector *FindRandomHidingSpot( CBaseEntity *me, Place place, bool isSniper = false );
+extern const Vector *FindNearbyRetreatSpot( CBaseEntity *me, const Vector &start, float maxRange = 1000.0f, int avoidTeam = 0 );
+
+
+#endif // BOT_H
diff --git a/game/shared/cstrike/bot/bot_constants.h b/game/shared/cstrike/bot/bot_constants.h
new file mode 100644
index 0000000..94f5090
--- /dev/null
+++ b/game/shared/cstrike/bot/bot_constants.h
@@ -0,0 +1,40 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+// Author: Matthew D. Campbell ([email protected]), 2003
+
+#ifndef BOT_CONSTANTS_H
+#define BOT_CONSTANTS_H
+
+/// version number is MAJOR.MINOR
+#define BOT_VERSION_MAJOR 1
+#define BOT_VERSION_MINOR 50
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * Difficulty levels
+ */
+enum BotDifficultyType
+{
+ BOT_EASY = 0,
+ BOT_NORMAL = 1,
+ BOT_HARD = 2,
+ BOT_EXPERT = 3,
+
+ NUM_DIFFICULTY_LEVELS
+};
+
+#ifdef DEFINE_DIFFICULTY_NAMES
+ const char *BotDifficultyName[] =
+ {
+ "EASY", "NORMAL", "HARD", "EXPERT", NULL
+ };
+#else
+ extern const char *BotDifficultyName[];
+#endif
+
+#endif // BOT_CONSTANTS_H
diff --git a/game/shared/cstrike/bot/bot_hide.cpp b/game/shared/cstrike/bot/bot_hide.cpp
new file mode 100644
index 0000000..9a2b02c
--- /dev/null
+++ b/game/shared/cstrike/bot/bot_hide.cpp
@@ -0,0 +1,490 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// bot_hide.cpp
+// Mechanisms for using Hiding Spots in the Navigation Mesh
+// Author: Michael Booth, 2003-2004
+
+#include "cbase.h"
+#include "bot.h"
+#include "cs_nav_pathfind.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * If a player is at the given spot, return true
+ */
+bool IsSpotOccupied( CBaseEntity *me, const Vector &pos )
+{
+ const float closeRange = 75.0f; // 50
+
+ // is there a player in this spot
+ float range;
+ CBasePlayer *player = UTIL_GetClosestPlayer( pos, &range );
+
+ if (player != me)
+ {
+ if (player && range < closeRange)
+ return true;
+ }
+
+ // is there is a hostage in this spot
+ // BOTPORT: Implement hostage manager
+ /*
+ if (g_pHostages)
+ {
+ CHostage *hostage = g_pHostages->GetClosestHostage( *pos, &range );
+ if (hostage && hostage != me && range < closeRange)
+ return true;
+ }
+ */
+
+ return false;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+class CollectHidingSpotsFunctor
+{
+public:
+ CollectHidingSpotsFunctor( CBaseEntity *me, const Vector &origin, float range, int flags, Place place = UNDEFINED_PLACE ) : m_origin( origin )
+ {
+ m_me = me;
+ m_count = 0;
+ m_range = range;
+ m_flags = (unsigned char)flags;
+ m_place = place;
+ m_totalWeight = 0;
+ }
+
+ enum { MAX_SPOTS = 256 };
+
+ bool operator() ( CNavArea *area )
+ {
+ // if a place is specified, only consider hiding spots from areas in that place
+ if (m_place != UNDEFINED_PLACE && area->GetPlace() != m_place)
+ return true;
+
+ // collect all the hiding spots in this area
+ const HidingSpotVector *pSpots = area->GetHidingSpots();
+
+ FOR_EACH_VEC( (*pSpots), it )
+ {
+ const HidingSpot *spot = (*pSpots)[ it ];
+
+ // if we've filled up, stop searching
+ if (m_count == MAX_SPOTS)
+ {
+ return false;
+ }
+
+ // make sure hiding spot is in range
+ if (m_range > 0.0f)
+ {
+ if ((spot->GetPosition() - m_origin).IsLengthGreaterThan( m_range ))
+ {
+ continue;
+ }
+ }
+
+ // if a Player is using this hiding spot, don't consider it
+ if (IsSpotOccupied( m_me, spot->GetPosition() ))
+ {
+ // player is in hiding spot
+ /// @todo Check if player is moving or sitting still
+ continue;
+ }
+
+ if (spot->GetArea() && (spot->GetArea()->GetAttributes() & NAV_MESH_DONT_HIDE))
+ {
+ // the area has been marked as DONT_HIDE since the last analysis, so let's ignore it
+ continue;
+ }
+
+ // only collect hiding spots with matching flags
+ if (m_flags & spot->GetFlags())
+ {
+ m_hidingSpot[ m_count ] = &spot->GetPosition();
+ m_hidingSpotWeight[ m_count ] = m_totalWeight;
+
+ // if it's an 'avoid' area, give it a low weight
+ if ( spot->GetArea() && ( spot->GetArea()->GetAttributes() & NAV_MESH_AVOID ) )
+ {
+ m_totalWeight += 1;
+ }
+ else
+ {
+ m_totalWeight += 2;
+ }
+
+ ++m_count;
+ }
+ }
+
+ return (m_count < MAX_SPOTS);
+ }
+
+ /**
+ * Remove the spot at index "i"
+ */
+ void RemoveSpot( int i )
+ {
+ if (m_count == 0)
+ return;
+
+ for( int j=i+1; j<m_count; ++j )
+ m_hidingSpot[j-1] = m_hidingSpot[j];
+
+ --m_count;
+ }
+
+
+ int GetRandomHidingSpot( void )
+ {
+ int weight = RandomInt( 0, m_totalWeight-1 );
+ for ( int i=0; i<m_count-1; ++i )
+ {
+ // if the next spot's starting weight is over the target weight, this spot is the one
+ if ( m_hidingSpotWeight[i+1] >= weight )
+ {
+ return i;
+ }
+ }
+
+ // if we didn't find any, it's the last one
+ return m_count - 1;
+ }
+
+ CBaseEntity *m_me;
+ const Vector &m_origin;
+ float m_range;
+
+ const Vector *m_hidingSpot[ MAX_SPOTS ];
+ int m_hidingSpotWeight[ MAX_SPOTS ];
+ int m_totalWeight;
+ int m_count;
+
+ unsigned char m_flags;
+
+ Place m_place;
+};
+
+/**
+ * Do a breadth-first search to find a nearby hiding spot and return it.
+ * Don't pick a hiding spot that a Player is currently occupying.
+ * @todo Clean up this mess
+ */
+const Vector *FindNearbyHidingSpot( CBaseEntity *me, const Vector &pos, float maxRange, bool isSniper, bool useNearest )
+{
+ CNavArea *startArea = TheNavMesh->GetNearestNavArea( pos );
+ if (startArea == NULL)
+ return NULL;
+
+ // collect set of nearby hiding spots
+ if (isSniper)
+ {
+ CollectHidingSpotsFunctor collector( me, pos, maxRange, HidingSpot::IDEAL_SNIPER_SPOT );
+ SearchSurroundingAreas( startArea, pos, collector, maxRange );
+
+ if (collector.m_count)
+ {
+ int which = collector.GetRandomHidingSpot();
+ return collector.m_hidingSpot[ which ];
+ }
+ else
+ {
+ // no ideal sniping spots, look for "good" sniping spots
+ CollectHidingSpotsFunctor collector( me, pos, maxRange, HidingSpot::GOOD_SNIPER_SPOT );
+ SearchSurroundingAreas( startArea, pos, collector, maxRange );
+
+ if (collector.m_count)
+ {
+ int which = collector.GetRandomHidingSpot();
+ return collector.m_hidingSpot[ which ];
+ }
+
+ // no sniping spots at all.. fall through and pick a normal hiding spot
+ }
+ }
+
+ // collect hiding spots with decent "cover"
+ CollectHidingSpotsFunctor collector( me, pos, maxRange, HidingSpot::IN_COVER );
+ SearchSurroundingAreas( startArea, pos, collector, maxRange );
+
+ if (collector.m_count == 0)
+ {
+ // no hiding spots at all - if we're not a sniper, try to find a sniper spot to use instead
+ if (!isSniper)
+ {
+ return FindNearbyHidingSpot( me, pos, maxRange, true, useNearest );
+ }
+
+ return NULL;
+ }
+
+ if (useNearest)
+ {
+ // return closest hiding spot
+ const Vector *closest = NULL;
+ float closeRangeSq = 9999999999.9f;
+ for( int i=0; i<collector.m_count; ++i )
+ {
+ float rangeSq = (*collector.m_hidingSpot[i] - pos).LengthSqr();
+ if (rangeSq < closeRangeSq)
+ {
+ closeRangeSq = rangeSq;
+ closest = collector.m_hidingSpot[i];
+ }
+ }
+
+ return closest;
+ }
+
+ // select a hiding spot at random
+ int which = collector.GetRandomHidingSpot();
+ return collector.m_hidingSpot[ which ];
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Select a random hiding spot among the nav areas that are tagged with the given place
+ */
+const Vector *FindRandomHidingSpot( CBaseEntity *me, Place place, bool isSniper )
+{
+ // collect set of nearby hiding spots
+ if (isSniper)
+ {
+ CollectHidingSpotsFunctor collector( me, me->GetAbsOrigin(), -1.0f, HidingSpot::IDEAL_SNIPER_SPOT, place );
+ TheNavMesh->ForAllAreas( collector );
+
+ if (collector.m_count)
+ {
+ int which = RandomInt( 0, collector.m_count-1 );
+ return collector.m_hidingSpot[ which ];
+ }
+ else
+ {
+ // no ideal sniping spots, look for "good" sniping spots
+ CollectHidingSpotsFunctor collector( me, me->GetAbsOrigin(), -1.0f, HidingSpot::GOOD_SNIPER_SPOT, place );
+ TheNavMesh->ForAllAreas( collector );
+
+ if (collector.m_count)
+ {
+ int which = RandomInt( 0, collector.m_count-1 );
+ return collector.m_hidingSpot[ which ];
+ }
+
+ // no sniping spots at all.. fall through and pick a normal hiding spot
+ }
+ }
+
+ // collect hiding spots with decent "cover"
+ CollectHidingSpotsFunctor collector( me, me->GetAbsOrigin(), -1.0f, HidingSpot::IN_COVER, place );
+ TheNavMesh->ForAllAreas( collector );
+
+ if (collector.m_count == 0)
+ return NULL;
+
+ // select a hiding spot at random
+ int which = RandomInt( 0, collector.m_count-1 );
+ return collector.m_hidingSpot[ which ];
+}
+
+
+//--------------------------------------------------------------------------------------------------------------------
+/**
+ * Select a nearby retreat spot.
+ * Don't pick a hiding spot that a Player is currently occupying.
+ * If "avoidTeam" is nonzero, avoid getting close to members of that team.
+ */
+const Vector *FindNearbyRetreatSpot( CBaseEntity *me, const Vector &start, float maxRange, int avoidTeam )
+{
+ CNavArea *startArea = TheNavMesh->GetNearestNavArea( start );
+ if (startArea == NULL)
+ return NULL;
+
+ // collect hiding spots with decent "cover"
+ CollectHidingSpotsFunctor collector( me, start, maxRange, HidingSpot::IN_COVER );
+ SearchSurroundingAreas( startArea, start, collector, maxRange );
+
+ if (collector.m_count == 0)
+ return NULL;
+
+ // find the closest unoccupied hiding spot that crosses the least lines of fire and has the best cover
+ for( int i=0; i<collector.m_count; ++i )
+ {
+ // check if we would have to cross a line of fire to reach this hiding spot
+ if (IsCrossingLineOfFire( start, *collector.m_hidingSpot[i], me ))
+ {
+ collector.RemoveSpot( i );
+
+ // back up a step, so iteration won't skip a spot
+ --i;
+
+ continue;
+ }
+
+ // check if there is someone on the avoidTeam near this hiding spot
+ if (avoidTeam)
+ {
+ float range;
+ if (UTIL_GetClosestPlayer( *collector.m_hidingSpot[i], avoidTeam, &range ))
+ {
+ const float dangerRange = 150.0f;
+ if (range < dangerRange)
+ {
+ // there is an avoidable player too near this spot - remove it
+ collector.RemoveSpot( i );
+
+ // back up a step, so iteration won't skip a spot
+ --i;
+
+ continue;
+ }
+ }
+ }
+ }
+
+ if (collector.m_count <= 0)
+ return NULL;
+
+ // all remaining spots are ok - pick one at random
+ int which = RandomInt( 0, collector.m_count-1 );
+ return collector.m_hidingSpot[ which ];
+}
+
+
+//--------------------------------------------------------------------------------------------------------------------
+/**
+ * Functor to collect all hiding spots in range that we can reach before the enemy arrives.
+ * NOTE: This only works for the initial rush.
+ */
+class CollectArriveFirstSpotsFunctor
+{
+public:
+ CollectArriveFirstSpotsFunctor( CBaseEntity *me, const Vector &searchOrigin, float enemyArriveTime, float range, int flags ) : m_searchOrigin( searchOrigin )
+ {
+ m_me = me;
+ m_count = 0;
+ m_range = range;
+ m_flags = (unsigned char)flags;
+ m_enemyArriveTime = enemyArriveTime;
+ }
+
+ enum { MAX_SPOTS = 256 };
+
+ bool operator() ( CNavArea *area )
+ {
+ // collect all the hiding spots in this area
+ const HidingSpotVector *pSpots = area->GetHidingSpots();
+
+ FOR_EACH_VEC( (*pSpots), it )
+ {
+ const HidingSpot *spot = (*pSpots)[ it ];
+
+ // make sure hiding spot is in range
+ if (m_range > 0.0f)
+ {
+ if ((spot->GetPosition() - m_searchOrigin).IsLengthGreaterThan( m_range ))
+ {
+ continue;
+ }
+ }
+
+ // if a Player is using this hiding spot, don't consider it
+ if (IsSpotOccupied( m_me, spot->GetPosition() ))
+ {
+ // player is in hiding spot
+ /// @todo Check if player is moving or sitting still
+ continue;
+ }
+
+ // only collect hiding spots with matching flags
+ if (!(m_flags & spot->GetFlags()))
+ {
+ continue;
+ }
+
+ // only collect this hiding spot if we can reach it before the enemy arrives
+ // NOTE: This assumes the area is fairly small and the difference of moving to the corner vs the center is small
+ const float settleTime = 1.0f;
+ if (spot->GetArea()->GetEarliestOccupyTime( m_me->GetTeamNumber() ) + settleTime < m_enemyArriveTime)
+ {
+ m_hidingSpot[ m_count++ ] = spot;
+ }
+ }
+
+ // if we've filled up, stop searching
+ if (m_count == MAX_SPOTS)
+ return false;
+
+ return true;
+ }
+
+ CBaseEntity *m_me;
+ const Vector &m_searchOrigin;
+
+ float m_range;
+ float m_enemyArriveTime;
+ unsigned char m_flags;
+
+ const HidingSpot *m_hidingSpot[ MAX_SPOTS ];
+ int m_count;
+};
+
+
+/**
+ * Select a hiding spot that we can reach before the enemy arrives.
+ * NOTE: This only works for the initial rush.
+ */
+const HidingSpot *FindInitialEncounterSpot( CBaseEntity *me, const Vector &searchOrigin, float enemyArriveTime, float maxRange, bool isSniper )
+{
+ CNavArea *startArea = TheNavMesh->GetNearestNavArea( searchOrigin );
+ if (startArea == NULL)
+ return NULL;
+
+ // collect set of nearby hiding spots
+ if (isSniper)
+ {
+ CollectArriveFirstSpotsFunctor collector( me, searchOrigin, enemyArriveTime, maxRange, HidingSpot::IDEAL_SNIPER_SPOT );
+ SearchSurroundingAreas( startArea, searchOrigin, collector, maxRange );
+
+ if (collector.m_count)
+ {
+ int which = RandomInt( 0, collector.m_count-1 );
+ return collector.m_hidingSpot[ which ];
+ }
+ else
+ {
+ // no ideal sniping spots, look for "good" sniping spots
+ CollectArriveFirstSpotsFunctor collector( me, searchOrigin, enemyArriveTime, maxRange, HidingSpot::GOOD_SNIPER_SPOT );
+ SearchSurroundingAreas( startArea, searchOrigin, collector, maxRange );
+
+ if (collector.m_count)
+ {
+ int which = RandomInt( 0, collector.m_count-1 );
+ return collector.m_hidingSpot[ which ];
+ }
+
+ // no sniping spots at all.. fall through and pick a normal hiding spot
+ }
+ }
+
+ // collect hiding spots with decent "cover"
+ CollectArriveFirstSpotsFunctor collector( me, searchOrigin, enemyArriveTime, maxRange, HidingSpot::IN_COVER | HidingSpot::EXPOSED );
+ SearchSurroundingAreas( startArea, searchOrigin, collector, maxRange );
+
+ if (collector.m_count == 0)
+ return NULL;
+
+ // select a hiding spot at random
+ int which = RandomInt( 0, collector.m_count-1 );
+ return collector.m_hidingSpot[ which ];
+}
+
diff --git a/game/shared/cstrike/bot/bot_manager.cpp b/game/shared/cstrike/bot/bot_manager.cpp
new file mode 100644
index 0000000..ce15823
--- /dev/null
+++ b/game/shared/cstrike/bot/bot_manager.cpp
@@ -0,0 +1,402 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+// Author: Michael S. Booth ([email protected]), 2003
+
+#include "cbase.h"
+
+#include "bot.h"
+#include "bot_manager.h"
+#include "nav_area.h"
+#include "bot_util.h"
+#include "basegrenade_shared.h"
+
+#include "cs_bot.h"
+
+#include "tier0/vprof.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+float g_BotUpkeepInterval = 0.0f;
+float g_BotUpdateInterval = 0.0f;
+
+
+//--------------------------------------------------------------------------------------------------------------
+CBotManager::CBotManager()
+{
+ InitBotTrig();
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+CBotManager::~CBotManager()
+{
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Invoked when the round is restarting
+ */
+void CBotManager::RestartRound( void )
+{
+ DestroyAllGrenades();
+ ClearDebugMessages();
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Invoked at the start of each frame
+ */
+void CBotManager::StartFrame( void )
+{
+ VPROF_BUDGET( "CBotManager::StartFrame", VPROF_BUDGETGROUP_NPCS );
+
+ ValidateActiveGrenades();
+
+ // debug smoke grenade visualization
+ if (cv_bot_debug.GetInt() == 5)
+ {
+ Vector edge, lastEdge;
+
+ FOR_EACH_LL( m_activeGrenadeList, it )
+ {
+ ActiveGrenade *ag = m_activeGrenadeList[ it ];
+
+ const Vector &pos = ag->GetDetonationPosition();
+
+ UTIL_DrawBeamPoints( pos, pos + Vector( 0, 0, 50 ), 1, 255, 100, 0 );
+
+ lastEdge = Vector( ag->GetRadius() + pos.x, pos.y, pos.z );
+ float angle;
+ for( angle=0.0f; angle <= 180.0f; angle += 22.5f )
+ {
+ edge.x = ag->GetRadius() * BotCOS( angle ) + pos.x;
+ edge.y = pos.y;
+ edge.z = ag->GetRadius() * BotSIN( angle ) + pos.z;
+
+ UTIL_DrawBeamPoints( edge, lastEdge, 1, 255, 50, 0 );
+
+ lastEdge = edge;
+ }
+
+ lastEdge = Vector( pos.x, ag->GetRadius() + pos.y, pos.z );
+ for( angle=0.0f; angle <= 180.0f; angle += 22.5f )
+ {
+ edge.x = pos.x;
+ edge.y = ag->GetRadius() * BotCOS( angle ) + pos.y;
+ edge.z = ag->GetRadius() * BotSIN( angle ) + pos.z;
+
+ UTIL_DrawBeamPoints( edge, lastEdge, 1, 255, 50, 0 );
+
+ lastEdge = edge;
+ }
+ }
+ }
+
+ // set frame duration
+ g_BotUpkeepInterval = m_frameTimer.GetElapsedTime();
+ m_frameTimer.Start();
+
+ g_BotUpdateInterval = (g_BotUpdateSkipCount+1) * g_BotUpkeepInterval;
+
+ //
+ // Process each active bot
+ //
+ for( int i = 1; i <= gpGlobals->maxClients; ++i )
+ {
+ CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) );
+
+ if (!player)
+ continue;
+
+ // Hack for now so the temp bot code works. The temp bots are very useful for debugging
+ // because they can be setup to mimic the player's usercmds.
+ if (player->IsBot() && IsEntityValid( player ) )
+ {
+ // EVIL: Messes up vtables
+ //CBot< CBasePlayer > *bot = static_cast< CBot< CBasePlayer > * >( player );
+ CCSBot *bot = dynamic_cast< CCSBot * >( player );
+
+ if ( bot )
+ {
+ bot->Upkeep();
+
+ if (((gpGlobals->tickcount + bot->entindex()) % g_BotUpdateSkipCount) == 0)
+ {
+ bot->ResetCommand();
+ bot->Update();
+ }
+
+ bot->UpdatePlayer();
+ }
+ }
+ }
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Add an active grenade to the bot's awareness
+ */
+void CBotManager::AddGrenade( CBaseGrenade *grenade )
+{
+ ActiveGrenade *ag = new ActiveGrenade( grenade );
+ m_activeGrenadeList.AddToTail( ag );
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * The grenade entity in the world is going away
+ */
+void CBotManager::RemoveGrenade( CBaseGrenade *grenade )
+{
+ FOR_EACH_LL( m_activeGrenadeList, it )
+ {
+ ActiveGrenade *ag = m_activeGrenadeList[ it ];
+
+ if (ag->IsEntity( grenade ))
+ {
+ ag->OnEntityGone();
+ return;
+ }
+ }
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * The grenade entity has changed its radius
+ */
+void CBotManager::SetGrenadeRadius( CBaseGrenade *grenade, float radius )
+{
+ FOR_EACH_LL( m_activeGrenadeList, it )
+ {
+ ActiveGrenade *ag = m_activeGrenadeList[ it ];
+
+ if (ag->IsEntity( grenade ))
+ {
+ ag->SetRadius( radius );
+ return;
+ }
+ }
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Destroy any invalid active grenades
+ */
+void CBotManager::ValidateActiveGrenades( void )
+{
+ int it = m_activeGrenadeList.Head();
+
+ while( it != m_activeGrenadeList.InvalidIndex() )
+ {
+ ActiveGrenade *ag = m_activeGrenadeList[ it ];
+
+ int current = it;
+ it = m_activeGrenadeList.Next( it );
+
+ // lazy validation
+ if (!ag->IsValid())
+ {
+ m_activeGrenadeList.Remove( current );
+ delete ag;
+ continue;
+ }
+ else
+ {
+ ag->Update();
+ }
+ }
+}
+
+//--------------------------------------------------------------------------------------------------------------
+void CBotManager::DestroyAllGrenades( void )
+{
+ m_activeGrenadeList.PurgeAndDeleteElements();
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if position is inside a smoke cloud
+ */
+bool CBotManager::IsInsideSmokeCloud( const Vector *pos )
+{
+ int it = m_activeGrenadeList.Head();
+
+ while( it != m_activeGrenadeList.InvalidIndex() )
+ {
+ ActiveGrenade *ag = m_activeGrenadeList[ it ];
+
+ int current = it;
+ it = m_activeGrenadeList.Next( it );
+
+ // lazy validation
+ if (!ag->IsValid())
+ {
+ m_activeGrenadeList.Remove( current );
+ delete ag;
+ continue;
+ }
+
+ if (ag->IsSmoke())
+ {
+ const Vector &smokeOrigin = ag->GetDetonationPosition();
+
+ if ((smokeOrigin - *pos).IsLengthLessThan( ag->GetRadius() ))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if line intersects smoke volume
+ * Determine the length of the line of sight covered by each smoke cloud,
+ * and sum them (overlap is additive for obstruction).
+ * If the overlap exceeds the threshold, the bot can't see through.
+ */
+bool CBotManager::IsLineBlockedBySmoke( const Vector &from, const Vector &to, float grenadeBloat )
+{
+ VPROF_BUDGET( "CBotManager::IsLineBlockedBySmoke", VPROF_BUDGETGROUP_NPCS );
+
+ float totalSmokedLength = 0.0f; // distance along line of sight covered by smoke
+
+ // compute unit vector and length of line of sight segment
+ Vector sightDir = to - from;
+ float sightLength = sightDir.NormalizeInPlace();
+
+ FOR_EACH_LL( m_activeGrenadeList, it )
+ {
+ ActiveGrenade *ag = m_activeGrenadeList[ it ];
+ const float smokeRadiusSq = ag->GetRadius() * ag->GetRadius() * grenadeBloat * grenadeBloat;
+
+ if (ag->IsSmoke())
+ {
+ const Vector &smokeOrigin = ag->GetDetonationPosition();
+
+ Vector toGrenade = smokeOrigin - from;
+
+ float alongDist = DotProduct( toGrenade, sightDir );
+
+ // compute closest point to grenade along line of sight ray
+ Vector close;
+
+ // constrain closest point to line segment
+ if (alongDist < 0.0f)
+ close = from;
+ else if (alongDist >= sightLength)
+ close = to;
+ else
+ close = from + sightDir * alongDist;
+
+ // if closest point is within smoke radius, the line overlaps the smoke cloud
+ Vector toClose = close - smokeOrigin;
+ float lengthSq = toClose.LengthSqr();
+
+ if (lengthSq < smokeRadiusSq)
+ {
+ // some portion of the ray intersects the cloud
+
+ float fromSq = toGrenade.LengthSqr();
+ float toSq = (smokeOrigin - to).LengthSqr();
+
+ if (fromSq < smokeRadiusSq)
+ {
+ if (toSq < smokeRadiusSq)
+ {
+ // both 'from' and 'to' lie within the cloud
+ // entire length is smoked
+ totalSmokedLength += (to - from).Length();
+ }
+ else
+ {
+ // 'from' is inside the cloud, 'to' is outside
+ // compute half of total smoked length as if ray crosses entire cloud chord
+ float halfSmokedLength = (float)sqrt( smokeRadiusSq - lengthSq );
+
+ if (alongDist > 0.0f)
+ {
+ // ray goes thru 'close'
+ totalSmokedLength += halfSmokedLength + (close - from).Length();
+ }
+ else
+ {
+ // ray starts after 'close'
+ totalSmokedLength += halfSmokedLength - (close - from).Length();
+ }
+
+ }
+ }
+ else if (toSq < smokeRadiusSq)
+ {
+ // 'from' is outside the cloud, 'to' is inside
+ // compute half of total smoked length as if ray crosses entire cloud chord
+ float halfSmokedLength = (float)sqrt( smokeRadiusSq - lengthSq );
+
+ Vector v = to - smokeOrigin;
+ if (DotProduct( v, sightDir ) > 0.0f)
+ {
+ // ray goes thru 'close'
+ totalSmokedLength += halfSmokedLength + (close - to).Length();
+ }
+ else
+ {
+ // ray ends before 'close'
+ totalSmokedLength += halfSmokedLength - (close - to).Length();
+ }
+ }
+ else
+ {
+ // 'from' and 'to' lie outside of the cloud - the line of sight completely crosses it
+ // determine the length of the chord that crosses the cloud
+ float smokedLength = 2.0f * (float)sqrt( smokeRadiusSq - lengthSq );
+
+ totalSmokedLength += smokedLength;
+ }
+ }
+ }
+ }
+
+ // define how much smoke a bot can see thru
+ const float maxSmokedLength = 0.7f * SmokeGrenadeRadius;
+
+ // return true if the total length of smoke-covered line-of-sight is too much
+ return (totalSmokedLength > maxSmokedLength);
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+void CBotManager::ClearDebugMessages( void )
+{
+ m_debugMessageCount = 0;
+ m_currentDebugMessage = -1;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Add a new debug message to the message history
+ */
+void CBotManager::AddDebugMessage( const char *msg )
+{
+ if (++m_currentDebugMessage >= MAX_DBG_MSGS)
+ {
+ m_currentDebugMessage = 0;
+ }
+
+ if (m_debugMessageCount < MAX_DBG_MSGS)
+ {
+ ++m_debugMessageCount;
+ }
+
+ Q_strncpy( m_debugMessage[ m_currentDebugMessage ].m_string, msg, MAX_DBG_MSG_SIZE );
+ m_debugMessage[ m_currentDebugMessage ].m_age.Start();
+}
diff --git a/game/shared/cstrike/bot/bot_manager.h b/game/shared/cstrike/bot/bot_manager.h
new file mode 100644
index 0000000..b7d2bd1
--- /dev/null
+++ b/game/shared/cstrike/bot/bot_manager.h
@@ -0,0 +1,195 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+// Author: Michael S. Booth ([email protected]), 2003
+
+#ifndef BASE_CONTROL_H
+#define BASE_CONTROL_H
+
+#pragma warning( disable : 4530 ) // STL uses exceptions, but we are not compiling with them - ignore warning
+
+extern float g_BotUpkeepInterval; ///< duration between bot upkeeps
+extern float g_BotUpdateInterval; ///< duration between bot updates
+const int g_BotUpdateSkipCount = 2; ///< number of upkeep periods to skip update
+
+class CNavArea;
+
+/// TODO: move CS-specific defines into CSBot files
+enum
+{
+ SmokeGrenadeRadius = 155,
+ FlashbangGrenadeRadius = 115,
+ HEGrenadeRadius = 115,
+};
+
+//--------------------------------------------------------------------------------------------------------------
+class CBaseGrenade;
+
+/**
+ * An ActiveGrenade is a representation of a grenade in the world
+ * NOTE: Currently only used for smoke grenade line-of-sight testing
+ * @todo Use system allow bots to avoid HE and Flashbangs
+ */
+class ActiveGrenade
+{
+public:
+ ActiveGrenade( CBaseGrenade *grenadeEntity );
+
+ void OnEntityGone( void ); ///< called when the grenade in the world goes away
+ void Update( void ); ///< called every frame
+ bool IsValid( void ) const ; ///< return true if this grenade is valid
+
+ bool IsEntity( CBaseGrenade *grenade ) const { return (grenade == m_entity) ? true : false; }
+ CBaseGrenade *GetEntity( void ) const { return m_entity; }
+
+ const Vector &GetDetonationPosition( void ) const { return m_detonationPosition; }
+ const Vector &GetPosition( void ) const;
+ bool IsSmoke( void ) const { return m_isSmoke; }
+ bool IsFlashbang( void ) const { return m_isFlashbang; }
+ CBaseGrenade *GetGrenade( void ) { return m_entity; }
+ float GetRadius( void ) const { return m_radius; }
+ void SetRadius( float radius ) { m_radius = radius; }
+
+private:
+ CBaseGrenade *m_entity; ///< the entity
+ Vector m_detonationPosition; ///< the location where the grenade detonated (smoke)
+ float m_dieTimestamp; ///< time this should go away after m_entity is NULL
+ bool m_isSmoke; ///< true if this is a smoke grenade
+ bool m_isFlashbang; ///< true if this is a flashbang grenade
+ float m_radius;
+};
+
+typedef CUtlLinkedList<ActiveGrenade *> ActiveGrenadeList;
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * This class manages all active bots, propagating events to them and updating them.
+ */
+class CBotManager
+{
+public:
+ CBotManager();
+ virtual ~CBotManager();
+
+ CBasePlayer *AllocateAndBindBotEntity( edict_t *ed ); ///< allocate the appropriate entity for the bot and bind it to the given edict
+ virtual CBasePlayer *AllocateBotEntity( void ) = 0; ///< factory method to allocate the appropriate entity for the bot
+
+ virtual void ClientDisconnect( CBaseEntity *entity ) = 0;
+ virtual bool ClientCommand( CBasePlayer *player, const CCommand &args ) = 0;
+
+ virtual void ServerActivate( void ) = 0;
+ virtual void ServerDeactivate( void ) = 0;
+ virtual bool ServerCommand( const char * pcmd ) = 0;
+
+ virtual void RestartRound( void ); ///< (EXTEND) invoked when a new round begins
+ virtual void StartFrame( void ); ///< (EXTEND) called each frame
+
+ virtual unsigned int GetPlayerPriority( CBasePlayer *player ) const = 0; ///< return priority of player (0 = max pri)
+
+
+ void AddGrenade( CBaseGrenade *grenade ); ///< add an active grenade to the bot's awareness
+ void RemoveGrenade( CBaseGrenade *grenade ); ///< the grenade entity in the world is going away
+ void SetGrenadeRadius( CBaseGrenade *grenade, float radius ); ///< the radius of the grenade entity (or associated smoke cloud)
+ void ValidateActiveGrenades( void ); ///< destroy any invalid active grenades
+ void DestroyAllGrenades( void );
+ bool IsLineBlockedBySmoke( const Vector &from, const Vector &to, float grenadeBloat = 1.0f ); ///< return true if line intersects smoke volume, with grenade radius increased by the grenadeBloat factor
+ bool IsInsideSmokeCloud( const Vector *pos ); ///< return true if position is inside a smoke cloud
+
+ //
+ // Invoke functor on all active grenades.
+ // If any functor call return false, return false. Otherwise, return true.
+ //
+ template < typename T >
+ bool ForEachGrenade( T &func )
+ {
+ int it = m_activeGrenadeList.Head();
+
+ while( it != m_activeGrenadeList.InvalidIndex() )
+ {
+ ActiveGrenade *ag = m_activeGrenadeList[ it ];
+
+ int current = it;
+ it = m_activeGrenadeList.Next( it );
+
+ // lazy validation
+ if (!ag->IsValid())
+ {
+ m_activeGrenadeList.Remove( current );
+ delete ag;
+ continue;
+ }
+ else
+ {
+ if (func( ag ) == false)
+ {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ enum { MAX_DBG_MSG_SIZE = 1024 };
+ struct DebugMessage
+ {
+ char m_string[ MAX_DBG_MSG_SIZE ];
+ IntervalTimer m_age;
+ };
+
+ // debug message history -------------------------------------------------------------------------------
+ int GetDebugMessageCount( void ) const; ///< get number of debug messages in history
+ const DebugMessage *GetDebugMessage( int which = 0 ) const; ///< return the debug message emitted by the bot (0 = most recent)
+ void ClearDebugMessages( void );
+ void AddDebugMessage( const char *msg );
+
+
+private:
+ ActiveGrenadeList m_activeGrenadeList;///< the list of active grenades the bots are aware of
+
+ enum { MAX_DBG_MSGS = 6 };
+ DebugMessage m_debugMessage[ MAX_DBG_MSGS ]; ///< debug message history
+ int m_debugMessageCount;
+ int m_currentDebugMessage;
+
+ IntervalTimer m_frameTimer; ///< for measuring each frame's duration
+};
+
+
+inline CBasePlayer *CBotManager::AllocateAndBindBotEntity( edict_t *ed )
+{
+ CBasePlayer::s_PlayerEdict = ed;
+ return AllocateBotEntity();
+}
+
+inline int CBotManager::GetDebugMessageCount( void ) const
+{
+ return m_debugMessageCount;
+}
+
+inline const CBotManager::DebugMessage *CBotManager::GetDebugMessage( int which ) const
+{
+ if (which >= m_debugMessageCount)
+ return NULL;
+
+ int i = m_currentDebugMessage - which;
+ if (i < 0)
+ i += MAX_DBG_MSGS;
+
+ return &m_debugMessage[ i ];
+}
+
+
+
+
+
+// global singleton to create and control bots
+extern CBotManager *TheBots;
+
+
+#endif
diff --git a/game/shared/cstrike/bot/bot_profile.cpp b/game/shared/cstrike/bot/bot_profile.cpp
new file mode 100644
index 0000000..13da6b0
--- /dev/null
+++ b/game/shared/cstrike/bot/bot_profile.cpp
@@ -0,0 +1,704 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+// Author: Michael S. Booth ([email protected]), 2003
+
+#include "cbase.h"
+
+#pragma warning( disable : 4530 ) // STL uses exceptions, but we are not compiling with them - ignore warning
+
+#define DEFINE_DIFFICULTY_NAMES
+#include "bot_profile.h"
+#include "shared_util.h"
+
+#include "bot.h"
+#include "bot_util.h"
+#include "cs_bot.h" // BOTPORT: Remove this CS dependency
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+BotProfileManager *TheBotProfiles = NULL;
+
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * Generates a filename-decorated skin name
+ */
+static const char * GetDecoratedSkinName( const char *name, const char *filename )
+{
+ const int BufLen = _MAX_PATH + 64;
+ static char buf[BufLen];
+ Q_snprintf( buf, sizeof( buf ), "%s/%s", filename, name );
+ return buf;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+const char* BotProfile::GetWeaponPreferenceAsString( int i ) const
+{
+ if ( i < 0 || i >= m_weaponPreferenceCount )
+ return NULL;
+
+ return WeaponIDToAlias( m_weaponPreference[ i ] );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if this profile has a primary weapon preference
+ */
+bool BotProfile::HasPrimaryPreference( void ) const
+{
+ for( int i=0; i<m_weaponPreferenceCount; ++i )
+ {
+ if (IsPrimaryWeapon( m_weaponPreference[i] ))
+ return true;
+ }
+
+ return false;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if this profile has a pistol weapon preference
+ */
+bool BotProfile::HasPistolPreference( void ) const
+{
+ for( int i=0; i<m_weaponPreferenceCount; ++i )
+ if (IsSecondaryWeapon( m_weaponPreference[i] ))
+ return true;
+
+ return false;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if this profile is valid for the specified team
+ */
+bool BotProfile::IsValidForTeam( int team ) const
+{
+ return ( team == TEAM_UNASSIGNED || m_teams == TEAM_UNASSIGNED || team == m_teams );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+* Return true if this profile inherits from the specified template
+*/
+bool BotProfile::InheritsFrom( const char *name ) const
+{
+ if ( WildcardMatch( name, GetName() ) )
+ return true;
+
+ for ( int i=0; i<m_templates.Count(); ++i )
+ {
+ const BotProfile *queryTemplate = m_templates[i];
+ if ( queryTemplate->InheritsFrom( name ) )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Constructor
+ */
+BotProfileManager::BotProfileManager( void )
+{
+ m_nextSkin = 0;
+ for (int i=0; i<NumCustomSkins; ++i)
+ {
+ m_skins[i] = NULL;
+ m_skinFilenames[i] = NULL;
+ m_skinModelnames[i] = NULL;
+ }
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Load the bot profile database
+ */
+void BotProfileManager::Init( const char *filename, unsigned int *checksum )
+{
+ FileHandle_t file = filesystem->Open( filename, "r" );
+
+ if (!file)
+ {
+ if ( true ) // UTIL_IsGame( "czero" ) )
+ {
+ CONSOLE_ECHO( "WARNING: Cannot access bot profile database '%s'\n", filename );
+ }
+ return;
+ }
+
+ int dataLength = filesystem->Size( filename );
+ char *dataPointer = new char[ dataLength ];
+ int dataReadLength = filesystem->Read( dataPointer, dataLength, file );
+ filesystem->Close( file );
+ if ( dataReadLength > 0 )
+ {
+ // NULL-terminate based on the length read in, since Read() can transform \r\n to \n and
+ // return fewer bytes than we were expecting.
+ dataPointer[ dataReadLength - 1 ] = 0;
+ }
+
+ const char *dataFile = dataPointer;
+
+ // compute simple checksum
+ if (checksum)
+ {
+ *checksum = 0; // ComputeSimpleChecksum( (const unsigned char *)dataPointer, dataLength );
+ }
+
+ BotProfile defaultProfile;
+
+ //
+ // Parse the BotProfile.db into BotProfile instances
+ //
+ while( true )
+ {
+ dataFile = SharedParse( dataFile );
+ if (!dataFile)
+ break;
+
+ char *token = SharedGetToken();
+
+ bool isDefault = (!stricmp( token, "Default" ));
+ bool isTemplate = (!stricmp( token, "Template" ));
+ bool isCustomSkin = (!stricmp( token, "Skin" ));
+
+ if ( isCustomSkin )
+ {
+ const int BufLen = 64;
+ char skinName[BufLen];
+
+ // get skin name
+ dataFile = SharedParse( dataFile );
+ if (!dataFile)
+ {
+ CONSOLE_ECHO( "Error parsing %s - expected skin name\n", filename );
+ delete [] dataPointer;
+ return;
+ }
+ token = SharedGetToken();
+ Q_snprintf( skinName, sizeof( skinName ), "%s", token );
+
+ // get attribute name
+ dataFile = SharedParse( dataFile );
+ if (!dataFile)
+ {
+ CONSOLE_ECHO( "Error parsing %s - expected 'Model'\n", filename );
+ delete [] dataPointer;
+ return;
+ }
+ token = SharedGetToken();
+ if (stricmp( "Model", token ))
+ {
+ CONSOLE_ECHO( "Error parsing %s - expected 'Model'\n", filename );
+ delete [] dataPointer;
+ return;
+ }
+
+ // eat '='
+ dataFile = SharedParse( dataFile );
+ if (!dataFile)
+ {
+ CONSOLE_ECHO( "Error parsing %s - expected '='\n", filename );
+ delete [] dataPointer;
+ return;
+ }
+ token = SharedGetToken();
+ if (strcmp( "=", token ))
+ {
+ CONSOLE_ECHO( "Error parsing %s - expected '='\n", filename );
+ delete [] dataPointer;
+ return;
+ }
+
+ // get attribute value
+ dataFile = SharedParse( dataFile );
+ if (!dataFile)
+ {
+ CONSOLE_ECHO( "Error parsing %s - expected attribute value\n", filename );
+ delete [] dataPointer;
+ return;
+ }
+ token = SharedGetToken();
+
+ const char *decoratedName = GetDecoratedSkinName( skinName, filename );
+ bool skinExists = GetCustomSkinIndex( decoratedName ) > 0;
+ if ( m_nextSkin < NumCustomSkins && !skinExists )
+ {
+ // decorate the name
+ m_skins[ m_nextSkin ] = CloneString( decoratedName );
+
+ // construct the model filename
+ m_skinModelnames[ m_nextSkin ] = CloneString( token );
+ m_skinFilenames[ m_nextSkin ] = new char[ strlen(token)*2 + strlen("models/player//.mdl") + 1 ];
+ Q_snprintf( m_skinFilenames[ m_nextSkin ], sizeof( m_skinFilenames[ m_nextSkin ] ), "models/player/%s/%s.mdl", token, token );
+ ++m_nextSkin;
+ }
+
+ // eat 'End'
+ dataFile = SharedParse( dataFile );
+ if (!dataFile)
+ {
+ CONSOLE_ECHO( "Error parsing %s - expected 'End'\n", filename );
+ delete [] dataPointer;
+ return;
+ }
+ token = SharedGetToken();
+ if (strcmp( "End", token ))
+ {
+ CONSOLE_ECHO( "Error parsing %s - expected 'End'\n", filename );
+ delete [] dataPointer;
+ return;
+ }
+
+ continue; // it's just a custom skin - no need to do inheritance on a bot profile, etc.
+ }
+
+ // encountered a new profile
+ BotProfile *profile;
+
+ if (isDefault)
+ {
+ profile = &defaultProfile;
+ }
+ else
+ {
+ profile = new BotProfile;
+
+ // always inherit from Default
+ *profile = defaultProfile;
+ }
+
+ // do inheritance in order of appearance
+ if (!isTemplate && !isDefault)
+ {
+ const BotProfile *inherit = NULL;
+
+ // template names are separated by "+"
+ while(true)
+ {
+ char *c = strchr( token, '+' );
+ if (c)
+ *c = '\000';
+
+ // find the given template name
+ FOR_EACH_LL( m_templateList, it )
+ {
+ BotProfile *profile = m_templateList[ it ];
+ if (!stricmp( profile->GetName(), token ))
+ {
+ inherit = profile;
+ break;
+ }
+ }
+
+ if (inherit == NULL)
+ {
+ CONSOLE_ECHO( "Error parsing '%s' - invalid template reference '%s'\n", filename, token );
+ delete [] dataPointer;
+ return;
+ }
+
+ // inherit the data
+ profile->Inherit( inherit, &defaultProfile );
+
+ if (c == NULL)
+ break;
+
+ token = c+1;
+ }
+ }
+
+
+ // get name of this profile
+ if (!isDefault)
+ {
+ dataFile = SharedParse( dataFile );
+ if (!dataFile)
+ {
+ CONSOLE_ECHO( "Error parsing '%s' - expected name\n", filename );
+ delete [] dataPointer;
+ return;
+ }
+ profile->m_name = CloneString( SharedGetToken() );
+
+ /**
+ * HACK HACK
+ * Until we have a generalized means of storing bot preferences, we're going to hardcode the bot's
+ * preference towards silencers based on his name.
+ */
+ if ( profile->m_name[0] % 2 )
+ {
+ profile->m_prefersSilencer = true;
+ }
+ }
+
+ // read attributes for this profile
+ bool isFirstWeaponPref = true;
+ while( true )
+ {
+ // get next token
+ dataFile = SharedParse( dataFile );
+ if (!dataFile)
+ {
+ CONSOLE_ECHO( "Error parsing %s - expected 'End'\n", filename );
+ delete [] dataPointer;
+ return;
+ }
+ token = SharedGetToken();
+
+ // check for End delimiter
+ if (!stricmp( token, "End" ))
+ break;
+
+ // found attribute name - keep it
+ char attributeName[64];
+ strcpy( attributeName, token );
+
+ // eat '='
+ dataFile = SharedParse( dataFile );
+ if (!dataFile)
+ {
+ CONSOLE_ECHO( "Error parsing %s - expected '='\n", filename );
+ delete [] dataPointer;
+ return;
+ }
+
+ token = SharedGetToken();
+ if (strcmp( "=", token ))
+ {
+ CONSOLE_ECHO( "Error parsing %s - expected '='\n", filename );
+ delete [] dataPointer;
+ return;
+ }
+
+ // get attribute value
+ dataFile = SharedParse( dataFile );
+ if (!dataFile)
+ {
+ CONSOLE_ECHO( "Error parsing %s - expected attribute value\n", filename );
+ delete [] dataPointer;
+ return;
+ }
+ token = SharedGetToken();
+
+ // store value in appropriate attribute
+ if (!stricmp( "Aggression", attributeName ))
+ {
+ profile->m_aggression = (float)atof(token) / 100.0f;
+ }
+ else if (!stricmp( "Skill", attributeName ))
+ {
+ profile->m_skill = (float)atof(token) / 100.0f;
+ }
+ else if (!stricmp( "Skin", attributeName ))
+ {
+ profile->m_skin = atoi(token);
+ if ( profile->m_skin == 0 )
+ {
+ // atoi() failed - try to look up a custom skin by name
+ profile->m_skin = GetCustomSkinIndex( token, filename );
+ }
+ }
+ else if (!stricmp( "Teamwork", attributeName ))
+ {
+ profile->m_teamwork = (float)atof(token) / 100.0f;
+ }
+ else if (!stricmp( "Cost", attributeName ))
+ {
+ profile->m_cost = atoi(token);
+ }
+ else if (!stricmp( "VoicePitch", attributeName ))
+ {
+ profile->m_voicePitch = atoi(token);
+ }
+ else if (!stricmp( "VoiceBank", attributeName ))
+ {
+ profile->m_voiceBank = FindVoiceBankIndex( token );
+ }
+ else if (!stricmp( "WeaponPreference", attributeName ))
+ {
+ // weapon preferences override parent prefs
+ if (isFirstWeaponPref)
+ {
+ isFirstWeaponPref = false;
+ profile->m_weaponPreferenceCount = 0;
+ }
+
+ if (!stricmp( token, "none" ))
+ {
+ profile->m_weaponPreferenceCount = 0;
+ }
+ else
+ {
+ if (profile->m_weaponPreferenceCount < BotProfile::MAX_WEAPON_PREFS)
+ {
+ profile->m_weaponPreference[ profile->m_weaponPreferenceCount++ ] = AliasToWeaponID( token );
+ }
+ }
+ }
+ else if (!stricmp( "ReactionTime", attributeName ))
+ {
+ profile->m_reactionTime = (float)atof(token);
+
+#ifndef GAMEUI_EXPORTS
+ // subtract off latency due to "think" update rate.
+ // In GameUI, we don't really care.
+ //profile->m_reactionTime -= g_BotUpdateInterval;
+#endif
+
+ }
+ else if (!stricmp( "AttackDelay", attributeName ))
+ {
+ profile->m_attackDelay = (float)atof(token);
+ }
+ else if (!stricmp( "Difficulty", attributeName ))
+ {
+ // override inheritance
+ profile->m_difficultyFlags = 0;
+
+ // parse bit flags
+ while(true)
+ {
+ char *c = strchr( token, '+' );
+ if (c)
+ *c = '\000';
+
+ for( int i=0; i<NUM_DIFFICULTY_LEVELS; ++i )
+ if (!stricmp( BotDifficultyName[i], token ))
+ profile->m_difficultyFlags |= (1 << i);
+
+ if (c == NULL)
+ break;
+
+ token = c+1;
+ }
+ }
+ else if (!stricmp( "Team", attributeName ))
+ {
+ if ( !stricmp( token, "T" ) )
+ {
+ profile->m_teams = TEAM_TERRORIST;
+ }
+ else if ( !stricmp( token, "CT" ) )
+ {
+ profile->m_teams = TEAM_CT;
+ }
+ else
+ {
+ profile->m_teams = TEAM_UNASSIGNED;
+ }
+ }
+ else
+ {
+ CONSOLE_ECHO( "Error parsing %s - unknown attribute '%s'\n", filename, attributeName );
+ }
+ }
+
+ if (!isDefault)
+ {
+ if (isTemplate)
+ {
+ // add to template list
+ m_templateList.AddToTail( profile );
+ }
+ else
+ {
+ // add profile to the master list
+ m_profileList.AddToTail( profile );
+ }
+ }
+ }
+
+ delete [] dataPointer;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+BotProfileManager::~BotProfileManager( void )
+{
+ Reset();
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Free all bot profiles
+ */
+void BotProfileManager::Reset( void )
+{
+ m_profileList.PurgeAndDeleteElements();
+ m_templateList.PurgeAndDeleteElements();
+
+ int i;
+
+ for (i=0; i<NumCustomSkins; ++i)
+ {
+ if ( m_skins[i] )
+ {
+ delete[] m_skins[i];
+ m_skins[i] = NULL;
+ }
+ if ( m_skinFilenames[i] )
+ {
+ delete[] m_skinFilenames[i];
+ m_skinFilenames[i] = NULL;
+ }
+ if ( m_skinModelnames[i] )
+ {
+ delete[] m_skinModelnames[i];
+ m_skinModelnames[i] = NULL;
+ }
+ }
+
+ for ( i=0; i<m_voiceBanks.Count(); ++i )
+ {
+ delete[] m_voiceBanks[i];
+ }
+ m_voiceBanks.RemoveAll();
+}
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * Returns custom skin name at a particular index
+ */
+const char * BotProfileManager::GetCustomSkin( int index )
+{
+ if ( index < FirstCustomSkin || index > LastCustomSkin )
+ {
+ return NULL;
+ }
+
+ return m_skins[ index - FirstCustomSkin ];
+}
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * Returns custom skin filename at a particular index
+ */
+const char * BotProfileManager::GetCustomSkinFname( int index )
+{
+ if ( index < FirstCustomSkin || index > LastCustomSkin )
+ {
+ return NULL;
+ }
+
+ return m_skinFilenames[ index - FirstCustomSkin ];
+}
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * Returns custom skin modelname at a particular index
+ */
+const char * BotProfileManager::GetCustomSkinModelname( int index )
+{
+ if ( index < FirstCustomSkin || index > LastCustomSkin )
+ {
+ return NULL;
+ }
+
+ return m_skinModelnames[ index - FirstCustomSkin ];
+}
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * Looks up a custom skin index by filename-decorated name (will decorate the name if filename is given)
+ */
+int BotProfileManager::GetCustomSkinIndex( const char *name, const char *filename )
+{
+ const char * skinName = name;
+ if ( filename )
+ {
+ skinName = GetDecoratedSkinName( name, filename );
+ }
+
+ for (int i=0; i<NumCustomSkins; ++i)
+ {
+ if ( m_skins[i] )
+ {
+ if ( !stricmp( skinName, m_skins[i] ) )
+ {
+ return FirstCustomSkin + i;
+ }
+ }
+ }
+ return 0;
+}
+
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * return index of the (custom) bot phrase db, inserting it if needed
+ */
+int BotProfileManager::FindVoiceBankIndex( const char *filename )
+{
+ int index = 0;
+
+ for ( int i=0; i<m_voiceBanks.Count(); ++i )
+ {
+ if ( !stricmp( filename, m_voiceBanks[i] ) )
+ {
+ return index;
+ }
+ }
+
+ m_voiceBanks.AddToTail( CloneString( filename ) );
+ return index;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return random unused profile that matches the given difficulty level
+ */
+const BotProfile *BotProfileManager::GetRandomProfile( BotDifficultyType difficulty, int team, CSWeaponType weaponType ) const
+{
+ // count up valid profiles
+ CUtlVector< const BotProfile * > profiles;
+ FOR_EACH_LL( m_profileList, it )
+ {
+ const BotProfile *profile = m_profileList[ it ];
+
+ // Match difficulty
+ if ( !profile->IsDifficulty( difficulty ) )
+ continue;
+
+ // Prevent duplicate names
+ if ( UTIL_IsNameTaken( profile->GetName() ) )
+ continue;
+
+ // Match team choice
+ if ( !profile->IsValidForTeam( team ) )
+ continue;
+
+ // Match desired weapon
+ if ( weaponType != WEAPONTYPE_UNKNOWN )
+ {
+ if ( !profile->GetWeaponPreferenceCount() )
+ continue;
+
+ if ( weaponType != WeaponClassFromWeaponID( (CSWeaponID)profile->GetWeaponPreference( 0 ) ) )
+ continue;
+ }
+
+ profiles.AddToTail( profile );
+ }
+
+ if ( !profiles.Count() )
+ return NULL;
+
+ // select one at random
+ int which = RandomInt( 0, profiles.Count()-1 );
+ return profiles[which];
+}
+
diff --git a/game/shared/cstrike/bot/bot_profile.h b/game/shared/cstrike/bot/bot_profile.h
new file mode 100644
index 0000000..172380a
--- /dev/null
+++ b/game/shared/cstrike/bot/bot_profile.h
@@ -0,0 +1,251 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+// Author: Michael S. Booth ([email protected]), 2003
+
+#ifndef _BOT_PROFILE_H_
+#define _BOT_PROFILE_H_
+
+#pragma warning( disable : 4786 ) // long STL names get truncated in browse info.
+
+#include "bot_constants.h"
+#include "bot_util.h"
+#include "cs_weapon_parse.h"
+
+enum
+{
+ FirstCustomSkin = 100,
+ NumCustomSkins = 100,
+ LastCustomSkin = FirstCustomSkin + NumCustomSkins - 1,
+};
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * A BotProfile describes the "personality" of a given bot
+ */
+class BotProfile
+{
+public:
+ BotProfile( void )
+ {
+ m_name = NULL;
+ m_aggression = 0.0f;
+ m_skill = 0.0f;
+ m_teamwork = 0.0f;
+ m_weaponPreferenceCount = 0;
+ m_cost = 0;
+ m_skin = 0;
+ m_difficultyFlags = 0;
+ m_voicePitch = 100;
+ m_reactionTime = 0.3f;
+ m_attackDelay = 0.0f;
+ m_teams = TEAM_UNASSIGNED;
+ m_voiceBank = 0;
+ m_prefersSilencer = false;
+ }
+
+ ~BotProfile( void )
+ {
+ if ( m_name )
+ delete [] m_name;
+ }
+
+ const char *GetName( void ) const { return m_name; } ///< return bot's name
+ float GetAggression( void ) const { return m_aggression; }
+ float GetSkill( void ) const { return m_skill; }
+ float GetTeamwork( void ) const { return m_teamwork; }
+
+ CSWeaponID GetWeaponPreference( int i ) const { return m_weaponPreference[ i ]; }
+ const char *GetWeaponPreferenceAsString( int i ) const;
+ int GetWeaponPreferenceCount( void ) const { return m_weaponPreferenceCount; }
+ bool HasPrimaryPreference( void ) const; ///< return true if this profile has a primary weapon preference
+ bool HasPistolPreference( void ) const; ///< return true if this profile has a pistol weapon preference
+
+ int GetCost( void ) const { return m_cost; }
+ int GetSkin( void ) const { return m_skin; }
+ bool IsDifficulty( BotDifficultyType diff ) const; ///< return true if this profile can be used for the given difficulty level
+ int GetVoicePitch( void ) const { return m_voicePitch; }
+ float GetReactionTime( void ) const { return m_reactionTime; }
+ float GetAttackDelay( void ) const { return m_attackDelay; }
+ int GetVoiceBank() const { return m_voiceBank; }
+
+ bool IsValidForTeam( int team ) const;
+
+ bool PrefersSilencer() const { return m_prefersSilencer; }
+
+ bool InheritsFrom( const char *name ) const;
+
+private:
+ friend class BotProfileManager; ///< for loading profiles
+
+ void Inherit( const BotProfile *parent, const BotProfile *baseline ); ///< copy values from parent if they differ from baseline
+
+ char *m_name; ///< the bot's name
+ float m_aggression; ///< percentage: 0 = coward, 1 = berserker
+ float m_skill; ///< percentage: 0 = terrible, 1 = expert
+ float m_teamwork; ///< percentage: 0 = rogue, 1 = complete obeyance to team, lots of comm
+
+ enum { MAX_WEAPON_PREFS = 16 };
+ CSWeaponID m_weaponPreference[ MAX_WEAPON_PREFS ]; ///< which weapons this bot likes to use, in order of priority
+ int m_weaponPreferenceCount;
+
+ int m_cost; ///< reputation point cost for career mode
+ int m_skin; ///< "skin" index
+ unsigned char m_difficultyFlags; ///< bits set correspond to difficulty levels this is valid for
+ int m_voicePitch; ///< the pitch shift for bot chatter (100 = normal)
+ float m_reactionTime; //< our reaction time in seconds
+ float m_attackDelay; ///< time in seconds from when we notice an enemy to when we open fire
+ int m_teams; ///< teams for which this profile is valid
+
+ bool m_prefersSilencer; ///< does the bot prefer to use silencers?
+
+ int m_voiceBank; ///< Index of the BotChatter.db voice bank this profile uses (0 is the default)
+
+ CUtlVector< const BotProfile * > m_templates; ///< List of templates we inherit from
+};
+typedef CUtlLinkedList<BotProfile *> BotProfileList;
+
+
+inline bool BotProfile::IsDifficulty( BotDifficultyType diff ) const
+{
+ return (m_difficultyFlags & (1 << diff)) ? true : false;
+}
+
+/**
+ * Copy in data from parent if it differs from the baseline
+ */
+inline void BotProfile::Inherit( const BotProfile *parent, const BotProfile *baseline )
+{
+ if (parent->m_aggression != baseline->m_aggression)
+ m_aggression = parent->m_aggression;
+
+ if (parent->m_skill != baseline->m_skill)
+ m_skill = parent->m_skill;
+
+ if (parent->m_teamwork != baseline->m_teamwork)
+ m_teamwork = parent->m_teamwork;
+
+ if (parent->m_weaponPreferenceCount != baseline->m_weaponPreferenceCount)
+ {
+ m_weaponPreferenceCount = parent->m_weaponPreferenceCount;
+ for( int i=0; i<parent->m_weaponPreferenceCount; ++i )
+ m_weaponPreference[i] = parent->m_weaponPreference[i];
+ }
+
+ if (parent->m_cost != baseline->m_cost)
+ m_cost = parent->m_cost;
+
+ if (parent->m_skin != baseline->m_skin)
+ m_skin = parent->m_skin;
+
+ if (parent->m_difficultyFlags != baseline->m_difficultyFlags)
+ m_difficultyFlags = parent->m_difficultyFlags;
+
+ if (parent->m_voicePitch != baseline->m_voicePitch)
+ m_voicePitch = parent->m_voicePitch;
+
+ if (parent->m_reactionTime != baseline->m_reactionTime)
+ m_reactionTime = parent->m_reactionTime;
+
+ if (parent->m_attackDelay != baseline->m_attackDelay)
+ m_attackDelay = parent->m_attackDelay;
+
+ if (parent->m_teams != baseline->m_teams)
+ m_teams = parent->m_teams;
+
+ if (parent->m_voiceBank != baseline->m_voiceBank)
+ m_voiceBank = parent->m_voiceBank;
+
+ m_templates.AddToTail( parent );
+}
+
+
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * The BotProfileManager defines the interface to accessing BotProfiles
+ */
+class BotProfileManager
+{
+public:
+ BotProfileManager( void );
+ ~BotProfileManager( void );
+
+ void Init( const char *filename, unsigned int *checksum = NULL );
+ void Reset( void );
+
+ /// given a name, return a profile
+ const BotProfile *GetProfile( const char *name, int team ) const
+ {
+ FOR_EACH_LL( m_profileList, it )
+ {
+ BotProfile *profile = m_profileList[ it ];
+
+ if ( !stricmp( name, profile->GetName() ) && profile->IsValidForTeam( team ) )
+ return profile;
+ }
+
+ return NULL;
+ }
+
+ /// given a template name and difficulty, return a profile
+ const BotProfile *GetProfileMatchingTemplate( const char *profileName, int team, BotDifficultyType difficulty ) const
+ {
+ FOR_EACH_LL( m_profileList, it )
+ {
+ BotProfile *profile = m_profileList[ it ];
+
+ if ( !profile->InheritsFrom( profileName ) )
+ continue;
+
+ if ( !profile->IsValidForTeam( team ) )
+ continue;
+
+ if ( !profile->IsDifficulty( difficulty ) )
+ continue;
+
+ if ( UTIL_IsNameTaken( profile->GetName() ) )
+ continue;
+
+ return profile;
+ }
+
+ return NULL;
+ }
+
+ const BotProfileList *GetProfileList( void ) const { return &m_profileList; } ///< return list of all profiles
+
+ const BotProfile *GetRandomProfile( BotDifficultyType difficulty, int team, CSWeaponType weaponType ) const; ///< return random unused profile that matches the given difficulty level
+
+ const char * GetCustomSkin( int index ); ///< Returns custom skin name at a particular index
+ const char * GetCustomSkinModelname( int index ); ///< Returns custom skin modelname at a particular index
+ const char * GetCustomSkinFname( int index ); ///< Returns custom skin filename at a particular index
+ int GetCustomSkinIndex( const char *name, const char *filename = NULL ); ///< Looks up a custom skin index by name
+
+ typedef CUtlVector<char *> VoiceBankList;
+ const VoiceBankList *GetVoiceBanks( void ) const { return &m_voiceBanks; }
+ int FindVoiceBankIndex( const char *filename ); ///< return index of the (custom) bot phrase db, inserting it if needed
+
+protected:
+ BotProfileList m_profileList; ///< the list of all bot profiles
+ BotProfileList m_templateList; ///< the list of all bot templates
+
+ VoiceBankList m_voiceBanks;
+
+ char *m_skins[ NumCustomSkins ]; ///< Custom skin names
+ char *m_skinModelnames[ NumCustomSkins ]; ///< Custom skin modelnames
+ char *m_skinFilenames[ NumCustomSkins ]; ///< Custom skin filenames
+ int m_nextSkin; ///< Next custom skin to allocate
+};
+
+/// the global singleton for accessing BotProfiles
+extern BotProfileManager *TheBotProfiles;
+
+
+#endif // _BOT_PROFILE_H_
diff --git a/game/shared/cstrike/bot/bot_util.cpp b/game/shared/cstrike/bot/bot_util.cpp
new file mode 100644
index 0000000..bd10ff2
--- /dev/null
+++ b/game/shared/cstrike/bot/bot_util.cpp
@@ -0,0 +1,604 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+// Author: Michael S. Booth ([email protected]), 2003
+
+#include "cbase.h"
+#include "cs_shareddefs.h"
+#include "engine/IEngineSound.h"
+#include "KeyValues.h"
+
+#include "bot.h"
+#include "bot_util.h"
+#include "bot_profile.h"
+
+#include "cs_bot.h"
+#include <ctype.h>
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+static int s_iBeamSprite = 0;
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if given name is already in use by another player
+ */
+bool UTIL_IsNameTaken( const char *name, bool ignoreHumans )
+{
+ for ( int i = 1; i <= gpGlobals->maxClients; ++i )
+ {
+ CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) );
+
+ if (player == NULL)
+ continue;
+
+ if (player->IsPlayer() && player->IsBot())
+ {
+ // bots can have prefixes so we need to check the name
+ // against the profile name instead.
+ CCSBot *bot = dynamic_cast<CCSBot *>(player);
+ if ( bot && bot->GetProfile()->GetName() && FStrEq(name, bot->GetProfile()->GetName()))
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (!ignoreHumans)
+ {
+ if (FStrEq( name, player->GetPlayerName() ))
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+int UTIL_ClientsInGame( void )
+{
+ int count = 0;
+
+ for ( int i = 1; i <= gpGlobals->maxClients; ++i )
+ {
+ CBaseEntity *player = UTIL_PlayerByIndex( i );
+
+ if (player == NULL)
+ continue;
+
+ count++;
+ }
+
+ return count;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return the number of non-bots on the given team
+ */
+int UTIL_HumansOnTeam( int teamID, bool isAlive )
+{
+ int count = 0;
+
+ for ( int i = 1; i <= gpGlobals->maxClients; ++i )
+ {
+ CBaseEntity *entity = UTIL_PlayerByIndex( i );
+
+ if ( entity == NULL )
+ continue;
+
+ CBasePlayer *player = static_cast<CBasePlayer *>( entity );
+
+ if (player->IsBot())
+ continue;
+
+ if (player->GetTeamNumber() != teamID)
+ continue;
+
+ if (isAlive && !player->IsAlive())
+ continue;
+
+ count++;
+ }
+
+ return count;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+int UTIL_BotsInGame( void )
+{
+ int count = 0;
+
+ for (int i = 1; i <= gpGlobals->maxClients; ++i )
+ {
+ CBasePlayer *player = static_cast<CBasePlayer *>(UTIL_PlayerByIndex( i ));
+
+ if ( player == NULL )
+ continue;
+
+ if ( !player->IsBot() )
+ continue;
+
+ count++;
+ }
+
+ return count;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Kick a bot from the given team. If no bot exists on the team, return false.
+ */
+bool UTIL_KickBotFromTeam( int kickTeam )
+{
+ int i;
+
+ // try to kick a dead bot first
+ for ( i = 1; i <= gpGlobals->maxClients; ++i )
+ {
+ CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) );
+
+ if (player == NULL)
+ continue;
+
+ if (!player->IsBot())
+ continue;
+
+ if (!player->IsAlive() && player->GetTeamNumber() == kickTeam)
+ {
+ // its a bot on the right team - kick it
+ engine->ServerCommand( UTIL_VarArgs( "kick \"%s\"\n", player->GetPlayerName() ) );
+
+ return true;
+ }
+ }
+
+ // no dead bots, kick any bot on the given team
+ for ( i = 1; i <= gpGlobals->maxClients; ++i )
+ {
+ CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) );
+
+ if (player == NULL)
+ continue;
+
+ if (!player->IsBot())
+ continue;
+
+ if (player->GetTeamNumber() == kickTeam)
+ {
+ // its a bot on the right team - kick it
+ engine->ServerCommand( UTIL_VarArgs( "kick \"%s\"\n", player->GetPlayerName() ) );
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if all of the members of the given team are bots
+ */
+bool UTIL_IsTeamAllBots( int team )
+{
+ int botCount = 0;
+
+ for( int i=1; i <= gpGlobals->maxClients; ++i )
+ {
+ CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) );
+
+ if (player == NULL)
+ continue;
+
+ // skip players on other teams
+ if (player->GetTeamNumber() != team)
+ continue;
+
+ // if not a bot, fail the test
+ if (!player->IsBot())
+ return false;
+
+ // is a bot on given team
+ ++botCount;
+ }
+
+ // if team is empty, there are no bots
+ return (botCount) ? true : false;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return the closest active player to the given position.
+ * If 'distance' is non-NULL, the distance to the closest player is returned in it.
+ */
+extern CBasePlayer *UTIL_GetClosestPlayer( const Vector &pos, float *distance )
+{
+ CBasePlayer *closePlayer = NULL;
+ float closeDistSq = 999999999999.9f;
+
+ for ( int i = 1; i <= gpGlobals->maxClients; ++i )
+ {
+ CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) );
+
+ if (!IsEntityValid( player ))
+ continue;
+
+ if (!player->IsAlive())
+ continue;
+
+ Vector playerOrigin = GetCentroid( player );
+ float distSq = (playerOrigin - pos).LengthSqr();
+ if (distSq < closeDistSq)
+ {
+ closeDistSq = distSq;
+ closePlayer = static_cast<CBasePlayer *>( player );
+ }
+ }
+
+ if (distance)
+ *distance = (float)sqrt( closeDistSq );
+
+ return closePlayer;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return the closest active player on the given team to the given position.
+ * If 'distance' is non-NULL, the distance to the closest player is returned in it.
+ */
+extern CBasePlayer *UTIL_GetClosestPlayer( const Vector &pos, int team, float *distance )
+{
+ CBasePlayer *closePlayer = NULL;
+ float closeDistSq = 999999999999.9f;
+
+ for ( int i = 1; i <= gpGlobals->maxClients; ++i )
+ {
+ CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) );
+
+ if (!IsEntityValid( player ))
+ continue;
+
+ if (!player->IsAlive())
+ continue;
+
+ if (player->GetTeamNumber() != team)
+ continue;
+
+ Vector playerOrigin = GetCentroid( player );
+ float distSq = (playerOrigin - pos).LengthSqr();
+ if (distSq < closeDistSq)
+ {
+ closeDistSq = distSq;
+ closePlayer = static_cast<CBasePlayer *>( player );
+ }
+ }
+
+ if (distance)
+ *distance = (float)sqrt( closeDistSq );
+
+ return closePlayer;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+// Takes the bot pointer and constructs the net name using the current bot name prefix.
+void UTIL_ConstructBotNetName( char *name, int nameLength, const BotProfile *profile )
+{
+ if (profile == NULL)
+ {
+ name[0] = 0;
+ return;
+ }
+
+ // if there is no bot prefix just use the profile name.
+ if ((cv_bot_prefix.GetString() == NULL) || (strlen(cv_bot_prefix.GetString()) == 0))
+ {
+ Q_strncpy( name, profile->GetName(), nameLength );
+ return;
+ }
+
+ // find the highest difficulty
+ const char *diffStr = BotDifficultyName[0];
+ for ( int i=BOT_EXPERT; i>0; --i )
+ {
+ if ( profile->IsDifficulty( (BotDifficultyType)i ) )
+ {
+ diffStr = BotDifficultyName[i];
+ break;
+ }
+ }
+
+ const char *weaponStr = NULL;
+ if ( profile->GetWeaponPreferenceCount() )
+ {
+ weaponStr = profile->GetWeaponPreferenceAsString( 0 );
+
+ const char *translatedAlias = GetTranslatedWeaponAlias( weaponStr );
+
+ char wpnName[128];
+ Q_snprintf( wpnName, sizeof( wpnName ), "weapon_%s", translatedAlias );
+ WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( wpnName );
+ if ( hWpnInfo != GetInvalidWeaponInfoHandle() )
+ {
+ CCSWeaponInfo *pWeaponInfo = dynamic_cast< CCSWeaponInfo* >( GetFileWeaponInfoFromHandle( hWpnInfo ) );
+ if ( pWeaponInfo )
+ {
+ CSWeaponType weaponType = pWeaponInfo->m_WeaponType;
+ weaponStr = WeaponClassAsString( weaponType );
+ }
+ }
+ }
+ if ( !weaponStr )
+ {
+ weaponStr = "";
+ }
+
+ char skillStr[16];
+ Q_snprintf( skillStr, sizeof( skillStr ), "%.0f", profile->GetSkill()*100 );
+
+ char temp[MAX_PLAYER_NAME_LENGTH*2];
+ char prefix[MAX_PLAYER_NAME_LENGTH*2];
+ Q_strncpy( temp, cv_bot_prefix.GetString(), sizeof( temp ) );
+ Q_StrSubst( temp, "<difficulty>", diffStr, prefix, sizeof( prefix ) );
+ Q_StrSubst( prefix, "<weaponclass>", weaponStr, temp, sizeof( temp ) );
+ Q_StrSubst( temp, "<skill>", skillStr, prefix, sizeof( prefix ) );
+ Q_snprintf( name, nameLength, "%s %s", prefix, profile->GetName() );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if anyone on the given team can see the given spot
+ */
+bool UTIL_IsVisibleToTeam( const Vector &spot, int team )
+{
+ for( int i = 1; i <= gpGlobals->maxClients; ++i )
+ {
+ CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( i ) );
+
+ if (player == NULL)
+ continue;
+
+ if (!player->IsAlive())
+ continue;
+
+ if (player->GetTeamNumber() != team)
+ continue;
+
+ trace_t result;
+ UTIL_TraceLine( player->EyePosition(), spot, CONTENTS_SOLID, player, COLLISION_GROUP_NONE, &result );
+
+ if (result.fraction == 1.0f)
+ return true;
+ }
+
+ return false;
+}
+
+
+//------------------------------------------------------------------------------------------------------------
+void UTIL_DrawBeamFromEnt( int i, Vector vecEnd, int iLifetime, byte bRed, byte bGreen, byte bBlue )
+{
+/* BOTPORT: What is the replacement for MESSAGE_BEGIN?
+ MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecEnd ); // vecEnd = origin???
+ WRITE_BYTE( TE_BEAMENTPOINT );
+ WRITE_SHORT( i );
+ WRITE_COORD( vecEnd.x );
+ WRITE_COORD( vecEnd.y );
+ WRITE_COORD( vecEnd.z );
+ WRITE_SHORT( s_iBeamSprite );
+ WRITE_BYTE( 0 ); // startframe
+ WRITE_BYTE( 0 ); // framerate
+ WRITE_BYTE( iLifetime ); // life
+ WRITE_BYTE( 10 ); // width
+ WRITE_BYTE( 0 ); // noise
+ WRITE_BYTE( bRed ); // r, g, b
+ WRITE_BYTE( bGreen ); // r, g, b
+ WRITE_BYTE( bBlue ); // r, g, b
+ WRITE_BYTE( 255 ); // brightness
+ WRITE_BYTE( 0 ); // speed
+ MESSAGE_END();
+ */
+}
+
+
+//------------------------------------------------------------------------------------------------------------
+void UTIL_DrawBeamPoints( Vector vecStart, Vector vecEnd, int iLifetime, byte bRed, byte bGreen, byte bBlue )
+{
+ NDebugOverlay::Line( vecStart, vecEnd, bRed, bGreen, bBlue, true, 0.1f );
+
+ /*
+ MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecStart );
+ WRITE_BYTE( TE_BEAMPOINTS );
+ WRITE_COORD( vecStart.x );
+ WRITE_COORD( vecStart.y );
+ WRITE_COORD( vecStart.z );
+ WRITE_COORD( vecEnd.x );
+ WRITE_COORD( vecEnd.y );
+ WRITE_COORD( vecEnd.z );
+ WRITE_SHORT( s_iBeamSprite );
+ WRITE_BYTE( 0 ); // startframe
+ WRITE_BYTE( 0 ); // framerate
+ WRITE_BYTE( iLifetime ); // life
+ WRITE_BYTE( 10 ); // width
+ WRITE_BYTE( 0 ); // noise
+ WRITE_BYTE( bRed ); // r, g, b
+ WRITE_BYTE( bGreen ); // r, g, b
+ WRITE_BYTE( bBlue ); // r, g, b
+ WRITE_BYTE( 255 ); // brightness
+ WRITE_BYTE( 0 ); // speed
+ MESSAGE_END();
+ */
+}
+
+
+//------------------------------------------------------------------------------------------------------------
+void CONSOLE_ECHO( const char * pszMsg, ... )
+{
+ va_list argptr;
+ static char szStr[1024];
+
+ va_start( argptr, pszMsg );
+ vsprintf( szStr, pszMsg, argptr );
+ va_end( argptr );
+
+ Msg( "%s", szStr );
+}
+
+
+//------------------------------------------------------------------------------------------------------------
+void BotPrecache( void )
+{
+ s_iBeamSprite = CBaseEntity::PrecacheModel( "sprites/smoke.spr" );
+}
+
+//------------------------------------------------------------------------------------------------------------
+#define COS_TABLE_SIZE 256
+static float cosTable[ COS_TABLE_SIZE ];
+
+void InitBotTrig( void )
+{
+ for( int i=0; i<COS_TABLE_SIZE; ++i )
+ {
+ float angle = (float)(2.0f * M_PI * i / (float)(COS_TABLE_SIZE-1));
+ cosTable[i] = (float)cos( angle );
+ }
+}
+
+float BotCOS( float angle )
+{
+ angle = AngleNormalizePositive( angle );
+ int i = (int)( angle * (COS_TABLE_SIZE-1) / 360.0f );
+ return cosTable[i];
+}
+
+float BotSIN( float angle )
+{
+ angle = AngleNormalizePositive( angle - 90 );
+ int i = (int)( angle * (COS_TABLE_SIZE-1) / 360.0f );
+ return cosTable[i];
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Send a "hint" message to all players, dead or alive.
+ */
+void HintMessageToAllPlayers( const char *message )
+{
+ hudtextparms_t textParms;
+
+ textParms.x = -1.0f;
+ textParms.y = -1.0f;
+ textParms.fadeinTime = 1.0f;
+ textParms.fadeoutTime = 5.0f;
+ textParms.holdTime = 5.0f;
+ textParms.fxTime = 0.0f;
+ textParms.r1 = 100;
+ textParms.g1 = 255;
+ textParms.b1 = 100;
+ textParms.r2 = 255;
+ textParms.g2 = 255;
+ textParms.b2 = 255;
+ textParms.effect = 0;
+ textParms.channel = 0;
+
+ UTIL_HudMessageAll( textParms, message );
+}
+
+//--------------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if moving from "start" to "finish" will cross a player's line of fire.
+ * The path from "start" to "finish" is assumed to be a straight line.
+ * "start" and "finish" are assumed to be points on the ground.
+ */
+bool IsCrossingLineOfFire( const Vector &start, const Vector &finish, CBaseEntity *ignore, int ignoreTeam )
+{
+ for ( int p=1; p <= gpGlobals->maxClients; ++p )
+ {
+ CBasePlayer *player = static_cast<CBasePlayer *>( UTIL_PlayerByIndex( p ) );
+
+ if (!IsEntityValid( player ))
+ continue;
+
+ if (player == ignore)
+ continue;
+
+ if (!player->IsAlive())
+ continue;
+
+ if (ignoreTeam && player->GetTeamNumber() == ignoreTeam)
+ continue;
+
+ // compute player's unit aiming vector
+ Vector viewForward;
+ AngleVectors( player->EyeAngles() + player->GetPunchAngle(), &viewForward );
+
+ const float longRange = 5000.0f;
+ Vector playerOrigin = GetCentroid( player );
+ Vector playerTarget = playerOrigin + longRange * viewForward;
+
+ Vector result( 0, 0, 0 );
+ if (IsIntersecting2D( start, finish, playerOrigin, playerTarget, &result ))
+ {
+ // simple check to see if intersection lies in the Z range of the path
+ float loZ, hiZ;
+
+ if (start.z < finish.z)
+ {
+ loZ = start.z;
+ hiZ = finish.z;
+ }
+ else
+ {
+ loZ = finish.z;
+ hiZ = start.z;
+ }
+
+ if (result.z >= loZ && result.z <= hiZ + HumanHeight)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+* Performs a simple case-insensitive string comparison, honoring trailing * wildcards
+*/
+bool WildcardMatch( const char *query, const char *test )
+{
+ if ( !query || !test )
+ return false;
+
+ while ( *test && *query )
+ {
+ char nameChar = *test;
+ char queryChar = *query;
+ if ( tolower(nameChar) != tolower(queryChar) ) // case-insensitive
+ break;
+ ++test;
+ ++query;
+ }
+
+ if ( *query == 0 && *test == 0 )
+ return true;
+
+ // Support trailing *
+ if ( *query == '*' )
+ return true;
+
+ return false;
+}
+
+
+
diff --git a/game/shared/cstrike/bot/bot_util.h b/game/shared/cstrike/bot/bot_util.h
new file mode 100644
index 0000000..572b1db
--- /dev/null
+++ b/game/shared/cstrike/bot/bot_util.h
@@ -0,0 +1,167 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef BOT_UTIL_H
+#define BOT_UTIL_H
+
+
+#include "convar.h"
+#include "util.h"
+
+//--------------------------------------------------------------------------------------------------------------
+enum PriorityType
+{
+ PRIORITY_LOW, PRIORITY_MEDIUM, PRIORITY_HIGH, PRIORITY_UNINTERRUPTABLE
+};
+
+
+extern ConVar cv_bot_traceview;
+extern ConVar cv_bot_stop;
+extern ConVar cv_bot_show_nav;
+extern ConVar cv_bot_walk;
+extern ConVar cv_bot_difficulty;
+extern ConVar cv_bot_debug;
+extern ConVar cv_bot_debug_target;
+extern ConVar cv_bot_quota;
+extern ConVar cv_bot_quota_mode;
+extern ConVar cv_bot_prefix;
+extern ConVar cv_bot_allow_rogues;
+extern ConVar cv_bot_allow_pistols;
+extern ConVar cv_bot_allow_shotguns;
+extern ConVar cv_bot_allow_sub_machine_guns;
+extern ConVar cv_bot_allow_rifles;
+extern ConVar cv_bot_allow_machine_guns;
+extern ConVar cv_bot_allow_grenades;
+extern ConVar cv_bot_allow_snipers;
+extern ConVar cv_bot_allow_shield;
+extern ConVar cv_bot_join_team;
+extern ConVar cv_bot_join_after_player;
+extern ConVar cv_bot_auto_vacate;
+extern ConVar cv_bot_zombie;
+extern ConVar cv_bot_defer_to_human;
+extern ConVar cv_bot_chatter;
+extern ConVar cv_bot_profile_db;
+extern ConVar cv_bot_dont_shoot;
+extern ConVar cv_bot_eco_limit;
+extern ConVar cv_bot_auto_follow;
+extern ConVar cv_bot_flipout;
+
+#define RAD_TO_DEG( deg ) ((deg) * 180.0 / M_PI)
+#define DEG_TO_RAD( rad ) ((rad) * M_PI / 180.0)
+
+#define SIGN( num ) (((num) < 0) ? -1 : 1)
+#define ABS( num ) (SIGN(num) * (num))
+
+
+#define CREATE_FAKE_CLIENT ( *g_engfuncs.pfnCreateFakeClient )
+#define GET_USERINFO ( *g_engfuncs.pfnGetInfoKeyBuffer )
+#define SET_KEY_VALUE ( *g_engfuncs.pfnSetKeyValue )
+#define SET_CLIENT_KEY_VALUE ( *g_engfuncs.pfnSetClientKeyValue )
+
+class BotProfile;
+
+extern void BotPrecache( void );
+extern int UTIL_ClientsInGame( void );
+
+extern bool UTIL_IsNameTaken( const char *name, bool ignoreHumans = false ); ///< return true if given name is already in use by another player
+
+#define IS_ALIVE true
+extern int UTIL_HumansOnTeam( int teamID, bool isAlive = false );
+
+extern int UTIL_BotsInGame( void );
+extern bool UTIL_IsTeamAllBots( int team );
+extern void UTIL_DrawBeamFromEnt( int iIndex, Vector vecEnd, int iLifetime, byte bRed, byte bGreen, byte bBlue );
+extern void UTIL_DrawBeamPoints( Vector vecStart, Vector vecEnd, int iLifetime, byte bRed, byte bGreen, byte bBlue );
+extern CBasePlayer *UTIL_GetClosestPlayer( const Vector &pos, float *distance = NULL );
+extern CBasePlayer *UTIL_GetClosestPlayer( const Vector &pos, int team, float *distance = NULL );
+extern bool UTIL_KickBotFromTeam( int kickTeam ); ///< kick a bot from the given team. If no bot exists on the team, return false.
+
+extern bool UTIL_IsVisibleToTeam( const Vector &spot, int team ); ///< return true if anyone on the given team can see the given spot
+
+/// return true if moving from "start" to "finish" will cross a player's line of fire.
+extern bool IsCrossingLineOfFire( const Vector &start, const Vector &finish, CBaseEntity *ignore = NULL, int ignoreTeam = 0 );
+
+extern void UTIL_ConstructBotNetName(char *name, int nameLength, const BotProfile *bot); ///< constructs a complete name including prefix
+
+/**
+ * Echos text to the console, and prints it on the client's screen. This is NOT tied to the developer cvar.
+ * If you are adding debugging output in cstrike, use UTIL_DPrintf() (debug.h) instead.
+ */
+extern void CONSOLE_ECHO( PRINTF_FORMAT_STRING const char * pszMsg, ... );
+
+extern void InitBotTrig( void );
+extern float BotCOS( float angle );
+extern float BotSIN( float angle );
+
+extern void HintMessageToAllPlayers( const char *message );
+
+bool WildcardMatch( const char *query, const char *test ); ///< Performs a simple case-insensitive string comparison, honoring trailing * wildcards
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if the given entity is valid
+ */
+inline bool IsEntityValid( CBaseEntity *entity )
+{
+ if (entity == NULL)
+ return false;
+
+ if (FNullEnt( entity->edict() ))
+ return false;
+
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Given two line segments: startA to endA, and startB to endB, return true if they intesect
+ * and put the intersection point in "result".
+ * Note that this computes the intersection of the 2D (x,y) projection of the line segments.
+ */
+inline bool IsIntersecting2D( const Vector &startA, const Vector &endA,
+ const Vector &startB, const Vector &endB,
+ Vector *result = NULL )
+{
+ float denom = (endA.x - startA.x) * (endB.y - startB.y) - (endA.y - startA.y) * (endB.x - startB.x);
+ if (denom == 0.0f)
+ {
+ // parallel
+ return false;
+ }
+
+ float numS = (startA.y - startB.y) * (endB.x - startB.x) - (startA.x - startB.x) * (endB.y - startB.y);
+ if (numS == 0.0f)
+ {
+ // coincident
+ return true;
+ }
+
+ float numT = (startA.y - startB.y) * (endA.x - startA.x) - (startA.x - startB.x) * (endA.y - startA.y);
+
+ float s = numS / denom;
+ if (s < 0.0f || s > 1.0f)
+ {
+ // intersection is not within line segment of startA to endA
+ return false;
+ }
+
+ float t = numT / denom;
+ if (t < 0.0f || t > 1.0f)
+ {
+ // intersection is not within line segment of startB to endB
+ return false;
+ }
+
+ // compute intesection point
+ if (result)
+ *result = startA + s * (endA - startA);
+
+ return true;
+}
+
+
+#endif
diff --git a/game/shared/cstrike/bot/improv_locomotor.h b/game/shared/cstrike/bot/improv_locomotor.h
new file mode 100644
index 0000000..1705461
--- /dev/null
+++ b/game/shared/cstrike/bot/improv_locomotor.h
@@ -0,0 +1,57 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// improv_locomotor.h
+// Interface for moving Improvs along computed paths
+// Author: Michael Booth, July 2004
+
+#ifndef _IMPROV_LOCOMOTOR_H_
+#define _IMPROV_LOCOMOTOR_H_
+
+// TODO: Remove duplicate methods from CImprov, and update CImprov to use this class
+
+/**
+ * A locomotor owns the movement of an Improv
+ */
+class CImprovLocomotor
+{
+public:
+ virtual const Vector &GetCentroid( void ) const = 0;
+ virtual const Vector &GetFeet( void ) const = 0; ///< return position of "feet" - point below centroid of improv at feet level
+ virtual const Vector &GetEyes( void ) const = 0;
+ virtual float GetMoveAngle( void ) const = 0; ///< return direction of movement
+
+ virtual CNavArea *GetLastKnownArea( void ) const = 0;
+ virtual bool GetSimpleGroundHeightWithFloor( const Vector &pos, float *height, Vector *normal = NULL ) = 0; ///< find "simple" ground height, treating current nav area as part of the floor
+
+ virtual void Crouch( void ) = 0;
+ virtual void StandUp( void ) = 0; ///< "un-crouch"
+ virtual bool IsCrouching( void ) const = 0;
+
+ virtual void Jump( void ) = 0; ///< initiate a jump
+ virtual bool IsJumping( void ) const = 0;
+
+ virtual void Run( void ) = 0; ///< set movement speed to running
+ virtual void Walk( void ) = 0; ///< set movement speed to walking
+ virtual bool IsRunning( void ) const = 0;
+
+ virtual void StartLadder( const CNavLadder *ladder, NavTraverseType how, const Vector &approachPos, const Vector &departPos ) = 0; ///< invoked when a ladder is encountered while following a path
+ virtual bool TraverseLadder( const CNavLadder *ladder, NavTraverseType how, const Vector &approachPos, const Vector &departPos, float deltaT ) = 0; ///< traverse given ladder
+ virtual bool IsUsingLadder( void ) const = 0;
+
+ enum MoveToFailureType
+ {
+ FAIL_INVALID_PATH,
+ FAIL_STUCK,
+ FAIL_FELL_OFF,
+ };
+ virtual void TrackPath( const Vector &pathGoal, float deltaT ) = 0; ///< move along path by following "pathGoal"
+ virtual void OnMoveToSuccess( const Vector &goal ) { } ///< invoked when an improv reaches its MoveTo goal
+ virtual void OnMoveToFailure( const Vector &goal, MoveToFailureType reason ) { } ///< invoked when an improv fails to reach a MoveTo goal
+};
+
+#endif // _IMPROV_LOCOMOTOR_H_
diff --git a/game/shared/cstrike/bot/nav_path.cpp b/game/shared/cstrike/bot/nav_path.cpp
new file mode 100644
index 0000000..92285bc
--- /dev/null
+++ b/game/shared/cstrike/bot/nav_path.cpp
@@ -0,0 +1,1208 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// nav_path.cpp
+// Encapsulation of a path through space
+// Author: Michael S. Booth ([email protected]), November 2003
+
+#include "cbase.h"
+#include "cs_gamerules.h"
+#include "cs_player.h"
+
+#include "nav_mesh.h"
+#include "nav_path.h"
+#include "bot_util.h"
+#include "improv_locomotor.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#ifdef _WIN32
+#pragma warning (disable:4701) // disable warning that variable *may* not be initialized
+#endif
+
+
+#define DrawLine( from, to, duration, red, green, blue ) NDebugOverlay::Line( from, to, red, green, blue, true, 0.1f )
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Determine actual path positions
+ */
+bool CNavPath::ComputePathPositions( void )
+{
+ if (m_segmentCount == 0)
+ return false;
+
+ // start in first area's center
+ m_path[0].pos = m_path[0].area->GetCenter();
+ m_path[0].ladder = NULL;
+ m_path[0].how = NUM_TRAVERSE_TYPES;
+
+ for( int i=1; i<m_segmentCount; ++i )
+ {
+ const PathSegment *from = &m_path[ i-1 ];
+ PathSegment *to = &m_path[ i ];
+
+ if (to->how <= GO_WEST) // walk along the floor to the next area
+ {
+ to->ladder = NULL;
+
+ // compute next point, keeping path as straight as possible
+ from->area->ComputeClosestPointInPortal( to->area, (NavDirType)to->how, from->pos, &to->pos );
+
+ // move goal position into the goal area a bit
+ const float stepInDist = 5.0f; // how far to "step into" an area - must be less than min area size
+ AddDirectionVector( &to->pos, (NavDirType)to->how, stepInDist );
+
+ // we need to walk out of "from" area, so keep Z where we can reach it
+ to->pos.z = from->area->GetZ( to->pos );
+
+ // if this is a "jump down" connection, we must insert an additional point on the path
+ if (to->area->IsConnected( from->area, NUM_DIRECTIONS ) == false)
+ {
+ // this is a "jump down" link
+
+ // compute direction of path just prior to "jump down"
+ Vector2D dir;
+ DirectionToVector2D( (NavDirType)to->how, &dir );
+
+ // shift top of "jump down" out a bit to "get over the ledge"
+ const float pushDist = 25.0f;
+ to->pos.x += pushDist * dir.x;
+ to->pos.y += pushDist * dir.y;
+
+ // insert a duplicate node to represent the bottom of the fall
+ if (m_segmentCount < MAX_PATH_SEGMENTS-1)
+ {
+ // copy nodes down
+ for( int j=m_segmentCount; j>i; --j )
+ m_path[j] = m_path[j-1];
+
+ // path is one node longer
+ ++m_segmentCount;
+
+ // move index ahead into the new node we just duplicated
+ ++i;
+
+ m_path[i].pos.x = to->pos.x + pushDist * dir.x;
+ m_path[i].pos.y = to->pos.y + pushDist * dir.y;
+
+ // put this one at the bottom of the fall
+ m_path[i].pos.z = to->area->GetZ( m_path[i].pos );
+ }
+ }
+ }
+ else if (to->how == GO_LADDER_UP) // to get to next area, must go up a ladder
+ {
+ // find our ladder
+ const NavLadderConnectList *list = from->area->GetLadderList( CSNavLadder::LADDER_UP );
+ int it;
+ for( it = list->Head(); it != list->InvalidIndex(); it = list->Next(it))
+ {
+ CSNavLadder *ladder = (*list)[ it ].ladder;
+
+ // can't use "behind" area when ascending...
+ if (ladder->m_topForwardArea == to->area ||
+ ladder->m_topLeftArea == to->area ||
+ ladder->m_topRightArea == to->area)
+ {
+ to->ladder = ladder;
+ to->pos = ladder->m_bottom + ladder->GetNormal() * 2.0f * HalfHumanWidth;
+ break;
+ }
+ }
+
+ if (it == list->InvalidIndex())
+ {
+ //PrintIfWatched( "ERROR: Can't find ladder in path\n" );
+ return false;
+ }
+ }
+ else if (to->how == GO_LADDER_DOWN) // to get to next area, must go down a ladder
+ {
+ // find our ladder
+ const NavLadderConnectList *list = from->area->GetLadderList( CSNavLadder::LADDER_DOWN );
+ int it;
+ for( it = list->Head(); it != list->InvalidIndex(); it = list->Next(it))
+ {
+ CSNavLadder *ladder = (*list)[ it ].ladder;
+
+ if (ladder->m_bottomArea == to->area)
+ {
+ to->ladder = ladder;
+ to->pos = ladder->m_top;
+ to->pos = ladder->m_top - ladder->GetNormal() * 2.0f * HalfHumanWidth;
+ break;
+ }
+ }
+
+ if (it == list->InvalidIndex())
+ {
+ //PrintIfWatched( "ERROR: Can't find ladder in path\n" );
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return true if position is at the end of the path
+ */
+bool CNavPath::IsAtEnd( const Vector &pos ) const
+{
+ if (!IsValid())
+ return false;
+
+ const float epsilon = 20.0f;
+ return (pos - GetEndpoint()).IsLengthLessThan( epsilon );
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return length of path from start to finish
+ */
+float CNavPath::GetLength( void ) const
+{
+ float length = 0.0f;
+ for( int i=1; i<GetSegmentCount(); ++i )
+ {
+ length += (m_path[i].pos - m_path[i-1].pos).Length();
+ }
+
+ return length;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return point a given distance along the path - if distance is out of path bounds, point is clamped to start/end
+ * @todo Be careful of returning "positions" along one-way drops, ladders, etc.
+ */
+bool CNavPath::GetPointAlongPath( float distAlong, Vector *pointOnPath ) const
+{
+ if (!IsValid() || pointOnPath == NULL)
+ return false;
+
+ if (distAlong <= 0.0f)
+ {
+ *pointOnPath = m_path[0].pos;
+ return true;
+ }
+
+ float lengthSoFar = 0.0f;
+ float segmentLength;
+ Vector dir;
+ for( int i=1; i<GetSegmentCount(); ++i )
+ {
+ dir = m_path[i].pos - m_path[i-1].pos;
+ segmentLength = dir.Length();
+
+ if (segmentLength + lengthSoFar >= distAlong)
+ {
+ // desired point is on this segment of the path
+ float delta = distAlong - lengthSoFar;
+ float t = delta / segmentLength;
+
+ *pointOnPath = m_path[i].pos + t * dir;
+
+ return true;
+ }
+
+ lengthSoFar += segmentLength;
+ }
+
+ *pointOnPath = m_path[ GetSegmentCount()-1 ].pos;
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return the node index closest to the given distance along the path without going over - returns (-1) if error
+ */
+int CNavPath::GetSegmentIndexAlongPath( float distAlong ) const
+{
+ if (!IsValid())
+ return -1;
+
+ if (distAlong <= 0.0f)
+ {
+ return 0;
+ }
+
+ float lengthSoFar = 0.0f;
+ Vector dir;
+ for( int i=1; i<GetSegmentCount(); ++i )
+ {
+ lengthSoFar += (m_path[i].pos - m_path[i-1].pos).Length();
+
+ if (lengthSoFar > distAlong)
+ {
+ return i-1;
+ }
+ }
+
+ return GetSegmentCount()-1;
+}
+
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Compute closest point on path to given point
+ * NOTE: This does not do line-of-sight tests, so closest point may be thru the floor, etc
+ */
+bool CNavPath::FindClosestPointOnPath( const Vector *worldPos, int startIndex, int endIndex, Vector *close ) const
+{
+ if (!IsValid() || close == NULL)
+ return false;
+
+ Vector along, toWorldPos;
+ Vector pos;
+ const Vector *from, *to;
+ float length;
+ float closeLength;
+ float closeDistSq = 9999999999.9;
+ float distSq;
+
+ for( int i=startIndex; i<=endIndex; ++i )
+ {
+ from = &m_path[i-1].pos;
+ to = &m_path[i].pos;
+
+ // compute ray along this path segment
+ along = *to - *from;
+
+ // make it a unit vector along the path
+ length = along.NormalizeInPlace();
+
+ // compute vector from start of segment to our point
+ toWorldPos = *worldPos - *from;
+
+ // find distance of closest point on ray
+ closeLength = DotProduct( toWorldPos, along );
+
+ // constrain point to be on path segment
+ if (closeLength <= 0.0f)
+ pos = *from;
+ else if (closeLength >= length)
+ pos = *to;
+ else
+ pos = *from + closeLength * along;
+
+ distSq = (pos - *worldPos).LengthSqr();
+
+ // keep the closest point so far
+ if (distSq < closeDistSq)
+ {
+ closeDistSq = distSq;
+ *close = pos;
+ }
+ }
+
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Build trivial path when start and goal are in the same nav area
+ */
+bool CNavPath::BuildTrivialPath( const Vector &start, const Vector &goal )
+{
+ m_segmentCount = 0;
+
+ CNavArea *startArea = TheNavMesh->GetNearestNavArea( start );
+ if (startArea == NULL)
+ return false;
+
+ CNavArea *goalArea = TheNavMesh->GetNearestNavArea( goal );
+ if (goalArea == NULL)
+ return false;
+
+ m_segmentCount = 2;
+
+ m_path[0].area = startArea;
+ m_path[0].pos.x = start.x;
+ m_path[0].pos.y = start.y;
+ m_path[0].pos.z = startArea->GetZ( start );
+ m_path[0].ladder = NULL;
+ m_path[0].how = NUM_TRAVERSE_TYPES;
+
+ m_path[1].area = goalArea;
+ m_path[1].pos.x = goal.x;
+ m_path[1].pos.y = goal.y;
+ m_path[1].pos.z = goalArea->GetZ( goal );
+ m_path[1].ladder = NULL;
+ m_path[1].how = NUM_TRAVERSE_TYPES;
+
+ return true;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Draw the path for debugging.
+ */
+void CNavPath::Draw( const Vector &color )
+{
+ if (!IsValid())
+ return;
+
+ for( int i=1; i<m_segmentCount; ++i )
+ {
+ DrawLine( m_path[i-1].pos + Vector( 0, 0, HalfHumanHeight ),
+ m_path[i].pos + Vector( 0, 0, HalfHumanHeight ), 2, 255 * color.x, 255 * color.y, 255 * color.z );
+ }
+}
+
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Check line of sight from 'anchor' node on path to subsequent nodes until
+ * we find a node that can't been seen from 'anchor'.
+ */
+int CNavPath::FindNextOccludedNode( int anchor )
+{
+ for( int i=anchor+1; i<m_segmentCount; ++i )
+ {
+ // don't remove ladder nodes
+ if (m_path[i].ladder)
+ return i;
+
+ if (!IsWalkableTraceLineClear( m_path[ anchor ].pos, m_path[ i ].pos ))
+ {
+ // cant see this node from anchor node
+ return i;
+ }
+
+ Vector anchorPlusHalf = m_path[ anchor ].pos + Vector( 0, 0, HalfHumanHeight );
+ Vector iPlusHalf = m_path[ i ].pos +Vector( 0, 0, HalfHumanHeight );
+ if (!IsWalkableTraceLineClear( anchorPlusHalf, iPlusHalf) )
+ {
+ // cant see this node from anchor node
+ return i;
+ }
+
+ Vector anchorPlusFull = m_path[ anchor ].pos + Vector( 0, 0, HumanHeight );
+ Vector iPlusFull = m_path[ i ].pos + Vector( 0, 0, HumanHeight );
+ if (!IsWalkableTraceLineClear( anchorPlusFull, iPlusFull ))
+ {
+ // cant see this node from anchor node
+ return i;
+ }
+ }
+
+ return m_segmentCount;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Smooth out path, removing redundant nodes
+ */
+void CNavPath::Optimize( void )
+{
+// DONT USE THIS: Optimizing the path results in cutting thru obstacles
+return;
+
+ if (m_segmentCount < 3)
+ return;
+
+ int anchor = 0;
+
+ while( anchor < m_segmentCount )
+ {
+ int occluded = FindNextOccludedNode( anchor );
+ int nextAnchor = occluded-1;
+
+ if (nextAnchor > anchor)
+ {
+ // remove redundant nodes between anchor and nextAnchor
+ int removeCount = nextAnchor - anchor - 1;
+ if (removeCount > 0)
+ {
+ for( int i=nextAnchor; i<m_segmentCount; ++i )
+ {
+ m_path[i-removeCount] = m_path[i];
+ }
+ m_segmentCount -= removeCount;
+ }
+ }
+
+ ++anchor;
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+//--------------------------------------------------------------------------------------------------------------
+
+/**
+ * Constructor
+ */
+CNavPathFollower::CNavPathFollower( void )
+{
+ m_improv = NULL;
+ m_path = NULL;
+
+ m_segmentIndex = 0;
+ m_isLadderStarted = false;
+
+ m_isDebug = false;
+}
+
+void CNavPathFollower::Reset( void )
+{
+ m_segmentIndex = 1;
+ m_isLadderStarted = false;
+
+ m_stuckMonitor.Reset();
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Move improv along path
+ */
+void CNavPathFollower::Update( float deltaT, bool avoidObstacles )
+{
+ if (m_path == NULL || m_path->IsValid() == false)
+ return;
+
+ const CNavPath::PathSegment *node = (*m_path)[ m_segmentIndex ];
+
+ if (node == NULL)
+ {
+ m_improv->OnMoveToFailure( m_path->GetEndpoint(), CImprovLocomotor::FAIL_INVALID_PATH );
+ m_path->Invalidate();
+ return;
+ }
+
+ // handle ladders
+ /*
+ if (node->ladder)
+ {
+ const Vector *approachPos = NULL;
+ const Vector *departPos = NULL;
+
+ if (m_segmentIndex)
+ approachPos = &(*m_path)[ m_segmentIndex-1 ]->pos;
+
+ if (m_segmentIndex < m_path->GetSegmentCount()-1)
+ departPos = &(*m_path)[ m_segmentIndex+1 ]->pos;
+
+ if (!m_isLadderStarted)
+ {
+ // set up ladder movement
+ m_improv->StartLadder( node->ladder, node->how, approachPos, departPos );
+ m_isLadderStarted = true;
+ }
+
+ // move improv along ladder
+ if (m_improv->TraverseLadder( node->ladder, node->how, approachPos, departPos, deltaT ))
+ {
+ // completed ladder
+ ++m_segmentIndex;
+ }
+ return;
+ }
+ */
+
+ // reset ladder init flag
+ m_isLadderStarted = false;
+
+ //
+ // Check if we reached the end of the path
+ //
+ const float closeRange = 20.0f;
+ if ((m_improv->GetFeet() - node->pos).IsLengthLessThan( closeRange ))
+ {
+ ++m_segmentIndex;
+
+ if (m_segmentIndex >= m_path->GetSegmentCount())
+ {
+ m_improv->OnMoveToSuccess( m_path->GetEndpoint() );
+ m_path->Invalidate();
+ return;
+ }
+ }
+
+
+ m_goal = node->pos;
+
+ const float aheadRange = 300.0f;
+ m_segmentIndex = FindPathPoint( aheadRange, &m_goal, &m_behindIndex );
+ if (m_segmentIndex >= m_path->GetSegmentCount())
+ m_segmentIndex = m_path->GetSegmentCount()-1;
+
+
+ bool isApproachingJumpArea = false;
+
+ //
+ // Crouching
+ //
+ if (!m_improv->IsUsingLadder())
+ {
+ // because hostage crouching is not really supported by the engine,
+ // if we are standing in a crouch area, we must crouch to avoid collisions
+ if (m_improv->GetLastKnownArea() &&
+ m_improv->GetLastKnownArea()->GetAttributes() & NAV_MESH_CROUCH &&
+ !(m_improv->GetLastKnownArea()->GetAttributes() & NAV_MESH_JUMP))
+ {
+ m_improv->Crouch();
+ }
+
+ // if we are approaching a crouch area, crouch
+ // if there are no crouch areas coming up, stand
+ const float crouchRange = 50.0f;
+ bool didCrouch = false;
+ for( int i=m_segmentIndex; i<m_path->GetSegmentCount(); ++i )
+ {
+ const CNavArea *to = (*m_path)[i]->area;
+
+ // if there is a jump area on the way to the crouch area, don't crouch as it messes up the jump
+ if (to->GetAttributes() & NAV_MESH_JUMP)
+ {
+ isApproachingJumpArea = true;
+ break;
+ }
+
+ Vector close;
+ to->GetClosestPointOnArea( m_improv->GetCentroid(), &close );
+
+ if ((close - m_improv->GetFeet()).AsVector2D().IsLengthGreaterThan( crouchRange ))
+ break;
+
+ if (to->GetAttributes() & NAV_MESH_CROUCH)
+ {
+ m_improv->Crouch();
+ didCrouch = true;
+ break;
+ }
+
+ }
+
+ if (!didCrouch && !m_improv->IsJumping())
+ {
+ // no crouch areas coming up
+ m_improv->StandUp();
+ }
+
+ } // end crouching logic
+
+
+ if (m_isDebug)
+ {
+ m_path->Draw();
+ UTIL_DrawBeamPoints( m_improv->GetCentroid(), m_goal + Vector( 0, 0, StepHeight ), 1, 255, 0, 255 );
+ UTIL_DrawBeamPoints( m_goal + Vector( 0, 0, StepHeight ), m_improv->GetCentroid(), 1, 255, 0, 255 );
+ }
+
+ // check if improv becomes stuck
+ m_stuckMonitor.Update( m_improv );
+
+
+ // if improv has been stuck for too long, give up
+ const float giveUpTime = 2.0f;
+ if (m_stuckMonitor.GetDuration() > giveUpTime)
+ {
+ m_improv->OnMoveToFailure( m_path->GetEndpoint(), CImprovLocomotor::FAIL_STUCK );
+ m_path->Invalidate();
+ return;
+ }
+
+
+ // if our goal is high above us, we must have fallen
+ if (m_goal.z - m_improv->GetFeet().z > JumpCrouchHeight)
+ {
+ const float closeRange = 75.0f;
+ Vector2D to( m_improv->GetFeet().x - m_goal.x, m_improv->GetFeet().y - m_goal.y );
+ if (to.IsLengthLessThan( closeRange ))
+ {
+ // we can't reach the goal position
+ // check if we can reach the next node, in case this was a "jump down" situation
+ const CNavPath::PathSegment *nextNode = (*m_path)[ m_behindIndex+1 ];
+ if (m_behindIndex >=0 && nextNode)
+ {
+ if (nextNode->pos.z - m_improv->GetFeet().z > JumpCrouchHeight)
+ {
+ // the next node is too high, too - we really did fall of the path
+ m_improv->OnMoveToFailure( m_path->GetEndpoint(), CImprovLocomotor::FAIL_FELL_OFF );
+ m_path->Invalidate();
+ return;
+ }
+ }
+ else
+ {
+ // fell trying to get to the last node in the path
+ m_improv->OnMoveToFailure( m_path->GetEndpoint(), CImprovLocomotor::FAIL_FELL_OFF );
+ m_path->Invalidate();
+ return;
+ }
+ }
+ }
+
+
+ // avoid small obstacles
+ if (avoidObstacles && !isApproachingJumpArea && !m_improv->IsJumping() && m_segmentIndex < m_path->GetSegmentCount()-1)
+ {
+ FeelerReflexAdjustment( &m_goal );
+
+ // currently, this is only used for hostages, and their collision physics stinks
+ // do more feeler checks to avoid short obstacles
+ /*
+ const float inc = 0.25f;
+ for( float t = 0.5f; t < 1.0f; t += inc )
+ {
+ FeelerReflexAdjustment( &m_goal, t * StepHeight );
+ }
+ */
+
+ }
+
+ // move improv along path
+ m_improv->TrackPath( m_goal, deltaT );
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Return the closest point to our current position on our current path
+ * If "local" is true, only check the portion of the path surrounding m_pathIndex.
+ */
+int CNavPathFollower::FindOurPositionOnPath( Vector *close, bool local ) const
+{
+ if (!m_path->IsValid())
+ return -1;
+
+ Vector along, toFeet;
+ Vector feet = m_improv->GetFeet();
+ Vector eyes = m_improv->GetEyes();
+ Vector pos;
+ const Vector *from, *to;
+ float length;
+ float closeLength;
+ float closeDistSq = 9999999999.9;
+ int closeIndex = -1;
+ float distSq;
+
+ int start, end;
+
+ if (local)
+ {
+ start = m_segmentIndex - 3;
+ if (start < 1)
+ start = 1;
+
+ end = m_segmentIndex + 3;
+ if (end > m_path->GetSegmentCount())
+ end = m_path->GetSegmentCount();
+ }
+ else
+ {
+ start = 1;
+ end = m_path->GetSegmentCount();
+ }
+
+ for( int i=start; i<end; ++i )
+ {
+ from = &(*m_path)[i-1]->pos;
+ to = &(*m_path)[i]->pos;
+
+ // compute ray along this path segment
+ along = *to - *from;
+
+ // make it a unit vector along the path
+ length = along.NormalizeInPlace();
+
+ // compute vector from start of segment to our point
+ toFeet = feet - *from;
+
+ // find distance of closest point on ray
+ closeLength = DotProduct( toFeet, along );
+
+ // constrain point to be on path segment
+ if (closeLength <= 0.0f)
+ pos = *from;
+ else if (closeLength >= length)
+ pos = *to;
+ else
+ pos = *from + closeLength * along;
+
+ distSq = (pos - feet).LengthSqr();
+
+ // keep the closest point so far
+ if (distSq < closeDistSq)
+ {
+ // don't use points we cant see
+ Vector probe = pos + Vector( 0, 0, HalfHumanHeight );
+ if (!IsWalkableTraceLineClear( eyes, probe, WALK_THRU_DOORS | WALK_THRU_BREAKABLES ))
+ continue;
+
+ // don't use points we cant reach
+ //if (!IsStraightLinePathWalkable( &pos ))
+ // continue;
+
+ closeDistSq = distSq;
+ if (close)
+ *close = pos;
+ closeIndex = i-1;
+ }
+ }
+
+ return closeIndex;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Compute a point a fixed distance ahead along our path.
+ * Returns path index just after point.
+ */
+int CNavPathFollower::FindPathPoint( float aheadRange, Vector *point, int *prevIndex )
+{
+ // find path index just past aheadRange
+ int afterIndex;
+
+ // finds the closest point on local area of path, and returns the path index just prior to it
+ Vector close;
+ int startIndex = FindOurPositionOnPath( &close, true );
+
+ if (prevIndex)
+ *prevIndex = startIndex;
+
+ if (startIndex <= 0)
+ {
+ // went off the end of the path
+ // or next point in path is unwalkable (ie: jump-down)
+ // keep same point
+ return m_segmentIndex;
+ }
+
+ // if we are crouching, just follow the path exactly
+ if (m_improv->IsCrouching())
+ {
+ // we want to move to the immediately next point along the path from where we are now
+ int index = startIndex+1;
+ if (index >= m_path->GetSegmentCount())
+ index = m_path->GetSegmentCount()-1;
+
+ *point = (*m_path)[ index ]->pos;
+
+ // if we are very close to the next point in the path, skip ahead to the next one to avoid wiggling
+ // we must do a 2D check here, in case the goal point is floating in space due to jump down, etc
+ const float closeEpsilon = 20.0f; // 10
+ while ((*point - close).AsVector2D().IsLengthLessThan( closeEpsilon ))
+ {
+ ++index;
+
+ if (index >= m_path->GetSegmentCount())
+ {
+ index = m_path->GetSegmentCount()-1;
+ break;
+ }
+
+ *point = (*m_path)[ index ]->pos;
+ }
+
+ return index;
+ }
+
+ // make sure we use a node a minimum distance ahead of us, to avoid wiggling
+ while (startIndex < m_path->GetSegmentCount()-1)
+ {
+ Vector pos = (*m_path)[ startIndex+1 ]->pos;
+
+ // we must do a 2D check here, in case the goal point is floating in space due to jump down, etc
+ const float closeEpsilon = 20.0f;
+ if ((pos - close).AsVector2D().IsLengthLessThan( closeEpsilon ))
+ {
+ ++startIndex;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ // if we hit a ladder or jump area, must stop (dont use ladder behind us)
+ if (startIndex > m_segmentIndex && startIndex < m_path->GetSegmentCount() &&
+ ((*m_path)[ startIndex ]->ladder || (*m_path)[ startIndex ]->area->GetAttributes() & NAV_MESH_JUMP))
+ {
+ *point = (*m_path)[ startIndex ]->pos;
+ return startIndex;
+ }
+
+ // we need the point just *ahead* of us
+ ++startIndex;
+ if (startIndex >= m_path->GetSegmentCount())
+ startIndex = m_path->GetSegmentCount()-1;
+
+ // if we hit a ladder or jump area, must stop
+ if (startIndex < m_path->GetSegmentCount() &&
+ ((*m_path)[ startIndex ]->ladder || (*m_path)[ startIndex ]->area->GetAttributes() & NAV_MESH_JUMP))
+ {
+ *point = (*m_path)[ startIndex ]->pos;
+ return startIndex;
+ }
+
+ // note direction of path segment we are standing on
+ Vector initDir = (*m_path)[ startIndex ]->pos - (*m_path)[ startIndex-1 ]->pos;
+ initDir.NormalizeInPlace();
+
+ Vector feet = m_improv->GetFeet();
+ Vector eyes = m_improv->GetEyes();
+ float rangeSoFar = 0;
+
+ // this flag is true if our ahead point is visible
+ bool visible = true;
+
+ Vector prevDir = initDir;
+
+ // step along the path until we pass aheadRange
+ bool isCorner = false;
+ int i;
+ for( i=startIndex; i<m_path->GetSegmentCount(); ++i )
+ {
+ Vector pos = (*m_path)[i]->pos;
+ Vector to = pos - (*m_path)[i-1]->pos;
+ Vector dir = to;
+ dir.NormalizeInPlace();
+
+ // don't allow path to double-back from our starting direction (going upstairs, down curved passages, etc)
+ if (DotProduct( dir, initDir ) < 0.0f) // -0.25f
+ {
+ --i;
+ break;
+ }
+
+ // if the path turns a corner, we want to move towards the corner, not into the wall/stairs/etc
+ if (DotProduct( dir, prevDir ) < 0.5f)
+ {
+ isCorner = true;
+ --i;
+ break;
+ }
+ prevDir = dir;
+
+ // don't use points we cant see
+ Vector probe = pos + Vector( 0, 0, HalfHumanHeight );
+ if (!IsWalkableTraceLineClear( eyes, probe, WALK_THRU_BREAKABLES ))
+ {
+ // presumably, the previous point is visible, so we will interpolate
+ visible = false;
+ break;
+ }
+
+ // if we encounter a ladder or jump area, we must stop
+ if (i < m_path->GetSegmentCount() &&
+ ((*m_path)[ i ]->ladder || (*m_path)[ i ]->area->GetAttributes() & NAV_MESH_JUMP))
+ break;
+
+ // Check straight-line path from our current position to this position
+ // Test for un-jumpable height change, or unrecoverable fall
+ //if (!IsStraightLinePathWalkable( &pos ))
+ //{
+ // --i;
+ // break;
+ //}
+
+ Vector along = (i == startIndex) ? (pos - feet) : (pos - (*m_path)[i-1]->pos);
+ rangeSoFar += along.Length2D();
+
+ // stop if we have gone farther than aheadRange
+ if (rangeSoFar >= aheadRange)
+ break;
+ }
+
+ if (i < startIndex)
+ afterIndex = startIndex;
+ else if (i < m_path->GetSegmentCount())
+ afterIndex = i;
+ else
+ afterIndex = m_path->GetSegmentCount()-1;
+
+
+ // compute point on the path at aheadRange
+ if (afterIndex == 0)
+ {
+ *point = (*m_path)[0]->pos;
+ }
+ else
+ {
+ // interpolate point along path segment
+ const Vector *afterPoint = &(*m_path)[ afterIndex ]->pos;
+ const Vector *beforePoint = &(*m_path)[ afterIndex-1 ]->pos;
+
+ Vector to = *afterPoint - *beforePoint;
+ float length = to.Length2D();
+
+ float t = 1.0f - ((rangeSoFar - aheadRange) / length);
+
+ if (t < 0.0f)
+ t = 0.0f;
+ else if (t > 1.0f)
+ t = 1.0f;
+
+ *point = *beforePoint + t * to;
+
+ // if afterPoint wasn't visible, slide point backwards towards beforePoint until it is
+ if (!visible)
+ {
+ const float sightStepSize = 25.0f;
+ float dt = sightStepSize / length;
+
+ Vector probe = *point + Vector( 0, 0, HalfHumanHeight );
+ while( t > 0.0f && !IsWalkableTraceLineClear( eyes, probe, WALK_THRU_BREAKABLES ) )
+ {
+ t -= dt;
+ *point = *beforePoint + t * to;
+ }
+
+ if (t <= 0.0f)
+ *point = *beforePoint;
+ }
+ }
+
+ // if position found is too close to us, or behind us, force it farther down the path so we don't stop and wiggle
+ if (!isCorner)
+ {
+ const float epsilon = 50.0f;
+ Vector2D toPoint;
+ Vector2D centroid( m_improv->GetCentroid().x, m_improv->GetCentroid().y );
+
+ toPoint.x = point->x - centroid.x;
+ toPoint.y = point->y - centroid.y;
+
+ if (DotProduct2D( toPoint, initDir.AsVector2D() ) < 0.0f || toPoint.IsLengthLessThan( epsilon ))
+ {
+ int i;
+ for( i=startIndex; i<m_path->GetSegmentCount(); ++i )
+ {
+ toPoint.x = (*m_path)[i]->pos.x - centroid.x;
+ toPoint.y = (*m_path)[i]->pos.y - centroid.y;
+ if ((*m_path)[i]->ladder || (*m_path)[i]->area->GetAttributes() & NAV_MESH_JUMP || toPoint.IsLengthGreaterThan( epsilon ))
+ {
+ *point = (*m_path)[i]->pos;
+ startIndex = i;
+ break;
+ }
+ }
+
+ if (i == m_path->GetSegmentCount())
+ {
+ *point = m_path->GetEndpoint();
+ startIndex = m_path->GetSegmentCount()-1;
+ }
+ }
+ }
+
+ // m_pathIndex should always be the next point on the path, even if we're not moving directly towards it
+ if (startIndex < m_path->GetSegmentCount())
+ return startIndex;
+
+ return m_path->GetSegmentCount()-1;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Do reflex avoidance movements if our "feelers" are touched
+ * @todo Parameterize feeler spacing
+ */
+void CNavPathFollower::FeelerReflexAdjustment( Vector *goalPosition, float height )
+{
+ // if we are in a "precise" area, do not do feeler adjustments
+ if (m_improv->GetLastKnownArea() && m_improv->GetLastKnownArea()->GetAttributes() & NAV_MESH_PRECISE)
+ return;
+
+ // use the direction towards the goal
+ Vector dir = *goalPosition - m_improv->GetFeet();
+ dir.z = 0.0f;
+ dir.NormalizeInPlace();
+
+ Vector lat( -dir.y, dir.x, 0.0f );
+
+ const float feelerOffset = (m_improv->IsCrouching()) ? 15.0f : 20.0f; // 15, 20
+ const float feelerLengthRun = 50.0f; // 100 - too long for tight hallways (cs_747)
+ const float feelerLengthWalk = 30.0f;
+
+ const float feelerHeight = (height > 0.0f) ? height : StepHeight + 0.1f; // if obstacle is lower than StepHeight, we'll walk right over it
+
+ float feelerLength = (m_improv->IsRunning()) ? feelerLengthRun : feelerLengthWalk;
+
+ feelerLength = (m_improv->IsCrouching()) ? 20.0f : feelerLength;
+
+ //
+ // Feelers must follow floor slope
+ //
+ float ground;
+ Vector normal;
+ if (m_improv->GetSimpleGroundHeightWithFloor( m_improv->GetEyes(), &ground, &normal ) == false)
+ return;
+
+ // get forward vector along floor
+ dir = CrossProduct( lat, normal );
+
+ // correct the sideways vector
+ lat = CrossProduct( dir, normal );
+
+
+ Vector feet = m_improv->GetFeet();
+ feet.z += feelerHeight;
+
+ Vector from = feet + feelerOffset * lat;
+ Vector to = from + feelerLength * dir;
+
+ bool leftClear = IsWalkableTraceLineClear( from, to, WALK_THRU_DOORS | WALK_THRU_BREAKABLES );
+
+ // draw debug beams
+ if (m_isDebug)
+ {
+ if (leftClear)
+ UTIL_DrawBeamPoints( from, to, 1, 0, 255, 0 );
+ else
+ UTIL_DrawBeamPoints( from, to, 1, 255, 0, 0 );
+ }
+
+ from = feet - feelerOffset * lat;
+ to = from + feelerLength * dir;
+
+ bool rightClear = IsWalkableTraceLineClear( from, to, WALK_THRU_DOORS | WALK_THRU_BREAKABLES );
+
+ // draw debug beams
+ if (m_isDebug)
+ {
+ if (rightClear)
+ UTIL_DrawBeamPoints( from, to, 1, 0, 255, 0 );
+ else
+ UTIL_DrawBeamPoints( from, to, 1, 255, 0, 0 );
+ }
+
+
+
+ const float avoidRange = (m_improv->IsCrouching()) ? 150.0f : 300.0f;
+
+ if (!rightClear)
+ {
+ if (leftClear)
+ {
+ // right hit, left clear - veer left
+ *goalPosition = *goalPosition + avoidRange * lat;
+ //*goalPosition = m_improv->GetFeet() + avoidRange * lat;
+
+ //m_improv->StrafeLeft();
+ }
+ }
+ else if (!leftClear)
+ {
+ // right clear, left hit - veer right
+ *goalPosition = *goalPosition - avoidRange * lat;
+ //*goalPosition = m_improv->GetFeet() - avoidRange * lat;
+
+ //m_improv->StrafeRight();
+ }
+
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Reset the stuck-checker.
+ */
+CStuckMonitor::CStuckMonitor( void )
+{
+ m_isStuck = false;
+ m_avgVelIndex = 0;
+ m_avgVelCount = 0;
+}
+
+/**
+ * Reset the stuck-checker.
+ */
+void CStuckMonitor::Reset( void )
+{
+ m_isStuck = false;
+ m_avgVelIndex = 0;
+ m_avgVelCount = 0;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Test if the improv has become stuck
+ */
+void CStuckMonitor::Update( CImprovLocomotor *improv )
+{
+ if (m_isStuck)
+ {
+ // improv is stuck - see if it has moved far enough to be considered unstuck
+ const float unstuckRange = 75.0f;
+ if ((improv->GetCentroid() - m_stuckSpot).IsLengthGreaterThan( unstuckRange ))
+ {
+ // no longer stuck
+ Reset();
+ //PrintIfWatched( "UN-STUCK\n" );
+ }
+ }
+ else
+ {
+ // check if improv has become stuck
+
+ // compute average velocity over a short period (for stuck check)
+ Vector vel = improv->GetCentroid() - m_lastCentroid;
+
+ // if we are jumping, ignore Z
+ //if (improv->IsJumping())
+ // vel.z = 0.0f;
+
+ // ignore Z unless we are on a ladder (which is only Z)
+ if (!improv->IsUsingLadder())
+ vel.z = 0.0f;
+
+ // cannot be Length2D, or will break ladder movement (they are only Z)
+ float moveDist = vel.Length();
+
+ float deltaT = gpGlobals->curtime - m_lastTime;
+ if (deltaT <= 0.0f)
+ return;
+
+ m_lastTime = gpGlobals->curtime;
+
+ // compute current velocity
+ m_avgVel[ m_avgVelIndex++ ] = moveDist/deltaT;
+
+ if (m_avgVelIndex == MAX_VEL_SAMPLES)
+ m_avgVelIndex = 0;
+
+ if (m_avgVelCount < MAX_VEL_SAMPLES)
+ {
+ ++m_avgVelCount;
+ }
+ else
+ {
+ // we have enough samples to know if we're stuck
+
+ float avgVel = 0.0f;
+ for( int t=0; t<m_avgVelCount; ++t )
+ avgVel += m_avgVel[t];
+
+ avgVel /= m_avgVelCount;
+
+ // cannot make this velocity too high, or actors will get "stuck" when going down ladders
+ float stuckVel = (improv->IsUsingLadder()) ? 10.0f : 20.0f;
+
+ if (avgVel < stuckVel)
+ {
+ // note when and where we initially become stuck
+ m_stuckTimer.Start();
+ m_stuckSpot = improv->GetCentroid();
+ m_isStuck = true;
+ }
+ }
+ }
+
+ // always need to track this
+ m_lastCentroid = improv->GetCentroid();
+}
+
diff --git a/game/shared/cstrike/bot/nav_path.h b/game/shared/cstrike/bot/nav_path.h
new file mode 100644
index 0000000..2542b84
--- /dev/null
+++ b/game/shared/cstrike/bot/nav_path.h
@@ -0,0 +1,246 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// nav_path.h
+// Navigation Path encapsulation
+// Author: Michael S. Booth ([email protected]), November 2003
+
+#ifndef _NAV_PATH_H_
+#define _NAV_PATH_H_
+
+#include "cs_nav_area.h"
+#include "bot_util.h"
+
+class CImprovLocomotor;
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * The CNavPath class encapsulates a path through space
+ */
+class CNavPath
+{
+public:
+ CNavPath( void )
+ {
+ m_segmentCount = 0;
+ }
+
+ struct PathSegment
+ {
+ CNavArea *area; ///< the area along the path
+ NavTraverseType how; ///< how to enter this area from the previous one
+ Vector pos; ///< our movement goal position at this point in the path
+ const CNavLadder *ladder; ///< if "how" refers to a ladder, this is it
+ };
+
+ const PathSegment * operator[] ( int i ) const { return (i >= 0 && i < m_segmentCount) ? &m_path[i] : NULL; }
+ const PathSegment *GetSegment( int i ) const { return (i >= 0 && i < m_segmentCount) ? &m_path[i] : NULL; }
+ int GetSegmentCount( void ) const { return m_segmentCount; }
+ const Vector &GetEndpoint( void ) const { return m_path[ m_segmentCount-1 ].pos; }
+ bool IsAtEnd( const Vector &pos ) const; ///< return true if position is at the end of the path
+
+ float GetLength( void ) const; ///< return length of path from start to finish
+ bool GetPointAlongPath( float distAlong, Vector *pointOnPath ) const; ///< return point a given distance along the path - if distance is out of path bounds, point is clamped to start/end
+
+ /// return the node index closest to the given distance along the path without going over - returns (-1) if error
+ int GetSegmentIndexAlongPath( float distAlong ) const;
+
+ bool IsValid( void ) const { return (m_segmentCount > 0); }
+ void Invalidate( void ) { m_segmentCount = 0; }
+
+ void Draw( const Vector &color = Vector( 1.0f, 0.3f, 0 ) ); ///< draw the path for debugging
+
+ /// compute closest point on path to given point
+ bool FindClosestPointOnPath( const Vector *worldPos, int startIndex, int endIndex, Vector *close ) const;
+
+ void Optimize( void );
+
+ /**
+ * Compute shortest path from 'start' to 'goal' via A* algorithm.
+ * If returns true, path was build to the goal position.
+ * If returns false, path may either be invalid (use IsValid() to check), or valid but
+ * doesn't reach all the way to the goal.
+ */
+ template< typename CostFunctor >
+ bool Compute( const Vector &start, const Vector &goal, CostFunctor &costFunc )
+ {
+ Invalidate();
+
+ CNavArea *startArea = TheNavMesh->GetNearestNavArea( start + Vector( 0.0f, 0.0f, 1.0f ) );
+ if (startArea == NULL)
+ {
+ return false;
+ }
+
+ CNavArea *goalArea = TheNavMesh->GetNavArea( goal );
+
+ // if we are already in the goal area, build trivial path
+ if (startArea == goalArea)
+ {
+ BuildTrivialPath( start, goal );
+ return true;
+ }
+
+ // make sure path end position is on the ground
+ Vector pathEndPosition = goal;
+ if (goalArea)
+ {
+ pathEndPosition.z = goalArea->GetZ( pathEndPosition );
+ }
+ else
+ {
+ TheNavMesh->GetGroundHeight( pathEndPosition, &pathEndPosition.z );
+ }
+
+ //
+ // Compute shortest path to goal
+ //
+ CNavArea *closestArea;
+ bool pathResult = NavAreaBuildPath( startArea, goalArea, &goal, costFunc, &closestArea );
+
+ //
+ // Build path by following parent links
+ //
+
+ // get count
+ int count = 0;
+ CNavArea *area;
+ for( area = closestArea; area; area = area->GetParent() )
+ {
+ ++count;
+ }
+
+ // save room for endpoint
+ if (count > MAX_PATH_SEGMENTS-1)
+ {
+ count = MAX_PATH_SEGMENTS-1;
+ }
+
+ if (count == 0)
+ {
+ return false;
+ }
+
+ if (count == 1)
+ {
+ BuildTrivialPath( start, goal );
+ return true;
+ }
+
+ // build path
+ m_segmentCount = count;
+ for( area = closestArea; count && area; area = area->GetParent() )
+ {
+ --count;
+ m_path[ count ].area = area;
+ m_path[ count ].how = area->GetParentHow();
+ }
+
+ // compute path positions
+ if (ComputePathPositions() == false)
+ {
+ //PrintIfWatched( "CNavPath::Compute: Error building path\n" );
+ Invalidate();
+ return false;
+ }
+
+ // append path end position
+ m_path[ m_segmentCount ].area = closestArea;
+ m_path[ m_segmentCount ].pos = pathEndPosition;
+ m_path[ m_segmentCount ].ladder = NULL;
+ m_path[ m_segmentCount ].how = NUM_TRAVERSE_TYPES;
+ ++m_segmentCount;
+
+ return pathResult;
+ }
+
+private:
+ enum { MAX_PATH_SEGMENTS = 256 };
+ PathSegment m_path[ MAX_PATH_SEGMENTS ];
+ int m_segmentCount;
+
+ bool ComputePathPositions( void ); ///< determine actual path positions
+ bool BuildTrivialPath( const Vector &start, const Vector &goal ); ///< utility function for when start and goal are in the same area
+
+ int FindNextOccludedNode( int anchor ); ///< used by Optimize()
+};
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * Monitor improv movement and determine if it becomes stuck
+ */
+class CStuckMonitor
+{
+public:
+ CStuckMonitor( void );
+
+ void Reset( void );
+ void Update( CImprovLocomotor *improv );
+ bool IsStuck( void ) const { return m_isStuck; }
+
+ float GetDuration( void ) const { return (m_isStuck) ? m_stuckTimer.GetElapsedTime() : 0.0f; }
+
+private:
+ bool m_isStuck; ///< if true, we are stuck
+ Vector m_stuckSpot; ///< the location where we became stuck
+ IntervalTimer m_stuckTimer; ///< how long we have been stuck
+
+ enum { MAX_VEL_SAMPLES = 5 };
+ float m_avgVel[ MAX_VEL_SAMPLES ];
+ int m_avgVelIndex;
+ int m_avgVelCount;
+ Vector m_lastCentroid;
+ float m_lastTime;
+};
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * The CNavPathFollower class implements path following behavior
+ */
+class CNavPathFollower
+{
+public:
+ CNavPathFollower( void );
+
+ void SetImprov( CImprovLocomotor *improv ) { m_improv = improv; }
+ void SetPath( CNavPath *path ) { m_path = path; }
+
+ void Reset( void );
+
+ #define DONT_AVOID_OBSTACLES false
+ void Update( float deltaT, bool avoidObstacles = true ); ///< move improv along path
+ void Debug( bool status ) { m_isDebug = status; } ///< turn debugging on/off
+
+ bool IsStuck( void ) const { return m_stuckMonitor.IsStuck(); } ///< return true if improv is stuck
+ void ResetStuck( void ) { m_stuckMonitor.Reset(); }
+ float GetStuckDuration( void ) const { return m_stuckMonitor.GetDuration(); } ///< return how long we've been stuck
+
+ void FeelerReflexAdjustment( Vector *goalPosition, float height = -1.0f ); ///< adjust goal position if "feelers" are touched
+
+private:
+ CImprovLocomotor *m_improv; ///< who is doing the path following
+
+ CNavPath *m_path; ///< the path being followed
+
+ int m_segmentIndex; ///< the point on the path the improv is moving towards
+ int m_behindIndex; ///< index of the node on the path just behind us
+ Vector m_goal; ///< last computed follow goal
+
+ bool m_isLadderStarted;
+
+ bool m_isDebug;
+
+ int FindOurPositionOnPath( Vector *close, bool local ) const; ///< return the closest point to our current position on current path
+ int FindPathPoint( float aheadRange, Vector *point, int *prevIndex ); ///< compute a point a fixed distance ahead along our path.
+
+ CStuckMonitor m_stuckMonitor;
+};
+
+
+
+#endif // _NAV_PATH_H_
+
diff --git a/game/shared/cstrike/bot/shared_util.cpp b/game/shared/cstrike/bot/shared_util.cpp
new file mode 100644
index 0000000..4d66845
--- /dev/null
+++ b/game/shared/cstrike/bot/shared_util.cpp
@@ -0,0 +1,207 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: dll-agnostic routines (no dll dependencies here)
+//
+// $NoKeywords: $
+//=============================================================================//
+
+// Author: Matthew D. Campbell ([email protected]), 2003
+
+#include "cbase.h"
+
+#include <ctype.h>
+#include "shared_util.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+static char s_shared_token[ 1500 ];
+static char s_shared_quote = '\"';
+
+//--------------------------------------------------------------------------------------------------------------
+char * SharedVarArgs(const char *format, ...)
+{
+ va_list argptr;
+ const int BufLen = 1024;
+ const int NumBuffers = 4;
+ static char string[NumBuffers][BufLen];
+ static int curstring = 0;
+
+ curstring = ( curstring + 1 ) % NumBuffers;
+
+ va_start (argptr, format);
+ V_vsprintf_safe( string[curstring], format, argptr );
+ va_end (argptr);
+
+ return string[curstring];
+}
+
+//--------------------------------------------------------------------------------------------------------------
+char * BufPrintf(char *buf, int& len, const char *fmt, ...)
+{
+ if (len <= 0)
+ return NULL;
+
+ va_list argptr;
+
+ va_start(argptr, fmt);
+ _vsnprintf(buf, len, fmt, argptr);
+ buf[ len - 1 ] = 0;
+ va_end(argptr);
+
+ len -= strlen(buf);
+ return buf + strlen(buf);
+}
+
+//--------------------------------------------------------------------------------------------------------------
+wchar_t * BufWPrintf(wchar_t *buf, int& len, const wchar_t *fmt, ...)
+{
+ if (len <= 0)
+ return NULL;
+
+ va_list argptr;
+
+ va_start(argptr, fmt);
+#ifdef WIN32
+ _vsnwprintf(buf, len, fmt, argptr);
+#else
+ vswprintf( buf, len, fmt, argptr );
+#endif
+ buf[ len - 1 ] = 0;
+ va_end(argptr);
+
+ len -= wcslen(buf);
+ return buf + wcslen(buf);
+}
+
+//--------------------------------------------------------------------------------------------------------------
+const wchar_t * NumAsWString( int val )
+{
+ const int BufLen = 16;
+ static wchar_t buf[BufLen];
+ int len = BufLen;
+ BufWPrintf( buf, len, L"%d", val );
+ return buf;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+const char * NumAsString( int val )
+{
+ const int BufLen = 16;
+ static char buf[BufLen];
+ int len = BufLen;
+ BufPrintf( buf, len, "%d", val );
+ return buf;
+}
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * Returns the token parsed by SharedParse()
+ */
+char *SharedGetToken( void )
+{
+ return s_shared_token;
+}
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * Returns the token parsed by SharedParse()
+ */
+void SharedSetQuoteChar( char c )
+{
+ s_shared_quote = c;
+}
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * Parse a token out of a string
+ */
+const char *SharedParse( const char *data )
+{
+ int c;
+ int len;
+
+ len = 0;
+ s_shared_token[0] = 0;
+
+ if (!data)
+ return NULL;
+
+// skip whitespace
+skipwhite:
+ while ( (c = *data) <= ' ')
+ {
+ if (c == 0)
+ return NULL; // end of file;
+ data++;
+ }
+
+// skip // comments
+ if (c=='/' && data[1] == '/')
+ {
+ while (*data && *data != '\n')
+ data++;
+ goto skipwhite;
+ }
+
+
+// handle quoted strings specially
+ if (c == s_shared_quote)
+ {
+ data++;
+ while (1)
+ {
+ c = *data++;
+ if (c==s_shared_quote || !c)
+ {
+ s_shared_token[len] = 0;
+ return data;
+ }
+ s_shared_token[len] = c;
+ len++;
+ }
+ }
+
+// parse single characters
+ if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c == ',' )
+ {
+ s_shared_token[len] = c;
+ len++;
+ s_shared_token[len] = 0;
+ return data+1;
+ }
+
+// parse a regular word
+ do
+ {
+ s_shared_token[len] = c;
+ data++;
+ len++;
+ c = *data;
+ if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c == ',' )
+ break;
+ } while (c>32);
+
+ s_shared_token[len] = 0;
+ return data;
+}
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * Returns true if additional data is waiting to be processed on this line
+ */
+bool SharedTokenWaiting( const char *buffer )
+{
+ const char *p;
+
+ p = buffer;
+ while ( *p && *p!='\n')
+ {
+ if ( !isspace( *p ) || isalnum( *p ) )
+ return true;
+
+ p++;
+ }
+
+ return false;
+}
diff --git a/game/shared/cstrike/bot/shared_util.h b/game/shared/cstrike/bot/shared_util.h
new file mode 100644
index 0000000..664dcd7
--- /dev/null
+++ b/game/shared/cstrike/bot/shared_util.h
@@ -0,0 +1,83 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: dll-agnostic routines (no dll dependencies here)
+//
+// $NoKeywords: $
+//=============================================================================//
+
+// Author: Matthew D. Campbell ([email protected]), 2003
+
+#ifndef SHARED_UTIL_H
+#define SHARED_UTIL_H
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * Returns the token parsed by SharedParse()
+ */
+char *SharedGetToken( void );
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * Sets the character used to delimit quoted strings. Default is '\"'. Be sure to set it back when done.
+ */
+void SharedSetQuoteChar( char c );
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * Parse a token out of a string
+ */
+const char *SharedParse( const char *data );
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * Returns true if additional data is waiting to be processed on this line
+ */
+bool SharedTokenWaiting( const char *buffer );
+
+//--------------------------------------------------------------------------------------------------------
+/**
+ * Simple utility function to allocate memory and duplicate a string
+ */
+inline char *CloneString( const char *str )
+{
+ char *cloneStr = new char [ strlen(str)+1 ];
+ strcpy( cloneStr, str );
+ return cloneStr;
+}
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * snprintf-alike that allows multiple prints into a buffer
+ */
+char * BufPrintf(char *buf, int& len, PRINTF_FORMAT_STRING const char *fmt, ...);
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * wide char version of BufPrintf
+ */
+wchar_t * BufWPrintf(wchar_t *buf, int& len, PRINTF_FORMAT_STRING const wchar_t *fmt, ...);
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * convenience function that prints an int into a static wchar_t*
+ */
+const wchar_t * NumAsWString( int val );
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * convenience function that prints an int into a static char*
+ */
+const char * NumAsString( int val );
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * convenience function that composes a string into a static char*
+ */
+char * SharedVarArgs(PRINTF_FORMAT_STRING const char *format, ...);
+
+#include "tier0/memdbgoff.h"
+
+#endif // SHARED_UTIL_H
diff --git a/game/shared/cstrike/cs_achievement_constants.h b/game/shared/cstrike/cs_achievement_constants.h
new file mode 100644
index 0000000..35d21a3
--- /dev/null
+++ b/game/shared/cstrike/cs_achievement_constants.h
@@ -0,0 +1,54 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//-------------------------------------------------------------
+// File: cs_achievement_constants.h
+// Desc: Declare contants used by achievements (mostly) in one location for simpler tweaking
+// Author: Peter Freese <[email protected]>
+// Date: 2009/03/11
+// Copyright: � 2009 Hidden Path Entertainment
+//-------------------------------------------------------------
+
+#ifndef CS_ACHIEVEMENT_CONSTANTS_H
+#define CS_ACHIEVEMENT_CONSTANTS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+namespace AchievementConsts
+{
+ const int DefaultMinOpponentsForAchievement = 5;
+ const int KillingSpree_Kills = 5;
+ const float KillingSpree_WindowTime = 15.0f;
+ const float KillingSpreeEnder_TimeWindow = 5.0f;
+ const int KillEnemyTeam_MinKills = 5;
+ const int LastPlayerAlive_MinPlayersOnTeam = 5;
+ const int KillsWithMultipleGuns_MinWeapons = 5;
+ const float BombDefuseCloseCall_MaxTimeRemaining = 1.0f;
+ const int KillLowDamage_MaxHealthLeft = 5;
+ const int DamageNoKill_MaxHealthLeftOnKill = 5;
+ const float BombDefuseNeededKit_MaxTime = 5.0f;
+ const float FastBombPlant_Time = 25.0f;
+ const int KillEnemiesWhileBlind_Kills = 1;
+ const int KillEnemiesWhileBlindHard_Kills = 2;
+ const int SurviveGrenade_MinDamage = 80;
+ const int KillWhenAtLowHealth_MaxHealth = 1;
+ const int GrenadeMultiKill_MinKills = 3;
+ const int BombMultiKill_MinKills = 5;
+ const float FastRoundWin_Time = 30.0f;
+ const int UnstoppableForce_Kills = 10;
+ const int BreakPropsInRound_Props = 15;
+ const int HeadshotsInRound_Kills = 5;
+ const int BreakWindowsInOfficeRound_Windows = 14;
+ const float FastHostageRescue_Time = 90.0f;
+ const int SurviveManyAttacks_NumberDamagingPlayers = 5;
+ const float KillInAir_MinimumHeight = 100.0f; //100-120 is probably best. Also used for killing while in the air
+ const float KillBombPickup_MaxTime = 3.0f;
+ const int WinRoundsWithoutBuying_Rounds = 10;
+ const int ConcurrentDominations_MinDominations = 3;
+ const int ExtendedDomination_AdditionalKills = 4;
+ const int SameUniform_MinPlayers = 5;
+ const int FriendsSameUniform_MinPlayers = 4;
+ const float KillEnemyNearBomb_MaxDistance = 480.0f;
+ const int GrenadeDamage_MinDamage = 200;
+}
+
+#endif // CS_ACHIEVEMENT_CONSTANTS_H
diff --git a/game/shared/cstrike/cs_achievementdefs.h b/game/shared/cstrike/cs_achievementdefs.h
new file mode 100644
index 0000000..77615de
--- /dev/null
+++ b/game/shared/cstrike/cs_achievementdefs.h
@@ -0,0 +1,210 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Shared CS definitions.
+//
+//=============================================================================//
+
+#ifndef CS_ACHIEVEMENTDEFS_H
+#define CS_ACHIEVEMENTDEFS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+
+//=============================================================================
+// Achievement ID Definitions
+//=============================================================================
+
+
+typedef enum
+{
+ CSInvalidAchievement = -1,
+
+ // Bomb-related Achievements
+ CSBombAchievementsStart = 1000, // First bomb-related achievement
+
+ CSWinBombPlant,
+ CSWinBombDefuse,
+ CSDefuseAndNeededKit,
+ CSBombDefuseCloseCall,
+ CSKilledDefuser,
+ CSPlantBombWithin25Seconds,
+ CSKillBombPickup,
+ CSBombMultikill,
+ CSGooseChase,
+ CSWinBombPlantAfterRecovery,
+ CSDefuseDefense,
+ CSPlantBombsLow,
+ CSDefuseBombsLow,
+
+ CSBombAchievementsEnd, // Must be after last bomb-related achievement
+
+
+ // Hostage-related Achievements
+ CSHostageAchievementsStart = 2000, // First hostage-related achievement
+
+ CSRescueAllHostagesInARound,
+ CSKilledRescuer,
+ CSFastHostageRescue,
+ CSRescueHostagesLow,
+ CSRescueHostagesMid,
+
+ CSHostageAchievmentEnd, // Must be after last hostage-related achievement
+
+ // General Kill Achievements
+ CSKillAchievementsStart = 3000, // First kill-related achievement
+
+ CSEnemyKillsLow,
+ CSEnemyKillsMed,
+ CSEnemyKillsHigh,
+ CSSurvivedHeadshotDueToHelmet,
+ CSKillEnemyReloading,
+ CSKillingSpree,
+ CSKillsWithMultipleGuns,
+ CSHeadshots,
+ CSAvengeFriend,
+ CSSurviveGrenade,
+ CSDominationsLow,
+ CSDominationsHigh,
+ CSRevengesLow,
+ CSRevengesHigh,
+ CSDominationOverkillsLow,
+ CSDominationOverkillsHigh,
+ CSDominationOverkillsMatch,
+ CSExtendedDomination,
+ CSConcurrentDominations,
+ CSKillEnemyBlinded,
+ CSKillEnemiesWhileBlind,
+ CSKillEnemiesWhileBlindHard,
+ CSKillsEnemyWeapon,
+ CSKillWithEveryWeapon,
+ CSWinKnifeFightsLow,
+ CSWinKnifeFightsHigh,
+ CSKilledDefuserWithGrenade,
+ CSKillSniperWithSniper,
+ CSKillSniperWithKnife,
+ CSHipShot,
+ CSKillSnipers,
+ CSKillWhenAtLowHealth,
+ CSPistolRoundKnifeKill,
+ CSWinDualDuel,
+ CSGrenadeMultikill,
+ CSKillWhileInAir,
+ CSKillEnemyInAir,
+ CSKillerAndEnemyInAir,
+ CSKillEnemyWithFormerGun,
+ CSKillTwoWithOneShot,
+ CSSnipeTwoFromSameSpot,
+
+ CSKillAchievementEnd, // Must be after last kill-related achievement
+
+ // Weapon-related Achievements
+ CSWeaponAchievementsStart = 4000, // First weapon-related achievement
+
+ CSEnemyKillsDeagle,
+ CSEnemyKillsUSP,
+ CSEnemyKillsGlock,
+ CSEnemyKillsP228,
+ CSEnemyKillsElite,
+ CSEnemyKillsFiveSeven,
+ CSEnemyKillsAWP,
+ CSEnemyKillsAK47,
+ CSEnemyKillsM4A1,
+ CSEnemyKillsAUG,
+ CSEnemyKillsSG552,
+ CSEnemyKillsSG550,
+ CSEnemyKillsGALIL,
+ CSEnemyKillsFAMAS,
+ CSEnemyKillsScout,
+ CSEnemyKillsG3SG1,
+ CSEnemyKillsP90,
+ CSEnemyKillsMP5NAVY,
+ CSEnemyKillsTMP,
+ CSEnemyKillsMAC10,
+ CSEnemyKillsUMP45,
+ CSEnemyKillsM3,
+ CSEnemyKillsXM1014,
+ CSEnemyKillsM249,
+ CSEnemyKillsKnife,
+ CSEnemyKillsHEGrenade,
+ CSMetaPistol,
+ CSMetaRifle,
+ CSMetaSMG,
+ CSMetaShotgun,
+ CSMetaWeaponMaster,
+
+ CSWeaponAchievementsEnd, // Must be after last weapon-related achievement
+
+ // General Achievements
+ CSGeneralAchievementsStart = 5000, // First general achievement
+
+ CSWinRoundsLow,
+ CSWinRoundsMed,
+ CSWinRoundsHigh,
+ CSMoneyEarnedLow,
+ CSMoneyEarnedMed,
+ CSMoneyEarnedHigh,
+ CSGiveDamageLow,
+ CSGiveDamageMed,
+ CSGiveDamageHigh,
+ CSPosthumousGrenadeKill,
+ CSKillEnemyTeam,
+ CSLastPlayerAlive,
+ CSKillEnemyLastBullet,
+ CSKillingSpreeEnder,
+ CSDamageNoKill,
+ CSKillLowDamage,
+ CSSurviveManyAttacks,
+ CSLosslessExtermination,
+ CSFlawlessVictory,
+ CSDecalSprays,
+ CSBreakWindows,
+ CSBreakProps,
+ CSUnstoppableForce,
+ CSImmovableObject,
+ CSHeadshotsInRound,
+ CSWinPistolRoundsLow,
+ CSWinPistolRoundsMed,
+ CSWinPistolRoundsHigh,
+ CSFastRoundWin,
+ CSNightvisionDamage,
+ CSSilentWin,
+ CSBloodlessVictory,
+ CSDonateWeapons,
+ CSWinRoundsWithoutBuying,
+ CSSameUniform,
+ CSFriendsSameUniform,
+ CSCauseFriendlyFireWithFlashbang,
+ CSWinClanMatch,
+ CSCollectHolidayGifts,
+
+ CSGeneralAchievementsEnd, // Must be after last general achievement
+
+ CSWinMapAchievementsStart = 6000,
+
+ CSWinMapCS_ASSAULT,
+ CSWinMapCS_COMPOUND,
+ CSWinMapCS_HAVANA,
+ CSWinMapCS_ITALY,
+ CSWinMapCS_MILITIA,
+ CSWinMapCS_OFFICE,
+ CSWinMapDE_AZTEC,
+ CSWinMapDE_CBBLE,
+ CSWinMapDE_CHATEAU,
+ CSWinMapDE_DUST,
+ CSWinMapDE_DUST2,
+ CSWinMapDE_INFERNO,
+ CSWinMapDE_NUKE,
+ CSWinMapDE_PIRANESI,
+ CSWinMapDE_PORT,
+ CSWinMapDE_PRODIGY,
+ CSWinMapDE_TIDES,
+ CSWinMapDE_TRAIN,
+
+ CSWinMapAchievementsEnd //Must be after last map-based achievement
+
+} eCSAchievementType;
+
+
+#endif // CS_ACHIEVEMENTDEFS_H
diff --git a/game/shared/cstrike/cs_ammodef.cpp b/game/shared/cstrike/cs_ammodef.cpp
new file mode 100644
index 0000000..1b444d1
--- /dev/null
+++ b/game/shared/cstrike/cs_ammodef.cpp
@@ -0,0 +1,57 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "cs_ammodef.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+void CCSAmmoDef::AddAmmoCost( char const* name, int cost, int buySize )
+{
+ int index = Index( name );
+ if ( index < 1 || index >= m_nAmmoIndex )
+ return;
+
+ m_csAmmo[index].buySize = buySize;
+ m_csAmmo[index].cost = cost;
+}
+
+
+//-----------------------------------------------------------------------------
+int CCSAmmoDef::GetBuySize( int index ) const
+{
+ if ( index < 1 || index >= m_nAmmoIndex )
+ return 0;
+
+ return m_csAmmo[index].buySize;
+}
+
+
+//-----------------------------------------------------------------------------
+int CCSAmmoDef::GetCost( int index ) const
+{
+ if ( index < 1 || index >= m_nAmmoIndex )
+ return 0;
+
+ return m_csAmmo[index].cost;
+}
+
+
+//-----------------------------------------------------------------------------
+CCSAmmoDef::CCSAmmoDef(void)
+{
+ memset( m_csAmmo, 0, sizeof( m_csAmmo ) );
+}
+
+
+//-----------------------------------------------------------------------------
+CCSAmmoDef::~CCSAmmoDef( void )
+{
+}
+
+//-----------------------------------------------------------------------------
diff --git a/game/shared/cstrike/cs_ammodef.h b/game/shared/cstrike/cs_ammodef.h
new file mode 100644
index 0000000..0175bcd
--- /dev/null
+++ b/game/shared/cstrike/cs_ammodef.h
@@ -0,0 +1,55 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Holds defintion for game ammo types
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef CS_AMMODEF_H
+#define CS_AMMODEF_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "ammodef.h"
+#include "cs_blackmarket.h"
+
+class ConVar;
+
+struct CSAmmoCost
+{
+ int buySize;
+ int cost;
+};
+
+//=============================================================================
+// >> CCSAmmoDef
+//=============================================================================
+class CCSAmmoDef : public CAmmoDef
+{
+
+public:
+
+ void AddAmmoCost( char const* name, int cost, int buySize );
+
+ CCSAmmoDef(void);
+ ~CCSAmmoDef( void );
+
+ int GetBuySize( int nAmmoIndex ) const;
+ int GetCost( int nAmmoIndex ) const;
+
+private:
+ CSAmmoCost m_csAmmo[MAX_AMMO_TYPES];
+};
+
+
+// Get the global ammodef object. This is usually implemented in each mod's game rules file somewhere,
+// so the mod can setup custom ammo types.
+CCSAmmoDef* GetCSAmmoDef();
+
+
+#endif // CS_AMMODEF_H
+ \ No newline at end of file
diff --git a/game/shared/cstrike/cs_blackmarket.cpp b/game/shared/cstrike/cs_blackmarket.cpp
new file mode 100644
index 0000000..f68e988
--- /dev/null
+++ b/game/shared/cstrike/cs_blackmarket.cpp
@@ -0,0 +1,147 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "cs_gamerules.h"
+#include "cs_blackmarket.h"
+#include "weapon_csbase.h"
+#include "filesystem.h"
+#include <KeyValues.h>
+#include "cs_gamestats.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+extern int g_iRoundCount;
+
+#ifndef CLIENT_DLL
+inline void CBlackMarketElement::NetworkStateChanged()
+{
+}
+
+
+inline void CBlackMarketElement::NetworkStateChanged( void *pVar )
+{
+}
+
+blackmarket_items_t blackmarket_items[] =
+{
+ { "kevlar", KEVLAR_PRICE },
+ { "assaultsuit", ASSAULTSUIT_PRICE },
+ { "nightvision", NVG_PRICE },
+};
+
+
+CUtlVector<CBlackMarketElement> g_BlackMarket_WeaponsBought;
+
+void TrackAutoBuyPurchases( const char *pWeaponName, CCSPlayer *pBuyer )
+{
+ if ( pBuyer->IsInAutoBuy() == false )
+ return;
+
+ if ( pBuyer->IsInAutoBuy() == true )
+ {
+ if ( Q_stristr( pWeaponName, "m4a1" ) )
+ {
+ g_iAutoBuyM4A1Purchases++;
+ }
+ else if ( Q_stristr( pWeaponName, "ak47" ) )
+ {
+ g_iAutoBuyAK47Purchases++;
+ }
+ else if ( Q_stristr( pWeaponName, "famas" ) )
+ {
+ g_iAutoBuyFamasPurchases++;
+ }
+ else if ( Q_stristr( pWeaponName, "galil" ) )
+ {
+ g_iAutoBuyGalilPurchases++;
+ }
+ else if ( Q_stristr( pWeaponName, "assault" ) )
+ {
+ g_iAutoBuyVestHelmPurchases++;
+ }
+ else if ( Q_stristr( pWeaponName, "kevlar" ) )
+ {
+ g_iAutoBuyVestPurchases++;
+ }
+ }
+}
+
+void BlackMarketAddWeapon( const char *pWeaponName, CCSPlayer *pBuyer )
+{
+ //Ignore bot purchases.
+ if ( pBuyer && pBuyer->IsBot() )
+ return;
+
+ CSWeaponID iWeaponID = AliasToWeaponID( pWeaponName );
+
+ TrackAutoBuyPurchases( pWeaponName, pBuyer );
+
+ if ( g_BlackMarket_WeaponsBought.Count() > 0 )
+ {
+ for ( int i = 0; i < g_BlackMarket_WeaponsBought.Count(); i++ )
+ {
+ if ( g_BlackMarket_WeaponsBought[i].m_iWeaponID == iWeaponID )
+ {
+ g_BlackMarket_WeaponsBought[i].m_iTimesBought++;
+ g_iWeaponPurchases[g_BlackMarket_WeaponsBought[i].m_iWeaponID]++;
+ return;
+ }
+ }
+ }
+
+ CBlackMarketElement newweapon;
+
+ newweapon.m_iWeaponID = iWeaponID;
+ newweapon.m_iTimesBought = 1;
+ newweapon.m_iPrice = 0;
+ g_iWeaponPurchases[newweapon.m_iWeaponID] = 1;
+
+ g_BlackMarket_WeaponsBought.AddToTail( newweapon );
+}
+
+int GetPistolCount( void )
+{
+ int iNumPistol = 0;
+
+ for ( int j = 1; j < WEAPON_MAX; j++ )
+ {
+ CCSWeaponInfo *pWeaponInfo = GetWeaponInfo( (CSWeaponID)j );
+
+ if ( pWeaponInfo )
+ {
+ if ( pWeaponInfo->m_WeaponType == WEAPONTYPE_PISTOL )
+ {
+ iNumPistol++;
+ }
+ }
+ }
+
+ return iNumPistol;
+}
+
+int GetRifleCount( void )
+{
+ int iNumRifle = 0;
+
+ for ( int j = 1; j < WEAPON_MAX; j++ )
+ {
+ CCSWeaponInfo *pWeaponInfo = GetWeaponInfo( (CSWeaponID)j );
+
+ if ( pWeaponInfo )
+ {
+ if ( pWeaponInfo->m_WeaponType != WEAPONTYPE_PISTOL )
+ {
+ iNumRifle++;
+ }
+ }
+ }
+
+ return iNumRifle + ARRAYSIZE( blackmarket_items );
+}
+
+#endif \ No newline at end of file
diff --git a/game/shared/cstrike/cs_blackmarket.h b/game/shared/cstrike/cs_blackmarket.h
new file mode 100644
index 0000000..e31b888
--- /dev/null
+++ b/game/shared/cstrike/cs_blackmarket.h
@@ -0,0 +1,89 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Holds defintion for game ammo types
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef CS_BLACKMARKET_H
+#define CS_BLACKMARKET_H
+
+#include "cs_weapon_parse.h"
+
+#ifdef CLIENT_DLL
+#include "c_cs_player.h"
+#else
+#include "cs_player.h"
+#endif
+
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+struct blackmarket_items_t
+{
+ const char *pClassname;
+ int iDefaultPrice;
+};
+
+extern blackmarket_items_t blackmarket_items[];
+
+void BlackMarketAddWeapon( const char *pWeaponName, CCSPlayer *pBuyer );
+
+
+enum
+{
+ KEVLAR_PRICE = 650,
+ HELMET_PRICE = 350,
+ ASSAULTSUIT_PRICE = 1000,
+ DEFUSEKIT_PRICE = 200,
+ NVG_PRICE = 1250,
+ SHIELD_PRICE = 2200
+};
+
+#ifndef CLIENT_DLL
+class CBlackMarketElement
+{
+public:
+
+ DECLARE_CLASS_NOBASE( CBlackMarketElement );
+
+ // For CNetworkVars.
+ void NetworkStateChanged();
+ void NetworkStateChanged( void *pVar );
+
+ CBlackMarketElement()
+ {
+ m_iPrice = 0;
+ m_iTimesBought = 0;
+ m_iWeaponID = 0;
+ }
+
+ CNetworkVar( int, m_iPrice );
+ CNetworkVar( int, m_iWeaponID );
+
+ int m_iTimesBought;
+};
+
+
+#else
+
+class C_BlackMarketElement
+{
+public:
+
+ // This allows the datatables to access private members.
+ ALLOW_DATATABLES_PRIVATE_ACCESS();
+
+ int m_iWeaponID;
+ int m_iPrice;
+};
+
+#define CBlackMarketElement C_BlackMarketElement
+#endif
+
+#endif // CS_BLACKMARKET_H
+ \ No newline at end of file
diff --git a/game/shared/cstrike/cs_gamemovement.cpp b/game/shared/cstrike/cs_gamemovement.cpp
new file mode 100644
index 0000000..fa5c52a
--- /dev/null
+++ b/game/shared/cstrike/cs_gamemovement.cpp
@@ -0,0 +1,1144 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "gamemovement.h"
+#include "cs_gamerules.h"
+#include "in_buttons.h"
+#include "movevars_shared.h"
+#include "weapon_csbase.h"
+
+#ifdef CLIENT_DLL
+ #include "c_cs_player.h"
+#else
+ #include "cs_player.h"
+ #include "KeyValues.h"
+#endif
+
+#define STAMINA_MAX 100.0
+#define STAMINA_COST_JUMP 25.0
+#define STAMINA_COST_FALL 20.0
+#define STAMINA_RECOVER_RATE 19.0
+
+extern bool g_bMovementOptimizations;
+
+ConVar sv_timebetweenducks( "sv_timebetweenducks", "0", FCVAR_REPLICATED, "Minimum time before recognizing consecutive duck key", true, 0.0, true, 2.0 );
+ConVar sv_enableboost( "sv_enableboost", "0", FCVAR_REPLICATED | FCVAR_NOTIFY, "Allow boost exploits");
+
+
+class CCSGameMovement : public CGameMovement
+{
+public:
+ DECLARE_CLASS( CCSGameMovement, CGameMovement );
+
+ CCSGameMovement();
+
+ virtual void ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMove );
+ virtual bool CanAccelerate();
+ virtual bool CheckJumpButton( void );
+ virtual void PreventBunnyJumping( void );
+ virtual void ReduceTimers( void );
+ virtual void WalkMove( void );
+ virtual void AirMove( void );
+ virtual bool LadderMove( void );
+ virtual void DecayPunchAngle( void );
+ virtual void CheckParameters( void );
+
+ // allow overridden versions to respond to jumping
+ virtual void OnJump( float fImpulse );
+ virtual void OnLand( float fVelocity );
+
+ // Ducking
+ virtual void Duck( void );
+ virtual void FinishUnDuck( void );
+ virtual void FinishDuck( void );
+ virtual bool CanUnduck();
+ virtual void HandleDuckingSpeedCrop();
+
+
+ virtual bool OnLadder( trace_t &trace );
+ virtual float LadderDistance( void ) const
+ {
+ if ( player->GetMoveType() == MOVETYPE_LADDER )
+ return 10.0f;
+ return 2.0f;
+ }
+
+ virtual unsigned int LadderMask( void ) const
+ {
+ return MASK_PLAYERSOLID & ( ~CONTENTS_PLAYERCLIP );
+ }
+
+ virtual float ClimbSpeed( void ) const;
+ virtual float LadderLateralMultiplier( void ) const;
+
+ virtual void TryTouchGround( const Vector& start, const Vector& end, const Vector& mins, const Vector& maxs, unsigned int fMask, int collisionGroup, trace_t& pm );
+
+
+protected:
+ virtual void PlayerMove();
+
+ void CheckForLadders( bool wasOnGround );
+ virtual unsigned int PlayerSolidMask( bool brushOnly = false ); ///< returns the solid mask for the given player, so bots can have a more-restrictive set
+
+ float m_fTimeLastUnducked;
+
+public:
+ CCSPlayer *m_pCSPlayer;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: used by the TryTouchGround function to exclude non-standables from
+// consideration
+//-----------------------------------------------------------------------------
+
+bool CheckForStandable( IHandleEntity *pHandleEntity, int contentsMask )
+{
+ CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
+
+ if ( !pEntity )
+ return false;
+
+ return ( pEntity->IsPlayer() && pEntity->GetGroundEntity() != NULL ) || pEntity->IsStandable();
+}
+
+
+// Expose our interface.
+static CCSGameMovement g_GameMovement;
+IGameMovement *g_pGameMovement = ( IGameMovement * )&g_GameMovement;
+
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CGameMovement, IGameMovement,INTERFACENAME_GAMEMOVEMENT, g_GameMovement );
+
+
+// ---------------------------------------------------------------------------------------- //
+// CCSGameMovement.
+// ---------------------------------------------------------------------------------------- //
+
+CCSGameMovement::CCSGameMovement()
+{
+ m_fTimeLastUnducked = 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Allow bots etc to use slightly different solid masks
+//-----------------------------------------------------------------------------
+unsigned int CCSGameMovement::PlayerSolidMask( bool brushOnly )
+{
+ bool isBot = !player || player->IsBot();
+
+ if ( brushOnly )
+ {
+ if ( isBot )
+ {
+ return MASK_PLAYERSOLID_BRUSHONLY | CONTENTS_MONSTERCLIP;
+ }
+ else
+ {
+ return MASK_PLAYERSOLID_BRUSHONLY;
+ }
+ }
+ else
+ {
+ if ( isBot )
+ {
+ return MASK_PLAYERSOLID | CONTENTS_MONSTERCLIP;
+ }
+ else
+ {
+ return MASK_PLAYERSOLID;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CCSGameMovement::CheckParameters( void )
+{
+ QAngle v_angle;
+
+ // maintaining auto-duck during jumps
+ if ( m_pCSPlayer->m_duckUntilOnGround )
+ {
+ if ( !player->GetGroundEntity() )
+ {
+ if ( mv->m_nButtons & IN_DUCK )
+ {
+ m_pCSPlayer->m_duckUntilOnGround = false; // player hit +duck, so cancel our auto duck
+ }
+ else
+ {
+ mv->m_nButtons |= IN_DUCK;
+ }
+ }
+ }
+
+ // it would be nice to put this into the player->GetPlayerMaxSpeed() method, but
+ // this flag is only stored in the move!
+ if ( mv->m_nButtons & IN_SPEED )
+ {
+ mv->m_flMaxSpeed *= CS_PLAYER_SPEED_WALK_MODIFIER;
+ }
+
+ if ( player->GetMoveType() != MOVETYPE_ISOMETRIC &&
+ player->GetMoveType() != MOVETYPE_NOCLIP &&
+ player->GetMoveType() != MOVETYPE_OBSERVER )
+ {
+ float spd;
+
+ spd = ( mv->m_flForwardMove * mv->m_flForwardMove ) +
+ ( mv->m_flSideMove * mv->m_flSideMove ) +
+ ( mv->m_flUpMove * mv->m_flUpMove );
+
+
+ // Slow down by the speed factor
+ float flSpeedFactor = 1.0f;
+ if (player->m_pSurfaceData)
+ {
+ flSpeedFactor = player->m_pSurfaceData->game.maxSpeedFactor;
+ }
+
+ // If we have a constraint, slow down because of that too.
+ float flConstraintSpeedFactor = ComputeConstraintSpeedFactor();
+ if (flConstraintSpeedFactor < flSpeedFactor)
+ flSpeedFactor = flConstraintSpeedFactor;
+
+ // Take the player's velocity modifier into account
+ if ( FBitSet( m_pCSPlayer->GetFlags(), FL_ONGROUND ) )
+ {
+ flSpeedFactor *= m_pCSPlayer->m_flVelocityModifier;
+ }
+
+
+ mv->m_flMaxSpeed *= flSpeedFactor;
+
+ if ( g_bMovementOptimizations )
+ {
+ // Same thing but only do the sqrt if we have to.
+ if ( ( spd != 0.0 ) && ( spd > mv->m_flMaxSpeed*mv->m_flMaxSpeed ) )
+ {
+ float fRatio = mv->m_flMaxSpeed / sqrt( spd );
+ mv->m_flForwardMove *= fRatio;
+ mv->m_flSideMove *= fRatio;
+ mv->m_flUpMove *= fRatio;
+ }
+ }
+ else
+ {
+ spd = sqrt( spd );
+ if ( ( spd != 0.0 ) && ( spd > mv->m_flMaxSpeed ) )
+ {
+ float fRatio = mv->m_flMaxSpeed / spd;
+ mv->m_flForwardMove *= fRatio;
+ mv->m_flSideMove *= fRatio;
+ mv->m_flUpMove *= fRatio;
+ }
+ }
+ }
+
+
+ if ( player->GetFlags() & FL_FROZEN ||
+ player->GetFlags() & FL_ONTRAIN ||
+ IsDead() )
+ {
+ mv->m_flForwardMove = 0;
+ mv->m_flSideMove = 0;
+ mv->m_flUpMove = 0;
+ }
+
+ DecayPunchAngle();
+
+ // Take angles from command.
+ if ( !IsDead() )
+ {
+ v_angle = mv->m_vecAngles;
+ v_angle = v_angle + player->m_Local.m_vecPunchAngle;
+
+ // Now adjust roll angle
+ if ( player->GetMoveType() != MOVETYPE_ISOMETRIC &&
+ player->GetMoveType() != MOVETYPE_NOCLIP )
+ {
+ mv->m_vecAngles[ROLL] = CalcRoll( v_angle, mv->m_vecVelocity, sv_rollangle.GetFloat(), sv_rollspeed.GetFloat() );
+ }
+ else
+ {
+ mv->m_vecAngles[ROLL] = 0.0; // v_angle[ ROLL ];
+ }
+ mv->m_vecAngles[PITCH] = v_angle[PITCH];
+ mv->m_vecAngles[YAW] = v_angle[YAW];
+ }
+ else
+ {
+ mv->m_vecAngles = mv->m_vecOldAngles;
+ }
+
+ // Set dead player view_offset
+ if ( IsDead() )
+ {
+ player->SetViewOffset( VEC_DEAD_VIEWHEIGHT );
+ }
+
+ // Adjust client view angles to match values used on server.
+ if ( mv->m_vecAngles[YAW] > 180.0f )
+ {
+ mv->m_vecAngles[YAW] -= 360.0f;
+ }
+
+ // If we're standing on a player, then force them off.
+ if ( !player->IsObserver() && ( player->GetMoveType() != MOVETYPE_LADDER ) )
+ {
+ int nLevels = 0;
+ CBaseEntity *pCurGround = player->GetGroundEntity();
+ while ( pCurGround && pCurGround->IsPlayer() && nLevels < 1000 )
+ {
+ pCurGround = pCurGround->GetGroundEntity();
+ ++nLevels;
+ }
+ if ( nLevels == 1000 )
+ Warning( "BUG: CCSGameMovement::CheckParameters - too many stacking levels.\n" );
+
+ // If they're stacked too many levels deep, slide them off.
+ if ( nLevels > 1 )
+ {
+ mv->m_flForwardMove = mv->m_flMaxSpeed * 3;
+ mv->m_flSideMove = 0;
+ mv->m_nButtons = 0;
+ mv->m_nImpulseCommand = 0;
+ }
+ }
+}
+
+
+void CCSGameMovement::ProcessMovement( CBasePlayer *pBasePlayer, CMoveData *pMove )
+{
+ m_pCSPlayer = ToCSPlayer( pBasePlayer );
+ Assert( m_pCSPlayer );
+
+ BaseClass::ProcessMovement( pBasePlayer, pMove );
+}
+
+
+bool CCSGameMovement::CanAccelerate()
+{
+ // Only allow the player to accelerate when in certain states.
+ CSPlayerState curState = m_pCSPlayer->State_Get();
+ if ( curState == STATE_ACTIVE )
+ {
+ return player->GetWaterJumpTime() == 0;
+ }
+ else if ( player->IsObserver() )
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+void CCSGameMovement::PlayerMove()
+{
+ if ( !m_pCSPlayer->CanMove() )
+ {
+ mv->m_flForwardMove = 0;
+ mv->m_flSideMove = 0;
+ mv->m_flUpMove = 0;
+ mv->m_nButtons &= ~(IN_JUMP | IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT);
+ }
+
+ BaseClass::PlayerMove();
+
+ if ( FBitSet( m_pCSPlayer->GetFlags(), FL_ONGROUND ) )
+ {
+ if ( m_pCSPlayer->m_flVelocityModifier < 1.0 )
+ {
+ m_pCSPlayer->m_flVelocityModifier += gpGlobals->frametime / 3.0f;
+ }
+
+ if ( m_pCSPlayer->m_flVelocityModifier > 1.0 )
+ m_pCSPlayer->m_flVelocityModifier = 1.0;
+ }
+
+#if !defined(CLIENT_DLL)
+ if ( m_pCSPlayer->IsAlive() )
+ {
+ // Check if our eye height is too close to the ceiling and lower it.
+ // This is needed because we have taller models with the old collision bounds.
+
+ const float eyeClearance = 12.0f; // eye pos must be this far below the ceiling
+
+ Vector offset = player->GetViewOffset();
+
+ Vector vHullMin = GetPlayerMins( player->m_Local.m_bDucked );
+ vHullMin.z = 0.0f;
+ Vector vHullMax = GetPlayerMaxs( player->m_Local.m_bDucked );
+
+ Vector start = player->GetAbsOrigin();
+ start.z += vHullMax.z;
+ Vector end = start;
+ end.z += eyeClearance - vHullMax.z;
+ end.z += player->m_Local.m_bDucked ? VEC_DUCK_VIEW_SCALED( player ).z : VEC_VIEW_SCALED( player ).z;
+
+ vHullMax.z = 0.0f;
+
+ Vector fudge( 1, 1, 0 );
+ vHullMin += fudge;
+ vHullMax -= fudge;
+
+ trace_t trace;
+ Ray_t ray;
+ ray.Init( start, end, vHullMin, vHullMax );
+ UTIL_TraceRay( ray, PlayerSolidMask(), mv->m_nPlayerHandle.Get(), COLLISION_GROUP_PLAYER_MOVEMENT, &trace );
+
+ if ( trace.fraction < 1.0f )
+ {
+ float est = start.z + trace.fraction * (end.z - start.z) - player->GetAbsOrigin().z - eyeClearance;
+ if ( ( player->GetFlags() & FL_DUCKING ) == 0 && !player->m_Local.m_bDucking && !player->m_Local.m_bDucked )
+ {
+ offset.z = est;
+ }
+ else
+ {
+ offset.z = MIN( est, offset.z );
+ }
+ player->SetViewOffset( offset );
+ }
+ else
+ {
+ if ( ( player->GetFlags() & FL_DUCKING ) == 0 && !player->m_Local.m_bDucking && !player->m_Local.m_bDucked )
+ {
+
+ player->SetViewOffset( VEC_VIEW_SCALED( player ) );
+ }
+ else if ( m_pCSPlayer->m_duckUntilOnGround )
+ {
+ // Duck Hull, but we're in the air. Calculate where the view would be.
+ Vector hullSizeNormal = VEC_HULL_MAX_SCALED( player ) - VEC_HULL_MIN_SCALED( player );
+ Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED( player ) - VEC_DUCK_HULL_MIN_SCALED( player );
+
+ // We've got the duck hull, pulled up to the top of where the player should be
+ Vector lowerClearance = hullSizeNormal - hullSizeCrouch;
+ Vector duckEyeHeight = GetPlayerViewOffset( false ) - lowerClearance;
+ player->SetViewOffset( duckEyeHeight );
+ }
+ else if( player->m_Local.m_bDucked && !player->m_Local.m_bDucking )
+ {
+ player->SetViewOffset( VEC_DUCK_VIEW );
+ }
+ }
+ }
+#endif
+}
+
+
+void CCSGameMovement::WalkMove( void )
+{
+ if ( m_pCSPlayer->m_flStamina > 0 )
+ {
+ float flRatio;
+
+ flRatio = ( STAMINA_MAX - ( ( m_pCSPlayer->m_flStamina / 1000.0 ) * STAMINA_RECOVER_RATE ) ) / STAMINA_MAX;
+
+ // This Goldsrc code was run with variable timesteps and it had framerate dependencies.
+ // People looking at Goldsrc for reference are usually
+ // (these days) measuring the stoppage at 60fps or greater, so we need
+ // to account for the fact that Goldsrc was applying more stopping power
+ // since it applied the slowdown across more frames.
+ float flReferenceFrametime = 1.0f / 70.0f;
+ float flFrametimeRatio = gpGlobals->frametime / flReferenceFrametime;
+
+ flRatio = pow( flRatio, flFrametimeRatio );
+
+ mv->m_vecVelocity.x *= flRatio;
+ mv->m_vecVelocity.y *= flRatio;
+ }
+
+ BaseClass::WalkMove();
+
+ CheckForLadders( player->GetGroundEntity() != NULL );
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------
+void CCSGameMovement::AirMove( void )
+{
+ BaseClass::AirMove();
+
+ CheckForLadders( false );
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------
+bool CCSGameMovement::OnLadder( trace_t &trace )
+{
+ if ( trace.plane.normal.z == 1.0f )
+ return false;
+
+ return BaseClass::OnLadder( trace );
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------
+bool CCSGameMovement::LadderMove( void )
+{
+ bool isOnLadder = BaseClass::LadderMove();
+ if ( isOnLadder && m_pCSPlayer )
+ {
+ m_pCSPlayer->SurpressLadderChecks( mv->GetAbsOrigin(), m_pCSPlayer->m_vecLadderNormal );
+ }
+
+ return isOnLadder;
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------
+/**
+ * In CS, crouching up ladders goes slowly and doesn't make a sound.
+ */
+float CCSGameMovement::ClimbSpeed( void ) const
+{
+ if ( mv->m_nButtons & IN_DUCK )
+ {
+ return BaseClass::ClimbSpeed() * CS_PLAYER_SPEED_CLIMB_MODIFIER;
+ }
+ else
+ {
+ return BaseClass::ClimbSpeed();
+ }
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------
+/**
+* In CS, strafing on ladders goes slowly.
+*/
+float CCSGameMovement::LadderLateralMultiplier( void ) const
+{
+ if ( mv->m_nButtons & IN_DUCK )
+ {
+ return 1.0f;
+ }
+ else
+ {
+ return 0.5f;
+ }
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------
+/**
+ * Looks behind and beneath the player in the air, in case he skips out over the top of a ladder. If the
+ * trace hits a ladder, the player is snapped to the ladder.
+ */
+void CCSGameMovement::CheckForLadders( bool wasOnGround )
+{
+ if ( !wasOnGround )
+ {
+ // If we're higher than the last place we were on the ground, bail - obviously we're not dropping
+ // past a ladder we might want to grab.
+ if ( mv->GetAbsOrigin().z > m_pCSPlayer->m_lastStandingPos.z )
+ return;
+
+ Vector dir = -m_pCSPlayer->m_lastStandingPos + mv->GetAbsOrigin();
+ if ( !dir.x && !dir.y )
+ {
+ // If we're dropping straight down, we don't know which way to look for a ladder. Oh well.
+ return;
+ }
+
+ dir.z = 0.0f;
+ float dist = dir.NormalizeInPlace();
+ if ( dist > 64.0f )
+ {
+ // Don't grab ladders too far behind us.
+ return;
+ }
+
+ trace_t trace;
+
+ TracePlayerBBox(
+ mv->GetAbsOrigin(),
+ m_pCSPlayer->m_lastStandingPos - dir*(5+dist),
+ (PlayerSolidMask() & (~CONTENTS_PLAYERCLIP)), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
+
+ if ( trace.fraction != 1.0f && OnLadder( trace ) && trace.plane.normal.z != 1.0f )
+ {
+ if ( m_pCSPlayer->CanGrabLadder( trace.endpos, trace.plane.normal ) )
+ {
+ player->SetMoveType( MOVETYPE_LADDER );
+ player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
+
+ player->SetLadderNormal( trace.plane.normal );
+ mv->m_vecVelocity.Init();
+
+ // The ladder check ignored playerclips, to fix a bug exposed by de_train, where a clipbrush is
+ // flush with a ladder. This causes the above tracehull to fail unless we ignore playerclips.
+ // However, we have to check for playerclips before we snap to that pos, so we don't warp a
+ // player into a clipbrush.
+ TracePlayerBBox(
+ mv->GetAbsOrigin(),
+ m_pCSPlayer->m_lastStandingPos - dir*(5+dist),
+ PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
+
+ mv->SetAbsOrigin( trace.endpos );
+ }
+ }
+ }
+ else
+ {
+ m_pCSPlayer->m_lastStandingPos = mv->GetAbsOrigin();
+ }
+}
+
+
+void CCSGameMovement::ReduceTimers( void )
+{
+ float frame_msec = 1000.0f * gpGlobals->frametime;
+
+ if ( m_pCSPlayer->m_flStamina > 0 )
+ {
+ m_pCSPlayer->m_flStamina -= frame_msec;
+
+ if ( m_pCSPlayer->m_flStamina < 0 )
+ {
+ m_pCSPlayer->m_flStamina = 0;
+ }
+ }
+
+ BaseClass::ReduceTimers();
+}
+
+ConVar sv_enablebunnyhopping( "sv_enablebunnyhopping", "0", FCVAR_REPLICATED | FCVAR_NOTIFY );
+
+// Only allow bunny jumping up to 1.1x server / player maxspeed setting
+#define BUNNYJUMP_MAX_SPEED_FACTOR 1.1f
+
+// taken from TF2 but changed BUNNYJUMP_MAX_SPEED_FACTOR from 1.1 to 1.0
+void CCSGameMovement::PreventBunnyJumping()
+{
+ // Speed at which bunny jumping is limited
+ float maxscaledspeed = BUNNYJUMP_MAX_SPEED_FACTOR * player->m_flMaxspeed;
+ if ( maxscaledspeed <= 0.0f )
+ return;
+
+ // Current player speed
+ float spd = mv->m_vecVelocity.Length();
+
+ if ( spd <= maxscaledspeed )
+ return;
+
+ // Apply this cropping fraction to velocity
+ float fraction = ( maxscaledspeed / spd );
+
+ mv->m_vecVelocity *= fraction;
+
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CCSGameMovement::CheckJumpButton( void )
+{
+ if (m_pCSPlayer->pl.deadflag)
+ {
+ mv->m_nOldButtons |= IN_JUMP ; // don't jump again until released
+ return false;
+ }
+
+ // See if we are waterjumping. If so, decrement count and return.
+ if (m_pCSPlayer->m_flWaterJumpTime)
+ {
+ m_pCSPlayer->m_flWaterJumpTime -= gpGlobals->frametime;
+ if (m_pCSPlayer->m_flWaterJumpTime < 0)
+ m_pCSPlayer->m_flWaterJumpTime = 0;
+
+ return false;
+ }
+
+ // If we are in the water most of the way...
+ if ( m_pCSPlayer->GetWaterLevel() >= 2 )
+ {
+ // swimming, not jumping
+ SetGroundEntity( NULL );
+
+ if(m_pCSPlayer->GetWaterType() == CONTENTS_WATER) // We move up a certain amount
+ mv->m_vecVelocity[2] = 100;
+ else if (m_pCSPlayer->GetWaterType() == CONTENTS_SLIME)
+ mv->m_vecVelocity[2] = 80;
+
+ // play swiming sound
+ if ( m_pCSPlayer->m_flSwimSoundTime <= 0 )
+ {
+ // Don't play sound again for 1 second
+ m_pCSPlayer->m_flSwimSoundTime = 1000;
+ PlaySwimSound();
+ }
+
+ return false;
+ }
+
+ // No more effect
+ if (m_pCSPlayer->GetGroundEntity() == NULL)
+ {
+ mv->m_nOldButtons |= IN_JUMP;
+ return false; // in air, so no effect
+ }
+
+ if ( mv->m_nOldButtons & IN_JUMP )
+ return false; // don't pogo stick
+
+ if ( !sv_enablebunnyhopping.GetBool() )
+ {
+ PreventBunnyJumping();
+ }
+
+ // In the air now.
+ SetGroundEntity( NULL );
+
+ m_pCSPlayer->PlayStepSound( (Vector &)mv->GetAbsOrigin(), player->m_pSurfaceData, 1.0, true );
+
+ //MoveHelper()->PlayerSetAnimation( PLAYER_JUMP );
+ m_pCSPlayer->DoAnimationEvent( PLAYERANIMEVENT_JUMP );
+
+ float flGroundFactor = 1.0f;
+ if (player->m_pSurfaceData)
+ {
+ flGroundFactor = player->m_pSurfaceData->game.jumpFactor;
+ }
+
+ // if we weren't ducking, bots and hostages do a crouchjump programatically
+ if ( (!player || player->IsBot()) && !(mv->m_nButtons & IN_DUCK) )
+ {
+ m_pCSPlayer->m_duckUntilOnGround = true;
+ FinishDuck();
+ }
+
+ // Acclerate upward
+ // If we are ducking...
+ float startz = mv->m_vecVelocity[2];
+ if ( m_pCSPlayer->m_duckUntilOnGround || ( m_pCSPlayer->m_Local.m_bDucking ) || ( m_pCSPlayer->GetFlags() & FL_DUCKING ) )
+ {
+ // d = 0.5 * g * t^2 - distance traveled with linear accel
+ // t = sqrt(2.0 * 45 / g) - how long to fall 45 units
+ // v = g * t - velocity at the end (just invert it to jump up that high)
+ // v = g * sqrt(2.0 * 45 / g )
+ // v^2 = g * g * 2.0 * 45 / g
+ // v = sqrt( g * 2.0 * 45 )
+
+ mv->m_vecVelocity[2] = flGroundFactor * sqrt(2 * 800 * 57.0); // 2 * gravity * height
+ }
+ else
+ {
+ mv->m_vecVelocity[2] += flGroundFactor * sqrt(2 * 800 * 57.0); // 2 * gravity * height
+ }
+
+ if ( m_pCSPlayer->m_flStamina > 0 )
+ {
+ float flRatio;
+
+ flRatio = ( STAMINA_MAX - ( ( m_pCSPlayer->m_flStamina / 1000.0 ) * STAMINA_RECOVER_RATE ) ) / STAMINA_MAX;
+
+ mv->m_vecVelocity[2] *= flRatio;
+ }
+
+ m_pCSPlayer->m_flStamina = ( STAMINA_COST_JUMP / STAMINA_RECOVER_RATE ) * 1000.0;
+
+ FinishGravity();
+
+ mv->m_outWishVel.z += mv->m_vecVelocity[2] - startz;
+ mv->m_outStepHeight += 0.1f;
+
+ OnJump(mv->m_outWishVel.z);
+
+#ifndef CLIENT_DLL
+ // allow bots to react
+ IGameEvent * event = gameeventmanager->CreateEvent( "player_jump" );
+ if ( event )
+ {
+ event->SetInt( "userid", m_pCSPlayer->GetUserID() );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+
+ // Flag that we jumped.
+ mv->m_nOldButtons |= IN_JUMP; // don't jump again until released
+ return true;
+}
+
+
+void CCSGameMovement::DecayPunchAngle( void )
+{
+ float len;
+
+ Vector vPunchAngle;
+
+ vPunchAngle.x = m_pCSPlayer->m_Local.m_vecPunchAngle->x;
+ vPunchAngle.y = m_pCSPlayer->m_Local.m_vecPunchAngle->y;
+ vPunchAngle.z = m_pCSPlayer->m_Local.m_vecPunchAngle->z;
+
+ len = VectorNormalize ( vPunchAngle );
+ len -= (10.0 + len * 0.5) * gpGlobals->frametime;
+ len = MAX( len, 0.0 );
+ VectorScale ( vPunchAngle, len, vPunchAngle );
+
+ m_pCSPlayer->m_Local.m_vecPunchAngle.Set( 0, vPunchAngle.x );
+ m_pCSPlayer->m_Local.m_vecPunchAngle.Set( 1, vPunchAngle.y );
+ m_pCSPlayer->m_Local.m_vecPunchAngle.Set( 2, vPunchAngle.z );
+}
+
+
+void CCSGameMovement::HandleDuckingSpeedCrop()
+{
+ //=============================================================================
+ // HPE_BEGIN:
+ // [Forrest]
+ //=============================================================================
+ // Movement speed in free look camera mode is unaffected by ducking state.
+ if ( player->GetObserverMode() == OBS_MODE_ROAMING )
+ return;
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ if ( !( m_iSpeedCropped & SPEED_CROPPED_DUCK ) )
+ {
+ if ( ( mv->m_nButtons & IN_DUCK ) || ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) )
+ {
+ mv->m_flForwardMove *= CS_PLAYER_SPEED_DUCK_MODIFIER;
+ mv->m_flSideMove *= CS_PLAYER_SPEED_DUCK_MODIFIER;
+ mv->m_flUpMove *= CS_PLAYER_SPEED_DUCK_MODIFIER;
+ m_iSpeedCropped |= SPEED_CROPPED_DUCK;
+ }
+ }
+}
+
+bool CCSGameMovement::CanUnduck()
+{
+ trace_t trace;
+ Vector newOrigin;
+
+ VectorCopy( mv->GetAbsOrigin(), newOrigin );
+
+ if ( player->GetGroundEntity() != NULL )
+ {
+ newOrigin += VEC_DUCK_HULL_MIN_SCALED( player ) - VEC_HULL_MIN_SCALED( player );
+ }
+ else
+ {
+ // If in air an letting go of croush, make sure we can offset origin to make
+ // up for uncrouching
+ Vector hullSizeNormal = VEC_HULL_MAX_SCALED( player ) - VEC_HULL_MIN_SCALED( player );
+ Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED( player ) - VEC_DUCK_HULL_MIN_SCALED( player );
+
+ newOrigin += -0.5f * ( hullSizeNormal - hullSizeCrouch );
+ }
+
+ UTIL_TraceHull( mv->GetAbsOrigin(), newOrigin, VEC_HULL_MIN_SCALED( player ), VEC_HULL_MAX_SCALED( player ), PlayerSolidMask(), player, COLLISION_GROUP_PLAYER_MOVEMENT, &trace );
+
+ if ( trace.startsolid || ( trace.fraction != 1.0f ) )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stop ducking
+//-----------------------------------------------------------------------------
+void CCSGameMovement::FinishUnDuck( void )
+{
+ trace_t trace;
+ Vector newOrigin;
+
+ VectorCopy( mv->GetAbsOrigin(), newOrigin );
+
+ if ( player->GetGroundEntity() != NULL )
+ {
+ newOrigin += VEC_DUCK_HULL_MIN_SCALED( player ) - VEC_HULL_MIN_SCALED( player );
+ }
+ else
+ {
+ // If in air an letting go of croush, make sure we can offset origin to make
+ // up for uncrouching
+ Vector hullSizeNormal = VEC_HULL_MAX_SCALED( player ) - VEC_HULL_MIN_SCALED( player );
+ Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED( player ) - VEC_DUCK_HULL_MIN_SCALED( player );
+
+ Vector viewDelta = -0.5f * ( hullSizeNormal - hullSizeCrouch );
+
+ VectorAdd( newOrigin, viewDelta, newOrigin );
+ }
+
+ player->m_Local.m_bDucked = false;
+ player->RemoveFlag( FL_DUCKING );
+ player->m_Local.m_bDucking = false;
+ player->SetViewOffset( GetPlayerViewOffset( false ) );
+ player->m_Local.m_flDucktime = 0;
+
+ mv->SetAbsOrigin( newOrigin );
+
+ // Recategorize position since ducking can change origin
+ CategorizePosition();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Finish ducking
+//-----------------------------------------------------------------------------
+void CCSGameMovement::FinishDuck( void )
+{
+
+ Vector hullSizeNormal = VEC_HULL_MAX_SCALED( player ) - VEC_HULL_MIN_SCALED( player );
+ Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED( player ) - VEC_DUCK_HULL_MIN_SCALED( player );
+
+ Vector viewDelta = 0.5f * ( hullSizeNormal - hullSizeCrouch );
+
+ player->SetViewOffset( GetPlayerViewOffset( true ) );
+ player->AddFlag( FL_DUCKING );
+ player->m_Local.m_bDucking = false;
+
+ if ( !player->m_Local.m_bDucked )
+ {
+
+ Vector org = mv->GetAbsOrigin();
+
+ if ( player->GetGroundEntity() != NULL )
+ {
+ org -= VEC_DUCK_HULL_MIN_SCALED( player ) - VEC_HULL_MIN_SCALED( player );
+ }
+ else
+ {
+ org += viewDelta;
+ }
+ mv->SetAbsOrigin( org );
+
+ player->m_Local.m_bDucked = true;
+ }
+
+ // See if we are stuck?
+ FixPlayerCrouchStuck( true );
+
+ // Recategorize position since ducking can change origin
+ CategorizePosition();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: See if duck button is pressed and do the appropriate things
+//-----------------------------------------------------------------------------
+void CCSGameMovement::Duck( void )
+{
+
+ // Fix taken from zblock for rapid crouch/stand not showing stand on other clients
+
+ if ( player->GetFlags() & FL_ONGROUND )
+ {
+ // if prevent crouch
+ if ( !( mv->m_nButtons & IN_DUCK ) && ( mv->m_nOldButtons & IN_DUCK ) )
+ {
+ // Player has released crouch and moving to standing
+ m_fTimeLastUnducked = gpGlobals->curtime;
+ }
+ else if ( ( mv->m_nButtons & IN_DUCK ) && !( mv->m_nOldButtons & IN_DUCK ) )
+ {
+ // Crouch from standing
+ if ( ( player->GetFlags() & FL_DUCKING )
+ && ( m_fTimeLastUnducked > (gpGlobals->curtime - sv_timebetweenducks.GetFloat() ) ) )
+ {
+ // if the server thinks the player is still crouched
+ // AND the time the player started to stand (from being ducked) was less than 300ms ago
+ // prevent the player from ducking again
+ mv->m_nButtons &= ~IN_DUCK;
+ }
+ }
+ }
+
+ int buttonsChanged = ( mv->m_nOldButtons ^ mv->m_nButtons ); // These buttons have changed this frame
+ int buttonsPressed = buttonsChanged & mv->m_nButtons; // The changed ones still down are "pressed"
+ int buttonsReleased = buttonsChanged & mv->m_nOldButtons; // The changed ones which were previously down are "released"
+
+ // Check to see if we are in the air.
+ bool bInAir = player->GetGroundEntity() == NULL && player->GetMoveType() != MOVETYPE_LADDER;
+
+ if ( mv->m_nButtons & IN_DUCK )
+ {
+ mv->m_nOldButtons |= IN_DUCK;
+ }
+ else
+ {
+ mv->m_nOldButtons &= ~IN_DUCK;
+ }
+
+ if ( IsDead() )
+ {
+ // Unduck
+ if ( player->GetFlags() & FL_DUCKING )
+ {
+ FinishUnDuck();
+ }
+ return;
+ }
+
+ HandleDuckingSpeedCrop();
+
+ if ( m_pCSPlayer->m_duckUntilOnGround )
+ {
+ if ( !bInAir )
+ {
+ m_pCSPlayer->m_duckUntilOnGround = false;
+ if ( CanUnduck() )
+ {
+ FinishUnDuck();
+ }
+ return;
+ }
+ else
+ {
+ if ( mv->m_vecVelocity.z > 0.0f )
+ return;
+
+ // Check if we can un-duck. We want to unduck if we have space for the standing hull, and
+ // if it is less than 2 inches off the ground.
+ trace_t trace;
+ Vector newOrigin;
+ Vector groundCheck;
+
+ VectorCopy( mv->GetAbsOrigin(), newOrigin );
+ Vector hullSizeNormal = VEC_HULL_MAX_SCALED( player ) - VEC_HULL_MIN_SCALED( player );
+ Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED( player ) - VEC_DUCK_HULL_MIN_SCALED( player );
+ newOrigin -= ( hullSizeNormal - hullSizeCrouch );
+ groundCheck = newOrigin;
+ groundCheck.z -= player->GetStepSize();
+
+ UTIL_TraceHull( newOrigin, groundCheck, VEC_HULL_MIN_SCALED( player ), VEC_HULL_MAX_SCALED( player ), PlayerSolidMask(), player, COLLISION_GROUP_PLAYER_MOVEMENT, &trace );
+
+ if ( trace.startsolid || trace.fraction == 1.0f )
+ return; // Can't even stand up, or there's no ground underneath us
+
+ m_pCSPlayer->m_duckUntilOnGround = false;
+ if ( CanUnduck() )
+ {
+ FinishUnDuck();
+ }
+ return;
+ }
+ }
+
+ // Holding duck, in process of ducking or fully ducked?
+ if ( ( mv->m_nButtons & IN_DUCK ) || ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) )
+ {
+ if ( mv->m_nButtons & IN_DUCK )
+ {
+ bool alreadyDucked = ( player->GetFlags() & FL_DUCKING ) ? true : false;
+
+ if ( (buttonsPressed & IN_DUCK ) && !( player->GetFlags() & FL_DUCKING ) )
+ {
+ // Use 1 second so super long jump will work
+ player->m_Local.m_flDucktime = 1000;
+ player->m_Local.m_bDucking = true;
+ }
+
+ float duckmilliseconds = MAX( 0.0f, 1000.0f - (float)player->m_Local.m_flDucktime );
+ float duckseconds = duckmilliseconds / 1000.0f;
+
+ //time = MAX( 0.0, ( 1.0 - (float)player->m_Local.m_flDucktime / 1000.0 ) );
+
+ if ( player->m_Local.m_bDucking )
+ {
+ if ( !( player->GetFlags() & FL_ANIMDUCKING ) )
+ player->AddFlag( FL_ANIMDUCKING );
+
+ // Finish ducking immediately if duck time is over or not on ground
+ if ( ( duckseconds > TIME_TO_DUCK ) ||
+ ( player->GetGroundEntity() == NULL ) ||
+ alreadyDucked)
+ {
+ FinishDuck();
+ }
+ else
+ {
+ // Calc parametric time
+ float duckFraction = SimpleSpline( duckseconds / TIME_TO_DUCK );
+ SetDuckedEyeOffset( duckFraction );
+ }
+ }
+ }
+ else
+ {
+ // Try to unduck unless automovement is not allowed
+ // NOTE: When not onground, you can always unduck
+ if ( player->m_Local.m_bAllowAutoMovement || player->GetGroundEntity() == NULL )
+ {
+ if ( (buttonsReleased & IN_DUCK ) && ( player->GetFlags() & FL_DUCKING ) )
+ {
+ // Use 1 second so super long jump will work
+ player->m_Local.m_flDucktime = 1000;
+ player->m_Local.m_bDucking = true; // or unducking
+ }
+
+ float duckmilliseconds = MAX( 0.0f, 1000.0f - (float)player->m_Local.m_flDucktime );
+ float duckseconds = duckmilliseconds / 1000.0f;
+
+ if ( CanUnduck() )
+ {
+ if ( player->m_Local.m_bDucking ||
+ player->m_Local.m_bDucked ) // or unducking
+ {
+ if ( player->GetFlags() & FL_ANIMDUCKING )
+ player->RemoveFlag( FL_ANIMDUCKING );
+
+ // Finish ducking immediately if duck time is over or not on ground
+ if ( ( duckseconds > TIME_TO_UNDUCK ) ||
+ ( player->GetGroundEntity() == NULL ) )
+ {
+ FinishUnDuck();
+ }
+ else
+ {
+ // Calc parametric time
+ float duckFraction = SimpleSpline( 1.0f - ( duckseconds / TIME_TO_UNDUCK ) );
+ SetDuckedEyeOffset( duckFraction );
+ }
+ }
+ }
+ else
+ {
+ // Still under something where we can't unduck, so make sure we reset this timer so
+ // that we'll unduck once we exit the tunnel, etc.
+ player->m_Local.m_flDucktime = 1000;
+ }
+ }
+ }
+ }
+}
+
+
+void CCSGameMovement::OnJump( float fImpulse )
+{
+ m_pCSPlayer->OnJump( fImpulse );
+}
+
+void CCSGameMovement::OnLand( float fVelocity )
+{
+ m_pCSPlayer->OnLand( fVelocity );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Essentially the same as TracePlayerBBox, but adds a callback to
+// exclude entities that are not standable (except for other players)
+//-----------------------------------------------------------------------------
+void CCSGameMovement::TryTouchGround( const Vector& start, const Vector& end, const Vector& mins, const Vector& maxs, unsigned int fMask, int collisionGroup, trace_t& pm )
+{
+ VPROF( "CCSGameMovement::TryTouchGround" );
+
+ Ray_t ray;
+ ray.Init( start, end, mins, maxs );
+
+ ShouldHitFunc_t pStandingTestCallback = sv_enableboost.GetBool() ? NULL : CheckForStandable;
+
+ UTIL_TraceRay( ray, fMask, mv->m_nPlayerHandle.Get(), collisionGroup, &pm, pStandingTestCallback );
+
+}
diff --git a/game/shared/cstrike/cs_gamerules.cpp b/game/shared/cstrike/cs_gamerules.cpp
new file mode 100644
index 0000000..c688140
--- /dev/null
+++ b/game/shared/cstrike/cs_gamerules.cpp
@@ -0,0 +1,5859 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: The TF Game rules
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "cs_gamerules.h"
+#include "cs_ammodef.h"
+#include "weapon_csbase.h"
+#include "cs_shareddefs.h"
+#include "KeyValues.h"
+#include "cs_achievement_constants.h"
+#include "fmtstr.h"
+
+#ifdef CLIENT_DLL
+
+ #include "networkstringtable_clientdll.h"
+ #include "utlvector.h"
+
+#else
+
+ #include "bot.h"
+ #include "utldict.h"
+ #include "cs_player.h"
+ #include "cs_team.h"
+ #include "cs_gamerules.h"
+ #include "voice_gamemgr.h"
+ #include "igamesystem.h"
+ #include "weapon_c4.h"
+ #include "mapinfo.h"
+ #include "shake.h"
+ #include "mapentities.h"
+ #include "game.h"
+ #include "cs_simple_hostage.h"
+ #include "cs_gameinterface.h"
+ #include "player_resource.h"
+ #include "info_view_parameters.h"
+ #include "cs_bot_manager.h"
+ #include "cs_bot.h"
+ #include "eventqueue.h"
+ #include "fmtstr.h"
+ #include "teamplayroundbased_gamerules.h"
+ #include "gameweaponmanager.h"
+
+ #include "cs_gamestats.h"
+ #include "cs_urlretrieveprices.h"
+ #include "networkstringtable_gamedll.h"
+ #include "player_resource.h"
+ #include "cs_player_resource.h"
+
+#if defined( REPLAY_ENABLED )
+ #include "replay/ireplaysystem.h"
+ #include "replay/iserverreplaycontext.h"
+ #include "replay/ireplaysessionrecorder.h"
+#endif // REPLAY_ENABLED
+
+#endif
+
+
+#include "cs_blackmarket.h"
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+#ifndef CLIENT_DLL
+
+
+#define CS_GAME_STATS_UPDATE 79200 //22 hours
+#define CS_GAME_STATS_UPDATE_PERIOD 7200 // 2 hours
+
+extern IUploadGameStats *gamestatsuploader;
+
+#if defined( REPLAY_ENABLED )
+extern IReplaySystem *g_pReplay;
+#endif // REPLAY_ENABLED
+
+#endif
+
+
+/**
+ * Player hull & eye position for standing, ducking, etc. This version has a taller
+ * player height, but goldsrc-compatible collision bounds.
+ */
+static CViewVectors g_CSViewVectors(
+ Vector( 0, 0, 64 ), // eye position
+
+ Vector(-16, -16, 0 ), // hull min
+ Vector( 16, 16, 62 ), // hull max
+
+ Vector(-16, -16, 0 ), // duck hull min
+ Vector( 16, 16, 45 ), // duck hull max
+ Vector( 0, 0, 47 ), // duck view
+
+ Vector(-10, -10, -10 ), // observer hull min
+ Vector( 10, 10, 10 ), // observer hull max
+
+ Vector( 0, 0, 14 ) // dead view height
+);
+
+
+#ifndef CLIENT_DLL
+LINK_ENTITY_TO_CLASS(info_player_terrorist, CPointEntity);
+LINK_ENTITY_TO_CLASS(info_player_counterterrorist,CPointEntity);
+LINK_ENTITY_TO_CLASS(info_player_logo,CPointEntity);
+#endif
+
+REGISTER_GAMERULES_CLASS( CCSGameRules );
+
+
+BEGIN_NETWORK_TABLE_NOBASE( CCSGameRules, DT_CSGameRules )
+ #ifdef CLIENT_DLL
+ RecvPropBool( RECVINFO( m_bFreezePeriod ) ),
+ RecvPropInt( RECVINFO( m_iRoundTime ) ),
+ RecvPropFloat( RECVINFO( m_fRoundStartTime ) ),
+ RecvPropFloat( RECVINFO( m_flGameStartTime ) ),
+ RecvPropInt( RECVINFO( m_iHostagesRemaining ) ),
+ RecvPropBool( RECVINFO( m_bMapHasBombTarget ) ),
+ RecvPropBool( RECVINFO( m_bMapHasRescueZone ) ),
+ RecvPropBool( RECVINFO( m_bLogoMap ) ),
+ RecvPropBool( RECVINFO( m_bBlackMarket ) )
+ #else
+ SendPropBool( SENDINFO( m_bFreezePeriod ) ),
+ SendPropInt( SENDINFO( m_iRoundTime ), 16 ),
+ SendPropFloat( SENDINFO( m_fRoundStartTime ), 32, SPROP_NOSCALE ),
+ SendPropFloat( SENDINFO( m_flGameStartTime ), 32, SPROP_NOSCALE ),
+ SendPropInt( SENDINFO( m_iHostagesRemaining ), 4 ),
+ SendPropBool( SENDINFO( m_bMapHasBombTarget ) ),
+ SendPropBool( SENDINFO( m_bMapHasRescueZone ) ),
+ SendPropBool( SENDINFO( m_bLogoMap ) ),
+ SendPropBool( SENDINFO( m_bBlackMarket ) )
+ #endif
+END_NETWORK_TABLE()
+
+
+LINK_ENTITY_TO_CLASS( cs_gamerules, CCSGameRulesProxy );
+IMPLEMENT_NETWORKCLASS_ALIASED( CSGameRulesProxy, DT_CSGameRulesProxy )
+
+
+#ifdef CLIENT_DLL
+ void RecvProxy_CSGameRules( const RecvProp *pProp, void **pOut, void *pData, int objectID )
+ {
+ CCSGameRules *pRules = CSGameRules();
+ Assert( pRules );
+ *pOut = pRules;
+ }
+
+ BEGIN_RECV_TABLE( CCSGameRulesProxy, DT_CSGameRulesProxy )
+ RecvPropDataTable( "cs_gamerules_data", 0, 0, &REFERENCE_RECV_TABLE( DT_CSGameRules ), RecvProxy_CSGameRules )
+ END_RECV_TABLE()
+#else
+ void* SendProxy_CSGameRules( const SendProp *pProp, const void *pStructBase, const void *pData, CSendProxyRecipients *pRecipients, int objectID )
+ {
+ CCSGameRules *pRules = CSGameRules();
+ Assert( pRules );
+ return pRules;
+ }
+
+ BEGIN_SEND_TABLE( CCSGameRulesProxy, DT_CSGameRulesProxy )
+ SendPropDataTable( "cs_gamerules_data", 0, &REFERENCE_SEND_TABLE( DT_CSGameRules ), SendProxy_CSGameRules )
+ END_SEND_TABLE()
+#endif
+
+
+
+ConVar ammo_50AE_max( "ammo_50AE_max", "35", FCVAR_REPLICATED );
+ConVar ammo_762mm_max( "ammo_762mm_max", "90", FCVAR_REPLICATED );
+ConVar ammo_556mm_max( "ammo_556mm_max", "90", FCVAR_REPLICATED );
+ConVar ammo_556mm_box_max( "ammo_556mm_box_max", "200", FCVAR_REPLICATED );
+ConVar ammo_338mag_max( "ammo_338mag_max", "30", FCVAR_REPLICATED );
+ConVar ammo_9mm_max( "ammo_9mm_max", "120", FCVAR_REPLICATED );
+ConVar ammo_buckshot_max( "ammo_buckshot_max", "32", FCVAR_REPLICATED );
+ConVar ammo_45acp_max( "ammo_45acp_max", "100", FCVAR_REPLICATED );
+ConVar ammo_357sig_max( "ammo_357sig_max", "52", FCVAR_REPLICATED );
+ConVar ammo_57mm_max( "ammo_57mm_max", "100", FCVAR_REPLICATED );
+ConVar ammo_hegrenade_max( "ammo_hegrenade_max", "1", FCVAR_REPLICATED );
+ConVar ammo_flashbang_max( "ammo_flashbang_max", "2", FCVAR_REPLICATED );
+ConVar ammo_smokegrenade_max( "ammo_smokegrenade_max", "1", FCVAR_REPLICATED );
+
+//ConVar mp_dynamicpricing( "mp_dynamicpricing", "0", FCVAR_REPLICATED, "Enables or Disables the dynamic weapon prices" );
+
+
+extern ConVar sv_stopspeed;
+
+ConVar mp_buytime(
+ "mp_buytime",
+ "1.5",
+ FCVAR_REPLICATED,
+ "How many minutes after round start players can buy items for.",
+ true, 0.25,
+ false, 0 );
+
+ConVar mp_playerid(
+ "mp_playerid",
+ "0",
+ FCVAR_REPLICATED,
+ "Controls what information player see in the status bar: 0 all names; 1 team names; 2 no names",
+ true, 0,
+ true, 2 );
+
+ConVar mp_playerid_delay(
+ "mp_playerid_delay",
+ "0.5",
+ FCVAR_REPLICATED,
+ "Number of seconds to delay showing information in the status bar",
+ true, 0,
+ true, 1 );
+
+ConVar mp_playerid_hold(
+ "mp_playerid_hold",
+ "0.25",
+ FCVAR_REPLICATED,
+ "Number of seconds to keep showing old information in the status bar",
+ true, 0,
+ true, 1 );
+
+ConVar mp_round_restart_delay(
+ "mp_round_restart_delay",
+ "5.0",
+ FCVAR_REPLICATED,
+ "Number of seconds to delay before restarting a round after a win",
+ true, 0.0f,
+ true, 10.0f );
+
+ConVar sv_allowminmodels(
+ "sv_allowminmodels",
+ "1",
+ FCVAR_REPLICATED | FCVAR_NOTIFY,
+ "Allow or disallow the use of cl_minmodels on this server." );
+
+#ifdef CLIENT_DLL
+
+ConVar cl_autowepswitch(
+ "cl_autowepswitch",
+ "1",
+ FCVAR_ARCHIVE | FCVAR_USERINFO,
+ "Automatically switch to picked up weapons (if more powerful)" );
+
+ConVar cl_autohelp(
+ "cl_autohelp",
+ "1",
+ FCVAR_ARCHIVE | FCVAR_USERINFO,
+ "Auto-help" );
+
+#else
+
+ // longest the intermission can last, in seconds
+ #define MAX_INTERMISSION_TIME 120
+
+ // Falling damage stuff.
+ #define CS_PLAYER_FATAL_FALL_SPEED 1100 // approx 60 feet
+ #define CS_PLAYER_MAX_SAFE_FALL_SPEED 580 // approx 20 feet
+ #define CS_DAMAGE_FOR_FALL_SPEED ((float)100 / ( CS_PLAYER_FATAL_FALL_SPEED - CS_PLAYER_MAX_SAFE_FALL_SPEED )) // damage per unit per second.
+
+ // These entities are preserved each round restart. The rest are removed and recreated.
+ static const char *s_PreserveEnts[] =
+ {
+ "ai_network",
+ "ai_hint",
+ "cs_gamerules",
+ "cs_team_manager",
+ "cs_player_manager",
+ "env_soundscape",
+ "env_soundscape_proxy",
+ "env_soundscape_triggerable",
+ "env_sun",
+ "env_wind",
+ "env_fog_controller",
+ "func_brush",
+ "func_wall",
+ "func_buyzone",
+ "func_illusionary",
+ "func_hostage_rescue",
+ "func_bomb_target",
+ "infodecal",
+ "info_projecteddecal",
+ "info_node",
+ "info_target",
+ "info_node_hint",
+ "info_player_counterterrorist",
+ "info_player_terrorist",
+ "info_map_parameters",
+ "keyframe_rope",
+ "move_rope",
+ "info_ladder",
+ "player",
+ "point_viewcontrol",
+ "scene_manager",
+ "shadow_control",
+ "sky_camera",
+ "soundent",
+ "trigger_soundscape",
+ "viewmodel",
+ "predicted_viewmodel",
+ "worldspawn",
+ "point_devshot_camera",
+ "", // END Marker
+ };
+
+
+ // --------------------------------------------------------------------------------------------------- //
+ // Voice helper
+ // --------------------------------------------------------------------------------------------------- //
+
+ class CVoiceGameMgrHelper : public IVoiceGameMgrHelper
+ {
+ public:
+ virtual bool CanPlayerHearPlayer( CBasePlayer *pListener, CBasePlayer *pTalker, bool &bProximity )
+ {
+ // Dead players can only be heard by other dead team mates
+ if ( pTalker->IsAlive() == false )
+ {
+ if ( pListener->IsAlive() == false )
+ return ( pListener->InSameTeam( pTalker ) );
+
+ return false;
+ }
+
+ return ( pListener->InSameTeam( pTalker ) );
+ }
+ };
+ CVoiceGameMgrHelper g_VoiceGameMgrHelper;
+ IVoiceGameMgrHelper *g_pVoiceGameMgrHelper = &g_VoiceGameMgrHelper;
+
+
+
+ // --------------------------------------------------------------------------------------------------- //
+ // Globals.
+ // --------------------------------------------------------------------------------------------------- //
+
+ // NOTE: the indices here must match TEAM_TERRORIST, TEAM_CT, TEAM_SPECTATOR, etc.
+ const char *sTeamNames[] =
+ {
+ "Unassigned",
+ "Spectator",
+ "TERRORIST",
+ "CT"
+ };
+
+ extern ConVar mp_maxrounds;
+
+ ConVar mp_startmoney(
+ "mp_startmoney",
+ "800",
+ FCVAR_REPLICATED | FCVAR_NOTIFY,
+ "amount of money each player gets when they reset",
+ true, 800,
+ true, 16000 );
+
+ ConVar mp_roundtime(
+ "mp_roundtime",
+ "2.5",
+ FCVAR_REPLICATED | FCVAR_NOTIFY,
+ "How many minutes each round takes.",
+ true, 1, // min value
+ true, 9 // max value
+ );
+
+ ConVar mp_freezetime(
+ "mp_freezetime",
+ "6",
+ FCVAR_REPLICATED | FCVAR_NOTIFY,
+ "how many seconds to keep players frozen when the round starts",
+ true, 0, // min value
+ true, 60 // max value
+ );
+
+ ConVar mp_c4timer(
+ "mp_c4timer",
+ "45",
+ FCVAR_REPLICATED | FCVAR_NOTIFY,
+ "how long from when the C4 is armed until it blows",
+ true, 10, // min value
+ true, 90 // max value
+ );
+
+ ConVar mp_limitteams(
+ "mp_limitteams",
+ "2",
+ FCVAR_REPLICATED | FCVAR_NOTIFY,
+ "Max # of players 1 team can have over another (0 disables check)",
+ true, 0, // min value
+ true, 30 // max value
+ );
+
+ ConVar mp_tkpunish(
+ "mp_tkpunish",
+ "0",
+ FCVAR_REPLICATED,
+ "Will a TK'er be punished in the next round? {0=no, 1=yes}" );
+
+ ConVar mp_autokick(
+ "mp_autokick",
+ "1",
+ FCVAR_REPLICATED,
+ "Kick idle/team-killing players" );
+
+ ConVar mp_spawnprotectiontime(
+ "mp_spawnprotectiontime",
+ "5",
+ FCVAR_REPLICATED,
+ "Kick players who team-kill within this many seconds of a round restart." );
+
+ ConVar mp_humanteam(
+ "mp_humanteam",
+ "any",
+ FCVAR_REPLICATED,
+ "Restricts human players to a single team {any, CT, T}" );
+
+ ConVar mp_ignore_round_win_conditions(
+ "mp_ignore_round_win_conditions",
+ "0",
+ FCVAR_REPLICATED,
+ "Ignore conditions which would end the current round");
+
+ ConCommand EndRound( "endround", &CCSGameRules::EndRound, "End the current round.", FCVAR_CHEAT );
+
+
+ // --------------------------------------------------------------------------------------------------- //
+ // Global helper functions.
+ // --------------------------------------------------------------------------------------------------- //
+
+ void InitBodyQue(void)
+ {
+ // FIXME: Make this work
+ }
+
+
+ Vector DropToGround(
+ CBaseEntity *pMainEnt,
+ const Vector &vPos,
+ const Vector &vMins,
+ const Vector &vMaxs )
+ {
+ trace_t trace;
+ UTIL_TraceHull( vPos, vPos + Vector( 0, 0, -500 ), vMins, vMaxs, MASK_SOLID, pMainEnt, COLLISION_GROUP_NONE, &trace );
+ return trace.endpos;
+ }
+
+
+ //-----------------------------------------------------------------------------
+ // Purpose: This function can be used to find a valid placement location for an entity.
+ // Given an origin to start looking from and a minimum radius to place the entity at,
+ // it will sweep out a circle around vOrigin and try to find a valid spot (on the ground)
+ // where mins and maxs will fit.
+ // Input : *pMainEnt - Entity to place
+ // &vOrigin - Point to search around
+ // fRadius - Radius to search within
+ // nTries - Number of tries to attempt
+ // &mins - mins of the Entity
+ // &maxs - maxs of the Entity
+ // &outPos - Return point
+ // Output : Returns true and fills in outPos if it found a spot.
+ //-----------------------------------------------------------------------------
+ bool EntityPlacementTest( CBaseEntity *pMainEnt, const Vector &vOrigin, Vector &outPos, bool bDropToGround )
+ {
+ // This function moves the box out in each dimension in each step trying to find empty space like this:
+ //
+ // X
+ // X X
+ // Step 1: X Step 2: XXX Step 3: XXXXX
+ // X X
+ // X
+ //
+
+ Vector mins, maxs;
+ pMainEnt->CollisionProp()->WorldSpaceAABB( &mins, &maxs );
+ mins -= pMainEnt->GetAbsOrigin();
+ maxs -= pMainEnt->GetAbsOrigin();
+
+ // Put some padding on their bbox.
+ float flPadSize = 5;
+ Vector vTestMins = mins - Vector( flPadSize, flPadSize, flPadSize );
+ Vector vTestMaxs = maxs + Vector( flPadSize, flPadSize, flPadSize );
+
+ // First test the starting origin.
+ if ( UTIL_IsSpaceEmpty( pMainEnt, vOrigin + vTestMins, vOrigin + vTestMaxs ) )
+ {
+ if ( bDropToGround )
+ {
+ outPos = DropToGround( pMainEnt, vOrigin, vTestMins, vTestMaxs );
+ }
+ else
+ {
+ outPos = vOrigin;
+ }
+ return true;
+ }
+
+ Vector vDims = vTestMaxs - vTestMins;
+
+ // Keep branching out until we get too far.
+ int iCurIteration = 0;
+ int nMaxIterations = 15;
+
+ int offset = 0;
+ do
+ {
+ for ( int iDim=0; iDim < 3; iDim++ )
+ {
+ float flCurOffset = offset * vDims[iDim];
+
+ for ( int iSign=0; iSign < 2; iSign++ )
+ {
+ Vector vBase = vOrigin;
+ vBase[iDim] += (iSign*2-1) * flCurOffset;
+
+ if ( UTIL_IsSpaceEmpty( pMainEnt, vBase + vTestMins, vBase + vTestMaxs ) )
+ {
+ // Ensure that there is a clear line of sight from the spawnpoint entity to the actual spawn point.
+ // (Useful for keeping things from spawning behind walls near a spawn point)
+ trace_t tr;
+ UTIL_TraceLine( vOrigin, vBase, MASK_SOLID, pMainEnt, COLLISION_GROUP_NONE, &tr );
+
+ if ( tr.fraction != 1.0 )
+ {
+ continue;
+ }
+
+ if ( bDropToGround )
+ outPos = DropToGround( pMainEnt, vBase, vTestMins, vTestMaxs );
+ else
+ outPos = vBase;
+
+ return true;
+ }
+ }
+ }
+
+ ++offset;
+ } while ( iCurIteration++ < nMaxIterations );
+
+ // Warning( "EntityPlacementTest for ent %d:%s failed!\n", pMainEnt->entindex(), pMainEnt->GetClassname() );
+ return false;
+ }
+
+ int UTIL_HumansInGame( bool ignoreSpectators )
+ {
+ int iCount = 0;
+
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CCSPlayer *entity = CCSPlayer::Instance( i );
+
+ if ( entity && !FNullEnt( entity->edict() ) )
+ {
+ if ( FStrEq( entity->GetPlayerName(), "" ) )
+ continue;
+
+ if ( FBitSet( entity->GetFlags(), FL_FAKECLIENT ) )
+ continue;
+
+ if ( ignoreSpectators && entity->GetTeamNumber() != TEAM_TERRORIST && entity->GetTeamNumber() != TEAM_CT )
+ continue;
+
+ if ( ignoreSpectators && entity->State_Get() == STATE_PICKINGCLASS )
+ continue;
+
+ iCount++;
+ }
+ }
+
+ return iCount;
+ }
+
+ // --------------------------------------------------------------------------------------------------- //
+ // CCSGameRules implementation.
+ // --------------------------------------------------------------------------------------------------- //
+
+ CCSGameRules::CCSGameRules()
+ {
+ m_iRoundTime = 0;
+ m_iRoundWinStatus = WINNER_NONE;
+ m_iFreezeTime = 0;
+
+ m_fRoundStartTime = 0;
+ m_bAllowWeaponSwitch = true;
+ m_bFreezePeriod = true;
+ m_iNumTerrorist = m_iNumCT = 0; // number of players per team
+ m_flRestartRoundTime = 0.1f; // restart first round as soon as possible
+ m_iNumSpawnableTerrorist = m_iNumSpawnableCT = 0;
+ m_bFirstConnected = false;
+ m_bCompleteReset = false;
+ m_iAccountTerrorist = m_iAccountCT = 0;
+ m_iNumCTWins = 0;
+ m_iNumTerroristWins = 0;
+ m_iNumConsecutiveCTLoses = 0;
+ m_iNumConsecutiveTerroristLoses = 0;
+ m_bTargetBombed = false;
+ m_bBombDefused = false;
+ m_iTotalRoundsPlayed = -1;
+ m_iUnBalancedRounds = 0;
+ m_flGameStartTime = 0;
+ m_iHostagesRemaining = 0;
+ m_bLevelInitialized = false;
+ m_bLogoMap = false;
+ m_tmNextPeriodicThink = 0;
+
+ m_bMapHasBombTarget = false;
+ m_bMapHasRescueZone = false;
+
+ m_iSpawnPointCount_Terrorist = 0;
+ m_iSpawnPointCount_CT = 0;
+
+ m_bTCantBuy = false;
+ m_bCTCantBuy = false;
+ m_bMapHasBuyZone = false;
+
+ m_iLoserBonus = 0;
+
+ m_iHostagesRescued = 0;
+ m_iHostagesTouched = 0;
+ m_flNextHostageAnnouncement = 0.0f;
+
+ //=============================================================================
+ // HPE_BEGIN
+ // [dwenger] Reset rescue-related achievement values
+ //=============================================================================
+
+ // [tj] reset flawless and lossless round related flags
+ m_bNoTerroristsKilled = true;
+ m_bNoCTsKilled = true;
+ m_bNoTerroristsDamaged = true;
+ m_bNoCTsDamaged = true;
+ m_pFirstKill = NULL;
+ m_firstKillTime = 0;
+
+ // [menglish] Reset fun fact values
+ m_pFirstBlood = NULL;
+ m_firstBloodTime = 0;
+
+ m_bCanDonateWeapons = true;
+
+ // [dwenger] Reset rescue-related achievement values
+ m_pLastRescuer = NULL;
+ m_iNumRescuers = 0;
+
+ m_hostageWasInjured = false;
+ m_hostageWasKilled = false;
+
+ m_pFunFactManager = new CCSFunFactMgr();
+ m_pFunFactManager->Init();
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ m_iHaveEscaped = 0;
+ m_bMapHasEscapeZone = false;
+ m_iNumEscapers = 0;
+ m_iNumEscapeRounds = 0;
+
+ m_iMapHasVIPSafetyZone = 0;
+ m_pVIP = NULL;
+ m_iConsecutiveVIP = 0;
+
+ m_bMapHasBombZone = false;
+ m_bBombDropped = false;
+ m_bBombPlanted = false;
+ m_pLastBombGuy = NULL;
+
+ m_bAllowWeaponSwitch = true;
+
+ m_flNextHostageAnnouncement = gpGlobals->curtime; // asap.
+
+ ReadMultiplayCvars();
+
+ m_pPrices = NULL;
+ m_bBlackMarket = false;
+ m_bDontUploadStats = false;
+
+ // Create the team managers
+ for ( int i = 0; i < ARRAYSIZE( sTeamNames ); i++ )
+ {
+ CTeam *pTeam = static_cast<CTeam*>(CreateEntityByName( "cs_team_manager" ));
+ pTeam->Init( sTeamNames[i], i );
+
+ g_Teams.AddToTail( pTeam );
+ }
+
+ if ( filesystem->FileExists( UTIL_VarArgs( "maps/cfg/%s.cfg", STRING(gpGlobals->mapname) ) ) )
+ {
+ // Execute a map specific cfg file - as in Day of Defeat
+ // Map names cannot contain quotes or control characters so this is safe but silly that we have to do it.
+ engine->ServerCommand( UTIL_VarArgs( "exec \"%s.cfg\" */maps\n", STRING(gpGlobals->mapname) ) );
+ engine->ServerExecute();
+ }
+
+#ifndef CLIENT_DLL
+ // stats
+
+ if ( g_flGameStatsUpdateTime == 0.0f )
+ {
+ memset( g_iWeaponPurchases, 0, sizeof( g_iWeaponPurchases) );
+ memset( g_iTerroristVictories, 0, sizeof( g_iTerroristVictories) );
+ memset( g_iCounterTVictories, 0, sizeof( g_iTerroristVictories) );
+ g_flGameStatsUpdateTime = CS_GAME_STATS_UPDATE; //Next update is between 22 and 24 hours.
+ }
+#endif
+ }
+
+ void CCSGameRules::AddPricesToTable( weeklyprice_t prices )
+ {
+ int iIndex = m_StringTableBlackMarket->FindStringIndex( "blackmarket_prices" );
+
+ if ( iIndex == INVALID_STRING_INDEX )
+ {
+ m_StringTableBlackMarket->AddString( CBaseEntity::IsServer(), "blackmarket_prices", sizeof( weeklyprice_t), &prices );
+ }
+ else
+ {
+ m_StringTableBlackMarket->SetStringUserData( iIndex, sizeof( weeklyprice_t), &prices );
+ }
+
+ SetBlackMarketPrices( false );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ CCSGameRules::~CCSGameRules()
+ {
+ // Note, don't delete each team since they are in the gEntList and will
+ // automatically be deleted from there, instead.
+ g_Teams.Purge();
+ if( m_pFunFactManager )
+ {
+ delete m_pFunFactManager;
+ }
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ void CCSGameRules::UpdateClientData( CBasePlayer *player )
+ {
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: TF2 Specific Client Commands
+ // Input :
+ // Output :
+ //-----------------------------------------------------------------------------
+ bool CCSGameRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
+ {
+ CCSPlayer *pPlayer = ToCSPlayer( pEdict );
+
+ if ( FStrEq( args[0], "changeteam" ) )
+ {
+ return true;
+ }
+ else if ( FStrEq( args[0], "nextmap" ) )
+ {
+ if ( pPlayer->m_iNextTimeCheck < gpGlobals->curtime )
+ {
+ char szNextMap[32];
+
+ if ( nextlevel.GetString() && *nextlevel.GetString() )
+ {
+ Q_strncpy( szNextMap, nextlevel.GetString(), sizeof( szNextMap ) );
+ }
+ else
+ {
+ GetNextLevelName( szNextMap, sizeof( szNextMap ) );
+ }
+
+ ClientPrint( pPlayer, HUD_PRINTTALK, "#game_nextmap", szNextMap);
+
+ pPlayer->m_iNextTimeCheck = gpGlobals->curtime + 1;
+ }
+ return true;
+ }
+ else if( pPlayer->ClientCommand( args ) )
+ {
+ return true;
+ }
+ else if( BaseClass::ClientCommand( pEdict, args ) )
+ {
+ return true;
+ }
+ else if ( TheBots->ServerCommand( args.GetCommandString() ) )
+ {
+ return true;
+ }
+ else
+ {
+ return TheBots->ClientCommand( pPlayer, args );
+ }
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Player has just spawned. Equip them.
+ //-----------------------------------------------------------------------------
+ void CCSGameRules::ClientCommandKeyValues( edict_t *pEntity, KeyValues *pKeyValues )
+ {
+ CCSPlayer *pPlayer = dynamic_cast< CCSPlayer * >( CBaseEntity::Instance( pEntity ) );
+ if ( pPlayer )
+ {
+ char const *pszCommand = pKeyValues->GetName();
+ if ( pszCommand && pszCommand[0] )
+ {
+ if ( FStrEq( pszCommand, "ClanTagChanged" ) )
+ {
+ pPlayer->SetClanTag( pKeyValues->GetString( "tag", "" ) );
+
+ const char *teamName = "UNKNOWN";
+ if ( pPlayer->GetTeam() )
+ {
+ teamName = pPlayer->GetTeam()->GetName();
+ }
+ UTIL_LogPrintf("\"%s<%i><%s><%s>\" triggered \"clantag\" (value \"%s\")\n",
+ pPlayer->GetPlayerName(),
+ pPlayer->GetUserID(),
+ pPlayer->GetNetworkIDString(),
+ teamName,
+ pKeyValues->GetString( "tag", "unknown" ) );
+ }
+ }
+ }
+
+ BaseClass::ClientCommandKeyValues( pEntity, pKeyValues );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Player has just spawned. Equip them.
+ //-----------------------------------------------------------------------------
+ void CCSGameRules::PlayerSpawn( CBasePlayer *pBasePlayer )
+ {
+ CCSPlayer *pPlayer = ToCSPlayer( pBasePlayer );
+ if ( !pPlayer )
+ Error( "PlayerSpawn" );
+
+ if ( pPlayer->State_Get() != STATE_ACTIVE )
+ return;
+
+ pPlayer->EquipSuit();
+
+ bool addDefault = true;
+
+ CBaseEntity *pWeaponEntity = NULL;
+ while ( ( pWeaponEntity = gEntList.FindEntityByClassname( pWeaponEntity, "game_player_equip" )) != NULL )
+ {
+ if ( addDefault )
+ {
+ // remove all our weapons and armor before touching the first game_player_equip
+ pPlayer->RemoveAllItems( true );
+ }
+ pWeaponEntity->Touch( pPlayer );
+ addDefault = false;
+ }
+
+
+ if ( addDefault || pPlayer->m_bIsVIP )
+ pPlayer->GiveDefaultItems();
+ }
+
+ void CCSGameRules::BroadcastSound( const char *sound, int team )
+ {
+ CBroadcastRecipientFilter filter;
+ filter.MakeReliable();
+
+ if( team != -1 )
+ {
+ filter.RemoveAllRecipients();
+ filter.AddRecipientsByTeam( GetGlobalTeam(team) );
+ }
+
+ UserMessageBegin ( filter, "SendAudio" );
+ WRITE_STRING( sound );
+ MessageEnd();
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Player has just spawned. Equip them.
+ //-----------------------------------------------------------------------------
+
+ // return a multiplier that should adjust the damage done by a blast at position vecSrc to something at the position
+ // vecEnd. This will take into account the density of an entity that blocks the line of sight from one position to
+ // the other.
+ //
+ // this algorithm was taken from the HL2 version of RadiusDamage.
+ float CCSGameRules::GetExplosionDamageAdjustment(Vector & vecSrc, Vector & vecEnd, CBaseEntity *pEntityToIgnore)
+ {
+ float retval = 0.0;
+ trace_t tr;
+
+ UTIL_TraceLine(vecSrc, vecEnd, MASK_SHOT, pEntityToIgnore, COLLISION_GROUP_NONE, &tr);
+ if (tr.fraction == 1.0)
+ {
+ retval = 1.0;
+ }
+ else if (!(tr.DidHitWorld()) && (tr.m_pEnt != NULL) && (tr.m_pEnt != pEntityToIgnore) && (tr.m_pEnt->GetOwnerEntity() != pEntityToIgnore))
+ {
+ // if we didn't hit world geometry perhaps there's still damage to be done here.
+
+ CBaseEntity *blockingEntity = tr.m_pEnt;
+
+ // check to see if this part of the player is visible if entities are ignored.
+ UTIL_TraceLine(vecSrc, vecEnd, CONTENTS_SOLID, NULL, COLLISION_GROUP_NONE, &tr);
+
+ if (tr.fraction == 1.0)
+ {
+ if ((blockingEntity != NULL) && (blockingEntity->VPhysicsGetObject() != NULL))
+ {
+ int nMaterialIndex = blockingEntity->VPhysicsGetObject()->GetMaterialIndex();
+
+ float flDensity;
+ float flThickness;
+ float flFriction;
+ float flElasticity;
+
+ physprops->GetPhysicsProperties( nMaterialIndex, &flDensity,
+ &flThickness, &flFriction, &flElasticity );
+
+ const float DENSITY_ABSORB_ALL_DAMAGE = 3000.0;
+ float scale = flDensity / DENSITY_ABSORB_ALL_DAMAGE;
+ if ((scale >= 0.0) && (scale < 1.0))
+ {
+ retval = 1.0 - scale;
+ }
+ else if (scale < 0.0)
+ {
+ // should never happen, but just in case.
+ retval = 1.0;
+ }
+ }
+ else
+ {
+ retval = 0.75; // we're blocked by something that isn't an entity with a physics module or world geometry, just cut damage in half for now.
+ }
+ }
+ }
+
+ return retval;
+ }
+
+ // returns the percentage of the player that is visible from the given point in the world.
+ // return value is between 0 and 1.
+ float CCSGameRules::GetAmountOfEntityVisible(Vector & vecSrc, CBaseEntity *entity)
+ {
+ float retval = 0.0;
+
+ const float damagePercentageChest = 0.40;
+ const float damagePercentageHead = 0.20;
+ const float damagePercentageFeet = 0.20;
+ const float damagePercentageRightSide = 0.10;
+ const float damagePercentageLeftSide = 0.10;
+
+ if (!(entity->IsPlayer()))
+ {
+ // the entity is not a player, so the damage is all or nothing.
+ Vector vecTarget;
+ vecTarget = entity->BodyTarget(vecSrc, false);
+
+ return GetExplosionDamageAdjustment(vecSrc, vecTarget, entity);
+ }
+
+ CCSPlayer *player = (CCSPlayer *)entity;
+
+ // check what parts of the player we can see from this point and modify the return value accordingly.
+ float chestHeightFromFeet;
+
+ float armDistanceFromChest = HalfHumanWidth;
+
+ // calculate positions of various points on the target player's body
+ Vector vecFeet = player->GetAbsOrigin();
+
+ Vector vecChest = player->BodyTarget(vecSrc, false);
+ chestHeightFromFeet = vecChest.z - vecFeet.z; // compute the distance from the chest to the feet. (this accounts for ducking and the like)
+
+ Vector vecHead = player->GetAbsOrigin();
+ vecHead.z += HumanHeight;
+
+ Vector vecRightFacing;
+ AngleVectors(player->GetAbsAngles(), NULL, &vecRightFacing, NULL);
+
+ vecRightFacing.NormalizeInPlace();
+ vecRightFacing = vecRightFacing * armDistanceFromChest;
+
+ Vector vecLeftSide = player->GetAbsOrigin();
+ vecLeftSide.x -= vecRightFacing.x;
+ vecLeftSide.y -= vecRightFacing.y;
+ vecLeftSide.z += chestHeightFromFeet;
+
+ Vector vecRightSide = player->GetAbsOrigin();
+ vecRightSide.x += vecRightFacing.x;
+ vecRightSide.y += vecRightFacing.y;
+ vecRightSide.z += chestHeightFromFeet;
+
+ // check chest
+ float damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecChest, entity);
+ retval += (damagePercentageChest * damageAdjustment);
+
+ // check top of head
+ damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecHead, entity);
+ retval += (damagePercentageHead * damageAdjustment);
+
+ // check feet
+ damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecFeet, entity);
+ retval += (damagePercentageFeet * damageAdjustment);
+
+ // check left "edge"
+ damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecLeftSide, entity);
+ retval += (damagePercentageLeftSide * damageAdjustment);
+
+ // check right "edge"
+ damageAdjustment = GetExplosionDamageAdjustment(vecSrc, vecRightSide, entity);
+ retval += (damagePercentageRightSide * damageAdjustment);
+
+ return retval;
+ }
+
+ void CCSGameRules::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, CBaseEntity * pEntityIgnore )
+ {
+ RadiusDamage( info, vecSrcIn, flRadius, iClassIgnore, false );
+ }
+
+ // Add the ability to ignore the world trace
+ void CCSGameRules::RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, bool bIgnoreWorld )
+ {
+ CBaseEntity *pEntity = NULL;
+ trace_t tr;
+ float falloff, damagePercentage;
+ Vector vecSpot;
+ Vector vecToTarget;
+ Vector vecEndPos;
+
+ //=============================================================================
+ // HPE_BEGIN:
+ //=============================================================================
+
+ // [tj] The number of enemy players this explosion killed
+ int numberOfEnemyPlayersKilledByThisExplosion = 0;
+
+ // [tj] who we award the achievement to if enough players are killed
+ CCSPlayer* pCSExplosionAttacker = ToCSPlayer(info.GetAttacker());
+
+ // [tj] used to determine which achievement to award for sufficient kills
+ CBaseEntity* pInflictor = info.GetInflictor();
+ bool isGrenade = pInflictor && V_strcmp(pInflictor->GetClassname(), "hegrenade_projectile") == 0;
+ bool isBomb = pInflictor && V_strcmp(pInflictor->GetClassname(), "planted_c4") == 0;
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+
+ vecEndPos.Init();
+
+ Vector vecSrc = vecSrcIn;
+
+ damagePercentage = 1.0;
+
+ if ( flRadius )
+ falloff = info.GetDamage() / flRadius;
+ else
+ falloff = 1.0;
+
+ int bInWater = (UTIL_PointContents ( vecSrc ) & MASK_WATER) ? true : false;
+
+ vecSrc.z += 1;// in case grenade is lying on the ground
+
+ // iterate on all entities in the vicinity.
+ for ( CEntitySphereQuery sphere( vecSrc, flRadius ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
+ {
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] We have to save whether or not the player is killed so we don't give credit
+ // for pre-dead players.
+ //=============================================================================
+ bool wasAliveBeforeExplosion = false;
+ CCSPlayer* pCSExplosionVictim = ToCSPlayer(pEntity);
+ if (pCSExplosionVictim)
+ {
+ wasAliveBeforeExplosion = pCSExplosionVictim->IsAlive();
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ if ( pEntity->m_takedamage != DAMAGE_NO )
+ {
+ // UNDONE: this should check a damage mask, not an ignore
+ if ( iClassIgnore != CLASS_NONE && pEntity->Classify() == iClassIgnore )
+ {// houndeyes don't hurt other houndeyes with their attack
+ continue;
+ }
+
+ // blasts don't travel into or out of water
+ if ( !bIgnoreWorld )
+ {
+ if (bInWater && pEntity->GetWaterLevel() == 0)
+ continue;
+ if (!bInWater && pEntity->GetWaterLevel() == 3)
+ continue;
+ }
+
+ // radius damage can only be blocked by the world
+ vecSpot = pEntity->BodyTarget( vecSrc );
+
+ bool bHit = false;
+
+ if( bIgnoreWorld )
+ {
+ vecEndPos = vecSpot;
+ bHit = true;
+ }
+ else
+ {
+ // get the percentage of the target entity that is visible from the
+ // explosion position.
+ damagePercentage = GetAmountOfEntityVisible(vecSrc, pEntity);
+ if (damagePercentage > 0.0)
+ {
+ vecEndPos = vecSpot;
+
+ bHit = true;
+ }
+ }
+
+ if ( bHit )
+ {
+ // the explosion can 'see' this entity, so hurt them!
+ //vecToTarget = ( vecSrc - vecEndPos );
+ vecToTarget = ( vecEndPos - vecSrc );
+
+ // use a Gaussian function to describe the damage falloff over distance, with flRadius equal to 3 * sigma
+ // this results in the following values:
+ //
+ // Range Fraction Damage
+ // 0.0 100%
+ // 0.1 96%
+ // 0.2 84%
+ // 0.3 67%
+ // 0.4 49%
+ // 0.5 32%
+ // 0.6 20%
+ // 0.7 11%
+ // 0.8 6%
+ // 0.9 3%
+ // 1.0 1%
+
+ float fDist = vecToTarget.Length();
+ float fSigma = flRadius / 3.0f; // flRadius specifies 3rd standard deviation (0.0111 damage at this range)
+ float fGaussianFalloff = exp(-fDist * fDist / (2.0f * fSigma * fSigma));
+ float flAdjustedDamage = info.GetDamage() * fGaussianFalloff * damagePercentage;
+
+ if ( flAdjustedDamage > 0 )
+ {
+ CTakeDamageInfo adjustedInfo = info;
+ adjustedInfo.SetDamage( flAdjustedDamage );
+
+ Vector dir = vecToTarget;
+ VectorNormalize( dir );
+
+ // If we don't have a damage force, manufacture one
+ if ( adjustedInfo.GetDamagePosition() == vec3_origin || adjustedInfo.GetDamageForce() == vec3_origin )
+ {
+ CalculateExplosiveDamageForce( &adjustedInfo, dir, vecSrc, 1.5 /* explosion scale! */ );
+ }
+ else
+ {
+ // Assume the force passed in is the maximum force. Decay it based on falloff.
+ float flForce = adjustedInfo.GetDamageForce().Length() * falloff;
+ adjustedInfo.SetDamageForce( dir * flForce );
+ adjustedInfo.SetDamagePosition( vecSrc );
+ }
+
+ Vector vecTarget;
+ vecTarget = pEntity->BodyTarget(vecSrc, false);
+
+ UTIL_TraceLine(vecSrc, vecTarget, MASK_SHOT, NULL, COLLISION_GROUP_NONE, &tr);
+
+ // blasts always hit chest
+ tr.hitgroup = HITGROUP_GENERIC;
+
+ if (tr.fraction != 1.0)
+ {
+ // this has to be done to make breakable glass work.
+ ClearMultiDamage( );
+ pEntity->DispatchTraceAttack( adjustedInfo, dir, &tr );
+ ApplyMultiDamage();
+ }
+ else
+ {
+ pEntity->TakeDamage( adjustedInfo );
+ }
+
+ // Now hit all triggers along the way that respond to damage...
+ pEntity->TraceAttackToTriggers( adjustedInfo, vecSrc, vecEndPos, dir );
+ //=============================================================================
+ // HPE_BEGIN:
+ // [sbodenbender] Increment grenade damage stat
+ //=============================================================================
+ if (pCSExplosionVictim && pCSExplosionAttacker && isGrenade)
+ {
+ CCS_GameStats.IncrementStat(pCSExplosionAttacker, CSSTAT_GRENADE_DAMAGE, static_cast<int>(adjustedInfo.GetDamage()));
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ }
+ }
+ }
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Count up victims of area of effect damage for achievement purposes
+ //=============================================================================
+
+ if (pCSExplosionVictim)
+ {
+ //If the bomb is exploding, set the attacker to the planter (we can't put this in the CTakeDamageInfo, since
+ //players aren't supposed to get credit for bomb kills)
+ if (isBomb)
+ {
+ CPlantedC4* bomb = static_cast<CPlantedC4*> (pInflictor);
+ if (bomb)
+ {
+ pCSExplosionAttacker = bomb->GetPlanter();
+ }
+ }
+
+ //Count check to make sure we killed an enemy player
+ if( pCSExplosionAttacker &&
+ !pCSExplosionVictim->IsAlive() &&
+ wasAliveBeforeExplosion &&
+ pCSExplosionVictim->GetTeamNumber() != pCSExplosionAttacker->GetTeamNumber())
+ {
+ numberOfEnemyPlayersKilledByThisExplosion++;
+ }
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ }
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] //Depending on which type of explosion it was, award the appropriate achievement.
+ //=============================================================================
+
+ if (pCSExplosionAttacker && isGrenade && numberOfEnemyPlayersKilledByThisExplosion >= AchievementConsts::GrenadeMultiKill_MinKills)
+ {
+ pCSExplosionAttacker->AwardAchievement(CSGrenadeMultikill);
+ pCSExplosionAttacker->CheckMaxGrenadeKills(numberOfEnemyPlayersKilledByThisExplosion);
+
+ }
+ if (pCSExplosionAttacker && isBomb && numberOfEnemyPlayersKilledByThisExplosion >= AchievementConsts::BombMultiKill_MinKills)
+ {
+ pCSExplosionAttacker->AwardAchievement(CSBombMultikill);
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ // Input : *pVictim -
+ // *pKiller -
+ // *pInflictor -
+ //-----------------------------------------------------------------------------
+ void CCSGameRules::DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info )
+ {
+ // Work out what killed the player, and send a message to all clients about it
+ const char *killer_weapon_name = "world"; // by default, the player is killed by the world
+ int killer_ID = 0;
+
+ // Find the killer & the scorer
+ CBaseEntity *pInflictor = info.GetInflictor();
+ CBaseEntity *pKiller = info.GetAttacker();
+ CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor );
+ CCSPlayer *pCSVictim = (CCSPlayer*)(pVictim);
+
+ bool bHeadshot = false;
+
+ if ( pScorer ) // Is the killer a client?
+ {
+ killer_ID = pScorer->GetUserID();
+
+ if( info.GetDamageType() & DMG_HEADSHOT )
+ {
+ //to enable drawing the headshot icon as well as the weapon icon,
+ bHeadshot = true;
+ }
+
+ if ( pInflictor )
+ {
+ if ( pInflictor == pScorer )
+ {
+ // If the inflictor is the killer, then it must be their current weapon doing the damage
+ if ( pScorer->GetActiveWeapon() )
+ {
+ killer_weapon_name = pScorer->GetActiveWeapon()->GetClassname(); //GetDeathNoticeName();
+ }
+ }
+ else
+ {
+ killer_weapon_name = STRING( pInflictor->m_iClassname ); // it's just that easy
+ }
+ }
+ }
+ else
+ {
+ killer_weapon_name = STRING( pInflictor->m_iClassname );
+ }
+
+ // strip the NPC_* or weapon_* from the inflictor's classname
+ if ( strncmp( killer_weapon_name, "weapon_", 7 ) == 0 )
+ {
+ killer_weapon_name += 7;
+ }
+ else if ( strncmp( killer_weapon_name, "NPC_", 8 ) == 0 )
+ {
+ killer_weapon_name += 8;
+ }
+ else if ( strncmp( killer_weapon_name, "func_", 5 ) == 0 )
+ {
+ killer_weapon_name += 5;
+ }
+ else if( strncmp( killer_weapon_name, "hegrenade", 9 ) == 0 ) //"hegrenade_projectile"
+ {
+ killer_weapon_name = "hegrenade";
+ }
+ else if( strncmp( killer_weapon_name, "flashbang", 9 ) == 0 ) //"flashbang_projectile"
+ {
+ killer_weapon_name = "flashbang";
+ }
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "player_death" );
+
+ if ( event )
+ {
+ event->SetInt("userid", pVictim->GetUserID() );
+ event->SetInt("attacker", killer_ID );
+ event->SetString("weapon", killer_weapon_name );
+ event->SetInt("headshot", bHeadshot ? 1 : 0 );
+ event->SetInt("priority", bHeadshot ? 8 : 7 ); // HLTV event priority, not transmitted
+ if ( pCSVictim->GetDeathFlags() & CS_DEATH_DOMINATION )
+ {
+ event->SetInt( "dominated", 1 );
+ }
+ else if ( pCSVictim->GetDeathFlags() & CS_DEATH_REVENGE )
+ {
+ event->SetInt( "revenge", 1 );
+ }
+
+ gameeventmanager->FireEvent( event );
+ }
+ }
+
+ //=========================================================
+ //=========================================================
+ void CCSGameRules::PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info )
+ {
+ CBaseEntity *pInflictor = info.GetInflictor();
+ CBaseEntity *pKiller = info.GetAttacker();
+ CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor );
+ CCSPlayer *pCSVictim = (CCSPlayer *)pVictim;
+ CCSPlayer *pCSScorer = (CCSPlayer *)pScorer;
+
+ CCS_GameStats.PlayerKilled( pVictim, info );
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Flag the round as non-lossless for the appropriate team.
+ // [menglish] Set the death flags depending on a nemesis system
+ //=============================================================================
+
+ if (pVictim->GetTeamNumber() == TEAM_TERRORIST)
+ {
+ m_bNoTerroristsKilled = false;
+ m_bNoTerroristsDamaged = false;
+ }
+ if (pVictim->GetTeamNumber() == TEAM_CT)
+ {
+ m_bNoCTsKilled = false;
+ m_bNoCTsDamaged = false;
+ }
+
+ m_bCanDonateWeapons = false;
+
+ if ( m_pFirstKill == NULL && pCSScorer != pVictim )
+ {
+ m_pFirstKill = pCSScorer;
+ m_firstKillTime = gpGlobals->curtime - m_fRoundStartTime;
+ }
+
+ // determine if this kill affected a nemesis relationship
+ int iDeathFlags = 0;
+ if ( pScorer )
+ {
+ CCS_GameStats.CalculateOverkill( pCSScorer, pCSVictim);
+ CCS_GameStats.CalcDominationAndRevenge( pCSScorer, pCSVictim, &iDeathFlags );
+ }
+ pCSVictim->SetDeathFlags( iDeathFlags );
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ // If we're killed by the C4, we do a subset of BaseClass::PlayerKilled()
+ // Specifically, we shouldn't lose any points or show death notices, to match goldsrc
+ if ( Q_strcmp(pKiller->GetClassname(), "planted_c4" ) == 0 )
+ {
+ // dvsents2: uncomment when removing all FireTargets
+ // variant_t value;
+ // g_EventQueue.AddEvent( "game_playerdie", "Use", value, 0, pVictim, pVictim );
+ FireTargets( "game_playerdie", pVictim, pVictim, USE_TOGGLE, 0 );
+ }
+ else
+ {
+ BaseClass::PlayerKilled( pVictim, info );
+ }
+
+ // check for team-killing, and give monetary rewards/penalties
+ // Find the killer & the scorer
+ if ( !pScorer )
+ return;
+
+ if ( IPointsForKill( pScorer, pVictim ) < 0 )
+ {
+ // team-killer!
+ pCSScorer->AddAccount( -3300 );
+ ++pCSScorer->m_iTeamKills;
+ pCSScorer->m_bJustKilledTeammate = true;
+
+ ClientPrint( pCSScorer, HUD_PRINTCENTER, "#Killed_Teammate" );
+ if ( mp_autokick.GetBool() )
+ {
+ char strTeamKills[64];
+ Q_snprintf( strTeamKills, sizeof( strTeamKills ), "%d", pCSScorer->m_iTeamKills );
+ ClientPrint( pCSScorer, HUD_PRINTCONSOLE, "#Game_teammate_kills", strTeamKills ); // this includes a " of 3" in it
+
+ if ( pCSScorer->m_iTeamKills >= 3 )
+ {
+ ClientPrint( pCSScorer, HUD_PRINTCONSOLE, "#Banned_For_Killing_Teammates" );
+ engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", pCSScorer->GetUserID() ) );
+ }
+ else if ( mp_spawnprotectiontime.GetInt() > 0 && GetRoundElapsedTime() < mp_spawnprotectiontime.GetInt() )
+ {
+ ClientPrint( pCSScorer, HUD_PRINTCONSOLE, "#Banned_For_Killing_Teammates" );
+ engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", pCSScorer->GetUserID() ) );
+ }
+ }
+
+ if ( !(pCSScorer->m_iDisplayHistoryBits & DHF_FRIEND_KILLED) )
+ {
+ pCSScorer->m_iDisplayHistoryBits |= DHF_FRIEND_KILLED;
+ pCSScorer->HintMessage( "#Hint_careful_around_teammates", false );
+ }
+ }
+ else
+ {
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Added a check to make sure we don't get money for suicides.
+ //=============================================================================
+ if (pCSScorer != pCSVictim)
+ {
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ if ( pCSVictim->IsVIP() )
+ {
+ pCSScorer->HintMessage( "#Hint_reward_for_killing_vip", true );
+ pCSScorer->AddAccount( 2500 );
+ }
+ else
+ {
+ pCSScorer->AddAccount( 300 );
+ }
+ }
+
+ if ( !(pCSScorer->m_iDisplayHistoryBits & DHF_ENEMY_KILLED) )
+ {
+ pCSScorer->m_iDisplayHistoryBits |= DHF_ENEMY_KILLED;
+ pCSScorer->HintMessage( "#Hint_win_round_by_killing_enemy", false );
+ }
+ }
+ }
+
+
+ void CCSGameRules::InitDefaultAIRelationships()
+ {
+ // Allocate memory for default relationships
+ CBaseCombatCharacter::AllocateDefaultRelationships();
+
+ // --------------------------------------------------------------
+ // First initialize table so we can report missing relationships
+ // --------------------------------------------------------------
+ int i, j;
+ for (i=0;i<NUM_AI_CLASSES;i++)
+ {
+ for (j=0;j<NUM_AI_CLASSES;j++)
+ {
+ // By default all relationships are neutral of priority zero
+ CBaseCombatCharacter::SetDefaultRelationship( (Class_T)i, (Class_T)j, D_NU, 0 );
+ }
+ }
+ }
+
+ //------------------------------------------------------------------------------
+ // Purpose : Return classify text for classify type
+ //------------------------------------------------------------------------------
+ const char *CCSGameRules::AIClassText(int classType)
+ {
+ switch (classType)
+ {
+ case CLASS_NONE: return "CLASS_NONE";
+ case CLASS_PLAYER: return "CLASS_PLAYER";
+ default: return "MISSING CLASS in ClassifyText()";
+ }
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: When gaining new technologies in TF, prevent auto switching if we
+ // receive a weapon during the switch
+ // Input : *pPlayer -
+ // *pWeapon -
+ // Output : Returns true on success, false on failure.
+ //-----------------------------------------------------------------------------
+ bool CCSGameRules::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon )
+ {
+ bool bIsBeingGivenItem = false;
+ CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer );
+ if ( pCSPlayer && pCSPlayer->IsBeingGivenItem() )
+ bIsBeingGivenItem = true;
+
+ if ( pPlayer->GetActiveWeapon() && pPlayer->IsNetClient() && !bIsBeingGivenItem )
+ {
+ // Player has an active item, so let's check cl_autowepswitch.
+ const char *cl_autowepswitch = engine->GetClientConVarValue( engine->IndexOfEdict( pPlayer->edict() ), "cl_autowepswitch" );
+ if ( cl_autowepswitch && atoi( cl_autowepswitch ) <= 0 )
+ {
+ return false;
+ }
+ }
+
+ if ( pPlayer->IsBot() && !bIsBeingGivenItem )
+ {
+ return false;
+ }
+
+ if ( !GetAllowWeaponSwitch() )
+ {
+ return false;
+ }
+
+ return BaseClass::FShouldSwitchWeapon( pPlayer, pWeapon );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ // Input : allow -
+ //-----------------------------------------------------------------------------
+ void CCSGameRules::SetAllowWeaponSwitch( bool allow )
+ {
+ m_bAllowWeaponSwitch = allow;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ // Output : Returns true on success, false on failure.
+ //-----------------------------------------------------------------------------
+ bool CCSGameRules::GetAllowWeaponSwitch()
+ {
+ return m_bAllowWeaponSwitch;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ // Input : *pPlayer -
+ // Output : const char
+ //-----------------------------------------------------------------------------
+ const char *CCSGameRules::SetDefaultPlayerTeam( CBasePlayer *pPlayer )
+ {
+ Assert( pPlayer );
+ return BaseClass::SetDefaultPlayerTeam( pPlayer );
+ }
+
+
+ void CCSGameRules::LevelInitPreEntity()
+ {
+ BaseClass::LevelInitPreEntity();
+
+ // TODO for CZ-style hostages: TheHostageChatter->Precache();
+ }
+
+
+ void CCSGameRules::LevelInitPostEntity()
+ {
+ BaseClass::LevelInitPostEntity();
+
+ m_bLevelInitialized = false; // re-count CT and T start spots now that they exist
+
+ // Figure out from the entities in the map what kind of map this is (bomb run, prison escape, etc).
+ CheckMapConditions();
+ }
+
+ INetworkStringTable *g_StringTableBlackMarket = NULL;
+
+ void CCSGameRules::CreateCustomNetworkStringTables( void )
+ {
+ m_StringTableBlackMarket = g_StringTableBlackMarket;
+
+ if ( 0 )//mp_dynamicpricing.GetBool() )
+ {
+ m_bBlackMarket = BlackMarket_DownloadPrices();
+
+ if ( m_bBlackMarket == false )
+ {
+ Msg( "ERROR: mp_dynamicpricing set to 1 but couldn't download the price list!\n" );
+ }
+ }
+ else
+ {
+ m_bBlackMarket = false;
+ SetBlackMarketPrices( true );
+ }
+ }
+
+ float CCSGameRules::FlPlayerFallDamage( CBasePlayer *pPlayer )
+ {
+ float fFallVelocity = pPlayer->m_Local.m_flFallVelocity - CS_PLAYER_MAX_SAFE_FALL_SPEED;
+ float fallDamage = fFallVelocity * CS_DAMAGE_FOR_FALL_SPEED * 1.25;
+
+ if ( fallDamage > 0.0f )
+ {
+ // let the bots know
+ IGameEvent * event = gameeventmanager->CreateEvent( "player_falldamage" );
+ if ( event )
+ {
+ event->SetInt( "userid", pPlayer->GetUserID() );
+ event->SetFloat( "damage", fallDamage );
+ event->SetInt( "priority", 4 ); // HLTV event priority, not transmitted
+
+ gameeventmanager->FireEvent( event );
+ }
+ }
+
+ return fallDamage;
+ }
+
+
+ void CCSGameRules::ClientDisconnected( edict_t *pClient )
+ {
+ BaseClass::ClientDisconnected( pClient );
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Clear domination data when a player disconnects
+ //=============================================================================
+
+ CCSPlayer *pPlayer = ToCSPlayer( GetContainingEntity( pClient ) );
+ if ( pPlayer )
+ {
+ pPlayer->RemoveNemesisRelationships();
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+
+ CheckWinConditions();
+ }
+
+
+ // Called when game rules are destroyed by CWorld
+ void CCSGameRules::LevelShutdown()
+ {
+ int iLevelIndex = GetCSLevelIndex( STRING( gpGlobals->mapname ) );
+
+ if ( iLevelIndex != -1 )
+ {
+ g_iTerroristVictories[iLevelIndex] += m_iNumTerroristWins;
+ g_iCounterTVictories[iLevelIndex] += m_iNumCTWins;
+ }
+
+ BaseClass::LevelShutdown();
+ }
+
+
+ //---------------------------------------------------------------------------------------------------
+ /**
+ * Check if the scenario has been won/lost.
+ * Return true if the scenario is over, false if the scenario is still in progress
+ */
+ bool CCSGameRules::CheckWinConditions( void )
+ {
+ if ( mp_ignore_round_win_conditions.GetBool() )
+ {
+ return false;
+ }
+
+ // If a winner has already been determined.. then get the heck out of here
+ if (m_iRoundWinStatus != WINNER_NONE)
+ {
+ // still check if we lost players to where we need to do a full reset next round...
+ int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT;
+ InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT );
+
+ bool bNeededPlayers = false;
+ NeededPlayersCheck( bNeededPlayers );
+
+ return true;
+ }
+
+ // Initialize the player counts..
+ int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT;
+ InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT );
+
+
+ /***************************** OTHER PLAYER's CHECK *********************************************************/
+ bool bNeededPlayers = false;
+ if ( NeededPlayersCheck( bNeededPlayers ) )
+ return false;
+
+ /****************************** ASSASINATION/VIP SCENARIO CHECK *******************************************************/
+ if ( VIPRoundEndCheck( bNeededPlayers ) )
+ return true;
+
+ /****************************** PRISON ESCAPE CHECK *******************************************************/
+ if ( PrisonRoundEndCheck() )
+ return true;
+
+
+ /****************************** BOMB CHECK ********************************************************/
+ if ( BombRoundEndCheck( bNeededPlayers ) )
+ return true;
+
+
+ /***************************** TEAM EXTERMINATION CHECK!! *********************************************************/
+ // CounterTerrorists won by virture of elimination
+ if ( TeamExterminationCheck( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT, bNeededPlayers ) )
+ return true;
+
+
+ /******************************** HOSTAGE RESCUE CHECK ******************************************************/
+ if ( HostageRescueRoundEndCheck( bNeededPlayers ) )
+ return true;
+
+ // scenario not won - still in progress
+ return false;
+ }
+
+
+ bool CCSGameRules::NeededPlayersCheck( bool &bNeededPlayers )
+ {
+ // We needed players to start scoring
+ // Do we have them now?
+ if( !m_iNumSpawnableTerrorist || !m_iNumSpawnableCT )
+ {
+ Msg( "Game will not start until both teams have players.\n" );
+ UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#Game_scoring" );
+ bNeededPlayers = true;
+
+ m_bFirstConnected = false;
+ }
+
+ if ( !m_bFirstConnected && m_iNumSpawnableTerrorist && m_iNumSpawnableCT )
+ {
+ // Start the round immediately when the first person joins
+ // UTIL_LogPrintf( "World triggered \"Game_Commencing\"\n" );
+
+ m_bFreezePeriod = false; //Make sure we are not on the FreezePeriod.
+ m_bCompleteReset = true;
+
+ TerminateRound( 3.0f, Game_Commencing );
+ m_bFirstConnected = true;
+ return true;
+ }
+
+ return false;
+ }
+
+
+ void CCSGameRules::InitializePlayerCounts(
+ int &NumAliveTerrorist,
+ int &NumAliveCT,
+ int &NumDeadTerrorist,
+ int &NumDeadCT
+ )
+ {
+ NumAliveTerrorist = NumAliveCT = NumDeadCT = NumDeadTerrorist = 0;
+ m_iNumTerrorist = m_iNumCT = m_iNumSpawnableTerrorist = m_iNumSpawnableCT = 0;
+ m_iHaveEscaped = 0;
+
+ // Count how many dead players there are on each team.
+ for ( int iTeam=0; iTeam < GetNumberOfTeams(); iTeam++ )
+ {
+ CTeam *pTeam = GetGlobalTeam( iTeam );
+
+ for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
+ {
+ CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) );
+ Assert( pPlayer );
+ if ( !pPlayer )
+ continue;
+
+ Assert( pPlayer->GetTeamNumber() == pTeam->GetTeamNumber() );
+
+ switch ( pTeam->GetTeamNumber() )
+ {
+ case TEAM_CT:
+ m_iNumCT++;
+
+ if ( pPlayer->State_Get() != STATE_PICKINGCLASS )
+ m_iNumSpawnableCT++;
+
+ if ( pPlayer->m_lifeState != LIFE_ALIVE )
+ NumDeadCT++;
+ else
+ NumAliveCT++;
+
+ break;
+
+ case TEAM_TERRORIST:
+ m_iNumTerrorist++;
+
+ if ( pPlayer->State_Get() != STATE_PICKINGCLASS )
+ m_iNumSpawnableTerrorist++;
+
+ if ( pPlayer->m_lifeState != LIFE_ALIVE )
+ NumDeadTerrorist++;
+ else
+ NumAliveTerrorist++;
+
+ // Check to see if this guy escaped.
+ if ( pPlayer->m_bEscaped == true )
+ m_iHaveEscaped++;
+
+ break;
+ }
+ }
+ }
+ }
+
+ bool CCSGameRules::HostageRescueRoundEndCheck( bool bNeededPlayers )
+ {
+ // Check to see if 50% of the hostages have been rescued.
+ CHostage* hostage = NULL;
+
+ int iNumHostages = g_Hostages.Count();
+ int iNumLeftToRescue = 0;
+ int i;
+
+ for ( i=0; i<iNumHostages; i++ )
+ {
+ hostage = g_Hostages[i];
+
+ if ( hostage->m_iHealth > 0 && !hostage->IsRescued() ) // We've found a live hostage. don't end the round
+ iNumLeftToRescue++;
+ }
+
+ m_iHostagesRemaining = iNumLeftToRescue;
+
+ if ( (iNumLeftToRescue == 0) && (iNumHostages > 0) )
+ {
+ if ( m_iHostagesRescued >= (iNumHostages * 0.5) )
+ {
+ m_iAccountCT += 2500;
+
+ if ( !bNeededPlayers )
+ {
+ m_iNumCTWins ++;
+ // Update the clients team score
+ UpdateTeamScores();
+ }
+ CCS_GameStats.Event_AllHostagesRescued();
+ // tell the bots all the hostages have been rescued
+ IGameEvent * event = gameeventmanager->CreateEvent( "hostage_rescued_all" );
+ if ( event )
+ {
+ gameeventmanager->FireEvent( event );
+ }
+
+ TerminateRound( mp_round_restart_delay.GetFloat(), All_Hostages_Rescued );
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ bool CCSGameRules::PrisonRoundEndCheck()
+ {
+ //MIKETODO: get this working when working on prison escape
+ /*
+ if (m_bMapHasEscapeZone == true)
+ {
+ float flEscapeRatio;
+
+ flEscapeRatio = (float) m_iHaveEscaped / (float) m_iNumEscapers;
+
+ if (flEscapeRatio >= m_flRequiredEscapeRatio)
+ {
+ BroadcastSound( "Event.TERWin" );
+ m_iAccountTerrorist += 3150;
+
+ if ( !bNeededPlayers )
+ {
+ m_iNumTerroristWins ++;
+ // Update the clients team score
+ UpdateTeamScores();
+ }
+ EndRoundMessage( "#Terrorists_Escaped", Terrorists_Escaped );
+ TerminateRound( mp_round_restart_delay.GetFloat(), WINNER_TER );
+ return;
+ }
+ else if ( NumAliveTerrorist == 0 && flEscapeRatio < m_flRequiredEscapeRatio)
+ {
+ BroadcastSound( "Event.CTWin" );
+ m_iAccountCT += (1 - flEscapeRatio) * 3500; // CTs are rewarded based on how many terrorists have escaped...
+
+ if ( !bNeededPlayers )
+ {
+ m_iNumCTWins++;
+ // Update the clients team score
+ UpdateTeamScores();
+ }
+ EndRoundMessage( "#CTs_PreventEscape", CTs_PreventEscape );
+ TerminateRound( mp_round_restart_delay.GetFloat(), WINNER_CT );
+ return;
+ }
+
+ else if ( NumAliveTerrorist == 0 && NumDeadTerrorist != 0 && m_iNumSpawnableCT > 0 )
+ {
+ BroadcastSound( "Event.CTWin" );
+ m_iAccountCT += (1 - flEscapeRatio) * 3250; // CTs are rewarded based on how many terrorists have escaped...
+
+ if ( !bNeededPlayers )
+ {
+ m_iNumCTWins++;
+ // Update the clients team score
+ UpdateTeamScores();
+ }
+ EndRoundMessage( "#Escaping_Terrorists_Neutralized", Escaping_Terrorists_Neutralized );
+ TerminateRound( mp_round_restart_delay.GetFloat(), WINNER_CT );
+ return;
+ }
+ // else return;
+ }
+ */
+
+ return false;
+ }
+
+
+ bool CCSGameRules::VIPRoundEndCheck( bool bNeededPlayers )
+ {
+ if (m_iMapHasVIPSafetyZone != 1)
+ return false;
+
+ if (m_pVIP == NULL)
+ return false;
+
+ if (m_pVIP->m_bEscaped == true)
+ {
+ m_iAccountCT += 3500;
+
+ if ( !bNeededPlayers )
+ {
+ m_iNumCTWins ++;
+ // Update the clients team score
+ UpdateTeamScores();
+ }
+
+ //MIKETODO: get this working when working on VIP scenarios
+ /*
+ MessageBegin( MSG_SPEC, SVC_DIRECTOR );
+ WRITE_BYTE ( 9 ); // command length in bytes
+ WRITE_BYTE ( DRC_CMD_EVENT ); // VIP rescued
+ WRITE_SHORT( ENTINDEX(m_pVIP->edict()) ); // index number of primary entity
+ WRITE_SHORT( 0 ); // index number of secondary entity
+ WRITE_LONG( 15 | DRC_FLAG_FINAL); // eventflags (priority and flags)
+ MessageEnd();
+ */
+
+ // tell the bots the VIP got out
+ IGameEvent * event = gameeventmanager->CreateEvent( "vip_escaped" );
+ if ( event )
+ {
+ event->SetInt( "userid", m_pVIP->GetUserID() );
+ event->SetInt( "priority", 9 );
+ gameeventmanager->FireEvent( event );
+ }
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [menglish] If the VIP has escaped award him an MVP
+ //=============================================================================
+
+ m_pVIP->IncrementNumMVPs( CSMVP_UNDEFINED );
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ TerminateRound( mp_round_restart_delay.GetFloat(), VIP_Escaped );
+ return true;
+ }
+ else if ( m_pVIP->m_lifeState == LIFE_DEAD ) // The VIP is dead
+ {
+ m_iAccountTerrorist += 3250;
+
+ if ( !bNeededPlayers )
+ {
+ m_iNumTerroristWins ++;
+ // Update the clients team score
+ UpdateTeamScores();
+ }
+
+ // tell the bots the VIP was killed
+ IGameEvent * event = gameeventmanager->CreateEvent( "vip_killed" );
+ if ( event )
+ {
+ event->SetInt( "userid", m_pVIP->GetUserID() );
+ event->SetInt( "priority", 9 );
+ gameeventmanager->FireEvent( event );
+ }
+
+ TerminateRound( mp_round_restart_delay.GetFloat(), VIP_Assassinated );
+ return true;
+ }
+
+ return false;
+ }
+
+
+ bool CCSGameRules::BombRoundEndCheck( bool bNeededPlayers )
+ {
+ // Check to see if the bomb target was hit or the bomb defused.. if so, then let's end the round!
+ if ( ( m_bTargetBombed == true ) && ( m_bMapHasBombTarget == true ) )
+ {
+ m_iAccountTerrorist += 3500;
+
+ if ( !bNeededPlayers )
+ {
+ m_iNumTerroristWins ++;
+ // Update the clients team score
+ UpdateTeamScores();
+ }
+
+ TerminateRound( mp_round_restart_delay.GetFloat(), Target_Bombed );
+ return true;
+ }
+ else
+ if ( ( m_bBombDefused == true ) && ( m_bMapHasBombTarget == true ) )
+ {
+ m_iAccountCT += 3250;
+
+ m_iAccountTerrorist += 800; // give the T's a little bonus for planting the bomb even though it was defused.
+
+ if ( !bNeededPlayers )
+ {
+ m_iNumCTWins++;
+ // Update the clients team score
+ UpdateTeamScores();
+ }
+
+ TerminateRound( mp_round_restart_delay.GetFloat(), Bomb_Defused );
+ return true;
+ }
+
+ return false;
+ }
+
+
+ bool CCSGameRules::TeamExterminationCheck(
+ int NumAliveTerrorist,
+ int NumAliveCT,
+ int NumDeadTerrorist,
+ int NumDeadCT,
+ bool bNeededPlayers
+ )
+ {
+ if ( ( m_iNumCT > 0 && m_iNumSpawnableCT > 0 ) && ( m_iNumTerrorist > 0 && m_iNumSpawnableTerrorist > 0 ) )
+ {
+ if ( NumAliveTerrorist == 0 && NumDeadTerrorist != 0 && m_iNumSpawnableCT > 0 )
+ {
+ bool nowin = false;
+
+ for ( int iGrenade=0; iGrenade < g_PlantedC4s.Count(); iGrenade++ )
+ {
+ CPlantedC4 *pC4 = g_PlantedC4s[iGrenade];
+
+ if ( pC4->IsBombActive() )
+ nowin = true;
+ }
+
+ if ( !nowin )
+ {
+ if ( m_bMapHasBombTarget )
+ m_iAccountCT += 3250;
+ else
+ m_iAccountCT += 3000;
+
+ if ( !bNeededPlayers )
+ {
+ m_iNumCTWins++;
+ // Update the clients team score
+ UpdateTeamScores();
+ }
+
+ TerminateRound( mp_round_restart_delay.GetFloat(), CTs_Win );
+ return true;
+ }
+ }
+
+ // Terrorists WON
+ if ( NumAliveCT == 0 && NumDeadCT != 0 && m_iNumSpawnableTerrorist > 0 )
+ {
+ if ( m_bMapHasBombTarget )
+ m_iAccountTerrorist += 3250;
+ else
+ m_iAccountTerrorist += 3000;
+
+ if ( !bNeededPlayers )
+ {
+ m_iNumTerroristWins++;
+ // Update the clients team score
+ UpdateTeamScores();
+ }
+
+ TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Win );
+ return true;
+ }
+ }
+ else if ( NumAliveCT == 0 && NumAliveTerrorist == 0 )
+ {
+ TerminateRound( mp_round_restart_delay.GetFloat(), Round_Draw );
+ return true;
+ }
+
+ return false;
+ }
+
+
+ void CCSGameRules::PickNextVIP()
+ {
+ // MIKETODO: work on this when getting VIP maps running.
+ /*
+ if (IsVIPQueueEmpty() != true)
+ {
+ // Remove the current VIP from his VIP status and make him a regular CT.
+ if (m_pVIP != NULL)
+ ResetCurrentVIP();
+
+ for (int i = 0; i <= 4; i++)
+ {
+ if (VIPQueue[i] != NULL)
+ {
+ m_pVIP = VIPQueue[i];
+ m_pVIP->MakeVIP();
+
+ VIPQueue[i] = NULL; // remove this player from the VIP queue
+ StackVIPQueue(); // and re-organize the queue
+ m_iConsecutiveVIP = 0;
+ return;
+ }
+ }
+ }
+ else if (m_iConsecutiveVIP >= 3) // If it's been the same VIP for 3 rounds already.. then randomly pick a new one
+ {
+ m_iLastPick++;
+
+ if (m_iLastPick > m_iNumCT)
+ m_iLastPick = 1;
+
+ int iCount = 1;
+
+ CBaseEntity* pPlayer = NULL;
+ CBasePlayer* player = NULL;
+ CBasePlayer* pLastPlayer = NULL;
+
+ pPlayer = UTIL_FindEntityByClassname ( pPlayer, "player" );
+ while ( (pPlayer != NULL) && (!FNullEnt(pPlayer->edict())) )
+ {
+ if ( !(pPlayer->pev->flags & FL_DORMANT) )
+ {
+ player = GetClassPtr((CBasePlayer *)pPlayer->pev);
+
+ if ( (player->m_iTeam == CT) && (iCount == m_iLastPick) )
+ {
+ if ( (player == m_pVIP) && (pLastPlayer != NULL) )
+ player = pLastPlayer;
+
+ // Remove the current VIP from his VIP status and make him a regular CT.
+ if (m_pVIP != NULL)
+ ResetCurrentVIP();
+
+ player->MakeVIP();
+ m_iConsecutiveVIP = 0;
+
+ return;
+ }
+ else if ( player->m_iTeam == CT )
+ iCount++;
+
+ if ( player->m_iTeam != SPECTATOR )
+ pLastPlayer = player;
+ }
+ pPlayer = UTIL_FindEntityByClassname ( pPlayer, "player" );
+ }
+ }
+ else if (m_pVIP == NULL) // There is no VIP and there is no one waiting to be the VIP.. therefore just pick the first CT player we can find.
+ {
+ CBaseEntity* pPlayer = NULL;
+ CBasePlayer* player = NULL;
+
+ pPlayer = UTIL_FindEntityByClassname ( pPlayer, "player" );
+ while ( (pPlayer != NULL) && (!FNullEnt(pPlayer->edict())) )
+ {
+ if ( pPlayer->pev->flags != FL_DORMANT )
+ {
+ player = GetClassPtr((CBasePlayer *)pPlayer->pev);
+
+ if ( player->m_iTeam == CT )
+ {
+ player->MakeVIP();
+ m_iConsecutiveVIP = 0;
+ return;
+ }
+ }
+ pPlayer = UTIL_FindEntityByClassname ( pPlayer, "player" );
+ }
+ }
+ */
+ }
+
+
+ void CCSGameRules::ReadMultiplayCvars()
+ {
+ m_iRoundTime = (int)(mp_roundtime.GetFloat() * 60);
+ m_iFreezeTime = mp_freezetime.GetInt();
+ }
+
+
+ void CCSGameRules::RestartRound()
+ {
+#if defined( REPLAY_ENABLED )
+ if ( g_pReplay )
+ {
+ // Write replay and stop recording if appropriate
+ if ( g_pReplay->IsRecording() )
+ {
+ g_pReplay->SV_EndRecordingSession();
+ }
+
+ int nActivePlayerCount = m_iNumTerrorist + m_iNumCT;
+ if ( nActivePlayerCount && g_pReplay->SV_ShouldBeginRecording( false ) )
+ {
+ // Tell the replay manager that it should begin recording the new round as soon as possible
+ g_pReplay->SV_GetContext()->GetSessionRecorder()->StartRecording();
+ }
+ }
+#endif
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Notify players that the round is about to be reset
+ //=============================================================================
+ for ( int clientIndex = 1; clientIndex <= gpGlobals->maxClients; clientIndex++ )
+ {
+ CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( clientIndex );
+ if(pPlayer)
+ {
+ pPlayer->OnPreResetRound();
+ }
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ if ( !IsFinite( gpGlobals->curtime ) )
+ {
+ Warning( "NaN curtime in RestartRound\n" );
+ gpGlobals->curtime = 0.0f;
+ }
+
+ int i;
+
+ m_iTotalRoundsPlayed++;
+
+ //ClearBodyQue();
+
+ // Hardlock the player accelaration to 5.0
+ //CVAR_SET_FLOAT( "sv_accelerate", 5.0 );
+ //CVAR_SET_FLOAT( "sv_friction", 4.0 );
+ //CVAR_SET_FLOAT( "sv_stopspeed", 75 );
+
+ sv_stopspeed.SetValue( 75.0f );
+
+ // Tabulate the number of players on each team.
+ int NumDeadCT, NumDeadTerrorist, NumAliveTerrorist, NumAliveCT;
+ InitializePlayerCounts( NumAliveTerrorist, NumAliveCT, NumDeadTerrorist, NumDeadCT );
+
+ m_bBombDropped = false;
+ m_bBombPlanted = false;
+
+ if ( GetHumanTeam() != TEAM_UNASSIGNED )
+ {
+ MoveHumansToHumanTeam();
+ }
+
+ /*************** AUTO-BALANCE CODE *************/
+ if ( mp_autoteambalance.GetInt() != 0 &&
+ (m_iUnBalancedRounds >= 1) )
+ {
+ if ( GetHumanTeam() == TEAM_UNASSIGNED )
+ {
+ BalanceTeams();
+ }
+ }
+
+ if ( ((m_iNumSpawnableCT - m_iNumSpawnableTerrorist) >= 2) ||
+ ((m_iNumSpawnableTerrorist - m_iNumSpawnableCT) >= 2) )
+ {
+ m_iUnBalancedRounds++;
+ }
+ else
+ {
+ m_iUnBalancedRounds = 0;
+ }
+
+ // Warn the players of an impending auto-balance next round...
+ if ( mp_autoteambalance.GetInt() != 0 &&
+ (m_iUnBalancedRounds == 1) )
+ {
+ if ( GetHumanTeam() == TEAM_UNASSIGNED )
+ {
+ UTIL_ClientPrintAll( HUD_PRINTCENTER,"#Auto_Team_Balance_Next_Round");
+ }
+ }
+
+ /*************** AUTO-BALANCE CODE *************/
+
+ if ( m_bCompleteReset )
+ {
+ // bounds check
+ if ( mp_timelimit.GetInt() < 0 )
+ {
+ mp_timelimit.SetValue( 0 );
+ }
+ m_flGameStartTime = gpGlobals->curtime;
+ if ( !IsFinite( m_flGameStartTime.Get() ) )
+ {
+ Warning( "Trying to set a NaN game start time\n" );
+ m_flGameStartTime.GetForModify() = 0.0f;
+ }
+
+ // Reset total # of rounds played
+ m_iTotalRoundsPlayed = 0;
+
+ // Reset score info
+ m_iNumTerroristWins = 0;
+ m_iNumCTWins = 0;
+ m_iNumConsecutiveTerroristLoses = 0;
+ m_iNumConsecutiveCTLoses = 0;
+
+
+ // Reset team scores
+ UpdateTeamScores();
+
+
+ // Reset the player stats
+ for ( i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CCSPlayer *pPlayer = CCSPlayer::Instance( i );
+
+ if ( pPlayer && !FNullEnt( pPlayer->edict() ) )
+ pPlayer->Reset();
+ }
+ }
+
+ m_bFreezePeriod = true;
+
+ ReadMultiplayCvars();
+
+ // Check to see if there's a mapping info paramater entity
+ if ( g_pMapInfo )
+ {
+ switch ( g_pMapInfo->m_iBuyingStatus )
+ {
+ case 0:
+ m_bCTCantBuy = false;
+ m_bTCantBuy = false;
+ Msg( "EVERYONE CAN BUY!\n" );
+ break;
+
+ case 1:
+ m_bCTCantBuy = false;
+ m_bTCantBuy = true;
+ Msg( "Only CT's can buy!!\n" );
+ break;
+
+ case 2:
+ m_bCTCantBuy = true;
+ m_bTCantBuy = false;
+ Msg( "Only T's can buy!!\n" );
+ break;
+
+ case 3:
+ m_bCTCantBuy = true;
+ m_bTCantBuy = true;
+ Msg( "No one can buy!!\n" );
+ break;
+
+ default:
+ m_bCTCantBuy = false;
+ m_bTCantBuy = false;
+ break;
+ }
+ }
+ else
+ {
+ // by default everyone can buy
+ m_bCTCantBuy = false;
+ m_bTCantBuy = false;
+ }
+
+
+ // Check to see if this map has a bomb target in it
+
+ if ( gEntList.FindEntityByClassname( NULL, "func_bomb_target" ) )
+ {
+ m_bMapHasBombTarget = true;
+ m_bMapHasBombZone = true;
+ }
+ else if ( gEntList.FindEntityByClassname( NULL, "info_bomb_target" ) )
+ {
+ m_bMapHasBombTarget = true;
+ m_bMapHasBombZone = false;
+ }
+ else
+ {
+ m_bMapHasBombTarget = false;
+ m_bMapHasBombZone = false;
+ }
+
+ // Check to see if this map has hostage rescue zones
+
+ if ( gEntList.FindEntityByClassname( NULL, "func_hostage_rescue" ) )
+ m_bMapHasRescueZone = true;
+ else
+ m_bMapHasRescueZone = false;
+
+
+ // See if the map has func_buyzone entities
+ // Used by CBasePlayer::HandleSignals() to support maps without these entities
+
+ if ( gEntList.FindEntityByClassname( NULL, "func_buyzone" ) )
+ m_bMapHasBuyZone = true;
+ else
+ m_bMapHasBuyZone = false;
+
+
+ // GOOSEMAN : See if this map has func_escapezone entities
+ if ( gEntList.FindEntityByClassname( NULL, "func_escapezone" ) )
+ {
+ m_bMapHasEscapeZone = true;
+ m_iHaveEscaped = 0;
+ m_iNumEscapers = 0; // Will increase this later when we count how many Ts are starting
+ if (m_iNumEscapeRounds >= 3)
+ {
+ SwapAllPlayers();
+ m_iNumEscapeRounds = 0;
+ }
+
+ m_iNumEscapeRounds++; // Increment the number of rounds played... After 8 rounds, the players will do a whole sale switch..
+ }
+ else
+ m_bMapHasEscapeZone = false;
+
+ // Check to see if this map has VIP safety zones
+ if ( gEntList.FindEntityByClassname( NULL, "func_vip_safetyzone" ) )
+ {
+ PickNextVIP();
+ m_iConsecutiveVIP++;
+ m_iMapHasVIPSafetyZone = 1;
+ }
+ else
+ m_iMapHasVIPSafetyZone = 2;
+
+ // Update accounts based on number of hostages remaining..
+ int iRescuedHostageBonus = 0;
+
+ for ( int iHostage=0; iHostage < g_Hostages.Count(); iHostage++ )
+ {
+ CHostage *pHostage = g_Hostages[iHostage];
+
+ if( pHostage->IsRescuable() ) //Alive and not rescued
+ {
+ iRescuedHostageBonus += 150;
+ }
+
+ if ( iRescuedHostageBonus >= 2000 )
+ break;
+ }
+
+ //*******Catch up code by SupraFiend. Scale up the loser bonus when teams fall into losing streaks
+ if (m_iRoundWinStatus == WINNER_TER) // terrorists won
+ {
+ //check to see if they just broke a losing streak
+ if(m_iNumConsecutiveTerroristLoses > 1)
+ m_iLoserBonus = 1500;//this is the default losing bonus
+
+ m_iNumConsecutiveTerroristLoses = 0;//starting fresh
+ m_iNumConsecutiveCTLoses++;//increment the number of wins the CTs have had
+ }
+ else if (m_iRoundWinStatus == WINNER_CT) // CT Won
+ {
+ //check to see if they just broke a losing streak
+ if(m_iNumConsecutiveCTLoses > 1)
+ m_iLoserBonus = 1500;//this is the default losing bonus
+
+ m_iNumConsecutiveCTLoses = 0;//starting fresh
+ m_iNumConsecutiveTerroristLoses++;//increment the number of wins the Terrorists have had
+ }
+
+ //check if the losing team is in a losing streak & that the loser bonus hasen't maxed out.
+ if((m_iNumConsecutiveTerroristLoses > 1) && (m_iLoserBonus < 3000))
+ m_iLoserBonus += 500;//help out the team in the losing streak
+ else
+ if((m_iNumConsecutiveCTLoses > 1) && (m_iLoserBonus < 3000))
+ m_iLoserBonus += 500;//help out the team in the losing streak
+
+ // assign the wining and losing bonuses
+ if (m_iRoundWinStatus == WINNER_TER) // terrorists won
+ {
+ m_iAccountTerrorist += iRescuedHostageBonus;
+ m_iAccountCT += m_iLoserBonus;
+ }
+ else if (m_iRoundWinStatus == WINNER_CT) // CT Won
+ {
+ m_iAccountCT += iRescuedHostageBonus;
+ if (m_bMapHasEscapeZone == false) // only give them the bonus if this isn't an escape map
+ m_iAccountTerrorist += m_iLoserBonus;
+ }
+
+
+ //Update CT account based on number of hostages rescued
+ m_iAccountCT += m_iHostagesRescued * 750;
+
+
+ // Update individual players accounts and respawn players
+
+ //**********new code by SupraFiend
+ //##########code changed by MartinO
+ //the round time stamp must be set before players are spawned
+ m_fRoundStartTime = gpGlobals->curtime + m_iFreezeTime;
+
+ if ( !IsFinite( m_fRoundStartTime.Get() ) )
+ {
+ Warning( "Trying to set a NaN round start time\n" );
+ m_fRoundStartTime.GetForModify() = 0.0f;
+ }
+
+ //Adrian - No cash for anyone at first rounds! ( well, only the default. )
+ if ( m_bCompleteReset )
+ {
+ m_iAccountTerrorist = m_iAccountCT = 0; //No extra cash!.
+
+ //We are starting fresh. So it's like no one has ever won or lost.
+ m_iNumTerroristWins = 0;
+ m_iNumCTWins = 0;
+ m_iNumConsecutiveTerroristLoses = 0;
+ m_iNumConsecutiveCTLoses = 0;
+ m_iLoserBonus = 1400;
+ }
+
+ for ( i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
+
+ if ( !pPlayer )
+ continue;
+
+ pPlayer->m_iNumSpawns = 0;
+ pPlayer->m_bTeamChanged = false;
+
+ if ( pPlayer->GetTeamNumber() == TEAM_CT )
+ {
+ if (pPlayer->DoesPlayerGetRoundStartMoney())
+ {
+ pPlayer->AddAccount( m_iAccountCT );
+ }
+ }
+ else if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST )
+ {
+ m_iNumEscapers++; // Add another potential escaper to the mix!
+ if (pPlayer->DoesPlayerGetRoundStartMoney())
+ {
+ pPlayer->AddAccount( m_iAccountTerrorist );
+ }
+ }
+
+ // tricky, make players non solid while moving to their spawn points
+ if ( (pPlayer->GetTeamNumber() == TEAM_CT) || (pPlayer->GetTeamNumber() == TEAM_TERRORIST) )
+ {
+ pPlayer->AddSolidFlags( FSOLID_NOT_SOLID );
+ }
+ }
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Keep track of number of players per side and if they have the same uniform
+ //=============================================================================
+
+ int terroristUniform = -1;
+ bool allTerroristsWearingSameUniform = true;
+ int numberOfTerrorists = 0;
+ int ctUniform = -1;
+ bool allCtsWearingSameUniform = true;
+ int numberOfCts = 0;
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ // know respawn all players
+ for ( i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
+
+ if ( !pPlayer )
+ continue;
+
+ if ( pPlayer->GetTeamNumber() == TEAM_CT && pPlayer->PlayerClass() >= FIRST_CT_CLASS && pPlayer->PlayerClass() <= LAST_CT_CLASS )
+ {
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Increment CT count and check CT uniforms.
+ //=============================================================================
+
+ numberOfCts++;
+ if (ctUniform == -1)
+ {
+ ctUniform = pPlayer->PlayerClass();
+ }
+ else if (pPlayer->PlayerClass() != ctUniform)
+ {
+ allCtsWearingSameUniform = false;
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ pPlayer->RoundRespawn();
+ }
+
+ if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST && pPlayer->PlayerClass() >= FIRST_T_CLASS && pPlayer->PlayerClass() <= LAST_T_CLASS )
+ {
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Increment terrorist count and check terrorist uniforms
+ //=============================================================================
+
+ numberOfTerrorists++;
+ if (terroristUniform == -1)
+ {
+ terroristUniform = pPlayer->PlayerClass();
+ }
+ else if (pPlayer->PlayerClass() != terroristUniform)
+ {
+ allTerroristsWearingSameUniform = false;
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ pPlayer->RoundRespawn();
+ }
+ else
+ {
+ pPlayer->ObserverRoundRespawn();
+ }
+
+ if ( pPlayer->m_iAccount > pPlayer->m_iShouldHaveCash )
+ {
+ m_bDontUploadStats = true;
+ }
+ }
+
+ //=============================================================================
+ // HPE_BEGIN:
+ //=============================================================================
+
+ // [tj] Award same uniform achievement for qualifying teams
+ for ( i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
+
+ if ( !pPlayer )
+ continue;
+
+ if ( pPlayer->GetTeamNumber() == TEAM_CT && allCtsWearingSameUniform && numberOfCts >= AchievementConsts::SameUniform_MinPlayers)
+ {
+ pPlayer->AwardAchievement(CSSameUniform);
+ }
+
+ if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST && allTerroristsWearingSameUniform && numberOfTerrorists >= AchievementConsts::SameUniform_MinPlayers)
+ {
+ pPlayer->AwardAchievement(CSSameUniform);
+ }
+ }
+
+ // [menglish] reset per-round achievement variables for each player
+ for ( i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
+ if( pPlayer )
+ {
+ pPlayer->ResetRoundBasedAchievementVariables();
+ }
+ }
+
+ // [pfreese] Reset all round or match stats, depending on type of restart
+ if ( m_bCompleteReset )
+ {
+ CCS_GameStats.ResetAllStats();
+ CCS_GameStats.ResetPlayerClassMatchStats();
+ }
+ else
+ {
+ CCS_GameStats.ResetRoundStats();
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ // Respawn entities (glass, doors, etc..)
+ CleanUpMap();
+
+ // now run a tkpunish check, after the map has been cleaned up
+ for ( i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
+
+ if ( !pPlayer )
+ continue;
+
+ if ( pPlayer->GetTeamNumber() == TEAM_CT && pPlayer->PlayerClass() >= FIRST_CT_CLASS && pPlayer->PlayerClass() <= LAST_CT_CLASS )
+ {
+ pPlayer->CheckTKPunishment();
+ }
+ if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST && pPlayer->PlayerClass() >= FIRST_T_CLASS && pPlayer->PlayerClass() <= LAST_T_CLASS )
+ {
+ pPlayer->CheckTKPunishment();
+ }
+ }
+
+ // Give C4 to the terrorists
+ if (m_bMapHasBombTarget == true )
+ GiveC4();
+
+ // Reset game variables
+ m_flIntermissionEndTime = 0;
+ m_flRestartRoundTime = 0.0;
+ m_iAccountTerrorist = m_iAccountCT = 0;
+ m_iHostagesRescued = 0;
+ m_iHostagesTouched = 0;
+
+ //=============================================================================
+ // HPE_BEGIN
+ // [dwenger] Reset rescue-related achievement values
+ //=============================================================================
+
+ // [tj] reset flawless and lossless round related flags
+ m_bNoTerroristsKilled = true;
+ m_bNoCTsKilled = true;
+ m_bNoTerroristsDamaged = true;
+ m_bNoCTsDamaged = true;
+ m_pFirstKill = NULL;
+ m_pFirstBlood = NULL;
+
+ m_bCanDonateWeapons = true;
+
+ // [dwenger] Reset rescue-related achievement values
+ m_iHostagesRemaining = 0;
+ m_pLastRescuer = NULL;
+
+ m_hostageWasInjured = false;
+ m_hostageWasKilled = false;
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ m_iNumRescuers = 0;
+ m_iRoundWinStatus = WINNER_NONE;
+ m_bTargetBombed = m_bBombDefused = false;
+ m_bCompleteReset = false;
+ m_flNextHostageAnnouncement = gpGlobals->curtime;
+
+ m_iHostagesRemaining = g_Hostages.Count();
+
+ // fire global game event
+ IGameEvent * event = gameeventmanager->CreateEvent( "round_start" );
+ if ( event )
+ {
+ event->SetInt("timelimit", m_iRoundTime );
+ event->SetInt("fraglimit", 0 );
+ event->SetInt( "priority", 6 ); // HLTV event priority, not transmitted
+
+ if ( m_bMapHasRescueZone )
+ {
+ event->SetString("objective","HOSTAGE RESCUE");
+ }
+ else if ( m_bMapHasEscapeZone )
+ {
+ event->SetString("objective","PRISON ESCAPE");
+ }
+ else if ( m_iMapHasVIPSafetyZone == 1 )
+ {
+ event->SetString("objective","VIP RESCUE");
+ }
+ else if ( m_bMapHasBombTarget || m_bMapHasBombZone )
+ {
+ event->SetString("objective","BOMB TARGET");
+ }
+ else
+ {
+ event->SetString("objective","DEATHMATCH");
+ }
+
+ gameeventmanager->FireEvent( event );
+ }
+
+ UploadGameStats();
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [pfreese] I commented out this call to CreateWeaponManager, as the
+ // CGameWeaponManager object doesn't appear to be actually used by the CSS
+ // code, and in any case, the weapon manager does not support wildcards in
+ // entity names (as seemingly indicated) below. When the manager fails to
+ // create its factory, it removes itself in any case.
+ //=============================================================================
+
+ // CreateWeaponManager( "weapon_*", gpGlobals->maxClients * 2 );
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ }
+
+ void CCSGameRules::GiveC4()
+ {
+ enum {
+ ALL_TERRORISTS = 0,
+ HUMAN_TERRORISTS,
+ };
+ int iTerrorists[2][ABSOLUTE_PLAYER_LIMIT];
+ int numAliveTs[2] = { 0, 0 };
+ int lastBombGuyIndex[2] = { -1, -1 };
+
+ //Create an array of the indeces of bomb carrier candidates
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( i ) );
+
+ if( pPlayer && pPlayer->IsAlive() && pPlayer->GetTeamNumber() == TEAM_TERRORIST && numAliveTs[ALL_TERRORISTS] < ABSOLUTE_PLAYER_LIMIT )
+ {
+ if ( pPlayer == m_pLastBombGuy )
+ {
+ lastBombGuyIndex[ALL_TERRORISTS] = numAliveTs[ALL_TERRORISTS];
+ lastBombGuyIndex[HUMAN_TERRORISTS] = numAliveTs[HUMAN_TERRORISTS];
+ }
+
+ iTerrorists[ALL_TERRORISTS][numAliveTs[ALL_TERRORISTS]] = i;
+ numAliveTs[ALL_TERRORISTS]++;
+ if ( !pPlayer->IsBot() )
+ {
+ iTerrorists[HUMAN_TERRORISTS][numAliveTs[HUMAN_TERRORISTS]] = i;
+ numAliveTs[HUMAN_TERRORISTS]++;
+ }
+ }
+ }
+
+ int which = cv_bot_defer_to_human.GetBool();
+ if ( numAliveTs[HUMAN_TERRORISTS] == 0 )
+ {
+ which = ALL_TERRORISTS;
+ }
+
+ //pick one of the candidates randomly
+ if( numAliveTs[which] > 0 )
+ {
+ int index = random->RandomInt(0,numAliveTs[which]-1);
+ if ( lastBombGuyIndex[which] >= 0 )
+ {
+ // give the C4 sequentially
+ index = (lastBombGuyIndex[which] + 1) % numAliveTs[which];
+ }
+ CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( iTerrorists[which][index] ) );
+
+ Assert( pPlayer && pPlayer->GetTeamNumber() == TEAM_TERRORIST && pPlayer->IsAlive() );
+
+ pPlayer->GiveNamedItem( WEAPON_C4_CLASSNAME );
+ m_pLastBombGuy = pPlayer;
+
+ //pPlayer->SetBombIcon();
+ //pPlayer->pev->body = 1;
+
+ pPlayer->m_iDisplayHistoryBits |= DHF_BOMB_RETRIEVED;
+ pPlayer->HintMessage( "#Hint_you_have_the_bomb", false, true );
+
+ // Log this information
+ //UTIL_LogPrintf("\"%s<%i><%s><TERRORIST>\" triggered \"Spawned_With_The_Bomb\"\n",
+ // STRING( pPlayer->GetPlayerName() ),
+ // GETPLAYERUSERID( pPlayer->edict() ),
+ // GETPLAYERAUTHID( pPlayer->edict() ) );
+ }
+
+ m_bBombDropped = false;
+ }
+
+ void CCSGameRules::Think()
+ {
+ CGameRules::Think();
+
+ for ( int i = 0; i < GetNumberOfTeams(); i++ )
+ {
+ GetGlobalTeam( i )->Think();
+ }
+
+ ///// Check game rules /////
+ if ( CheckGameOver() )
+ {
+ return;
+ }
+
+ // have we hit the max rounds?
+ if ( CheckMaxRounds() )
+ {
+ return;
+ }
+
+ // did somebaody hit the fraglimit ?
+ if ( CheckFragLimit() )
+ {
+ return;
+ }
+
+ if ( CheckWinLimit() )
+ {
+ return;
+ }
+
+
+ // Check for the end of the round.
+ if ( IsFreezePeriod() )
+ {
+ CheckFreezePeriodExpired();
+ }
+ else
+ {
+ CheckRoundTimeExpired();
+ }
+
+ CheckLevelInitialized();
+
+ if ( m_flRestartRoundTime > 0.0f && m_flRestartRoundTime <= gpGlobals->curtime )
+ {
+ bool botSpeaking = false;
+ for ( int i=1; i <= gpGlobals->maxClients; ++i )
+ {
+ CBasePlayer *player = UTIL_PlayerByIndex( i );
+ if (player == NULL)
+ continue;
+
+ if (!player->IsBot())
+ continue;
+
+ CCSBot *bot = dynamic_cast< CCSBot * >(player);
+ if ( !bot )
+ continue;
+
+ if ( bot->IsUsingVoice() )
+ {
+ if ( gpGlobals->curtime > m_flRestartRoundTime + 10.0f )
+ {
+ Msg( "Ignoring speaking bot %s at round end\n", bot->GetPlayerName() );
+ }
+ else
+ {
+ botSpeaking = true;
+ break;
+ }
+ }
+ }
+
+ if ( !botSpeaking )
+ {
+ RestartRound();
+ }
+ }
+
+ if ( gpGlobals->curtime > m_tmNextPeriodicThink )
+ {
+ CheckRestartRound();
+ m_tmNextPeriodicThink = gpGlobals->curtime + 1.0;
+ }
+ }
+
+
+ // The bots do their processing after physics simulation etc so their visibility checks don't recompute
+ // bone positions multiple times a frame.
+ void CCSGameRules::EndGameFrame( void )
+ {
+ TheBots->StartFrame();
+
+ BaseClass::EndGameFrame();
+ }
+
+ bool CCSGameRules::CheckGameOver()
+ {
+ if ( g_fGameOver ) // someone else quit the game already
+ {
+ //=============================================================================
+ // HPE_BEGIN:
+ // [Forrest] Calling ChangeLevel multiple times was causing IncrementMapCycleIndex
+ // to skip over maps in the list. Avoid this using a technique from CTeamplayRoundBasedRules::Think.
+ //=============================================================================
+ // check to see if we should change levels now
+ if ( m_flIntermissionEndTime && ( m_flIntermissionEndTime < gpGlobals->curtime ) )
+ {
+ ChangeLevel(); // intermission is over
+
+ // Don't run this code again
+ m_flIntermissionEndTime = 0.f;
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ return true;
+ }
+
+ return false;
+ }
+
+ bool CCSGameRules::CheckFragLimit()
+ {
+ if ( fraglimit.GetInt() <= 0 )
+ return false;
+
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
+
+ if ( pPlayer && pPlayer->FragCount() >= fraglimit.GetInt() )
+ {
+ const char *teamName = "UNKNOWN";
+ if ( pPlayer->GetTeam() )
+ {
+ teamName = pPlayer->GetTeam()->GetName();
+ }
+ UTIL_LogPrintf("\"%s<%i><%s><%s>\" triggered \"Intermission_Kill_Limit\"\n",
+ pPlayer->GetPlayerName(),
+ pPlayer->GetUserID(),
+ pPlayer->GetNetworkIDString(),
+ teamName
+ );
+ GoToIntermission();
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ bool CCSGameRules::CheckMaxRounds()
+ {
+ if ( mp_maxrounds.GetInt() != 0 )
+ {
+ if ( m_iTotalRoundsPlayed >= mp_maxrounds.GetInt() )
+ {
+ UTIL_LogPrintf("World triggered \"Intermission_Round_Limit\"\n");
+ GoToIntermission();
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ bool CCSGameRules::CheckWinLimit()
+ {
+ // has one team won the specified number of rounds?
+ if ( mp_winlimit.GetInt() != 0 )
+ {
+ if ( m_iNumCTWins >= mp_winlimit.GetInt() )
+ {
+ UTIL_LogPrintf("Team \"CT\" triggered \"Intermission_Win_Limit\"\n");
+ GoToIntermission();
+ return true;
+ }
+ if ( m_iNumTerroristWins >= mp_winlimit.GetInt() )
+ {
+ UTIL_LogPrintf("Team \"TERRORIST\" triggered \"Intermission_Win_Limit\"\n");
+ GoToIntermission();
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ void CCSGameRules::CheckFreezePeriodExpired()
+ {
+ float startTime = m_fRoundStartTime;
+ if ( !IsFinite( startTime ) )
+ {
+ Warning( "Infinite round start time!\n" );
+ m_fRoundStartTime.GetForModify() = gpGlobals->curtime;
+ }
+
+ if ( IsFinite( startTime ) && gpGlobals->curtime < startTime )
+ {
+ return; // not time yet to start round
+ }
+
+ // Log this information
+ UTIL_LogPrintf("World triggered \"Round_Start\"\n");
+
+ char CT_sentence[40];
+ char T_sentence[40];
+
+ switch ( random->RandomInt( 0, 3 ) )
+ {
+ case 0:
+ Q_strncpy(CT_sentence,"radio.moveout", sizeof( CT_sentence ) );
+ Q_strncpy(T_sentence ,"radio.moveout", sizeof( T_sentence ) );
+ break;
+
+ case 1:
+ Q_strncpy(CT_sentence, "radio.letsgo", sizeof( CT_sentence ) );
+ Q_strncpy(T_sentence , "radio.letsgo", sizeof( T_sentence ) );
+ break;
+
+ case 2:
+ Q_strncpy(CT_sentence , "radio.locknload", sizeof( CT_sentence ) );
+ Q_strncpy(T_sentence , "radio.locknload", sizeof( T_sentence ) );
+ break;
+
+ default:
+ Q_strncpy(CT_sentence , "radio.go", sizeof( CT_sentence ) );
+ Q_strncpy(T_sentence , "radio.go", sizeof( T_sentence ) );
+ break;
+ }
+
+ // More specific radio commands for the new scenarios : Prison & Assasination
+ if (m_bMapHasEscapeZone == TRUE)
+ {
+ Q_strncpy(CT_sentence , "radio.elim", sizeof( CT_sentence ) );
+ Q_strncpy(T_sentence , "radio.getout", sizeof( T_sentence ) );
+ }
+ else if (m_iMapHasVIPSafetyZone == 1)
+ {
+ Q_strncpy(CT_sentence , "radio.vip", sizeof( CT_sentence ) );
+ Q_strncpy(T_sentence , "radio.locknload", sizeof( T_sentence ) );
+ }
+
+ // Freeze period expired: kill the flag
+ m_bFreezePeriod = false;
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "round_freeze_end" );
+ if ( event )
+ {
+ gameeventmanager->FireEvent( event );
+ }
+
+ // Update the timers for all clients and play a sound
+ bool bCTPlayed = false;
+ bool bTPlayed = false;
+
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CCSPlayer *pPlayer = CCSPlayer::Instance( i );
+ if ( pPlayer && !FNullEnt( pPlayer->edict() ) )
+ {
+ if ( pPlayer->State_Get() == STATE_ACTIVE )
+ {
+ if ( (pPlayer->GetTeamNumber() == TEAM_CT) && !bCTPlayed )
+ {
+ pPlayer->Radio( CT_sentence );
+ bCTPlayed = true;
+ }
+ else if ( (pPlayer->GetTeamNumber() == TEAM_TERRORIST) && !bTPlayed )
+ {
+ pPlayer->Radio( T_sentence );
+ bTPlayed = true;
+ }
+
+ }
+
+ //pPlayer->SyncRoundTimer();
+ }
+ }
+ }
+
+
+ void CCSGameRules::CheckRoundTimeExpired()
+ {
+ if ( mp_ignore_round_win_conditions.GetBool() )
+ return;
+
+ if ( GetRoundRemainingTime() > 0 || m_iRoundWinStatus != WINNER_NONE )
+ return; //We haven't completed other objectives, so go for this!.
+
+ if( !m_bFirstConnected )
+ return;
+
+ // New code to get rid of round draws!!
+
+ if ( m_bMapHasBombTarget )
+ {
+ //If the bomb is planted, don't let the round timer end the round.
+ //keep going until the bomb explodes or is defused
+ if( !m_bBombPlanted )
+ {
+ m_iAccountCT += 3250;
+
+ m_iNumCTWins++;
+ TerminateRound( mp_round_restart_delay.GetFloat(), Target_Saved );
+ UpdateTeamScores();
+ MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(TEAM_TERRORIST);
+ }
+ }
+ else if ( m_bMapHasRescueZone )
+ {
+ m_iAccountTerrorist += 3250;
+
+ m_iNumTerroristWins++;
+ TerminateRound( mp_round_restart_delay.GetFloat(), Hostages_Not_Rescued );
+ UpdateTeamScores();
+ MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(TEAM_CT);
+ }
+ else if ( m_bMapHasEscapeZone )
+ {
+ m_iNumCTWins++;
+ TerminateRound( mp_round_restart_delay.GetFloat(), Terrorists_Not_Escaped );
+ UpdateTeamScores();
+ }
+ else if ( m_iMapHasVIPSafetyZone == 1 )
+ {
+ m_iAccountTerrorist += 3250;
+ m_iNumTerroristWins++;
+
+ TerminateRound( mp_round_restart_delay.GetFloat(), VIP_Not_Escaped );
+ UpdateTeamScores();
+ }
+
+#if defined( REPLAY_ENABLED )
+ if ( g_pReplay )
+ {
+ // Write replay and stop recording if appropriate
+ g_pReplay->SV_EndRecordingSession();
+ }
+#endif
+ }
+
+ void CCSGameRules::GoToIntermission( void )
+ {
+ Msg( "Going to intermission...\n" );
+
+ IGameEvent *winEvent = gameeventmanager->CreateEvent( "cs_win_panel_match" );
+
+ if( winEvent )
+ {
+ for ( int teamIndex = TEAM_TERRORIST; teamIndex <= TEAM_CT; teamIndex++ )
+ {
+ CTeam *team = GetGlobalTeam( teamIndex );
+ if ( team )
+ {
+ float kills = CCS_GameStats.GetTeamStats(teamIndex)[CSSTAT_KILLS];
+ float deaths = CCS_GameStats.GetTeamStats(teamIndex)[CSSTAT_DEATHS];
+ // choose dialog variables to set depending on team
+ switch ( teamIndex )
+ {
+ case TEAM_TERRORIST:
+ winEvent->SetInt( "t_score", team->GetScore() );
+ if(deaths == 0)
+ {
+ winEvent->SetFloat( "t_kd", kills );
+ }
+ else
+ {
+ winEvent->SetFloat( "t_kd", kills / deaths );
+ }
+ winEvent->SetInt( "t_objectives_done", CCS_GameStats.GetTeamStats(teamIndex)[CSSTAT_OBJECTIVES_COMPLETED] );
+ winEvent->SetInt( "t_money_earned", CCS_GameStats.GetTeamStats(teamIndex)[CSSTAT_MONEY_EARNED] );
+ break;
+ case TEAM_CT:
+ winEvent->SetInt( "ct_score", team->GetScore() );
+ if(deaths == 0)
+ {
+ winEvent->SetFloat( "ct_kd", kills );
+ }
+ else
+ {
+ winEvent->SetFloat( "ct_kd", kills / deaths );
+ }
+ winEvent->SetInt( "ct_objectives_done", CCS_GameStats.GetTeamStats(teamIndex)[CSSTAT_OBJECTIVES_COMPLETED] );
+ winEvent->SetInt( "ct_money_earned", CCS_GameStats.GetTeamStats(teamIndex)[CSSTAT_MONEY_EARNED] );
+ break;
+ default:
+ Assert( false );
+ break;
+ }
+ }
+ }
+
+ gameeventmanager->FireEvent( winEvent );
+ }
+
+ BaseClass::GoToIntermission();
+
+ // set all players to FL_FROZEN
+ for ( int i = 1; i <= MAX_PLAYERS; i++ )
+ {
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
+
+ if ( pPlayer )
+ {
+ pPlayer->AddFlag( FL_FROZEN );
+ }
+ }
+
+ // freeze players while in intermission
+ m_bFreezePeriod = true;
+ }
+
+ int PlayerScoreInfoSort( const playerscore_t *p1, const playerscore_t *p2 )
+ {
+ // check frags
+ if ( p1->iScore > p2->iScore )
+ return -1;
+ if ( p2->iScore > p1->iScore )
+ return 1;
+
+ // check index
+ if ( p1->iPlayerIndex < p2->iPlayerIndex )
+ return -1;
+
+ return 1;
+ }
+
+#if defined (_DEBUG)
+ void TestRoundWinpanel( void )
+ {
+ IGameEvent *event = gameeventmanager->CreateEvent( "round_end" );
+ event->SetInt( "winner", TEAM_TERRORIST );
+
+ if ( event )
+ {
+ gameeventmanager->FireEvent( event );
+ }
+
+
+ IGameEvent *event2 = gameeventmanager->CreateEvent( "player_death" );
+ if ( event2 )
+ {
+ CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex(1) );
+
+ // pCappingPlayers is a null terminated list of player indeces
+ event2->SetInt("userid", pPlayer->GetUserID() );
+ event2->SetInt("attacker", pPlayer->GetUserID() );
+ event2->SetString("weapon", "Bare Hands" );
+ event2->SetInt("headshot", 1 );
+ event2->SetInt( "revenge", 1 );
+
+ gameeventmanager->FireEvent( event2 );
+ }
+
+ IGameEvent *winEvent = gameeventmanager->CreateEvent( "cs_win_panel_round" );
+
+ if ( winEvent )
+ {
+ if ( 1 )
+ {
+ if ( 0 /*team == m_iTimerWinTeam */)
+ {
+ // timer expired, defenders win
+ // show total time that was defended
+ winEvent->SetBool( "show_timer_defend", true );
+ winEvent->SetInt( "timer_time", 0 /*m_pRoundTimer->GetTimerMaxLength() */);
+ }
+ else
+ {
+ // attackers win
+ // show time it took for them to win
+ winEvent->SetBool( "show_timer_attack", true );
+
+ int iTimeElapsed = 90; //m_pRoundTimer->GetTimerMaxLength() - (int)m_pRoundTimer->GetTimeRemaining();
+ winEvent->SetInt( "timer_time", iTimeElapsed );
+ }
+ }
+ else
+ {
+ winEvent->SetBool( "show_timer_attack", false );
+ winEvent->SetBool( "show_timer_defend", false );
+ }
+
+ int iLastEvent = Terrorists_Win;
+
+ winEvent->SetInt( "final_event", iLastEvent );
+
+ // Set the fun fact data in the event
+ winEvent->SetString( "funfact_token", "#funfact_first_blood" );
+ winEvent->SetInt( "funfact_player", 1 );
+ winEvent->SetInt( "funfact_data1", 20 );
+ winEvent->SetInt( "funfact_data2", 31 );
+ winEvent->SetInt( "funfact_data3", 45 );
+
+ gameeventmanager->FireEvent( winEvent );
+ }
+ }
+ ConCommand test_round_winpanel( "test_round_winpanel", TestRoundWinpanel, "", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT );
+
+ void TestMatchWinpanel( void )
+ {
+ IGameEvent *event = gameeventmanager->CreateEvent( "round_end" );
+ event->SetInt( "winner", TEAM_TERRORIST );
+
+ if ( event )
+ {
+ gameeventmanager->FireEvent( event );
+ }
+
+ IGameEvent *winEvent = gameeventmanager->CreateEvent( "cs_win_panel_match" );
+
+ if ( winEvent )
+ {
+ winEvent->SetInt( "t_score", 4 );
+ winEvent->SetInt( "ct_score", 1 );
+
+ winEvent->SetFloat( "t_kd", 1.8f );
+ winEvent->SetFloat( "ct_kd", 0.4f );
+
+ winEvent->SetInt( "t_objectives_done", 5 );
+ winEvent->SetInt( "ct_objectives_done", 2 );
+
+ winEvent->SetInt( "t_money_earned", 30000 );
+ winEvent->SetInt( "ct_money_earned", 19999 );
+
+ gameeventmanager->FireEvent( winEvent );
+ }
+ }
+ ConCommand test_match_winpanel( "test_match_winpanel", TestMatchWinpanel, "", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT );
+
+ void TestFreezePanel( void )
+ {
+ IGameEvent *event = gameeventmanager->CreateEvent( "freezecam_started" );
+ if ( event )
+ {
+ gameeventmanager->FireEvent( event );
+ }
+
+ IGameEvent *winEvent = gameeventmanager->CreateEvent( "show_freezepanel" );
+
+ if ( winEvent )
+ {
+ winEvent->SetInt( "killer", 1 );
+ gameeventmanager->FireEvent( winEvent );
+ }
+ }
+ ConCommand test_freezepanel( "test_freezepanel", TestFreezePanel, "", FCVAR_DEVELOPMENTONLY | FCVAR_CHEAT );
+#endif // _DEBUG
+
+ static void PrintToConsole( CBasePlayer *player, const char *text )
+ {
+ if ( player )
+ {
+ ClientPrint( player, HUD_PRINTCONSOLE, text );
+ }
+ else
+ {
+ Msg( "%s", text );
+ }
+ }
+
+ void CCSGameRules::DumpTimers( void ) const
+ {
+ extern ConVar bot_join_delay;
+ CBasePlayer *player = UTIL_GetCommandClient();
+ CFmtStr str;
+
+ PrintToConsole( player, str.sprintf( "Timers and related info at %f:\n", gpGlobals->curtime ) );
+ PrintToConsole( player, str.sprintf( "m_bCompleteReset: %d\n", m_bCompleteReset ) );
+ PrintToConsole( player, str.sprintf( "m_iTotalRoundsPlayed: %d\n", m_iTotalRoundsPlayed ) );
+ PrintToConsole( player, str.sprintf( "m_iRoundTime: %d\n", m_iRoundTime.Get() ) );
+ PrintToConsole( player, str.sprintf( "m_iRoundWinStatus: %d\n", m_iRoundWinStatus ) );
+
+ PrintToConsole( player, str.sprintf( "first connected: %d\n", m_bFirstConnected ) );
+ PrintToConsole( player, str.sprintf( "intermission end time: %f\n", m_flIntermissionEndTime ) );
+ PrintToConsole( player, str.sprintf( "freeze period: %d\n", m_bFreezePeriod.Get() ) );
+ PrintToConsole( player, str.sprintf( "round restart time: %f\n", m_flRestartRoundTime ) );
+ PrintToConsole( player, str.sprintf( "game start time: %f\n", m_flGameStartTime.Get() ) );
+ PrintToConsole( player, str.sprintf( "m_fRoundStartTime: %f\n", m_fRoundStartTime.Get() ) );
+ PrintToConsole( player, str.sprintf( "freeze time: %d\n", m_iFreezeTime ) );
+ PrintToConsole( player, str.sprintf( "next think: %f\n", m_tmNextPeriodicThink ) );
+
+ PrintToConsole( player, str.sprintf( "fraglimit: %d\n", fraglimit.GetInt() ) );
+ PrintToConsole( player, str.sprintf( "mp_maxrounds: %d\n", mp_maxrounds.GetInt() ) );
+ PrintToConsole( player, str.sprintf( "mp_winlimit: %d\n", mp_winlimit.GetInt() ) );
+ PrintToConsole( player, str.sprintf( "bot_quota: %d\n", cv_bot_quota.GetInt() ) );
+ PrintToConsole( player, str.sprintf( "bot_quota_mode: %s\n", cv_bot_quota_mode.GetString() ) );
+ PrintToConsole( player, str.sprintf( "bot_join_after_player: %d\n", cv_bot_join_after_player.GetInt() ) );
+ PrintToConsole( player, str.sprintf( "bot_join_delay: %d\n", bot_join_delay.GetInt() ) );
+ PrintToConsole( player, str.sprintf( "nextlevel: %s\n", nextlevel.GetString() ) );
+
+ int humansInGame = UTIL_HumansInGame( true );
+ int botsInGame = UTIL_BotsInGame();
+ PrintToConsole( player, str.sprintf( "%d humans and %d bots in game\n", humansInGame, botsInGame ) );
+
+ PrintToConsole( player, str.sprintf( "num CTs (spawnable): %d (%d)\n", m_iNumCT, m_iNumSpawnableCT ) );
+ PrintToConsole( player, str.sprintf( "num Ts (spawnable): %d (%d)\n", m_iNumTerrorist, m_iNumSpawnableTerrorist ) );
+
+ if ( g_fGameOver )
+ {
+ PrintToConsole( player, str.sprintf( "Game is over!\n" ) );
+ }
+ PrintToConsole( player, str.sprintf( "\n" ) );
+ }
+
+ CON_COMMAND( mp_dump_timers, "Prints round timers to the console for debugging" )
+ {
+ if ( !UTIL_IsCommandIssuedByServerAdmin() )
+ return;
+
+ if ( CSGameRules() )
+ {
+ CSGameRules()->DumpTimers();
+ }
+ }
+
+
+ // living players on the given team need to be marked as not receiving any money
+ // next round.
+ void CCSGameRules::MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(int team)
+ {
+ int playerNum;
+ for (playerNum = 1; playerNum <= gpGlobals->maxClients; ++playerNum)
+ {
+ CCSPlayer *player = (CCSPlayer *)UTIL_PlayerByIndex(playerNum);
+ if (player == NULL)
+ {
+ continue;
+ }
+
+ if ((player->GetTeamNumber() == team) && (player->IsAlive()))
+ {
+ player->MarkAsNotReceivingMoneyNextRound();
+ }
+ }
+ }
+
+
+ void CCSGameRules::CheckLevelInitialized( void )
+ {
+ if ( !m_bLevelInitialized )
+ {
+ // Count the number of spawn points for each team
+ // This determines the maximum number of players allowed on each
+
+ CBaseEntity* ent = NULL;
+
+ m_iSpawnPointCount_Terrorist = 0;
+ m_iSpawnPointCount_CT = 0;
+
+ while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_terrorist" ) ) != NULL )
+ {
+ if ( IsSpawnPointValid( ent, NULL ) )
+ {
+ m_iSpawnPointCount_Terrorist++;
+ }
+ else
+ {
+ Warning("Invalid terrorist spawnpoint at (%.1f,%.1f,%.1f)\n",
+ ent->GetAbsOrigin()[0],ent->GetAbsOrigin()[2],ent->GetAbsOrigin()[2] );
+ }
+ }
+
+ while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_counterterrorist" ) ) != NULL )
+ {
+ if ( IsSpawnPointValid( ent, NULL ) )
+ {
+ m_iSpawnPointCount_CT++;
+ }
+ else
+ {
+ Warning("Invalid counterterrorist spawnpoint at (%.1f,%.1f,%.1f)\n",
+ ent->GetAbsOrigin()[0],ent->GetAbsOrigin()[2],ent->GetAbsOrigin()[2] );
+ }
+ }
+
+ // Is this a logo map?
+ if ( gEntList.FindEntityByClassname( NULL, "info_player_logo" ) )
+ m_bLogoMap = true;
+
+ m_bLevelInitialized = true;
+ }
+ }
+
+ void CCSGameRules::ShowSpawnPoints( void )
+ {
+ CBaseEntity* ent = NULL;
+
+ while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_terrorist" ) ) != NULL )
+ {
+ if ( IsSpawnPointValid( ent, NULL ) )
+ {
+ NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 200, 600 );
+ }
+ else
+ {
+ NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 200, 600);
+ }
+ }
+
+ while ( ( ent = gEntList.FindEntityByClassname( ent, "info_player_counterterrorist" ) ) != NULL )
+ {
+ if ( IsSpawnPointValid( ent, NULL ) )
+ {
+ NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 0, 255, 0, 200, 600 );
+ }
+ else
+ {
+ NDebugOverlay::Box( ent->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX, 255, 0, 0, 200, 600 );
+ }
+ }
+ }
+
+ void CCSGameRules::CheckRestartRound( void )
+ {
+ // Restart the game if specified by the server
+ int iRestartDelay = mp_restartgame.GetInt();
+
+ if ( iRestartDelay > 0 )
+ {
+ if ( iRestartDelay > 60 )
+ iRestartDelay = 60;
+
+ // log the restart
+ UTIL_LogPrintf( "World triggered \"Restart_Round_(%i_%s)\"\n", iRestartDelay, iRestartDelay == 1 ? "second" : "seconds" );
+
+ UTIL_LogPrintf( "Team \"CT\" scored \"%i\" with \"%i\" players\n", m_iNumCTWins, m_iNumCT );
+ UTIL_LogPrintf( "Team \"TERRORIST\" scored \"%i\" with \"%i\" players\n", m_iNumTerroristWins, m_iNumTerrorist );
+
+ // let the players know
+ char strRestartDelay[64];
+ Q_snprintf( strRestartDelay, sizeof( strRestartDelay ), "%d", iRestartDelay );
+ UTIL_ClientPrintAll( HUD_PRINTCENTER, "#Game_will_restart_in", strRestartDelay, iRestartDelay == 1 ? "SECOND" : "SECONDS" );
+ UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#Game_will_restart_in", strRestartDelay, iRestartDelay == 1 ? "SECOND" : "SECONDS" );
+
+ m_flRestartRoundTime = gpGlobals->curtime + iRestartDelay;
+ m_bCompleteReset = true;
+ mp_restartgame.SetValue( 0 );
+ }
+ }
+
+
+ class SetHumanTeamFunctor
+ {
+ public:
+ SetHumanTeamFunctor( int targetTeam )
+ {
+ m_targetTeam = targetTeam;
+ m_sourceTeam = ( m_targetTeam == TEAM_CT ) ? TEAM_TERRORIST : TEAM_CT;
+
+ m_traitors.MakeReliable();
+ m_loyalists.MakeReliable();
+ m_loyalists.AddAllPlayers();
+ }
+
+ bool operator()( CBasePlayer *basePlayer )
+ {
+ CCSPlayer *player = ToCSPlayer( basePlayer );
+ if ( !player )
+ return true;
+
+ if ( player->IsBot() )
+ return true;
+
+ if ( player->GetTeamNumber() != m_sourceTeam )
+ return true;
+
+ if ( player->State_Get() == STATE_PICKINGCLASS )
+ return true;
+
+ if ( CSGameRules()->TeamFull( m_targetTeam ) )
+ return false;
+
+ if ( CSGameRules()->TeamStacked( m_targetTeam, m_sourceTeam ) )
+ return false;
+
+ player->SwitchTeam( m_targetTeam );
+ m_traitors.AddRecipient( player );
+ m_loyalists.RemoveRecipient( player );
+
+ return true;
+ }
+
+ void SendNotice( void )
+ {
+ if ( m_traitors.GetRecipientCount() > 0 )
+ {
+ UTIL_ClientPrintFilter( m_traitors, HUD_PRINTCENTER, "#Player_Balanced" );
+ UTIL_ClientPrintFilter( m_loyalists, HUD_PRINTCENTER, "#Teams_Balanced" );
+ }
+ }
+
+ private:
+ int m_targetTeam;
+ int m_sourceTeam;
+
+ CRecipientFilter m_traitors;
+ CRecipientFilter m_loyalists;
+ };
+
+
+ void CCSGameRules::MoveHumansToHumanTeam( void )
+ {
+ int targetTeam = GetHumanTeam();
+ if ( targetTeam != TEAM_TERRORIST && targetTeam != TEAM_CT )
+ return;
+
+ SetHumanTeamFunctor setTeam( targetTeam );
+ ForEachPlayer( setTeam );
+
+ setTeam.SendNotice();
+ }
+
+
+ void CCSGameRules::BalanceTeams( void )
+ {
+ int iTeamToSwap = TEAM_UNASSIGNED;
+ int iNumToSwap;
+
+ if (m_iMapHasVIPSafetyZone == 1) // The ratio for teams is different for Assasination maps
+ {
+ int iDesiredNumCT, iDesiredNumTerrorist;
+
+ if ( (m_iNumCT + m_iNumTerrorist)%2 != 0) // uneven number of players
+ iDesiredNumCT = (int)((m_iNumCT + m_iNumTerrorist) * 0.55) + 1;
+ else
+ iDesiredNumCT = (int)((m_iNumCT + m_iNumTerrorist)/2);
+ iDesiredNumTerrorist = (m_iNumCT + m_iNumTerrorist) - iDesiredNumCT;
+
+ if ( m_iNumCT < iDesiredNumCT )
+ {
+ iTeamToSwap = TEAM_TERRORIST;
+ iNumToSwap = iDesiredNumCT - m_iNumCT;
+ }
+ else if ( m_iNumTerrorist < iDesiredNumTerrorist )
+ {
+ iTeamToSwap = TEAM_CT;
+ iNumToSwap = iDesiredNumTerrorist - m_iNumTerrorist;
+ }
+ else
+ return;
+ }
+ else
+ {
+ if (m_iNumCT > m_iNumTerrorist)
+ {
+ iTeamToSwap = TEAM_CT;
+ iNumToSwap = (m_iNumCT - m_iNumTerrorist)/2;
+
+ }
+ else if (m_iNumTerrorist > m_iNumCT)
+ {
+ iTeamToSwap = TEAM_TERRORIST;
+ iNumToSwap = (m_iNumTerrorist - m_iNumCT)/2;
+ }
+ else
+ {
+ return; // Teams are even.. Get out of here.
+ }
+ }
+
+ if (iNumToSwap > 3) // Don't swap more than 3 players at a time.. This is a naive method of avoiding infinite loops.
+ iNumToSwap = 3;
+
+ int iTragetTeam = TEAM_UNASSIGNED;
+
+ if ( iTeamToSwap == TEAM_CT )
+ {
+ iTragetTeam = TEAM_TERRORIST;
+ }
+ else if ( iTeamToSwap == TEAM_TERRORIST )
+ {
+ iTragetTeam = TEAM_CT;
+ }
+ else
+ {
+ // no valid team to swap
+ return;
+ }
+
+ CRecipientFilter traitors;
+ CRecipientFilter loyalists;
+
+ traitors.MakeReliable();
+ loyalists.MakeReliable();
+ loyalists.AddAllPlayers();
+
+ for (int i = 0; i < iNumToSwap; i++)
+ {
+ // last person to join the server
+ int iHighestUserID = -1;
+ CCSPlayer *pPlayerToSwap = NULL;
+
+ // check if target team is full, exit if so
+ if ( TeamFull(iTragetTeam) )
+ break;
+
+ // search for player with highest UserID = most recently joined to switch over
+ for ( int j = 1; j <= gpGlobals->maxClients; j++ )
+ {
+ CCSPlayer *pPlayer = (CCSPlayer *)UTIL_PlayerByIndex( j );
+
+ if ( !pPlayer )
+ continue;
+
+ CCSBot *bot = dynamic_cast< CCSBot * >(pPlayer);
+ if ( bot )
+ continue; // don't swap bots - the bot system will handle that
+
+ if ( pPlayer &&
+ ( m_pVIP != pPlayer ) &&
+ ( pPlayer->GetTeamNumber() == iTeamToSwap ) &&
+ ( engine->GetPlayerUserId( pPlayer->edict() ) > iHighestUserID ) &&
+ ( pPlayer->State_Get() != STATE_PICKINGCLASS ) )
+ {
+ iHighestUserID = engine->GetPlayerUserId( pPlayer->edict() );
+ pPlayerToSwap = pPlayer;
+ }
+ }
+
+ if ( pPlayerToSwap != NULL )
+ {
+ traitors.AddRecipient( pPlayerToSwap );
+ loyalists.RemoveRecipient( pPlayerToSwap );
+ pPlayerToSwap->SwitchTeam( iTragetTeam );
+ }
+ }
+
+ if ( traitors.GetRecipientCount() > 0 )
+ {
+ UTIL_ClientPrintFilter( traitors, HUD_PRINTCENTER, "#Player_Balanced" );
+ UTIL_ClientPrintFilter( loyalists, HUD_PRINTCENTER, "#Teams_Balanced" );
+ }
+ }
+
+
+ bool CCSGameRules::TeamFull( int team_id )
+ {
+ CheckLevelInitialized();
+
+ switch ( team_id )
+ {
+ case TEAM_TERRORIST:
+ return m_iNumTerrorist >= m_iSpawnPointCount_Terrorist;
+
+ case TEAM_CT:
+ return m_iNumCT >= m_iSpawnPointCount_CT;
+ }
+
+ return false;
+ }
+
+ int CCSGameRules::GetHumanTeam()
+ {
+ if ( FStrEq( "CT", mp_humanteam.GetString() ) )
+ {
+ return TEAM_CT;
+ }
+ else if ( FStrEq( "T", mp_humanteam.GetString() ) )
+ {
+ return TEAM_TERRORIST;
+ }
+
+ return TEAM_UNASSIGNED;
+ }
+
+ int CCSGameRules::SelectDefaultTeam( bool ignoreBots /*= false*/ )
+ {
+ if ( ignoreBots && ( FStrEq( cv_bot_join_team.GetString(), "T" ) || FStrEq( cv_bot_join_team.GetString(), "CT" ) ) )
+ {
+ ignoreBots = false; // don't ignore bots when they can't switch teams
+ }
+
+ if ( ignoreBots && !mp_autoteambalance.GetBool() )
+ {
+ ignoreBots = false; // don't ignore bots when they can't switch teams
+ }
+
+ int team = TEAM_UNASSIGNED;
+ int numTerrorists = m_iNumTerrorist;
+ int numCTs = m_iNumCT;
+ if ( ignoreBots )
+ {
+ numTerrorists = UTIL_HumansOnTeam( TEAM_TERRORIST );
+ numCTs = UTIL_HumansOnTeam( TEAM_CT );
+ }
+
+ // Choose the team that's lacking players
+ if ( numTerrorists < numCTs )
+ {
+ team = TEAM_TERRORIST;
+ }
+ else if ( numTerrorists > numCTs )
+ {
+ team = TEAM_CT;
+ }
+ // Choose the team that's losing
+ else if ( m_iNumTerroristWins < m_iNumCTWins )
+ {
+ team = TEAM_TERRORIST;
+ }
+ else if ( m_iNumCTWins < m_iNumTerroristWins )
+ {
+ team = TEAM_CT;
+ }
+ else
+ {
+ // Teams and scores are equal, pick a random team
+ if ( random->RandomInt( 0, 1 ) == 0 )
+ {
+ team = TEAM_CT;
+ }
+ else
+ {
+ team = TEAM_TERRORIST;
+ }
+ }
+
+ if ( TeamFull( team ) )
+ {
+ // Pick the opposite team
+ if ( team == TEAM_TERRORIST )
+ {
+ team = TEAM_CT;
+ }
+ else
+ {
+ team = TEAM_TERRORIST;
+ }
+
+ // No choices left
+ if ( TeamFull( team ) )
+ return TEAM_UNASSIGNED;
+ }
+
+ return team;
+ }
+
+ //checks to see if the desired team is stacked, returns true if it is
+ bool CCSGameRules::TeamStacked( int newTeam_id, int curTeam_id )
+ {
+ //players are allowed to change to their own team
+ if(newTeam_id == curTeam_id)
+ return false;
+
+ // if mp_limitteams is 0, don't check
+ if ( mp_limitteams.GetInt() == 0 )
+ return false;
+
+ switch ( newTeam_id )
+ {
+ case TEAM_TERRORIST:
+ if(curTeam_id != TEAM_UNASSIGNED && curTeam_id != TEAM_SPECTATOR)
+ {
+ if((m_iNumTerrorist + 1) > (m_iNumCT + mp_limitteams.GetInt() - 1))
+ return true;
+ else
+ return false;
+ }
+ else
+ {
+ if((m_iNumTerrorist + 1) > (m_iNumCT + mp_limitteams.GetInt()))
+ return true;
+ else
+ return false;
+ }
+ break;
+ case TEAM_CT:
+ if(curTeam_id != TEAM_UNASSIGNED && curTeam_id != TEAM_SPECTATOR)
+ {
+ if((m_iNumCT + 1) > (m_iNumTerrorist + mp_limitteams.GetInt() - 1))
+ return true;
+ else
+ return false;
+ }
+ else
+ {
+ if((m_iNumCT + 1) > (m_iNumTerrorist + mp_limitteams.GetInt()))
+ return true;
+ else
+ return false;
+ }
+ break;
+ }
+
+ return false;
+ }
+
+
+ //=========================================================
+ //=========================================================
+ bool CCSGameRules::FPlayerCanRespawn( CBasePlayer *pBasePlayer )
+ {
+ CCSPlayer *pPlayer = ToCSPlayer( pBasePlayer );
+ if ( !pPlayer )
+ Error( "FPlayerCanRespawn: pPlayer=0" );
+
+ // Player cannot respawn twice in a round
+ if ( pPlayer->m_iNumSpawns > 0 && m_bFirstConnected )
+ return false;
+
+ // If they're dead after the map has ended, and it's about to start the next round,
+ // wait for the round restart to respawn them.
+ if ( gpGlobals->curtime < m_flRestartRoundTime )
+ return false;
+
+ // Only valid team members can spawn
+ if ( pPlayer->GetTeamNumber() != TEAM_CT && pPlayer->GetTeamNumber() != TEAM_TERRORIST )
+ return false;
+
+ // Only players with a valid class can spawn
+ if ( pPlayer->GetClass() == CS_CLASS_NONE )
+ return false;
+
+ // Player cannot respawn until next round if more than 20 seconds in
+
+ // Tabulate the number of players on each team.
+ m_iNumCT = GetGlobalTeam( TEAM_CT )->GetNumPlayers();
+ m_iNumTerrorist = GetGlobalTeam( TEAM_TERRORIST )->GetNumPlayers();
+
+ if ( m_iNumTerrorist > 0 && m_iNumCT > 0 )
+ {
+ if ( gpGlobals->curtime > (m_fRoundStartTime + 20) )
+ {
+ //If this player just connected and fadetoblack is on, then maybe
+ //the server admin doesn't want him peeking around.
+ color32_s clr = {0,0,0,255};
+ if ( mp_fadetoblack.GetBool() )
+ {
+ UTIL_ScreenFade( pPlayer, clr, 3, 3, FFADE_OUT | FFADE_STAYOUT );
+ }
+
+ return false;
+ }
+ }
+
+ // Player cannot respawn while in the Choose Appearance menu
+ //if ( pPlayer->m_iMenu == Menu_ChooseAppearance )
+ // return false;
+
+ return true;
+ }
+
+ void CCSGameRules::TerminateRound(float tmDelay, int iReason )
+ {
+ variant_t emptyVariant;
+ int iWinnerTeam = WINNER_NONE;
+ const char *text = "UNKNOWN";
+
+ // UTIL_ClientPrintAll( HUD_PRINTCENTER, sentence );
+
+ switch ( iReason )
+ {
+// Terror wins:
+ case Target_Bombed:
+ text = "#Target_Bombed";
+ iWinnerTeam = WINNER_TER;
+ break;
+
+ case VIP_Assassinated:
+ text = "#VIP_Assassinated";
+ iWinnerTeam = WINNER_TER;
+ break;
+
+ case Terrorists_Escaped:
+ text = "#Terrorists_Escaped";
+ iWinnerTeam = WINNER_TER;
+ break;
+
+ case Terrorists_Win:
+ text = "#Terrorists_Win";
+ iWinnerTeam = WINNER_TER;
+ break;
+
+ case Hostages_Not_Rescued:
+ text = "#Hostages_Not_Rescued";
+ iWinnerTeam = WINNER_TER;
+ break;
+
+ case VIP_Not_Escaped:
+ text = "#VIP_Not_Escaped";
+ iWinnerTeam = WINNER_TER;
+ break;
+// CT wins:
+ case VIP_Escaped:
+ text = "#VIP_Escaped";
+ iWinnerTeam = WINNER_CT;
+ break;
+
+ case CTs_PreventEscape:
+ text = "#CTs_PreventEscape";
+ iWinnerTeam = WINNER_CT;
+ break;
+
+ case Escaping_Terrorists_Neutralized:
+ text = "#Escaping_Terrorists_Neutralized";
+ iWinnerTeam = WINNER_CT;
+ break;
+
+ case Bomb_Defused:
+ text = "#Bomb_Defused";
+ iWinnerTeam = WINNER_CT;
+ break;
+
+ case CTs_Win:
+ text = "#CTs_Win";
+ iWinnerTeam = WINNER_CT;
+ break;
+
+ case All_Hostages_Rescued:
+ text = "#All_Hostages_Rescued";
+ iWinnerTeam = WINNER_CT;
+ break;
+
+ case Target_Saved:
+ text = "#Target_Saved";
+ iWinnerTeam = WINNER_CT;
+ break;
+
+ case Terrorists_Not_Escaped:
+ text = "#Terrorists_Not_Escaped";
+ iWinnerTeam = WINNER_CT;
+ break;
+// no winners:
+ case Game_Commencing:
+ text = "#Game_Commencing";
+ iWinnerTeam = WINNER_DRAW;
+ break;
+
+ case Round_Draw:
+ text = "#Round_Draw";
+ iWinnerTeam = WINNER_DRAW;
+ break;
+
+ default:
+ DevMsg("TerminateRound: unknown round end ID %i\n", iReason );
+ break;
+ }
+
+ m_iRoundWinStatus = iWinnerTeam;
+ m_flRestartRoundTime = gpGlobals->curtime + tmDelay;
+
+ if ( iWinnerTeam == WINNER_CT )
+ {
+ for( int i=0;i<g_Hostages.Count();i++ )
+ g_Hostages[i]->AcceptInput( "CTsWin", NULL, NULL, emptyVariant, 0 );
+ }
+
+ else if ( iWinnerTeam == WINNER_TER )
+ {
+ for( int i=0;i<g_Hostages.Count();i++ )
+ g_Hostages[i]->AcceptInput( "TerroristsWin", NULL, NULL, emptyVariant, 0 );
+ }
+ else
+ {
+ Assert( iWinnerTeam == WINNER_NONE || iWinnerTeam == WINNER_DRAW );
+ }
+
+ //=============================================================================
+ // HPE_BEGIN:
+ //=============================================================================
+
+ // [tj] Check for any non-player-specific achievements.
+ ProcessEndOfRoundAchievements(iWinnerTeam, iReason);
+
+ if( iReason != Game_Commencing )
+ {
+ // [pfreese] Setup and send win panel event (primarily funfact data)
+
+ FunFact funfact;
+ funfact.szLocalizationToken = "";
+ funfact.iPlayer = 0;
+ funfact.iData1 = 0;
+ funfact.iData2 = 0;
+ funfact.iData3 = 0;
+
+ m_pFunFactManager->GetRoundEndFunFact( iWinnerTeam, iReason, funfact);
+
+ //Send all the info needed for the win panel
+ IGameEvent *winEvent = gameeventmanager->CreateEvent( "cs_win_panel_round" );
+
+ if ( winEvent )
+ {
+ // determine what categories to send
+ if ( GetRoundRemainingTime() <= 0 )
+ {
+ // timer expired, defenders win
+ // show total time that was defended
+ winEvent->SetBool( "show_timer_defend", true );
+ winEvent->SetInt( "timer_time", m_iRoundTime );
+ }
+ else
+ {
+ // attackers win
+ // show time it took for them to win
+ winEvent->SetBool( "show_timer_attack", true );
+
+ int iTimeElapsed = m_iRoundTime - GetRoundRemainingTime();
+ winEvent->SetInt( "timer_time", iTimeElapsed );
+ }
+
+ winEvent->SetInt( "final_event", iReason );
+
+ // Set the fun fact data in the event
+ winEvent->SetString( "funfact_token", funfact.szLocalizationToken);
+ winEvent->SetInt( "funfact_player", funfact.iPlayer );
+ winEvent->SetInt( "funfact_data1", funfact.iData1 );
+ winEvent->SetInt( "funfact_data2", funfact.iData2 );
+ winEvent->SetInt( "funfact_data3", funfact.iData3 );
+ gameeventmanager->FireEvent( winEvent );
+ }
+ }
+
+ // [tj] Inform players that the round is over
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
+ if(pPlayer)
+ {
+ pPlayer->OnRoundEnd(iWinnerTeam, iReason);
+ }
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "round_end" );
+ if ( event )
+ {
+ event->SetInt( "winner", iWinnerTeam );
+ event->SetInt( "reason", iReason );
+ event->SetString( "message", text );
+ event->SetInt( "priority", 6 ); // HLTV event priority, not transmitted
+ gameeventmanager->FireEvent( event );
+ }
+
+ if ( GetMapRemainingTime() == 0.0f )
+ {
+ UTIL_LogPrintf("World triggered \"Intermission_Time_Limit\"\n");
+ GoToIntermission();
+ }
+ }
+
+ //=============================================================================
+ // HPE_BEGIN:
+ //=============================================================================
+
+ // Helper to determine if all players on a team are playing for the same clan
+ static bool IsClanTeam( CTeam *pTeam )
+ {
+ uint32 iTeamClan = 0;
+ for ( int iPlayer = 0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
+ {
+ CBasePlayer *pPlayer = pTeam->GetPlayer( iPlayer );
+ if ( !pPlayer )
+ return false;
+
+ const char *pClanID = engine->GetClientConVarValue( pPlayer->entindex(), "cl_clanid" );
+ uint32 iPlayerClan = atoi( pClanID );
+ if ( iPlayer == 0 )
+ {
+ // Initialize the team clan
+ iTeamClan = iPlayerClan;
+ }
+ else
+ {
+ if ( iPlayerClan != iTeamClan || iPlayerClan == 0 )
+ return false;
+ }
+ }
+ return iTeamClan != 0;
+ }
+
+ // [tj] This is where we check non-player-specific that occur at the end of the round
+ void CCSGameRules::ProcessEndOfRoundAchievements(int iWinnerTeam, int iReason)
+ {
+ if (iWinnerTeam == WINNER_CT || iWinnerTeam == WINNER_TER)
+ {
+ int losingTeamId = (iWinnerTeam == TEAM_CT) ? TEAM_TERRORIST : TEAM_CT;
+ CTeam* losingTeam = GetGlobalTeam(losingTeamId);
+
+
+ //Check for players we should ignore when checking team size.
+ int ignoreCount = 0;
+
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
+ if (pPlayer)
+ {
+ int teamNum = pPlayer->GetTeamNumber();
+ if ( teamNum == losingTeamId )
+ {
+ if (pPlayer->WasNotKilledNaturally())
+ {
+ ignoreCount++;
+ }
+ }
+ }
+ }
+
+
+ // [tj] Check extermination with no losses achievement
+ if ( ( ( iReason == CTs_Win && m_bNoCTsKilled ) || ( iReason == Terrorists_Win && m_bNoTerroristsKilled ) )
+ && losingTeam && losingTeam->GetNumPlayers() - ignoreCount >= AchievementConsts::DefaultMinOpponentsForAchievement)
+ {
+ CTeam *pTeam = GetGlobalTeam( iWinnerTeam );
+
+ for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
+ {
+ CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) );
+ Assert( pPlayer );
+ if ( !pPlayer )
+ continue;
+
+ pPlayer->AwardAchievement(CSLosslessExtermination);
+ }
+ }
+
+ // [tj] Check flawless victory achievement - currently requiring extermination
+ if (((iReason == CTs_Win && m_bNoCTsDamaged) || (iReason == Terrorists_Win && m_bNoTerroristsDamaged))
+ && losingTeam && losingTeam->GetNumPlayers() - ignoreCount >= AchievementConsts::DefaultMinOpponentsForAchievement)
+ {
+ CTeam *pTeam = GetGlobalTeam( iWinnerTeam );
+
+ for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
+ {
+ CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) );
+ Assert( pPlayer );
+ if ( !pPlayer )
+ continue;
+
+ pPlayer->AwardAchievement(CSFlawlessVictory);
+ }
+ }
+
+ // [tj] Check bloodless victory achievement
+ if (((iWinnerTeam == TEAM_TERRORIST && m_bNoCTsKilled) || (iWinnerTeam == Terrorists_Win && m_bNoTerroristsKilled))
+ && losingTeam && losingTeam->GetNumPlayers() >= AchievementConsts::DefaultMinOpponentsForAchievement)
+ {
+ CTeam *pTeam = GetGlobalTeam( iWinnerTeam );
+
+ for ( int iPlayer=0; iPlayer < pTeam->GetNumPlayers(); iPlayer++ )
+ {
+ CCSPlayer *pPlayer = ToCSPlayer( pTeam->GetPlayer( iPlayer ) );
+ Assert( pPlayer );
+ if ( !pPlayer )
+ continue;
+
+ pPlayer->AwardAchievement(CSBloodlessVictory);
+ }
+ }
+
+ // Check the clan match achievement
+ CTeam *pWinningTeam = GetGlobalTeam( iWinnerTeam );
+ if ( pWinningTeam && pWinningTeam->GetNumPlayers() >= AchievementConsts::DefaultMinOpponentsForAchievement &&
+ losingTeam && losingTeam->GetNumPlayers() - ignoreCount >= AchievementConsts::DefaultMinOpponentsForAchievement &&
+ IsClanTeam( pWinningTeam ) && IsClanTeam( losingTeam ) )
+ {
+ for ( int iPlayer=0; iPlayer < pWinningTeam->GetNumPlayers(); iPlayer++ )
+ {
+ CCSPlayer *pPlayer = ToCSPlayer( pWinningTeam->GetPlayer( iPlayer ) );
+ if ( !pPlayer )
+ continue;
+
+ pPlayer->AwardAchievement( CSWinClanMatch );
+ }
+ }
+ }
+ }
+
+ //[tj] Counts the number of players in each category in the struct (dead, alive, etc...)
+ void CCSGameRules::GetPlayerCounts(TeamPlayerCounts teamCounts[TEAM_MAXCOUNT])
+ {
+ memset(teamCounts, 0, sizeof(TeamPlayerCounts) * TEAM_MAXCOUNT);
+
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CCSPlayer* pPlayer = (CCSPlayer*)UTIL_PlayerByIndex( i );
+ if (pPlayer)
+ {
+ int iTeam = pPlayer->GetTeamNumber();
+
+ if (iTeam >= 0 && iTeam < TEAM_MAXCOUNT)
+ {
+ ++teamCounts[iTeam].totalPlayers;
+ if (pPlayer->IsAlive())
+ {
+ ++teamCounts[iTeam].totalAlivePlayers;
+ }
+ else
+ {
+ ++teamCounts[iTeam].totalDeadPlayers;
+
+ //If the player has joined a team bit isn't in the game yet
+ if (pPlayer->State_Get() == STATE_PICKINGCLASS)
+ {
+ ++teamCounts[iTeam].unenteredPlayers;
+ }
+ else if (pPlayer->WasNotKilledNaturally())
+ {
+ ++teamCounts[iTeam].suicidedPlayers;
+ }
+ else
+ {
+ ++teamCounts[iTeam].killedPlayers;
+ }
+ }
+ }
+ }
+ }
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ void CCSGameRules::UpdateTeamScores()
+ {
+ CTeam *pTerrorists = GetGlobalTeam( TEAM_TERRORIST );
+ CTeam *pCTs = GetGlobalTeam( TEAM_CT );
+
+ Assert( pTerrorists && pCTs );
+
+ if( pTerrorists )
+ pTerrorists->SetScore( m_iNumTerroristWins );
+
+ if( pCTs )
+ pCTs->SetScore( m_iNumCTWins );
+ }
+
+
+ void CCSGameRules::CheckMapConditions()
+ {
+ // Check to see if this map has a bomb target in it
+ if ( gEntList.FindEntityByClassname( NULL, "func_bomb_target" ) )
+ {
+ m_bMapHasBombTarget = true;
+ m_bMapHasBombZone = true;
+ }
+ else if ( gEntList.FindEntityByClassname( NULL, "info_bomb_target" ) )
+ {
+ m_bMapHasBombTarget = true;
+ m_bMapHasBombZone = false;
+ }
+ else
+ {
+ m_bMapHasBombTarget = false;
+ m_bMapHasBombZone = false;
+ }
+
+ // See if the map has func_buyzone entities
+ // Used by CBasePlayer::HandleSignals() to support maps without these entities
+ if ( gEntList.FindEntityByClassname( NULL, "func_buyzone" ) )
+ {
+ m_bMapHasBuyZone = true;
+ }
+ else
+ {
+ m_bMapHasBuyZone = false;
+ }
+
+ // Check to see if this map has hostage rescue zones
+ if ( gEntList.FindEntityByClassname( NULL, "func_hostage_rescue" ) )
+ {
+ m_bMapHasRescueZone = true;
+ }
+ else
+ {
+ m_bMapHasRescueZone = false;
+ }
+
+ // GOOSEMAN : See if this map has func_escapezone entities
+ if ( gEntList.FindEntityByClassname( NULL, "func_escapezone" ) )
+ {
+ m_bMapHasEscapeZone = true;
+ }
+ else
+ {
+ m_bMapHasEscapeZone = false;
+ }
+
+ // Check to see if this map has VIP safety zones
+ if ( gEntList.FindEntityByClassname( NULL, "func_vip_safetyzone" ) )
+ {
+ m_iMapHasVIPSafetyZone = 1;
+ }
+ else
+ {
+ m_iMapHasVIPSafetyZone = 2;
+ }
+ }
+
+
+ void CCSGameRules::SwapAllPlayers()
+ {
+ // MOTODO we have to make sure that enought spaning points exits
+ Assert ( 0 );
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ /* CCSPlayer *pPlayer = CCSPlayer::Instance( i );
+ if ( pPlayer && !FNullEnt( pPlayer->edict() ) )
+ pPlayer->SwitchTeam(); */
+ }
+
+ // Swap Team victories
+ int iTemp;
+
+ iTemp = m_iNumCTWins;
+ m_iNumCTWins = m_iNumTerroristWins;
+ m_iNumTerroristWins = iTemp;
+
+ // Update the clients team score
+ UpdateTeamScores();
+ }
+
+
+ bool CS_FindInList( const char **pStrings, const char *pToFind )
+ {
+ return FindInList( pStrings, pToFind );
+ }
+
+ void CCSGameRules::CleanUpMap()
+ {
+ if (IsLogoMap())
+ return;
+
+ // Recreate all the map entities from the map data (preserving their indices),
+ // then remove everything else except the players.
+
+ // Get rid of all entities except players.
+ CBaseEntity *pCur = gEntList.FirstEnt();
+ while ( pCur )
+ {
+ CWeaponCSBase *pWeapon = dynamic_cast< CWeaponCSBase* >( pCur );
+ // Weapons with owners don't want to be removed..
+ if ( pWeapon )
+ {
+ //=============================================================================
+ // HPE_BEGIN:
+ // [dwenger] Handle round restart processing for the weapon.
+ //=============================================================================
+
+ pWeapon->OnRoundRestart();
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ if ( pWeapon->ShouldRemoveOnRoundRestart() )
+ {
+ UTIL_Remove( pCur );
+ }
+ }
+ // remove entities that has to be restored on roundrestart (breakables etc)
+ else if ( !CS_FindInList( s_PreserveEnts, pCur->GetClassname() ) )
+ {
+ UTIL_Remove( pCur );
+ }
+
+ pCur = gEntList.NextEnt( pCur );
+ }
+
+ // Really remove the entities so we can have access to their slots below.
+ gEntList.CleanupDeleteList();
+
+ // Cancel all queued events, in case a func_bomb_target fired some delayed outputs that
+ // could kill respawning CTs
+ g_EventQueue.Clear();
+
+ // Now reload the map entities.
+ class CCSMapEntityFilter : public IMapEntityFilter
+ {
+ public:
+ virtual bool ShouldCreateEntity( const char *pClassname )
+ {
+ // Don't recreate the preserved entities.
+ if ( !CS_FindInList( s_PreserveEnts, pClassname ) )
+ {
+ return true;
+ }
+ else
+ {
+ // Increment our iterator since it's not going to call CreateNextEntity for this ent.
+ if ( m_iIterator != g_MapEntityRefs.InvalidIndex() )
+ m_iIterator = g_MapEntityRefs.Next( m_iIterator );
+
+ return false;
+ }
+ }
+
+
+ virtual CBaseEntity* CreateNextEntity( const char *pClassname )
+ {
+ if ( m_iIterator == g_MapEntityRefs.InvalidIndex() )
+ {
+ // This shouldn't be possible. When we loaded the map, it should have used
+ // CCSMapLoadEntityFilter, which should have built the g_MapEntityRefs list
+ // with the same list of entities we're referring to here.
+ Assert( false );
+ return NULL;
+ }
+ else
+ {
+ CMapEntityRef &ref = g_MapEntityRefs[m_iIterator];
+ m_iIterator = g_MapEntityRefs.Next( m_iIterator ); // Seek to the next entity.
+
+ if ( ref.m_iEdict == -1 || engine->PEntityOfEntIndex( ref.m_iEdict ) )
+ {
+ // Doh! The entity was delete and its slot was reused.
+ // Just use any old edict slot. This case sucks because we lose the baseline.
+ return CreateEntityByName( pClassname );
+ }
+ else
+ {
+ // Cool, the slot where this entity was is free again (most likely, the entity was
+ // freed above). Now create an entity with this specific index.
+ return CreateEntityByName( pClassname, ref.m_iEdict );
+ }
+ }
+ }
+
+ public:
+ int m_iIterator; // Iterator into g_MapEntityRefs.
+ };
+ CCSMapEntityFilter filter;
+ filter.m_iIterator = g_MapEntityRefs.Head();
+
+ // DO NOT CALL SPAWN ON info_node ENTITIES!
+
+ MapEntity_ParseAllEntities( engine->GetMapEntitiesString(), &filter, true );
+ }
+
+
+ bool CCSGameRules::IsThereABomber()
+ {
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CCSPlayer *pPlayer = CCSPlayer::Instance( i );
+
+ if ( pPlayer && !FNullEnt( pPlayer->edict() ) )
+ {
+ if ( pPlayer->GetTeamNumber() == TEAM_CT )
+ continue;
+
+ if ( pPlayer->HasC4() )
+ return true; //There you are.
+ }
+ }
+
+ //Didn't find a bomber.
+ return false;
+ }
+
+
+ void CCSGameRules::EndRound()
+ {
+ // fake a round end
+ CSGameRules()->TerminateRound( 0.0f, Round_Draw );
+ }
+
+ CBaseEntity *CCSGameRules::GetPlayerSpawnSpot( CBasePlayer *pPlayer )
+ {
+ // gat valid spwan point
+ CBaseEntity *pSpawnSpot = pPlayer->EntSelectSpawnPoint();
+
+ // drop down to ground
+ Vector GroundPos = DropToGround( pPlayer, pSpawnSpot->GetAbsOrigin(), VEC_HULL_MIN, VEC_HULL_MAX );
+
+ // Move the player to the place it said.
+ pPlayer->Teleport( &pSpawnSpot->GetAbsOrigin(), &pSpawnSpot->GetLocalAngles(), &vec3_origin );
+ pPlayer->m_Local.m_vecPunchAngle = vec3_angle;
+
+ return pSpawnSpot;
+ }
+
+ // checks if the spot is clear of players
+ bool CCSGameRules::IsSpawnPointValid( CBaseEntity *pSpot, CBasePlayer *pPlayer )
+ {
+ if ( !pSpot->IsTriggered( pPlayer ) )
+ {
+ return false;
+ }
+
+ Vector mins = GetViewVectors()->m_vHullMin;
+ Vector maxs = GetViewVectors()->m_vHullMax;
+
+ Vector vTestMins = pSpot->GetAbsOrigin() + mins;
+ Vector vTestMaxs = pSpot->GetAbsOrigin() + maxs;
+
+ // First test the starting origin.
+ return UTIL_IsSpaceEmpty( pPlayer, vTestMins, vTestMaxs );
+ }
+
+
+ bool CCSGameRules::IsThereABomb()
+ {
+ bool bBombFound = false;
+
+ /* are there any bombs, either laying around, or in someone's inventory? */
+ if( gEntList.FindEntityByClassname( NULL, WEAPON_C4_CLASSNAME ) != 0 )
+ {
+ bBombFound = true;
+ }
+ /* what about planted bombs!? */
+ else if( gEntList.FindEntityByClassname( NULL, PLANTED_C4_CLASSNAME ) != 0 )
+ {
+ bBombFound = true;
+ }
+
+ return bBombFound;
+ }
+
+ void CCSGameRules::HostageTouched()
+ {
+ if( gpGlobals->curtime > m_flNextHostageAnnouncement && m_iRoundWinStatus == WINNER_NONE )
+ {
+ //BroadcastSound( "Event.HostageTouched" );
+ m_flNextHostageAnnouncement = gpGlobals->curtime + 60.0;
+ }
+ }
+
+ void CCSGameRules::CreateStandardEntities()
+ {
+ // Create the player resource
+ g_pPlayerResource = (CPlayerResource*)CBaseEntity::Create( "cs_player_manager", vec3_origin, vec3_angle );
+
+ // Create the entity that will send our data to the client.
+#ifdef DBGFLAG_ASSERT
+ CBaseEntity *pEnt =
+#endif
+ CBaseEntity::Create( "cs_gamerules", vec3_origin, vec3_angle );
+ Assert( pEnt );
+ }
+
+#define MY_USHRT_MAX 0xffff
+#define MY_UCHAR_MAX 0xff
+
+bool DataHasChanged( void )
+{
+ for ( int i = 0; i < CS_NUM_LEVELS; i++ )
+ {
+ if ( g_iTerroristVictories[i] != 0 || g_iCounterTVictories[i] != 0 )
+ return true;
+ }
+
+ for ( int i = 0; i < WEAPON_MAX; i++ )
+ {
+ if ( g_iWeaponPurchases[i] != 0 )
+ return true;
+ }
+
+ return false;
+}
+
+void CCSGameRules::UploadGameStats( void )
+{
+ g_flGameStatsUpdateTime -= gpGlobals->curtime;
+
+ if ( g_flGameStatsUpdateTime > 0 )
+ return;
+
+ if ( IsBlackMarket() == false )
+ return;
+
+ if ( m_bDontUploadStats == true )
+ return;
+
+ if ( DataHasChanged() == true )
+ {
+ cs_gamestats_t stats;
+ memset( &stats, 0, sizeof(stats) );
+
+ // Header
+ stats.header.iVersion = CS_STATS_BLOB_VERSION;
+ Q_strncpy( stats.header.szGameName, "cstrike", sizeof(stats.header.szGameName) );
+ Q_strncpy( stats.header.szMapName, STRING( gpGlobals->mapname ), sizeof( stats.header.szMapName ) );
+
+ ConVar *hostip = cvar->FindVar( "hostip" );
+ if ( hostip )
+ {
+ int ip = hostip->GetInt();
+ stats.header.ipAddr[0] = ip >> 24;
+ stats.header.ipAddr[1] = ( ip >> 16 ) & MY_UCHAR_MAX;
+ stats.header.ipAddr[2] = ( ip >> 8 ) & MY_UCHAR_MAX;
+ stats.header.ipAddr[3] = ( ip ) & MY_UCHAR_MAX;
+ }
+
+ ConVar *hostport = cvar->FindVar( "hostip" );
+ if ( hostport )
+ {
+ stats.header.port = hostport->GetInt();
+ }
+
+ stats.header.serverid = 0;
+
+ stats.iMinutesPlayed = clamp( (short)( gpGlobals->curtime / 60 ), 0, MY_USHRT_MAX );
+
+ memcpy( stats.iTerroristVictories, g_iTerroristVictories, sizeof( g_iTerroristVictories) );
+ memcpy( stats.iCounterTVictories, g_iCounterTVictories, sizeof( g_iCounterTVictories) );
+ memcpy( stats.iBlackMarketPurchases, g_iWeaponPurchases, sizeof( g_iWeaponPurchases) );
+
+ stats.iAutoBuyPurchases = g_iAutoBuyPurchases;
+ stats.iReBuyPurchases = g_iReBuyPurchases;
+
+ stats.iAutoBuyM4A1Purchases = g_iAutoBuyM4A1Purchases;
+ stats.iAutoBuyAK47Purchases = g_iAutoBuyAK47Purchases;
+ stats.iAutoBuyFamasPurchases = g_iAutoBuyFamasPurchases;
+ stats.iAutoBuyGalilPurchases = g_iAutoBuyGalilPurchases;
+ stats.iAutoBuyVestHelmPurchases = g_iAutoBuyVestHelmPurchases;
+ stats.iAutoBuyVestPurchases = g_iAutoBuyVestPurchases;
+
+ const void *pvBlobData = ( const void * )( &stats );
+ unsigned int uBlobSize = sizeof( stats );
+
+ if ( gamestatsuploader )
+ {
+ gamestatsuploader->UploadGameStats(
+ STRING( gpGlobals->mapname ),
+ CS_STATS_BLOB_VERSION,
+ uBlobSize,
+ pvBlobData );
+ }
+
+
+ memset( g_iWeaponPurchases, 0, sizeof( g_iWeaponPurchases) );
+ memset( g_iTerroristVictories, 0, sizeof( g_iTerroristVictories) );
+ memset( g_iCounterTVictories, 0, sizeof( g_iTerroristVictories) );
+
+ g_iAutoBuyPurchases = 0;
+ g_iReBuyPurchases = 0;
+
+ g_iAutoBuyM4A1Purchases = 0;
+ g_iAutoBuyAK47Purchases = 0;
+ g_iAutoBuyFamasPurchases = 0;
+ g_iAutoBuyGalilPurchases = 0;
+ g_iAutoBuyVestHelmPurchases = 0;
+ g_iAutoBuyVestPurchases = 0;
+ }
+
+ g_flGameStatsUpdateTime = CS_GAME_STATS_UPDATE; //Next update is between 22 and 24 hours.
+}
+#endif // CLIENT_DLL
+
+CBaseCombatWeapon *CCSGameRules::GetNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon )
+{
+ CBaseCombatWeapon *bestWeapon = NULL;
+
+ // search all the weapons looking for the closest next
+ for ( int i = 0; i < MAX_WEAPONS; i++ )
+ {
+ CBaseCombatWeapon *weapon = pPlayer->GetWeapon(i);
+ if ( !weapon )
+ continue;
+
+ if ( !weapon->CanBeSelected() || weapon == pCurrentWeapon )
+ continue;
+
+#ifndef CLIENT_DLL
+ CCSPlayer *csPlayer = ToCSPlayer(pPlayer);
+ CWeaponCSBase *csWeapon = static_cast< CWeaponCSBase * >(weapon);
+ if ( csPlayer && csPlayer->IsBot() && !TheCSBots()->IsWeaponUseable( csWeapon ) )
+ continue;
+#endif // CLIENT_DLL
+
+ if ( bestWeapon )
+ {
+ if ( weapon->GetSlot() < bestWeapon->GetSlot() )
+ {
+ bestWeapon = weapon;
+ }
+ else if ( weapon->GetSlot() == bestWeapon->GetSlot() && weapon->GetPosition() < bestWeapon->GetPosition() )
+ {
+ bestWeapon = weapon;
+ }
+ }
+ else
+ {
+ bestWeapon = weapon;
+ }
+ }
+
+ return bestWeapon;
+}
+
+float CCSGameRules::GetMapRemainingTime()
+{
+#ifdef GAME_DLL
+ if ( nextlevel.GetString() && *nextlevel.GetString() )
+ {
+ return 0;
+ }
+#endif
+
+ // if timelimit is disabled, return -1
+ if ( mp_timelimit.GetInt() <= 0 )
+ return -1;
+
+ // timelimit is in minutes
+ float flTimeLeft = ( m_flGameStartTime + mp_timelimit.GetInt() * 60 ) - gpGlobals->curtime;
+
+ // never return a negative value
+ if ( flTimeLeft < 0 )
+ flTimeLeft = 0;
+
+ return flTimeLeft;
+}
+
+float CCSGameRules::GetMapElapsedTime( void )
+{
+ return gpGlobals->curtime;
+}
+
+float CCSGameRules::GetRoundRemainingTime()
+{
+ return (float) (m_fRoundStartTime + m_iRoundTime) - gpGlobals->curtime;
+}
+
+float CCSGameRules::GetRoundStartTime()
+{
+ return m_fRoundStartTime;
+}
+
+
+float CCSGameRules::GetRoundElapsedTime()
+{
+ return gpGlobals->curtime - m_fRoundStartTime;
+}
+
+
+bool CCSGameRules::ShouldCollide( int collisionGroup0, int collisionGroup1 )
+{
+ if ( collisionGroup0 > collisionGroup1 )
+ {
+ // swap so that lowest is always first
+ ::V_swap(collisionGroup0,collisionGroup1);
+ }
+
+ //Don't stand on COLLISION_GROUP_WEAPONs
+ if( collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT &&
+ collisionGroup1 == COLLISION_GROUP_WEAPON )
+ {
+ return false;
+ }
+
+ // TODO: make a CS-SPECIFIC COLLISION GROUP FOR PHYSICS PROPS THAT USE THIS COLLISION BEHAVIOR.
+
+
+ if ( (collisionGroup0 == COLLISION_GROUP_PLAYER || collisionGroup0 == COLLISION_GROUP_PLAYER_MOVEMENT) &&
+ collisionGroup1 == COLLISION_GROUP_PUSHAWAY )
+ {
+ return false;
+ }
+
+ if ( collisionGroup0 == COLLISION_GROUP_DEBRIS && collisionGroup1 == COLLISION_GROUP_PUSHAWAY )
+ {
+ // let debris and multiplayer objects collide
+ return true;
+ }
+
+ return BaseClass::ShouldCollide( collisionGroup0, collisionGroup1 );
+}
+
+
+bool CCSGameRules::IsFreezePeriod()
+{
+ return m_bFreezePeriod;
+}
+
+
+bool CCSGameRules::IsVIPMap() const
+{
+ //MIKETODO: VIP mode
+ return false;
+}
+
+
+bool CCSGameRules::IsBombDefuseMap() const
+{
+ return m_bMapHasBombTarget;
+}
+
+bool CCSGameRules::IsHostageRescueMap() const
+{
+ return m_bMapHasRescueZone;
+}
+
+bool CCSGameRules::IsLogoMap() const
+{
+ return m_bLogoMap;
+}
+
+float CCSGameRules::GetBuyTimeLength() const
+{
+ return ( mp_buytime.GetFloat() * 60 );
+}
+
+bool CCSGameRules::IsBuyTimeElapsed()
+{
+ return ( GetRoundElapsedTime() > GetBuyTimeLength() );
+}
+
+int CCSGameRules::DefaultFOV()
+{
+ return 90;
+}
+
+const CViewVectors* CCSGameRules::GetViewVectors() const
+{
+ return &g_CSViewVectors;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Init CS ammo definitions
+//-----------------------------------------------------------------------------
+
+// shared ammo definition
+// JAY: Trying to make a more physical bullet response
+#define BULLET_MASS_GRAINS_TO_LB(grains) (0.002285*(grains)/16.0f)
+#define BULLET_MASS_GRAINS_TO_KG(grains) lbs2kg(BULLET_MASS_GRAINS_TO_LB(grains))
+
+// exaggerate all of the forces, but use real numbers to keep them consistent
+#define BULLET_IMPULSE_EXAGGERATION 1
+
+// convert a velocity in ft/sec and a mass in grains to an impulse in kg in/s
+#define BULLET_IMPULSE(grains, ftpersec) ((ftpersec)*12*BULLET_MASS_GRAINS_TO_KG(grains)*BULLET_IMPULSE_EXAGGERATION)
+
+
+static CCSAmmoDef ammoDef;
+CCSAmmoDef* GetCSAmmoDef()
+{
+ GetAmmoDef(); // to initialize the ammo info
+ return &ammoDef;
+}
+
+CAmmoDef* GetAmmoDef()
+{
+ static bool bInitted = false;
+
+ if ( !bInitted )
+ {
+ bInitted = true;
+
+ ammoDef.AddAmmoType( BULLET_PLAYER_50AE, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_50AE_max", 2400 * BULLET_IMPULSE_EXAGGERATION, 0, 10, 14 );
+ ammoDef.AddAmmoType( BULLET_PLAYER_762MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_762mm_max", 2400 * BULLET_IMPULSE_EXAGGERATION, 0, 10, 14 );
+ ammoDef.AddAmmoType( BULLET_PLAYER_556MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_556mm_max", 2400 * BULLET_IMPULSE_EXAGGERATION, 0, 10, 14 );
+ ammoDef.AddAmmoType( BULLET_PLAYER_556MM_BOX, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_556mm_box_max",2400 * BULLET_IMPULSE_EXAGGERATION, 0, 10, 14 );
+ ammoDef.AddAmmoType( BULLET_PLAYER_338MAG, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_338mag_max", 2800 * BULLET_IMPULSE_EXAGGERATION, 0, 12, 16 );
+ ammoDef.AddAmmoType( BULLET_PLAYER_9MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_9mm_max", 2000 * BULLET_IMPULSE_EXAGGERATION, 0, 5, 10 );
+ ammoDef.AddAmmoType( BULLET_PLAYER_BUCKSHOT, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_buckshot_max", 600 * BULLET_IMPULSE_EXAGGERATION, 0, 3, 6 );
+ ammoDef.AddAmmoType( BULLET_PLAYER_45ACP, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_45acp_max", 2100 * BULLET_IMPULSE_EXAGGERATION, 0, 6, 10 );
+ ammoDef.AddAmmoType( BULLET_PLAYER_357SIG, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_357sig_max", 2000 * BULLET_IMPULSE_EXAGGERATION, 0, 4, 8 );
+ ammoDef.AddAmmoType( BULLET_PLAYER_57MM, DMG_BULLET, TRACER_LINE, 0, 0, "ammo_57mm_max", 2000 * BULLET_IMPULSE_EXAGGERATION, 0, 4, 8 );
+ ammoDef.AddAmmoType( AMMO_TYPE_HEGRENADE, DMG_BLAST, TRACER_LINE, 0, 0, "ammo_hegrenade_max", 1, 0 );
+ ammoDef.AddAmmoType( AMMO_TYPE_FLASHBANG, 0, TRACER_LINE, 0, 0, "ammo_flashbang_max", 1, 0 );
+ ammoDef.AddAmmoType( AMMO_TYPE_SMOKEGRENADE, 0, TRACER_LINE, 0, 0, "ammo_smokegrenade_max", 1, 0 );
+
+ //Adrian: I set all the prices to 0 just so the rest of the buy code works
+ //This should be revisited.
+ ammoDef.AddAmmoCost( BULLET_PLAYER_50AE, 0, 7 );
+ ammoDef.AddAmmoCost( BULLET_PLAYER_762MM, 0, 30 );
+ ammoDef.AddAmmoCost( BULLET_PLAYER_556MM, 0, 30 );
+ ammoDef.AddAmmoCost( BULLET_PLAYER_556MM_BOX, 0, 30 );
+ ammoDef.AddAmmoCost( BULLET_PLAYER_338MAG, 0, 10 );
+ ammoDef.AddAmmoCost( BULLET_PLAYER_9MM, 0, 30 );
+ ammoDef.AddAmmoCost( BULLET_PLAYER_BUCKSHOT, 0, 8 );
+ ammoDef.AddAmmoCost( BULLET_PLAYER_45ACP, 0, 25 );
+ ammoDef.AddAmmoCost( BULLET_PLAYER_357SIG, 0, 13 );
+ ammoDef.AddAmmoCost( BULLET_PLAYER_57MM, 0, 50 );
+ }
+
+ return &ammoDef;
+}
+
+#ifndef CLIENT_DLL
+const char *CCSGameRules::GetChatPrefix( bool bTeamOnly, CBasePlayer *pPlayer )
+{
+ const char *pszPrefix = NULL;
+
+ if ( !pPlayer ) // dedicated server output
+ {
+ pszPrefix = "";
+ }
+ else
+ {
+ // team only
+ if ( bTeamOnly == TRUE )
+ {
+ if ( pPlayer->GetTeamNumber() == TEAM_CT )
+ {
+ if ( pPlayer->m_lifeState == LIFE_ALIVE )
+ {
+ pszPrefix = "(Counter-Terrorist)";
+ }
+ else
+ {
+ pszPrefix = "*DEAD*(Counter-Terrorist)";
+ }
+ }
+ else if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST )
+ {
+ if ( pPlayer->m_lifeState == LIFE_ALIVE )
+ {
+ pszPrefix = "(Terrorist)";
+ }
+ else
+ {
+ pszPrefix = "*DEAD*(Terrorist)";
+ }
+ }
+ else if ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR )
+ {
+ pszPrefix = "(Spectator)";
+ }
+ }
+ // everyone
+ else
+ {
+ if ( pPlayer->m_lifeState == LIFE_ALIVE )
+ {
+ pszPrefix = "";
+ }
+ else
+ {
+ if ( pPlayer->GetTeamNumber() != TEAM_SPECTATOR )
+ {
+ pszPrefix = "*DEAD*";
+ }
+ else
+ {
+ pszPrefix = "*SPEC*";
+ }
+ }
+ }
+ }
+
+ return pszPrefix;
+}
+
+const char *CCSGameRules::GetChatLocation( bool bTeamOnly, CBasePlayer *pPlayer )
+{
+ if ( !pPlayer ) // dedicated server output
+ {
+ return NULL;
+ }
+
+ // only teammates see locations
+ if ( !bTeamOnly )
+ return NULL;
+
+ // only living players have locations
+ if ( pPlayer->GetTeamNumber() != TEAM_CT && pPlayer->GetTeamNumber() != TEAM_TERRORIST )
+ return NULL;
+
+ if ( !pPlayer->IsAlive() )
+ return NULL;
+
+ return pPlayer->GetLastKnownPlaceName();
+}
+
+const char *CCSGameRules::GetChatFormat( bool bTeamOnly, CBasePlayer *pPlayer )
+{
+ if ( !pPlayer ) // dedicated server output
+ {
+ return NULL;
+ }
+
+ const char *pszFormat = NULL;
+
+ // team only
+ if ( bTeamOnly == TRUE )
+ {
+ if ( pPlayer->GetTeamNumber() == TEAM_CT )
+ {
+ if ( pPlayer->m_lifeState == LIFE_ALIVE )
+ {
+ const char *chatLocation = GetChatLocation( bTeamOnly, pPlayer );
+ if ( chatLocation && *chatLocation )
+ {
+ pszFormat = "Cstrike_Chat_CT_Loc";
+ }
+ else
+ {
+ pszFormat = "Cstrike_Chat_CT";
+ }
+ }
+ else
+ {
+ pszFormat = "Cstrike_Chat_CT_Dead";
+ }
+ }
+ else if ( pPlayer->GetTeamNumber() == TEAM_TERRORIST )
+ {
+ if ( pPlayer->m_lifeState == LIFE_ALIVE )
+ {
+ const char *chatLocation = GetChatLocation( bTeamOnly, pPlayer );
+ if ( chatLocation && *chatLocation )
+ {
+ pszFormat = "Cstrike_Chat_T_Loc";
+ }
+ else
+ {
+ pszFormat = "Cstrike_Chat_T";
+ }
+ }
+ else
+ {
+ pszFormat = "Cstrike_Chat_T_Dead";
+ }
+ }
+ else if ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR )
+ {
+ pszFormat = "Cstrike_Chat_Spec";
+ }
+ }
+ // everyone
+ else
+ {
+ if ( pPlayer->m_lifeState == LIFE_ALIVE )
+ {
+ pszFormat = "Cstrike_Chat_All";
+ }
+ else
+ {
+ if ( pPlayer->GetTeamNumber() != TEAM_SPECTATOR )
+ {
+ pszFormat = "Cstrike_Chat_AllDead";
+ }
+ else
+ {
+ pszFormat = "Cstrike_Chat_AllSpec";
+ }
+ }
+ }
+
+ return pszFormat;
+}
+
+void CCSGameRules::ClientSettingsChanged( CBasePlayer *pPlayer )
+{
+ const char *pszNewName = engine->GetClientConVarValue( pPlayer->entindex(), "name" );
+ const char *pszOldName = pPlayer->GetPlayerName();
+ CCSPlayer *pCSPlayer = (CCSPlayer*)pPlayer;
+ if ( pszOldName[0] != 0 && Q_strncmp( pszOldName, pszNewName, MAX_PLAYER_NAME_LENGTH-1 ) )
+ {
+ pCSPlayer->ChangeName( pszNewName );
+ }
+
+ pCSPlayer->m_bShowHints = true;
+ if ( pCSPlayer->IsNetClient() )
+ {
+ const char *pShowHints = engine->GetClientConVarValue( engine->IndexOfEdict( pCSPlayer->edict() ), "cl_autohelp" );
+ if ( pShowHints && atoi( pShowHints ) <= 0 )
+ {
+ pCSPlayer->m_bShowHints = false;
+ }
+ }
+}
+
+bool CCSGameRules::FAllowNPCs( void )
+{
+ return false;
+}
+
+bool CCSGameRules::IsFriendlyFireOn( void )
+{
+ return friendlyfire.GetBool();
+}
+
+
+CON_COMMAND( map_showspawnpoints, "Shows player spawn points (red=invalid)" )
+{
+ CSGameRules()->ShowSpawnPoints();
+}
+
+void DrawSphere( const Vector& pos, float radius, int r, int g, int b, float lifetime )
+{
+ Vector edge, lastEdge;
+ NDebugOverlay::Line( pos, pos + Vector( 0, 0, 50 ), r, g, b, true, lifetime );
+
+ lastEdge = Vector( radius + pos.x, pos.y, pos.z );
+ float angle;
+ for( angle=0.0f; angle <= 360.0f; angle += 22.5f )
+ {
+ edge.x = radius * BotCOS( angle ) + pos.x;
+ edge.y = pos.y;
+ edge.z = radius * BotSIN( angle ) + pos.z;
+
+ NDebugOverlay::Line( edge, lastEdge, r, g, b, true, lifetime );
+
+ lastEdge = edge;
+ }
+
+ lastEdge = Vector( pos.x, radius + pos.y, pos.z );
+ for( angle=0.0f; angle <= 360.0f; angle += 22.5f )
+ {
+ edge.x = pos.x;
+ edge.y = radius * BotCOS( angle ) + pos.y;
+ edge.z = radius * BotSIN( angle ) + pos.z;
+
+ NDebugOverlay::Line( edge, lastEdge, r, g, b, true, lifetime );
+
+ lastEdge = edge;
+ }
+
+ lastEdge = Vector( pos.x, radius + pos.y, pos.z );
+ for( angle=0.0f; angle <= 360.0f; angle += 22.5f )
+ {
+ edge.x = radius * BotCOS( angle ) + pos.x;
+ edge.y = radius * BotSIN( angle ) + pos.y;
+ edge.z = pos.z;
+
+ NDebugOverlay::Line( edge, lastEdge, r, g, b, true, lifetime );
+
+ lastEdge = edge;
+ }
+}
+
+CON_COMMAND_F( map_showbombradius, "Shows bomb radius from the center of each bomb site and planted bomb.", FCVAR_CHEAT )
+{
+ float flBombDamage = 500.0f;
+ if ( g_pMapInfo )
+ flBombDamage = g_pMapInfo->m_flBombRadius;
+ float flBombRadius = flBombDamage * 3.5f;
+ Msg( "Bomb Damage is %.0f, Radius is %.0f\n", flBombDamage, flBombRadius );
+
+ CBaseEntity* ent = NULL;
+ while ( ( ent = gEntList.FindEntityByClassname( ent, "func_bomb_target" ) ) != NULL )
+ {
+ const Vector &pos = ent->WorldSpaceCenter();
+ DrawSphere( pos, flBombRadius, 255, 255, 0, 10 );
+ }
+
+ ent = NULL;
+ while ( ( ent = gEntList.FindEntityByClassname( ent, "planted_c4" ) ) != NULL )
+ {
+ const Vector &pos = ent->WorldSpaceCenter();
+ DrawSphere( pos, flBombRadius, 255, 0, 0, 10 );
+ }
+}
+
+CON_COMMAND_F( map_setbombradius, "Sets the bomb radius for the map.", FCVAR_CHEAT )
+{
+ if ( args.ArgC() != 2 )
+ return;
+
+ if ( !UTIL_IsCommandIssuedByServerAdmin() )
+ return;
+
+ if ( !g_pMapInfo )
+ CBaseEntity::Create( "info_map_parameters", vec3_origin, vec3_angle );
+
+ if ( !g_pMapInfo )
+ return;
+
+ g_pMapInfo->m_flBombRadius = atof( args[1] );
+ map_showbombradius( args );
+}
+
+void CreateBlackMarketString( void )
+{
+ g_StringTableBlackMarket = networkstringtable->CreateStringTable( "BlackMarketTable" , 1 );
+}
+
+int CCSGameRules::GetStartMoney( void )
+{
+ if ( IsBlackMarket() )
+ {
+ return atoi( mp_startmoney.GetDefault() );
+ }
+
+ return mp_startmoney.GetInt();
+}
+
+
+
+//=============================================================================
+// HPE_BEGIN:
+// [menglish] Set up anything for all players that changes based on new players spawning mid-game
+// Find and return fun fact data
+//=============================================================================
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when a player joins the game after it's started yet can still spawn in
+//-----------------------------------------------------------------------------
+void CCSGameRules::SpawningLatePlayer( CCSPlayer* pLatePlayer )
+{
+ //Reset the round kills number of enemies for the opposite team
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CCSPlayer *pPlayer = (CCSPlayer*) UTIL_PlayerByIndex( i );
+ if(pPlayer)
+ {
+ if(pPlayer->GetTeamNumber() == pLatePlayer->GetTeamNumber())
+ {
+ continue;
+ }
+ pPlayer->m_NumEnemiesAtRoundStart++;
+ }
+ }
+}
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+//=============================================================================
+// HPE_BEGIN:
+// [pfreese] Test for "pistol" round, defined as the default starting round
+// when players cannot purchase anything primary weapons
+//=============================================================================
+
+bool CCSGameRules::IsPistolRound()
+{
+ return m_iTotalRoundsPlayed == 0 && GetStartMoney() <= 800;
+}
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+//=============================================================================
+// HPE_BEGIN:
+// [tj] So game rules can react to damage taken
+// [menglish]
+//=============================================================================
+
+void CCSGameRules::PlayerTookDamage(CCSPlayer* player, const CTakeDamageInfo &damageInfo)
+{
+ CBaseEntity *pInflictor = damageInfo.GetInflictor();
+ CBaseEntity *pAttacker = damageInfo.GetAttacker();
+ CCSPlayer *pCSScorer = (CCSPlayer *)(GetDeathScorer( pAttacker, pInflictor ));
+
+ if ( player && pCSScorer )
+ {
+ if (player->GetTeamNumber() == TEAM_CT)
+ {
+ m_bNoCTsDamaged = false;
+ }
+
+ if (player->GetTeamNumber() == TEAM_TERRORIST)
+ {
+ m_bNoTerroristsDamaged = false;
+ }
+ // set the first blood if this is the first and the victim is on a different team then the player
+ if ( m_pFirstBlood == NULL && pCSScorer != player && pCSScorer->GetTeamNumber() != player ->GetTeamNumber() )
+ {
+ m_pFirstBlood = pCSScorer;
+ m_firstBloodTime = gpGlobals->curtime - m_fRoundStartTime;
+ }
+ }
+}
+
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+#endif
+
+bool CCSGameRules::IsConnectedUserInfoChangeAllowed( CBasePlayer *pPlayer )
+{
+#ifdef GAME_DLL
+ if( pPlayer )
+ {
+ int iPlayerTeam = pPlayer->GetTeamNumber();
+ if( ( iPlayerTeam == TEAM_CT ) || ( iPlayerTeam == TEAM_TERRORIST ) )
+ return false;
+ }
+#else
+ int iLocalPlayerTeam = GetLocalPlayerTeam();
+ if( ( iLocalPlayerTeam == TEAM_CT ) || ( iLocalPlayerTeam == TEAM_TERRORIST ) )
+ return false;
+#endif
+
+ return true;
+}
+
+#ifdef GAME_DLL
+
+struct convar_tags_t
+{
+ const char *pszConVar;
+ const char *pszTag;
+};
+
+// The list of convars that automatically turn on tags when they're changed.
+// Convars in this list need to have the FCVAR_NOTIFY flag set on them, so the
+// tags are recalculated and uploaded to the master server when the convar is changed.
+convar_tags_t convars_to_check_for_tags[] =
+{
+ { "mp_friendlyfire", "friendlyfire" },
+ { "bot_quota", "bots" },
+ { "sv_nostats", "nostats" },
+ { "mp_startmoney", "startmoney" },
+ { "sv_allowminmodels", "nominmodels" },
+ { "sv_enablebunnyhopping", "bunnyhopping" },
+ { "sv_competitive_minspec", "compspec" },
+ { "mp_holiday_nogifts", "nogifts" },
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Engine asks for the list of convars that should tag the server
+//-----------------------------------------------------------------------------
+void CCSGameRules::GetTaggedConVarList( KeyValues *pCvarTagList )
+{
+ BaseClass::GetTaggedConVarList( pCvarTagList );
+
+ for ( int i = 0; i < ARRAYSIZE(convars_to_check_for_tags); i++ )
+ {
+ KeyValues *pKV = new KeyValues( "tag" );
+ pKV->SetString( "convar", convars_to_check_for_tags[i].pszConVar );
+ pKV->SetString( "tag", convars_to_check_for_tags[i].pszTag );
+
+ pCvarTagList->AddSubKey( pKV );
+ }
+}
+
+#endif
+
+
+int CCSGameRules::GetBlackMarketPriceForWeapon( int iWeaponID )
+{
+ if ( m_pPrices == NULL )
+ {
+ GetBlackMarketPriceList();
+ }
+
+ if ( m_pPrices )
+ return m_pPrices->iCurrentPrice[iWeaponID];
+ else
+ return 0;
+}
+
+int CCSGameRules::GetBlackMarketPreviousPriceForWeapon( int iWeaponID )
+{
+ if ( m_pPrices == NULL )
+ {
+ GetBlackMarketPriceList();
+ }
+
+ if ( m_pPrices )
+ return m_pPrices->iPreviousPrice[iWeaponID];
+ else
+ return 0;
+}
+
+const weeklyprice_t *CCSGameRules::GetBlackMarketPriceList( void )
+{
+ if ( m_StringTableBlackMarket == NULL )
+ {
+ m_StringTableBlackMarket = networkstringtable->FindTable( CS_GAMERULES_BLACKMARKET_TABLE_NAME);
+ }
+
+ if ( m_pPrices == NULL )
+ {
+ int iSize = 0;
+ INetworkStringTable *pTable = m_StringTableBlackMarket;
+ if ( pTable && pTable->GetNumStrings() > 0 )
+ {
+ m_pPrices = (const weeklyprice_t *)pTable->GetStringUserData( 0, &iSize );
+ }
+ }
+
+ if ( m_pPrices )
+ {
+ PrepareEquipmentInfo();
+ }
+
+ return m_pPrices;
+}
+
+void CCSGameRules::SetBlackMarketPrices( bool bSetDefaults )
+{
+ for ( int i = 1; i < WEAPON_MAX; i++ )
+ {
+ if ( i == WEAPON_SHIELDGUN )
+ continue;
+
+ CCSWeaponInfo *info = GetWeaponInfo( (CSWeaponID)i );
+
+ if ( info == NULL )
+ continue;
+
+ if ( bSetDefaults == false )
+ {
+ info->SetWeaponPrice( GetBlackMarketPriceForWeapon( i ) );
+ info->SetPreviousPrice( GetBlackMarketPreviousPriceForWeapon( i ) );
+ }
+ else
+ {
+ info->SetWeaponPrice( info->GetDefaultPrice() );
+ }
+ }
+}
+
+#ifdef CLIENT_DLL
+
+CCSGameRules::CCSGameRules()
+{
+ CSGameRules()->m_StringTableBlackMarket = NULL;
+ m_pPrices = NULL;
+ m_bBlackMarket = false;
+}
+
+void TestTable( void )
+{
+ CSGameRules()->m_StringTableBlackMarket = networkstringtable->FindTable( CS_GAMERULES_BLACKMARKET_TABLE_NAME);
+
+ if ( CSGameRules()->m_StringTableBlackMarket == NULL )
+ return;
+
+ int iIndex = CSGameRules()->m_StringTableBlackMarket->FindStringIndex( "blackmarket_prices" );
+ int iSize = 0;
+
+ const weeklyprice_t *pPrices = NULL;
+
+ pPrices = (const weeklyprice_t *)(CSGameRules()->m_StringTableBlackMarket)->GetStringUserData( iIndex, &iSize );
+}
+
+#ifdef DEBUG
+ConCommand cs_testtable( "cs_testtable", TestTable );
+#endif
+
+//-----------------------------------------------------------------------------
+// Enforce certain values on the specified convar.
+//-----------------------------------------------------------------------------
+void EnforceCompetitiveCVar( const char *szCvarName, float fMinValue, float fMaxValue = FLT_MAX, int iArgs = 0, ... )
+{
+ // Doing this check first because OK values might be outside the min/max range
+ ConVarRef competitiveConvar(szCvarName);
+ float fValue = competitiveConvar.GetFloat();
+ va_list vl;
+ va_start(vl, iArgs);
+ for( int i=0; i< iArgs; ++i )
+ {
+ if( (int)fValue == (int)va_arg(vl,double) )
+ return;
+ }
+ va_end(vl);
+
+ if( fValue < fMinValue || fValue > fMaxValue )
+ {
+ float fNewValue = MAX( MIN( fValue, fMaxValue ), fMinValue );
+ competitiveConvar.SetValue( fNewValue );
+ DevMsg( "Convar %s enforced by server (see sv_competitive_minspec.) Set to %2f.\n", szCvarName, fNewValue );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// An interface used by ENABLE_COMPETITIVE_CONVAR macro that lets the classes
+// defined in the macro to be stored and acted on.
+//-----------------------------------------------------------------------------
+class ICompetitiveConvar
+{
+public:
+ // It is a best practice to always have a virtual destructor in an interface
+ // class. Otherwise if the derived classes have destructors they will not be
+ // called.
+ virtual ~ICompetitiveConvar() {}
+ virtual void BackupConvar() = 0;
+ virtual void EnforceRestrictions() = 0;
+ virtual void RestoreOriginalValue() = 0;
+ virtual void InstallChangeCallback() = 0;
+};
+
+//-----------------------------------------------------------------------------
+// A manager for all enforced competitive convars.
+//-----------------------------------------------------------------------------
+class CCompetitiveCvarManager : public CAutoGameSystem
+{
+public:
+ typedef CUtlVector<ICompetitiveConvar*> CompetitiveConvarList_t;
+ static void AddConvarToList( ICompetitiveConvar* pCVar )
+ {
+ GetConvarList()->AddToTail( pCVar );
+ }
+
+ static void BackupAllConvars()
+ {
+ FOR_EACH_VEC( *GetConvarList(), i )
+ {
+ (*GetConvarList())[i]->BackupConvar();
+ }
+ }
+
+ static void EnforceRestrictionsOnAllConvars()
+ {
+ FOR_EACH_VEC( *GetConvarList(), i )
+ {
+ (*GetConvarList())[i]->EnforceRestrictions();
+ }
+ }
+
+ static void RestoreAllOriginalValues()
+ {
+ FOR_EACH_VEC( *GetConvarList(), i )
+ {
+ (*GetConvarList())[i]->RestoreOriginalValue();
+ }
+ }
+
+ static CompetitiveConvarList_t* GetConvarList()
+ {
+ if( !s_pCompetitiveConvars )
+ {
+ s_pCompetitiveConvars = new CompetitiveConvarList_t();
+ }
+ return s_pCompetitiveConvars;
+ }
+
+ static KeyValues* GetConVarBackupKV()
+ {
+ if( !s_pConVarBackups )
+ {
+ s_pConVarBackups = new KeyValues("ConVarBackups");
+ }
+ return s_pConVarBackups;
+ }
+
+ virtual bool Init()
+ {
+ FOR_EACH_VEC( *GetConvarList(), i )
+ {
+ (*GetConvarList())[i]->InstallChangeCallback();
+ }
+ return true;
+ }
+
+ virtual void Shutdown()
+ {
+ FOR_EACH_VEC( *GetConvarList(), i )
+ {
+ delete (*GetConvarList())[i];
+ }
+ delete s_pCompetitiveConvars;
+ s_pCompetitiveConvars = null;
+ s_pConVarBackups->deleteThis();
+ s_pConVarBackups = null;
+ }
+private:
+ static CompetitiveConvarList_t* s_pCompetitiveConvars;
+ static KeyValues* s_pConVarBackups;
+};
+static CCompetitiveCvarManager *s_pCompetitiveCvarManager = new CCompetitiveCvarManager();
+CCompetitiveCvarManager::CompetitiveConvarList_t* CCompetitiveCvarManager::s_pCompetitiveConvars = null;
+KeyValues* CCompetitiveCvarManager::s_pConVarBackups = null;
+
+//-----------------------------------------------------------------------------
+// Macro to define restrictions on convars with "sv_competitive_minspec 1"
+// Usage: ENABLE_COMPETITIVE_CONVAR( convarName, minValue, maxValue, optionalValues, opVal1, opVal2, ...
+//-----------------------------------------------------------------------------
+#define ENABLE_COMPETITIVE_CONVAR( convarName, ... ) \
+class CCompetitiveMinspecConvar##convarName : public ICompetitiveConvar { \
+public: \
+ CCompetitiveMinspecConvar##convarName(){ CCompetitiveCvarManager::AddConvarToList(this);} \
+ static void on_changed_##convarName( IConVar *var, const char *pOldValue, float flOldValue ){ \
+ if( sv_competitive_minspec.GetBool() ) { \
+ EnforceCompetitiveCVar( #convarName , __VA_ARGS__ ); }\
+ else {\
+ CCompetitiveCvarManager::GetConVarBackupKV()->SetFloat( #convarName, ConVarRef( #convarName ).GetFloat() ); } } \
+ virtual void BackupConvar() { CCompetitiveCvarManager::GetConVarBackupKV()->SetFloat( #convarName, ConVarRef( #convarName ).GetFloat() ); } \
+ virtual void EnforceRestrictions() { EnforceCompetitiveCVar( #convarName , __VA_ARGS__ ); } \
+ virtual void RestoreOriginalValue() { ConVarRef(#convarName).SetValue(CCompetitiveCvarManager::GetConVarBackupKV()->GetFloat( #convarName ) ); } \
+ virtual void InstallChangeCallback() { static_cast<ConVar*>(ConVarRef( #convarName ).GetLinkedConVar())->InstallChangeCallback( CCompetitiveMinspecConvar##convarName::on_changed_##convarName); } \
+}; \
+static CCompetitiveMinspecConvar##convarName *s_pCompetitiveConvar##convarName = new CCompetitiveMinspecConvar##convarName();
+
+//-----------------------------------------------------------------------------
+// Callback function for sv_competitive_minspec convar value change.
+//-----------------------------------------------------------------------------
+void sv_competitive_minspec_changed_f( IConVar *var, const char *pOldValue, float flOldValue )
+{
+ ConVar *pCvar = static_cast<ConVar*>(var);
+
+ if( pCvar->GetBool() == true && (bool)flOldValue == false )
+ {
+ // Backup the values of each cvar and enforce new ones
+ CCompetitiveCvarManager::BackupAllConvars();
+ CCompetitiveCvarManager::EnforceRestrictionsOnAllConvars();
+ }
+ else if( pCvar->GetBool() == false && (bool)flOldValue == true )
+ {
+ // If sv_competitive_minspec is disabled, restore old client values
+ CCompetitiveCvarManager::RestoreAllOriginalValues();
+ }
+}
+#endif
+
+static ConVar sv_competitive_minspec( "sv_competitive_minspec",
+ "0",
+ FCVAR_REPLICATED | FCVAR_NOTIFY,
+ "Enable to force certain client convars to minimum/maximum values to help prevent competitive advantages:\n \
+ r_drawdetailprops = 1\n \
+ r_staticprop_lod = minimum -1 maximum 3\n \
+ fps_max minimum 59 (0 works too)\n \
+ cl_detailfade minimum 400\n \
+ cl_detaildist minimum 1200\n \
+ cl_interp_ratio = minimum 1 maximum 2\n \
+ cl_interp = minimum 0 maximum 0.031\n \
+ "
+#ifdef CLIENT_DLL
+ ,sv_competitive_minspec_changed_f
+#endif
+ );
+
+#ifdef CLIENT_DLL
+
+ENABLE_COMPETITIVE_CONVAR( r_drawdetailprops, true, true ); // force r_drawdetailprops on
+ENABLE_COMPETITIVE_CONVAR( r_staticprop_lod, -1, 3 ); // force r_staticprop_lod from -1 to 3
+ENABLE_COMPETITIVE_CONVAR( fps_max, 59, FLT_MAX, 1, 0 ); // force fps_max above 59. One additional value (0) works
+ENABLE_COMPETITIVE_CONVAR( cl_detailfade, 400 ); // force cl_detailfade above 400.
+ENABLE_COMPETITIVE_CONVAR( cl_detaildist, 1200 ); // force cl_detaildist above 1200.
+ENABLE_COMPETITIVE_CONVAR( cl_interp_ratio, 1, 2 ); // force cl_interp_ratio from 1 to 2
+ENABLE_COMPETITIVE_CONVAR( cl_interp, 0, 0.031 ); // force cl_interp from 0.0152 to 0.031
+
+// Stubs for replay client code
+const char *GetMapDisplayName( const char *pMapName )
+{
+ return pMapName;
+}
+
+bool IsTakingAFreezecamScreenshot()
+{
+ return false;
+}
+
+#endif
diff --git a/game/shared/cstrike/cs_gamerules.h b/game/shared/cstrike/cs_gamerules.h
new file mode 100644
index 0000000..02bda1b
--- /dev/null
+++ b/game/shared/cstrike/cs_gamerules.h
@@ -0,0 +1,537 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: The TF Game rules object
+//
+// $Workfile: $
+// $Date: $
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef CS_GAMERULES_H
+#define CS_GAMERULES_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "teamplay_gamerules.h"
+#include "convar.h"
+#include "cs_shareddefs.h"
+#include "gamevars_shared.h"
+
+#ifdef CLIENT_DLL
+ #include "c_cs_player.h"
+ #include "networkstringtable_clientdll.h"
+#else
+ #include "cs_player.h"
+ #include "funfactmgr_cs.h"
+#endif
+
+#include "cs_urlretrieveprices.h"
+
+//extern ConVar mp_dynamicpricing;
+
+#define CS_GAMERULES_BLACKMARKET_TABLE_NAME "BlackMarketTable"
+
+#define WINNER_NONE 0
+#define WINNER_DRAW 1
+#define WINNER_TER TEAM_TERRORIST
+#define WINNER_CT TEAM_CT
+
+//=============================================================================
+// HPE_BEGIN:
+// [tj] Forward declaration so we can track bot suicides in the game rules.
+//=============================================================================
+
+class CCSBot;
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+extern ConVar mp_startmoney;
+extern ConVar mp_tkpunish;
+extern ConVar mp_c4timer;
+extern ConVar mp_buytime;
+extern ConVar mp_freezetime;
+extern ConVar mp_playerid;
+
+#ifndef CLIENT_DLL
+ extern ConVar mp_autoteambalance;
+#endif // !CLIENT_DLL
+
+
+#ifdef CLIENT_DLL
+ #define CCSGameRules C_CSGameRules
+ #define CCSGameRulesProxy C_CSGameRulesProxy
+#endif
+
+#ifndef CLIENT_DLL
+ struct playerscore_t
+ {
+ int iPlayerIndex;
+ int iScore;
+ };
+#endif
+
+
+class CCSGameRulesProxy : public CGameRulesProxy
+{
+public:
+ DECLARE_CLASS( CCSGameRulesProxy, CGameRulesProxy );
+ DECLARE_NETWORKCLASS();
+};
+
+
+class CCSGameRules : public CTeamplayRules
+{
+public:
+ DECLARE_CLASS( CCSGameRules, CTeamplayRules );
+
+ // Stuff that is shared between client and server.
+ bool IsFreezePeriod();
+
+ virtual bool ShouldCollide( int collisionGroup0, int collisionGroup1 );
+
+ float GetMapRemainingTime(); // time till end of map, -1 if timelimit is disabled
+ float GetMapElapsedTime(); // How much time has elapsed since the map started.
+ float GetRoundRemainingTime(); // time till end of round
+ float GetRoundStartTime(); // When this round started.
+ float GetRoundElapsedTime(); // How much time has elapsed since the round started.
+ float GetBuyTimeLength() const;
+ int GetRoundLength() const { return m_iRoundTime; }
+ int SelectDefaultTeam( bool ignoreBots = false );
+ int GetHumanTeam(); // TEAM_UNASSIGNED if no restrictions
+
+ bool IsVIPMap() const;
+ bool IsBombDefuseMap() const;
+ bool IsHostageRescueMap() const;
+ bool IsIntermission() const;
+ bool IsLogoMap() const;
+ bool IsSpawnPointValid( CBaseEntity *pSpot, CBasePlayer *pPlayer );
+
+ bool IsBuyTimeElapsed();
+
+ virtual int DefaultFOV();
+
+ // Get the view vectors for this mod.
+ virtual const CViewVectors* GetViewVectors() const;
+
+ void UploadGameStats( void );
+ int GetStartMoney( void );
+
+ virtual bool IsConnectedUserInfoChangeAllowed( CBasePlayer *pPlayer );
+
+private:
+
+ float GetExplosionDamageAdjustment(Vector & vecSrc, Vector & vecEnd, CBaseEntity *pEntityToIgnore); // returns multiplier between 0.0 and 1.0 that is the percentage of any damage done from vecSrc to vecEnd that actually makes it.
+ float GetAmountOfEntityVisible(Vector & src, CBaseEntity *player); // returns a value from 0 to 1 that is the percentage of player visible from src.
+
+ CNetworkVar( bool, m_bFreezePeriod ); // TRUE at beginning of round, set to FALSE when the period expires
+ CNetworkVar( int, m_iRoundTime ); // (From mp_roundtime) - How many seconds long this round is.
+ CNetworkVar( float, m_fRoundStartTime ); // time round has started
+ CNetworkVar( float, m_flGameStartTime );
+ CNetworkVar( int, m_iHostagesRemaining );
+ CNetworkVar( bool, m_bMapHasBombTarget );
+ CNetworkVar( bool, m_bMapHasRescueZone );
+ CNetworkVar( bool, m_bLogoMap ); // If there's an info_player_logo entity, then it's a logo map.
+ CNetworkVar( bool, m_bBlackMarket );
+
+ bool m_bDontUploadStats;
+
+public:
+
+ bool IsBlackMarket( void ) { return m_bBlackMarket; }
+ int GetNumHostagesRemaining( void ) { return m_iHostagesRemaining; }
+
+ virtual CBaseCombatWeapon *GetNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon );
+
+ virtual const unsigned char *GetEncryptionKey( void ) { return (unsigned char *)"d7NSuLq2"; } // both the client and server need this key
+
+#ifdef CLIENT_DLL
+
+ DECLARE_CLIENTCLASS_NOBASE(); // This makes datatables able to access our private vars.
+ CCSGameRules();
+
+#else
+
+ DECLARE_SERVERCLASS_NOBASE(); // This makes datatables able to access our private vars.
+
+ CCSGameRules();
+ virtual ~CCSGameRules();
+
+ void DumpTimers( void ) const; // debugging to help track down a stuck server (rare?)
+
+ CBaseEntity *GetPlayerSpawnSpot( CBasePlayer *pPlayer );
+
+ static void EndRound();
+
+ virtual void PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info );
+ virtual void Think();
+
+ // Called at the end of GameFrame (i.e. after all game logic has run this frame)
+ virtual void EndGameFrame( void );
+
+ // Called when game rules are destroyed by CWorld
+ virtual void LevelShutdown( void );
+
+ virtual bool ClientCommand( CBaseEntity *pEdict, const CCommand &args );
+ virtual void PlayerSpawn( CBasePlayer *pPlayer );
+ void ShowSpawnPoints();
+
+ virtual void ClientCommandKeyValues( edict_t *pEntity, KeyValues *pKeyValues );
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [menglish] Set up anything for all players that changes based on new players spawning mid-game
+ // Find and return fun fact data
+ // [pfreese] Tracking of "pistol" round
+ //=============================================================================
+ virtual void SpawningLatePlayer(CCSPlayer* pLatePlayer);
+
+ bool IsPistolRound();
+
+ void HostageKilled() { m_hostageWasKilled = true; }
+ void HostageInjured() { m_hostageWasInjured = true; }
+
+ bool WasHostageKilled() { return m_hostageWasKilled; }
+ bool WasHostageInjured() { return m_hostageWasInjured; }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] So game rules can react to damage taken
+ //=============================================================================
+
+ void PlayerTookDamage(CCSPlayer* player, const CTakeDamageInfo &damageInfo);
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+
+ virtual bool PlayTextureSounds( void ) { return true; }
+ // Let the game rules specify if fall death should fade screen to black
+ virtual bool FlPlayerFallDeathDoesScreenFade( CBasePlayer *pl ) { return FALSE; }
+
+ virtual void RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, CBaseEntity *pEntityIgnore );
+ void RadiusDamage( const CTakeDamageInfo &info, const Vector &vecSrcIn, float flRadius, int iClassIgnore, bool bIgnoreWorld );
+
+ virtual void UpdateClientData( CBasePlayer *pl );
+
+ // Death notices
+ virtual void DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info );
+
+ virtual void InitDefaultAIRelationships( void );
+
+ virtual const char *GetGameDescription( void ) { return "Counter-Strike: Source"; } // this is the game name that gets seen in the server browser
+ virtual const char *AIClassText(int classType);
+
+ virtual bool FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon );
+
+ virtual const char *SetDefaultPlayerTeam( CBasePlayer *pPlayer );
+
+ // Called before entities are created
+ virtual void LevelInitPreEntity();
+
+ // Called after the map has finished loading.
+ virtual void LevelInitPostEntity();
+
+ virtual float FlPlayerFallDamage( CBasePlayer *pPlayer );
+
+ virtual void ClientDisconnected( edict_t *pClient );
+
+ // Recreate all the map entities from the map data (preserving their indices),
+ // then remove everything else except the players.
+ // Also get rid of all world decals.
+ void CleanUpMap();
+
+ void CheckFreezePeriodExpired();
+ void CheckRoundTimeExpired();
+
+ // check if the scenario has been won/lost
+ // return true if the scenario is over, false if the scenario is still in progress
+ bool CheckWinConditions( void );
+
+ void TerminateRound( float tmDelay, int reason );
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] A place to check achievements that occur at the end of the round
+ //=============================================================================
+ void ProcessEndOfRoundAchievements(int iWinnerTeam, int iReason);
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ void RestartRound( void );
+ void BalanceTeams( void );
+ void MoveHumansToHumanTeam( void );
+ bool TeamFull( int team_id );
+ bool TeamStacked( int newTeam_id, int curTeam_id );
+ bool FPlayerCanRespawn( CBasePlayer *pPlayer );
+ void UpdateTeamScores();
+ void CheckMapConditions();
+ void MarkLivingPlayersOnTeamAsNotReceivingMoneyNextRound(int team);
+
+ // Check various conditions to end the map.
+ bool CheckGameOver();
+ bool CheckMaxRounds();
+ bool CheckWinLimit();
+ bool CheckFragLimit();
+
+ void CheckLevelInitialized();
+ void CheckRestartRound();
+
+
+ // Checks if it still needs players to start a round, or if it has enough players to start rounds.
+ // Starts a round and returns true if there are enough players.
+ bool NeededPlayersCheck( bool &bNeededPlayers );
+
+ // Setup counts for m_iNumTerrorist, m_iNumCT, m_iNumSpawnableTerrorist, m_iNumSpawnableCT, etc.
+ void InitializePlayerCounts(
+ int &NumAliveTerrorist,
+ int &NumAliveCT,
+ int &NumDeadTerrorist,
+ int &NumDeadCT
+ );
+
+ // Check to see if the round is over for the various game types. Terminates the round
+ // and returns true if the round should end.
+ bool PrisonRoundEndCheck();
+ bool BombRoundEndCheck( bool bNeededPlayers );
+ bool HostageRescueRoundEndCheck( bool bNeededPlayers );
+
+ // Check to see if the teams exterminated each other. Ends the round and returns true if so.
+ bool TeamExterminationCheck(
+ int NumAliveTerrorist,
+ int NumAliveCT,
+ int NumDeadTerrorist,
+ int NumDeadCT,
+ bool bNeededPlayers
+ );
+
+ void ReadMultiplayCvars();
+ void SwapAllPlayers();
+
+ void BroadcastSound( const char *sound, int team = -1 );
+
+
+ // VIP FUNCTIONS
+ bool VIPRoundEndCheck( bool bNeededPlayers );
+ void PickNextVIP();
+
+
+ // BOMB MAP FUNCTIONS
+ void GiveC4();
+ bool IsThereABomber();
+ bool IsThereABomb();
+
+ // HOSTAGE MAP FUNCTIONS
+ void HostageTouched();
+
+
+ // Sets up g_pPlayerResource.
+ virtual void CreateStandardEntities();
+ virtual const char *GetChatPrefix( bool bTeamOnly, CBasePlayer *pPlayer );
+ virtual const char *GetChatLocation( bool bTeamOnly, CBasePlayer *pPlayer );
+ virtual const char *GetChatFormat( bool bTeamOnly, CBasePlayer *pPlayer );
+ void ClientSettingsChanged( CBasePlayer *pPlayer );
+
+ bool IsCareer( void ) const { return false; } // returns true if this is a CZ "career" game
+
+ virtual bool FAllowNPCs( void );
+
+protected:
+ virtual void GoToIntermission( void );
+
+public:
+
+ bool IsFriendlyFireOn();
+
+ virtual void SetAllowWeaponSwitch( bool allow );
+ virtual bool GetAllowWeaponSwitch( void );
+
+ // VARIABLES FOR ALL TYPES OF MAPS
+ bool m_bLevelInitialized;
+ int m_iRoundWinStatus; // 1 == CT's won last round, 2 == Terrorists did, 3 == Draw, no winner
+ int m_iTotalRoundsPlayed;
+ int m_iUnBalancedRounds; // keeps track of the # of consecutive rounds that have gone by where one team outnumbers the other team by more than 2
+
+ // GAME TIMES
+ int m_iFreezeTime; // (From mp_freezetime) - How many seconds long the intro round (when players are frozen) is.
+ float m_flRestartRoundTime; // the global time when the round is supposed to end, if this is not 0
+
+ int m_iNumTerrorist; // The number of terrorists on the team (this is generated at the end of a round)
+ int m_iNumCT; // The number of CTs on the team (this is generated at the end of a round)
+ int m_iNumSpawnableTerrorist;
+ int m_iNumSpawnableCT;
+
+ bool m_bFirstConnected;
+ bool m_bCompleteReset; // Set to TRUE to have the scores reset next time round restarts
+
+ int m_iAccountTerrorist;
+ int m_iAccountCT;
+
+ short m_iNumCTWins;
+ short m_iNumTerroristWins;
+
+ int m_iNumConsecutiveCTLoses; //SupraFiend: the number of rounds the CTs have lost in a row.
+ int m_iNumConsecutiveTerroristLoses;//SupraFiend: the number of rounds the Terrorists have lost in a row.
+
+ int m_iSpawnPointCount_Terrorist; // Number of Terrorist spawn points
+ int m_iSpawnPointCount_CT; // Number of CT spawn points
+
+ bool m_bTCantBuy; // Who can and can't buy.
+ bool m_bCTCantBuy;
+ bool m_bMapHasBuyZone;
+
+ int m_iLoserBonus; // SupraFiend: the amount of money the losing team gets. This scales up as they lose more rounds in a row
+ float m_tmNextPeriodicThink;
+
+
+ // HOSTAGE RESCUE VARIABLES
+ int m_iHostagesRescued;
+ int m_iHostagesTouched;
+ float m_flNextHostageAnnouncement;
+
+ //=============================================================================
+ // HPE_BEGIN
+ //=============================================================================
+
+ // [tj] Accessor for weapons donation ability
+ bool GetCanDonateWeapon() { return m_bCanDonateWeapons; }
+
+ // [tj] flawless and lossless round related flags
+ bool m_bNoTerroristsKilled;
+ bool m_bNoCTsKilled;
+ bool m_bNoTerroristsDamaged;
+ bool m_bNoCTsDamaged;
+
+ // [tj] Find out if dropped weapons count as donations
+ bool m_bCanDonateWeapons;
+
+ // [tj] Keep track of first kill
+ CHandle<CCSPlayer> m_pFirstKill;
+ float m_firstKillTime;
+
+ // [menglish] Keep track of first blood
+ CHandle<CCSPlayer> m_pFirstBlood;
+ float m_firstBloodTime;
+
+
+ // [dwenger] Rescue-related achievement values
+ CHandle<CCSPlayer> m_pLastRescuer;
+ int m_iNumRescuers;
+
+ bool m_hostageWasInjured;
+ bool m_hostageWasKilled;
+
+ // [menglish] Fun Fact Manager
+ CCSFunFactMgr *m_pFunFactManager;
+
+ // [tj] To avoid rewriting the same piece of code, we can get all the information
+ // we want from one call that fills in an array of structures.
+ struct TeamPlayerCounts
+ {
+ int totalPlayers;
+ int totalAlivePlayers;
+ int totalDeadPlayers; //sum of killedPlayers + suicidedPlayers + unenteredPlayers
+ int killedPlayers;
+ int suicidedPlayers;
+ int unenteredPlayers;
+ };
+
+ void GetPlayerCounts(TeamPlayerCounts teamCounts[TEAM_MAXCOUNT]);
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+
+ // PRISON ESCAPE VARIABLES
+ int m_iHaveEscaped;
+ bool m_bMapHasEscapeZone;
+ int m_iNumEscapers;
+ int m_iNumEscapeRounds; // keeps track of the # of consecutive rounds of escape played.. Teams will be swapped after 8 rounds
+
+
+ // VIP VARIABLES
+ int m_iMapHasVIPSafetyZone; // 0 = uninitialized; 1 = has VIP safety zone; 2 = DOES not have VIP safetyzone
+ CHandle<CCSPlayer> m_pVIP;
+ int m_iConsecutiveVIP;
+
+
+ // BOMB MAP VARIABLES
+ bool m_bTargetBombed; // whether or not the bomb has been bombed
+ bool m_bBombDefused; // whether or not the bomb has been defused
+ bool m_bMapHasBombZone;
+ bool m_bBombDropped;
+ bool m_bBombPlanted;
+ EHANDLE m_pLastBombGuy;
+
+private:
+
+ // Don't allow switching weapons while gaining new technologies
+ bool m_bAllowWeaponSwitch;
+
+public:
+
+
+
+ void AddPricesToTable( weeklyprice_t prices );
+ virtual void CreateCustomNetworkStringTables( void );
+
+#endif
+
+
+#ifdef GAME_DLL
+public:
+ virtual void GetTaggedConVarList( KeyValues *pCvarTagList );
+#endif
+
+public:
+ const weeklyprice_t *GetBlackMarketPriceList( void );
+
+ int GetBlackMarketPriceForWeapon( int iWeaponID );
+ int GetBlackMarketPreviousPriceForWeapon( int iWeaponID );
+
+ void SetBlackMarketPrices( bool bSetDefaults );
+
+ // Black market
+ INetworkStringTable *m_StringTableBlackMarket;
+ const weeklyprice_t *m_pPrices;
+};
+
+//-----------------------------------------------------------------------------
+// Gets us at the team fortress game rules
+//-----------------------------------------------------------------------------
+
+inline CCSGameRules* CSGameRules()
+{
+ return static_cast<CCSGameRules*>(g_pGameRules);
+}
+
+#define IGNORE_SPECTATORS true
+int UTIL_HumansInGame( bool ignoreSpectators = false );
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Useful utility functions
+//-----------------------------------------------------------------------------
+#ifdef CLIENT_DLL
+
+#else
+
+ class CTFTeam;
+ CTFTeam *GetOpposingTeam( CTeam *pTeam );
+ bool EntityPlacementTest( CBaseEntity *pMainEnt, const Vector &vOrigin, Vector &outPos, bool bDropToGround );
+
+#endif
+
+#endif // TF_GAMERULES_H
diff --git a/game/shared/cstrike/cs_gamestats_shared.cpp b/game/shared/cstrike/cs_gamestats_shared.cpp
new file mode 100644
index 0000000..cd250c8
--- /dev/null
+++ b/game/shared/cstrike/cs_gamestats_shared.cpp
@@ -0,0 +1,410 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "fmtstr.h"
+#ifdef GAME_DLL
+#include "gamestats.h"
+#endif
+#include "cs_gamestats_shared.h"
+
+ConVar sv_noroundstats( "sv_noroundstats", "1", FCVAR_REPLICATED, "A temporary variable that can be used for disabling upload of round stats." );
+
+const MapName_MapStatId MapName_StatId_Table[] =
+{
+ {"cs_assault", CSSTAT_MAP_WINS_CS_ASSAULT, CSSTAT_MAP_ROUNDS_CS_ASSAULT },
+ {"cs_compound", CSSTAT_MAP_WINS_CS_COMPOUND, CSSTAT_MAP_ROUNDS_CS_COMPOUND },
+ {"cs_havana", CSSTAT_MAP_WINS_CS_HAVANA, CSSTAT_MAP_ROUNDS_CS_HAVANA },
+ {"cs_italy", CSSTAT_MAP_WINS_CS_ITALY, CSSTAT_MAP_ROUNDS_CS_ITALY },
+ {"cs_militia", CSSTAT_MAP_WINS_CS_MILITIA, CSSTAT_MAP_ROUNDS_CS_MILITIA },
+ {"cs_office", CSSTAT_MAP_WINS_CS_OFFICE, CSSTAT_MAP_ROUNDS_CS_OFFICE },
+ {"de_aztec", CSSTAT_MAP_WINS_DE_AZTEC, CSSTAT_MAP_ROUNDS_DE_AZTEC },
+ {"de_cbble", CSSTAT_MAP_WINS_DE_CBBLE, CSSTAT_MAP_ROUNDS_DE_CBBLE },
+ {"de_chateau", CSSTAT_MAP_WINS_DE_CHATEAU, CSSTAT_MAP_ROUNDS_DE_CHATEAU },
+ {"de_dust2", CSSTAT_MAP_WINS_DE_DUST2, CSSTAT_MAP_ROUNDS_DE_DUST2 },
+ {"de_dust", CSSTAT_MAP_WINS_DE_DUST, CSSTAT_MAP_ROUNDS_DE_DUST },
+ {"de_inferno", CSSTAT_MAP_WINS_DE_INFERNO, CSSTAT_MAP_ROUNDS_DE_INFERNO },
+ {"de_nuke", CSSTAT_MAP_WINS_DE_NUKE, CSSTAT_MAP_ROUNDS_DE_NUKE },
+ {"de_piranesi", CSSTAT_MAP_WINS_DE_PIRANESI, CSSTAT_MAP_ROUNDS_DE_PIRANESI },
+ {"de_port", CSSTAT_MAP_WINS_DE_PORT, CSSTAT_MAP_ROUNDS_DE_PORT },
+ {"de_prodigy", CSSTAT_MAP_WINS_DE_PRODIGY, CSSTAT_MAP_ROUNDS_DE_PRODIGY },
+ {"de_tides", CSSTAT_MAP_WINS_DE_TIDES, CSSTAT_MAP_ROUNDS_DE_TIDES },
+ {"de_train", CSSTAT_MAP_WINS_DE_TRAIN, CSSTAT_MAP_ROUNDS_DE_TRAIN },
+ {"", CSSTAT_UNDEFINED, CSSTAT_UNDEFINED },
+};
+
+const WeaponName_StatId WeaponName_StatId_Table[] =
+{
+ { WEAPON_DEAGLE, CSSTAT_KILLS_DEAGLE, CSSTAT_SHOTS_DEAGLE, CSSTAT_HITS_DEAGLE, CSSTAT_DAMAGE_DEAGLE },
+ { WEAPON_USP, CSSTAT_KILLS_USP, CSSTAT_SHOTS_USP, CSSTAT_HITS_USP, CSSTAT_DAMAGE_USP },
+ { WEAPON_GLOCK, CSSTAT_KILLS_GLOCK, CSSTAT_SHOTS_GLOCK, CSSTAT_HITS_GLOCK, CSSTAT_DAMAGE_GLOCK },
+ { WEAPON_P228, CSSTAT_KILLS_P228, CSSTAT_SHOTS_P228, CSSTAT_HITS_P228, CSSTAT_DAMAGE_P228 },
+ { WEAPON_ELITE, CSSTAT_KILLS_ELITE, CSSTAT_SHOTS_ELITE, CSSTAT_HITS_ELITE, CSSTAT_DAMAGE_ELITE },
+ { WEAPON_FIVESEVEN, CSSTAT_KILLS_FIVESEVEN, CSSTAT_SHOTS_FIVESEVEN, CSSTAT_HITS_FIVESEVEN, CSSTAT_DAMAGE_FIVESEVEN },
+ { WEAPON_AWP, CSSTAT_KILLS_AWP, CSSTAT_SHOTS_AWP, CSSTAT_HITS_AWP, CSSTAT_DAMAGE_AWP },
+ { WEAPON_AK47, CSSTAT_KILLS_AK47, CSSTAT_SHOTS_AK47, CSSTAT_HITS_AK47, CSSTAT_DAMAGE_AK47 },
+ { WEAPON_M4A1, CSSTAT_KILLS_M4A1, CSSTAT_SHOTS_M4A1, CSSTAT_HITS_M4A1, CSSTAT_DAMAGE_M4A1 },
+ { WEAPON_AUG, CSSTAT_KILLS_AUG, CSSTAT_SHOTS_AUG, CSSTAT_HITS_AUG, CSSTAT_DAMAGE_AUG },
+ { WEAPON_SG552, CSSTAT_KILLS_SG552, CSSTAT_SHOTS_SG552, CSSTAT_HITS_SG552, CSSTAT_DAMAGE_SG552 },
+ { WEAPON_SG550, CSSTAT_KILLS_SG550, CSSTAT_SHOTS_SG550, CSSTAT_HITS_SG550, CSSTAT_DAMAGE_SG550 },
+ { WEAPON_GALIL, CSSTAT_KILLS_GALIL, CSSTAT_SHOTS_GALIL, CSSTAT_HITS_GALIL, CSSTAT_DAMAGE_GALIL },
+ { WEAPON_FAMAS, CSSTAT_KILLS_FAMAS, CSSTAT_SHOTS_FAMAS, CSSTAT_HITS_FAMAS, CSSTAT_DAMAGE_FAMAS },
+ { WEAPON_SCOUT, CSSTAT_KILLS_SCOUT, CSSTAT_SHOTS_SCOUT, CSSTAT_HITS_SCOUT, CSSTAT_DAMAGE_SCOUT },
+ { WEAPON_G3SG1, CSSTAT_KILLS_G3SG1, CSSTAT_SHOTS_G3SG1, CSSTAT_HITS_G3SG1, CSSTAT_DAMAGE_G3SG1 },
+ { WEAPON_P90, CSSTAT_KILLS_P90, CSSTAT_SHOTS_P90, CSSTAT_HITS_P90, CSSTAT_DAMAGE_P90 },
+ { WEAPON_MP5NAVY, CSSTAT_KILLS_MP5NAVY, CSSTAT_SHOTS_MP5NAVY, CSSTAT_HITS_MP5NAVY, CSSTAT_DAMAGE_MP5NAVY },
+ { WEAPON_TMP, CSSTAT_KILLS_TMP, CSSTAT_SHOTS_TMP, CSSTAT_HITS_TMP, CSSTAT_DAMAGE_TMP },
+ { WEAPON_MAC10, CSSTAT_KILLS_MAC10, CSSTAT_SHOTS_MAC10, CSSTAT_HITS_MAC10, CSSTAT_DAMAGE_MAC10 },
+ { WEAPON_UMP45, CSSTAT_KILLS_UMP45, CSSTAT_SHOTS_UMP45, CSSTAT_HITS_UMP45, CSSTAT_DAMAGE_UMP45 },
+ { WEAPON_M3, CSSTAT_KILLS_M3, CSSTAT_SHOTS_M3, CSSTAT_HITS_M3, CSSTAT_DAMAGE_M3 },
+ { WEAPON_XM1014, CSSTAT_KILLS_XM1014, CSSTAT_SHOTS_XM1014, CSSTAT_HITS_XM1014, CSSTAT_DAMAGE_XM1014 },
+ { WEAPON_M249, CSSTAT_KILLS_M249, CSSTAT_SHOTS_M249, CSSTAT_HITS_M249, CSSTAT_DAMAGE_M249 },
+ { WEAPON_KNIFE, CSSTAT_KILLS_KNIFE, CSSTAT_SHOTS_KNIFE, CSSTAT_HITS_KNIFE, CSSTAT_DAMAGE_KNIFE },
+ { WEAPON_HEGRENADE, CSSTAT_KILLS_HEGRENADE, CSSTAT_SHOTS_HEGRENADE, CSSTAT_HITS_HEGRENADE, CSSTAT_DAMAGE_HEGRENADE },
+
+ { WEAPON_NONE, CSSTAT_UNDEFINED, CSSTAT_UNDEFINED, CSSTAT_UNDEFINED, CSSTAT_UNDEFINED }, // This is a sentinel value so we can loop through all the stats
+};
+
+CSStatProperty CSStatProperty_Table[] =
+{
+// StatId Steam Name Localization Token Client Update Priority
+ { CSSTAT_SHOTS_HIT, "i_NumShotsHit", "#GAMEUI_Stat_NumHits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_FIRED, "i_NumShotsFired", "#GAMEUI_Stat_NumShots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_KILLS, "i_Number_Of_Kills", "#GAMEUI_Stat_NumKills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_DEATHS, "i_Number_Of_Deaths", "#GAMEUI_Stat_NumDeaths", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_DAMAGE, "i_Damage_Done", "#GAMEUI_Stat_DamageDone", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_NUM_BOMBS_PLANTED, "i_Number_Of_PlantedBombs", "#GAMEUI_Stat_NumPlantedBombs", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_NUM_BOMBS_DEFUSED, "i_Number_Of_DefusedBombs", "#GAMEUI_Stat_NumDefusedBombs", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_PLAYTIME, "i_Time_Played", "#GAMEUI_Stat_TimePlayed", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_ROUNDS_WON, "total_wins", "#GAMEUI_Stat_TotalWins", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_T_ROUNDS_WON, NULL, NULL, CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_CT_ROUNDS_WON, NULL, NULL, CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_ROUNDS_PLAYED, "i_Total_Rounds", "#GAMEUI_Stat_TotalRounds", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_PISTOLROUNDS_WON, "total_wins_pistolround", "#GAMEUI_Stat_PistolRoundWins", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MONEY_EARNED, "i_Money_Earned", "#GAMEUI_Stat_MoneyEarned", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_OBJECTIVES_COMPLETED, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_BOMBS_DEFUSED_WITHKIT, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+
+ { CSSTAT_KILLS_DEAGLE, "total_kills_deagle", "#GAMEUI_Stat_DeagleKills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_USP, "total_kills_usp", "#GAMEUI_Stat_USPKills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_GLOCK, "total_kills_glock", "#GAMEUI_Stat_GlockKills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_P228, "total_kills_p228", "#GAMEUI_Stat_P228Kills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_ELITE, "total_kills_elite", "#GAMEUI_Stat_EliteKills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_FIVESEVEN, "total_kills_fiveseven", "#GAMEUI_Stat_FiveSevenKills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_AWP, "total_kills_awp", "#GAMEUI_Stat_AWPKills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_AK47, "total_kills_ak47", "#GAMEUI_Stat_AK47Kills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_M4A1, "total_kills_m4a1", "#GAMEUI_Stat_M4A1Kills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_AUG, "total_kills_aug", "#GAMEUI_Stat_AUGKills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_SG552, "total_kills_sg552", "#GAMEUI_Stat_SG552Kills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_SG550, "total_kills_sg550", "#GAMEUI_Stat_SG550Kills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_GALIL, "total_kills_galil", "#GAMEUI_Stat_GALILKills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_FAMAS, "total_kills_famas", "#GAMEUI_Stat_FAMASKills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_SCOUT, "total_kills_scout", "#GAMEUI_Stat_ScoutKills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_G3SG1, "total_kills_g3sg1", "#GAMEUI_Stat_G3SG1Kills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_P90, "total_kills_p90", "#GAMEUI_Stat_P90Kills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_MP5NAVY, "total_kills_mp5navy", "#GAMEUI_Stat_MP5NavyKills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_TMP, "total_kills_tmp", "#GAMEUI_Stat_TMPKills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_MAC10, "total_kills_mac10", "#GAMEUI_Stat_MAC10Kills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_UMP45, "total_kills_ump45", "#GAMEUI_Stat_UMP45Kills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_M3, "total_kills_m3", "#GAMEUI_Stat_M3Kills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_XM1014, "total_kills_xm1014", "#GAMEUI_Stat_XM1014Kills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_M249, "total_kills_m249", "#GAMEUI_Stat_M249Kills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_KNIFE, "total_kills_knife", "#GAMEUI_Stat_KnifeKills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_HEGRENADE, "total_kills_hegrenade", "#GAMEUI_Stat_HEGrenadeKills", CSSTAT_PRIORITY_HIGH, },
+
+ { CSSTAT_SHOTS_DEAGLE, "total_shots_deagle", "#GAMEUI_Stat_DeagleShots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_USP, "total_shots_usp", "#GAMEUI_Stat_USPShots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_GLOCK, "total_shots_glock", "#GAMEUI_Stat_GlockShots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_P228, "total_shots_p228", "#GAMEUI_Stat_P228Shots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_ELITE, "total_shots_elite", "#GAMEUI_Stat_EliteShots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_FIVESEVEN, "total_shots_fiveseven", "#GAMEUI_Stat_FiveSevenShots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_AWP, "total_shots_awp", "#GAMEUI_Stat_AWPShots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_AK47, "total_shots_ak47", "#GAMEUI_Stat_AK47Shots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_M4A1, "total_shots_m4a1", "#GAMEUI_Stat_M4A1Shots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_AUG, "total_shots_aug", "#GAMEUI_Stat_AUGShots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_SG552, "total_shots_sg552", "#GAMEUI_Stat_SG552Shots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_SG550, "total_shots_sg550", "#GAMEUI_Stat_SG550Shots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_GALIL, "total_shots_galil", "#GAMEUI_Stat_GALILShots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_FAMAS, "total_shots_famas", "#GAMEUI_Stat_FAMASShots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_SCOUT, "total_shots_scout", "#GAMEUI_Stat_ScoutShots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_G3SG1, "total_shots_g3sg1", "#GAMEUI_Stat_G3SG1Shots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_P90, "total_shots_p90", "#GAMEUI_Stat_P90Shots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_MP5NAVY, "total_shots_mp5navy", "#GAMEUI_Stat_MP5NavyShots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_TMP, "total_shots_tmp", "#GAMEUI_Stat_TMPShots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_MAC10, "total_shots_mac10", "#GAMEUI_Stat_MAC10Shots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_UMP45, "total_shots_ump45", "#GAMEUI_Stat_UMP45Shots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_M3, "total_shots_m3", "#GAMEUI_Stat_M3Shots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_XM1014, "total_shots_xm1014", "#GAMEUI_Stat_XM1014Shots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_M249, "total_shots_m249", "#GAMEUI_Stat_M249Shots", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_SHOTS_KNIFE, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_SHOTS_HEGRENADE, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+
+ { CSSTAT_HITS_DEAGLE, "total_hits_deagle", "#GAMEUI_Stat_DeagleHits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_USP, "total_hits_usp", "#GAMEUI_Stat_USPHits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_GLOCK, "total_hits_glock", "#GAMEUI_Stat_GlockHits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_P228, "total_hits_p228", "#GAMEUI_Stat_P228Hits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_ELITE, "total_hits_elite", "#GAMEUI_Stat_EliteHits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_FIVESEVEN, "total_hits_fiveseven", "#GAMEUI_Stat_FiveSevenHits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_AWP, "total_hits_awp", "#GAMEUI_Stat_AWPHits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_AK47, "total_hits_ak47", "#GAMEUI_Stat_AK47Hits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_M4A1, "total_hits_m4a1", "#GAMEUI_Stat_M4A1Hits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_AUG, "total_hits_aug", "#GAMEUI_Stat_AUGHits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_SG552, "total_hits_sg552", "#GAMEUI_Stat_SG552Hits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_SG550, "total_hits_sg550", "#GAMEUI_Stat_SG550Hits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_GALIL, "total_hits_galil", "#GAMEUI_Stat_GALILHits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_FAMAS, "total_hits_famas", "#GAMEUI_Stat_FAMASHits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_SCOUT, "total_hits_scout", "#GAMEUI_Stat_ScoutHits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_G3SG1, "total_hits_g3sg1", "#GAMEUI_Stat_G3SG1Hits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_P90, "total_hits_p90", "#GAMEUI_Stat_P90Hits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_MP5NAVY, "total_hits_mp5navy", "#GAMEUI_Stat_MP5NavyHits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_TMP, "total_hits_tmp", "#GAMEUI_Stat_TMPHits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_MAC10, "total_hits_mac10", "#GAMEUI_Stat_MAC10Hits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_UMP45, "total_hits_ump45", "#GAMEUI_Stat_UMP45Hits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_M3, "total_hits_m3", "#GAMEUI_Stat_M3Hits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_XM1014, "total_hits_xm1014", "#GAMEUI_Stat_XM1014Hits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_M249, "total_hits_m249", "#GAMEUI_Stat_M249Hits", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_HITS_KNIFE, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_HITS_HEGRENADE, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+
+ { CSSTAT_DAMAGE_DEAGLE, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_USP, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_GLOCK, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_P228, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_ELITE, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_FIVESEVEN, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_AWP, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_AK47, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_M4A1, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_AUG, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_SG552, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_SG550, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_GALIL, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_FAMAS, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_SCOUT, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_G3SG1, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_P90, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_MP5NAVY, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_TMP, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_MAC10, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_UMP45, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_M3, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_XM1014, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_M249, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_KNIFE, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_DAMAGE_HEGRENADE, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+
+ { CSSTAT_KILLS_HEADSHOT, "total_kills_headshot", "#GAMEUI_Stat_HeadshotKills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_ENEMY_BLINDED, "total_kills_enemy_blinded", "#GAMEUI_Stat_BlindedEnemyKills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_WHILE_BLINDED, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_KILLS_WITH_LAST_ROUND, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_KILLS_ENEMY_WEAPON, "total_kills_enemy_weapon", "#GAMEUI_Stat_EnemyWeaponKills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_KNIFE_FIGHT, "total_kills_knife_fight", "#GAMEUI_Stat_KnifeFightKills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_KILLS_WHILE_DEFENDING_BOMB, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+
+ { CSSTAT_DECAL_SPRAYS, "total_decal_sprays", "#GAMEUI_Stat_DecalSprays", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_TOTAL_JUMPS, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_NIGHTVISION_DAMAGE, "total_nightvision_damage", "#GAMEUI_Stat_NightvisionDamage", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_KILLS_WHILE_LAST_PLAYER_ALIVE, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_KILLS_ENEMY_WOUNDED, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_FALL_DAMAGE, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+
+ { CSSTAT_NUM_HOSTAGES_RESCUED, "i_Number_Of_RescuedHostages", "#GAMEUI_Stat_NumRescuedHostages", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_NUM_BROKEN_WINDOWS, "i_Number_Of_BrokenWindows", "#GAMEUI_Stat_NumBrokenWindows", CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_PROPSBROKEN_ALL, NULL, NULL, CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_PROPSBROKEN_MELON, NULL, NULL, CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_PROPSBROKEN_OFFICEELECTRONICS, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_PROPSBROKEN_OFFICERADIO, NULL, NULL, CSSTAT_PRIORITY_LOW, },
+ { CSSTAT_PROPSBROKEN_OFFICEJUNK, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_PROPSBROKEN_ITALY_MELON, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+
+ { CSSTAT_KILLS_AGAINST_ZOOMED_SNIPER, "total_kills_against_zoomed_sniper","#GAMEUI_Stat_ZoomedSniperKills", CSSTAT_PRIORITY_HIGH, },
+
+ { CSSTAT_WEAPONS_DONATED, "total_weapons_donated", "#GAMEUI_Stat_WeaponsDonated", CSSTAT_PRIORITY_HIGH, },
+
+ { CSSTAT_ITEMS_PURCHASED, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_MONEY_SPENT, NULL, NULL, CSSTAT_PRIORITY_LOW, },
+
+ { CSSTAT_DOMINATIONS, "total_dominations", "#GAMEUI_Stat_Dominations", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_DOMINATION_OVERKILLS, "total_domination_overkills", "#GAMEUI_Stat_DominationOverkills", CSSTAT_PRIORITY_HIGH, },
+ { CSSTAT_REVENGES, "total_revenges", "#GAMEUI_Stat_Revenges", CSSTAT_PRIORITY_HIGH, },
+
+ { CSSTAT_MVPS, "total_mvps", "#GAMEUI_Stat_MVPs", CSSTAT_PRIORITY_HIGH, },
+
+ { CSSTAT_GRENADE_DAMAGE, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_GRENADE_POSTHUMOUSKILLS, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSSTAT_GRENADES_THROWN, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+ { CSTAT_ITEMS_DROPPED_VALUE, NULL, NULL, CSSTAT_PRIORITY_NEVER, },
+
+ { CSSTAT_MAP_WINS_CS_ASSAULT, "total_wins_map_cs_assault", "#GAMEUI_Stat_WinsMapCSAssault", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_WINS_CS_COMPOUND, "total_wins_map_cs_compound", "#GAMEUI_Stat_WinsMapCSCompound", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_WINS_CS_HAVANA, "total_wins_map_cs_havana", "#GAMEUI_Stat_WinsMapCSHavana", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_WINS_CS_ITALY, "total_wins_map_cs_italy", "#GAMEUI_Stat_WinsMapCSItaly", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_WINS_CS_MILITIA, "total_wins_map_cs_militia", "#GAMEUI_Stat_WinsMapCSMilitia", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_WINS_CS_OFFICE, "total_wins_map_cs_office", "#GAMEUI_Stat_WinsMapCSOffice", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_WINS_DE_AZTEC, "total_wins_map_de_aztec", "#GAMEUI_Stat_WinsMapDEAztec", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_WINS_DE_CBBLE, "total_wins_map_de_cbble", "#GAMEUI_Stat_WinsMapDECbble", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_WINS_DE_CHATEAU, "total_wins_map_de_chateau", "#GAMEUI_Stat_WinsMapDEChateau", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_WINS_DE_DUST2, "total_wins_map_de_dust2", "#GAMEUI_Stat_WinsMapDEDust2", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_WINS_DE_DUST, "total_wins_map_de_dust", "#GAMEUI_Stat_WinsMapDEDust", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_WINS_DE_INFERNO, "total_wins_map_de_inferno", "#GAMEUI_Stat_WinsMapDEInferno", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_WINS_DE_NUKE, "total_wins_map_de_nuke", "#GAMEUI_Stat_WinsMapDENuke", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_WINS_DE_PIRANESI, "total_wins_map_de_piranesi", "#GAMEUI_Stat_WinsMapDEPiranesi", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_WINS_DE_PORT, "total_wins_map_de_port", "#GAMEUI_Stat_WinsMapDEPort", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_WINS_DE_PRODIGY, "total_wins_map_de_prodigy", "#GAMEUI_Stat_WinsMapDEProdigy", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_WINS_DE_TIDES, "total_wins_map_de_tides", "#GAMEUI_Stat_WinsMapDETides", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_WINS_DE_TRAIN, "total_wins_map_de_train", "#GAMEUI_Stat_WinsMapDETrain", CSSTAT_PRIORITY_ENDROUND, },
+
+ { CSSTAT_MAP_ROUNDS_CS_ASSAULT, "total_rounds_map_cs_assault", "#GAMEUI_Stat_RoundsMapCSAssault", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_ROUNDS_CS_COMPOUND, "total_rounds_map_cs_compound", "#GAMEUI_Stat_RoundsMapCSCompound", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_ROUNDS_CS_HAVANA, "total_rounds_map_cs_havana", "#GAMEUI_Stat_RoundsMapCSHavana", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_ROUNDS_CS_ITALY, "total_rounds_map_cs_italy", "#GAMEUI_Stat_RoundsMapCSItaly", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_ROUNDS_CS_MILITIA, "total_rounds_map_cs_militia", "#GAMEUI_Stat_RoundsMapCSMilitia", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_ROUNDS_CS_OFFICE, "total_rounds_map_cs_office", "#GAMEUI_Stat_RoundsMapCSOffice", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_ROUNDS_DE_AZTEC, "total_rounds_map_de_aztec", "#GAMEUI_Stat_RoundsMapDEAztec", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_ROUNDS_DE_CBBLE, "total_rounds_map_de_cbble", "#GAMEUI_Stat_RoundsMapDECbble", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_ROUNDS_DE_CHATEAU, "total_rounds_map_de_chateau", "#GAMEUI_Stat_RoundsMapDEChateau", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_ROUNDS_DE_DUST2, "total_rounds_map_de_dust2", "#GAMEUI_Stat_RoundsMapDEDust2", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_ROUNDS_DE_DUST, "total_rounds_map_de_dust", "#GAMEUI_Stat_RoundsMapDEDust", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_ROUNDS_DE_INFERNO, "total_rounds_map_de_inferno", "#GAMEUI_Stat_RoundsMapDEInferno", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_ROUNDS_DE_NUKE, "total_rounds_map_de_nuke", "#GAMEUI_Stat_RoundsMapDENuke", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_ROUNDS_DE_PIRANESI, "total_rounds_map_de_piranesi", "#GAMEUI_Stat_RoundsMapDEPiranesi", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_ROUNDS_DE_PORT, "total_rounds_map_de_port", "#GAMEUI_Stat_RoundsMapDEPort", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_ROUNDS_DE_PRODIGY, "total_rounds_map_de_prodigy", "#GAMEUI_Stat_RoundsMapDEProdigy", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_ROUNDS_DE_TIDES, "total_rounds_map_de_tides", "#GAMEUI_Stat_RoundsMapDETides", CSSTAT_PRIORITY_ENDROUND, },
+ { CSSTAT_MAP_ROUNDS_DE_TRAIN, "total_rounds_map_de_train", "#GAMEUI_Stat_RoundsMapDETrain", CSSTAT_PRIORITY_ENDROUND, },
+
+ // only client tracks these
+ { CSSTAT_LASTMATCH_T_ROUNDS_WON, "last_match_t_wins", "#GameUI_Stat_LastMatch_TWins", CSSTAT_PRIORITY_NEVER },
+ { CSSTAT_LASTMATCH_CT_ROUNDS_WON, "last_match_ct_wins", "#GameUI_Stat_LastMatch_CTWins", CSSTAT_PRIORITY_NEVER },
+ { CSSTAT_LASTMATCH_ROUNDS_WON, "last_match_wins", "#GameUI_Stat_LastMatch_RoundsWon", CSSTAT_PRIORITY_NEVER },
+ { CSSTAT_LASTMATCH_KILLS, "last_match_kills", "#GameUI_Stat_LastMatch_Kills", CSSTAT_PRIORITY_NEVER },
+ { CSSTAT_LASTMATCH_DEATHS, "last_match_deaths", "#GameUI_Stat_LastMatch_Deaths", CSSTAT_PRIORITY_NEVER },
+ { CSSTAT_LASTMATCH_MVPS, "last_match_mvps", "#GameUI_Stat_LastMatch_MVPS", CSSTAT_PRIORITY_NEVER },
+ { CSSTAT_LASTMATCH_DAMAGE, "last_match_damage", "#GameUI_Stat_LastMatch_Damage", CSSTAT_PRIORITY_NEVER },
+ { CSSTAT_LASTMATCH_MONEYSPENT, "last_match_money_spent", "#GameUI_Stat_LastMatch_MoneySpent", CSSTAT_PRIORITY_NEVER },
+ { CSSTAT_LASTMATCH_DOMINATIONS, "last_match_dominations", "#GameUI_Stat_LastMatch_Dominations", CSSTAT_PRIORITY_NEVER },
+ { CSSTAT_LASTMATCH_REVENGES, "last_match_revenges", "#GameUI_Stat_LastMatch_Revenges", CSSTAT_PRIORITY_NEVER },
+ { CSSTAT_LASTMATCH_MAX_PLAYERS, "last_match_max_players", "#GameUI_Stat_LastMatch_MaxPlayers", CSSTAT_PRIORITY_NEVER },
+ { CSSTAT_LASTMATCH_FAVWEAPON_ID, "last_match_favweapon_id", "#GameUI_Stat_LastMatch_FavWeapon", CSSTAT_PRIORITY_NEVER },
+ { CSSTAT_LASTMATCH_FAVWEAPON_SHOTS, "last_match_favweapon_shots", "#GameUI_Stat_LastMatch_FavWeaponShots",CSSTAT_PRIORITY_NEVER },
+ { CSSTAT_LASTMATCH_FAVWEAPON_HITS, "last_match_favweapon_hits", "#GameUI_Stat_LastMatch_FavWeaponHits", CSSTAT_PRIORITY_NEVER },
+ { CSSTAT_LASTMATCH_FAVWEAPON_KILLS, "last_match_favweapon_kills", "#GameUI_Stat_LastMatch_FavWeaponKills",CSSTAT_PRIORITY_NEVER },
+
+ { CSSTAT_UNDEFINED }, // sentinel
+};
+
+const WeaponName_StatId& GetWeaponTableEntryFromWeaponId( CSWeaponID id )
+{
+ int i;
+
+ //yes this for loop has no statement block. All we are doing is incrementing i to the appropriate point.
+ for (i = 0 ; WeaponName_StatId_Table[i].weaponId != WEAPON_NONE ; ++i)
+ {
+ if (WeaponName_StatId_Table[i].weaponId == id )
+ {
+ break;
+ }
+ }
+ return WeaponName_StatId_Table[i];
+}
+
+void StatsCollection_t::Aggregate( const StatsCollection_t& other )
+{
+ for ( int i = 0; i < CSSTAT_MAX; ++i )
+ {
+ m_iValue[i] += other[i];
+ }
+}
+
+//=============================================================================
+//
+// Helper functions for creating key values
+//
+void AddDataToKV( KeyValues* pKV, const char* name, int data )
+{
+ pKV->SetInt( name, data );
+}
+void AddDataToKV( KeyValues* pKV, const char* name, uint64 data )
+{
+ pKV->SetUint64( name, data );
+}
+void AddDataToKV( KeyValues* pKV, const char* name, float data )
+{
+ pKV->SetFloat( name, data );
+}
+void AddDataToKV( KeyValues* pKV, const char* name, bool data )
+{
+ pKV->SetInt( name, data ? true : false );
+}
+void AddDataToKV( KeyValues* pKV, const char* name, const char* data )
+{
+ pKV->SetString( name, data );
+}
+void AddDataToKV( KeyValues* pKV, const char* name, const Color& data )
+{
+ pKV->SetColor( name, data );
+}
+void AddDataToKV( KeyValues* pKV, const char* name, short data )
+{
+ pKV->SetInt( name, data );
+}
+void AddDataToKV( KeyValues* pKV, const char* name, unsigned data )
+{
+ pKV->SetInt( name, data );
+}
+void AddPositionDataToKV( KeyValues* pKV, const char* name, const Vector &data )
+{
+ // Append the data name to the member
+ pKV->SetFloat( CFmtStr("%s%s", name, "_X"), data.x );
+ pKV->SetFloat( CFmtStr("%s%s", name, "_Y"), data.y );
+ pKV->SetFloat( CFmtStr("%s%s", name, "_Z"), data.z );
+}
+
+//=============================================================================//
+
+//=============================================================================
+//
+// Helper functions for creating key values from arrays
+//
+void AddArrayDataToKV( KeyValues* pKV, const char* name, const short *data, unsigned size )
+{
+ for( unsigned i=0; i<size; ++i )
+ pKV->SetInt( CFmtStr("%s_%d", name, i) , data[i] );
+}
+void AddArrayDataToKV( KeyValues* pKV, const char* name, const byte *data, unsigned size )
+{
+ for( unsigned i=0; i<size; ++i )
+ pKV->SetInt( CFmtStr("%s_%d", name, i), data[i] );
+}
+void AddArrayDataToKV( KeyValues* pKV, const char* name, const unsigned *data, unsigned size )
+{
+ for( unsigned i=0; i<size; ++i )
+ pKV->SetInt( CFmtStr("%s_%d", name, i), data[i] );
+}
+void AddStringDataToKV( KeyValues* pKV, const char* name, const char*data )
+{
+ if( name == NULL )
+ return;
+
+ pKV->SetString( name, data );
+}
+//=============================================================================//
+
+
+void IGameStatTracker::PrintGamestatMemoryUsage( void )
+{
+ StatContainerList_t* pStatList = GetStatContainerList();
+ if( !pStatList )
+ return;
+
+ int iListSize = pStatList->Count();
+
+ // For every stat list being tracked, print out its memory usage
+ for( int i=0; i < iListSize; ++i )
+ {
+ pStatList->operator []( i )->PrintMemoryUsage();
+ }
+}
diff --git a/game/shared/cstrike/cs_gamestats_shared.h b/game/shared/cstrike/cs_gamestats_shared.h
new file mode 100644
index 0000000..dc58a77
--- /dev/null
+++ b/game/shared/cstrike/cs_gamestats_shared.h
@@ -0,0 +1,824 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+#ifndef CS_GAMESTATS_SHARED_H
+#define CS_GAMESTATS_SHARED_H
+#ifdef _WIN32
+#pragma once
+#endif
+#include "cbase.h"
+// #include "tier1/utlvector.h"
+// #include "tier1/utldict.h"
+#include "shareddefs.h"
+#include "cs_shareddefs.h"
+#include "cs_weapon_parse.h"
+#include "fmtstr.h"
+
+
+#define CS_NUM_LEVELS 18
+
+//=============================================================================
+// Helper class for simple manipulation of bit arrays.
+// Used for server->client packets containing delta stats
+//=============================================================================
+
+template <int BitLength>
+class BitArray
+{
+ enum { ByteLength = (BitLength + 7) / 8 };
+public:
+ BitArray() { ClearAll(); }
+
+ void SetBit(int n) { m_bytes[n / 8] |= 1 << (n & 7); }
+ void ClearBit(int n) { m_bytes[n / 8] &= (~(1 << (n & 7))); }
+ bool IsBitSet(int n) const { return (m_bytes[n / 8] & (1 << (n & 7))) != 0;}
+
+ void ClearAll() { V_memset(m_bytes, 0, sizeof(m_bytes)); }
+ int NumBits() { return BitLength; }
+ int NumBytes() { return ByteLength; }
+
+ byte* RawPointer() { return m_bytes; }
+
+private:
+ byte m_bytes[ByteLength];
+};
+
+
+//=============================================================================
+//
+// CS Game Stats Enums
+//
+// WARNING! ANY CHANGE TO THE ORDERING OR NUMBER OF STATS WILL REQUIRE
+// SYNCHRONOUS UPDATE OF CLIENT AND SERVER DLLS. If you change these enums
+// (including adding new stats) without forcing an update of both the client
+// and server, stats will become corrupted on the game client and these
+// corrupted values will be uploaded to steam, which is very very bad.
+//
+// If you add new stats, if will be safest to add them at the end of the enum
+// (although this will still require a server update); make sure you also add
+// the stats to the CSStatProperty_Table in cs_gamestats_shared.cpp at the
+// appropriate location.
+
+enum CSStatType_t
+{
+ CSSTAT_UNDEFINED = -1,
+ CSSTAT_SHOTS_HIT,
+ CSSTAT_SHOTS_FIRED,
+ CSSTAT_KILLS,
+ CSSTAT_DEATHS,
+ CSSTAT_DAMAGE,
+ CSSTAT_NUM_BOMBS_PLANTED,
+ CSSTAT_NUM_BOMBS_DEFUSED,
+ CSSTAT_PLAYTIME,
+ CSSTAT_ROUNDS_WON,
+ CSSTAT_T_ROUNDS_WON,
+ CSSTAT_CT_ROUNDS_WON,
+ CSSTAT_ROUNDS_PLAYED,
+ CSSTAT_PISTOLROUNDS_WON,
+ CSSTAT_MONEY_EARNED,
+ CSSTAT_OBJECTIVES_COMPLETED,
+ CSSTAT_BOMBS_DEFUSED_WITHKIT,
+
+ CSSTAT_KILLS_DEAGLE,
+ CSSTAT_KILLS_USP,
+ CSSTAT_KILLS_GLOCK,
+ CSSTAT_KILLS_P228,
+ CSSTAT_KILLS_ELITE,
+ CSSTAT_KILLS_FIVESEVEN,
+ CSSTAT_KILLS_AWP,
+ CSSTAT_KILLS_AK47,
+ CSSTAT_KILLS_M4A1,
+ CSSTAT_KILLS_AUG,
+ CSSTAT_KILLS_SG552,
+ CSSTAT_KILLS_SG550,
+ CSSTAT_KILLS_GALIL,
+ CSSTAT_KILLS_FAMAS,
+ CSSTAT_KILLS_SCOUT,
+ CSSTAT_KILLS_G3SG1,
+ CSSTAT_KILLS_P90,
+ CSSTAT_KILLS_MP5NAVY,
+ CSSTAT_KILLS_TMP,
+ CSSTAT_KILLS_MAC10,
+ CSSTAT_KILLS_UMP45,
+ CSSTAT_KILLS_M3,
+ CSSTAT_KILLS_XM1014,
+ CSSTAT_KILLS_M249,
+ CSSTAT_KILLS_KNIFE,
+ CSSTAT_KILLS_HEGRENADE,
+
+ CSSTAT_SHOTS_DEAGLE,
+ CSSTAT_SHOTS_USP,
+ CSSTAT_SHOTS_GLOCK,
+ CSSTAT_SHOTS_P228,
+ CSSTAT_SHOTS_ELITE,
+ CSSTAT_SHOTS_FIVESEVEN,
+ CSSTAT_SHOTS_AWP,
+ CSSTAT_SHOTS_AK47,
+ CSSTAT_SHOTS_M4A1,
+ CSSTAT_SHOTS_AUG,
+ CSSTAT_SHOTS_SG552,
+ CSSTAT_SHOTS_SG550,
+ CSSTAT_SHOTS_GALIL,
+ CSSTAT_SHOTS_FAMAS,
+ CSSTAT_SHOTS_SCOUT,
+ CSSTAT_SHOTS_G3SG1,
+ CSSTAT_SHOTS_P90,
+ CSSTAT_SHOTS_MP5NAVY,
+ CSSTAT_SHOTS_TMP,
+ CSSTAT_SHOTS_MAC10,
+ CSSTAT_SHOTS_UMP45,
+ CSSTAT_SHOTS_M3,
+ CSSTAT_SHOTS_XM1014,
+ CSSTAT_SHOTS_M249,
+ CSSTAT_SHOTS_KNIFE,
+ CSSTAT_SHOTS_HEGRENADE,
+
+ CSSTAT_HITS_DEAGLE,
+ CSSTAT_HITS_USP,
+ CSSTAT_HITS_GLOCK,
+ CSSTAT_HITS_P228,
+ CSSTAT_HITS_ELITE,
+ CSSTAT_HITS_FIVESEVEN,
+ CSSTAT_HITS_AWP,
+ CSSTAT_HITS_AK47,
+ CSSTAT_HITS_M4A1,
+ CSSTAT_HITS_AUG,
+ CSSTAT_HITS_SG552,
+ CSSTAT_HITS_SG550,
+ CSSTAT_HITS_GALIL,
+ CSSTAT_HITS_FAMAS,
+ CSSTAT_HITS_SCOUT,
+ CSSTAT_HITS_G3SG1,
+ CSSTAT_HITS_P90,
+ CSSTAT_HITS_MP5NAVY,
+ CSSTAT_HITS_TMP,
+ CSSTAT_HITS_MAC10,
+ CSSTAT_HITS_UMP45,
+ CSSTAT_HITS_M3,
+ CSSTAT_HITS_XM1014,
+ CSSTAT_HITS_M249,
+ CSSTAT_HITS_KNIFE,
+ CSSTAT_HITS_HEGRENADE,
+
+ CSSTAT_DAMAGE_DEAGLE,
+ CSSTAT_DAMAGE_USP,
+ CSSTAT_DAMAGE_GLOCK,
+ CSSTAT_DAMAGE_P228,
+ CSSTAT_DAMAGE_ELITE,
+ CSSTAT_DAMAGE_FIVESEVEN,
+ CSSTAT_DAMAGE_AWP,
+ CSSTAT_DAMAGE_AK47,
+ CSSTAT_DAMAGE_M4A1,
+ CSSTAT_DAMAGE_AUG,
+ CSSTAT_DAMAGE_SG552,
+ CSSTAT_DAMAGE_SG550,
+ CSSTAT_DAMAGE_GALIL,
+ CSSTAT_DAMAGE_FAMAS,
+ CSSTAT_DAMAGE_SCOUT,
+ CSSTAT_DAMAGE_G3SG1,
+ CSSTAT_DAMAGE_P90,
+ CSSTAT_DAMAGE_MP5NAVY,
+ CSSTAT_DAMAGE_TMP,
+ CSSTAT_DAMAGE_MAC10,
+ CSSTAT_DAMAGE_UMP45,
+ CSSTAT_DAMAGE_M3,
+ CSSTAT_DAMAGE_XM1014,
+ CSSTAT_DAMAGE_M249,
+ CSSTAT_DAMAGE_KNIFE,
+ CSSTAT_DAMAGE_HEGRENADE,
+
+ CSSTAT_KILLS_HEADSHOT,
+ CSSTAT_KILLS_ENEMY_BLINDED,
+ CSSTAT_KILLS_WHILE_BLINDED,
+ CSSTAT_KILLS_WITH_LAST_ROUND,
+ CSSTAT_KILLS_ENEMY_WEAPON,
+ CSSTAT_KILLS_KNIFE_FIGHT,
+ CSSTAT_KILLS_WHILE_DEFENDING_BOMB,
+
+ CSSTAT_DECAL_SPRAYS,
+ CSSTAT_TOTAL_JUMPS,
+ CSSTAT_NIGHTVISION_DAMAGE,
+ CSSTAT_KILLS_WHILE_LAST_PLAYER_ALIVE,
+ CSSTAT_KILLS_ENEMY_WOUNDED,
+ CSSTAT_FALL_DAMAGE,
+
+ CSSTAT_NUM_HOSTAGES_RESCUED,
+
+ CSSTAT_NUM_BROKEN_WINDOWS,
+ CSSTAT_PROPSBROKEN_ALL,
+ CSSTAT_PROPSBROKEN_MELON,
+ CSSTAT_PROPSBROKEN_OFFICEELECTRONICS,
+ CSSTAT_PROPSBROKEN_OFFICERADIO,
+ CSSTAT_PROPSBROKEN_OFFICEJUNK,
+ CSSTAT_PROPSBROKEN_ITALY_MELON,
+
+ CSSTAT_KILLS_AGAINST_ZOOMED_SNIPER,
+
+ CSSTAT_WEAPONS_DONATED,
+
+ CSSTAT_ITEMS_PURCHASED,
+ CSSTAT_MONEY_SPENT,
+
+ CSSTAT_DOMINATIONS,
+ CSSTAT_DOMINATION_OVERKILLS,
+ CSSTAT_REVENGES,
+
+ CSSTAT_MVPS,
+
+ CSSTAT_GRENADE_DAMAGE,
+ CSSTAT_GRENADE_POSTHUMOUSKILLS,
+ CSSTAT_GRENADES_THROWN,
+
+ CSTAT_ITEMS_DROPPED_VALUE,
+
+ //Map win stats
+ CSSTAT_MAP_WINS_CS_ASSAULT,
+ CSSTAT_MAP_WINS_CS_COMPOUND,
+ CSSTAT_MAP_WINS_CS_HAVANA,
+ CSSTAT_MAP_WINS_CS_ITALY,
+ CSSTAT_MAP_WINS_CS_MILITIA,
+ CSSTAT_MAP_WINS_CS_OFFICE,
+ CSSTAT_MAP_WINS_DE_AZTEC,
+ CSSTAT_MAP_WINS_DE_CBBLE,
+ CSSTAT_MAP_WINS_DE_CHATEAU,
+ CSSTAT_MAP_WINS_DE_DUST2,
+ CSSTAT_MAP_WINS_DE_DUST,
+ CSSTAT_MAP_WINS_DE_INFERNO,
+ CSSTAT_MAP_WINS_DE_NUKE,
+ CSSTAT_MAP_WINS_DE_PIRANESI,
+ CSSTAT_MAP_WINS_DE_PORT,
+ CSSTAT_MAP_WINS_DE_PRODIGY,
+ CSSTAT_MAP_WINS_DE_TIDES,
+ CSSTAT_MAP_WINS_DE_TRAIN,
+
+ CSSTAT_MAP_ROUNDS_CS_ASSAULT,
+ CSSTAT_MAP_ROUNDS_CS_COMPOUND,
+ CSSTAT_MAP_ROUNDS_CS_HAVANA,
+ CSSTAT_MAP_ROUNDS_CS_ITALY,
+ CSSTAT_MAP_ROUNDS_CS_MILITIA,
+ CSSTAT_MAP_ROUNDS_CS_OFFICE,
+ CSSTAT_MAP_ROUNDS_DE_AZTEC,
+ CSSTAT_MAP_ROUNDS_DE_CBBLE,
+ CSSTAT_MAP_ROUNDS_DE_CHATEAU,
+ CSSTAT_MAP_ROUNDS_DE_DUST2,
+ CSSTAT_MAP_ROUNDS_DE_DUST,
+ CSSTAT_MAP_ROUNDS_DE_INFERNO,
+ CSSTAT_MAP_ROUNDS_DE_NUKE,
+ CSSTAT_MAP_ROUNDS_DE_PIRANESI,
+ CSSTAT_MAP_ROUNDS_DE_PORT,
+ CSSTAT_MAP_ROUNDS_DE_PRODIGY,
+ CSSTAT_MAP_ROUNDS_DE_TIDES,
+ CSSTAT_MAP_ROUNDS_DE_TRAIN,
+
+ CSSTAT_LASTMATCH_T_ROUNDS_WON,
+ CSSTAT_LASTMATCH_CT_ROUNDS_WON,
+ CSSTAT_LASTMATCH_ROUNDS_WON,
+ CSSTAT_LASTMATCH_KILLS,
+ CSSTAT_LASTMATCH_DEATHS,
+ CSSTAT_LASTMATCH_MVPS,
+ CSSTAT_LASTMATCH_DAMAGE,
+ CSSTAT_LASTMATCH_MONEYSPENT,
+ CSSTAT_LASTMATCH_DOMINATIONS,
+ CSSTAT_LASTMATCH_REVENGES,
+ CSSTAT_LASTMATCH_MAX_PLAYERS,
+ CSSTAT_LASTMATCH_FAVWEAPON_ID,
+ CSSTAT_LASTMATCH_FAVWEAPON_SHOTS,
+ CSSTAT_LASTMATCH_FAVWEAPON_HITS,
+ CSSTAT_LASTMATCH_FAVWEAPON_KILLS,
+
+ CSSTAT_MAX //Must be last entry.
+};
+
+
+#define CSSTAT_FIRST (CSSTAT_UNDEFINED+1)
+#define CSSTAT_LAST (CSSTAT_MAX-1)
+
+//
+// CS Game Stats Flags
+//
+#define CSSTAT_PRIORITY_MASK 0x000F
+#define CSSTAT_PRIORITY_NEVER 0x0000 // not sent to client
+#define CSSTAT_PRIORITY_ENDROUND 0x0001 // sent at end of round
+#define CSSTAT_PRIORITY_LOW 0x0002 // sent every 2500ms
+#define CSSTAT_PRIORITY_HIGH 0x0003 // sent every 250ms
+
+struct CSStatProperty
+{
+ int statId; // verify that table ordering is correct
+ const char* szSteamName; // name of the stat on steam
+ const char* szLocalizationToken; // localization token for the stat
+ uint flags; // priority flags for sending to client
+};
+
+extern CSStatProperty CSStatProperty_Table[];
+
+
+//=============================================================================
+//
+// CS Player Round Stats
+//
+struct StatsCollection_t
+{
+ StatsCollection_t() { Reset(); }
+
+ inline int Get( int i ) const
+ {
+ AssertMsg( i >= CSSTAT_FIRST && i < CSSTAT_MAX, "Stat index out of range!" );
+ if ( i >= 0 )
+ return m_iValue[ i ];
+ return 0;
+ }
+
+ inline void Set( int i, int nValue )
+ {
+ AssertMsg( i >= CSSTAT_FIRST && i < CSSTAT_MAX, "Stat index out of range!" );
+ if ( i >= 0 )
+ m_iValue[ i ] = nValue;
+ }
+
+ void Reset()
+ {
+ for ( int i = 0; i < ARRAYSIZE( m_iValue ); i++ )
+ {
+ m_iValue[i] = 0;
+ }
+ }
+
+ int operator[] ( int index ) const
+ {
+ Assert(index >= 0 && index < ARRAYSIZE(m_iValue));
+ return m_iValue[index];
+ }
+
+ int& operator[] ( int index )
+ {
+ Assert(index >= 0 && index < ARRAYSIZE(m_iValue));
+ return m_iValue[index];
+ }
+
+ void Aggregate( const StatsCollection_t& other );
+
+private:
+ int m_iValue[CSSTAT_MAX];
+};
+
+
+//=============================================================================
+// HPE_BEGIN:
+// [tj] A couple variations on the RoundStats structure to handle extra operations
+// for averaging and accumulating
+//=============================================================================
+struct RoundStatsDirectAverage_t
+{
+ float m_fStat[CSSTAT_MAX];
+
+
+ RoundStatsDirectAverage_t()
+ {
+ Reset();
+ }
+
+ void Reset()
+ {
+ for ( int i = 0; i < ARRAYSIZE( m_fStat ); i++ )
+ {
+ m_fStat[i] = 0;
+ }
+ }
+
+ RoundStatsDirectAverage_t& operator +=( const StatsCollection_t &other )
+ {
+ for ( int i = 0; i < ARRAYSIZE( m_fStat ); i++ )
+ {
+ m_fStat[i] += other[i];
+ }
+ return *this;
+ }
+
+ RoundStatsDirectAverage_t& operator /=( const float &divisor)
+ {
+ if (divisor > 0)
+ {
+ for ( int i = 0; i < ARRAYSIZE( m_fStat ); i++ )
+ {
+ m_fStat[i] /= divisor;
+ }
+ }
+ return *this;
+ }
+
+ RoundStatsDirectAverage_t& operator *=( const float &divisor)
+ {
+ for ( int i = 0; i < ARRAYSIZE( m_fStat ); i++ )
+ {
+ m_fStat[i] *= divisor;
+ }
+ return *this;
+ }
+};
+
+
+struct RoundStatsRollingAverage_t
+{
+ float m_fStat[CSSTAT_MAX];
+ int m_numberOfDataSets;
+
+ RoundStatsRollingAverage_t()
+ {
+ Reset();
+ }
+
+ void Reset()
+ {
+ for ( int i = 0; i < ARRAYSIZE( m_fStat ); i++ )
+ {
+ m_fStat[i] = 0;
+ }
+ m_numberOfDataSets = 0;
+ }
+
+ RoundStatsRollingAverage_t& operator +=( const RoundStatsRollingAverage_t &other )
+ {
+ for ( int i = 0; i < ARRAYSIZE( m_fStat ); i++ )
+ {
+ m_fStat[i] += other.m_fStat[i];
+ }
+ return *this;
+ }
+
+ RoundStatsRollingAverage_t& operator +=( const StatsCollection_t &other )
+ {
+ for ( int i = 0; i < ARRAYSIZE( m_fStat ); i++ )
+ {
+ m_fStat[i] += other[i];
+ }
+ return *this;
+ }
+
+ RoundStatsRollingAverage_t& operator /=( const float &divisor)
+ {
+ if (divisor > 0)
+ {
+ for ( int i = 0; i < ARRAYSIZE( m_fStat ); i++ )
+ {
+ m_fStat[i] /= divisor;
+ }
+ }
+ return *this;
+ }
+
+ void RollDataSetIntoAverage ( const RoundStatsRollingAverage_t &other )
+ {
+ for ( int i = 0; i < ARRAYSIZE( m_fStat ); i++ )
+ {
+ m_fStat[i] *= m_numberOfDataSets;
+ m_fStat[i] += other.m_fStat[i];
+ m_fStat[i] /= (m_numberOfDataSets + 1);
+ }
+ m_numberOfDataSets++;
+ }
+};
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+enum CSGameStatsVersions_t
+{
+ CS_GAMESTATS_FILE_VERSION = 006,
+ CS_GAMESTATS_MAGIC = 0xDEADBEEF
+};
+
+struct CS_Gamestats_Version_t
+{
+ int m_iMagic; // always CS_GAMESTATS_MAGIC
+ int m_iVersion;
+};
+
+
+struct KillStats_t
+{
+ KillStats_t() { Reset(); }
+
+ void Reset()
+ {
+ Q_memset( iNumKilled, 0, sizeof( iNumKilled ) );
+ Q_memset( iNumKilledBy, 0, sizeof( iNumKilledBy ) );
+ Q_memset( iNumKilledByUnanswered, 0, sizeof( iNumKilledByUnanswered ) );
+ }
+
+ int iNumKilled[MAX_PLAYERS+1]; // how many times this player has killed each other player
+ int iNumKilledBy[MAX_PLAYERS+1]; // how many times this player has been killed by each other player
+ int iNumKilledByUnanswered[MAX_PLAYERS+1]; // how many unanswered kills this player has been dealt by each other player
+};
+
+//=============================================================================
+//
+// CS Player Stats
+//
+struct PlayerStats_t
+{
+ PlayerStats_t()
+ {
+ Reset();
+ }
+
+ void Reset()
+ {
+ statsDelta.Reset();
+ statsCurrentRound.Reset();
+ statsCurrentMatch.Reset();
+ statsKills.Reset();
+ }
+
+ PlayerStats_t( const PlayerStats_t &other )
+ {
+ statsDelta = other.statsDelta;
+ statsCurrentRound = other.statsCurrentRound;
+ statsCurrentMatch = other.statsCurrentMatch;
+ }
+
+ StatsCollection_t statsDelta;
+ StatsCollection_t statsCurrentRound;
+ StatsCollection_t statsCurrentMatch;
+ KillStats_t statsKills;
+};
+
+
+struct WeaponName_StatId
+{
+ CSWeaponID weaponId;
+ CSStatType_t killStatId;
+ CSStatType_t shotStatId;
+ CSStatType_t hitStatId;
+ CSStatType_t damageStatId;
+};
+
+struct MapName_MapStatId
+{
+ const char* szMapName;
+ CSStatType_t statWinsId;
+ CSStatType_t statRoundsId;
+};
+
+extern const MapName_MapStatId MapName_StatId_Table[];
+
+//A mapping from weapon names to weapon stat IDs
+extern const WeaponName_StatId WeaponName_StatId_Table[];
+
+//Used to look up the appropriate entry by the ID of the actual weapon
+const WeaponName_StatId& GetWeaponTableEntryFromWeaponId(CSWeaponID id);
+
+#include "steamworks_gamestats.h"
+
+//=============================================================================
+//
+// Helper functions for creating key values
+//
+void AddDataToKV( KeyValues* pKV, const char* name, int data );
+void AddDataToKV( KeyValues* pKV, const char* name, uint64 data );
+void AddDataToKV( KeyValues* pKV, const char* name, float data );
+void AddDataToKV( KeyValues* pKV, const char* name, bool data );
+void AddDataToKV( KeyValues* pKV, const char* name, const char* data );
+void AddDataToKV( KeyValues* pKV, const char* name, const Color& data );
+void AddDataToKV( KeyValues* pKV, const char* name, short data );
+void AddDataToKV( KeyValues* pKV, const char* name, unsigned data );
+void AddDataToKV( KeyValues* pKV, const char* name, const Vector& data );
+void AddPositionDataToKV( KeyValues* pKV, const char* name, const Vector &data );
+//=============================================================================
+
+//=============================================================================
+//
+// Helper functions for creating key values from arrays
+//
+void AddArrayDataToKV( KeyValues* pKV, const char* name, const short *data, unsigned size );
+void AddArrayDataToKV( KeyValues* pKV, const char* name, const byte *data, unsigned size );
+void AddArrayDataToKV( KeyValues* pKV, const char* name, const unsigned *data, unsigned size );
+void AddStringDataToKV( KeyValues* pKV, const char* name, const char *data );
+
+//=============================================================================
+
+// Macros to ease the creation of SendData method for stats structs/classes
+#define BEGIN_STAT_TABLE( tableName ) \
+ static const char* GetStatTableName( void ) { return tableName; } \
+ void BuildGamestatDataTable( KeyValues* pKV ) \
+{ \
+ pKV->SetName( GetStatTableName() );
+
+#define REGISTER_STAT( varName ) \
+ AddDataToKV(pKV, #varName, varName);
+
+#define REGISTER_STAT_NAMED( varName, dbName ) \
+ AddDataToKV(pKV, dbName, varName);
+
+#define REGISTER_STAT_POSITION( varName ) \
+ AddPositionDataToKV(pKV, #varName, varName);
+
+#define REGISTER_STAT_POSITION_NAMED( varName, dbName ) \
+ AddPositionDataToKV(pKV, dbName, varName);
+
+#define REGISTER_STAT_ARRAY( varName ) \
+ AddArrayDataToKV( pKV, #varName, varName, ARRAYSIZE( varName ) );
+
+#define REGISTER_STAT_ARRAY_NAMED( varName, dbName ) \
+ AddArrayDataToKV( pKV, dbName, varName, ARRAYSIZE( varName ) );
+
+#define REGISTER_STAT_STRING( varName ) \
+ AddStringDataToKV( pKV, #varName, varName );
+
+#define REGISTER_STAT_STRING_NAMED( varName, dbName ) \
+ AddStringDataToKV( pKV, dbName, varName );
+
+#define AUTO_STAT_TABLE_KEY() \
+ pKV->SetInt( "TimeSubmitted", GetUniqueIDForStatTable( *this ) );
+
+#define END_STAT_TABLE() \
+ pKV->SetUint64( ::BaseStatData::m_bUseGlobalData ? "TimeSubmitted" : "SessionTime", ::BaseStatData::TimeSubmitted ); \
+ GetSteamWorksSGameStatsUploader().AddStatsForUpload( pKV ); \
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Templatized class for getting unique ID's for stat tables that need
+// to be submitted multiple times per-session.
+//-----------------------------------------------------------------------------
+
+template < typename T >
+class UniqueStatID_t
+{
+public:
+ static unsigned GetNext( void )
+ {
+ return ++s_nLastID;
+ }
+
+ static void Reset( void )
+ {
+ s_nLastID = 0;
+ }
+
+private:
+ static unsigned s_nLastID;
+};
+
+template < typename T >
+unsigned UniqueStatID_t< T >::s_nLastID = 0;
+
+template < typename T >
+unsigned GetUniqueIDForStatTable( const T &table )
+{
+ return UniqueStatID_t< T >::GetNext();
+}
+
+
+//=============================================================================
+//
+// An interface for tracking gamestats.
+//
+class IGameStatTracker
+{
+public:
+
+ //-----------------------------------------------------------------------------
+ // Templatized methods to track a per-mission stat.
+ // The stat is copied, then deleted after it's sent to the SQL server.
+ //-----------------------------------------------------------------------------
+ template < typename T >
+ void SubmitStat( T& stat )
+ {
+ // Make a copy of the stat. All of the stat lists require pointers,
+ // so we need to protect against a stat allocated on the stack
+ T* pT = new T();
+ if( !pT )
+ return;
+
+ *pT = stat;
+ SubmitStat( pT );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Templatized methods to track a per-mission stat (by pointer)
+ // The stat is deleted after it's sent to the SQL server
+ //-----------------------------------------------------------------------------
+ template < typename T >
+ void SubmitStat( T* pStat )
+ {
+ // Get the static stat table for this type and add the stat to it
+ GetStatTable<T>()->AddToTail( pStat );
+ }
+
+ //-----------------------------------------------------------------------------
+ // Add all stats to an existing key value file for submit.
+ //-----------------------------------------------------------------------------
+ virtual void SubmitGameStats( KeyValues *pKV ) = 0;
+
+ //-----------------------------------------------------------------------------
+ // Prints the memory usage of all of the stats being tracked
+ //-----------------------------------------------------------------------------
+ void PrintGamestatMemoryUsage( void );
+
+protected:
+ //=============================================================================
+ //
+ // Used as a base interface to store a list of all templatized stat containers
+ //
+ class IStatContainer
+ {
+ public:
+ virtual void SendData( KeyValues *pKV ) = 0;
+ virtual void Clear( void ) = 0;
+ virtual void PrintMemoryUsage( void ) = 0;
+ };
+
+ // Defines a list of stat containers.
+ typedef CUtlVector< IStatContainer* > StatContainerList_t;
+
+ //-----------------------------------------------------------------------------
+ // Used to get a list of all stats containers being tracked by the deriving class
+ //-----------------------------------------------------------------------------
+ virtual StatContainerList_t* GetStatContainerList( void ) = 0;
+
+private:
+
+ //=============================================================================
+ //
+ // Templatized list of stats submitted
+ //
+ template < typename T >
+ class CGameStatList : public IStatContainer, public CUtlVector< T* >
+ {
+ public:
+ //-----------------------------------------------------------------------------
+ // Get data ready to send to the SQL server
+ //-----------------------------------------------------------------------------
+ virtual void SendData( KeyValues *pKV )
+ {
+ //ASSERT( pKV != NULL );
+
+ // Duplicate the master KeyValue for each stat instance
+ for( int i=0; i < this->m_Size; ++i )
+ {
+ // Make a copy of the master key value and build the stat table
+ KeyValues *pKVCopy = this->operator [](i)->m_bUseGlobalData ? pKV->MakeCopy() : new KeyValues( "" );
+ this->operator [](i)->BuildGamestatDataTable( pKVCopy );
+ }
+
+ // Reset unique ID counter for the stat type
+ UniqueStatID_t< T >::Reset();
+ }
+
+ //-----------------------------------------------------------------------------
+ // Clear and delete every stat in this list
+ //-----------------------------------------------------------------------------
+ virtual void Clear( void )
+ {
+ this->PurgeAndDeleteElements();
+ }
+
+ //-----------------------------------------------------------------------------
+ // Print out details about this lists memory usage
+ //-----------------------------------------------------------------------------
+ virtual void PrintMemoryUsage( void )
+ {
+ if( this->m_Size == 0 )
+ return;
+
+ // Compute the memory used as the size of type times the list count
+ unsigned uMemoryUsed = this->m_Size * ( sizeof( T ) );
+
+ Msg( " %d\tbytes used by %s table\n", uMemoryUsed, T::GetStatTableName() );
+ }
+ };
+
+ //-----------------------------------------------------------------------------
+ // Templatized method to get a single instance of a stat list per data type.
+ //-----------------------------------------------------------------------------
+ template < typename T >
+ CGameStatList< T >* GetStatTable( void )
+ {
+ static CGameStatList< T > *s_vecOfType = 0;
+ if( s_vecOfType == 0 )
+ {
+ s_vecOfType = new CGameStatList< T >();
+ GetStatContainerList()->AddToTail( s_vecOfType );
+ }
+ return s_vecOfType;
+ }
+
+};
+
+struct BaseStatData
+{
+ BaseStatData( bool bUseGlobalData = true ) : m_bUseGlobalData( bUseGlobalData )
+ {
+ TimeSubmitted = GetSteamWorksSGameStatsUploader().GetTimeSinceEpoch();
+ }
+
+ bool m_bUseGlobalData;
+ uint64 TimeSubmitted;
+
+};
+
+extern ConVar sv_noroundstats;
+
+#endif // CS_GAMESTATS_SHARED_H
diff --git a/game/shared/cstrike/cs_player_shared.cpp b/game/shared/cstrike/cs_player_shared.cpp
new file mode 100644
index 0000000..e0f3d91
--- /dev/null
+++ b/game/shared/cstrike/cs_player_shared.cpp
@@ -0,0 +1,943 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbase.h"
+#include "decals.h"
+#include "cs_gamerules.h"
+#include "weapon_c4.h"
+#include "in_buttons.h"
+#include "datacache/imdlcache.h"
+
+#ifdef CLIENT_DLL
+ #include "c_cs_player.h"
+#else
+ #include "cs_player.h"
+ #include "soundent.h"
+ #include "bot/cs_bot.h"
+ #include "KeyValues.h"
+ #include "triggers.h"
+ #include "cs_gamestats.h"
+#endif
+
+#include "cs_playeranimstate.h"
+#include "basecombatweapon_shared.h"
+#include "util_shared.h"
+#include "takedamageinfo.h"
+#include "effect_dispatch_data.h"
+#include "engine/ivdebugoverlay.h"
+#include "obstacle_pushaway.h"
+#include "props_shared.h"
+
+ConVar sv_showimpacts("sv_showimpacts", "0", FCVAR_REPLICATED, "Shows client (red) and server (blue) bullet impact point (1=both, 2=client-only, 3=server-only)" );
+ConVar sv_showplayerhitboxes( "sv_showplayerhitboxes", "0", FCVAR_REPLICATED, "Show lag compensated hitboxes for the specified player index whenever a player fires." );
+
+#define CS_MASK_SHOOT (MASK_SOLID|CONTENTS_DEBRIS)
+
+void DispatchEffect( const char *pName, const CEffectData &data );
+
+
+#ifdef _DEBUG
+
+ // This is some extra code to collect weapon accuracy stats:
+
+ struct bulletdata_s
+ {
+ float timedelta; // time delta since first shot of this round
+ float derivation; // derivation for first shoot view angle
+ int count;
+ };
+
+ #define STATS_MAX_BULLETS 50
+
+ static bulletdata_s s_bullet_stats[STATS_MAX_BULLETS];
+
+ Vector s_firstImpact = Vector(0,0,0);
+ float s_firstTime = 0;
+ float s_LastTime = 0;
+ int s_bulletCount = 0;
+
+ void ResetBulletStats()
+ {
+ s_firstTime = 0;
+ s_LastTime = 0;
+ s_bulletCount = 0;
+ s_firstImpact = Vector(0,0,0);
+ Q_memset( s_bullet_stats, 0, sizeof(s_bullet_stats) );
+ }
+
+ void PrintBulletStats()
+ {
+ for (int i=0; i<STATS_MAX_BULLETS; i++ )
+ {
+ if (s_bullet_stats[i].count == 0)
+ break;
+
+ Msg("%3i;%3i;%.4f;%.4f\n", i, s_bullet_stats[i].count,
+ s_bullet_stats[i].timedelta, s_bullet_stats[i].derivation );
+ }
+ }
+
+ void AddBulletStat( float time, float dist, Vector &impact )
+ {
+ if ( time > s_LastTime + 2.0f )
+ {
+ // time delta since last shoot is bigger than 2 seconds, start new row
+ s_LastTime = s_firstTime = time;
+ s_bulletCount = 0;
+ s_firstImpact = impact;
+
+ }
+ else
+ {
+ s_LastTime = time;
+ s_bulletCount++;
+ }
+
+ if ( s_bulletCount >= STATS_MAX_BULLETS )
+ s_bulletCount = STATS_MAX_BULLETS -1;
+
+ if ( dist < 1 )
+ dist = 1;
+
+ int i = s_bulletCount;
+
+ float offset = VectorLength( s_firstImpact - impact );
+
+ float timedelta = time - s_firstTime;
+ float derivation = offset / dist;
+
+ float weight = (float)s_bullet_stats[i].count/(float)(s_bullet_stats[i].count+1);
+
+ s_bullet_stats[i].timedelta *= weight;
+ s_bullet_stats[i].timedelta += (1.0f-weight) * timedelta;
+
+ s_bullet_stats[i].derivation *= weight;
+ s_bullet_stats[i].derivation += (1.0f-weight) * derivation;
+
+ s_bullet_stats[i].count++;
+ }
+
+ CON_COMMAND( stats_bullets_reset, "Reset bullet stats")
+ {
+ ResetBulletStats();
+ }
+
+ CON_COMMAND( stats_bullets_print, "Print bullet stats")
+ {
+ PrintBulletStats();
+ }
+
+#endif
+
+float CCSPlayer::GetPlayerMaxSpeed()
+{
+ if ( GetMoveType() == MOVETYPE_NONE )
+ {
+ return CS_PLAYER_SPEED_STOPPED;
+ }
+
+ if ( IsObserver() )
+ {
+ // Player gets speed bonus in observer mode
+ return CS_PLAYER_SPEED_OBSERVER;
+ }
+
+ bool bValidMoveState = ( State_Get() == STATE_ACTIVE || State_Get() == STATE_OBSERVER_MODE );
+ if ( !bValidMoveState || m_bIsDefusing || CSGameRules()->IsFreezePeriod() )
+ {
+ // Player should not move during the freeze period
+ return CS_PLAYER_SPEED_STOPPED;
+ }
+
+ float speed = BaseClass::GetPlayerMaxSpeed();
+
+ if ( IsVIP() == true ) // VIP is slow due to the armour he's wearing
+ {
+ speed = MIN(speed, CS_PLAYER_SPEED_VIP);
+ }
+ else
+ {
+
+ CWeaponCSBase *pWeapon = dynamic_cast<CWeaponCSBase*>( GetActiveWeapon() );
+
+ if ( pWeapon )
+ {
+ if ( HasShield() && IsShieldDrawn() )
+ {
+ speed = MIN(speed, CS_PLAYER_SPEED_SHIELD);
+ }
+ else
+ {
+ speed = MIN(speed, pWeapon->GetMaxSpeed());
+ }
+ }
+ }
+
+ return speed;
+}
+
+
+void CCSPlayer::GetBulletTypeParameters(
+ int iBulletType,
+ float &fPenetrationPower,
+ float &flPenetrationDistance )
+{
+ //MIKETODO: make ammo types come from a script file.
+ if ( IsAmmoType( iBulletType, BULLET_PLAYER_50AE ) )
+ {
+ fPenetrationPower = 30;
+ flPenetrationDistance = 1000.0;
+ }
+ else if ( IsAmmoType( iBulletType, BULLET_PLAYER_762MM ) )
+ {
+ fPenetrationPower = 39;
+ flPenetrationDistance = 5000.0;
+ }
+ else if ( IsAmmoType( iBulletType, BULLET_PLAYER_556MM ) ||
+ IsAmmoType( iBulletType, BULLET_PLAYER_556MM_BOX ) )
+ {
+ fPenetrationPower = 35;
+ flPenetrationDistance = 4000.0;
+ }
+ else if ( IsAmmoType( iBulletType, BULLET_PLAYER_338MAG ) )
+ {
+ fPenetrationPower = 45;
+ flPenetrationDistance = 8000.0;
+ }
+ else if ( IsAmmoType( iBulletType, BULLET_PLAYER_9MM ) )
+ {
+ fPenetrationPower = 21;
+ flPenetrationDistance = 800.0;
+ }
+ else if ( IsAmmoType( iBulletType, BULLET_PLAYER_BUCKSHOT ) )
+ {
+ fPenetrationPower = 0;
+ flPenetrationDistance = 0.0;
+ }
+ else if ( IsAmmoType( iBulletType, BULLET_PLAYER_45ACP ) )
+ {
+ fPenetrationPower = 15;
+ flPenetrationDistance = 500.0;
+ }
+ else if ( IsAmmoType( iBulletType, BULLET_PLAYER_357SIG ) )
+ {
+ fPenetrationPower = 25;
+ flPenetrationDistance = 800.0;
+ }
+ else if ( IsAmmoType( iBulletType, BULLET_PLAYER_57MM ) )
+ {
+ fPenetrationPower = 30;
+ flPenetrationDistance = 2000.0;
+ }
+ else
+ {
+ // What kind of ammo is this?
+ Assert( false );
+ fPenetrationPower = 0;
+ flPenetrationDistance = 0.0;
+ }
+}
+
+static void GetMaterialParameters( int iMaterial, float &flPenetrationModifier, float &flDamageModifier )
+{
+ switch ( iMaterial )
+ {
+ case CHAR_TEX_METAL :
+ flPenetrationModifier = 0.5; // If we hit metal, reduce the thickness of the brush we can't penetrate
+ flDamageModifier = 0.3;
+ break;
+ case CHAR_TEX_DIRT :
+ flPenetrationModifier = 0.5;
+ flDamageModifier = 0.3;
+ break;
+ case CHAR_TEX_CONCRETE :
+ flPenetrationModifier = 0.4;
+ flDamageModifier = 0.25;
+ break;
+ case CHAR_TEX_GRATE :
+ flPenetrationModifier = 1.0;
+ flDamageModifier = 0.99;
+ break;
+ case CHAR_TEX_VENT :
+ flPenetrationModifier = 0.5;
+ flDamageModifier = 0.45;
+ break;
+ case CHAR_TEX_TILE :
+ flPenetrationModifier = 0.65;
+ flDamageModifier = 0.3;
+ break;
+ case CHAR_TEX_COMPUTER :
+ flPenetrationModifier = 0.4;
+ flDamageModifier = 0.45;
+ break;
+ case CHAR_TEX_WOOD :
+ flPenetrationModifier = 1.0;
+ flDamageModifier = 0.6;
+ break;
+ default :
+ flPenetrationModifier = 1.0;
+ flDamageModifier = 0.5;
+ break;
+ }
+
+ Assert( flPenetrationModifier > 0 );
+ Assert( flDamageModifier < 1.0f ); // Less than 1.0f for avoiding infinite loops
+}
+
+
+static bool TraceToExit(Vector &start, Vector &dir, Vector &end, float flStepSize, float flMaxDistance )
+{
+ float flDistance = 0;
+ Vector last = start;
+
+ while ( flDistance <= flMaxDistance )
+ {
+ flDistance += flStepSize;
+
+ end = start + flDistance *dir;
+
+ if ( (UTIL_PointContents ( end ) & MASK_SOLID) == 0 )
+ {
+ // found first free point
+ return true;
+ }
+ }
+
+ return false;
+}
+
+inline void UTIL_TraceLineIgnoreTwoEntities( const Vector& vecAbsStart, const Vector& vecAbsEnd, unsigned int mask,
+ const IHandleEntity *ignore, const IHandleEntity *ignore2, int collisionGroup, trace_t *ptr )
+{
+ Ray_t ray;
+ ray.Init( vecAbsStart, vecAbsEnd );
+ CTraceFilterSkipTwoEntities traceFilter( ignore, ignore2, collisionGroup );
+ enginetrace->TraceRay( ray, mask, &traceFilter, ptr );
+ if( r_visualizetraces.GetBool() )
+ {
+ DebugDrawLine( ptr->startpos, ptr->endpos, 255, 0, 0, true, -1.0f );
+ }
+}
+
+void CCSPlayer::FireBullet(
+ Vector vecSrc, // shooting postion
+ const QAngle &shootAngles, //shooting angle
+ float flDistance, // max distance
+ int iPenetration, // how many obstacles can be penetrated
+ int iBulletType, // ammo type
+ int iDamage, // base damage
+ float flRangeModifier, // damage range modifier
+ CBaseEntity *pevAttacker, // shooter
+ bool bDoEffects,
+ float xSpread, float ySpread
+ )
+{
+ float fCurrentDamage = iDamage; // damage of the bullet at it's current trajectory
+ float flCurrentDistance = 0.0; //distance that the bullet has traveled so far
+
+ Vector vecDirShooting, vecRight, vecUp;
+ AngleVectors( shootAngles, &vecDirShooting, &vecRight, &vecUp );
+
+ // MIKETODO: put all the ammo parameters into a script file and allow for CS-specific params.
+ float flPenetrationPower = 0; // thickness of a wall that this bullet can penetrate
+ float flPenetrationDistance = 0; // distance at which the bullet is capable of penetrating a wall
+ float flDamageModifier = 0.5; // default modification of bullets power after they go through a wall.
+ float flPenetrationModifier = 1.f;
+
+ GetBulletTypeParameters( iBulletType, flPenetrationPower, flPenetrationDistance );
+
+
+ if ( !pevAttacker )
+ pevAttacker = this; // the default attacker is ourselves
+
+ // add the spray
+ Vector vecDir = vecDirShooting + xSpread * vecRight + ySpread * vecUp;
+
+ VectorNormalize( vecDir );
+
+ //Adrian: visualize server/client player positions
+ //This is used to show where the lag compesator thinks the player should be at.
+#if 0
+ for ( int k = 1; k <= gpGlobals->maxClients; k++ )
+ {
+ CBasePlayer *clientClass = (CBasePlayer *)CBaseEntity::Instance( k );
+
+ if ( clientClass == NULL )
+ continue;
+
+ if ( k == entindex() )
+ continue;
+
+#ifdef CLIENT_DLL
+ debugoverlay->AddBoxOverlay( clientClass->GetAbsOrigin(), clientClass->WorldAlignMins(), clientClass->WorldAlignMaxs(), QAngle( 0, 0, 0), 255,0,0,127, 4 );
+#else
+ NDebugOverlay::Box( clientClass->GetAbsOrigin(), clientClass->WorldAlignMins(), clientClass->WorldAlignMaxs(), 0,0,255,127, 4 );
+#endif
+
+ }
+
+#endif
+
+
+//=============================================================================
+// HPE_BEGIN:
+//=============================================================================
+
+#ifndef CLIENT_DLL
+ // [pfreese] Track number player entities killed with this bullet
+ int iPenetrationKills = 0;
+
+ // [menglish] Increment the shots fired for this player
+ CCS_GameStats.Event_ShotFired( this, GetActiveWeapon() );
+#endif
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+ bool bFirstHit = true;
+
+ CBasePlayer *lastPlayerHit = NULL;
+
+ if( sv_showplayerhitboxes.GetInt() > 0 )
+ {
+ CBasePlayer *lagPlayer = UTIL_PlayerByIndex( sv_showplayerhitboxes.GetInt() );
+ if( lagPlayer )
+ {
+#ifdef CLIENT_DLL
+ lagPlayer->DrawClientHitboxes(4, true);
+#else
+ lagPlayer->DrawServerHitboxes(4, true);
+#endif
+ }
+ }
+
+ MDLCACHE_CRITICAL_SECTION();
+ while ( fCurrentDamage > 0 )
+ {
+ Vector vecEnd = vecSrc + vecDir * flDistance;
+
+ trace_t tr; // main enter bullet trace
+
+ UTIL_TraceLineIgnoreTwoEntities( vecSrc, vecEnd, CS_MASK_SHOOT|CONTENTS_HITBOX, this, lastPlayerHit, COLLISION_GROUP_NONE, &tr );
+ {
+ CTraceFilterSkipTwoEntities filter( this, lastPlayerHit, COLLISION_GROUP_NONE );
+
+ // Check for player hitboxes extending outside their collision bounds
+ const float rayExtension = 40.0f;
+ UTIL_ClipTraceToPlayers( vecSrc, vecEnd + vecDir * rayExtension, CS_MASK_SHOOT|CONTENTS_HITBOX, &filter, &tr );
+ }
+
+ lastPlayerHit = ToBasePlayer(tr.m_pEnt);
+
+ if ( tr.fraction == 1.0f )
+ break; // we didn't hit anything, stop tracing shoot
+
+#ifdef _DEBUG
+ if ( bFirstHit )
+ AddBulletStat( gpGlobals->realtime, VectorLength( vecSrc-tr.endpos), tr.endpos );
+#endif
+
+ bFirstHit = false;
+
+#ifndef CLIENT_DLL
+ //
+ // Propogate a bullet impact event
+ // @todo Add this for shotgun pellets (which dont go thru here)
+ //
+ IGameEvent * event = gameeventmanager->CreateEvent( "bullet_impact" );
+ if ( event )
+ {
+ event->SetInt( "userid", GetUserID() );
+ event->SetFloat( "x", tr.endpos.x );
+ event->SetFloat( "y", tr.endpos.y );
+ event->SetFloat( "z", tr.endpos.z );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+
+ /************* MATERIAL DETECTION ***********/
+ surfacedata_t *pSurfaceData = physprops->GetSurfaceData( tr.surface.surfaceProps );
+ int iEnterMaterial = pSurfaceData->game.material;
+
+ GetMaterialParameters( iEnterMaterial, flPenetrationModifier, flDamageModifier );
+
+ bool hitGrate = tr.contents & CONTENTS_GRATE;
+
+ // since some railings in de_inferno are CONTENTS_GRATE but CHAR_TEX_CONCRETE, we'll trust the
+ // CONTENTS_GRATE and use a high damage modifier.
+ if ( hitGrate )
+ {
+ // If we're a concrete grate (TOOLS/TOOLSINVISIBLE texture) allow more penetrating power.
+ flPenetrationModifier = 1.0f;
+ flDamageModifier = 0.99f;
+ }
+
+#ifdef CLIENT_DLL
+ if ( sv_showimpacts.GetInt() == 1 || sv_showimpacts.GetInt() == 2 )
+ {
+ // draw red client impact markers
+ debugoverlay->AddBoxOverlay( tr.endpos, Vector(-2,-2,-2), Vector(2,2,2), QAngle( 0, 0, 0), 255,0,0,127, 4 );
+
+ if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() )
+ {
+ C_BasePlayer *player = ToBasePlayer( tr.m_pEnt );
+ player->DrawClientHitboxes( 4, true );
+ }
+ }
+#else
+ if ( sv_showimpacts.GetInt() == 1 || sv_showimpacts.GetInt() == 3 )
+ {
+ // draw blue server impact markers
+ NDebugOverlay::Box( tr.endpos, Vector(-2,-2,-2), Vector(2,2,2), 0,0,255,127, 4 );
+
+ if ( tr.m_pEnt && tr.m_pEnt->IsPlayer() )
+ {
+ CBasePlayer *player = ToBasePlayer( tr.m_pEnt );
+ player->DrawServerHitboxes( 4, true );
+ }
+ }
+#endif
+
+ //calculate the damage based on the distance the bullet travelled.
+ flCurrentDistance += tr.fraction * flDistance;
+ fCurrentDamage *= pow (flRangeModifier, (flCurrentDistance / 500));
+
+ // check if we reach penetration distance, no more penetrations after that
+ if (flCurrentDistance > flPenetrationDistance && iPenetration > 0)
+ iPenetration = 0;
+
+#ifndef CLIENT_DLL
+ // This just keeps track of sounds for AIs (it doesn't play anything).
+ CSoundEnt::InsertSound( SOUND_BULLET_IMPACT, tr.endpos, 400, 0.2f, this );
+#endif
+
+ int iDamageType = DMG_BULLET | DMG_NEVERGIB;
+
+ if( bDoEffects )
+ {
+ // See if the bullet ended up underwater + started out of the water
+ if ( enginetrace->GetPointContents( tr.endpos ) & (CONTENTS_WATER|CONTENTS_SLIME) )
+ {
+ trace_t waterTrace;
+ UTIL_TraceLine( vecSrc, tr.endpos, (MASK_SHOT|CONTENTS_WATER|CONTENTS_SLIME), this, COLLISION_GROUP_NONE, &waterTrace );
+
+ if( waterTrace.allsolid != 1 )
+ {
+ CEffectData data;
+ data.m_vOrigin = waterTrace.endpos;
+ data.m_vNormal = waterTrace.plane.normal;
+ data.m_flScale = random->RandomFloat( 8, 12 );
+
+ if ( waterTrace.contents & CONTENTS_SLIME )
+ {
+ data.m_fFlags |= FX_WATER_IN_SLIME;
+ }
+
+ DispatchEffect( "gunshotsplash", data );
+ }
+ }
+ else
+ {
+ //Do Regular hit effects
+
+ // Don't decal nodraw surfaces
+ if ( !( tr.surface.flags & (SURF_SKY|SURF_NODRAW|SURF_HINT|SURF_SKIP) ) )
+ {
+ CBaseEntity *pEntity = tr.m_pEnt;
+ if ( !( !friendlyfire.GetBool() && pEntity && pEntity->GetTeamNumber() == GetTeamNumber() ) )
+ {
+ UTIL_ImpactTrace( &tr, iDamageType );
+ }
+ }
+ }
+ } // bDoEffects
+
+ // add damage to entity that we hit
+
+#ifndef CLIENT_DLL
+ ClearMultiDamage();
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [pfreese] Check if enemy players were killed by this bullet, and if so,
+ // add them to the iPenetrationKills count
+ //=============================================================================
+
+ CBaseEntity *pEntity = tr.m_pEnt;
+
+ CTakeDamageInfo info( pevAttacker, pevAttacker, fCurrentDamage, iDamageType );
+ CalculateBulletDamageForce( &info, iBulletType, vecDir, tr.endpos );
+ pEntity->DispatchTraceAttack( info, vecDir, &tr );
+
+ bool bWasAlive = pEntity->IsAlive();
+
+ TraceAttackToTriggers( info, tr.startpos, tr.endpos, vecDir );
+
+ ApplyMultiDamage();
+
+ if (bWasAlive && !pEntity->IsAlive() && pEntity->IsPlayer() && pEntity->GetTeamNumber() != GetTeamNumber())
+ {
+ ++iPenetrationKills;
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+#endif
+
+ // check if bullet can penetrate another entity
+ if ( iPenetration == 0 && !hitGrate )
+ break; // no, stop
+
+ // If we hit a grate with iPenetration == 0, stop on the next thing we hit
+ if ( iPenetration < 0 )
+ break;
+
+ Vector penetrationEnd;
+
+ // try to penetrate object, maximum penetration is 128 inch
+ if ( !TraceToExit( tr.endpos, vecDir, penetrationEnd, 24, 128 ) )
+ break;
+
+ // find exact penetration exit
+ trace_t exitTr;
+ UTIL_TraceLine( penetrationEnd, tr.endpos, CS_MASK_SHOOT|CONTENTS_HITBOX, NULL, &exitTr );
+
+ if( exitTr.m_pEnt != tr.m_pEnt && exitTr.m_pEnt != NULL )
+ {
+ // something was blocking, trace again
+ UTIL_TraceLine( penetrationEnd, tr.endpos, CS_MASK_SHOOT|CONTENTS_HITBOX, exitTr.m_pEnt, COLLISION_GROUP_NONE, &exitTr );
+ }
+
+ // get material at exit point
+ pSurfaceData = physprops->GetSurfaceData( exitTr.surface.surfaceProps );
+ int iExitMaterial = pSurfaceData->game.material;
+
+ hitGrate = hitGrate && ( exitTr.contents & CONTENTS_GRATE );
+
+ // if enter & exit point is wood or metal we assume this is
+ // a hollow crate or barrel and give a penetration bonus
+ if ( iEnterMaterial == iExitMaterial )
+ {
+ if( iExitMaterial == CHAR_TEX_WOOD ||
+ iExitMaterial == CHAR_TEX_METAL )
+ {
+ flPenetrationModifier *= 2;
+ }
+ }
+
+ float flTraceDistance = VectorLength( exitTr.endpos - tr.endpos );
+
+ // check if bullet has enough power to penetrate this distance for this material
+ if ( flTraceDistance > ( flPenetrationPower * flPenetrationModifier ) )
+ break; // bullet hasn't enough power to penetrate this distance
+
+ // penetration was successful
+
+ // bullet did penetrate object, exit Decal
+ if ( bDoEffects )
+ {
+ UTIL_ImpactTrace( &exitTr, iDamageType );
+ }
+
+ //setup new start end parameters for successive trace
+
+ flPenetrationPower -= flTraceDistance / flPenetrationModifier;
+ flCurrentDistance += flTraceDistance;
+
+ // NDebugOverlay::Box( exitTr.endpos, Vector(-2,-2,-2), Vector(2,2,2), 0,255,0,127, 8 );
+
+ vecSrc = exitTr.endpos;
+ flDistance = (flDistance - flCurrentDistance) * 0.5;
+
+ // reduce damage power each time we hit something other than a grate
+ fCurrentDamage *= flDamageModifier;
+
+ // reduce penetration counter
+ iPenetration--;
+ }
+
+#ifndef CLIENT_DLL
+ //=============================================================================
+ // HPE_BEGIN:
+ // [pfreese] If we killed at least two enemies with a single bullet, award the
+ // TWO_WITH_ONE_SHOT achievement
+ //=============================================================================
+
+ if (iPenetrationKills >= 2)
+ {
+ AwardAchievement(CSKillTwoWithOneShot);
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+#endif
+}
+
+
+void CCSPlayer::UpdateStepSound( surfacedata_t *psurface, const Vector &vecOrigin, const Vector &vecVelocity )
+{
+ float speedSqr = vecVelocity.AsVector2D().LengthSqr();
+
+ // the fastest walk is 135 ( scout ), see CCSGameMovement::CheckParameters()
+ if ( speedSqr < 150.0 * 150.0 )
+ return; // player is not running, no footsteps
+
+ BaseClass::UpdateStepSound( psurface, vecOrigin, vecVelocity );
+}
+
+
+// GOOSEMAN : Kick the view..
+void CCSPlayer::KickBack( float up_base, float lateral_base, float up_modifier, float lateral_modifier, float up_max, float lateral_max, int direction_change )
+{
+ float flKickUp;
+ float flKickLateral;
+
+ if (m_iShotsFired == 1) // This is the first round fired
+ {
+ flKickUp = up_base;
+ flKickLateral = lateral_base;
+ }
+ else
+ {
+ flKickUp = up_base + m_iShotsFired*up_modifier;
+ flKickLateral = lateral_base + m_iShotsFired*lateral_modifier;
+ }
+
+
+ QAngle angle = GetPunchAngle();
+
+ angle.x -= flKickUp;
+ if ( angle.x < -1 * up_max )
+ angle.x = -1 * up_max;
+
+ if ( m_iDirection == 1 )
+ {
+ angle.y += flKickLateral;
+ if (angle.y > lateral_max)
+ angle.y = lateral_max;
+ }
+ else
+ {
+ angle.y -= flKickLateral;
+ if ( angle.y < -1 * lateral_max )
+ angle.y = -1 * lateral_max;
+ }
+
+ if ( !SharedRandomInt( "KickBack", 0, direction_change ) )
+ m_iDirection = 1 - m_iDirection;
+
+ SetPunchAngle( angle );
+}
+
+
+bool CCSPlayer::CanMove() const
+{
+ // When we're in intro camera mode, it's important to return false here
+ // so our physics object doesn't fall out of the world.
+ if ( GetMoveType() == MOVETYPE_NONE )
+ return false;
+
+ if ( IsObserver() )
+ return true; // observers can move all the time
+
+ bool bValidMoveState = (State_Get() == STATE_ACTIVE || State_Get() == STATE_OBSERVER_MODE);
+
+ if ( m_bIsDefusing || !bValidMoveState || CSGameRules()->IsFreezePeriod() )
+ {
+ return false;
+ }
+ else
+ {
+ // Can't move while planting C4.
+ CC4 *pC4 = dynamic_cast< CC4* >( GetActiveWeapon() );
+ if ( pC4 && pC4->m_bStartedArming )
+ return false;
+
+ return true;
+ }
+}
+
+
+void CCSPlayer::OnJump( float fImpulse )
+{
+ CWeaponCSBase* pActiveWeapon = GetActiveCSWeapon();
+ if ( pActiveWeapon != NULL )
+ pActiveWeapon->OnJump(fImpulse);
+}
+
+
+void CCSPlayer::OnLand( float fVelocity )
+{
+ CWeaponCSBase* pActiveWeapon = GetActiveCSWeapon();
+ if ( pActiveWeapon != NULL )
+ pActiveWeapon->OnLand(fVelocity);
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------
+/**
+* Track the last time we were on a ladder, along with the ladder's normal and where we
+* were grabbing it, so we don't reach behind us and grab it again as we are trying to
+* dismount.
+*/
+void CCSPlayer::SurpressLadderChecks( const Vector& pos, const Vector& normal )
+{
+ m_ladderSurpressionTimer.Start( 1.0f );
+ m_lastLadderPos = pos;
+ m_lastLadderNormal = normal;
+}
+
+
+//-------------------------------------------------------------------------------------------------------------------------------
+/**
+* Prevent us from re-grabbing the same ladder we were just on:
+* - if the timer is elapsed, let us grab again
+* - if the normal is different, let us grab
+* - if the 2D pos is very different, let us grab, since it's probably a different ladder
+*/
+bool CCSPlayer::CanGrabLadder( const Vector& pos, const Vector& normal )
+{
+ if ( m_ladderSurpressionTimer.GetRemainingTime() <= 0.0f )
+ {
+ return true;
+ }
+
+ const float MaxDist = 64.0f;
+ if ( pos.AsVector2D().DistToSqr( m_lastLadderPos.AsVector2D() ) < MaxDist * MaxDist )
+ {
+ return false;
+ }
+
+ if ( normal != m_lastLadderNormal )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+
+void CCSPlayer::SetAnimation( PLAYER_ANIM playerAnim )
+{
+ // In CS, its CPlayerAnimState object manages ALL the animation state.
+ return;
+}
+
+
+CWeaponCSBase* CCSPlayer::CSAnim_GetActiveWeapon()
+{
+ return GetActiveCSWeapon();
+}
+
+
+bool CCSPlayer::CSAnim_CanMove()
+{
+ return CanMove();
+}
+
+//--------------------------------------------------------------------------------------------------------------
+
+#define MATERIAL_NAME_LENGTH 16
+
+#ifdef GAME_DLL
+
+class CFootstepControl : public CBaseTrigger
+{
+public:
+ DECLARE_CLASS( CFootstepControl, CBaseTrigger );
+ DECLARE_DATADESC();
+ DECLARE_SERVERCLASS();
+
+ virtual int UpdateTransmitState( void );
+ virtual void Spawn( void );
+
+ CNetworkVar( string_t, m_source );
+ CNetworkVar( string_t, m_destination );
+};
+
+LINK_ENTITY_TO_CLASS( func_footstep_control, CFootstepControl );
+
+
+BEGIN_DATADESC( CFootstepControl )
+ DEFINE_KEYFIELD( m_source, FIELD_STRING, "Source" ),
+ DEFINE_KEYFIELD( m_destination, FIELD_STRING, "Destination" ),
+END_DATADESC()
+
+IMPLEMENT_SERVERCLASS_ST( CFootstepControl, DT_FootstepControl )
+ SendPropStringT( SENDINFO(m_source) ),
+ SendPropStringT( SENDINFO(m_destination) ),
+END_SEND_TABLE()
+
+int CFootstepControl::UpdateTransmitState( void )
+{
+ return SetTransmitState( FL_EDICT_ALWAYS );
+}
+
+void CFootstepControl::Spawn( void )
+{
+ InitTrigger();
+}
+
+#else
+
+//--------------------------------------------------------------------------------------------------------------
+
+class C_FootstepControl : public C_BaseEntity
+{
+public:
+ DECLARE_CLASS( C_FootstepControl, C_BaseEntity );
+ DECLARE_CLIENTCLASS();
+
+ C_FootstepControl( void );
+ ~C_FootstepControl();
+
+ char m_source[MATERIAL_NAME_LENGTH];
+ char m_destination[MATERIAL_NAME_LENGTH];
+};
+
+IMPLEMENT_CLIENTCLASS_DT(C_FootstepControl, DT_FootstepControl, CFootstepControl)
+ RecvPropString( RECVINFO(m_source) ),
+ RecvPropString( RECVINFO(m_destination) ),
+END_RECV_TABLE()
+
+CUtlVector< C_FootstepControl * > s_footstepControllers;
+
+C_FootstepControl::C_FootstepControl( void )
+{
+ s_footstepControllers.AddToTail( this );
+}
+
+C_FootstepControl::~C_FootstepControl()
+{
+ s_footstepControllers.FindAndRemove( this );
+}
+
+surfacedata_t * CCSPlayer::GetFootstepSurface( const Vector &origin, const char *surfaceName )
+{
+ for ( int i=0; i<s_footstepControllers.Count(); ++i )
+ {
+ C_FootstepControl *control = s_footstepControllers[i];
+
+ if ( FStrEq( control->m_source, surfaceName ) )
+ {
+ if ( control->CollisionProp()->IsPointInBounds( origin ) )
+ {
+ return physprops->GetSurfaceData( physprops->GetSurfaceIndex( control->m_destination ) );
+ }
+ }
+ }
+
+ return physprops->GetSurfaceData( physprops->GetSurfaceIndex( surfaceName ) );
+}
+
+#endif
+
+
diff --git a/game/shared/cstrike/cs_playeranimstate.cpp b/game/shared/cstrike/cs_playeranimstate.cpp
new file mode 100644
index 0000000..19c3a18
--- /dev/null
+++ b/game/shared/cstrike/cs_playeranimstate.cpp
@@ -0,0 +1,1057 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "cs_playeranimstate.h"
+#include "base_playeranimstate.h"
+#include "tier0/vprof.h"
+#include "animation.h"
+#include "weapon_csbase.h"
+#include "studio.h"
+#include "apparent_velocity_helper.h"
+#include "utldict.h"
+#include "weapon_basecsgrenade.h"
+#include "datacache/imdlcache.h"
+
+#ifdef CLIENT_DLL
+ #include "c_cs_player.h"
+ #include "bone_setup.h"
+ #include "interpolatedvar.h"
+ #include "c_cs_hostage.h"
+#else
+ #include "cs_player.h"
+ #include "cs_simple_hostage.h"
+ #include "cs_gamestats.h"
+#endif
+
+#define ANIM_TOPSPEED_WALK 100
+#define ANIM_TOPSPEED_RUN 250
+#define ANIM_TOPSPEED_RUN_CROUCH 85
+
+#define DEFAULT_IDLE_NAME "idle_upper_"
+#define DEFAULT_CROUCH_IDLE_NAME "crouch_idle_upper_"
+#define DEFAULT_CROUCH_WALK_NAME "crouch_walk_upper_"
+#define DEFAULT_WALK_NAME "walk_upper_"
+#define DEFAULT_RUN_NAME "run_upper_"
+
+#define DEFAULT_FIRE_IDLE_NAME "idle_shoot_"
+#define DEFAULT_FIRE_CROUCH_NAME "crouch_idle_shoot_"
+#define DEFAULT_FIRE_CROUCH_WALK_NAME "crouch_walk_shoot_"
+#define DEFAULT_FIRE_WALK_NAME "walk_shoot_"
+#define DEFAULT_FIRE_RUN_NAME "run_shoot_"
+
+
+#define FIRESEQUENCE_LAYER (AIMSEQUENCE_LAYER+NUM_AIMSEQUENCE_LAYERS+1)
+#define RELOADSEQUENCE_LAYER (FIRESEQUENCE_LAYER + 1)
+#define GRENADESEQUENCE_LAYER (RELOADSEQUENCE_LAYER + 1)
+#define NUM_LAYERS_WANTED (GRENADESEQUENCE_LAYER + 1)
+
+
+
+// ------------------------------------------------------------------------------------------------ //
+// CCSPlayerAnimState declaration.
+// ------------------------------------------------------------------------------------------------ //
+
+class CCSPlayerAnimState : public CBasePlayerAnimState, public ICSPlayerAnimState
+{
+public:
+ DECLARE_CLASS( CCSPlayerAnimState, CBasePlayerAnimState );
+ friend ICSPlayerAnimState* CreatePlayerAnimState( CBaseAnimatingOverlay *pEntity, ICSPlayerAnimStateHelpers *pHelpers, LegAnimType_t legAnimType, bool bUseAimSequences );
+
+ CCSPlayerAnimState();
+
+ virtual void DoAnimationEvent( PlayerAnimEvent_t event, int nData );
+ virtual bool IsThrowingGrenade();
+ virtual int CalcAimLayerSequence( float *flCycle, float *flAimSequenceWeight, bool bForceIdle );
+ virtual void ClearAnimationState();
+ virtual bool CanThePlayerMove();
+ virtual float GetCurrentMaxGroundSpeed();
+ virtual Activity CalcMainActivity();
+ virtual void DebugShowAnimState( int iStartLine );
+ virtual void ComputeSequences( CStudioHdr *pStudioHdr );
+ virtual void ClearAnimationLayers();
+ virtual int SelectWeightedSequence( Activity activity );
+
+ void InitCS( CBaseAnimatingOverlay *pPlayer, ICSPlayerAnimStateHelpers *pHelpers, LegAnimType_t legAnimType, bool bUseAimSequences );
+
+protected:
+
+ int CalcFireLayerSequence(PlayerAnimEvent_t event);
+ void ComputeFireSequence( CStudioHdr *pStudioHdr );
+
+ void ComputeReloadSequence( CStudioHdr *pStudioHdr );
+ int CalcReloadLayerSequence( PlayerAnimEvent_t event );
+
+ bool IsOuterGrenadePrimed();
+ void ComputeGrenadeSequence( CStudioHdr *pStudioHdr );
+ int CalcGrenadePrimeSequence();
+ int CalcGrenadeThrowSequence();
+ int GetOuterGrenadeThrowCounter();
+
+ const char* GetWeaponSuffix();
+ bool HandleJumping();
+
+ void UpdateLayerSequenceGeneric( CStudioHdr *pStudioHdr, int iLayer, bool &bEnabled, float &flCurCycle, int &iSequence, bool bWaitAtEnd );
+
+ virtual int CalcSequenceIndex( const char *pBaseName, ... );
+
+private:
+
+ // Current state variables.
+ bool m_bJumping; // Set on a jump event.
+ float m_flJumpStartTime;
+ bool m_bFirstJumpFrame;
+
+ // Aim sequence plays reload while this is on.
+ bool m_bReloading;
+ float m_flReloadCycle;
+ int m_iReloadSequence;
+ float m_flReloadHoldEndTime; // Intermediate shotgun reloads get held a fraction of a second
+
+ // This is set to true if ANY animation is being played in the fire layer.
+ bool m_bFiring; // If this is on, then it'll continue the fire animation in the fire layer
+ // until it completes.
+ int m_iFireSequence; // (For any sequences in the fire layer, including grenade throw).
+ float m_flFireCycle;
+ PlayerAnimEvent_t m_delayedFire; // if we fire while reloading, delay the fire by one frame so we can cancel the reload first
+
+ // These control grenade animations.
+ bool m_bThrowingGrenade;
+ bool m_bPrimingGrenade;
+ float m_flGrenadeCycle;
+ int m_iGrenadeSequence;
+ int m_iLastThrowGrenadeCounter; // used to detect when the guy threw the grenade.
+
+ CCSPlayer *m_pPlayer;
+
+ ICSPlayerAnimStateHelpers *m_pHelpers;
+
+ void CheckCachedSequenceValidity( void );
+
+ int m_sequenceCache[ ACT_CROUCHIDLE+1 ]; // Cache the first N sequences, since we don't have weights.
+ int m_cachedModelIndex; // Model index for which the sequence cache is valid.
+
+ CUtlDict<int,int> m_namedSequence; // Dictionary of sequences computed with CalcSequenceIndex. This is because LookupSequence is a performance hit - CS:S player models have 750+ sequences!
+};
+
+
+ICSPlayerAnimState* CreatePlayerAnimState( CBaseAnimatingOverlay *pEntity, ICSPlayerAnimStateHelpers *pHelpers, LegAnimType_t legAnimType, bool bUseAimSequences )
+{
+ CCSPlayerAnimState *pRet = new CCSPlayerAnimState;
+ pRet->InitCS( pEntity, pHelpers, legAnimType, bUseAimSequences );
+ return pRet;
+}
+
+
+
+
+//----------------------------------------------------------------------------------------------
+/**
+ * Hostage animation mechanism
+ */
+class CCSHostageAnimState : public CCSPlayerAnimState
+{
+public:
+ DECLARE_CLASS( CCSHostageAnimState, CCSPlayerAnimState );
+
+ CCSHostageAnimState();
+
+ virtual Activity CalcMainActivity();
+
+ // No need to cache sequences, and we *do* have multiple sequences per activity
+ virtual int SelectWeightedSequence( Activity activity ) { return GetOuter()->SelectWeightedSequence( activity ); }
+};
+
+
+//----------------------------------------------------------------------------------------------
+ICSPlayerAnimState* CreateHostageAnimState( CBaseAnimatingOverlay *pEntity, ICSPlayerAnimStateHelpers *pHelpers, LegAnimType_t legAnimType, bool bUseAimSequences )
+{
+ CCSHostageAnimState *anim = new CCSHostageAnimState;
+ anim->InitCS( pEntity, pHelpers, legAnimType, bUseAimSequences );
+ return anim;
+}
+
+
+//----------------------------------------------------------------------------------------------
+//----------------------------------------------------------------------------------------------
+CCSHostageAnimState::CCSHostageAnimState()
+{
+}
+
+
+//----------------------------------------------------------------------------------------------
+/**
+ * Set hostage animation state
+ */
+Activity CCSHostageAnimState::CalcMainActivity()
+{
+ float flOuterSpeed = GetOuterXYSpeed();
+
+ if ( HandleJumping() )
+ {
+ return ACT_HOP;
+ }
+ else
+ {
+ Assert( dynamic_cast<CHostage*>( m_pOuter ) );
+ CHostage *me = (CHostage*)m_pOuter;
+
+ // if we have no leader, hang out
+ Activity idealActivity = me->GetLeader() ? ACT_IDLE : ACT_BUSY_QUEUE;
+
+ if ( m_pOuter->GetFlags() & FL_DUCKING )
+ {
+ if ( flOuterSpeed > MOVING_MINIMUM_SPEED )
+ idealActivity = ACT_RUN_CROUCH;
+ else
+ idealActivity = ACT_COVER_LOW;
+ }
+ else
+ {
+ if ( flOuterSpeed > MOVING_MINIMUM_SPEED )
+ {
+ if ( flOuterSpeed > ARBITRARY_RUN_SPEED )
+ idealActivity = ACT_RUN;
+ else
+ idealActivity = ACT_WALK;
+ }
+ }
+
+ return idealActivity;
+ }
+}
+
+
+// ------------------------------------------------------------------------------------------------ //
+// CCSPlayerAnimState implementation.
+// ------------------------------------------------------------------------------------------------ //
+
+CCSPlayerAnimState::CCSPlayerAnimState()
+{
+ m_pOuter = NULL;
+
+ m_bJumping = false;
+ m_flJumpStartTime = 0.0f;
+ m_bFirstJumpFrame = false;
+
+ m_bReloading = false;
+ m_flReloadCycle = 0.0f;
+ m_iReloadSequence = -1;
+ m_flReloadHoldEndTime = 0.0f;
+
+ m_bFiring = false;
+ m_iFireSequence = -1;
+ m_flFireCycle = 0.0f;
+ m_delayedFire = PLAYERANIMEVENT_COUNT;
+
+ m_bThrowingGrenade = false;
+ m_bPrimingGrenade = false;
+ m_flGrenadeCycle = 0.0f;
+ m_iGrenadeSequence = -1;
+ m_iLastThrowGrenadeCounter = 0;
+ m_cachedModelIndex = -1;
+
+ m_pPlayer = NULL;
+
+ m_pHelpers = NULL;
+}
+
+
+void CCSPlayerAnimState::InitCS( CBaseAnimatingOverlay *pEntity, ICSPlayerAnimStateHelpers *pHelpers, LegAnimType_t legAnimType, bool bUseAimSequences )
+{
+ CModAnimConfig config;
+ config.m_flMaxBodyYawDegrees = 90;
+ config.m_LegAnimType = legAnimType;
+ config.m_bUseAimSequences = bUseAimSequences;
+
+ m_pPlayer = ToCSPlayer( pEntity );
+
+ m_pHelpers = pHelpers;
+
+ BaseClass::Init( pEntity, config );
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+void CCSPlayerAnimState::CheckCachedSequenceValidity( void )
+{
+ if ( m_cachedModelIndex != GetOuter()->GetModelIndex() )
+ {
+ m_namedSequence.RemoveAll();
+
+ m_cachedModelIndex = GetOuter()->GetModelIndex();
+ for ( int i=0; i<=ACT_CROUCHIDLE; ++i )
+ {
+ m_sequenceCache[i] = -1;
+ }
+
+ // precache the sequences we'll be using for movement
+ if ( m_cachedModelIndex > 0 )
+ {
+ m_sequenceCache[ACT_HOP - 1] = GetOuter()->SelectWeightedSequence( ACT_HOP );
+ m_sequenceCache[ACT_IDLE - 1] = GetOuter()->SelectWeightedSequence( ACT_IDLE );
+ m_sequenceCache[ACT_RUN_CROUCH - 1] = GetOuter()->SelectWeightedSequence( ACT_RUN_CROUCH );
+ m_sequenceCache[ACT_CROUCHIDLE - 1] = GetOuter()->SelectWeightedSequence( ACT_CROUCHIDLE );
+ m_sequenceCache[ACT_RUN - 1] = GetOuter()->SelectWeightedSequence( ACT_RUN );
+ m_sequenceCache[ACT_WALK - 1] = GetOuter()->SelectWeightedSequence( ACT_WALK );
+ m_sequenceCache[ACT_IDLE - 1] = GetOuter()->SelectWeightedSequence( ACT_IDLE );
+ }
+ }
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Cache the sequence numbers for the first ACT_HOP activities, since the CS player doesn't have multiple
+ * sequences per activity.
+ */
+int CCSPlayerAnimState::SelectWeightedSequence( Activity activity )
+{
+ VPROF( "CCSPlayerAnimState::ComputeMainSequence" );
+
+ if ( activity > ACT_CROUCHIDLE || activity < 1 )
+ {
+ return GetOuter()->SelectWeightedSequence( activity );
+ }
+
+ CheckCachedSequenceValidity();
+
+ int sequence = m_sequenceCache[ activity - 1 ];
+ if ( sequence < 0 )
+ {
+ // just in case, look up the sequence if we didn't precache it above
+ sequence = m_sequenceCache[ activity - 1 ] = GetOuter()->SelectWeightedSequence( activity );
+ }
+
+#if defined(CLIENT_DLL) && defined(_DEBUG)
+ int realSequence = GetOuter()->SelectWeightedSequence( activity );
+ Assert( realSequence == sequence );
+#endif
+
+ return sequence;
+}
+
+
+//--------------------------------------------------------------------------------------------------------------
+/**
+ * Try to look up named sequences in a CUtlDict cache before falling back to the normal LookupSequence. It's
+ * best to avoid the normal LookupSequence when your models have 750+ sequences...
+ */
+int CCSPlayerAnimState::CalcSequenceIndex( const char *pBaseName, ... )
+{
+ VPROF( "CCSPlayerAnimState::CalcSequenceIndex" );
+
+ CheckCachedSequenceValidity();
+
+ char szFullName[512];
+ va_list marker;
+ va_start( marker, pBaseName );
+ Q_vsnprintf( szFullName, sizeof( szFullName ), pBaseName, marker );
+ va_end( marker );
+
+ int iSequence = m_namedSequence.Find( szFullName );
+ if ( iSequence == m_namedSequence.InvalidIndex() )
+ {
+ iSequence = GetOuter()->LookupSequence( szFullName );
+ m_namedSequence.Insert( szFullName, iSequence );
+ }
+ else
+ {
+ iSequence = m_namedSequence[iSequence];
+ }
+
+#if defined(CLIENT_DLL) && defined(_DEBUG)
+ int realSequence = GetOuter()->LookupSequence( szFullName );
+ Assert( realSequence == iSequence );
+#endif
+
+ // Show warnings if we can't find anything here.
+ if ( iSequence == -1 )
+ {
+ static CUtlDict<int,int> dict;
+ if ( dict.Find( szFullName ) == -1 )
+ {
+ dict.Insert( szFullName, 0 );
+ Warning( "CalcSequenceIndex: can't find '%s'.\n", szFullName );
+ }
+
+ iSequence = 0;
+ }
+
+ return iSequence;
+}
+
+
+void CCSPlayerAnimState::ClearAnimationState()
+{
+ m_bJumping = false;
+ m_bFiring = false;
+ m_bReloading = false;
+ m_flReloadHoldEndTime = 0.0f;
+ m_bThrowingGrenade = m_bPrimingGrenade = false;
+ m_iLastThrowGrenadeCounter = GetOuterGrenadeThrowCounter();
+
+ BaseClass::ClearAnimationState();
+}
+
+
+void CCSPlayerAnimState::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
+{
+ Assert( event != PLAYERANIMEVENT_THROW_GRENADE );
+
+ MDLCACHE_CRITICAL_SECTION();
+ switch ( event )
+ {
+ case PLAYERANIMEVENT_FIRE_GUN_PRIMARY:
+ case PLAYERANIMEVENT_FIRE_GUN_SECONDARY:
+ // Regardless of what we're doing in the fire layer, restart it.
+ m_flFireCycle = 0;
+ m_iFireSequence = CalcFireLayerSequence( event );
+ m_bFiring = m_iFireSequence != -1;
+
+ // If we are interrupting a (shotgun) reload, cancel the reload, and fire next frame.
+ if ( m_bFiring && m_bReloading )
+ {
+ m_bReloading = false;
+ m_iReloadSequence = -1;
+
+ m_delayedFire = event;
+ m_bFiring = false;
+ m_iFireSequence = -1;
+
+ CAnimationLayer *pLayer = m_pOuter->GetAnimOverlay( RELOADSEQUENCE_LAYER );
+ if ( pLayer )
+ {
+ pLayer->m_flWeight = 0.0f;
+ pLayer->m_nOrder = 15;
+ }
+ }
+
+#ifdef CLIENT_DLL
+ if ( m_bFiring && !m_bReloading )
+ {
+ if ( m_pPlayer )
+ {
+ m_pPlayer->ProcessMuzzleFlashEvent();
+ }
+ }
+#endif
+ break;
+
+ case PLAYERANIMEVENT_JUMP:
+ // Play the jump animation.
+ m_bJumping = true;
+ m_bFirstJumpFrame = true;
+ m_flJumpStartTime = gpGlobals->curtime;
+ break;
+
+ case PLAYERANIMEVENT_RELOAD:
+ {
+ // ignore normal reload events for shotguns - they get sent to trigger sounds etc only
+ CWeaponCSBase *pWeapon = m_pHelpers->CSAnim_GetActiveWeapon();
+ if ( pWeapon && pWeapon->GetCSWpnData().m_WeaponType != WEAPONTYPE_SHOTGUN )
+ {
+ m_iReloadSequence = CalcReloadLayerSequence( event );
+ if ( m_iReloadSequence != -1 )
+ {
+ m_bReloading = true;
+ m_flReloadCycle = 0;
+ }
+ else
+ {
+ m_bReloading = false;
+ }
+ }
+ }
+ break;
+
+ case PLAYERANIMEVENT_RELOAD_START:
+ case PLAYERANIMEVENT_RELOAD_LOOP:
+ // Set the hold time for _start and _loop anims, then fall through to the _end case
+ m_flReloadHoldEndTime = gpGlobals->curtime + 0.75f;
+
+ case PLAYERANIMEVENT_RELOAD_END:
+ {
+ // ignore shotgun reload events for non-shotguns
+ CWeaponCSBase *pWeapon = m_pHelpers->CSAnim_GetActiveWeapon();
+ if ( pWeapon && pWeapon->GetCSWpnData().m_WeaponType != WEAPONTYPE_SHOTGUN )
+ {
+ m_flReloadHoldEndTime = 0.0f; // clear this out in case we set it in _START or _LOOP above
+ }
+ else
+ {
+ m_iReloadSequence = CalcReloadLayerSequence( event );
+ if ( m_iReloadSequence != -1 )
+ {
+ m_bReloading = true;
+ m_flReloadCycle = 0;
+ }
+ else
+ {
+ m_bReloading = false;
+ }
+ }
+ }
+ break;
+
+ case PLAYERANIMEVENT_CLEAR_FIRING:
+ {
+ m_iFireSequence = -1;
+ }
+ break;
+
+ default:
+ Assert( !"CCSPlayerAnimState::DoAnimationEvent" );
+ }
+}
+
+
+float g_flThrowGrenadeFraction = 0.25;
+bool CCSPlayerAnimState::IsThrowingGrenade()
+{
+ if ( m_bThrowingGrenade )
+ {
+ // An animation event would be more appropriate here.
+ return m_flGrenadeCycle < g_flThrowGrenadeFraction;
+ }
+ else
+ {
+ bool bThrowPending = (m_iLastThrowGrenadeCounter != GetOuterGrenadeThrowCounter());
+ return bThrowPending || IsOuterGrenadePrimed();
+ }
+}
+
+
+int CCSPlayerAnimState::CalcReloadLayerSequence( PlayerAnimEvent_t event )
+{
+ if ( m_delayedFire != PLAYERANIMEVENT_COUNT )
+ return -1;
+
+ const char *weaponSuffix = GetWeaponSuffix();
+ if ( !weaponSuffix )
+ return -1;
+
+ CWeaponCSBase *pWeapon = m_pHelpers->CSAnim_GetActiveWeapon();
+ if ( !pWeapon )
+ return -1;
+
+ const char *prefix = "";
+ switch ( GetCurrentMainSequenceActivity() )
+ {
+ case ACT_PLAYER_RUN_FIRE:
+ case ACT_RUN:
+ prefix = "run";
+ break;
+
+ case ACT_PLAYER_WALK_FIRE:
+ case ACT_WALK:
+ prefix = "walk";
+ break;
+
+ case ACT_PLAYER_CROUCH_FIRE:
+ case ACT_CROUCHIDLE:
+ prefix = "crouch_idle";
+ break;
+
+ case ACT_PLAYER_CROUCH_WALK_FIRE:
+ case ACT_RUN_CROUCH:
+ prefix = "crouch_walk";
+ break;
+
+ default:
+ case ACT_PLAYER_IDLE_FIRE:
+ prefix = "idle";
+ break;
+ }
+
+ const char *reloadSuffix = "";
+ switch ( event )
+ {
+ case PLAYERANIMEVENT_RELOAD_START:
+ reloadSuffix = "_start";
+ break;
+
+ case PLAYERANIMEVENT_RELOAD_LOOP:
+ reloadSuffix = "_loop";
+ break;
+
+ case PLAYERANIMEVENT_RELOAD_END:
+ reloadSuffix = "_end";
+ break;
+ }
+
+ // First, look for <prefix>_reload_<weapon name><_start|_loop|_end>.
+ char szName[512];
+ Q_snprintf( szName, sizeof( szName ), "%s_reload_%s%s", prefix, weaponSuffix, reloadSuffix );
+ int iReloadSequence = m_pOuter->LookupSequence( szName );
+ if ( iReloadSequence != -1 )
+ return iReloadSequence;
+
+ // Next, look for reload_<weapon name><_start|_loop|_end>.
+ Q_snprintf( szName, sizeof( szName ), "reload_%s%s", weaponSuffix, reloadSuffix );
+ iReloadSequence = m_pOuter->LookupSequence( szName );
+ if ( iReloadSequence != -1 )
+ return iReloadSequence;
+
+ // Ok, look for generic categories.. pistol, shotgun, rifle, etc.
+ if ( pWeapon->GetCSWpnData().m_WeaponType == WEAPONTYPE_PISTOL )
+ {
+ Q_snprintf( szName, sizeof( szName ), "reload_pistol" );
+ iReloadSequence = m_pOuter->LookupSequence( szName );
+ if ( iReloadSequence != -1 )
+ return iReloadSequence;
+ }
+
+ // Fall back to reload_m4.
+ iReloadSequence = CalcSequenceIndex( "reload_m4" );
+ if ( iReloadSequence > 0 )
+ return iReloadSequence;
+
+ return -1;
+}
+
+ void CCSPlayerAnimState::UpdateLayerSequenceGeneric( CStudioHdr *pStudioHdr, int iLayer, bool &bEnabled, float &flCurCycle, int &iSequence, bool bWaitAtEnd )
+{
+ if ( !bEnabled || iSequence < 0 )
+ return;
+
+ // Increment the fire sequence's cycle.
+ flCurCycle += m_pOuter->GetSequenceCycleRate( pStudioHdr, iSequence ) * gpGlobals->frametime;
+ if ( flCurCycle > 1 )
+ {
+ if ( bWaitAtEnd )
+ {
+ flCurCycle = 1;
+ }
+ else
+ {
+ // Not firing anymore.
+ bEnabled = false;
+ iSequence = 0;
+ return;
+ }
+ }
+
+ // Now dump the state into its animation layer.
+ CAnimationLayer *pLayer = m_pOuter->GetAnimOverlay( iLayer );
+
+ pLayer->m_flCycle = flCurCycle;
+ pLayer->m_nSequence = iSequence;
+
+ pLayer->m_flPlaybackRate = 1.0f;
+ pLayer->m_flWeight = 1.0f;
+ pLayer->m_nOrder = iLayer;
+#ifndef CLIENT_DLL
+ pLayer->m_fFlags |= ANIM_LAYER_ACTIVE;
+#endif
+}
+
+bool CCSPlayerAnimState::IsOuterGrenadePrimed()
+{
+ CBaseCombatCharacter *pChar = m_pOuter->MyCombatCharacterPointer();
+ if ( pChar )
+ {
+ CBaseCSGrenade *pGren = dynamic_cast<CBaseCSGrenade*>( pChar->GetActiveWeapon() );
+ return pGren && pGren->IsPinPulled();
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+void CCSPlayerAnimState::ComputeGrenadeSequence( CStudioHdr *pStudioHdr )
+{
+ VPROF( "CCSPlayerAnimState::ComputeGrenadeSequence" );
+
+ if ( m_bThrowingGrenade )
+ {
+ UpdateLayerSequenceGeneric( pStudioHdr, GRENADESEQUENCE_LAYER, m_bThrowingGrenade, m_flGrenadeCycle, m_iGrenadeSequence, false );
+ }
+ else
+ {
+ if ( m_pPlayer )
+ {
+ CBaseCombatWeapon *pWeapon = m_pPlayer->GetActiveWeapon();
+ CBaseCSGrenade *pGren = dynamic_cast<CBaseCSGrenade*>( pWeapon );
+ if ( !pGren )
+ {
+ // The player no longer has a grenade equipped. Bail.
+ m_iLastThrowGrenadeCounter = GetOuterGrenadeThrowCounter();
+ return;
+ }
+ }
+
+ // Priming the grenade isn't an event.. we just watch the player for it.
+ // Also play the prime animation first if he wants to throw the grenade.
+ bool bThrowPending = (m_iLastThrowGrenadeCounter != GetOuterGrenadeThrowCounter());
+ if ( IsOuterGrenadePrimed() || bThrowPending )
+ {
+ if ( !m_bPrimingGrenade )
+ {
+ // If this guy just popped into our PVS, and he's got his grenade primed, then
+ // let's assume that it's all the way primed rather than playing the prime
+ // animation from the start.
+ if ( TimeSinceLastAnimationStateClear() < 0.4f )
+ {
+ m_flGrenadeCycle = 1;
+ }
+ else
+ {
+ m_flGrenadeCycle = 0;
+ }
+
+ m_iGrenadeSequence = CalcGrenadePrimeSequence();
+ m_bPrimingGrenade = true;
+ }
+
+ UpdateLayerSequenceGeneric( pStudioHdr, GRENADESEQUENCE_LAYER, m_bPrimingGrenade, m_flGrenadeCycle, m_iGrenadeSequence, true );
+
+ // If we're waiting to throw and we're done playing the prime animation...
+ if ( bThrowPending && m_flGrenadeCycle == 1 )
+ {
+ m_iLastThrowGrenadeCounter = GetOuterGrenadeThrowCounter();
+
+ // Now play the throw animation.
+ m_iGrenadeSequence = CalcGrenadeThrowSequence();
+ if ( m_iGrenadeSequence != -1 )
+ {
+ // Configure to start playing
+ m_bThrowingGrenade = true;
+ m_bPrimingGrenade = false;
+ m_flGrenadeCycle = 0;
+ }
+ }
+ }
+ else
+ {
+ m_bPrimingGrenade = false;
+ }
+ }
+}
+
+
+int CCSPlayerAnimState::CalcGrenadePrimeSequence()
+{
+ return CalcSequenceIndex( "idle_shoot_gren1" );
+}
+
+
+int CCSPlayerAnimState::CalcGrenadeThrowSequence()
+{
+ return CalcSequenceIndex( "idle_shoot_gren2" );
+}
+
+
+int CCSPlayerAnimState::GetOuterGrenadeThrowCounter()
+{
+ if ( m_pPlayer )
+ return m_pPlayer->m_iThrowGrenadeCounter;
+ else
+ return 0;
+}
+
+
+void CCSPlayerAnimState::ComputeReloadSequence( CStudioHdr *pStudioHdr )
+{
+ VPROF( "CCSPlayerAnimState::ComputeReloadSequence" );
+ bool hold = m_flReloadHoldEndTime > gpGlobals->curtime;
+ UpdateLayerSequenceGeneric( pStudioHdr, RELOADSEQUENCE_LAYER, m_bReloading, m_flReloadCycle, m_iReloadSequence, hold );
+ if ( !m_bReloading )
+ {
+ m_flReloadHoldEndTime = 0.0f;
+ }
+}
+
+
+int CCSPlayerAnimState::CalcAimLayerSequence( float *flCycle, float *flAimSequenceWeight, bool bForceIdle )
+{
+ VPROF( "CCSPlayerAnimState::CalcAimLayerSequence" );
+
+ const char *pSuffix = GetWeaponSuffix();
+ if ( !pSuffix )
+ return 0;
+
+ if ( bForceIdle )
+ {
+ switch ( GetCurrentMainSequenceActivity() )
+ {
+ case ACT_CROUCHIDLE:
+ case ACT_RUN_CROUCH:
+ return CalcSequenceIndex( "%s%s", DEFAULT_CROUCH_IDLE_NAME, pSuffix );
+
+ default:
+ return CalcSequenceIndex( "%s%s", DEFAULT_IDLE_NAME, pSuffix );
+ }
+ }
+ else
+ {
+ switch ( GetCurrentMainSequenceActivity() )
+ {
+ case ACT_RUN:
+ return CalcSequenceIndex( "%s%s", DEFAULT_RUN_NAME, pSuffix );
+
+ case ACT_WALK:
+ case ACT_RUNTOIDLE:
+ case ACT_IDLETORUN:
+ return CalcSequenceIndex( "%s%s", DEFAULT_WALK_NAME, pSuffix );
+
+ case ACT_CROUCHIDLE:
+ return CalcSequenceIndex( "%s%s", DEFAULT_CROUCH_IDLE_NAME, pSuffix );
+
+ case ACT_RUN_CROUCH:
+ return CalcSequenceIndex( "%s%s", DEFAULT_CROUCH_WALK_NAME, pSuffix );
+
+ case ACT_IDLE:
+ default:
+ return CalcSequenceIndex( "%s%s", DEFAULT_IDLE_NAME, pSuffix );
+ }
+ }
+}
+
+
+const char* CCSPlayerAnimState::GetWeaponSuffix()
+{
+ VPROF( "CCSPlayerAnimState::GetWeaponSuffix" );
+
+ // Figure out the weapon suffix.
+ CWeaponCSBase *pWeapon = m_pHelpers->CSAnim_GetActiveWeapon();
+ if ( !pWeapon )
+ return 0;
+
+ const char *pSuffix = pWeapon->GetCSWpnData().m_szAnimExtension;
+
+#ifdef CS_SHIELD_ENABLED
+ if ( m_pOuter->HasShield() == true )
+ {
+ if ( m_pOuter->IsShieldDrawn() == true )
+ pSuffix = "shield";
+ else
+ pSuffix = "shield_undeployed";
+ }
+#endif
+
+ return pSuffix;
+}
+
+
+int CCSPlayerAnimState::CalcFireLayerSequence(PlayerAnimEvent_t event)
+{
+ // Figure out the weapon suffix.
+ CWeaponCSBase *pWeapon = m_pHelpers->CSAnim_GetActiveWeapon();
+ if ( !pWeapon )
+ return -1;
+
+ const char *pSuffix = GetWeaponSuffix();
+ if ( !pSuffix )
+ return -1;
+
+ char tempsuffix[32];
+ if ( pWeapon->GetWeaponID() == WEAPON_ELITE )
+ {
+ bool bPrimary = (event == PLAYERANIMEVENT_FIRE_GUN_PRIMARY);
+ Q_snprintf( tempsuffix, sizeof(tempsuffix), "%s_%c", pSuffix, bPrimary?'r':'l' );
+ pSuffix = tempsuffix;
+ }
+
+ // Grenades handle their fire events separately
+ if ( event == PLAYERANIMEVENT_THROW_GRENADE ||
+ pWeapon->GetWeaponID() == WEAPON_HEGRENADE ||
+ pWeapon->GetWeaponID() == WEAPON_SMOKEGRENADE ||
+ pWeapon->GetWeaponID() == WEAPON_FLASHBANG )
+ {
+ return -1;
+ }
+
+ switch ( GetCurrentMainSequenceActivity() )
+ {
+ case ACT_PLAYER_RUN_FIRE:
+ case ACT_RUN:
+ return CalcSequenceIndex( "%s%s", DEFAULT_FIRE_RUN_NAME, pSuffix );
+
+ case ACT_PLAYER_WALK_FIRE:
+ case ACT_WALK:
+ return CalcSequenceIndex( "%s%s", DEFAULT_FIRE_WALK_NAME, pSuffix );
+
+ case ACT_PLAYER_CROUCH_FIRE:
+ case ACT_CROUCHIDLE:
+ return CalcSequenceIndex( "%s%s", DEFAULT_FIRE_CROUCH_NAME, pSuffix );
+
+ case ACT_PLAYER_CROUCH_WALK_FIRE:
+ case ACT_RUN_CROUCH:
+ return CalcSequenceIndex( "%s%s", DEFAULT_FIRE_CROUCH_WALK_NAME, pSuffix );
+
+ default:
+ case ACT_PLAYER_IDLE_FIRE:
+ return CalcSequenceIndex( "%s%s", DEFAULT_FIRE_IDLE_NAME, pSuffix );
+ }
+}
+
+
+bool CCSPlayerAnimState::CanThePlayerMove()
+{
+ return m_pHelpers->CSAnim_CanMove();
+}
+
+
+float CCSPlayerAnimState::GetCurrentMaxGroundSpeed()
+{
+ Activity currentActivity = m_pOuter->GetSequenceActivity( m_pOuter->GetSequence() );
+ if ( currentActivity == ACT_WALK || currentActivity == ACT_IDLE )
+ return ANIM_TOPSPEED_WALK;
+ else if ( currentActivity == ACT_RUN )
+ {
+ if ( m_pPlayer )
+ {
+ CBaseCombatWeapon *activeWeapon = m_pPlayer->GetActiveWeapon();
+ if ( activeWeapon )
+ {
+ CWeaponCSBase *csWeapon = dynamic_cast< CWeaponCSBase * >( activeWeapon );
+ if ( csWeapon )
+ {
+ return csWeapon->GetMaxSpeed();
+ }
+ }
+ }
+ return ANIM_TOPSPEED_RUN;
+ }
+ else if ( currentActivity == ACT_RUN_CROUCH )
+ return ANIM_TOPSPEED_RUN_CROUCH;
+ else
+ return 0;
+}
+
+
+bool CCSPlayerAnimState::HandleJumping()
+{
+ if ( m_bJumping )
+ {
+ if ( m_bFirstJumpFrame )
+ {
+
+#if !defined(CLIENT_DLL)
+ //=============================================================================
+ // HPE_BEGIN:
+ // [dwenger] Needed for fun-fact implementation
+ //=============================================================================
+
+ CCS_GameStats.IncrementStat(m_pPlayer, CSSTAT_TOTAL_JUMPS, 1);
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+#endif
+
+ m_bFirstJumpFrame = false;
+ RestartMainSequence(); // Reset the animation.
+ }
+
+ // Don't check if he's on the ground for a sec.. sometimes the client still has the
+ // on-ground flag set right when the message comes in.
+ if ( gpGlobals->curtime - m_flJumpStartTime > 0.2f )
+ {
+ if ( m_pOuter->GetFlags() & FL_ONGROUND )
+ {
+ m_bJumping = false;
+ RestartMainSequence(); // Reset the animation.
+ }
+ }
+ }
+
+ // Are we still jumping? If so, keep playing the jump animation.
+ return m_bJumping;
+}
+
+
+Activity CCSPlayerAnimState::CalcMainActivity()
+{
+ float flOuterSpeed = GetOuterXYSpeed();
+
+ if ( HandleJumping() )
+ {
+ return ACT_HOP;
+ }
+ else
+ {
+ Activity idealActivity = ACT_IDLE;
+
+ if ( m_pOuter->GetFlags() & FL_ANIMDUCKING )
+ {
+ if ( flOuterSpeed > MOVING_MINIMUM_SPEED )
+ idealActivity = ACT_RUN_CROUCH;
+ else
+ idealActivity = ACT_CROUCHIDLE;
+ }
+ else
+ {
+ if ( flOuterSpeed > MOVING_MINIMUM_SPEED )
+ {
+ if ( flOuterSpeed > ARBITRARY_RUN_SPEED )
+ idealActivity = ACT_RUN;
+ else
+ idealActivity = ACT_WALK;
+ }
+ else
+ {
+ idealActivity = ACT_IDLE;
+ }
+ }
+
+ return idealActivity;
+ }
+}
+
+
+void CCSPlayerAnimState::DebugShowAnimState( int iStartLine )
+{
+ engine->Con_NPrintf( iStartLine++, "fire : %s, cycle: %.2f\n", m_bFiring ? GetSequenceName( m_pOuter->GetModelPtr(), m_iFireSequence ) : "[not firing]", m_flFireCycle );
+ engine->Con_NPrintf( iStartLine++, "reload: %s, cycle: %.2f\n", m_bReloading ? GetSequenceName( m_pOuter->GetModelPtr(), m_iReloadSequence ) : "[not reloading]", m_flReloadCycle );
+ BaseClass::DebugShowAnimState( iStartLine );
+}
+
+
+void CCSPlayerAnimState::ComputeSequences( CStudioHdr *pStudioHdr )
+{
+ BaseClass::ComputeSequences( pStudioHdr );
+
+ VPROF( "CCSPlayerAnimState::ComputeSequences" );
+
+ ComputeFireSequence( pStudioHdr );
+ ComputeReloadSequence( pStudioHdr );
+ ComputeGrenadeSequence( pStudioHdr );
+}
+
+
+void CCSPlayerAnimState::ClearAnimationLayers()
+{
+ if ( !m_pOuter )
+ return;
+
+ m_pOuter->SetNumAnimOverlays( NUM_LAYERS_WANTED );
+ for ( int i=0; i < m_pOuter->GetNumAnimOverlays(); i++ )
+ {
+ // Client obeys Order of CBaseAnimatingOverlay::MAX_OVERLAYS (15), but server trusts only the ANIM_LAYER_ACTIVE flag.
+ m_pOuter->GetAnimOverlay( i )->SetOrder( CBaseAnimatingOverlay::MAX_OVERLAYS );
+#ifndef CLIENT_DLL
+ m_pOuter->GetAnimOverlay( i )->m_fFlags = 0;
+#endif
+ }
+}
+
+
+void CCSPlayerAnimState::ComputeFireSequence( CStudioHdr *pStudioHdr )
+{
+ VPROF( "CCSPlayerAnimState::ComputeFireSequence" );
+
+ if ( m_delayedFire != PLAYERANIMEVENT_COUNT )
+ {
+ DoAnimationEvent( m_delayedFire, 0 );
+ m_delayedFire = PLAYERANIMEVENT_COUNT;
+ }
+
+ UpdateLayerSequenceGeneric( pStudioHdr, FIRESEQUENCE_LAYER, m_bFiring, m_flFireCycle, m_iFireSequence, false );
+}
diff --git a/game/shared/cstrike/cs_playeranimstate.h b/game/shared/cstrike/cs_playeranimstate.h
new file mode 100644
index 0000000..aad335c
--- /dev/null
+++ b/game/shared/cstrike/cs_playeranimstate.h
@@ -0,0 +1,82 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef TF_PLAYERANIMSTATE_H
+#define TF_PLAYERANIMSTATE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "convar.h"
+#include "iplayeranimstate.h"
+#include "base_playeranimstate.h"
+
+#ifdef CLIENT_DLL
+ class C_BaseAnimatingOverlay;
+ class C_WeaponCSBase;
+ // Avoid redef warnings
+ #undef CBaseAnimatingOverlay
+ #define CBaseAnimatingOverlay C_BaseAnimatingOverlay
+ #define CWeaponCSBase C_WeaponCSBase
+ #define CCSPlayer C_CSPlayer
+#else
+ class CBaseAnimatingOverlay;
+ class CWeaponCSBase;
+ class CCSPlayer;
+#endif
+
+
+// When moving this fast, he plays run anim.
+#define ARBITRARY_RUN_SPEED 175.0f
+
+
+enum PlayerAnimEvent_t
+{
+ PLAYERANIMEVENT_FIRE_GUN_PRIMARY=0,
+ PLAYERANIMEVENT_FIRE_GUN_SECONDARY,
+ PLAYERANIMEVENT_THROW_GRENADE,
+ PLAYERANIMEVENT_JUMP,
+ PLAYERANIMEVENT_RELOAD,
+ PLAYERANIMEVENT_RELOAD_START, ///< w_model partial reload for shotguns
+ PLAYERANIMEVENT_RELOAD_LOOP, ///< w_model partial reload for shotguns
+ PLAYERANIMEVENT_RELOAD_END, ///< w_model partial reload for shotguns
+ PLAYERANIMEVENT_CLEAR_FIRING, ///< clear animations on the firing layer
+
+ PLAYERANIMEVENT_COUNT
+};
+
+
+class ICSPlayerAnimState : virtual public IPlayerAnimState
+{
+public:
+ // This is called by both the client and the server in the same way to trigger events for
+ // players firing, jumping, throwing grenades, etc.
+ virtual void DoAnimationEvent( PlayerAnimEvent_t event, int nData = 0 ) = 0;
+
+ // Returns true if we're playing the grenade prime or throw animation.
+ virtual bool IsThrowingGrenade() = 0;
+};
+
+
+// This abstracts the differences between CS players and hostages.
+class ICSPlayerAnimStateHelpers
+{
+public:
+ virtual CWeaponCSBase* CSAnim_GetActiveWeapon() = 0;
+ virtual bool CSAnim_CanMove() = 0;
+};
+
+
+ICSPlayerAnimState* CreatePlayerAnimState( CBaseAnimatingOverlay *pEntity, ICSPlayerAnimStateHelpers *pHelpers, LegAnimType_t legAnimType, bool bUseAimSequences );
+ICSPlayerAnimState* CreateHostageAnimState( CBaseAnimatingOverlay *pEntity, ICSPlayerAnimStateHelpers *pHelpers, LegAnimType_t legAnimType, bool bUseAimSequences );
+
+// If this is set, then the game code needs to make sure to send player animation events
+// to the local player if he's the one being watched.
+extern ConVar cl_showanimstate;
+
+
+#endif // TF_PLAYERANIMSTATE_H
diff --git a/game/shared/cstrike/cs_shareddefs.cpp b/game/shared/cstrike/cs_shareddefs.cpp
new file mode 100644
index 0000000..89b8ae3
--- /dev/null
+++ b/game/shared/cstrike/cs_shareddefs.cpp
@@ -0,0 +1,67 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "cs_shareddefs.h"
+
+const float CS_PLAYER_SPEED_RUN = 260.0f;
+const float CS_PLAYER_SPEED_VIP = 227.0f;
+const float CS_PLAYER_SPEED_WALK = 100.0f;
+const float CS_PLAYER_SPEED_SHIELD = 160.0f;
+const float CS_PLAYER_SPEED_STOPPED = 1.0f;
+const float CS_PLAYER_SPEED_OBSERVER = 900.0f;
+
+const float CS_PLAYER_SPEED_DUCK_MODIFIER = 0.34f;
+const float CS_PLAYER_SPEED_WALK_MODIFIER = 0.52f;
+const float CS_PLAYER_SPEED_CLIMB_MODIFIER = 0.34f;
+
+
+CCSClassInfo g_ClassInfos[] =
+{
+ { "None" },
+
+ { "Phoenix Connection" },
+ { "L337 KREW" },
+ { "Arctic Avengers" },
+ { "Guerilla Warfare" },
+
+ { "Seal Team 6" },
+ { "GSG-9" },
+ { "SAS" },
+ { "GIGN" }
+};
+
+const CCSClassInfo* GetCSClassInfo( int i )
+{
+ Assert( i >= 0 && i < ARRAYSIZE( g_ClassInfos ) );
+ return &g_ClassInfos[i];
+}
+
+const char *pszWinPanelCategoryHeaders[] =
+{
+ "",
+ "#winpanel_topdamage",
+ "#winpanel_topheadshots",
+ "#winpanel_kills"
+};
+
+// Construct some arrays of player model strings, so we can statically initialize CUtlVectors for general usage
+const char *CTPlayerModelStrings[] =
+{
+ "models/player/ct_urban.mdl",
+ "models/player/ct_gsg9.mdl",
+ "models/player/ct_sas.mdl",
+ "models/player/ct_gign.mdl",
+};
+const char *TerroristPlayerModelStrings[] =
+{
+ "models/player/t_phoenix.mdl",
+ "models/player/t_leet.mdl",
+ "models/player/t_arctic.mdl",
+ "models/player/t_guerilla.mdl",
+};
+CUtlVectorInitialized< const char * > CTPlayerModels( CTPlayerModelStrings, ARRAYSIZE( CTPlayerModelStrings ) );
+CUtlVectorInitialized< const char * > TerroristPlayerModels( TerroristPlayerModelStrings, ARRAYSIZE( TerroristPlayerModelStrings ) );
diff --git a/game/shared/cstrike/cs_shareddefs.h b/game/shared/cstrike/cs_shareddefs.h
new file mode 100644
index 0000000..3e41431
--- /dev/null
+++ b/game/shared/cstrike/cs_shareddefs.h
@@ -0,0 +1,261 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Shared CS definitions.
+//
+//=============================================================================//
+
+#ifndef CS_SHAREDDEFS_H
+#define CS_SHAREDDEFS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+/*======================*/
+// Menu stuff //
+/*======================*/
+
+#include <game/client/iviewport.h>
+
+//=============================================================================
+// HPE_BEGIN:
+// Including Achievement ID Definitions
+//=============================================================================
+
+#include "cs_achievementdefs.h"
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+// CS-specific viewport panels
+#define PANEL_CLASS_CT "class_ct"
+#define PANEL_CLASS_TER "class_ter"
+
+// Buy sub menus
+#define MENU_PISTOL "menu_pistol"
+#define MENU_SHOTGUN "menu_shotgun"
+#define MENU_RIFLE "menu_rifle"
+#define MENU_SMG "menu_smg"
+#define MENU_MACHINEGUN "menu_mg"
+#define MENU_EQUIPMENT "menu_equip"
+
+
+#define MAX_HOSTAGES 12
+#define MAX_HOSTAGE_RESCUES 4
+
+
+
+
+#define CSTRIKE_DEFAULT_AVATAR "avatar_default_64"
+#define CSTRIKE_DEFAULT_T_AVATAR "avatar_default-t_64"
+#define CSTRIKE_DEFAULT_CT_AVATAR "avatar_default_64"
+
+extern const float CS_PLAYER_SPEED_RUN;
+extern const float CS_PLAYER_SPEED_VIP;
+extern const float CS_PLAYER_SPEED_WALK;
+extern const float CS_PLAYER_SPEED_SHIELD;
+extern const float CS_PLAYER_SPEED_STOPPED;
+extern const float CS_PLAYER_SPEED_OBSERVER;
+
+extern const float CS_PLAYER_SPEED_DUCK_MODIFIER;
+extern const float CS_PLAYER_SPEED_WALK_MODIFIER;
+extern const float CS_PLAYER_SPEED_CLIMB_MODIFIER;
+
+
+template< class T >
+class CUtlVectorInitialized : public CUtlVector< T >
+{
+public:
+ CUtlVectorInitialized( T* pMemory, int numElements ) : CUtlVector< T >( pMemory, numElements )
+ {
+ this->SetSize( numElements );
+ }
+};
+
+extern CUtlVectorInitialized< const char * > CTPlayerModels;
+extern CUtlVectorInitialized< const char * > TerroristPlayerModels;
+
+
+// These go in CCSPlayer::m_iAddonBits and get sent to the client so it can create
+// grenade models hanging off players.
+#define ADDON_FLASHBANG_1 0x001
+#define ADDON_FLASHBANG_2 0x002
+#define ADDON_HE_GRENADE 0x004
+#define ADDON_SMOKE_GRENADE 0x008
+#define ADDON_C4 0x010
+#define ADDON_DEFUSEKIT 0x020
+#define ADDON_PRIMARY 0x040
+#define ADDON_PISTOL 0x080
+#define ADDON_PISTOL2 0x100
+#define NUM_ADDON_BITS 9
+
+
+// Indices of each weapon slot.
+#define WEAPON_SLOT_RIFLE 0 // (primary slot)
+#define WEAPON_SLOT_PISTOL 1 // (secondary slot)
+#define WEAPON_SLOT_KNIFE 2
+#define WEAPON_SLOT_GRENADES 3
+#define WEAPON_SLOT_C4 4
+
+#define WEAPON_SLOT_FIRST 0
+#define WEAPON_SLOT_LAST 4
+
+
+// CS Team IDs.
+#define TEAM_TERRORIST 2
+#define TEAM_CT 3
+#define TEAM_MAXCOUNT 4 // update this if we ever add teams (unlikely)
+
+#define MAX_CLAN_TAG_LENGTH 16 // max for new tags is actually 12, this allows some backward compat.
+
+//=============================================================================
+// HPE_BEGIN:
+// [menglish] CS specific death animation time now that freeze cam is implemented
+// in order to linger on the players body less
+// [tj] The number of times you must kill a given player to be dominating them
+// [menglish] Flags to use upon player death
+//=============================================================================
+
+// [menglish] CS specific death animation time now that freeze cam is implemented
+// in order to linger on the players body less
+#define CS_DEATH_ANIMATION_TIME 0.8
+
+// [tj] The number of times you must kill a given player to be dominating them
+// Should always be more than 1
+#define CS_KILLS_FOR_DOMINATION 4
+
+#define CS_DEATH_DOMINATION 0x0001 // killer is dominating victim
+#define CS_DEATH_REVENGE 0x0002 // killer got revenge on victim
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+
+//--------------
+// CSPort Specific damage flags
+//--------------
+#define DMG_HEADSHOT (DMG_LASTGENERICFLAG<<1)
+
+
+// The various states the player can be in during the join game process.
+enum CSPlayerState
+{
+ // Happily running around in the game.
+ // You can't move though if CSGameRules()->IsFreezePeriod() returns true.
+ // This state can jump to a bunch of other states like STATE_PICKINGCLASS or STATE_DEATH_ANIM.
+ STATE_ACTIVE=0,
+
+ // This is the state you're in when you first enter the server.
+ // It's switching between intro cameras every few seconds, and there's a level info
+ // screen up.
+ STATE_WELCOME, // Show the level intro screen.
+
+ // During these states, you can either be a new player waiting to join, or
+ // you can be a live player in the game who wants to change teams.
+ // Either way, you can't move while choosing team or class (or while any menu is up).
+ STATE_PICKINGTEAM, // Choosing team.
+ STATE_PICKINGCLASS, // Choosing class.
+
+ STATE_DEATH_ANIM, // Playing death anim, waiting for that to finish.
+ STATE_DEATH_WAIT_FOR_KEY, // Done playing death anim. Waiting for keypress to go into observer mode.
+ STATE_OBSERVER_MODE, // Noclipping around, watching players, etc.
+ NUM_PLAYER_STATES
+};
+
+
+enum e_RoundEndReason
+{
+ Invalid_Round_End_Reason = -1,
+ Target_Bombed,
+ VIP_Escaped,
+ VIP_Assassinated,
+ Terrorists_Escaped,
+ CTs_PreventEscape,
+ Escaping_Terrorists_Neutralized,
+ Bomb_Defused,
+ CTs_Win,
+ Terrorists_Win,
+ Round_Draw,
+ All_Hostages_Rescued,
+ Target_Saved,
+ Hostages_Not_Rescued,
+ Terrorists_Not_Escaped,
+ VIP_Not_Escaped,
+ Game_Commencing,
+ RoundEndReason_Count
+};
+
+#define PUSHAWAY_THINK_INTERVAL (1.0f / 20.0f)
+
+enum
+{
+ CS_CLASS_NONE=0,
+
+ // Terrorist classes (keep in sync with FIRST_T_CLASS/LAST_T_CLASS).
+ CS_CLASS_PHOENIX_CONNNECTION,
+ CS_CLASS_L337_KREW,
+ CS_CLASS_ARCTIC_AVENGERS,
+ CS_CLASS_GUERILLA_WARFARE,
+
+ // CT classes (keep in sync with FIRST_CT_CLASS/LAST_CT_CLASS).
+ CS_CLASS_SEAL_TEAM_6,
+ CS_CLASS_GSG_9,
+ CS_CLASS_SAS,
+ CS_CLASS_GIGN,
+
+ CS_NUM_CLASSES
+};
+
+//=============================================================================
+// HPE_BEGIN:
+// [menglish] List of equipment dropped by players with freeze panel callouts
+//=============================================================================
+enum
+{
+ DROPPED_C4,
+ DROPPED_DEFUSE,
+ DROPPED_WEAPON,
+ DROPPED_GRENADE, // This could be an HE greande, flashbang, or smoke
+ DROPPED_COUNT
+};
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+
+//=============================================================================
+//
+// MVP reasons
+//
+enum CSMvpReason_t
+{
+ CSMVP_UNDEFINED = 0,
+ CSMVP_ELIMINATION,
+ CSMVP_BOMBPLANT,
+ CSMVP_BOMBDEFUSE,
+ CSMVP_HOSTAGERESCUE,
+};
+
+
+// Keep these in sync with CSClasses.
+#define FIRST_T_CLASS CS_CLASS_PHOENIX_CONNNECTION
+#define LAST_T_CLASS CS_CLASS_GUERILLA_WARFARE
+
+#define FIRST_CT_CLASS CS_CLASS_SEAL_TEAM_6
+#define LAST_CT_CLASS CS_CLASS_GIGN
+
+#define CS_MUZZLEFLASH_NONE -1
+#define CS_MUZZLEFLASH_NORM 0
+#define CS_MUZZLEFLASH_X 1
+
+class CCSClassInfo
+{
+public:
+ const char *m_pClassName;
+};
+
+const CCSClassInfo* GetCSClassInfo( int i );
+
+extern const char *pszWinPanelCategoryHeaders[];
+
+#endif // CS_SHAREDDEFS_H
diff --git a/game/shared/cstrike/cs_urlretrieveprices.cpp b/game/shared/cstrike/cs_urlretrieveprices.cpp
new file mode 100644
index 0000000..c094263
--- /dev/null
+++ b/game/shared/cstrike/cs_urlretrieveprices.cpp
@@ -0,0 +1,237 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+//#include "stdafx.h"
+#ifdef _WIN32
+#include "winlite.h"
+#include "winsock.h"
+#elif POSIX
+#define INVALID_SOCKET -1
+#define SOCKET_ERROR -1
+#define SOCKET int
+#define LPSOCKADDR struct sockaddr *
+#define SOCKADDR_IN struct sockaddr_in
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#define closesocket close
+#endif
+#include "tier1/strtools.h"
+#include "KeyValues.h"
+#include "utlbuffer.h"
+#include "tier1/checksum_crc.h"
+#include "tier1/convar.h"
+#include "cbase.h"
+#include "cs_gamestats.h"
+#include "cs_gamerules.h"
+#include "cs_urlretrieveprices.h"
+
+#if _DEBUG
+#define WEEKLY_PRICE_URL "http://gamestats/weeklyprices.dat"
+#else
+#define WEEKLY_PRICE_URL "http://www.steampowered.com/stats/csmarket/weeklyprices.dat"
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Purpose: request a URL from connection
+//-----------------------------------------------------------------------------
+bool SendHTTPRequest( const char *pchRequestURL, SOCKET socketHTML )
+{
+ char szHeader[ MED_BUFFER_SIZE ];
+ char szHostName[ SMALL_BUFFER_SIZE ];
+ ::gethostname( szHostName, sizeof(szHostName) );
+
+ Q_snprintf( szHeader, sizeof(szHeader), "GET %s HTTP/1.0\r\n" \
+ "Accept: */*\r\n" \
+ "Accept-Language: en-us\r\n" \
+ "User-Agent: Steam/3.0\r\n" \
+ "Host: %s\r\n" \
+ "\r\n",
+ pchRequestURL, szHostName );
+
+ return ::send( socketHTML, szHeader, Q_strlen(szHeader) + 1, 0 ) != SOCKET_ERROR ;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Given a previous HTTP request parse the response into a key values buffer
+//-----------------------------------------------------------------------------
+bool ParseHTTPResponse( SOCKET socketHTML, uint32 *unPageHash = NULL )
+{
+ char szHeaderBuf[ MED_BUFFER_SIZE ];
+ char szBodyBuf[ MED_BUFFER_SIZE ];
+
+ int dwRet = 0;
+ bool bFinishedHeaderRead = false;
+ int iRecvPosition = 0;
+ int cCharsInLine = 0;
+
+ // scan for the end of the header
+ while ( !bFinishedHeaderRead && iRecvPosition < sizeof(szHeaderBuf) )
+ {
+ dwRet = ::recv( socketHTML, &szHeaderBuf[ iRecvPosition ] , 1, 0);
+ if ( dwRet < 0 )
+ {
+ bFinishedHeaderRead = true;
+ }
+
+ switch( szHeaderBuf[ iRecvPosition ] )
+ {
+ case '\r':
+ break;
+ case '\n':
+ if ( cCharsInLine == 0 )
+ bFinishedHeaderRead = true;
+
+ cCharsInLine = 0;
+ break;
+ default:
+ cCharsInLine++;
+ break;
+ }
+
+ iRecvPosition++;
+ }
+
+ CUtlBuffer buf;
+ buf.SetBufferType( false, false );
+ while( 1 )
+ {
+ dwRet = ::recv( socketHTML, szBodyBuf, sizeof(szBodyBuf)-1, 0);
+ if ( dwRet <= 0 )
+ break;
+
+ buf.Put( szBodyBuf, sizeof(szBodyBuf)-1 );
+ }
+
+ weeklyprice_t weeklyprice;
+ Q_memset( &weeklyprice, 0, sizeof( weeklyprice_t) );
+
+ buf.Get( &weeklyprice, sizeof( weeklyprice_t ) );
+
+ if ( weeklyprice.iVersion != PRICE_BLOB_VERSION )
+ {
+ Msg( "Incorrect price blob version! Update your server!\n" );
+ return false;
+ }
+
+ CSGameRules()->AddPricesToTable( weeklyprice );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Given http url crack it into an address and the request part
+//-----------------------------------------------------------------------------
+bool ProcessURL( const char *pchURL, void *pSockAddrIn, char *pchRequest, int cchRequest )
+{
+ char rgchHost[ MAX_DNS_NAME ];
+ char rgchRequest[ MED_BUFFER_SIZE ];
+ uint16 iPort;
+
+ if ( Q_strnicmp( pchURL, "http://", 7 ) != 0 )
+ {
+ Assert( !"http protocol only supported" );
+ return false;
+ }
+
+ const char *pchColon = strchr( pchURL + 7, ':' );
+ if ( pchColon )
+ {
+ Q_strncpy( rgchHost, pchURL + 7, pchColon - ( pchURL + 7 ) + 1 );
+ const char *pchForwardSlash = strchr( pchColon + 1, '/' );
+ if ( !pchForwardSlash )
+ return false;
+ Q_strncpy( rgchRequest, pchColon + 1, pchForwardSlash - ( pchColon + 1 ) + 1 );
+ iPort = atoi( rgchRequest );
+ Q_strncpy( rgchRequest, pchForwardSlash, ( pchURL + Q_strlen(pchURL) ) - pchForwardSlash + 1 );
+ }
+ else
+ {
+ const char *pchForwardSlash = strchr( pchURL + 7, '/' );
+ if ( !pchForwardSlash )
+ return false;
+
+ Q_strncpy( rgchHost, pchURL + 7, pchForwardSlash - ( pchURL + 7 ) + 1 );
+ iPort = 80;
+ Q_strncpy( rgchRequest, pchForwardSlash, ( pchURL + Q_strlen(pchURL) ) - pchForwardSlash + 1 );
+ }
+
+ struct hostent *hp = NULL;
+ if ( inet_addr( rgchHost ) == INADDR_NONE )
+ {
+ hp = gethostbyname( rgchHost );
+ }
+ else
+ {
+ uint32 addr = inet_addr( rgchHost );
+ hp = gethostbyaddr( ( char* )&addr, sizeof( addr ), AF_INET );
+ }
+
+ if( hp == NULL )
+ {
+ return false;
+ }
+
+ sockaddr_in &sockAddrIn = *((sockaddr_in *)pSockAddrIn);
+ sockAddrIn.sin_addr.s_addr = *( ( unsigned long* )hp->h_addr );
+ sockAddrIn.sin_family = AF_INET;
+ sockAddrIn.sin_port = htons( iPort );
+
+ Q_strncpy( pchRequest, rgchRequest, cchRequest );
+ return true;
+}
+
+//networkstringtable
+
+bool BlackMarket_DownloadPrices( void )
+{
+ char szRequest[ MED_BUFFER_SIZE ];
+ sockaddr_in server;
+ bool bConnected = false;
+
+ if ( ProcessURL( WEEKLY_PRICE_URL, &server, szRequest, sizeof(szRequest) ) )
+ {
+ SOCKET socketHTML = ::socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
+ if ( socketHTML != INVALID_SOCKET)
+ {
+ int iRet = ::connect( socketHTML, (LPSOCKADDR)&server, sizeof(SOCKADDR_IN) );
+
+ if ( !iRet )
+ {
+ bConnected = true;
+ if ( SendHTTPRequest( szRequest, socketHTML ) )
+ {
+ uint32 unHash = 0;
+ bool bRet = ParseHTTPResponse( socketHTML, &unHash );
+
+ closesocket( socketHTML );
+
+ return bRet;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ closesocket( socketHTML );
+ return false;
+ }
+ }
+ }
+ else
+ {
+ return false;
+ }
+
+ return true;
+}
diff --git a/game/shared/cstrike/cs_urlretrieveprices.h b/game/shared/cstrike/cs_urlretrieveprices.h
new file mode 100644
index 0000000..e81ce45
--- /dev/null
+++ b/game/shared/cstrike/cs_urlretrieveprices.h
@@ -0,0 +1,29 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef URLRETRIEVETHREAD_H
+#define URLRETRIEVETHREAD_H
+
+#include "KeyValues.h"
+#include "cs_weapon_parse.h"
+
+bool BlackMarket_DownloadPrices( void );
+
+#define MED_BUFFER_SIZE 1024
+#define SMALL_BUFFER_SIZE 255
+#define MAX_DNS_NAME 255
+
+#define PRICE_BLOB_VERSION 1
+#define PRICE_BLOB_NAME "weeklyprices.dat"
+
+struct weeklyprice_t
+{
+ short iVersion;
+ short iPreviousPrice[WEAPON_MAX];
+ short iCurrentPrice[WEAPON_MAX];
+};
+
+#endif // URLRETRIEVETHREAD_H
diff --git a/game/shared/cstrike/cs_usermessages.cpp b/game/shared/cstrike/cs_usermessages.cpp
new file mode 100644
index 0000000..0c03b6d
--- /dev/null
+++ b/game/shared/cstrike/cs_usermessages.cpp
@@ -0,0 +1,80 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "usermessages.h"
+#include "shake.h"
+#include "voice_gamemgr.h"
+
+// NVNT include to register in haptic user messages
+#include "haptics/haptic_msgs.h"
+
+void RegisterUserMessages()
+{
+ usermessages->Register( "Geiger", 1 ); // geiger info data
+ usermessages->Register( "Train", 1 ); // train control data
+ usermessages->Register( "HudText", -1 );
+ usermessages->Register( "SayText", -1 );
+ usermessages->Register( "SayText2", -1 );
+ usermessages->Register( "TextMsg", -1 );
+ usermessages->Register( "HudMsg", -1 );
+ usermessages->Register( "ResetHUD", 1 ); // called every respawn
+ usermessages->Register( "GameTitle", 0 ); // show game title
+ usermessages->Register( "ItemPickup", -1 ); // for item history on screen
+ usermessages->Register( "ShowMenu", -1 ); // show hud menu
+ usermessages->Register( "Shake", 13 ); // shake view
+ usermessages->Register( "Fade", 10 ); // fade HUD in/out
+ usermessages->Register( "VGUIMenu", -1 ); // Show VGUI menu
+ usermessages->Register( "Rumble", 3 ); // Send a rumble to a controller
+ usermessages->Register( "CloseCaption", -1 ); // Show a caption (by string id number)(duration in 10th of a second)
+
+ usermessages->Register( "SendAudio", -1 ); // play radio command
+ usermessages->Register( "RawAudio", -1 ); // play a .wav as a radio command
+
+ usermessages->Register( "VoiceMask", VOICE_MAX_PLAYERS_DW*4 * 2 + 1 );
+ usermessages->Register( "RequestState", 0 );
+
+ usermessages->Register( "BarTime", -1 ); // For the C4 progress bar.
+ usermessages->Register( "Damage", -1 ); // for HUD damage indicators
+ usermessages->Register( "RadioText", -1 ); // for radio text display
+ usermessages->Register( "HintText", -1 ); // Displays hint text display
+ usermessages->Register( "KeyHintText", -1 ); // Displays hint text display
+
+ usermessages->Register( "ReloadEffect", 2 ); // a player reloading..
+ usermessages->Register( "PlayerAnimEvent", -1 ); // jumping, firing, reload, etc.
+
+ usermessages->Register( "AmmoDenied", 2 );
+ usermessages->Register( "UpdateRadar", -1 );
+ usermessages->Register( "KillCam", -1 );
+ usermessages->Register( "MarkAchievement", -1 );
+
+ // Voting
+ usermessages->Register( "CallVoteFailed", -1 );
+ usermessages->Register( "VoteStart", -1 );
+ usermessages->Register( "VotePass", -1 );
+ usermessages->Register( "VoteFailed", 2 );
+ usermessages->Register( "VoteSetup", -1 ); // Initiates client-side voting UI
+
+ // NVNT register haptic user messages
+ RegisterHapticMessages();
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [menglish] Registering PlayerStatsUpdate for Stats implementation
+ // [dwenger] AchievementEvent: Necessary for server-side achievement awarding
+ // [tj] Added support for absolute current match statistics updates
+ //=============================================================================
+
+ usermessages->Register( "PlayerStatsUpdate_DEPRECATED", -1 ); // Protocol changed, this message replaced below
+ usermessages->Register( "AchievementEvent", -1 );
+ usermessages->Register( "MatchEndConditions", -1 ); //The end conditions for the match. long frag limit, long max rounds, long rounds needed won, and long time
+ usermessages->Register( "MatchStatsUpdate", -1 );
+ usermessages->Register( "PlayerStatsUpdate", -1 ); //Processes stats update
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+}
diff --git a/game/shared/cstrike/cs_weapon_parse.cpp b/game/shared/cstrike/cs_weapon_parse.cpp
new file mode 100644
index 0000000..d23ea72
--- /dev/null
+++ b/game/shared/cstrike/cs_weapon_parse.cpp
@@ -0,0 +1,455 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include <KeyValues.h>
+#include "cs_weapon_parse.h"
+#include "cs_shareddefs.h"
+#include "weapon_csbase.h"
+#include "icvar.h"
+#include "cs_gamerules.h"
+#include "cs_blackmarket.h"
+
+
+//--------------------------------------------------------------------------------------------------------
+struct WeaponTypeInfo
+{
+ CSWeaponType type;
+ const char * name;
+};
+
+
+//--------------------------------------------------------------------------------------------------------
+WeaponTypeInfo s_weaponTypeInfo[] =
+{
+ { WEAPONTYPE_KNIFE, "Knife" },
+ { WEAPONTYPE_PISTOL, "Pistol" },
+ { WEAPONTYPE_SUBMACHINEGUN, "Submachine Gun" }, // First match is printable
+ { WEAPONTYPE_SUBMACHINEGUN, "submachinegun" },
+ { WEAPONTYPE_SUBMACHINEGUN, "smg" },
+ { WEAPONTYPE_RIFLE, "Rifle" },
+ { WEAPONTYPE_SHOTGUN, "Shotgun" },
+ { WEAPONTYPE_SNIPER_RIFLE, "Sniper Rifle" }, // First match is printable
+ { WEAPONTYPE_SNIPER_RIFLE, "SniperRifle" },
+ { WEAPONTYPE_MACHINEGUN, "Machine Gun" }, // First match is printable
+ { WEAPONTYPE_MACHINEGUN, "machinegun" },
+ { WEAPONTYPE_MACHINEGUN, "mg" },
+ { WEAPONTYPE_C4, "C4" },
+ { WEAPONTYPE_GRENADE, "Grenade" },
+};
+
+
+struct WeaponNameInfo
+{
+ CSWeaponID id;
+ const char *name;
+};
+
+WeaponNameInfo s_weaponNameInfo[] =
+{
+ { WEAPON_P228, "weapon_p228" },
+ { WEAPON_GLOCK, "weapon_glock" },
+ { WEAPON_SCOUT, "weapon_scout" },
+ { WEAPON_HEGRENADE, "weapon_hegrenade" },
+ { WEAPON_XM1014, "weapon_xm1014" },
+ { WEAPON_C4, "weapon_c4" },
+ { WEAPON_MAC10, "weapon_mac10" },
+ { WEAPON_AUG, "weapon_aug" },
+ { WEAPON_SMOKEGRENADE, "weapon_smokegrenade" },
+ { WEAPON_ELITE, "weapon_elite" },
+ { WEAPON_FIVESEVEN, "weapon_fiveseven" },
+ { WEAPON_UMP45, "weapon_ump45" },
+ { WEAPON_SG550, "weapon_sg550" },
+
+ { WEAPON_GALIL, "weapon_galil" },
+ { WEAPON_FAMAS, "weapon_famas" },
+ { WEAPON_USP, "weapon_usp" },
+ { WEAPON_AWP, "weapon_awp" },
+ { WEAPON_MP5NAVY, "weapon_mp5navy" },
+ { WEAPON_M249, "weapon_m249" },
+ { WEAPON_M3, "weapon_m3" },
+ { WEAPON_M4A1, "weapon_m4a1" },
+ { WEAPON_TMP, "weapon_tmp" },
+ { WEAPON_G3SG1, "weapon_g3sg1" },
+ { WEAPON_FLASHBANG, "weapon_flashbang" },
+ { WEAPON_DEAGLE, "weapon_deagle" },
+ { WEAPON_SG552, "weapon_sg552" },
+ { WEAPON_AK47, "weapon_ak47" },
+ { WEAPON_KNIFE, "weapon_knife" },
+ { WEAPON_P90, "weapon_p90" },
+
+ // not sure any of these are needed
+ { WEAPON_SHIELDGUN, "weapon_shieldgun" },
+ { WEAPON_KEVLAR, "weapon_kevlar" },
+ { WEAPON_ASSAULTSUIT, "weapon_assaultsuit" },
+ { WEAPON_NVG, "weapon_nvg" },
+
+ { WEAPON_NONE, "weapon_none" },
+};
+
+
+
+//--------------------------------------------------------------------------------------------------------------
+
+
+CCSWeaponInfo g_EquipmentInfo[MAX_EQUIPMENT];
+
+void PrepareEquipmentInfo( void )
+{
+ memset( g_EquipmentInfo, 0, ARRAYSIZE( g_EquipmentInfo ) );
+
+ g_EquipmentInfo[2].SetWeaponPrice( CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_KEVLAR ) );
+ g_EquipmentInfo[2].SetDefaultPrice( KEVLAR_PRICE );
+ g_EquipmentInfo[2].SetPreviousPrice( CSGameRules()->GetBlackMarketPreviousPriceForWeapon( WEAPON_KEVLAR ) );
+ g_EquipmentInfo[2].m_iTeam = TEAM_UNASSIGNED;
+ Q_strcpy( g_EquipmentInfo[2].szClassName, "weapon_vest" );
+
+#ifdef CLIENT_DLL
+ g_EquipmentInfo[2].iconActive = new CHudTexture;
+ g_EquipmentInfo[2].iconActive->cCharacterInFont = 't';
+#endif
+
+ g_EquipmentInfo[1].SetWeaponPrice( CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_ASSAULTSUIT ) );
+ g_EquipmentInfo[1].SetDefaultPrice( ASSAULTSUIT_PRICE );
+ g_EquipmentInfo[1].SetPreviousPrice( CSGameRules()->GetBlackMarketPreviousPriceForWeapon( WEAPON_ASSAULTSUIT ) );
+ g_EquipmentInfo[1].m_iTeam = TEAM_UNASSIGNED;
+ Q_strcpy( g_EquipmentInfo[1].szClassName, "weapon_vesthelm" );
+
+#ifdef CLIENT_DLL
+ g_EquipmentInfo[1].iconActive = new CHudTexture;
+ g_EquipmentInfo[1].iconActive->cCharacterInFont = 'u';
+#endif
+
+ g_EquipmentInfo[0].SetWeaponPrice( CSGameRules()->GetBlackMarketPriceForWeapon( WEAPON_NVG ) );
+ g_EquipmentInfo[0].SetPreviousPrice( CSGameRules()->GetBlackMarketPreviousPriceForWeapon( WEAPON_NVG ) );
+ g_EquipmentInfo[0].SetDefaultPrice( NVG_PRICE );
+ g_EquipmentInfo[0].m_iTeam = TEAM_UNASSIGNED;
+ Q_strcpy( g_EquipmentInfo[0].szClassName, "weapon_nvgs" );
+
+#ifdef CLIENT_DLL
+ g_EquipmentInfo[0].iconActive = new CHudTexture;
+ g_EquipmentInfo[0].iconActive->cCharacterInFont = 's';
+#endif
+
+}
+
+//--------------------------------------------------------------------------------------------------------------
+CCSWeaponInfo * GetWeaponInfo( CSWeaponID weaponID )
+{
+ if ( weaponID == WEAPON_NONE )
+ return NULL;
+
+ if ( weaponID >= WEAPON_KEVLAR )
+ {
+ int iIndex = (WEAPON_MAX - weaponID) - 1;
+
+ return &g_EquipmentInfo[iIndex];
+
+ }
+
+ const char *weaponName = WeaponIdAsString(weaponID);
+ WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( weaponName );
+ if ( hWpnInfo == GetInvalidWeaponInfoHandle() )
+ {
+ return NULL;
+ }
+
+ CCSWeaponInfo *pWeaponInfo = dynamic_cast< CCSWeaponInfo* >( GetFileWeaponInfoFromHandle( hWpnInfo ) );
+
+ return pWeaponInfo;
+}
+
+//--------------------------------------------------------------------------------------------------------
+const char* WeaponClassAsString( CSWeaponType weaponType )
+{
+ for ( int i = 0; i < ARRAYSIZE(s_weaponTypeInfo); ++i )
+ {
+ if ( s_weaponTypeInfo[i].type == weaponType )
+ {
+ return s_weaponTypeInfo[i].name;
+ }
+ }
+
+ return NULL;
+}
+
+
+//--------------------------------------------------------------------------------------------------------
+CSWeaponType WeaponClassFromString( const char* weaponType )
+{
+ for ( int i = 0; i < ARRAYSIZE(s_weaponTypeInfo); ++i )
+ {
+ if ( !Q_stricmp( s_weaponTypeInfo[i].name, weaponType ) )
+ {
+ return s_weaponTypeInfo[i].type;
+ }
+ }
+
+ return WEAPONTYPE_UNKNOWN;
+}
+
+
+//--------------------------------------------------------------------------------------------------------
+CSWeaponType WeaponClassFromWeaponID( CSWeaponID weaponID )
+{
+ const char *weaponStr = WeaponIDToAlias( weaponID );
+ const char *translatedAlias = GetTranslatedWeaponAlias( weaponStr );
+
+ char wpnName[128];
+ Q_snprintf( wpnName, sizeof( wpnName ), "weapon_%s", translatedAlias );
+ WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( wpnName );
+ if ( hWpnInfo != GetInvalidWeaponInfoHandle() )
+ {
+ CCSWeaponInfo *pWeaponInfo = dynamic_cast< CCSWeaponInfo* >( GetFileWeaponInfoFromHandle( hWpnInfo ) );
+ if ( pWeaponInfo )
+ {
+ return pWeaponInfo->m_WeaponType;
+ }
+ }
+
+ return WEAPONTYPE_UNKNOWN;
+}
+
+
+//--------------------------------------------------------------------------------------------------------
+const char * WeaponIdAsString( CSWeaponID weaponID )
+{
+ for ( int i = 0; i < ARRAYSIZE(s_weaponNameInfo); ++i )
+ {
+ if (s_weaponNameInfo[i].id == weaponID )
+ return s_weaponNameInfo[i].name;
+ }
+
+ return NULL;
+}
+
+
+//--------------------------------------------------------------------------------------------------------
+CSWeaponID WeaponIdFromString( const char *szWeaponName )
+{
+ for ( int i = 0; i < ARRAYSIZE(s_weaponNameInfo); ++i )
+ {
+ if ( Q_stricmp(s_weaponNameInfo[i].name, szWeaponName) == 0 )
+ return s_weaponNameInfo[i].id;
+ }
+
+ return WEAPON_NONE;
+}
+
+
+//--------------------------------------------------------------------------------------------------------
+void ParseVector( KeyValues *keyValues, const char *keyName, Vector& vec )
+{
+ vec.x = vec.y = vec.z = 0.0f;
+
+ if ( !keyValues || !keyName )
+ return;
+
+ const char *vecString = keyValues->GetString( keyName, "0 0 0" );
+ if ( vecString && *vecString )
+ {
+ float x = 0.0f, y = 0.0f, z = 0.0f;
+ if ( 3 == sscanf( vecString, "%f %f %f", &x, &y, &z ) )
+ {
+ vec.x = x;
+ vec.y = y;
+ vec.z = z;
+ }
+ }
+}
+
+
+FileWeaponInfo_t* CreateWeaponInfo()
+{
+ return new CCSWeaponInfo;
+}
+
+
+CCSWeaponInfo::CCSWeaponInfo()
+{
+ m_flMaxSpeed = 1; // This should always be set in the script.
+ m_szAddonModel[0] = 0;
+}
+
+int CCSWeaponInfo::GetWeaponPrice( void ) const
+{
+ return m_iWeaponPrice;
+}
+
+int CCSWeaponInfo::GetDefaultPrice( void )
+{
+ return m_iDefaultPrice;
+}
+
+int CCSWeaponInfo::GetPrevousPrice( void )
+{
+ return m_iPreviousPrice;
+}
+
+
+void CCSWeaponInfo::Parse( KeyValues *pKeyValuesData, const char *szWeaponName )
+{
+ BaseClass::Parse( pKeyValuesData, szWeaponName );
+
+ m_flMaxSpeed = (float)pKeyValuesData->GetInt( "MaxPlayerSpeed", 1 );
+
+ m_iDefaultPrice = m_iWeaponPrice = pKeyValuesData->GetInt( "WeaponPrice", -1 );
+ if ( m_iWeaponPrice == -1 )
+ {
+ // This weapon should have the price in its script.
+ Assert( false );
+ }
+
+ if ( CSGameRules()->IsBlackMarket() )
+ {
+ CSWeaponID iWeaponID = AliasToWeaponID( GetTranslatedWeaponAlias ( szWeaponName ) );
+
+ m_iDefaultPrice = m_iWeaponPrice;
+ m_iPreviousPrice = CSGameRules()->GetBlackMarketPreviousPriceForWeapon( iWeaponID );
+ m_iWeaponPrice = CSGameRules()->GetBlackMarketPriceForWeapon( iWeaponID );
+ }
+
+ m_flArmorRatio = pKeyValuesData->GetFloat( "WeaponArmorRatio", 1 );
+ m_iCrosshairMinDistance = pKeyValuesData->GetInt( "CrosshairMinDistance", 4 );
+ m_iCrosshairDeltaDistance = pKeyValuesData->GetInt( "CrosshairDeltaDistance", 3 );
+ m_bCanUseWithShield = !!pKeyValuesData->GetInt( "CanEquipWithShield", false );
+ m_flMuzzleScale = pKeyValuesData->GetFloat( "MuzzleFlashScale", 1 );
+
+ const char *pMuzzleFlashStyle = pKeyValuesData->GetString( "MuzzleFlashStyle", "CS_MUZZLEFLASH_NORM" );
+
+ if( pMuzzleFlashStyle )
+ {
+ if ( Q_stricmp( pMuzzleFlashStyle, "CS_MUZZLEFLASH_X" ) == 0 )
+ {
+ m_iMuzzleFlashStyle = CS_MUZZLEFLASH_X;
+ }
+ else if ( Q_stricmp( pMuzzleFlashStyle, "CS_MUZZLEFLASH_NONE" ) == 0 )
+ {
+ m_iMuzzleFlashStyle = CS_MUZZLEFLASH_NONE;
+ }
+ else
+ {
+ m_iMuzzleFlashStyle = CS_MUZZLEFLASH_NORM;
+ }
+ }
+ else
+ {
+ Assert( false );
+ }
+
+ m_iPenetration = pKeyValuesData->GetInt( "Penetration", 1 );
+ m_iDamage = pKeyValuesData->GetInt( "Damage", 42 ); // Douglas Adams 1952 - 2001
+ m_flRange = pKeyValuesData->GetFloat( "Range", 8192.0f );
+ m_flRangeModifier = pKeyValuesData->GetFloat( "RangeModifier", 0.98f );
+ m_iBullets = pKeyValuesData->GetInt( "Bullets", 1 );
+ m_flCycleTime = pKeyValuesData->GetFloat( "CycleTime", 0.15 );
+ m_bAccuracyQuadratic= pKeyValuesData->GetInt( "AccuracyQuadratic", 0 );
+ m_flAccuracyDivisor = pKeyValuesData->GetFloat( "AccuracyDivisor", -1 ); // -1 = off
+ m_flAccuracyOffset = pKeyValuesData->GetFloat( "AccuracyOffset", 0 );
+ m_flMaxInaccuracy = pKeyValuesData->GetFloat( "MaxInaccuracy", 0 );
+
+ // new accuracy model parameters
+ m_fSpread[0] = pKeyValuesData->GetFloat("Spread", 0.0f);
+ m_fInaccuracyCrouch[0] = pKeyValuesData->GetFloat("InaccuracyCrouch", 0.0f);
+ m_fInaccuracyStand[0] = pKeyValuesData->GetFloat("InaccuracyStand", 0.0f);
+ m_fInaccuracyJump[0] = pKeyValuesData->GetFloat("InaccuracyJump", 0.0f);
+ m_fInaccuracyLand[0] = pKeyValuesData->GetFloat("InaccuracyLand", 0.0f);
+ m_fInaccuracyLadder[0] = pKeyValuesData->GetFloat("InaccuracyLadder", 0.0f);
+ m_fInaccuracyImpulseFire[0] = pKeyValuesData->GetFloat("InaccuracyFire", 0.0f);
+ m_fInaccuracyMove[0] = pKeyValuesData->GetFloat("InaccuracyMove", 0.0f);
+
+ m_fSpread[1] = pKeyValuesData->GetFloat("SpreadAlt", 0.0f);
+ m_fInaccuracyCrouch[1] = pKeyValuesData->GetFloat("InaccuracyCrouchAlt", 0.0f);
+ m_fInaccuracyStand[1] = pKeyValuesData->GetFloat("InaccuracyStandAlt", 0.0f);
+ m_fInaccuracyJump[1] = pKeyValuesData->GetFloat("InaccuracyJumpAlt", 0.0f);
+ m_fInaccuracyLand[1] = pKeyValuesData->GetFloat("InaccuracyLandAlt", 0.0f);
+ m_fInaccuracyLadder[1] = pKeyValuesData->GetFloat("InaccuracyLadderAlt", 0.0f);
+ m_fInaccuracyImpulseFire[1] = pKeyValuesData->GetFloat("InaccuracyFireAlt", 0.0f);
+ m_fInaccuracyMove[1] = pKeyValuesData->GetFloat("InaccuracyMoveAlt", 0.0f);
+
+ m_fInaccuracyReload = pKeyValuesData->GetFloat("InaccuracyReload", 0.0f);
+ m_fInaccuracyAltSwitch = pKeyValuesData->GetFloat("InaccuracyAltSwitch", 0.0f);
+
+ m_fRecoveryTimeCrouch = pKeyValuesData->GetFloat("RecoveryTimeCrouch", 1.0f);
+ m_fRecoveryTimeStand = pKeyValuesData->GetFloat("RecoveryTimeStand", 1.0f);
+
+ m_flTimeToIdleAfterFire = pKeyValuesData->GetFloat( "TimeToIdle", 2 );
+ m_flIdleInterval = pKeyValuesData->GetFloat( "IdleInterval", 20 );
+
+ // Figure out what team can have this weapon.
+ m_iTeam = TEAM_UNASSIGNED;
+ const char *pTeam = pKeyValuesData->GetString( "Team", NULL );
+ if ( pTeam )
+ {
+ if ( Q_stricmp( pTeam, "CT" ) == 0 )
+ {
+ m_iTeam = TEAM_CT;
+ }
+ else if ( Q_stricmp( pTeam, "TERRORIST" ) == 0 )
+ {
+ m_iTeam = TEAM_TERRORIST;
+ }
+ else if ( Q_stricmp( pTeam, "ANY" ) == 0 )
+ {
+ m_iTeam = TEAM_UNASSIGNED;
+ }
+ else
+ {
+ Assert( false );
+ }
+ }
+ else
+ {
+ Assert( false );
+ }
+
+
+ const char *pWrongTeamMsg = pKeyValuesData->GetString( "WrongTeamMsg", "" );
+ Q_strncpy( m_WrongTeamMsg, pWrongTeamMsg, sizeof( m_WrongTeamMsg ) );
+
+ const char *pShieldViewModel = pKeyValuesData->GetString( "shieldviewmodel", "" );
+ Q_strncpy( m_szShieldViewModel, pShieldViewModel, sizeof( m_szShieldViewModel ) );
+
+ const char *pAnimEx = pKeyValuesData->GetString( "PlayerAnimationExtension", "m4" );
+ Q_strncpy( m_szAnimExtension, pAnimEx, sizeof( m_szAnimExtension ) );
+
+ // Default is 2000.
+ m_flBotAudibleRange = pKeyValuesData->GetFloat( "BotAudibleRange", 2000.0f );
+
+ const char *pTypeString = pKeyValuesData->GetString( "WeaponType", "" );
+ m_WeaponType = WeaponClassFromString(pTypeString);
+
+ m_bFullAuto = pKeyValuesData->GetBool("FullAuto");
+
+ // Read the addon model.
+ Q_strncpy( m_szAddonModel, pKeyValuesData->GetString( "AddonModel" ), sizeof( m_szAddonModel ) );
+
+ // Read the dropped model.
+ Q_strncpy( m_szDroppedModel, pKeyValuesData->GetString( "DroppedModel" ), sizeof( m_szDroppedModel ) );
+
+ // Read the silencer model.
+ Q_strncpy( m_szSilencerModel, pKeyValuesData->GetString( "SilencerModel" ), sizeof( m_szSilencerModel ) );
+
+#ifndef CLIENT_DLL
+ // Enforce consistency for the weapon here, since that way we don't need to save off the model bounds
+ // for all time.
+ // Moved to pure_server_minimal.txt
+// engine->ForceExactFile( UTIL_VarArgs("scripts/%s.ctx", szWeaponName ) );
+
+ // Model bounds are rounded to the nearest integer, then extended by 1
+ engine->ForceModelBounds( szWorldModel, Vector( -15, -12, -18 ), Vector( 44, 16, 19 ) );
+ if ( m_szAddonModel[0] )
+ {
+ engine->ForceModelBounds( m_szAddonModel, Vector( -5, -5, -6 ), Vector( 13, 5, 7 ) );
+ }
+ if ( m_szSilencerModel[0] )
+ {
+ engine->ForceModelBounds( m_szSilencerModel, Vector( -15, -12, -18 ), Vector( 44, 16, 19 ) );
+ }
+#endif // !CLIENT_DLL
+}
+
+
diff --git a/game/shared/cstrike/cs_weapon_parse.h b/game/shared/cstrike/cs_weapon_parse.h
new file mode 100644
index 0000000..33bf0e4
--- /dev/null
+++ b/game/shared/cstrike/cs_weapon_parse.h
@@ -0,0 +1,192 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef CS_WEAPON_PARSE_H
+#define CS_WEAPON_PARSE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "weapon_parse.h"
+#include "networkvar.h"
+
+
+//--------------------------------------------------------------------------------------------------------
+enum CSWeaponType
+{
+ WEAPONTYPE_KNIFE=0,
+ WEAPONTYPE_PISTOL,
+ WEAPONTYPE_SUBMACHINEGUN,
+ WEAPONTYPE_RIFLE,
+ WEAPONTYPE_SHOTGUN,
+ WEAPONTYPE_SNIPER_RIFLE,
+ WEAPONTYPE_MACHINEGUN,
+ WEAPONTYPE_C4,
+ WEAPONTYPE_GRENADE,
+ WEAPONTYPE_UNKNOWN
+
+};
+
+
+//--------------------------------------------------------------------------------------------------------
+enum CSWeaponID
+{
+ WEAPON_NONE = 0,
+
+ WEAPON_P228,
+ WEAPON_GLOCK,
+ WEAPON_SCOUT,
+ WEAPON_HEGRENADE,
+ WEAPON_XM1014,
+ WEAPON_C4,
+ WEAPON_MAC10,
+ WEAPON_AUG,
+ WEAPON_SMOKEGRENADE,
+ WEAPON_ELITE,
+ WEAPON_FIVESEVEN,
+ WEAPON_UMP45,
+ WEAPON_SG550,
+
+ WEAPON_GALIL,
+ WEAPON_FAMAS,
+ WEAPON_USP,
+ WEAPON_AWP,
+ WEAPON_MP5NAVY,
+ WEAPON_M249,
+ WEAPON_M3,
+ WEAPON_M4A1,
+ WEAPON_TMP,
+ WEAPON_G3SG1,
+ WEAPON_FLASHBANG,
+ WEAPON_DEAGLE,
+ WEAPON_SG552,
+ WEAPON_AK47,
+ WEAPON_KNIFE,
+ WEAPON_P90,
+
+ WEAPON_SHIELDGUN, // BOTPORT: Is this still needed?
+
+ WEAPON_KEVLAR,
+ WEAPON_ASSAULTSUIT,
+ WEAPON_NVG,
+
+ WEAPON_MAX, // number of weapons weapon index
+};
+
+#define MAX_EQUIPMENT (WEAPON_MAX - WEAPON_KEVLAR)
+
+void PrepareEquipmentInfo( void );
+
+//--------------------------------------------------------------------------------------------------------
+const char * WeaponClassAsString( CSWeaponType weaponType );
+
+//--------------------------------------------------------------------------------------------------------
+CSWeaponType WeaponClassFromString( const char* weaponType );
+
+//--------------------------------------------------------------------------------------------------------
+CSWeaponType WeaponClassFromWeaponID( CSWeaponID weaponID );
+
+//--------------------------------------------------------------------------------------------------------
+const char * WeaponIdAsString( CSWeaponID weaponID );
+
+//--------------------------------------------------------------------------------------------------------
+CSWeaponID WeaponIdFromString( const char *szWeaponName );
+
+
+//--------------------------------------------------------------------------------------------------------
+class CCSWeaponInfo : public FileWeaponInfo_t
+{
+public:
+ DECLARE_CLASS_GAMEROOT( CCSWeaponInfo, FileWeaponInfo_t );
+
+ CCSWeaponInfo();
+
+ virtual void Parse( ::KeyValues *pKeyValuesData, const char *szWeaponName );
+
+ int GetRealWeaponPrice( void ) { return m_iWeaponPrice; }
+
+
+public:
+
+ float m_flMaxSpeed; // How fast the player can run while this is his primary weapon.
+
+ CSWeaponType m_WeaponType;
+
+ bool m_bFullAuto; // is this a fully automatic weapon?
+
+ int m_iTeam; // Which team can have this weapon. TEAM_UNASSIGNED if both can have it.
+ float m_flBotAudibleRange; // How far away a bot can hear this weapon.
+ float m_flArmorRatio;
+
+ int m_iCrosshairMinDistance;
+ int m_iCrosshairDeltaDistance;
+
+ bool m_bCanUseWithShield;
+
+ char m_WrongTeamMsg[32]; // Reference to a string describing the error if someone tries to buy
+ // this weapon but they're on the wrong team to have it.
+ // Zero-length if no specific message for this weapon.
+
+ char m_szAnimExtension[16];
+ char m_szShieldViewModel[64];
+
+ char m_szAddonModel[MAX_WEAPON_STRING]; // If this is set, it is used as the addon model. Otherwise, szWorldModel is used.
+ char m_szDroppedModel[MAX_WEAPON_STRING]; // Alternate dropped model, if different from the szWorldModel the player holds
+ char m_szSilencerModel[MAX_WEAPON_STRING]; // Alternate model with silencer attached
+
+ int m_iMuzzleFlashStyle;
+ float m_flMuzzleScale;
+
+ // Parameters for FX_FireBullets:
+ int m_iPenetration;
+ int m_iDamage;
+ float m_flRange;
+ float m_flRangeModifier;
+ int m_iBullets;
+ float m_flCycleTime;
+
+ // Variables that control how fast the weapon's accuracy changes as it is fired.
+ bool m_bAccuracyQuadratic;
+ float m_flAccuracyDivisor;
+ float m_flAccuracyOffset;
+ float m_flMaxInaccuracy;
+
+ // variables for new accuracy model
+ float m_fSpread[2];
+ float m_fInaccuracyCrouch[2];
+ float m_fInaccuracyStand[2];
+ float m_fInaccuracyJump[2];
+ float m_fInaccuracyLand[2];
+ float m_fInaccuracyLadder[2];
+ float m_fInaccuracyImpulseFire[2];
+ float m_fInaccuracyMove[2];
+ float m_fRecoveryTimeStand;
+ float m_fRecoveryTimeCrouch;
+ float m_fInaccuracyReload;
+ float m_fInaccuracyAltSwitch;
+
+ // Delay until the next idle animation after shooting.
+ float m_flTimeToIdleAfterFire;
+ float m_flIdleInterval;
+
+ int GetWeaponPrice( void ) const;
+ int GetDefaultPrice( void );
+ int GetPrevousPrice( void );
+ void SetWeaponPrice( int iPrice ) { m_iWeaponPrice = iPrice; }
+ void SetDefaultPrice( int iPrice ) { m_iDefaultPrice = iPrice; }
+ void SetPreviousPrice( int iPrice ) { m_iPreviousPrice = iPrice; }
+
+private:
+
+ int m_iWeaponPrice;
+ int m_iDefaultPrice;
+ int m_iPreviousPrice;
+
+};
+
+
+#endif // CS_WEAPON_PARSE_H
diff --git a/game/shared/cstrike/flashbang_projectile.cpp b/game/shared/cstrike/flashbang_projectile.cpp
new file mode 100644
index 0000000..aeb9830
--- /dev/null
+++ b/game/shared/cstrike/flashbang_projectile.cpp
@@ -0,0 +1,316 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "flashbang_projectile.h"
+#include "shake.h"
+#include "engine/IEngineSound.h"
+#include "cs_player.h"
+#include "dlight.h"
+#include "KeyValues.h"
+#include "weapon_csbase.h"
+#include "collisionutils.h"
+#include "particle_smokegrenade.h"
+#include "smoke_fog_overlay_shared.h"
+
+#define GRENADE_MODEL "models/Weapons/w_eq_flashbang_thrown.mdl"
+
+
+LINK_ENTITY_TO_CLASS( flashbang_projectile, CFlashbangProjectile );
+PRECACHE_WEAPON_REGISTER( flashbang_projectile );
+
+float PercentageOfFlashForPlayer(CBaseEntity *player, Vector flashPos, CBaseEntity *pevInflictor)
+{
+ float retval = 0.0f;
+
+ trace_t tr;
+
+ Vector pos = player->EyePosition();
+ Vector vecRight, vecUp, vecForward;
+ AngleVectors( player->EyeAngles(), &vecForward );
+
+ QAngle tempAngle;
+ VectorAngles(player->EyePosition() - flashPos, tempAngle);
+ AngleVectors(tempAngle, NULL, &vecRight, &vecUp);
+
+ vecRight.NormalizeInPlace();
+ vecUp.NormalizeInPlace();
+
+ UTIL_TraceLine( flashPos, pos,
+ (CONTENTS_SOLID|CONTENTS_MOVEABLE|CONTENTS_DEBRIS|CONTENTS_MONSTER),
+ pevInflictor, COLLISION_GROUP_NONE, &tr );
+
+ if ((tr.fraction == 1.0) || (tr.m_pEnt == player))
+ {
+ retval = 1.0;
+ }
+ else
+ {
+ return 0.0;
+ }
+
+ CBaseEntity *pSGren;
+
+ for( pSGren = gEntList.FindEntityByClassname( NULL, "env_particlesmokegrenade" );
+ pSGren;
+ pSGren = gEntList.FindEntityByClassname( pSGren, "env_particlesmokegrenade" ) )
+ {
+ ParticleSmokeGrenade *pPSG =( ParticleSmokeGrenade* ) pSGren;
+
+ if ( gpGlobals->curtime > pPSG->m_flSpawnTime + pPSG->m_FadeStartTime ) // ignore the smoke grenade if it's fading.
+ continue;
+
+ float flHit1, flHit2;
+
+ float flInnerRadius = SMOKEGRENADE_PARTICLERADIUS;
+// float flOutterRadius = flInnerRadius + ( 0.5 * SMOKEPARTICLE_SIZE );
+
+ Vector vPos = pSGren->GetAbsOrigin();
+
+ /*debugoverlay->AddBoxOverlay( pSGren->GetAbsOrigin(), Vector( flInnerRadius, flInnerRadius, flInnerRadius ),
+ Vector( -flInnerRadius, -flInnerRadius, -flInnerRadius ), QAngle( 0, 0, 0 ), 0, 255, 0, 30, 10 );
+ debugoverlay->AddBoxOverlay( pSGren->GetAbsOrigin(), Vector( flOutterRadius, flOutterRadius, flOutterRadius ),
+ Vector( -flOutterRadius, -flOutterRadius, -flOutterRadius ), QAngle( 0, 0, 0 ), 255, 0, 0, 30, 10 ); */
+
+ if ( IntersectInfiniteRayWithSphere( pos, vecForward, vPos, flInnerRadius, &flHit1, &flHit2 ) )
+ {
+ retval *= 0.8;
+ }
+/* else if ( IntersectInfiniteRayWithSphere( pos, vecForward, vPos, flOutterRadius, &flHit1, &flHit2 ) )
+ {
+ retval *= 0.9;
+ }
+*/
+ }
+
+ return retval;
+
+}
+
+// --------------------------------------------------------------------------------------------------- //
+//
+// RadiusDamage - this entity is exploding, or otherwise needs to inflict damage upon entities within a certain range.
+//
+// only damage ents that can clearly be seen by the explosion!
+// --------------------------------------------------------------------------------------------------- //
+
+void RadiusFlash(
+ Vector vecSrc,
+ CBaseEntity *pevInflictor,
+ CBaseEntity *pevAttacker,
+ float flDamage,
+ int iClassIgnore,
+ int bitsDamageType )
+{
+ vecSrc.z += 1;// in case grenade is lying on the ground
+
+ if ( !pevAttacker )
+ pevAttacker = pevInflictor;
+
+ trace_t tr;
+ float flAdjustedDamage;
+ variant_t var;
+ Vector vecEyePos;
+ float fadeTime, fadeHold;
+ Vector vForward;
+ Vector vecLOS;
+ float flDot;
+
+ CBaseEntity *pEntity = NULL;
+ static float flRadius = 1500;
+ float falloff = flDamage / flRadius;
+
+ bool bInWater = (UTIL_PointContents( vecSrc ) == CONTENTS_WATER);
+
+ // iterate on all entities in the vicinity.
+ while ((pEntity = gEntList.FindEntityInSphere( pEntity, vecSrc, flRadius )) != NULL)
+ {
+ bool bPlayer = pEntity->IsPlayer();
+ bool bHostage = ( Q_stricmp( pEntity->GetClassname(), "hostage_entity" ) == 0 );
+
+ if( !bPlayer && !bHostage )
+ continue;
+
+ vecEyePos = pEntity->EyePosition();
+
+ // blasts don't travel into or out of water
+ if ( bInWater && pEntity->GetWaterLevel() == 0)
+ continue;
+ if (!bInWater && pEntity->GetWaterLevel() == 3)
+ continue;
+
+ float percentageOfFlash = PercentageOfFlashForPlayer(pEntity, vecSrc, pevInflictor);
+
+ if ( percentageOfFlash > 0.0 )
+ {
+ // decrease damage for an ent that's farther from the grenade
+ flAdjustedDamage = flDamage - ( vecSrc - pEntity->EyePosition() ).Length() * falloff;
+
+ if ( flAdjustedDamage > 0 )
+ {
+ // See if we were facing the flash
+ AngleVectors( pEntity->EyeAngles(), &vForward );
+
+ vecLOS = ( vecSrc - vecEyePos );
+
+ float flDistance = vecLOS.Length();
+
+ // Normalize both vectors so the dotproduct is in the range -1.0 <= x <= 1.0
+ vecLOS.NormalizeInPlace();
+
+ flDot = DotProduct (vecLOS, vForward);
+
+ float startingAlpha = 255;
+
+ // if target is facing the bomb, the effect lasts longer
+ if( flDot >= 0.5 )
+ {
+ // looking at the flashbang
+ fadeTime = flAdjustedDamage * 2.5f;
+ fadeHold = flAdjustedDamage * 1.25f;
+ }
+ else if( flDot >= -0.5 )
+ {
+ // looking to the side
+ fadeTime = flAdjustedDamage * 1.75f;
+ fadeHold = flAdjustedDamage * 0.8f;
+ }
+ else
+ {
+ // facing away
+ fadeTime = flAdjustedDamage * 1.0f;
+ fadeHold = flAdjustedDamage * 0.75f;
+ startingAlpha = 200;
+ }
+
+ fadeTime *= percentageOfFlash;
+ fadeHold *= percentageOfFlash;
+
+ if ( bPlayer )
+ {
+ // blind players and bots
+ CCSPlayer *player = static_cast< CCSPlayer * >( pEntity );
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Store who was responsible for the most recent flashbang blinding.
+ //=============================================================================
+
+ CCSPlayer *attacker = ToCSPlayer (pevAttacker);
+ if (attacker && player)
+ {
+ player->SetLastFlashbangAttacker(attacker);
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+
+
+ player->Blind( fadeHold, fadeTime, startingAlpha );
+
+ // deafen players and bots
+ player->Deafen( flDistance );
+ }
+ else if ( bHostage )
+ {
+ variant_t val;
+ val.SetFloat( fadeTime );
+ pEntity->AcceptInput( "flashbang", pevInflictor, pevAttacker, val, 0 );
+ }
+ }
+ }
+ }
+
+ CPVSFilter filter(vecSrc);
+ te->DynamicLight( filter, 0.0, &vecSrc, 255, 255, 255, 2, 400, 0.1, 768 );
+}
+
+// --------------------------------------------------------------------------------------------------- //
+// CFlashbangProjectile implementation.
+// --------------------------------------------------------------------------------------------------- //
+
+CFlashbangProjectile* CFlashbangProjectile::Create(
+ const Vector &position,
+ const QAngle &angles,
+ const Vector &velocity,
+ const AngularImpulse &angVelocity,
+ CBaseCombatCharacter *pOwner )
+{
+ CFlashbangProjectile *pGrenade = (CFlashbangProjectile*)CBaseEntity::Create( "flashbang_projectile", position, angles, pOwner );
+
+ // Set the timer for 1 second less than requested. We're going to issue a SOUND_DANGER
+ // one second before detonation.
+ pGrenade->SetAbsVelocity( velocity );
+ pGrenade->SetupInitialTransmittedGrenadeVelocity( velocity );
+ pGrenade->SetThrower( pOwner );
+ pGrenade->m_flDamage = 100;
+ pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
+
+ pGrenade->SetTouch( &CBaseGrenade::BounceTouch );
+
+ pGrenade->SetThink( &CBaseCSGrenadeProjectile::DangerSoundThink );
+ pGrenade->SetNextThink( gpGlobals->curtime );
+
+ pGrenade->SetDetonateTimerLength( 1.5 );
+
+ pGrenade->ApplyLocalAngularVelocityImpulse( angVelocity );
+
+ pGrenade->SetGravity( BaseClass::GetGrenadeGravity() );
+ pGrenade->SetFriction( BaseClass::GetGrenadeFriction() );
+ pGrenade->SetElasticity( BaseClass::GetGrenadeElasticity() );
+
+ pGrenade->m_pWeaponInfo = GetWeaponInfo( WEAPON_FLASHBANG );
+
+
+ return pGrenade;
+}
+
+void CFlashbangProjectile::Spawn()
+{
+ SetModel( GRENADE_MODEL );
+ BaseClass::Spawn();
+}
+
+void CFlashbangProjectile::Precache()
+{
+ PrecacheModel( GRENADE_MODEL );
+
+ PrecacheScriptSound( "Flashbang.Explode" );
+ PrecacheScriptSound( "Flashbang.Bounce" );
+
+ BaseClass::Precache();
+}
+
+void CFlashbangProjectile::Detonate()
+{
+ RadiusFlash ( GetAbsOrigin(), this, GetThrower(), 4, CLASS_NONE, DMG_BLAST );
+ EmitSound( "Flashbang.Explode" );
+
+ // tell the bots a flashbang grenade has exploded
+ CCSPlayer *player = ToCSPlayer(GetThrower());
+ if ( player )
+ {
+ IGameEvent * event = gameeventmanager->CreateEvent( "flashbang_detonate" );
+ if ( event )
+ {
+ event->SetInt( "userid", player->GetUserID() );
+ event->SetFloat( "x", GetAbsOrigin().x );
+ event->SetFloat( "y", GetAbsOrigin().y );
+ event->SetFloat( "z", GetAbsOrigin().z );
+ gameeventmanager->FireEvent( event );
+ }
+ }
+
+ UTIL_Remove( this );
+}
+
+//TODO: Let physics handle the sound!
+void CFlashbangProjectile::BounceSound( void )
+{
+ EmitSound( "Flashbang.Bounce" );
+}
diff --git a/game/shared/cstrike/flashbang_projectile.h b/game/shared/cstrike/flashbang_projectile.h
new file mode 100644
index 0000000..454ab1a
--- /dev/null
+++ b/game/shared/cstrike/flashbang_projectile.h
@@ -0,0 +1,39 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef HEGRENADE_PROJECTILE_H
+#define HEGRENADE_PROJECTILE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "basecsgrenade_projectile.h"
+
+
+class CFlashbangProjectile : public CBaseCSGrenadeProjectile
+{
+public:
+ DECLARE_CLASS( CFlashbangProjectile, CBaseCSGrenadeProjectile );
+
+// Overrides.
+public:
+ virtual void Spawn();
+ virtual void Precache();
+ virtual void BounceSound( void );
+ virtual void Detonate();
+
+// Grenade stuff.
+ static CFlashbangProjectile* Create(
+ const Vector &position,
+ const QAngle &angles,
+ const Vector &velocity,
+ const AngularImpulse &angVelocity,
+ CBaseCombatCharacter *pOwner );
+};
+
+
+#endif // HEGRENADE_PROJECTILE_H
diff --git a/game/shared/cstrike/fx_cs_shared.cpp b/game/shared/cstrike/fx_cs_shared.cpp
new file mode 100644
index 0000000..446d2b5
--- /dev/null
+++ b/game/shared/cstrike/fx_cs_shared.cpp
@@ -0,0 +1,345 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "fx_cs_shared.h"
+#include "weapon_csbase.h"
+
+#ifndef CLIENT_DLL
+ #include "ilagcompensationmanager.h"
+#endif
+
+ConVar weapon_accuracy_logging( "weapon_accuracy_logging", "0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY | FCVAR_ARCHIVE );
+
+#ifdef CLIENT_DLL
+
+#include "fx_impact.h"
+
+ // this is a cheap ripoff from CBaseCombatWeapon::WeaponSound():
+ void FX_WeaponSound(
+ int iPlayerIndex,
+ WeaponSound_t sound_type,
+ const Vector &vOrigin,
+ CCSWeaponInfo *pWeaponInfo, float flSoundTime )
+ {
+
+ // If we have some sounds from the weapon classname.txt file, play a random one of them
+ const char *shootsound = pWeaponInfo->aShootSounds[ sound_type ];
+ if ( !shootsound || !shootsound[0] )
+ return;
+
+ CBroadcastRecipientFilter filter; // this is client side only
+ if ( !te->CanPredict() )
+ return;
+
+ CBaseEntity::EmitSound( filter, iPlayerIndex, shootsound, &vOrigin, flSoundTime );
+ }
+
+ class CGroupedSound
+ {
+ public:
+ string_t m_SoundName;
+ Vector m_vPos;
+ };
+
+ CUtlVector<CGroupedSound> g_GroupedSounds;
+
+
+ // Called by the ImpactSound function.
+ void ShotgunImpactSoundGroup( const char *pSoundName, const Vector &vEndPos )
+ {
+ int i;
+ // Don't play the sound if it's too close to another impact sound.
+ for ( i=0; i < g_GroupedSounds.Count(); i++ )
+ {
+ CGroupedSound *pSound = &g_GroupedSounds[i];
+
+ if ( vEndPos.DistToSqr( pSound->m_vPos ) < 300*300 )
+ {
+ if ( Q_stricmp( pSound->m_SoundName, pSoundName ) == 0 )
+ return;
+ }
+ }
+
+ // Ok, play the sound and add it to the list.
+ CLocalPlayerFilter filter;
+ C_BaseEntity::EmitSound( filter, NULL, pSoundName, &vEndPos );
+
+ i = g_GroupedSounds.AddToTail();
+ g_GroupedSounds[i].m_SoundName = pSoundName;
+ g_GroupedSounds[i].m_vPos = vEndPos;
+ }
+
+
+ void StartGroupingSounds()
+ {
+ Assert( g_GroupedSounds.Count() == 0 );
+ SetImpactSoundRoute( ShotgunImpactSoundGroup );
+ }
+
+
+ void EndGroupingSounds()
+ {
+ g_GroupedSounds.Purge();
+ SetImpactSoundRoute( NULL );
+ }
+
+#else
+
+ #include "te_shotgun_shot.h"
+
+ // Server doesn't play sounds anyway.
+ void StartGroupingSounds() {}
+ void EndGroupingSounds() {}
+ void FX_WeaponSound ( int iPlayerIndex,
+ WeaponSound_t sound_type,
+ const Vector &vOrigin,
+ CCSWeaponInfo *pWeaponInfo, float flSoundTime ) {};
+
+#endif
+
+
+// This runs on both the client and the server.
+// On the server, it only does the damage calculations.
+// On the client, it does all the effects.
+void FX_FireBullets(
+ int iPlayerIndex,
+ const Vector &vOrigin,
+ const QAngle &vAngles,
+ int iWeaponID,
+ int iMode,
+ int iSeed,
+ float fInaccuracy,
+ float fSpread,
+ float flSoundTime
+ )
+{
+ bool bDoEffects = true;
+
+#ifdef CLIENT_DLL
+ C_CSPlayer *pPlayer = ToCSPlayer( ClientEntityList().GetBaseEntity( iPlayerIndex ) );
+#else
+ CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( iPlayerIndex) );
+#endif
+
+ const char * weaponAlias = WeaponIDToAlias( iWeaponID );
+
+ if ( !weaponAlias )
+ {
+ DevMsg("FX_FireBullets: weapon alias for ID %i not found\n", iWeaponID );
+ return;
+ }
+
+#if !defined(CLIENT_DLL)
+ if ( weapon_accuracy_logging.GetBool() )
+ {
+ char szFlags[256];
+
+ V_strcpy(szFlags, " ");
+
+// #if defined(CLIENT_DLL)
+// V_strcat(szFlags, "CLIENT ", sizeof(szFlags));
+// #else
+// V_strcat(szFlags, "SERVER ", sizeof(szFlags));
+// #endif
+//
+ if ( pPlayer->GetMoveType() == MOVETYPE_LADDER )
+ V_strcat(szFlags, "LADDER ", sizeof(szFlags));
+
+ if ( FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ V_strcat(szFlags, "GROUND ", sizeof(szFlags));
+
+ if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING) )
+ V_strcat(szFlags, "DUCKING ", sizeof(szFlags));
+
+ float fVelocity = pPlayer->GetAbsVelocity().Length2D();
+
+ Msg("FireBullets @ %10f [ %s ]: inaccuracy=%f spread=%f max dispersion=%f mode=%2i vel=%10f seed=%3i %s\n",
+ gpGlobals->curtime, weaponAlias, fInaccuracy, fSpread, fInaccuracy + fSpread, iMode, fVelocity, iSeed, szFlags);
+ }
+#endif
+
+ char wpnName[128];
+ Q_snprintf( wpnName, sizeof( wpnName ), "weapon_%s", weaponAlias );
+ WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( wpnName );
+
+ if ( hWpnInfo == GetInvalidWeaponInfoHandle() )
+ {
+ DevMsg("FX_FireBullets: LookupWeaponInfoSlot failed for weapon %s\n", wpnName );
+ return;
+ }
+
+ CCSWeaponInfo *pWeaponInfo = static_cast< CCSWeaponInfo* >( GetFileWeaponInfoFromHandle( hWpnInfo ) );
+
+ // Do the firing animation event.
+ if ( pPlayer && !pPlayer->IsDormant() )
+ {
+ if ( iMode == Primary_Mode )
+ pPlayer->GetPlayerAnimState()->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN_PRIMARY );
+ else
+ pPlayer->GetPlayerAnimState()->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN_SECONDARY );
+ }
+
+#ifndef CLIENT_DLL
+ // if this is server code, send the effect over to client as temp entity
+ // Dispatch one message for all the bullet impacts and sounds.
+ TE_FireBullets(
+ iPlayerIndex,
+ vOrigin,
+ vAngles,
+ iWeaponID,
+ iMode,
+ iSeed,
+ fInaccuracy,
+ fSpread
+ );
+
+
+ // Let the player remember the usercmd he fired a weapon on. Assists in making decisions about lag compensation.
+ pPlayer->NoteWeaponFired();
+
+ bDoEffects = false; // no effects on server
+#endif
+
+ iSeed++;
+
+ int iDamage = pWeaponInfo->m_iDamage;
+ float flRange = pWeaponInfo->m_flRange;
+ int iPenetration = pWeaponInfo->m_iPenetration;
+ float flRangeModifier = pWeaponInfo->m_flRangeModifier;
+ int iAmmoType = pWeaponInfo->iAmmoType;
+
+ WeaponSound_t sound_type = SINGLE;
+
+ // CS HACK, tweak some weapon values based on primary/secondary mode
+
+ if ( iWeaponID == WEAPON_GLOCK )
+ {
+ if ( iMode == Secondary_Mode )
+ {
+ iDamage = 18; // reduced power for burst shots
+ flRangeModifier = 0.9f;
+ }
+ }
+ else if ( iWeaponID == WEAPON_M4A1 )
+ {
+ if ( iMode == Secondary_Mode )
+ {
+ flRangeModifier = 0.95f; // slower bullets in silenced mode
+ sound_type = SPECIAL1;
+ }
+ }
+ else if ( iWeaponID == WEAPON_USP )
+ {
+ if ( iMode == Secondary_Mode )
+ {
+ iDamage = 30; // reduced damage in silenced mode
+ sound_type = SPECIAL1;
+ }
+ }
+
+ if ( bDoEffects)
+ {
+ FX_WeaponSound( iPlayerIndex, sound_type, vOrigin, pWeaponInfo, flSoundTime );
+ }
+
+
+ // Fire bullets, calculate impacts & effects
+
+ if ( !pPlayer )
+ return;
+
+ StartGroupingSounds();
+
+#ifdef GAME_DLL
+ pPlayer->StartNewBulletGroup();
+#endif
+
+#if !defined (CLIENT_DLL)
+ // Move other players back to history positions based on local player's lag
+ lagcompensation->StartLagCompensation( pPlayer, pPlayer->GetCurrentCommand() );
+#endif
+
+ RandomSeed( iSeed ); // init random system with this seed
+
+ // Get accuracy displacement
+ float fTheta0 = RandomFloat(0.0f, 2.0f * M_PI);
+ float fRadius0 = RandomFloat(0.0f, fInaccuracy);
+ float x0 = fRadius0 * cosf(fTheta0);
+ float y0 = fRadius0 * sinf(fTheta0);
+
+ const int kMaxBullets = 16;
+ float x1[kMaxBullets], y1[kMaxBullets];
+ Assert(pWeaponInfo->m_iBullets <= kMaxBullets);
+
+ // the RNG can be desynchronized by FireBullet(), so pre-generate all spread offsets
+ for ( int iBullet=0; iBullet < pWeaponInfo->m_iBullets; iBullet++ )
+ {
+ float fTheta1 = RandomFloat(0.0f, 2.0f * M_PI);
+ float fRadius1 = RandomFloat(0.0f, fSpread);
+ x1[iBullet] = fRadius1 * cosf(fTheta1);
+ y1[iBullet] = fRadius1 * sinf(fTheta1);
+ }
+
+ for ( int iBullet=0; iBullet < pWeaponInfo->m_iBullets; iBullet++ )
+ {
+ pPlayer->FireBullet(
+ vOrigin,
+ vAngles,
+ flRange,
+ iPenetration,
+ iAmmoType,
+ iDamage,
+ flRangeModifier,
+ pPlayer,
+ bDoEffects,
+ x0 + x1[iBullet], y0 + y1[iBullet] );
+ }
+
+#if !defined (CLIENT_DLL)
+ lagcompensation->FinishLagCompensation( pPlayer );
+#endif
+
+ EndGroupingSounds();
+}
+
+// This runs on both the client and the server.
+// On the server, it dispatches a TE_PlantBomb to visible clients.
+// On the client, it plays the planting animation.
+void FX_PlantBomb( int iPlayerIndex, const Vector &vOrigin, PlantBombOption_t option )
+{
+#ifdef CLIENT_DLL
+ C_CSPlayer *pPlayer = ToCSPlayer( ClientEntityList().GetBaseEntity( iPlayerIndex ) );
+#else
+ CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( iPlayerIndex) );
+#endif
+
+ // Do the firing animation event.
+ if ( pPlayer && !pPlayer->IsDormant() )
+ {
+ switch ( option )
+ {
+ case PLANTBOMB_PLANT:
+ {
+ pPlayer->GetPlayerAnimState()->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN_PRIMARY );
+ }
+ break;
+
+ case PLANTBOMB_ABORT:
+ {
+ pPlayer->GetPlayerAnimState()->DoAnimationEvent( PLAYERANIMEVENT_CLEAR_FIRING );
+ }
+ break;
+ }
+ }
+
+#ifndef CLIENT_DLL
+ // if this is server code, send the effect over to client as temp entity
+ // Dispatch one message for all the bullet impacts and sounds.
+ TE_PlantBomb( iPlayerIndex, vOrigin, option );
+#endif
+}
+
diff --git a/game/shared/cstrike/fx_cs_shared.h b/game/shared/cstrike/fx_cs_shared.h
new file mode 100644
index 0000000..19008e8
--- /dev/null
+++ b/game/shared/cstrike/fx_cs_shared.h
@@ -0,0 +1,47 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef FX_CS_SHARED_H
+#define FX_CS_SHARED_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#ifdef CLIENT_DLL
+ #include "c_cs_player.h"
+#else
+ #include "cs_player.h"
+#endif
+
+
+// This runs on both the client and the server.
+// On the server, it only does the damage calculations.
+// On the client, it does all the effects.
+void FX_FireBullets(
+ int iPlayer,
+ const Vector &vOrigin,
+ const QAngle &vAngles,
+ int iWeaponID,
+ int iMode,
+ int iSeed,
+ float fInaccuracy,
+ float fSpread,
+ float flSoundTime = 0.0f
+ );
+
+// This runs on both the client and the server.
+// On the server, it dispatches a TE_PlantBomb to visible clients.
+// On the client, it plays the planting animation.
+enum PlantBombOption_t
+{
+ PLANTBOMB_PLANT, // play the planting animation
+ PLANTBOMB_ABORT, // abort the planting animation
+ // NOTE: If you add additional items to this enum then m_option in CTEPlantBomb will need to have its SendPropInt setting changed to have more than one bit.
+};
+void FX_PlantBomb( int iPlayer, const Vector &vOrigin, PlantBombOption_t option );
+
+#endif // FX_CS_SHARED_H
diff --git a/game/shared/cstrike/hegrenade_projectile.cpp b/game/shared/cstrike/hegrenade_projectile.cpp
new file mode 100644
index 0000000..bd80239
--- /dev/null
+++ b/game/shared/cstrike/hegrenade_projectile.cpp
@@ -0,0 +1,94 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "hegrenade_projectile.h"
+#include "soundent.h"
+#include "cs_player.h"
+#include "KeyValues.h"
+#include "weapon_csbase.h"
+
+#define GRENADE_MODEL "models/Weapons/w_eq_fraggrenade_thrown.mdl"
+
+
+LINK_ENTITY_TO_CLASS( hegrenade_projectile, CHEGrenadeProjectile );
+PRECACHE_WEAPON_REGISTER( hegrenade_projectile );
+
+CHEGrenadeProjectile* CHEGrenadeProjectile::Create(
+ const Vector &position,
+ const QAngle &angles,
+ const Vector &velocity,
+ const AngularImpulse &angVelocity,
+ CBaseCombatCharacter *pOwner,
+ float timer )
+{
+ CHEGrenadeProjectile *pGrenade = (CHEGrenadeProjectile*)CBaseEntity::Create( "hegrenade_projectile", position, angles, pOwner );
+
+ // Set the timer for 1 second less than requested. We're going to issue a SOUND_DANGER
+ // one second before detonation.
+
+ pGrenade->SetDetonateTimerLength( 1.5 );
+ pGrenade->SetAbsVelocity( velocity );
+ pGrenade->SetupInitialTransmittedGrenadeVelocity( velocity );
+ pGrenade->SetThrower( pOwner );
+
+ pGrenade->SetGravity( BaseClass::GetGrenadeGravity() );
+ pGrenade->SetFriction( BaseClass::GetGrenadeFriction() );
+ pGrenade->SetElasticity( BaseClass::GetGrenadeElasticity() );
+
+ pGrenade->m_flDamage = 100;
+ pGrenade->m_DmgRadius = pGrenade->m_flDamage * 3.5f;
+ pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
+ pGrenade->ApplyLocalAngularVelocityImpulse( angVelocity );
+
+ // make NPCs afaid of it while in the air
+ pGrenade->SetThink( &CHEGrenadeProjectile::DangerSoundThink );
+ pGrenade->SetNextThink( gpGlobals->curtime );
+
+ pGrenade->m_pWeaponInfo = GetWeaponInfo( WEAPON_HEGRENADE );
+
+ return pGrenade;
+}
+
+void CHEGrenadeProjectile::Spawn()
+{
+ SetModel( GRENADE_MODEL );
+ BaseClass::Spawn();
+}
+
+void CHEGrenadeProjectile::Precache()
+{
+ PrecacheModel( GRENADE_MODEL );
+
+ PrecacheScriptSound( "HEGrenade.Bounce" );
+
+ BaseClass::Precache();
+}
+
+void CHEGrenadeProjectile::BounceSound( void )
+{
+ EmitSound( "HEGrenade.Bounce" );
+}
+
+void CHEGrenadeProjectile::Detonate()
+{
+ BaseClass::Detonate();
+
+ // tell the bots an HE grenade has exploded
+ CCSPlayer *player = ToCSPlayer(GetThrower());
+ if ( player )
+ {
+ IGameEvent * event = gameeventmanager->CreateEvent( "hegrenade_detonate" );
+ if ( event )
+ {
+ event->SetInt( "userid", player->GetUserID() );
+ event->SetFloat( "x", GetAbsOrigin().x );
+ event->SetFloat( "y", GetAbsOrigin().y );
+ event->SetFloat( "z", GetAbsOrigin().z );
+ gameeventmanager->FireEvent( event );
+ }
+ }
+}
diff --git a/game/shared/cstrike/hegrenade_projectile.h b/game/shared/cstrike/hegrenade_projectile.h
new file mode 100644
index 0000000..50df243
--- /dev/null
+++ b/game/shared/cstrike/hegrenade_projectile.h
@@ -0,0 +1,46 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef HEGRENADE_PROJECTILE_H
+#define HEGRENADE_PROJECTILE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "basecsgrenade_projectile.h"
+
+class CHEGrenadeProjectile : public CBaseCSGrenadeProjectile
+{
+public:
+ DECLARE_CLASS( CHEGrenadeProjectile, CBaseCSGrenadeProjectile );
+
+
+// Overrides.
+public:
+ virtual void Spawn();
+ virtual void Precache();
+ virtual void BounceSound( void );
+ virtual void Detonate();
+
+// Grenade stuff.
+public:
+
+ static CHEGrenadeProjectile* Create(
+ const Vector &position,
+ const QAngle &angles,
+ const Vector &velocity,
+ const AngularImpulse &angVelocity,
+ CBaseCombatCharacter *pOwner,
+ float timer );
+
+ void SetTimer( float timer );
+
+private:
+ float m_flDetonateTime;
+};
+
+
+#endif // HEGRENADE_PROJECTILE_H
diff --git a/game/shared/cstrike/weapon_ak47.cpp b/game/shared/cstrike/weapon_ak47.cpp
new file mode 100644
index 0000000..79b54b6
--- /dev/null
+++ b/game/shared/cstrike/weapon_ak47.cpp
@@ -0,0 +1,103 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbasegun.h"
+
+#if defined( CLIENT_DLL )
+
+ #define CAK47 C_AK47
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+
+#endif
+
+
+class CAK47 : public CWeaponCSBaseGun
+{
+public:
+ DECLARE_CLASS( CAK47, CWeaponCSBaseGun );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CAK47();
+
+ virtual void PrimaryAttack();
+
+ virtual float GetInaccuracy() const;
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_AK47; }
+
+private:
+ CAK47( const CAK47 & );
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( AK47, DT_WeaponAK47 )
+
+BEGIN_NETWORK_TABLE( CAK47, DT_WeaponAK47 )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CAK47 )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_ak47, CAK47 );
+PRECACHE_WEAPON_REGISTER( weapon_ak47 );
+
+// ---------------------------------------------------------------------------- //
+// CAK47 implementation.
+// ---------------------------------------------------------------------------- //
+
+CAK47::CAK47()
+{
+}
+
+
+float CAK47::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ return 0.04f + 0.4f * m_flAccuracy;
+ else if (pPlayer->GetAbsVelocity().Length2D() > 140)
+ return 0.04f + 0.07f * m_flAccuracy;
+ else
+ return 0.0275f * m_flAccuracy;
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+
+void CAK47::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( !CSBaseGunFire( GetCSWpnData().m_flCycleTime, Primary_Mode ) )
+ return;
+
+ // CSBaseGunFire can kill us, forcing us to drop our weapon, if we shoot something that explodes
+ pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if (pPlayer->GetAbsVelocity().Length2D() > 5 )
+ pPlayer->KickBack ( 1.5, 0.45, 0.225, 0.05, 6.5, 2.5, 7 );
+ else if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ pPlayer->KickBack ( 2, 1.0, 0.5, 0.35, 9, 6, 5 );
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ pPlayer->KickBack ( 0.9, 0.35, 0.15, 0.025, 5.5, 1.5, 9 );
+ else
+ pPlayer->KickBack ( 1, 0.375, 0.175, 0.0375, 5.75, 1.75, 8 );
+}
diff --git a/game/shared/cstrike/weapon_aug.cpp b/game/shared/cstrike/weapon_aug.cpp
new file mode 100644
index 0000000..319a9fc
--- /dev/null
+++ b/game/shared/cstrike/weapon_aug.cpp
@@ -0,0 +1,160 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbasegun.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponAug C_WeaponAug
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+
+#endif
+
+
+class CWeaponAug : public CWeaponCSBaseGun
+{
+public:
+ DECLARE_CLASS( CWeaponAug, CWeaponCSBaseGun );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponAug();
+
+ virtual void SecondaryAttack();
+ virtual void PrimaryAttack();
+
+ virtual float GetInaccuracy() const;
+ virtual bool Reload();
+ virtual bool Deploy();
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_AUG; }
+
+#ifdef CLIENT_DLL
+ virtual bool HideViewModelWhenZoomed( void ) { return false; }
+#endif
+
+private:
+
+ void AUGFire( float flSpread, bool bZoomed );
+
+ CWeaponAug( const CWeaponAug & );
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponAug, DT_WeaponAug )
+
+BEGIN_NETWORK_TABLE( CWeaponAug, DT_WeaponAug )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponAug )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_aug, CWeaponAug );
+PRECACHE_WEAPON_REGISTER( weapon_aug );
+
+
+
+CWeaponAug::CWeaponAug()
+{
+}
+
+void CWeaponAug::SecondaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( pPlayer->GetFOV() == pPlayer->GetDefaultFOV() )
+ {
+ pPlayer->SetFOV( pPlayer, 55, 0.2f );
+ m_weaponMode = Secondary_Mode;
+ }
+ else if ( pPlayer->GetFOV() == 55 )
+ {
+ pPlayer->SetFOV( pPlayer, pPlayer->GetDefaultFOV(), 0.15f );
+ m_weaponMode = Primary_Mode;
+ }
+ else
+ {
+ pPlayer->SetFOV( pPlayer, pPlayer->GetDefaultFOV() );
+ m_weaponMode = Primary_Mode;
+ }
+
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.3;
+}
+
+
+float CWeaponAug::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ return 0.035f + 0.4f * m_flAccuracy;
+
+ else if ( pPlayer->GetAbsVelocity().Length2D() > 140 )
+ return 0.035f + 0.07f * m_flAccuracy;
+ else
+ return 0.02f * m_flAccuracy;
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+void CWeaponAug::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ bool bZoomed = pPlayer->GetFOV() < pPlayer->GetDefaultFOV();
+
+ float flCycleTime = GetCSWpnData().m_flCycleTime;
+
+ if ( bZoomed )
+ flCycleTime = 0.135f;
+
+ if ( !CSBaseGunFire( flCycleTime, m_weaponMode ) )
+ return;
+
+ // CSBaseGunFire can kill us, forcing us to drop our weapon, if we shoot something that explodes
+ pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( pPlayer->GetAbsVelocity().Length2D() > 5 )
+ pPlayer->KickBack ( 1, 0.45, 0.275, 0.05, 4, 2.5, 7 );
+
+ else if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ pPlayer->KickBack ( 1.25, 0.45, 0.22, 0.18, 5.5, 4, 5 );
+
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ pPlayer->KickBack ( 0.575, 0.325, 0.2, 0.011, 3.25, 2, 8 );
+
+ else
+ pPlayer->KickBack ( 0.625, 0.375, 0.25, 0.0125, 3.5, 2.25, 8 );
+}
+
+
+bool CWeaponAug::Reload()
+{
+ m_weaponMode = Primary_Mode;
+ return BaseClass::Reload();
+}
+
+bool CWeaponAug::Deploy()
+{
+ m_weaponMode = Primary_Mode;
+ return BaseClass::Deploy();
+}
diff --git a/game/shared/cstrike/weapon_awp.cpp b/game/shared/cstrike/weapon_awp.cpp
new file mode 100644
index 0000000..bcd4113
--- /dev/null
+++ b/game/shared/cstrike/weapon_awp.cpp
@@ -0,0 +1,306 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbasegun.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponAWP C_WeaponAWP
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+ #include "KeyValues.h"
+
+#endif
+
+#define SNIPER_ZOOM_CONTEXT "SniperRifleThink"
+
+const int cAWPMidZoomFOV = 40;
+const int cAWPMaxZoomFOV = 10;
+
+#ifdef AWP_UNZOOM
+ ConVar sv_awpunzoomdelay(
+ "sv_awpunzoomdelay",
+ "1.0",
+ 0,
+ "how many seconds to zoom the zoom up after firing",
+ true, 0, // min value
+ false, 0 // max value
+ );
+#endif
+
+
+class CWeaponAWP : public CWeaponCSBaseGun
+{
+public:
+ DECLARE_CLASS( CWeaponAWP, CWeaponCSBaseGun );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+#ifndef CLIENT_DLL
+ DECLARE_DATADESC();
+#endif
+
+ CWeaponAWP();
+
+ virtual void Spawn();
+
+ virtual void PrimaryAttack();
+ virtual void SecondaryAttack();
+
+ virtual float GetInaccuracy() const;
+ virtual float GetMaxSpeed() const;
+ virtual bool IsAwp() const;
+ virtual bool Reload();
+ virtual bool Deploy();
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_AWP; }
+
+private:
+
+#ifdef AWP_UNZOOM
+ void UnzoomThink( void );
+#endif
+
+ CWeaponAWP( const CWeaponAWP & );
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponAWP, DT_WeaponAWP )
+
+BEGIN_NETWORK_TABLE( CWeaponAWP, DT_WeaponAWP )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponAWP )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_awp, CWeaponAWP );
+PRECACHE_WEAPON_REGISTER( weapon_awp );
+
+#ifndef CLIENT_DLL
+
+ BEGIN_DATADESC( CWeaponAWP )
+#ifdef AWP_UNZOOM
+ DEFINE_THINKFUNC( UnzoomThink ),
+#endif
+ END_DATADESC()
+
+#endif
+
+CWeaponAWP::CWeaponAWP()
+{
+}
+
+void CWeaponAWP::Spawn()
+{
+ Precache();
+
+ BaseClass::Spawn();
+}
+
+
+void CWeaponAWP::SecondaryAttack()
+{
+ const float kZoomTime = 0.10f;
+
+ CCSPlayer *pPlayer = GetPlayerOwner();
+
+ if ( pPlayer == NULL )
+ {
+ Assert( pPlayer != NULL );
+ return;
+ }
+
+ if ( pPlayer->GetFOV() == pPlayer->GetDefaultFOV() )
+ {
+ pPlayer->SetFOV( pPlayer, cAWPMidZoomFOV, kZoomTime );
+ m_weaponMode = Secondary_Mode;
+ m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyAltSwitch;
+ }
+ else if ( pPlayer->GetFOV() == cAWPMidZoomFOV )
+ {
+ pPlayer->SetFOV( pPlayer, cAWPMaxZoomFOV, kZoomTime );
+ m_weaponMode = Secondary_Mode;
+ }
+ else
+ {
+ pPlayer->SetFOV( pPlayer, pPlayer->GetDefaultFOV(), kZoomTime );
+ m_weaponMode = Primary_Mode;
+ }
+
+
+#ifndef CLIENT_DLL
+ // If this isn't guarded, the sound will be emitted twice, once by the server and once by the client.
+ // Let the server play it since if only the client plays it, it's liable to get played twice cause of
+ // a prediction error. joy.
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Playing this from the player so that we don't try to play the sound outside the level.
+ //=============================================================================
+ if ( GetPlayerOwner() )
+ {
+ GetPlayerOwner()->EmitSound( "Default.Zoom" );
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ // let the bots hear the rifle zoom
+ IGameEvent * event = gameeventmanager->CreateEvent( "weapon_zoom" );
+ if ( event )
+ {
+ event->SetInt( "userid", pPlayer->GetUserID() );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.3f;
+ m_zoomFullyActiveTime = gpGlobals->curtime + 0.15; // The worst zoom time from above.
+
+}
+
+float CWeaponAWP::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ float fSpread = 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ fSpread = 0.85f;
+
+ else if ( pPlayer->GetAbsVelocity().Length2D() > 140 )
+ fSpread = 0.25f;
+
+ else if ( pPlayer->GetAbsVelocity().Length2D() > 10 )
+ fSpread = 0.10f;
+
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ fSpread = 0.0f;
+
+ else
+ fSpread = 0.001f;
+
+ // If we are not zoomed in, or we have very recently zoomed and are still transitioning, the bullet diverts more.
+ if (pPlayer->GetFOV() == pPlayer->GetDefaultFOV() || (gpGlobals->curtime < m_zoomFullyActiveTime))
+ {
+ fSpread += 0.08f;
+ }
+
+ return fSpread;
+ }
+ else
+ {
+ return BaseClass::GetInaccuracy();
+ }
+}
+
+void CWeaponAWP::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( !CSBaseGunFire( GetCSWpnData().m_flCycleTime, m_weaponMode ) )
+ return;
+
+ if ( m_weaponMode == Secondary_Mode )
+ {
+ float midFOVdistance = fabs( pPlayer->GetFOV() - (float)cAWPMidZoomFOV );
+ float farFOVdistance = fabs( pPlayer->GetFOV() - (float)cAWPMaxZoomFOV );
+ if ( midFOVdistance < farFOVdistance )
+ {
+ pPlayer->m_iLastZoom = cAWPMidZoomFOV;
+ }
+ else
+ {
+ pPlayer->m_iLastZoom = cAWPMaxZoomFOV;
+ }
+
+ #ifdef AWP_UNZOOM
+ SetContextThink( &CWeaponAWP::UnzoomThink, gpGlobals->curtime + sv_awpunzoomdelay.GetFloat(), SNIPER_ZOOM_CONTEXT );
+ #else
+ pPlayer->m_bResumeZoom = true;
+ pPlayer->SetFOV( pPlayer, pPlayer->GetDefaultFOV(), 0.1f );
+ m_weaponMode = Primary_Mode;
+ #endif
+ }
+
+ QAngle angle = pPlayer->GetPunchAngle();
+ angle.x -= 2;
+ pPlayer->SetPunchAngle( angle );
+}
+
+#ifdef AWP_UNZOOM
+void CWeaponAWP::UnzoomThink( void )
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+
+ if (pPlayer == NULL)
+ {
+ Assert(pPlayer != NULL);
+ return;
+ }
+
+ pPlayer->SetFOV( pPlayer, pPlayer->GetDefaultFOV(), 0.1f );
+}
+#endif
+
+
+float CWeaponAWP::GetMaxSpeed() const
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+
+ if (pPlayer == NULL)
+ {
+ Assert(pPlayer != NULL);
+ return BaseClass::GetMaxSpeed();
+ }
+
+ if ( pPlayer->GetFOV() == pPlayer->GetDefaultFOV() )
+ {
+ return BaseClass::GetMaxSpeed();
+ }
+ else
+ {
+ // Slower speed when zoomed in.
+ return 150;
+ }
+}
+
+
+bool CWeaponAWP::IsAwp() const
+{
+ return true;
+}
+
+
+bool CWeaponAWP::Reload()
+{
+ m_weaponMode = Primary_Mode;
+ return BaseClass::Reload();
+}
+
+bool CWeaponAWP::Deploy()
+{
+ // don't allow weapon switching to shortcut cycle time (quickswitch exploit)
+ float fOldNextPrimaryAttack = m_flNextPrimaryAttack;
+ float fOldNextSecondaryAttack = m_flNextSecondaryAttack;
+
+ if ( !BaseClass::Deploy() )
+ return false;
+
+ m_weaponMode = Primary_Mode;
+ m_flNextPrimaryAttack = MAX( m_flNextPrimaryAttack, fOldNextPrimaryAttack );
+ m_flNextSecondaryAttack = MAX( m_flNextSecondaryAttack, fOldNextSecondaryAttack );
+ return true;
+}
diff --git a/game/shared/cstrike/weapon_basecsgrenade.cpp b/game/shared/cstrike/weapon_basecsgrenade.cpp
new file mode 100644
index 0000000..618a96b
--- /dev/null
+++ b/game/shared/cstrike/weapon_basecsgrenade.cpp
@@ -0,0 +1,438 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbase.h"
+#include "gamerules.h"
+#include "npcevent.h"
+#include "engine/IEngineSound.h"
+#include "weapon_basecsgrenade.h"
+#include "in_buttons.h"
+#include "datacache/imdlcache.h"
+
+
+
+#ifdef CLIENT_DLL
+
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+ #include "items.h"
+ #include "../../server/cstrike/cs_gamestats.h"
+
+#endif
+
+
+#define GRENADE_TIMER 1.5f //Seconds
+
+
+IMPLEMENT_NETWORKCLASS_ALIASED( BaseCSGrenade, DT_BaseCSGrenade )
+
+BEGIN_NETWORK_TABLE(CBaseCSGrenade, DT_BaseCSGrenade)
+
+#ifndef CLIENT_DLL
+ SendPropBool( SENDINFO(m_bRedraw) ),
+ SendPropBool( SENDINFO(m_bPinPulled) ),
+ SendPropFloat( SENDINFO(m_fThrowTime), 0, SPROP_NOSCALE ),
+#else
+ RecvPropBool( RECVINFO(m_bRedraw) ),
+ RecvPropBool( RECVINFO(m_bPinPulled) ),
+ RecvPropFloat( RECVINFO(m_fThrowTime) ),
+#endif
+
+END_NETWORK_TABLE()
+
+#if defined CLIENT_DLL
+BEGIN_PREDICTION_DATA( CBaseCSGrenade )
+ DEFINE_PRED_FIELD( m_bRedraw, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bRedraw, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_basecsgrenade, CBaseCSGrenade );
+
+#ifndef CLIENT_DLL
+ConVar sv_ignoregrenaderadio( "sv_ignoregrenaderadio", "0", 0, "Turn off Fire in the hole messages" );
+#endif
+
+CBaseCSGrenade::CBaseCSGrenade()
+{
+ m_bRedraw = false;
+ m_bPinPulled = false;
+ m_fThrowTime = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseCSGrenade::Precache()
+{
+ BaseClass::Precache();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CBaseCSGrenade::Deploy()
+{
+ m_bRedraw = false;
+ m_bPinPulled = false;
+ m_fThrowTime = 0;
+
+#ifndef CLIENT_DLL
+ // if we're officially out of grenades, ditch this weapon
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return false;
+
+ if( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
+ {
+ pPlayer->Weapon_Drop( this, NULL, NULL );
+ UTIL_Remove(this);
+ return false;
+ }
+#endif
+
+ return BaseClass::Deploy();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseCSGrenade::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ m_bRedraw = false;
+ m_bPinPulled = false; // when this is holstered make sure the pin isn�t pulled.
+ m_fThrowTime = 0;
+
+#ifndef CLIENT_DLL
+ // If they attempt to switch weapons before the throw animation is done,
+ // allow it, but kill the weapon if we have to.
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return false;
+
+ if( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
+ {
+ CBaseCombatCharacter *pOwner = (CBaseCombatCharacter *)pPlayer;
+ pOwner->Weapon_Drop( this );
+ UTIL_Remove(this);
+ }
+#endif
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseCSGrenade::PrimaryAttack()
+{
+ if ( m_bRedraw || m_bPinPulled || m_fThrowTime > 0.0f )
+ return;
+
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer || pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 )
+ return;
+
+ // The pull pin animation has to finish, then we wait until they aren't holding the primary
+ // attack button, then throw the grenade.
+ SendWeaponAnim( ACT_VM_PULLPIN );
+ m_bPinPulled = true;
+
+ // Don't let weapon idle interfere in the middle of a throw!
+ MDLCACHE_CRITICAL_SECTION();
+ SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseCSGrenade::SecondaryAttack()
+{
+ if ( m_bRedraw )
+ return;
+
+ CCSPlayer *pPlayer = GetPlayerOwner();
+
+ if ( pPlayer == NULL )
+ return;
+
+ //See if we're ducking
+ if ( pPlayer->GetFlags() & FL_DUCKING )
+ {
+ //Send the weapon animation
+ SendWeaponAnim( ACT_VM_SECONDARYATTACK );
+ }
+ else
+ {
+ //Send the weapon animation
+ SendWeaponAnim( ACT_VM_HAULBACK );
+ }
+
+ // Don't let weapon idle interfere in the middle of a throw!
+ SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
+
+ m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CBaseCSGrenade::Reload()
+{
+ if ( ( m_bRedraw ) && ( m_flNextPrimaryAttack <= gpGlobals->curtime ) && ( m_flNextSecondaryAttack <= gpGlobals->curtime ) )
+ {
+ //Redraw the weapon
+ SendWeaponAnim( ACT_VM_DRAW );
+
+ //Update our times
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration();
+ m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
+
+ SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
+
+ //Mark this as done
+ // m_bRedraw = false;
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBaseCSGrenade::ItemPostFrame()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ CBaseViewModel *vm = pPlayer->GetViewModel( m_nViewModelIndex );
+ if ( !vm )
+ return;
+
+ // If they let go of the fire button, they want to throw the grenade.
+ if ( m_bPinPulled && !(pPlayer->m_nButtons & IN_ATTACK) )
+ {
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_THROW_GRENADE );
+
+ StartGrenadeThrow();
+
+ MDLCACHE_CRITICAL_SECTION();
+ m_bPinPulled = false;
+ SendWeaponAnim( ACT_VM_THROW );
+ SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + SequenceDuration(); // we're still throwing, so reset our next primary attack
+
+#ifndef CLIENT_DLL
+ IGameEvent * event = gameeventmanager->CreateEvent( "weapon_fire" );
+ if( event )
+ {
+ const char *weaponName = STRING( m_iClassname );
+ if ( strncmp( weaponName, "weapon_", 7 ) == 0 )
+ {
+ weaponName += 7;
+ }
+
+ event->SetInt( "userid", pPlayer->GetUserID() );
+ event->SetString( "weapon", weaponName );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+ }
+ else if ((m_fThrowTime > 0) && (m_fThrowTime < gpGlobals->curtime))
+ {
+ // only decrement our ammo when we actually create the projectile
+ DecrementAmmo( pPlayer );
+
+ ThrowGrenade();
+ }
+ else if( m_bRedraw )
+ {
+ // Has the throw animation finished playing
+ if( m_flTimeWeaponIdle < gpGlobals->curtime )
+ {
+#ifdef GAME_DLL
+ // if we're officially out of grenades, ditch this weapon
+ if( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
+ {
+ pPlayer->Weapon_Drop( this, NULL, NULL );
+ UTIL_Remove(this);
+ }
+ else
+ {
+ pPlayer->SwitchToNextBestWeapon( this );
+ }
+#endif
+ return; //don't animate this grenade any more!
+ }
+ }
+ else if( !m_bRedraw )
+ {
+ BaseClass::ItemPostFrame();
+ }
+}
+
+
+
+#ifdef CLIENT_DLL
+
+ void CBaseCSGrenade::DecrementAmmo( CBaseCombatCharacter *pOwner )
+ {
+ }
+
+ void CBaseCSGrenade::DropGrenade()
+ {
+ m_bRedraw = true;
+ m_fThrowTime = 0.0f;
+ }
+
+ void CBaseCSGrenade::ThrowGrenade()
+ {
+ m_bRedraw = true;
+ m_fThrowTime = 0.0f;
+ }
+
+ void CBaseCSGrenade::StartGrenadeThrow()
+ {
+ m_fThrowTime = gpGlobals->curtime + 0.1f;
+ }
+
+#else
+
+ BEGIN_DATADESC( CBaseCSGrenade )
+ DEFINE_FIELD( m_bRedraw, FIELD_BOOLEAN ),
+ END_DATADESC()
+
+ int CBaseCSGrenade::CapabilitiesGet()
+ {
+ return bits_CAP_WEAPON_RANGE_ATTACK1;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ // Input : *pOwner -
+ //-----------------------------------------------------------------------------
+ void CBaseCSGrenade::DecrementAmmo( CBaseCombatCharacter *pOwner )
+ {
+ pOwner->RemoveAmmo( 1, m_iPrimaryAmmoType );
+ }
+
+ void CBaseCSGrenade::StartGrenadeThrow()
+ {
+ m_fThrowTime = gpGlobals->curtime + 0.1f;
+ }
+
+ void CBaseCSGrenade::ThrowGrenade()
+ {
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+ if ( !pPlayer )
+ {
+ Assert( false );
+ return;
+ }
+
+ QAngle angThrow = pPlayer->LocalEyeAngles();
+
+ Vector vForward, vRight, vUp;
+
+ if ( angThrow.x < 0 )
+ {
+ angThrow.x += 360; // make sure we have a positive angle from LocalEyeAngles()
+ }
+
+ if ( angThrow.x < 90 )
+ angThrow.x = -10 + angThrow.x * ((90 + 10) / 90.0);
+ else
+ {
+ angThrow.x = 360.0f - angThrow.x;
+ angThrow.x = -10 + angThrow.x * -((90 - 10) / 90.0);
+ }
+
+ float flVel = (90 - angThrow.x) * 6;
+
+ if (flVel > 750)
+ flVel = 750;
+
+ AngleVectors( angThrow, &vForward, &vRight, &vUp );
+
+ Vector vecSrc = pPlayer->GetAbsOrigin() + pPlayer->GetViewOffset();
+
+ // We want to throw the grenade from 16 units out. But that can cause problems if we're facing
+ // a thin wall. Do a hull trace to be safe.
+ trace_t trace;
+ Vector mins( -2, -2, -2 );
+ Vector maxs( 2, 2, 2 );
+ UTIL_TraceHull( vecSrc, vecSrc + vForward * 16, mins, maxs, MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &trace );
+ vecSrc = trace.endpos;
+
+ Vector vecThrow = vForward * flVel + pPlayer->GetAbsVelocity();
+
+ EmitGrenade( vecSrc, vec3_angle, vecThrow, AngularImpulse(600,random->RandomInt(-1200,1200),0), pPlayer );
+
+ m_bRedraw = true;
+ m_fThrowTime = 0.0f;
+
+ CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer );
+
+ if( pCSPlayer )
+ {
+ if ( !sv_ignoregrenaderadio.GetBool() )
+ {
+ pCSPlayer->Radio( "Radio.FireInTheHole", "#Cstrike_TitlesTXT_Fire_in_the_hole" );
+ }
+ CCS_GameStats.IncrementStat(pCSPlayer, CSSTAT_GRENADES_THROWN, 1);
+ }
+ }
+
+ void CBaseCSGrenade::DropGrenade()
+ {
+ CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
+ if ( !pPlayer )
+ {
+ Assert( false );
+ return;
+ }
+
+ Vector vForward;
+ pPlayer->EyeVectors( &vForward );
+ Vector vecSrc = pPlayer->GetAbsOrigin() + pPlayer->GetViewOffset() + vForward * 16;
+
+ Vector vecVel = pPlayer->GetAbsVelocity();
+
+ EmitGrenade( vecSrc, vec3_angle, vecVel, AngularImpulse(600,random->RandomInt(-1200,1200),0), pPlayer );
+
+ CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer );
+
+ if( pCSPlayer )
+ {
+ CCS_GameStats.IncrementStat(pCSPlayer, CSSTAT_GRENADES_THROWN, 1);
+ }
+
+ m_bRedraw = true;
+ m_fThrowTime = 0.0f;
+ }
+
+ void CBaseCSGrenade::EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer )
+ {
+ Assert( 0 && "CBaseCSGrenade::EmitGrenade should not be called. Make sure to implement this in your subclass!\n" );
+ }
+
+ bool CBaseCSGrenade::AllowsAutoSwitchFrom( void ) const
+ {
+ return !m_bPinPulled;
+ }
+
+#endif
+
diff --git a/game/shared/cstrike/weapon_basecsgrenade.h b/game/shared/cstrike/weapon_basecsgrenade.h
new file mode 100644
index 0000000..334e8ab
--- /dev/null
+++ b/game/shared/cstrike/weapon_basecsgrenade.h
@@ -0,0 +1,82 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef WEAPON_BASECSGRENADE_H
+#define WEAPON_BASECSGRENADE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "weapon_csbase.h"
+
+
+#ifdef CLIENT_DLL
+
+ #define CBaseCSGrenade C_BaseCSGrenade
+
+#endif
+
+
+class CBaseCSGrenade : public CWeaponCSBase
+{
+public:
+ DECLARE_CLASS( CBaseCSGrenade, CWeaponCSBase );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CBaseCSGrenade();
+
+ virtual void Precache();
+
+ bool Deploy();
+ bool Holster( CBaseCombatWeapon *pSwitchingTo );
+
+ void PrimaryAttack();
+ void SecondaryAttack();
+
+// virtual float GetSpread() const;
+
+ bool Reload();
+
+ virtual void ItemPostFrame();
+
+ void DecrementAmmo( CBaseCombatCharacter *pOwner );
+ virtual void StartGrenadeThrow();
+ virtual void ThrowGrenade();
+ virtual void DropGrenade();
+
+ bool IsPinPulled() const;
+ bool IsBeingThrown() const { return m_fThrowTime > 0; }
+
+#ifndef CLIENT_DLL
+ DECLARE_DATADESC();
+
+ virtual bool AllowsAutoSwitchFrom( void ) const;
+
+ int CapabilitiesGet();
+
+ // Each derived grenade class implements this.
+ virtual void EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer );
+#endif
+
+protected:
+ CNetworkVar( bool, m_bRedraw ); // Draw the weapon again after throwing a grenade
+ CNetworkVar( bool, m_bPinPulled ); // Set to true when the pin has been pulled but the grenade hasn't been thrown yet.
+ CNetworkVar( float, m_fThrowTime ); // the time at which the grenade will be thrown. If this value is 0 then the time hasn't been set yet.
+
+private:
+ CBaseCSGrenade( const CBaseCSGrenade & ) {}
+};
+
+
+inline bool CBaseCSGrenade::IsPinPulled() const
+{
+ return m_bPinPulled;
+}
+
+
+#endif // WEAPON_BASECSGRENADE_H
diff --git a/game/shared/cstrike/weapon_c4.cpp b/game/shared/cstrike/weapon_c4.cpp
new file mode 100644
index 0000000..e75d6a3
--- /dev/null
+++ b/game/shared/cstrike/weapon_c4.cpp
@@ -0,0 +1,1353 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_c4.h"
+#include "in_buttons.h"
+#include "cs_gamerules.h"
+#include "decals.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "KeyValues.h"
+#include "fx_cs_shared.h"
+#include "obstacle_pushaway.h"
+
+#if defined( CLIENT_DLL )
+ #include "c_cs_player.h"
+#else
+ #include "cs_player.h"
+ #include "explode.h"
+ #include "mapinfo.h"
+ #include "team.h"
+ #include "func_bomb_target.h"
+ #include "vguiscreen.h"
+ #include "bot.h"
+ #include "cs_player.h"
+ #include <KeyValues.h>
+
+//=============================================================================
+// HPE_BEGIN
+// [dwenger] Necessary for stats tracking
+//=============================================================================
+#include "cs_gamestats.h"
+#include "cs_achievement_constants.h"
+//=============================================================================
+// HPE_END
+//=============================================================================
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+#define BLINK_INTERVAL 2.0
+#define PLANTED_C4_MODEL "models/weapons/w_c4_planted.mdl"
+#define HEIST_MODE_C4_TIME 25
+
+int g_sModelIndexC4Glow = -1;
+
+#define WEAPON_C4_ARM_TIME 3.0
+
+
+#ifdef CLIENT_DLL
+
+#else
+
+
+ LINK_ENTITY_TO_CLASS( planted_c4, CPlantedC4 );
+ PRECACHE_REGISTER( planted_c4 );
+
+ BEGIN_DATADESC( CPlantedC4 )
+ DEFINE_FUNCTION( C4Think )
+ END_DATADESC()
+
+
+ IMPLEMENT_SERVERCLASS_ST( CPlantedC4, DT_PlantedC4 )
+ SendPropBool( SENDINFO(m_bBombTicking) ),
+ SendPropFloat( SENDINFO(m_flC4Blow), 0, SPROP_NOSCALE ),
+ SendPropFloat( SENDINFO(m_flTimerLength), 0, SPROP_NOSCALE ),
+ SendPropFloat( SENDINFO(m_flDefuseLength), 0, SPROP_NOSCALE ),
+ SendPropFloat( SENDINFO(m_flDefuseCountDown), 0, SPROP_NOSCALE ),
+ END_SEND_TABLE()
+
+
+BEGIN_PREDICTION_DATA( CPlantedC4 )
+END_PREDICTION_DATA()
+
+
+
+ CUtlVector< CPlantedC4* > g_PlantedC4s;
+
+
+ CPlantedC4::CPlantedC4()
+ {
+ g_PlantedC4s.AddToTail( this );
+ //=============================================================================
+ // HPE_BEGIN:
+ //=============================================================================
+
+ // [tj] No planter initially
+ m_pPlanter = NULL;
+
+ // [tj] Assume this is the original owner
+ m_bPlantedAfterPickup = false;
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ }
+
+ CPlantedC4::~CPlantedC4()
+ {
+ g_PlantedC4s.FindAndRemove( this );
+
+ int i;
+ // Kill the control panels
+ for ( i = m_hScreens.Count(); --i >= 0; )
+ {
+ DestroyVGuiScreen( m_hScreens[i].Get() );
+ }
+ m_hScreens.RemoveAll();
+ }
+
+ int CPlantedC4::UpdateTransmitState()
+ {
+ return SetTransmitState( FL_EDICT_FULLCHECK );
+ }
+
+ int CPlantedC4::ShouldTransmit( const CCheckTransmitInfo *pInfo )
+ {
+ // Terrorists always need this object for the radar
+ // Everybody needs it for hiding the round timer and showing the planted C4 scenario icon
+ return FL_EDICT_ALWAYS;
+ }
+
+ void CPlantedC4::Precache()
+ {
+ g_sModelIndexC4Glow = PrecacheModel( "sprites/ledglow.vmt" );
+ PrecacheModel( PLANTED_C4_MODEL );
+ PrecacheVGuiScreen( "c4_panel" );
+
+ engine->ForceModelBounds( PLANTED_C4_MODEL, Vector( -7, -13, -3 ), Vector( 9, 12, 11 ) );
+
+ PrecacheParticleSystem( "bomb_explosion_huge" );
+ }
+
+ void CPlantedC4::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
+ {
+ pPanelName = "c4_panel";
+ }
+
+ void CPlantedC4::GetControlPanelClassName( int nPanelIndex, const char *&pPanelName )
+ {
+ pPanelName = "vgui_screen";
+ }
+
+ //-----------------------------------------------------------------------------
+ // This is called by the base object when it's time to spawn the control panels
+ //-----------------------------------------------------------------------------
+ void CPlantedC4::SpawnControlPanels()
+ {
+ char buf[64];
+
+ // FIXME: Deal with dynamically resizing control panels?
+
+ // If we're attached to an entity, spawn control panels on it instead of use
+ CBaseAnimating *pEntityToSpawnOn = this;
+ const char *pOrgLL = "controlpanel%d_ll";
+ const char *pOrgUR = "controlpanel%d_ur";
+ const char *pAttachmentNameLL = pOrgLL;
+ const char *pAttachmentNameUR = pOrgUR;
+
+ Assert( pEntityToSpawnOn );
+
+ // Lookup the attachment point...
+ int nPanel;
+ for ( nPanel = 0; true; ++nPanel )
+ {
+ Q_snprintf( buf, sizeof( buf ), pAttachmentNameLL, nPanel );
+ int nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
+ if (nLLAttachmentIndex <= 0)
+ {
+ // Try and use my panels then
+ pEntityToSpawnOn = this;
+ Q_snprintf( buf, sizeof( buf ), pOrgLL, nPanel );
+ nLLAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
+ if (nLLAttachmentIndex <= 0)
+ return;
+ }
+
+ Q_snprintf( buf, sizeof( buf ), pAttachmentNameUR, nPanel );
+ int nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
+ if (nURAttachmentIndex <= 0)
+ {
+ // Try and use my panels then
+ Q_snprintf( buf, sizeof( buf ), pOrgUR, nPanel );
+ nURAttachmentIndex = pEntityToSpawnOn->LookupAttachment(buf);
+ if (nURAttachmentIndex <= 0)
+ return;
+ }
+
+ const char *pScreenName;
+ GetControlPanelInfo( nPanel, pScreenName );
+ if (!pScreenName)
+ continue;
+
+ const char *pScreenClassname;
+ GetControlPanelClassName( nPanel, pScreenClassname );
+ if ( !pScreenClassname )
+ continue;
+
+ // Compute the screen size from the attachment points...
+ matrix3x4_t panelToWorld;
+ pEntityToSpawnOn->GetAttachment( nLLAttachmentIndex, panelToWorld );
+
+ matrix3x4_t worldToPanel;
+ MatrixInvert( panelToWorld, worldToPanel );
+
+ // Now get the lower right position + transform into panel space
+ Vector lr, lrlocal;
+ pEntityToSpawnOn->GetAttachment( nURAttachmentIndex, panelToWorld );
+ MatrixGetColumn( panelToWorld, 3, lr );
+ VectorTransform( lr, worldToPanel, lrlocal );
+
+ float flWidth = lrlocal.x;
+ float flHeight = lrlocal.y;
+
+ CVGuiScreen *pScreen = CreateVGuiScreen( pScreenClassname, pScreenName, pEntityToSpawnOn, this, nLLAttachmentIndex );
+ pScreen->ChangeTeam( GetTeamNumber() );
+ pScreen->SetActualSize( flWidth, flHeight );
+ pScreen->SetActive( true );
+ pScreen->MakeVisibleOnlyToTeammates( false );
+ int nScreen = m_hScreens.AddToTail( );
+ m_hScreens[nScreen].Set( pScreen );
+ }
+ }
+
+ void CPlantedC4::SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways )
+ {
+ // Are we already marked for transmission?
+ if ( pInfo->m_pTransmitEdict->Get( entindex() ) )
+ return;
+
+ BaseClass::SetTransmit( pInfo, bAlways );
+
+ // Force our screens to be sent too.
+ for ( int i=0; i < m_hScreens.Count(); i++ )
+ {
+ CVGuiScreen *pScreen = m_hScreens[i].Get();
+ pScreen->SetTransmit( pInfo, bAlways );
+ }
+ }
+
+ CPlantedC4* CPlantedC4::ShootSatchelCharge( CCSPlayer *pevOwner, Vector vecStart, QAngle vecAngles )
+ {
+ CPlantedC4 *pGrenade = dynamic_cast< CPlantedC4* >( CreateEntityByName( "planted_c4" ) );
+ if ( pGrenade )
+ {
+ vecAngles[0] = 0;
+ vecAngles[2] = 0;
+ pGrenade->Init( pevOwner, vecStart, vecAngles );
+ return pGrenade;
+ }
+ else
+ {
+ Warning( "Can't create planted_c4 entity!\n" );
+ return NULL;
+ }
+ }
+
+
+ void CPlantedC4::Init( CCSPlayer *pevOwner, Vector vecStart, QAngle vecAngles )
+ {
+ SetMoveType( MOVETYPE_NONE );
+ SetSolid( SOLID_NONE );
+
+ SetModel( PLANTED_C4_MODEL ); // Change this to c4 model
+
+ SetCollisionBounds( Vector( 0, 0, 0 ), Vector( 8, 8, 8 ) );
+
+ SetAbsOrigin( vecStart );
+ SetAbsAngles( vecAngles );
+ SetOwnerEntity( pevOwner );
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Set the planter when the bomb is planted.
+ //=============================================================================
+
+ SetPlanter( pevOwner );
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+
+ // Detonate in "time" seconds
+ SetThink( &CPlantedC4::C4Think );
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+
+ m_flTimerLength = mp_c4timer.GetInt();
+
+ m_flC4Blow = gpGlobals->curtime + m_flTimerLength;
+ m_flNextDefuse = 0;
+
+ m_bStartDefuse = false;
+ m_bBombTicking = true;
+ SetFriction( 0.9 );
+
+ m_flDefuseLength = 0.0f;
+
+ SpawnControlPanels();
+ }
+
+ void CPlantedC4::C4Think()
+ {
+ if (!IsInWorld())
+ {
+ UTIL_Remove( this );
+ return;
+ }
+
+ //Bomb is dead, don't think anymore
+ if( !m_bBombTicking )
+ {
+ SetThink( NULL );
+ return;
+ }
+
+
+ SetNextThink( gpGlobals->curtime + 0.12 );
+
+#ifndef CLIENT_DLL
+ // let the bots hear the bomb beeping
+ // BOTPORT: Emit beep events at same time as client effects
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_beep" );
+ if( event )
+ {
+ event->SetInt( "entindex", entindex() );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+
+ // IF the timer has expired ! blow this bomb up!
+ if (m_flC4Blow <= gpGlobals->curtime)
+ {
+ // give the defuser credit for defusing the bomb
+ CCSPlayer* pBombOwner = ToCSPlayer(GetOwnerEntity());
+ if ( pBombOwner )
+ {
+ if (CSGameRules()->m_iRoundWinStatus == WINNER_NONE)
+ pBombOwner->IncrementFragCount( 3 );
+ }
+
+ CSGameRules()->m_bBombDropped = false;
+
+ trace_t tr;
+ Vector vecSpot = GetAbsOrigin();
+ vecSpot[2] += 8;
+
+ UTIL_TraceLine( vecSpot, vecSpot + Vector ( 0, 0, -40 ), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
+
+ Explode( &tr, DMG_BLAST );
+
+ CSGameRules()->m_bBombPlanted = false;
+
+ CCS_GameStats.Event_BombExploded(pBombOwner);
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_exploded" );
+ if( event )
+ {
+ event->SetInt( "userid", pBombOwner?pBombOwner->GetUserID():-1 );
+ event->SetInt( "site", m_iBombSiteIndex );
+ event->SetInt( "priority", 9 );
+ gameeventmanager->FireEvent( event );
+ }
+
+ // skip additional processing once the bomb has exploded
+ return;
+ }
+
+ //if the defusing process has started
+ if ((m_bStartDefuse == true) && (m_pBombDefuser != NULL))
+ {
+ //if the defusing process has not ended yet
+ if ( m_flDefuseCountDown > gpGlobals->curtime)
+ {
+ int iOnGround = FBitSet( m_pBombDefuser->GetFlags(), FL_ONGROUND );
+
+ //if the bomb defuser has stopped defusing the bomb
+ if( m_flNextDefuse < gpGlobals->curtime || !iOnGround )
+ {
+ if ( !iOnGround && m_pBombDefuser->IsAlive() )
+ ClientPrint( m_pBombDefuser, HUD_PRINTCENTER, "#C4_Defuse_Must_Be_On_Ground");
+
+ // release the player from being frozen
+ m_pBombDefuser->m_bIsDefusing = false;
+
+#ifndef CLIENT_DLL
+ // tell the bots someone has aborted defusing
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortdefuse" );
+ if( event )
+ {
+ event->SetInt("userid", m_pBombDefuser->GetUserID() );
+ event->SetInt( "priority", 6 );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+
+ //cancel the progress bar
+ m_pBombDefuser->SetProgressBarTime( 0 );
+ m_pBombDefuser->OnCanceledDefuse();
+ m_pBombDefuser = NULL;
+ m_bStartDefuse = false;
+ m_flDefuseCountDown = 0;
+ m_flDefuseLength = 0; //force it to show completely defused
+ }
+
+ return;
+ }
+
+ //if the defuse process has ended, kill the c4
+ if ( !m_pBombDefuser->IsDead() )
+ {
+ //=============================================================================
+ // HPE_BEGIN
+ // [dwenger] Stats update for bomb defusing
+ //=============================================================================
+ CCS_GameStats.Event_BombDefused( m_pBombDefuser );
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_defused" );
+ if( event )
+ {
+ event->SetInt("userid", m_pBombDefuser->GetUserID() );
+ event->SetInt("site", m_iBombSiteIndex );
+ event->SetInt( "priority", 9 );
+ gameeventmanager->FireEvent( event );
+
+ //=============================================================================
+ // HPE_BEGIN
+ // [dwenger] Server-side processing for defusing bombs
+ //=============================================================================
+ m_pBombDefuser->AwardAchievement(CSWinBombDefuse);
+
+ float timeToDetonation = (m_flC4Blow - gpGlobals->curtime);
+
+ if ((timeToDetonation > 0.0f) && (timeToDetonation <= AchievementConsts::BombDefuseCloseCall_MaxTimeRemaining))
+ {
+ // Give achievement for defusing with < 1 second before detonation
+ m_pBombDefuser->AwardAchievement(CSBombDefuseCloseCall);
+ }
+
+ if ((timeToDetonation > 0.0f) && (m_pBombDefuser->HasDefuser()) && (timeToDetonation < AchievementConsts::BombDefuseNeededKit_MaxTime))
+ {
+ // Give achievement for defusing with a defuse kit when not having the kit would have taken too long
+ m_pBombDefuser->AwardAchievement(CSDefuseAndNeededKit);
+ }
+
+ // [dwenger] Added for fun-fact support
+ if ( m_pBombDefuser->PickedUpDefuser() )
+ {
+ // Defuser kit was picked up, so set the fun fact
+ m_pBombDefuser->SetDefusedWithPickedUpKit(true);
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ }
+
+
+ Vector soundPosition = m_pBombDefuser->GetAbsOrigin() + Vector( 0, 0, 5 );
+ CPASAttenuationFilter filter( soundPosition );
+
+ EmitSound( filter, entindex(), "c4.disarmfinish" );
+
+ // The bomb has just been disarmed.. Check to see if the round should end now
+ m_bBombTicking = false;
+
+ // release the player from being frozen
+ m_pBombDefuser->m_bIsDefusing = false;
+
+ CSGameRules()->m_bBombDefused = true;
+ //=============================================================================
+ // HPE_BEGIN:
+ // [menglish] Give the bomb defuser an mvp if they ended the round
+ //=============================================================================
+ bool roundWasAlreadyWon = (CSGameRules()->m_iRoundWinStatus != WINNER_NONE);
+
+ if(CSGameRules()->CheckWinConditions() && !roundWasAlreadyWon)
+ {
+ m_pBombDefuser->IncrementNumMVPs( CSMVP_BOMBDEFUSE );
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ // give the defuser credit for defusing the bomb
+ m_pBombDefuser->IncrementFragCount( 3 );
+
+ CSGameRules()->m_bBombDropped = false;
+ CSGameRules()->m_bBombPlanted = false;
+
+ // Clear their progress bar.
+ m_pBombDefuser->SetProgressBarTime( 0 );
+
+ m_pBombDefuser = NULL;
+ m_bStartDefuse = false;
+
+ m_flDefuseLength = 10;
+
+ return;
+ }
+
+ //if it gets here then the previouse defuser has taken off or been killed
+
+#ifndef CLIENT_DLL
+ // tell the bots someone has aborted defusing
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortdefuse" );
+ if ( event )
+ {
+ event->SetInt("userid", m_pBombDefuser->GetUserID() );
+ event->SetInt( "priority", 6 );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+
+ // release the player from being frozen
+ m_pBombDefuser->m_bIsDefusing = false;
+ m_bStartDefuse = false;
+ m_pBombDefuser = NULL;
+ }
+ }
+
+ // Regular explosions
+ void CPlantedC4::Explode( trace_t *pTrace, int bitsDamageType )
+ {
+ // Check to see if the round is over after the bomb went off...
+ CSGameRules()->m_bTargetBombed = true;
+ m_bBombTicking = false;
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Saving off this value so we can see if the detonation is what caused the round to end.
+ //=============================================================================
+ bool roundWasAlreadyWon = (CSGameRules()->m_iRoundWinStatus != WINNER_NONE);
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ bool bWin = CSGameRules()->CheckWinConditions();
+
+ //=============================================================================
+ // HPE_BEGIN
+ //=============================================================================
+
+ // [dwenger] Server-side processing for winning round by planting a bomb
+ if (bWin)
+ {
+ CCSPlayer *pBombOwner = ToCSPlayer( GetOwnerEntity() );
+ if ( pBombOwner )
+ {
+ pBombOwner->AwardAchievement(CSWinBombPlant);
+
+ //[tj]more specific achievement for planting the bomb after recovering it.
+ if (m_bPlantedAfterPickup)
+ {
+ pBombOwner->AwardAchievement(CSWinBombPlantAfterRecovery);
+ }
+ // [menglish] awarding mvp to bomb planter
+ if (!roundWasAlreadyWon)
+ {
+ pBombOwner->IncrementNumMVPs( CSMVP_BOMBPLANT );
+ }
+ }
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ // Do the Damage
+ float flBombRadius = 500;
+ if ( g_pMapInfo )
+ flBombRadius = g_pMapInfo->m_flBombRadius;
+
+ // Output to the bomb target ent
+ CBaseEntity *pTarget = NULL;
+ variant_t emptyVariant;
+ while ((pTarget = gEntList.FindEntityByClassname( pTarget, "func_bomb_target" )) != NULL)
+ {
+ //Adrian - But only to the one we want!
+ if ( pTarget->entindex() != m_iBombSiteIndex )
+ continue;
+
+ pTarget->AcceptInput( "BombExplode", this, this, emptyVariant, 0 );
+ break;
+ }
+
+ // Pull out of the wall a bit
+ if ( pTrace->fraction != 1.0 )
+ {
+ SetAbsOrigin( pTrace->endpos + (pTrace->plane.normal * 0.6) );
+ }
+
+ {
+ Vector pos = GetAbsOrigin() + Vector( 0,0,8 );
+
+ // add an explosion TE so it affects clientside physics
+ CPASFilter filter( pos );
+ te->Explosion( filter, 0.0,
+ &pos,
+ g_sModelIndexFireball,
+ 50.0,
+ 25,
+ TE_EXPLFLAG_NONE,
+ flBombRadius * 3.5,
+ 200 );
+ }
+
+ // Sound! for everyone
+ CBroadcastRecipientFilter filter;
+ EmitSound( filter, entindex(), "c4.explode" );
+
+
+ // Decal!
+ UTIL_DecalTrace( pTrace, "Scorch" );
+
+
+ // Shake!
+ UTIL_ScreenShake( pTrace->endpos, 25.0, 150.0, 1.0, 3000, SHAKE_START );
+
+
+ SetOwnerEntity( NULL ); // can't traceline attack owner if this is set
+
+ CSGameRules()->RadiusDamage(
+ CTakeDamageInfo( this, GetOwnerEntity(), flBombRadius, bitsDamageType ),
+ GetAbsOrigin(),
+ flBombRadius * 3.5, //Matt - don't ask me, this is how CS does it.
+ CLASS_NONE,
+ true ); // IGNORE THE WORLD!!
+
+ // send director message, that something important happed here
+ /*
+ MESSAGE_BEGIN( MSG_SPEC, SVC_DIRECTOR );
+ WRITE_BYTE ( 9 ); // command length in bytes
+ WRITE_BYTE ( DRC_CMD_EVENT ); // bomb explode
+ WRITE_SHORT( ENTINDEX(this->edict()) ); // index number of primary entity
+ WRITE_SHORT( 0 ); // index number of secondary entity
+ WRITE_LONG( 15 | DRC_FLAG_FINAL ); // eventflags (priority and flags)
+ MESSAGE_END();
+ */
+ }
+
+
+ // For CTs to defuse the c4
+ void CPlantedC4::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+ {
+ //Can't defuse if its already defused or if it has blown up
+ if( !m_bBombTicking )
+ {
+ SetUse( NULL );
+ return;
+ }
+
+ CCSPlayer *player = dynamic_cast< CCSPlayer* >( pActivator );
+
+ if ( !player || player->GetTeamNumber() != TEAM_CT )
+ return;
+
+ if ( m_bStartDefuse )
+ {
+ if ( player != m_pBombDefuser )
+ {
+ if ( player->m_iNextTimeCheck < gpGlobals->curtime )
+ {
+ ClientPrint( player, HUD_PRINTCENTER, "#Bomb_Already_Being_Defused" );
+ player->m_iNextTimeCheck = gpGlobals->curtime + 1;
+ }
+ return;
+ }
+
+ m_flNextDefuse = gpGlobals->curtime + 0.5;
+ }
+ else
+ {
+ // freeze the player in place while defusing
+
+ IGameEvent * event = gameeventmanager->CreateEvent("bomb_begindefuse" );
+ if( event )
+ {
+ event->SetInt( "userid", player->GetUserID() );
+ if ( player->HasDefuser() )
+ {
+ event->SetInt( "haskit", 1 );
+ // TODO show messages on clients on event
+ ClientPrint( player, HUD_PRINTCENTER, "#Defusing_Bomb_With_Defuse_Kit" );
+ }
+ else
+ {
+ event->SetInt( "haskit", 0 );
+ // TODO show messages on clients on event
+ ClientPrint( player, HUD_PRINTCENTER, "#Defusing_Bomb_Without_Defuse_Kit" );
+ }
+ event->SetInt( "priority", 8 );
+ gameeventmanager->FireEvent( event );
+ }
+
+ Vector soundPosition = player->GetAbsOrigin() + Vector( 0, 0, 5 );
+ CPASAttenuationFilter filter( soundPosition );
+
+ EmitSound( filter, entindex(), "c4.disarmstart" );
+
+ m_flDefuseLength = player->HasDefuser() ? 5 : 10;
+
+
+ m_flNextDefuse = gpGlobals->curtime + 0.5;
+ m_pBombDefuser = player;
+ m_bStartDefuse = TRUE;
+ player->m_bIsDefusing = true;
+
+ m_flDefuseCountDown = gpGlobals->curtime + m_flDefuseLength;
+
+ //start the progress bar
+ player->SetProgressBarTime( m_flDefuseLength );
+
+
+ player->OnStartedDefuse();
+ }
+ }
+
+
+#endif
+
+
+
+// -------------------------------------------------------------------------------- //
+// Tables.
+// -------------------------------------------------------------------------------- //
+
+IMPLEMENT_NETWORKCLASS_ALIASED( C4, DT_WeaponC4 )
+
+BEGIN_NETWORK_TABLE( CC4, DT_WeaponC4 )
+ #ifdef CLIENT_DLL
+ RecvPropBool( RECVINFO( m_bStartedArming ) ),
+ RecvPropBool( RECVINFO( m_bBombPlacedAnimation ) ),
+ RecvPropFloat( RECVINFO( m_fArmedTime ) )
+ #else
+ SendPropBool( SENDINFO( m_bStartedArming ) ),
+ SendPropBool( SENDINFO( m_bBombPlacedAnimation ) ),
+ SendPropFloat( SENDINFO( m_fArmedTime ), 0, SPROP_NOSCALE )
+ #endif
+END_NETWORK_TABLE()
+
+#if defined CLIENT_DLL
+BEGIN_PREDICTION_DATA( CC4 )
+ DEFINE_PRED_FIELD( m_bStartedArming, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bBombPlacedAnimation, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_fArmedTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE )
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_c4, CC4 );
+PRECACHE_WEAPON_REGISTER( weapon_c4 );
+
+
+
+// -------------------------------------------------------------------------------- //
+// Globals.
+// -------------------------------------------------------------------------------- //
+
+CUtlVector< CC4* > g_C4s;
+
+
+
+// -------------------------------------------------------------------------------- //
+// CC4 implementation.
+// -------------------------------------------------------------------------------- //
+
+CC4::CC4()
+{
+ g_C4s.AddToTail( this );
+ m_bDroppedFromDeath = false;
+
+#if defined( CLIENT_DLL )
+ m_szScreenText[0] = '\0';
+#endif
+
+}
+
+
+CC4::~CC4()
+{
+ g_C4s.FindAndRemove( this );
+}
+
+void CC4::Spawn()
+{
+ BaseClass::Spawn();
+
+ //Don't allow players to shoot the C4 around
+ SetCollisionGroup( COLLISION_GROUP_DEBRIS );
+
+ //Don't be damaged / moved by explosions
+ m_takedamage = DAMAGE_NO;
+
+ m_bBombPlanted = false;
+}
+
+void CC4::ItemPostFrame()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ // Disable all the firing code.. the C4 grenade is all custom.
+ if ( pPlayer->m_nButtons & IN_ATTACK )
+ {
+ PrimaryAttack();
+ }
+ else
+ {
+ WeaponIdle();
+ }
+}
+
+#if defined( CLIENT_DLL )
+
+ bool CC4::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
+ {
+ if( event == 7001 )
+ {
+ //set the screen text to the string in 'options'
+ Q_strncpy( m_szScreenText, options, 16 );
+
+ return true;
+ }
+ return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options );
+ }
+
+ char *CC4::GetScreenText( void )
+ {
+ if( m_bStartedArming )
+ return m_szScreenText;
+ else
+ return "";
+ }
+
+#endif //CLIENT_DLL
+
+#ifdef GAME_DLL
+
+
+ unsigned int CC4::PhysicsSolidMaskForEntity( void ) const
+ {
+ return BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_PLAYERCLIP;
+ }
+
+ void CC4::Precache()
+ {
+ PrecacheVGuiScreen( "c4_view_panel" );
+
+ PrecacheScriptSound( "c4.disarmfinish" );
+ PrecacheScriptSound( "c4.explode" );
+ PrecacheScriptSound( "c4.disarmstart" );
+ PrecacheScriptSound( "c4.plant" );
+ PrecacheScriptSound( "C4.PlantSound" );
+
+ BaseClass::Precache();
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Gets info about the control panels
+ //-----------------------------------------------------------------------------
+ void CC4::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
+ {
+ pPanelName = "c4_view_panel";
+ }
+
+ bool CC4::Holster( CBaseCombatWeapon *pSwitchingTo )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( pPlayer )
+ pPlayer->SetProgressBarTime( 0 );
+
+ if ( m_bStartedArming )
+ {
+ AbortBombPlant();
+ }
+
+ return BaseClass::Holster( pSwitchingTo );
+ }
+
+
+ bool CC4::ShouldRemoveOnRoundRestart()
+ {
+ // Doesn't matter if we have an owner or not.. always remove the C4 when the round restarts.
+ // The gamerules will give another C4 to some lucky player.
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( pPlayer && pPlayer->GetActiveWeapon() == this )
+ engine->ClientCommand( pPlayer->edict(), "lastinv reset\n" );
+ return true;
+ }
+
+#endif
+
+
+void CC4::PrimaryAttack()
+{
+ bool bArmingTimeSatisfied = false;
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ int onGround = FBitSet( pPlayer->GetFlags(), FL_ONGROUND );
+ CBaseEntity *groundEntity = (onGround) ? pPlayer->GetGroundEntity() : NULL;
+ if ( groundEntity )
+ {
+ // Don't let us stand on players, breakables, or pushaway physics objects to plant
+ if ( groundEntity->IsPlayer() ||
+ IsPushableEntity( groundEntity ) ||
+#ifndef CLIENT_DLL
+ IsBreakableEntity( groundEntity ) ||
+#endif // !CLIENT_DLL
+ IsPushAwayEntity( groundEntity ) )
+ {
+ onGround = false;
+ }
+ }
+
+ if( m_bStartedArming == false && m_bBombPlanted == false )
+ {
+ if( pPlayer->m_bInBombZone && onGround )
+ {
+ m_bStartedArming = true;
+ m_fArmedTime = gpGlobals->curtime + WEAPON_C4_ARM_TIME;
+ m_bBombPlacedAnimation = false;
+
+
+#if !defined( CLIENT_DLL )
+ // init the beep flags
+ int i;
+ for( i=0;i<NUM_BEEPS;i++ )
+ m_bPlayedArmingBeeps[i] = false;
+
+ // freeze the player in place while planting
+
+ // player "arming bomb" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ pPlayer->SetNextAttack( gpGlobals->curtime );
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_beginplant" );
+ if( event )
+ {
+ event->SetInt("userid", pPlayer->GetUserID() );
+ event->SetInt("site", pPlayer->m_iBombSiteIndex );
+ event->SetInt( "priority", 8 );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+
+ FX_PlantBomb( pPlayer->entindex(), pPlayer->Weapon_ShootPosition(), PLANTBOMB_PLANT );
+ }
+ else
+ {
+ if ( !pPlayer->m_bInBombZone )
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "#C4_Plant_At_Bomb_Spot");
+ }
+ else
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "#C4_Plant_Must_Be_On_Ground");
+ }
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
+ return;
+ }
+ }
+ else
+ {
+ if ( !onGround || !pPlayer->m_bInBombZone )
+ {
+ if( !pPlayer->m_bInBombZone )
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "#C4_Arming_Cancelled" );
+ }
+ else
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "#C4_Plant_Must_Be_On_Ground" );
+ }
+
+ AbortBombPlant();
+
+ if(m_bBombPlacedAnimation == true) //this means the placement animation is canceled
+ {
+ SendWeaponAnim( ACT_VM_DRAW );
+ }
+ else
+ {
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+
+ return;
+ }
+ else
+ {
+#ifndef CLIENT_DLL
+ PlayArmingBeeps();
+#endif
+
+ if( gpGlobals->curtime >= m_fArmedTime ) //the c4 is ready to be armed
+ {
+ //check to make sure the player is still in the bomb target area
+ bArmingTimeSatisfied = true;
+ }
+ else if( ( gpGlobals->curtime >= (m_fArmedTime - 0.75) ) && ( !m_bBombPlacedAnimation ) )
+ {
+ //call the c4 Placement animation
+ m_bBombPlacedAnimation = true;
+
+ SendWeaponAnim( ACT_VM_SECONDARYATTACK );
+
+#if !defined( CLIENT_DLL )
+ // player "place" animation
+ //pPlayer->SetAnimation( PLAYER_HOLDBOMB );
+#endif
+ }
+ }
+ }
+
+ if ( bArmingTimeSatisfied && m_bStartedArming )
+ {
+ m_bStartedArming = false;
+ m_fArmedTime = 0;
+
+ if( pPlayer->m_bInBombZone )
+ {
+#if !defined( CLIENT_DLL )
+ CPlantedC4 *pC4 = CPlantedC4::ShootSatchelCharge( pPlayer, pPlayer->GetAbsOrigin(), pPlayer->GetAbsAngles() );
+
+ if ( pC4 )
+ {
+ pC4->SetBombSiteIndex( pPlayer->m_iBombSiteIndex );
+
+ trace_t tr;
+ UTIL_TraceEntity( pC4, GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,-200), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
+ pC4->SetAbsOrigin( tr.endpos );
+
+ CBombTarget *pBombTarget = (CBombTarget*)UTIL_EntityByIndex( pPlayer->m_iBombSiteIndex );
+
+ if ( pBombTarget )
+ {
+ CBaseEntity *pAttachPoint = gEntList.FindEntityByName( NULL, pBombTarget->GetBombMountTarget() );
+
+ if ( pAttachPoint )
+ {
+ pC4->SetAbsOrigin( pAttachPoint->GetAbsOrigin() );
+ pC4->SetAbsAngles( pAttachPoint->GetAbsAngles() );
+ pC4->SetParent( pAttachPoint );
+ }
+
+ variant_t emptyVariant;
+ pBombTarget->AcceptInput( "BombPlanted", pC4, pC4, emptyVariant, 0 );
+ }
+
+ // [tj] If the bomb is planted by someone that picked it up after the
+ // original owner was killed, pass that along to the planted bomb
+ pC4->SetPlantedAfterPickup( m_bDroppedFromDeath );
+ }
+
+ //=============================================================================
+ // HPE_BEGIN
+ // [dwenger] Stats update for bomb planting
+ //=============================================================================
+
+ // Determine how elapsed time from start of round until the bomb was planted
+ float plantingTime = gpGlobals->curtime - CSGameRules()->GetRoundStartTime();
+
+ // Award achievement to bomb planter if time <= 25 seconds
+ if ((plantingTime > 0.0f) && (plantingTime <= AchievementConsts::FastBombPlant_Time))
+ {
+ pPlayer->AwardAchievement(CSPlantBombWithin25Seconds);
+ }
+
+ CCS_GameStats.Event_BombPlanted( pPlayer );
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_planted" );
+ if( event )
+ {
+ event->SetInt("userid", pPlayer->GetUserID() );
+ event->SetInt("site", pPlayer->m_iBombSiteIndex );
+ event->SetInt("posx", pPlayer->GetAbsOrigin().x );
+ event->SetInt("posy", pPlayer->GetAbsOrigin().y );
+ event->SetInt( "priority", 8 );
+ gameeventmanager->FireEvent( event );
+ }
+
+ // Fire a beep event also so the bots have a chance to hear the bomb
+ event = gameeventmanager->CreateEvent( "bomb_beep" );
+
+ if ( event )
+ {
+ event->SetInt( "entindex", entindex() );
+ gameeventmanager->FireEvent( event );
+ }
+
+ pPlayer->SetProgressBarTime( 0 );
+
+ CSGameRules()->m_bBombDropped = false;
+ CSGameRules()->m_bBombPlanted = true;
+
+ // Play the plant sound.
+ Vector plantPosition = pPlayer->GetAbsOrigin() + Vector( 0, 0, 5 );
+ CPASAttenuationFilter filter( plantPosition );
+ EmitSound( filter, entindex(), "c4.plant" );
+
+ // No more c4!
+ pPlayer->Weapon_Drop( this, NULL, NULL );
+ UTIL_Remove( this );
+#endif
+
+ //don't allow the planting to start over again next frame.
+ m_bBombPlanted = true;
+
+ return;
+ }
+ else
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "#C4_Activated_At_Bomb_Spot" );
+
+#if !defined( CLIENT_DLL )
+ //pPlayer->SetAnimation( PLAYER_HOLDBOMB );
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortplant" );
+ if( event )
+ {
+ event->SetInt("userid", pPlayer->GetUserID() );
+ event->SetInt("site", pPlayer->m_iBombSiteIndex );
+ event->SetInt( "priority", 8 );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
+ return;
+ }
+ }
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.3;
+ SetWeaponIdleTime( gpGlobals->curtime + SharedRandomFloat("C4IdleTime", 10, 15 ) );
+}
+
+void CC4::WeaponIdle()
+{
+ // if the player releases the attack button cancel the arming sequence
+ if ( m_bStartedArming )
+ {
+ AbortBombPlant();
+
+ CCSPlayer *pPlayer = GetPlayerOwner();
+
+ // TODO: make this use SendWeaponAnim and activities when the C4 has the activities hooked up.
+ if ( pPlayer )
+ {
+ SendWeaponAnim( ACT_VM_IDLE );
+ pPlayer->SetNextAttack( gpGlobals->curtime );
+ }
+
+ if(m_bBombPlacedAnimation == true) //this means the placement animation is canceled
+ SendWeaponAnim( ACT_VM_DRAW );
+ else
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+}
+
+void CC4::UpdateShieldState( void )
+{
+ //ADRIANTODO
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( pPlayer->HasShield() )
+ {
+ pPlayer->SetShieldDrawnState( false );
+
+ CBaseViewModel *pVM = pPlayer->GetViewModel( 1 );
+
+ if ( pVM )
+ {
+ pVM->AddEffects( EF_NODRAW );
+ }
+ //pPlayer->SetHitBoxSet( 3 );
+ }
+ else
+ BaseClass::UpdateShieldState();
+}
+
+
+int m_iBeepFrames[NUM_BEEPS] = { 27, 37, 45, 51, 57, 63, 67 };
+int iNumArmingAnimFrames = 83;
+
+void CC4::PlayArmingBeeps( void )
+{
+ float flStartTime = m_fArmedTime - WEAPON_C4_ARM_TIME;
+
+ float flProgress = ( gpGlobals->curtime - flStartTime ) / ( WEAPON_C4_ARM_TIME - 0.75 );
+
+ int currentFrame = (int)( (float)iNumArmingAnimFrames * flProgress );
+
+ int i;
+ for( i=0;i<NUM_BEEPS;i++ )
+ {
+ if( currentFrame <= m_iBeepFrames[i] )
+ {
+ break;
+ }
+ else if( !m_bPlayedArmingBeeps[i] )
+ {
+ m_bPlayedArmingBeeps[i] = true;
+
+ CCSPlayer *owner = GetPlayerOwner();
+ Vector soundPosition = owner->GetAbsOrigin() + Vector( 0, 0, 5 );
+ CPASAttenuationFilter filter( soundPosition );
+
+ filter.RemoveRecipient( owner );
+
+ // remove anyone that is first person spec'ing the planter
+ int i;
+ CBasePlayer *pPlayer;
+ for( i=1;i<=gpGlobals->maxClients;i++ )
+ {
+ pPlayer = UTIL_PlayerByIndex( i );
+
+ if ( !pPlayer )
+ continue;
+
+ if( pPlayer->GetObserverMode() == OBS_MODE_IN_EYE && pPlayer->GetObserverTarget() == GetOwner() )
+ {
+ filter.RemoveRecipient( pPlayer );
+ }
+ }
+
+ EmitSound(filter, entindex(), "c4.click");
+
+ break;
+ }
+ }
+}
+
+float CC4::GetMaxSpeed() const
+{
+ if ( m_bStartedArming )
+ return CS_PLAYER_SPEED_STOPPED;
+ else
+ return BaseClass::GetMaxSpeed();
+}
+
+
+void CC4::OnPickedUp( CBaseCombatCharacter *pNewOwner )
+{
+ BaseClass::OnPickedUp( pNewOwner );
+
+#if !defined( CLIENT_DLL )
+ CCSPlayer *pPlayer = dynamic_cast<CCSPlayer *>( pNewOwner );
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_pickup" );
+ if ( event )
+ {
+ event->SetInt( "userid", pPlayer->GetUserID() );
+ event->SetInt( "priority", 6 );
+ gameeventmanager->FireEvent( event );
+ }
+
+ if ( pPlayer->m_bShowHints && !(pPlayer->m_iDisplayHistoryBits & DHF_BOMB_RETRIEVED) )
+ {
+ pPlayer->m_iDisplayHistoryBits |= DHF_BOMB_RETRIEVED;
+ pPlayer->HintMessage( "#Hint_you_have_the_bomb", false );
+ }
+ else
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "#Got_bomb" );
+ }
+
+ pPlayer->SetBombPickupTime(gpGlobals->curtime);
+#endif
+}
+
+// HACK - Ask Mike Booth...
+#ifndef CLIENT_DLL
+ #include "cs_bot.h"
+#endif
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+void CC4::Drop( const Vector &vecVelocity )
+{
+#if !defined( CLIENT_DLL )
+ if ( !CSGameRules()->m_bBombPlanted ) // its not dropped if its planted
+ {
+ // tell the bots about the dropped bomb
+ TheCSBots()->SetLooseBomb( this );
+
+ CBasePlayer *pPlayer = dynamic_cast<CBasePlayer *>(GetOwnerEntity());
+ Assert( pPlayer );
+ if ( pPlayer )
+ {
+ IGameEvent * event = gameeventmanager->CreateEvent("bomb_dropped" );
+ if ( event )
+ {
+ event->SetInt( "userid", pPlayer->GetUserID() );
+ event->SetInt( "priority", 6 );
+ gameeventmanager->FireEvent( event );
+ }
+ }
+ }
+#endif
+
+ if ( m_bStartedArming )
+ AbortBombPlant(); // stop arming sequence
+
+ BaseClass::Drop( vecVelocity );
+}
+
+void CC4::AbortBombPlant()
+{
+ m_bStartedArming = false;
+
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+#if !defined( CLIENT_DLL )
+ m_flNextPrimaryAttack = gpGlobals->curtime + 1.0;
+
+ pPlayer->SetProgressBarTime( 0 );
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "bomb_abortplant" );
+ if( event )
+ {
+ event->SetInt("userid", pPlayer->GetUserID() );
+ event->SetInt("site", pPlayer->m_iBombSiteIndex );
+ event->SetInt( "priority", 8 );
+ gameeventmanager->FireEvent( event );
+ }
+
+#endif
+
+ FX_PlantBomb( pPlayer->entindex(), pPlayer->Weapon_ShootPosition(), PLANTBOMB_ABORT );
+} \ No newline at end of file
diff --git a/game/shared/cstrike/weapon_c4.h b/game/shared/cstrike/weapon_c4.h
new file mode 100644
index 0000000..7aa50a7
--- /dev/null
+++ b/game/shared/cstrike/weapon_c4.h
@@ -0,0 +1,221 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef WEAPON_C4_H
+#define WEAPON_C4_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "weapon_csbase.h"
+#include "utlvector.h"
+
+#define NUM_BEEPS 7
+
+#if defined( CLIENT_DLL )
+
+ #define CC4 C_C4
+
+#else
+
+ // ------------------------------------------------------------------------------------------ //
+ // CPlantedC4 class.
+ // ------------------------------------------------------------------------------------------ //
+
+ class CPlantedC4 : public CBaseAnimating
+ {
+ public:
+ DECLARE_CLASS( CPlantedC4, CBaseAnimating );
+ DECLARE_DATADESC();
+ DECLARE_SERVERCLASS();
+
+ DECLARE_PREDICTABLE();
+
+ CPlantedC4();
+ virtual ~CPlantedC4();
+
+ virtual int UpdateTransmitState();
+ virtual void SetTransmit( CCheckTransmitInfo *pInfo, bool bAlways );
+ virtual int ShouldTransmit( const CCheckTransmitInfo *pInfo );
+
+ static CPlantedC4* ShootSatchelCharge( CCSPlayer *pevOwner, Vector vecStart, QAngle vecAngles );
+ virtual void Precache();
+
+ // Set these flags so CTs can use the C4 to disarm it.
+ virtual int ObjectCaps() { return BaseClass::ObjectCaps() | (FCAP_CONTINUOUS_USE | FCAP_USE_IN_RADIUS); }
+
+ void SetBombSiteIndex( int iIndex ){ m_iBombSiteIndex = iIndex; }
+
+ inline bool IsBombActive( void ) { return m_bBombTicking; }
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Accessors related to planting of the bomb
+ //=============================================================================
+
+ CCSPlayer* GetPlanter() { return m_pPlanter; }
+ void SetPlanter(CCSPlayer* player) { m_pPlanter = player; }
+
+ void SetPlantedAfterPickup (bool plantedAfterPickup) { m_bPlantedAfterPickup = plantedAfterPickup; }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+
+ public:
+
+ CNetworkVar( bool, m_bBombTicking );
+ CNetworkVar( float, m_flC4Blow );
+
+ private:
+
+ void Init( CCSPlayer *pevOwner, Vector vecStart, QAngle vecAngles );
+ void C4Think();
+
+ // This becomes the think function when the timer has expired and it is about to explode.
+ void DetonateThink();
+ void Explode( trace_t *pTrace, int bitsDamageType );
+
+ void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+
+ // Replicate timer length to the client for effects
+ CNetworkVar( float, m_flTimerLength );
+
+ // Info for defusing.
+ CHandle<CCSPlayer> m_pBombDefuser;
+ float m_flNextDefuse;
+ bool m_bStartDefuse;
+ int m_iBombSiteIndex;
+
+ CNetworkVar( float, m_flDefuseLength ); //How long does the defuse take? Depends on if a defuser was used
+ CNetworkVar( float, m_flDefuseCountDown ); //What time does the defuse complete?
+
+ // Control panel
+ void GetControlPanelInfo( int nPanelIndex, const char *&pPanelName );
+ void GetControlPanelClassName( int nPanelIndex, const char *&pPanelName );
+ void SpawnControlPanels();
+
+ typedef CHandle<CVGuiScreen> ScreenHandle_t;
+ CUtlVector<ScreenHandle_t> m_hScreens;
+
+ int m_iProgressBarTime;
+
+ //=============================================================================
+ // HPE_BEGIN:
+ //=============================================================================
+
+ // [tj] We need to store who planted the bomb so we can track who deserves credits for the kills
+ CHandle<CCSPlayer> m_pPlanter;
+
+ // [tj] We need to know if this was planted by a player who recovered the bomb
+ bool m_bPlantedAfterPickup;
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ };
+
+ extern CUtlVector< CPlantedC4* > g_PlantedC4s;
+
+#endif
+
+#define WEAPON_C4_CLASSNAME "weapon_c4"
+#define PLANTED_C4_CLASSNAME "planted_c4"
+
+class CC4 : public CWeaponCSBase
+{
+public:
+ DECLARE_CLASS( CC4, CWeaponCSBase );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CC4();
+ virtual ~CC4();
+
+ virtual void Spawn();
+
+ bool IsPistol() const;
+ void ItemPostFrame();
+ virtual void PrimaryAttack();
+ virtual void WeaponIdle();
+ virtual void UpdateShieldState( void );
+ virtual float GetMaxSpeed() const;
+
+// virtual float GetSpread() const;
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_C4; }
+
+ #ifdef CLIENT_DLL
+
+ virtual bool OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options );
+ char *GetScreenText( void );
+ char m_szScreenText[32];
+
+ #else
+
+ virtual void Precache();
+ virtual void GetControlPanelInfo( int nPanelIndex, const char *&pPanelName );
+ virtual unsigned int PhysicsSolidMaskForEntity( void ) const;
+
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo );
+ virtual bool ShouldRemoveOnRoundRestart();
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Simple Setter
+ //=============================================================================
+
+ void SetDroppedFromDeath(bool droppedFromDeath) { m_bDroppedFromDeath = droppedFromDeath; }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+
+ #endif
+
+ void AbortBombPlant();
+
+ void PlayArmingBeeps( void );
+ virtual void OnPickedUp( CBaseCombatCharacter *pNewOwner );
+ virtual void Drop( const Vector &vecVelocity );
+
+ CNetworkVar( bool, m_bStartedArming );
+ CNetworkVar( float, m_fArmedTime );
+ CNetworkVar( bool, m_bBombPlacedAnimation );
+
+ virtual bool IsRemoveable( void ) { return false; }
+
+private:
+ bool m_bPlayedArmingBeeps[NUM_BEEPS];
+ bool m_bBombPlanted;
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] we want to store if this bomb was dropped because the original owner was killed
+ //=============================================================================
+
+ bool m_bDroppedFromDeath;
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+
+private:
+
+ CC4( const CC4 & );
+};
+
+
+// All the currently-active C4 grenades.
+extern CUtlVector< CC4* > g_C4s;
+
+
+#endif // WEAPON_C4_H
diff --git a/game/shared/cstrike/weapon_csbase.cpp b/game/shared/cstrike/weapon_csbase.cpp
new file mode 100644
index 0000000..8063b91
--- /dev/null
+++ b/game/shared/cstrike/weapon_csbase.cpp
@@ -0,0 +1,1917 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Laser Rifle & Shield combo
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "in_buttons.h"
+#include "takedamageinfo.h"
+#include "weapon_csbase.h"
+#include "ammodef.h"
+#include "cs_gamerules.h"
+
+#define ALLOW_WEAPON_SPREAD_DISPLAY 0
+
+#if defined( CLIENT_DLL )
+
+ #include "vgui/ISurface.h"
+ #include "vgui_controls/Controls.h"
+ #include "c_cs_player.h"
+ #include "hud_crosshair.h"
+ #include "c_te_effect_dispatch.h"
+ #include "c_te_legacytempents.h"
+
+ extern IVModelInfoClient* modelinfo;
+
+#else
+
+ #include "cs_player.h"
+ #include "te_effect_dispatch.h"
+ #include "KeyValues.h"
+ #include "cs_ammodef.h"
+
+ extern IVModelInfo* modelinfo;
+
+#endif
+
+
+ConVar weapon_accuracy_model( "weapon_accuracy_model", "2", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY | FCVAR_ARCHIVE );
+
+
+// ----------------------------------------------------------------------------- //
+// Global functions.
+// ----------------------------------------------------------------------------- //
+
+
+
+struct WeaponAliasTranslationInfoStruct
+{
+ const char* alias;
+ const char* translatedAlias;
+};
+
+static const WeaponAliasTranslationInfoStruct s_WeaponAliasTranslationInfo[] =
+{
+ { "cv47", "ak47" },
+ { "defender", "galil" },
+ { "krieg552", "sg552" },
+ { "magnum", "awp" },
+ { "d3au1", "g3sg1" },
+ { "clarion", "famas" },
+ { "bullpup", "aug" },
+ { "krieg550", "sg550" },
+ { "9x19mm", "glock" },
+ { "km45", "usp" },
+ { "228compact", "p228" },
+ { "nighthawk", "deagle" },
+ { "elites", "elite" },
+ { "fn57", "fiveseven" },
+ { "12gauge", "m3" },
+ { "autoshotgun", "xm1014" },
+ { "mp", "tmp" },
+ { "smg", "mp5navy" },
+ { "mp5", "mp5navy" },
+ { "c90", "p90" },
+ { "vest", "kevlar" },
+ { "vesthelm", "assaultsuit" },
+ { "smokegrenade", "sgren" },
+ { "smokegrenade", "sgren" },
+ { "nvgs", "nightvision" },
+
+ { "", "" } // this needs to be last
+};
+
+
+struct WeaponAliasInfo
+{
+ CSWeaponID id;
+ const char* alias;
+};
+
+WeaponAliasInfo s_weaponAliasInfo[] =
+{
+ { WEAPON_P228, "p228" },
+ { WEAPON_GLOCK, "glock" },
+ { WEAPON_SCOUT, "scout" },
+ { WEAPON_XM1014, "xm1014" },
+ { WEAPON_MAC10, "mac10" },
+ { WEAPON_AUG, "aug" },
+ { WEAPON_ELITE, "elite" },
+ { WEAPON_FIVESEVEN, "fiveseven" },
+ { WEAPON_UMP45, "ump45" },
+ { WEAPON_SG550, "sg550" },
+ { WEAPON_GALIL, "galil" },
+ { WEAPON_FAMAS, "famas" },
+ { WEAPON_USP, "usp" },
+ { WEAPON_AWP, "awp" },
+ { WEAPON_MP5NAVY, "mp5navy" },
+ { WEAPON_M249, "m249" },
+ { WEAPON_M3, "m3" },
+ { WEAPON_M4A1, "m4a1" },
+ { WEAPON_TMP, "tmp" },
+ { WEAPON_G3SG1, "g3sg1" },
+ { WEAPON_DEAGLE, "deagle" },
+ { WEAPON_SG552, "sg552" },
+ { WEAPON_AK47, "ak47" },
+ { WEAPON_P90, "p90" },
+
+ { WEAPON_KNIFE, "knife" },
+ { WEAPON_C4, "c4" },
+ { WEAPON_FLASHBANG, "flashbang" },
+ { WEAPON_SMOKEGRENADE, "smokegrenade" },
+ { WEAPON_SMOKEGRENADE, "sgren" },
+ { WEAPON_HEGRENADE, "hegrenade" },
+ { WEAPON_HEGRENADE, "hegren" },
+
+ // not sure any of these are needed
+ { WEAPON_SHIELDGUN, "shield" },
+ { WEAPON_SHIELDGUN, "shieldgun" },
+ { WEAPON_KEVLAR, "kevlar" },
+ { WEAPON_ASSAULTSUIT, "assaultsuit" },
+ { WEAPON_NVG, "nightvision" },
+ { WEAPON_NVG, "nvg" },
+
+ { WEAPON_NONE, "none" },
+};
+
+
+bool IsAmmoType( int iAmmoType, const char *pAmmoName )
+{
+ return GetAmmoDef()->Index( pAmmoName ) == iAmmoType;
+}
+
+//--------------------------------------------------------------------------------------------------------
+//
+// Given an alias, return the translated alias.
+//
+const char * GetTranslatedWeaponAlias( const char *szAlias )
+{
+ for ( int i = 0; i < ARRAYSIZE(s_WeaponAliasTranslationInfo); ++i )
+ {
+ if ( Q_stricmp(s_WeaponAliasTranslationInfo[i].alias, szAlias) == 0 )
+ {
+ return s_WeaponAliasTranslationInfo[i].translatedAlias;
+ }
+ }
+
+ return szAlias;
+}
+
+//--------------------------------------------------------------------------------------------------------
+//
+// Given a translated alias, return the alias.
+//
+const char * GetWeaponAliasFromTranslated(const char *translatedAlias)
+{
+ int i = 0;
+ const WeaponAliasTranslationInfoStruct *info = &(s_WeaponAliasTranslationInfo[i]);
+
+ while (info->alias[0] != 0)
+ {
+ if (Q_stricmp(translatedAlias, info->translatedAlias) == 0)
+ {
+ return info->alias;
+ }
+ info = &(s_WeaponAliasTranslationInfo[++i]);
+ }
+
+ return translatedAlias;
+}
+
+//--------------------------------------------------------------------------------------------------------
+//
+// Given an alias, return the associated weapon ID
+//
+CSWeaponID AliasToWeaponID( const char *szAlias )
+{
+ if ( szAlias )
+ {
+ for ( int i=0; i < ARRAYSIZE(s_weaponAliasInfo); ++i)
+ {
+ if ( Q_stricmp( s_weaponAliasInfo[i].alias, szAlias) == 0 )
+ return s_weaponAliasInfo[i].id;
+ }
+ }
+
+ return WEAPON_NONE;
+}
+
+//--------------------------------------------------------------------------------------------------------
+//
+// Given a weapon ID, return its alias
+//
+const char *WeaponIDToAlias( int id )
+{
+ for ( int i=0; i < ARRAYSIZE(s_weaponAliasInfo); ++i)
+ {
+ if ( s_weaponAliasInfo[i].id == id )
+ return s_weaponAliasInfo[i].alias;
+ }
+
+ return NULL;
+}
+
+//--------------------------------------------------------------------------------------------------------
+//
+// Return true if given weapon ID is a primary weapon
+//
+bool IsPrimaryWeapon( CSWeaponID id )
+{
+ const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo( id );
+ if ( pWeaponInfo )
+ {
+ return pWeaponInfo->iSlot == WEAPON_SLOT_RIFLE;
+ }
+
+ return false;
+}
+
+//--------------------------------------------------------------------------------------------------------
+//
+// Return true if given weapon ID is a secondary weapon
+//
+bool IsSecondaryWeapon( CSWeaponID id )
+{
+ const CCSWeaponInfo* pWeaponInfo = GetWeaponInfo( id );
+ if ( pWeaponInfo )
+ return pWeaponInfo->iSlot == WEAPON_SLOT_PISTOL;
+
+ return false;
+}
+
+#ifdef CLIENT_DLL
+int GetShellForAmmoType( const char *ammoname )
+{
+ if ( !Q_strcmp( BULLET_PLAYER_762MM, ammoname ) )
+ return CS_SHELL_762NATO;
+
+ if ( !Q_strcmp( BULLET_PLAYER_556MM, ammoname ) )
+ return CS_SHELL_556;
+
+ if ( !Q_strcmp( BULLET_PLAYER_338MAG, ammoname ) )
+ return CS_SHELL_338MAG;
+
+ if ( !Q_strcmp( BULLET_PLAYER_BUCKSHOT, ammoname ) )
+ return CS_SHELL_12GAUGE;
+
+ if ( !Q_strcmp( BULLET_PLAYER_57MM, ammoname ) )
+ return CS_SHELL_57;
+
+ // default 9 mm
+ return CS_SHELL_9MM;
+}
+#endif
+
+
+// ----------------------------------------------------------------------------- //
+// CWeaponCSBase tables.
+// ----------------------------------------------------------------------------- //
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCSBase, DT_WeaponCSBase )
+
+BEGIN_NETWORK_TABLE( CWeaponCSBase, DT_WeaponCSBase )
+#if !defined( CLIENT_DLL )
+SendPropInt( SENDINFO( m_weaponMode ), 1, SPROP_UNSIGNED ),
+SendPropFloat(SENDINFO(m_fAccuracyPenalty) ),
+// world weapon models have no aminations
+SendPropExclude( "DT_AnimTimeMustBeFirst", "m_flAnimTime" ),
+SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
+// SendPropExclude( "DT_LocalActiveWeaponData", "m_flTimeWeaponIdle" ),
+#else
+RecvPropInt( RECVINFO( m_weaponMode ) ),
+RecvPropFloat( RECVINFO(m_fAccuracyPenalty)),
+#endif
+END_NETWORK_TABLE()
+
+#if defined(CLIENT_DLL)
+BEGIN_PREDICTION_DATA( CWeaponCSBase )
+ DEFINE_PRED_FIELD( m_flTimeWeaponIdle, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
+ DEFINE_PRED_FIELD( m_flNextPrimaryAttack, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
+ DEFINE_PRED_FIELD( m_flNextSecondaryAttack, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
+ DEFINE_PRED_FIELD( m_bDelayFire, FIELD_BOOLEAN, 0 ),
+ DEFINE_PRED_FIELD( m_flAccuracy, FIELD_FLOAT, 0 ),
+ DEFINE_PRED_FIELD( m_weaponMode, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD_TOL( m_fAccuracyPenalty, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, 0.00005f ),
+END_PREDICTION_DATA()
+#endif
+
+
+LINK_ENTITY_TO_CLASS( weapon_cs_base, CWeaponCSBase );
+
+
+#ifdef GAME_DLL
+
+ BEGIN_DATADESC( CWeaponCSBase )
+
+ //DEFINE_FUNCTION( DefaultTouch ),
+ DEFINE_THINKFUNC( FallThink )
+
+ END_DATADESC()
+
+#endif
+
+#if defined( CLIENT_DLL )
+ ConVar cl_crosshaircolor( "cl_crosshaircolor", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Set crosshair color: 0=green, 1=red, 2=blue, 3=yellow, 4=cyan, 5=custom" );
+ ConVar cl_dynamiccrosshair( "cl_dynamiccrosshair", "1", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Enables dynamic crosshair; 0=off, 1=normal behavior (based on actual weapon accuracy), 2=legacy simulated dynamic behavior, 3=legacy simulated static behavior" );
+ ConVar cl_crosshairspreadscale( "cl_crosshairspreadscale", "0.3", FCVAR_CLIENTDLL | FCVAR_ARCHIVE);
+ ConVar cl_scalecrosshair( "cl_scalecrosshair", "1", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Enable crosshair scaling (deprecated)" );
+ ConVar cl_crosshairscale( "cl_crosshairscale", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Crosshair scaling factor (deprecated)" );
+ ConVar cl_crosshairalpha( "cl_crosshairalpha", "200", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
+ ConVar cl_crosshairusealpha( "cl_crosshairusealpha", "1", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
+ ConVar cl_crosshairsize( "cl_crosshairsize", "5", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
+ ConVar cl_crosshairthickness( "cl_crosshairthickness", "0.5", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
+ ConVar cl_crosshairdot( "cl_crosshairdot", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
+ ConVar cl_crosshaircolor_r( "cl_crosshaircolor_r", "50", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
+ ConVar cl_crosshaircolor_g( "cl_crosshaircolor_g", "250", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
+ ConVar cl_crosshaircolor_b( "cl_crosshaircolor_b", "50", FCVAR_CLIENTDLL | FCVAR_ARCHIVE );
+
+#if ALLOW_WEAPON_SPREAD_DISPLAY
+ ConVar weapon_debug_spread_show( "weapon_debug_spread_show", "0", FCVAR_CLIENTDLL | FCVAR_DEVELOPMENTONLY, "Enables display of weapon accuracy; 1: show accuracy box, 2: show box with recoil offset" );
+ ConVar weapon_debug_spread_gap( "weapon_debug_spread_gap", "0.67", FCVAR_CLIENTDLL | FCVAR_DEVELOPMENTONLY );
+#endif
+
+ // [paquin] make sure crosshair scales independent of frame rate
+ // unless legacy cvar is set
+ ConVar cl_legacy_crosshair_recoil( "cl_legacy_crosshair_recoil", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Enable legacy framerate dependent crosshair recoil");
+
+ // use old scaling behavior
+ ConVar cl_legacy_crosshair_scale( "cl_legacy_crosshair_scale", "0", FCVAR_CLIENTDLL | FCVAR_ARCHIVE, "Enable legacy crosshair scaling");
+
+void DrawCrosshairRect( int x0, int y0, int x1, int y1, bool bAdditive )
+{
+ if ( bAdditive )
+ {
+ vgui::surface()->DrawTexturedRect( x0, y0, x1, y1 );
+ }
+ else
+ {
+ // Alpha-blended crosshair
+ vgui::surface()->DrawFilledRect( x0, y0, x1, y1 );
+ }
+}
+
+#endif
+
+// must be included after the above macros
+#ifndef CLIENT_DLL
+ #include "cs_bot.h"
+#endif
+
+
+// ----------------------------------------------------------------------------- //
+// CWeaponCSBase implementation.
+// ----------------------------------------------------------------------------- //
+CWeaponCSBase::CWeaponCSBase()
+{
+ SetPredictionEligible( true );
+ m_bDelayFire = true;
+ m_nextPrevOwnerTouchTime = 0.0;
+ m_prevOwner = NULL;
+ AddSolidFlags( FSOLID_TRIGGER ); // Nothing collides with these but it gets touches.
+
+#ifdef CLIENT_DLL
+ m_iCrosshairTextureID = 0;
+#else
+ m_iDefaultExtraAmmo = 0;
+#endif
+
+ m_fAccuracyPenalty = 0.0f;
+
+ m_weaponMode = Primary_Mode;
+}
+
+
+#ifndef CLIENT_DLL
+bool CWeaponCSBase::KeyValue( const char *szKeyName, const char *szValue )
+{
+ if ( !BaseClass::KeyValue( szKeyName, szValue ) )
+ {
+ if ( FStrEq( szKeyName, "ammo" ) )
+ {
+ int bullets = atoi( szValue );
+ if ( bullets < 0 )
+ return false;
+
+ m_iDefaultExtraAmmo = bullets;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+#endif
+
+
+bool CWeaponCSBase::IsPredicted() const
+{
+ return true;
+}
+
+
+bool CWeaponCSBase::IsPistol() const
+{
+ return GetCSWpnData().m_WeaponType == WEAPONTYPE_PISTOL;
+}
+
+
+bool CWeaponCSBase::IsFullAuto() const
+{
+ return GetCSWpnData().m_bFullAuto;
+}
+
+
+bool CWeaponCSBase::PlayEmptySound()
+{
+ //MIKETODO: certain weapons should override this to make it empty:
+ // C4
+ // Flashbang
+ // HE Grenade
+ // Smoke grenade
+
+ CPASAttenuationFilter filter( this );
+ filter.UsePredictionRules();
+
+ if ( IsPistol() )
+ {
+ EmitSound( filter, entindex(), "Default.ClipEmpty_Pistol" );
+ }
+ else
+ {
+ EmitSound( filter, entindex(), "Default.ClipEmpty_Rifle" );
+ }
+
+ return 0;
+}
+
+CCSPlayer* CWeaponCSBase::GetPlayerOwner() const
+{
+ return dynamic_cast< CCSPlayer* >( GetOwner() );
+}
+
+//=============================================================================
+// HPE_BEGIN:
+//=============================================================================
+
+//[dwenger] Accessors for the prior owner list
+void CWeaponCSBase::AddToPriorOwnerList(CCSPlayer* pPlayer)
+{
+ if ( !IsAPriorOwner( pPlayer ) )
+ {
+ // Add player to prior owner list
+ m_PriorOwners.AddToTail( pPlayer );
+ }
+}
+
+bool CWeaponCSBase::IsAPriorOwner(CCSPlayer* pPlayer)
+{
+ return (m_PriorOwners.Find( pPlayer ) != -1);
+}
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+
+void CWeaponCSBase::SecondaryAttack( void )
+{
+#ifndef CLIENT_DLL
+ CCSPlayer *pPlayer = GetPlayerOwner();
+
+ if ( !pPlayer )
+ return;
+
+ if ( pPlayer->HasShield() == false )
+ BaseClass::SecondaryAttack();
+ else
+ {
+ pPlayer->SetShieldDrawnState( !pPlayer->IsShieldDrawn() );
+
+ if ( pPlayer->IsShieldDrawn() )
+ SendWeaponAnim( ACT_SHIELD_UP );
+ else
+ SendWeaponAnim( ACT_SHIELD_DOWN );
+
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.4;
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.4;
+ }
+#endif
+}
+
+bool CWeaponCSBase::SendWeaponAnim( int iActivity )
+{
+#ifdef CS_SHIELD_ENABLED
+ CCSPlayer *pPlayer = GetPlayerOwner();
+
+ if ( pPlayer && pPlayer->HasShield() )
+ {
+ CBaseViewModel *vm = pPlayer->GetViewModel( 1 );
+
+ if ( vm == NULL )
+ return false;
+
+ vm->SetWeaponModel( SHIELD_VIEW_MODEL, this );
+
+ int idealSequence = vm->SelectWeightedSequence( (Activity)iActivity );
+
+ if ( idealSequence >= 0 )
+ {
+ vm->SendViewModelMatchingSequence( idealSequence );
+ }
+ }
+#endif
+
+ return BaseClass::SendWeaponAnim( iActivity );
+}
+
+
+void CWeaponCSBase::ItemPostFrame()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+
+ if ( !pPlayer )
+ return;
+
+ UpdateAccuracyPenalty();
+
+ UpdateShieldState();
+
+ if ((m_bInReload) && (pPlayer->m_flNextAttack <= gpGlobals->curtime))
+ {
+ // complete the reload.
+ int j = MIN( GetMaxClip1() - m_iClip1, pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) );
+
+ // Add them to the clip
+ m_iClip1 += j;
+ pPlayer->RemoveAmmo( j, m_iPrimaryAmmoType );
+
+ m_bInReload = false;
+ }
+
+ if ((pPlayer->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime))
+ {
+ if ( pPlayer->HasShield() )
+ CWeaponCSBase::SecondaryAttack();
+ else
+ SecondaryAttack();
+
+ pPlayer->m_nButtons &= ~IN_ATTACK2;
+ }
+ else if ((pPlayer->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime ))
+ {
+ if ( CSGameRules()->IsFreezePeriod() ) // Can't shoot during the freeze period
+ return;
+
+ if ( pPlayer->m_bIsDefusing )
+ return;
+
+ if ( pPlayer->State_Get() != STATE_ACTIVE )
+ return;
+
+ if ( pPlayer->IsShieldDrawn() )
+ return;
+
+ // we have to reset the FireOnEmpty flag before we can fire on an empty clip
+ if ( m_iClip1 == 0 && !m_bFireOnEmpty )
+ return;
+
+ // don't repeat fire if this is not a full auto weapon
+ if ( pPlayer->m_iShotsFired > 0 && !IsFullAuto() )
+ return;
+
+#if !defined(CLIENT_DLL)
+ // allow the bots to react to the gunfire
+ if ( GetCSWpnData().m_WeaponType != WEAPONTYPE_GRENADE )
+ {
+ IGameEvent * event = gameeventmanager->CreateEvent( (HasAmmo()) ? "weapon_fire" : "weapon_fire_on_empty" );
+ if( event )
+ {
+ const char *weaponName = STRING( m_iClassname );
+ if ( strncmp( weaponName, "weapon_", 7 ) == 0 )
+ {
+ weaponName += 7;
+ }
+
+ event->SetInt( "userid", pPlayer->GetUserID() );
+ event->SetString( "weapon", weaponName );
+ gameeventmanager->FireEvent( event );
+ }
+ }
+#endif
+ PrimaryAttack();
+ }
+ else if ( pPlayer->m_nButtons & IN_RELOAD && GetMaxClip1() != WEAPON_NOCLIP && !m_bInReload && m_flNextPrimaryAttack < gpGlobals->curtime)
+ {
+ // reload when reload is pressed, or if no buttons are down and weapon is empty.
+
+ //MIKETODO: add code for shields...
+ //if ( !FBitSet( m_iWeaponState, WPNSTATE_SHIELD_DRAWN ) )
+
+ if ( !pPlayer->IsShieldDrawn() )
+ {
+ if ( Reload() )
+ {
+#ifndef CLIENT_DLL
+ // allow the bots to react to the reload
+ IGameEvent * event = gameeventmanager->CreateEvent( "weapon_reload" );
+ if( event )
+ {
+ event->SetInt( "userid", pPlayer->GetUserID() );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+ }
+ }
+ }
+ else if ( !(pPlayer->m_nButtons & (IN_ATTACK|IN_ATTACK2) ) )
+ {
+ if ( weapon_accuracy_model.GetInt() == 2 )
+ {
+ // Fire button not down -- reset the shots fired count
+ if ( pPlayer->m_iShotsFired > 0 && ( !IsFullAuto() || m_iClip1 == 0 ) )
+ {
+ pPlayer->m_iShotsFired = 0;
+ }
+ }
+
+ // The following code prevents the player from tapping the firebutton repeatedly
+ // to simulate full auto and retaining the single shot accuracy of single fire
+ if ( m_bDelayFire )
+ {
+ m_bDelayFire = false;
+
+ if (pPlayer->m_iShotsFired > 15)
+ pPlayer->m_iShotsFired = 15;
+
+ m_flDecreaseShotsFired = gpGlobals->curtime + 0.4;
+ }
+
+ m_bFireOnEmpty = true;
+
+ // if it's a pistol then set the shots fired to 0 after the player releases a button
+ if ( IsPistol() )
+ {
+ pPlayer->m_iShotsFired = 0;
+ }
+ else
+ {
+ if ( (pPlayer->m_iShotsFired > 0) && (m_flDecreaseShotsFired < gpGlobals->curtime) )
+ {
+ m_flDecreaseShotsFired = gpGlobals->curtime + 0.0225;
+ pPlayer->m_iShotsFired--;
+ }
+ }
+
+ if ( (!IsUseable() && m_flNextPrimaryAttack < gpGlobals->curtime) )
+ {
+ // Intentionally blank -- used to switch weapons here
+ }
+ else
+ {
+ // weapon is useable. Reload if empty and weapon has waited as long as it has to after firing
+ if ( m_iClip1 == 0 && !(GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD) && m_flNextPrimaryAttack < gpGlobals->curtime )
+ {
+ Reload();
+ return;
+ }
+ }
+
+ WeaponIdle( );
+ return;
+ }
+}
+
+
+void CWeaponCSBase::ItemBusyFrame()
+{
+ UpdateAccuracyPenalty();
+
+ BaseClass::ItemBusyFrame();
+}
+
+
+float CWeaponCSBase::GetInaccuracy() const
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ const CCSWeaponInfo& weaponInfo = GetCSWpnData();
+
+ float fMaxSpeed = GetMaxSpeed();
+ if ( fMaxSpeed == 0.0f )
+ fMaxSpeed = GetCSWpnData().m_flMaxSpeed;
+
+ return m_fAccuracyPenalty +
+ RemapValClamped(pPlayer->GetAbsVelocity().Length2D(),
+ fMaxSpeed * CS_PLAYER_SPEED_DUCK_MODIFIER,
+ fMaxSpeed * 0.95f, // max out at 95% of run speed to avoid jitter near max speed
+ 0.0f, weaponInfo.m_fInaccuracyMove[m_weaponMode]);
+}
+
+
+float CWeaponCSBase::GetSpread() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ return 0.0f;
+
+ return GetCSWpnData().m_fSpread[m_weaponMode];
+}
+
+
+float CWeaponCSBase::GetMaxSpeed() const
+{
+ // The weapon should have set this in its constructor.
+ float flRet = GetCSWpnData().m_flMaxSpeed;
+ Assert( flRet > 1 );
+ return flRet;
+}
+
+const CCSWeaponInfo &CWeaponCSBase::GetCSWpnData() const
+{
+ const FileWeaponInfo_t *pWeaponInfo = &GetWpnData();
+ const CCSWeaponInfo *pCSInfo;
+
+ #ifdef _DEBUG
+ pCSInfo = dynamic_cast< const CCSWeaponInfo* >( pWeaponInfo );
+ Assert( pCSInfo );
+ #else
+ pCSInfo = static_cast< const CCSWeaponInfo* >( pWeaponInfo );
+ #endif
+
+ return *pCSInfo;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+const char *CWeaponCSBase::GetViewModel( int /*viewmodelindex = 0 -- this is ignored in the base class here*/ ) const
+{
+ CCSPlayer *pOwner = GetPlayerOwner();
+
+ if ( pOwner == NULL )
+ return BaseClass::GetViewModel();
+
+ if ( pOwner->HasShield() && GetCSWpnData().m_bCanUseWithShield )
+ return GetCSWpnData().m_szShieldViewModel;
+ else
+ return GetWpnData().szViewModel;
+
+ return BaseClass::GetViewModel();
+
+}
+
+void CWeaponCSBase::Precache( void )
+{
+ BaseClass::Precache();
+
+#ifdef CS_SHIELD_ENABLED
+ if ( GetCSWpnData().m_bCanUseWithShield )
+ {
+ PrecacheModel( GetCSWpnData().m_szShieldViewModel );
+ }
+#endif
+
+ PrecacheScriptSound( "Default.ClipEmpty_Pistol" );
+ PrecacheScriptSound( "Default.ClipEmpty_Rifle" );
+
+ PrecacheScriptSound( "Default.Zoom" );
+}
+
+Activity CWeaponCSBase::GetDeployActivity( void )
+{
+ return ACT_VM_DRAW;
+}
+
+bool CWeaponCSBase::DefaultDeploy( char *szViewModel, char *szWeaponModel, int iActivity, char *szAnimExt )
+{
+ // Msg( "deploy %s at %f\n", GetClassname(), gpGlobals->curtime );
+ CCSPlayer *pOwner = GetPlayerOwner();
+ if ( !pOwner )
+ {
+ return false;
+ }
+
+ pOwner->SetAnimationExtension( szAnimExt );
+
+ SetViewModel();
+ SendWeaponAnim( GetDeployActivity() );
+
+ pOwner->SetNextAttack( gpGlobals->curtime + SequenceDuration() );
+ m_flNextPrimaryAttack = gpGlobals->curtime;
+ m_flNextSecondaryAttack = gpGlobals->curtime;
+
+ SetWeaponVisible( true );
+ pOwner->SetShieldDrawnState( false );
+
+ if ( pOwner->HasShield() == true )
+ SetWeaponModelIndex( SHIELD_WORLD_MODEL);
+ else
+ SetWeaponModelIndex( szWeaponModel );
+
+ return true;
+}
+
+void CWeaponCSBase::UpdateShieldState( void )
+{
+ //empty by default.
+ CCSPlayer *pOwner = GetPlayerOwner();
+
+ if ( pOwner == NULL )
+ return;
+
+ //ADRIANTODO
+ //Make the hitbox set switches here!!!
+ if ( pOwner->HasShield() == false )
+ {
+
+ pOwner->SetShieldDrawnState( false );
+ //pOwner->SetHitBoxSet( 0 );
+ return;
+ }
+ else
+ {
+ //pOwner->SetHitBoxSet( 1 );
+ }
+}
+
+void CWeaponCSBase::SetWeaponModelIndex( const char *pName )
+{
+ m_iWorldModelIndex = modelinfo->GetModelIndex( pName );
+}
+
+bool CWeaponCSBase::CanBeSelected( void )
+{
+ if ( !VisibleInWeaponSelection() )
+ return false;
+
+ return true;
+}
+
+bool CWeaponCSBase::CanDeploy( void )
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return false;
+
+ if ( pPlayer->HasShield() && GetCSWpnData().m_bCanUseWithShield == false )
+ return false;
+
+ return BaseClass::CanDeploy();
+}
+
+float CWeaponCSBase::CalculateNextAttackTime( float fCycleTime )
+{
+ float fCurAttack = m_flNextPrimaryAttack;
+ float fDeltaAttack = gpGlobals->curtime - fCurAttack;
+ if ( fDeltaAttack < 0 || fDeltaAttack > gpGlobals->interval_per_tick )
+ {
+ fCurAttack = gpGlobals->curtime;
+ }
+ m_flNextSecondaryAttack = m_flNextPrimaryAttack = fCurAttack + fCycleTime;
+
+ return fCurAttack;
+}
+
+bool CWeaponCSBase::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return false;
+
+ if ( pPlayer )
+ pPlayer->SetFOV( pPlayer, 0 ); // reset the default FOV.
+
+ if ( pPlayer )
+ pPlayer->SetShieldDrawnState( false );
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+bool CWeaponCSBase::Deploy()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+
+#ifdef CLIENT_DLL
+ m_iAlpha = 80;
+ if ( pPlayer )
+ {
+ pPlayer->m_iLastZoom = 0;
+ pPlayer->SetFOV( pPlayer, 0 );
+ }
+#else
+
+ m_flDecreaseShotsFired = gpGlobals->curtime;
+
+
+ if ( pPlayer )
+ {
+ pPlayer->m_iShotsFired = 0;
+ pPlayer->m_bResumeZoom = false;
+ pPlayer->m_iLastZoom = 0;
+ pPlayer->SetFOV( pPlayer, 0 );
+ }
+#endif
+
+ m_fAccuracyPenalty = 0.0f;
+
+ return BaseClass::Deploy();
+}
+
+#ifndef CLIENT_DLL
+bool CWeaponCSBase::IsRemoveable()
+{
+ if ( BaseClass::IsRemoveable() == true )
+ {
+ if ( m_nextPrevOwnerTouchTime > gpGlobals->curtime )
+ {
+ return false;
+ }
+ }
+
+ return BaseClass::IsRemoveable();
+}
+#endif
+
+void CWeaponCSBase::Drop(const Vector &vecVelocity)
+{
+
+#ifdef CLIENT_DLL
+ BaseClass::Drop(vecVelocity);
+ return;
+#else
+
+ // Once somebody drops a gun, it's fair game for removal when/if
+ // a game_weapon_manager does a cleanup on surplus weapons in the
+ // world.
+ SetRemoveable( true );
+
+ StopAnimation();
+ StopFollowingEntity( );
+ SetMoveType( MOVETYPE_FLYGRAVITY );
+ // clear follow stuff, setup for collision
+ SetGravity(1.0);
+ m_iState = WEAPON_NOT_CARRIED;
+ RemoveEffects( EF_NODRAW );
+ FallInit();
+ SetGroundEntity( NULL );
+
+ m_bInReload = false; // stop reloading
+
+ SetThink( NULL );
+ m_nextPrevOwnerTouchTime = gpGlobals->curtime + 0.8f;
+ m_prevOwner = GetPlayerOwner();
+
+ SetTouch(&CWeaponCSBase::DefaultTouch);
+
+ IPhysicsObject *pObj = VPhysicsGetObject();
+ if ( pObj != NULL )
+ {
+ AngularImpulse angImp( 200, 200, 200 );
+ pObj->AddVelocity( &vecVelocity, &angImp );
+ }
+ else
+ {
+ SetAbsVelocity( vecVelocity );
+ }
+
+ SetNextThink( gpGlobals->curtime );
+
+ SetOwnerEntity( NULL );
+ SetOwner( NULL );
+#endif
+}
+
+// whats going on here is that if the player drops this weapon, they shouldn't take it back themselves
+// for a little while. But if they throw it at someone else, the other player should get it immediately.
+void CWeaponCSBase::DefaultTouch(CBaseEntity *pOther)
+{
+ if ((m_prevOwner != NULL) && (pOther == m_prevOwner) && (gpGlobals->curtime < m_nextPrevOwnerTouchTime))
+ {
+ return;
+ }
+
+ BaseClass::DefaultTouch(pOther);
+}
+
+#if defined( CLIENT_DLL )
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Draw the weapon's crosshair
+ //-----------------------------------------------------------------------------
+ void CWeaponCSBase::DrawCrosshair()
+ {
+ if ( !crosshair.GetInt() )
+ return;
+
+ CHudCrosshair *pCrosshair = GET_HUDELEMENT( CHudCrosshair );
+
+ if ( !pCrosshair )
+ return;
+
+ // clear crosshair
+ pCrosshair->SetCrosshair( 0, Color( 255, 255, 255, 255 ) );
+
+ CCSPlayer* pPlayer = (CCSPlayer*)C_BasePlayer::GetLocalPlayer();
+
+ if ( !pPlayer )
+ return;
+
+ // localplayer must be owner if not in Spec mode
+ Assert( (pPlayer == GetPlayerOwner()) || ( pPlayer->GetObserverMode()==OBS_MODE_IN_EYE) );
+
+ // Draw the targeting zone around the pCrosshair
+ if ( pPlayer->IsInVGuiInputMode() )
+ return;
+
+ int r, g, b;
+ switch ( cl_crosshaircolor.GetInt() )
+ {
+ case 0 : r = 50; g = 250; b = 50; break;
+ case 1 : r = 250; g = 50; b = 50; break;
+ case 2 : r = 50; g = 50; b = 250; break;
+ case 3 : r = 250; g = 250; b = 50; break;
+ case 4 : r = 50; g = 250; b = 250; break;
+ case 5 :
+ r = cl_crosshaircolor_r.GetInt();
+ g = cl_crosshaircolor_g.GetInt();
+ b = cl_crosshaircolor_b.GetInt();
+ break;
+ default : r = 50; g = 250; b = 50; break;
+ }
+
+ // if user is using nightvision, make the crosshair red.
+ if (pPlayer->m_bNightVisionOn)
+ {
+ r = 250;
+ g = 50;
+ b = 50;
+ }
+
+ int alpha = clamp( cl_crosshairalpha.GetInt(), 0, 255 );
+ vgui::surface()->DrawSetColor( r, g, b, alpha );
+
+ if ( !m_iCrosshairTextureID )
+ {
+ CHudTexture *pTexture = gHUD.GetIcon( "whiteAdditive" );
+ if ( pTexture )
+ {
+ m_iCrosshairTextureID = pTexture->textureId;
+ }
+ }
+
+ bool bAdditive = !cl_crosshairusealpha.GetBool() && !pPlayer->m_bNightVisionOn;
+ if ( bAdditive )
+ {
+ vgui::surface()->DrawSetColor( r, g, b, 200 );
+ vgui::surface()->DrawSetTexture( m_iCrosshairTextureID );
+ }
+
+ if ( pPlayer->HasShield() && pPlayer->IsShieldDrawn() == true )
+ return;
+
+ // no crosshair for sniper rifles
+ bool bCrosshairVisible = crosshair.GetBool() && GetCSWpnData().m_WeaponType != WEAPONTYPE_SNIPER_RIFLE;
+
+ if ( !bCrosshairVisible
+#if ALLOW_WEAPON_SPREAD_DISPLAY
+ && !weapon_debug_spread_show.GetBool()
+#endif
+ )
+ return;
+
+ float fHalfFov = DEG2RAD(pPlayer->GetFOV()) * 0.5f;
+
+ int iCrosshairDistance;
+ int iBarSize = RoundFloatToInt(YRES(cl_crosshairsize.GetFloat()));
+ int iBarThickness = MAX( 1, RoundFloatToInt(YRES(cl_crosshairthickness.GetFloat())));
+
+ switch ( cl_dynamiccrosshair.GetInt() )
+ {
+ case 0:
+ default:
+ {
+ // static crosshair
+ float fSpread = (GetCSWpnData().m_fSpread[m_weaponMode] + GetCSWpnData().m_fInaccuracyStand[m_weaponMode]) * 320.0f / tanf(fHalfFov);
+ iCrosshairDistance = MAX( 0, RoundFloatToInt( YRES( fSpread * cl_crosshairspreadscale.GetFloat() ) ) );
+ }
+ break;
+
+ case 1:
+ {
+ float fSpread = (GetInaccuracy() + GetSpread()) * 320.0f / tanf(fHalfFov);
+ iCrosshairDistance = MAX( 0, RoundFloatToInt( YRES( fSpread * cl_crosshairspreadscale.GetFloat() ) ) );
+ }
+ break;
+
+ case 2:
+ case 3:
+ {
+ float fCrosshairDistanceGoal = GetCSWpnData().m_iCrosshairMinDistance; // The minimum distance the crosshair can achieve...
+
+ // legacy dynamic crosshair
+ if ( cl_dynamiccrosshair.GetInt() == 2 )
+ {
+ if ( !( pPlayer->GetFlags() & FL_ONGROUND ) )
+ fCrosshairDistanceGoal *= 2.0f;
+ else if ( pPlayer->GetFlags() & FL_DUCKING )
+ fCrosshairDistanceGoal *= 0.5f;
+ else if ( pPlayer->GetAbsVelocity().Length() > 100 )
+ fCrosshairDistanceGoal *= 1.5f;
+ }
+
+ // [jpaquin] changed to only bump up the crosshair size if the player is still shooting or is spectating someone else
+ int iDeltaDistance = GetCSWpnData().m_iCrosshairDeltaDistance; // Amount by which the crosshair expands when shooting (per frame)
+ if ( pPlayer->m_iShotsFired > m_iAmmoLastCheck && (pPlayer->m_nButtons & (IN_ATTACK|IN_ATTACK2)) )
+ fCrosshairDistanceGoal += iDeltaDistance;
+
+ m_iAmmoLastCheck = pPlayer->m_iShotsFired;
+
+ if ( m_flCrosshairDistance > fCrosshairDistanceGoal )
+ {
+ // [jpaquin] if we're not in legacy crosshair mode, use an exponential decay function so
+ // that the crosshair shrinks at the same rate regardless of the frame rate
+ if ( !cl_legacy_crosshair_recoil.GetBool() )
+ {
+ // .44888 on the next line makes the decay very close to what old method produces at 100fps.
+ m_flCrosshairDistance = Lerp(expf(-gpGlobals->frametime / 0.44888f), fCrosshairDistanceGoal, m_flCrosshairDistance);
+ }
+ else
+ {
+ m_flCrosshairDistance -= 0.1f + m_flCrosshairDistance * 0.013;
+ }
+ }
+
+ // clamp max crosshair expansion
+ m_flCrosshairDistance = clamp(m_flCrosshairDistance, fCrosshairDistanceGoal, 25.0f);
+
+ if ( cl_legacy_crosshair_scale.GetBool() )
+ {
+ //scale bar size to the resolution
+ int crosshairScale = cl_crosshairscale.GetInt();
+ if ( crosshairScale < 1 )
+ {
+ if ( ScreenHeight() <= 600 )
+ {
+ crosshairScale = 600;
+ }
+ else if ( ScreenHeight() <= 768 )
+ {
+ crosshairScale = 768;
+ }
+ else
+ {
+ crosshairScale = 1200;
+ }
+ }
+
+ float scale;
+ if( cl_scalecrosshair.GetBool() == false )
+ {
+ scale = 1.0f;
+ }
+ else
+ {
+ scale = (float)ScreenHeight() / (float)crosshairScale;
+ }
+
+ // calculate the inner distance of the crosshair in current screen units
+ iCrosshairDistance = (int)ceil( m_flCrosshairDistance * scale );
+
+ iBarSize = XRES(5); // + (iCrosshairDistance - fCrosshairDistanceGoal) / 2;
+ iBarSize = MAX( 1, (int)( (float)iBarSize * scale ) );
+ iBarThickness = MAX( 1, (int)floor( scale + 0.5f ) );
+ }
+ else
+ {
+ iCrosshairDistance = RoundFloatToInt(m_flCrosshairDistance * ScreenHeight() / 1200.0f);
+ }
+ }
+ break;
+ }
+
+ int iCenterX = ScreenWidth() / 2;
+ int iCenterY = ScreenHeight() / 2;
+
+ if ( bCrosshairVisible )
+ {
+ // draw horizontal crosshair lines
+ int iInnerLeft = iCenterX - iCrosshairDistance - iBarThickness / 2;
+ int iInnerRight = iInnerLeft + 2 * iCrosshairDistance + iBarThickness;
+ int iOuterLeft = iInnerLeft - iBarSize;
+ int iOuterRight = iInnerRight + iBarSize;
+ int y0 = iCenterY - iBarThickness / 2;
+ int y1 = y0 + iBarThickness;
+ DrawCrosshairRect( iOuterLeft, y0, iInnerLeft, y1, bAdditive );
+ DrawCrosshairRect( iInnerRight, y0, iOuterRight, y1, bAdditive );
+
+ // draw vertical crosshair lines
+ int iInnerTop = iCenterY - iCrosshairDistance - iBarThickness / 2;
+ int iInnerBottom = iInnerTop + 2 * iCrosshairDistance + iBarThickness;
+ int iOuterTop = iInnerTop - iBarSize;
+ int iOuterBottom = iInnerBottom + iBarSize;
+ int x0 = iCenterX - iBarThickness / 2;
+ int x1 = x0 + iBarThickness;
+ DrawCrosshairRect( x0, iOuterTop, x1, iInnerTop, bAdditive );
+ DrawCrosshairRect( x0, iInnerBottom, x1, iOuterBottom, bAdditive );
+
+ // draw dot
+ if ( cl_crosshairdot.GetBool() )
+ {
+ int x0 = iCenterX - iBarThickness / 2;
+ int x1 = x0 + iBarThickness;
+ int y0 = iCenterY - iBarThickness / 2;
+ int y1 = y0 + iBarThickness;
+ DrawCrosshairRect( x0, y0, x1, y1, bAdditive );
+ }
+ }
+
+#if ALLOW_WEAPON_SPREAD_DISPLAY
+ // show accuracy brackets
+ if ( weapon_debug_spread_show.GetInt() == 1 || weapon_debug_spread_show.GetInt() == 2 )
+ {
+ if ( weapon_debug_spread_show.GetInt() == 2 )
+ {
+ const QAngle& punchAngles = pPlayer->GetPunchAngle();
+ Vector vecDirShooting;
+ AngleVectors( punchAngles, &vecDirShooting );
+
+ float iOffsetX = RoundFloatToInt(YRES(vecDirShooting.y * 320.0f / tanf(fHalfFov)));
+ float iOffsetY = RoundFloatToInt(YRES(vecDirShooting.z * 320.0f / tanf(fHalfFov)));
+
+ iCenterX -= iOffsetX;
+ iCenterY -= iOffsetY;
+ }
+
+ // colors
+ r = 250;
+ g = 250;
+ b = 50;
+ vgui::surface()->DrawSetColor( r, g, b, alpha );
+
+ int iBarThickness = MAX( 1, RoundFloatToInt(YRES(cl_crosshairthickness.GetFloat())));
+
+ float fSpreadDistance = (GetInaccuracy() + GetSpread()) * 320.0f / tanf(fHalfFov);
+ int iSpreadDistance = RoundFloatToInt(YRES(fSpreadDistance));
+
+ // draw vertical spread lines
+ int iInnerLeft = iCenterX - iSpreadDistance;
+ int iInnerRight = iCenterX + iSpreadDistance;
+ int iOuterLeft = iInnerLeft - iBarThickness;
+ int iOuterRight = iInnerRight + iBarThickness;
+ int iInnerTop = iCenterY - iSpreadDistance;
+ int iInnerBottom = iCenterY + iSpreadDistance;
+ int iOuterTop = iInnerTop - iBarThickness;
+ int iOuterBottom = iInnerBottom + iBarThickness;
+
+ int iGap = RoundFloatToInt(weapon_debug_spread_gap.GetFloat() * iSpreadDistance);
+
+ // draw horizontal lines
+ DrawCrosshairRect( iOuterLeft, iOuterTop, iCenterX - iGap, iInnerTop, bAdditive );
+ DrawCrosshairRect( iCenterX + iGap, iOuterTop, iOuterRight, iInnerTop, bAdditive );
+ DrawCrosshairRect( iOuterLeft, iInnerBottom, iCenterX - iGap, iOuterBottom, bAdditive );
+ DrawCrosshairRect( iCenterX + iGap, iInnerBottom, iOuterRight, iOuterBottom, bAdditive );
+
+ // draw vertical lines
+ DrawCrosshairRect( iOuterLeft, iOuterTop, iInnerLeft, iCenterY - iGap, bAdditive );
+ DrawCrosshairRect( iOuterLeft, iCenterY + iGap, iInnerLeft, iOuterBottom, bAdditive );
+ DrawCrosshairRect( iInnerRight, iOuterTop, iOuterRight, iCenterY - iGap, bAdditive );
+ DrawCrosshairRect( iInnerRight, iCenterY + iGap, iOuterRight, iOuterBottom, bAdditive );
+ }
+#endif
+ }
+
+ void CWeaponCSBase::OnDataChanged( DataUpdateType_t type )
+ {
+ BaseClass::OnDataChanged( type );
+
+ if ( GetPredictable() && !ShouldPredict() )
+ ShutdownPredictable();
+ }
+
+
+ bool CWeaponCSBase::ShouldPredict()
+ {
+ if ( GetOwner() && GetOwner() == C_BasePlayer::GetLocalPlayer() )
+ return true;
+
+ return BaseClass::ShouldPredict();
+ }
+
+ void CWeaponCSBase::ProcessMuzzleFlashEvent()
+ {
+ // This is handled from the player's animstate, so it can match up to the beginning of the fire animation
+ }
+
+
+ bool CWeaponCSBase::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
+ {
+ if( event == 5001 )
+ {
+ C_CSPlayer *pPlayer = ToCSPlayer( GetOwner() );
+ if( pPlayer && pPlayer->GetFOV() < pPlayer->GetDefaultFOV() && HideViewModelWhenZoomed() )
+ return true;
+
+ CEffectData data;
+ data.m_fFlags = 0;
+ data.m_hEntity = pViewModel->GetRefEHandle();
+ data.m_nAttachmentIndex = 1;
+ data.m_flScale = GetCSWpnData().m_flMuzzleScale;
+
+ switch( GetMuzzleFlashStyle() )
+ {
+ case CS_MUZZLEFLASH_NONE:
+ break;
+
+ case CS_MUZZLEFLASH_X:
+ {
+ DispatchEffect( "CS_MuzzleFlash_X", data );
+ }
+ break;
+
+ case CS_MUZZLEFLASH_NORM:
+ default:
+ {
+ DispatchEffect( "CS_MuzzleFlash", data );
+ }
+ break;
+ }
+
+ return true;
+ }
+
+ return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options );
+ }
+
+ int CWeaponCSBase::GetMuzzleFlashStyle( void )
+ {
+ return GetCSWpnData().m_iMuzzleFlashStyle;
+ }
+
+ int CWeaponCSBase::GetMuzzleAttachment( void )
+ {
+ return LookupAttachment( "muzzle_flash" );
+ }
+
+#else
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Get the accuracy derived from weapon and player, and return it
+ //-----------------------------------------------------------------------------
+ const Vector& CWeaponCSBase::GetBulletSpread()
+ {
+ static Vector cone = VECTOR_CONE_8DEGREES;
+ return cone;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Match the anim speed to the weapon speed while crouching
+ //-----------------------------------------------------------------------------
+ float CWeaponCSBase::GetDefaultAnimSpeed()
+ {
+ return 1.0;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose: Draw the laser rifle effect
+ //-----------------------------------------------------------------------------
+ void CWeaponCSBase::BulletWasFired( const Vector &vecStart, const Vector &vecEnd )
+ {
+ }
+
+
+ bool CWeaponCSBase::ShouldRemoveOnRoundRestart()
+ {
+ if ( GetPlayerOwner() )
+ return false;
+ else
+ return true;
+ }
+
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [dwenger] Handle round restart processing for the weapon.
+ //=============================================================================
+
+ void CWeaponCSBase::OnRoundRestart()
+ {
+ // Clear out the list of prior owners
+ m_PriorOwners.RemoveAll();
+ }
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ //=========================================================
+ // Materialize - make a CWeaponCSBase visible and tangible
+ //=========================================================
+ void CWeaponCSBase::Materialize()
+ {
+ if ( IsEffectActive( EF_NODRAW ) )
+ {
+ // changing from invisible state to visible.
+ RemoveEffects( EF_NODRAW );
+ DoMuzzleFlash();
+ }
+
+ AddSolidFlags( FSOLID_TRIGGER );
+
+ //SetTouch( &CWeaponCSBase::DefaultTouch );
+
+ SetThink( NULL );
+
+ }
+
+ //=========================================================
+ // AttemptToMaterialize - the item is trying to rematerialize,
+ // should it do so now or wait longer?
+ //=========================================================
+ void CWeaponCSBase::AttemptToMaterialize()
+ {
+ float time = g_pGameRules->FlWeaponTryRespawn( this );
+
+ if ( time == 0 )
+ {
+ Materialize();
+ return;
+ }
+
+ SetNextThink( gpGlobals->curtime + time );
+ }
+
+ //=========================================================
+ // CheckRespawn - a player is taking this weapon, should
+ // it respawn?
+ //=========================================================
+ void CWeaponCSBase::CheckRespawn()
+ {
+ //GOOSEMAN : Do not respawn weapons!
+ return;
+ }
+
+
+ //=========================================================
+ // Respawn- this item is already in the world, but it is
+ // invisible and intangible. Make it visible and tangible.
+ //=========================================================
+ CBaseEntity* CWeaponCSBase::Respawn()
+ {
+ // make a copy of this weapon that is invisible and inaccessible to players (no touch function). The weapon spawn/respawn code
+ // will decide when to make the weapon visible and touchable.
+ CBaseEntity *pNewWeapon = CBaseEntity::Create( GetClassname(), g_pGameRules->VecWeaponRespawnSpot( this ), GetAbsAngles(), GetOwner() );
+
+ if ( pNewWeapon )
+ {
+ pNewWeapon->AddEffects( EF_NODRAW );// invisible for now
+ pNewWeapon->SetTouch( NULL );// no touch
+ pNewWeapon->SetThink( &CWeaponCSBase::AttemptToMaterialize );
+
+ UTIL_DropToFloor( this, MASK_SOLID );
+
+ // not a typo! We want to know when the weapon the player just picked up should respawn! This new entity we created is the replacement,
+ // but when it should respawn is based on conditions belonging to the weapon that was taken.
+ pNewWeapon->SetNextThink( gpGlobals->curtime + g_pGameRules->FlWeaponRespawnTime( this ) );
+ }
+ else
+ {
+ Msg( "Respawn failed to create %s!\n", GetClassname() );
+ }
+
+ return pNewWeapon;
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ //-----------------------------------------------------------------------------
+ void CWeaponCSBase::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+ {
+ CBasePlayer *pPlayer = ToBasePlayer( pActivator );
+
+ if ( pPlayer )
+ {
+ m_OnPlayerUse.FireOutput( pActivator, pCaller );
+ }
+ }
+
+ bool CWeaponCSBase::Reload()
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return false;
+
+ pPlayer->m_iShotsFired = 0;
+
+ bool retval = BaseClass::Reload();
+
+ return retval;
+ }
+
+ void CWeaponCSBase::Spawn()
+ {
+ BaseClass::Spawn();
+
+ // Override the bloat that our base class sets as it's a little bit bigger than we want.
+ // If it's too big, you drop a weapon and its box is so big that you're still touching it
+ // when it falls and you pick it up again right away.
+ CollisionProp()->UseTriggerBounds( true, 30 );
+
+ // Set this here to allow players to shoot dropped weapons
+ SetCollisionGroup( COLLISION_GROUP_WEAPON );
+
+ SetExtraAmmoCount( m_iDefaultExtraAmmo ); //Start with no additional ammo
+
+ m_nextPrevOwnerTouchTime = 0.0;
+ m_prevOwner = NULL;
+
+ //=============================================================================
+ // HPE_BEGIN:
+ //=============================================================================
+
+ // [tj] initialize donor of this weapon
+ m_donor = NULL;
+ m_donated = false;
+
+ m_weaponMode = Primary_Mode;
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ }
+
+ bool CWeaponCSBase::DefaultReload( int iClipSize1, int iClipSize2, int iActivity )
+ {
+ if ( BaseClass::DefaultReload( iClipSize1, iClipSize2, iActivity ) )
+ {
+ SendReloadEvents();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ void CWeaponCSBase::SendReloadEvents()
+ {
+ CCSPlayer *pPlayer = dynamic_cast< CCSPlayer* >( GetOwner() );
+ if ( !pPlayer )
+ return;
+
+ // Send a message to any clients that have this entity to play the reload.
+ CPASFilter filter( pPlayer->GetAbsOrigin() );
+ filter.RemoveRecipient( pPlayer );
+
+ UserMessageBegin( filter, "ReloadEffect" );
+ WRITE_SHORT( pPlayer->entindex() );
+ MessageEnd();
+
+ // Make the player play his reload animation.
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD );
+ }
+
+#endif
+
+
+bool CWeaponCSBase::DefaultPistolReload()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return false;
+
+ if (pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) <= 0)
+ return true;
+
+ if ( !DefaultReload( GetCSWpnData().iDefaultClip1, 0, ACT_VM_RELOAD ) )
+ return false;
+
+ pPlayer->m_iShotsFired = 0;
+
+ return true;
+}
+
+bool CWeaponCSBase::IsUseable()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return false;
+
+ if ( Clip1() <= 0 )
+ {
+ if ( pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) <= 0 && GetMaxClip1() != -1 )
+ {
+ // clip is empty (or nonexistant) and the player has no more ammo of this type.
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+#if defined( CLIENT_DLL )
+
+ float g_lateralBob = 0;
+ float g_verticalBob = 0;
+
+ static ConVar cl_bobcycle( "cl_bobcycle","0.8", FCVAR_CHEAT );
+ static ConVar cl_bob( "cl_bob","0.002", FCVAR_CHEAT );
+ static ConVar cl_bobup( "cl_bobup","0.5", FCVAR_CHEAT );
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ // Output : float
+ //-----------------------------------------------------------------------------
+ float CWeaponCSBase::CalcViewmodelBob( void )
+ {
+ static float bobtime;
+ static float lastbobtime;
+ static float lastspeed;
+ float cycle;
+
+ CBasePlayer *player = ToBasePlayer( GetOwner() );
+ //Assert( player );
+
+ //NOTENOTE: For now, let this cycle continue when in the air, because it snaps badly without it
+
+ if ( ( !gpGlobals->frametime ) ||
+ ( player == NULL ) ||
+ ( cl_bobcycle.GetFloat() <= 0.0f ) ||
+ ( cl_bobup.GetFloat() <= 0.0f ) ||
+ ( cl_bobup.GetFloat() >= 1.0f ) )
+ {
+ //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!)
+ return 0.0f;// just use old value
+ }
+
+ //Find the speed of the player
+ float speed = player->GetLocalVelocity().Length2D();
+ float flmaxSpeedDelta = MAX( 0, (gpGlobals->curtime - lastbobtime) * 320.0f );
+
+ // don't allow too big speed changes
+ speed = clamp( speed, lastspeed-flmaxSpeedDelta, lastspeed+flmaxSpeedDelta );
+ speed = clamp( speed, -320, 320 );
+
+ lastspeed = speed;
+
+ //FIXME: This maximum speed value must come from the server.
+ // MaxSpeed() is not sufficient for dealing with sprinting - jdw
+
+
+
+ float bob_offset = RemapVal( speed, 0, 320, 0.0f, 1.0f );
+
+ bobtime += ( gpGlobals->curtime - lastbobtime ) * bob_offset;
+ lastbobtime = gpGlobals->curtime;
+
+ //Calculate the vertical bob
+ cycle = bobtime - (int)(bobtime/cl_bobcycle.GetFloat())*cl_bobcycle.GetFloat();
+ cycle /= cl_bobcycle.GetFloat();
+
+ if ( cycle < cl_bobup.GetFloat() )
+ {
+ cycle = M_PI * cycle / cl_bobup.GetFloat();
+ }
+ else
+ {
+ cycle = M_PI + M_PI*(cycle-cl_bobup.GetFloat())/(1.0 - cl_bobup.GetFloat());
+ }
+
+ g_verticalBob = speed*0.005f;
+ g_verticalBob = g_verticalBob*0.3 + g_verticalBob*0.7*sin(cycle);
+
+ g_verticalBob = clamp( g_verticalBob, -7.0f, 4.0f );
+
+ //Calculate the lateral bob
+ cycle = bobtime - (int)(bobtime/cl_bobcycle.GetFloat()*2)*cl_bobcycle.GetFloat()*2;
+ cycle /= cl_bobcycle.GetFloat()*2;
+
+ if ( cycle < cl_bobup.GetFloat() )
+ {
+ cycle = M_PI * cycle / cl_bobup.GetFloat();
+ }
+ else
+ {
+ cycle = M_PI + M_PI*(cycle-cl_bobup.GetFloat())/(1.0 - cl_bobup.GetFloat());
+ }
+
+ g_lateralBob = speed*0.005f;
+ g_lateralBob = g_lateralBob*0.3 + g_lateralBob*0.7*sin(cycle);
+ g_lateralBob = clamp( g_lateralBob, -7.0f, 4.0f );
+
+ //NOTENOTE: We don't use this return value in our case (need to restructure the calculation function setup!)
+ return 0.0f;
+
+ }
+
+ //-----------------------------------------------------------------------------
+ // Purpose:
+ // Input : &origin -
+ // &angles -
+ // viewmodelindex -
+ //-----------------------------------------------------------------------------
+ void CWeaponCSBase::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles )
+ {
+ Vector forward, right;
+ AngleVectors( angles, &forward, &right, NULL );
+
+ CalcViewmodelBob();
+
+ // Apply bob, but scaled down to 40%
+ VectorMA( origin, g_verticalBob * 0.4f, forward, origin );
+
+ // Z bob a bit more
+ origin[2] += g_verticalBob * 0.1f;
+
+ // bob the angles
+ angles[ ROLL ] += g_verticalBob * 0.5f;
+ angles[ PITCH ] -= g_verticalBob * 0.4f;
+
+ angles[ YAW ] -= g_lateralBob * 0.3f;
+
+ // VectorMA( origin, g_lateralBob * 0.2f, right, origin );
+ }
+
+#else
+
+ void CWeaponCSBase::AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles )
+ {
+
+ }
+
+ float CWeaponCSBase::CalcViewmodelBob( void )
+ {
+ return 0.0f;
+ }
+
+#endif
+
+#ifndef CLIENT_DLL
+bool CWeaponCSBase::PhysicsSplash( const Vector &centerPoint, const Vector &normal, float rawSpeed, float scaledSpeed )
+{
+ if ( rawSpeed > 20 )
+ {
+
+ float size = 4.0f;
+ if ( !IsPistol() )
+ size += 2.0f;
+
+ // adjust splash size based on speed
+ size += RemapValClamped( rawSpeed, 0, 400, 0, 3 );
+
+ CEffectData data;
+ data.m_vOrigin = centerPoint;
+ data.m_vNormal = normal;
+ data.m_flScale = random->RandomFloat( size, size + 1.0f );
+
+ if ( GetWaterType() & CONTENTS_SLIME )
+ {
+ data.m_fFlags |= FX_WATER_IN_SLIME;
+ }
+
+ DispatchEffect( "gunshotsplash", data );
+
+ return true;
+ }
+
+ return false;
+}
+#endif // !CLIENT_DLL
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pPicker -
+//-----------------------------------------------------------------------------
+void CWeaponCSBase::OnPickedUp( CBaseCombatCharacter *pNewOwner )
+{
+#if !defined( CLIENT_DLL )
+ RemoveEffects( EF_ITEM_BLINK );
+
+ if( pNewOwner->IsPlayer() && pNewOwner->IsAlive() )
+ {
+ m_OnPlayerPickup.FireOutput(pNewOwner, this);
+
+ // Play the pickup sound for 1st-person observers
+ CRecipientFilter filter;
+ for ( int i=0; i<gpGlobals->maxClients; ++i )
+ {
+ CBasePlayer *player = UTIL_PlayerByIndex(i);
+ if ( player && !player->IsAlive() && player->GetObserverMode() == OBS_MODE_IN_EYE )
+ {
+ filter.AddRecipient( player );
+ }
+ }
+ if ( filter.GetRecipientCount() )
+ {
+ CBaseEntity::EmitSound( filter, pNewOwner->entindex(), "Player.PickupWeapon" );
+ }
+
+ // Robin: We don't want to delete weapons the player has picked up, so
+ // clear the name of the weapon. This prevents wildcards that are meant
+ // to find NPCs finding weapons dropped by the NPCs as well.
+ SetName( NULL_STRING );
+ }
+
+ // Someone picked me up, so make it so that I can't be removed.
+ SetRemoveable( false );
+#endif
+}
+
+
+void CWeaponCSBase::UpdateAccuracyPenalty()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ const CCSWeaponInfo& weaponInfo = GetCSWpnData();
+
+ float fNewPenalty = 0.0f;
+
+ // on ladder?
+ if ( pPlayer->GetMoveType() == MOVETYPE_LADDER )
+ {
+ fNewPenalty += weaponInfo.m_fInaccuracyStand[m_weaponMode] + weaponInfo.m_fInaccuracyLadder[m_weaponMode];
+ }
+ // in the air?
+// else if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+// {
+// fNewPenalty += weaponInfo.m_fInaccuracyStand[m_weaponMode] + weaponInfo.m_fInaccuracyJump[m_weaponMode];
+// }
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING) )
+ {
+ fNewPenalty += weaponInfo.m_fInaccuracyCrouch[m_weaponMode];
+ }
+ else
+ {
+ fNewPenalty += weaponInfo.m_fInaccuracyStand[m_weaponMode];
+ }
+
+ if ( m_bInReload )
+ {
+ fNewPenalty += weaponInfo.m_fInaccuracyReload;
+ }
+
+ if ( fNewPenalty > m_fAccuracyPenalty )
+ {
+ m_fAccuracyPenalty = fNewPenalty;
+ }
+ else
+ {
+ float fDecayFactor;
+
+ if ( pPlayer->GetMoveType() == MOVETYPE_LADDER )
+ {
+ fDecayFactor = logf(10.0f) / weaponInfo.m_fRecoveryTimeStand;
+ }
+ else if ( !FBitSet(pPlayer->GetFlags(), FL_ONGROUND) ) // in air
+ {
+ // enforce a large recovery speed penalty (300%) for players in the air; this helps to provide
+ // comparable in-air accuracy to the old weapon model
+ fDecayFactor = logf(10.0f) / (weaponInfo.m_fRecoveryTimeCrouch * 3.0f);
+ }
+ else if ( FBitSet(pPlayer->GetFlags(), FL_DUCKING) )
+ {
+ fDecayFactor = logf(10.0f) / weaponInfo.m_fRecoveryTimeCrouch;
+ }
+ else
+ {
+ fDecayFactor = logf(10.0f) / weaponInfo.m_fRecoveryTimeStand;
+ }
+ m_fAccuracyPenalty = Lerp(expf(TICK_INTERVAL * -fDecayFactor), fNewPenalty, (float)m_fAccuracyPenalty);
+ }
+}
+
+
+const float kJumpVelocity = sqrtf(2.0f * 800.0f * 57.0f); // see CCSGameMovement::CheckJumpButton()
+
+void CWeaponCSBase::OnJump( float fImpulse )
+{
+ m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyJump[m_weaponMode] * fImpulse / kJumpVelocity;
+}
+
+void CWeaponCSBase::OnLand( float fVelocity )
+{
+ float fPenalty = GetCSWpnData().m_fInaccuracyLand[m_weaponMode] * fVelocity / kJumpVelocity;
+ m_fAccuracyPenalty += fPenalty;
+
+/*
+ // this bit of code is only if we want to punch the player view on all landings
+
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ QAngle angle = pPlayer->GetPunchAngle();
+ float fVKick = RAD2DEG(asinf(fPenalty)) * 0.4f;
+ float fHKick = SharedRandomFloat("LandPunchAngleYaw", -1.0f, +1.0f) * fVKick * 0.1f;
+
+ angle.x += fVKick; // pitch
+ angle.y += fHKick; // yaw
+ pPlayer->SetPunchAngle( angle );
+*/
+}
diff --git a/game/shared/cstrike/weapon_csbase.h b/game/shared/cstrike/weapon_csbase.h
new file mode 100644
index 0000000..9a81b61
--- /dev/null
+++ b/game/shared/cstrike/weapon_csbase.h
@@ -0,0 +1,289 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef WEAPON_CSBASE_H
+#define WEAPON_CSBASE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "cs_playeranimstate.h"
+#include "cs_weapon_parse.h"
+
+
+#if defined( CLIENT_DLL )
+ #define CWeaponCSBase C_WeaponCSBase
+#endif
+
+extern CSWeaponID AliasToWeaponID( const char *alias );
+extern const char *WeaponIDToAlias( int id );
+extern const char *GetTranslatedWeaponAlias( const char *alias);
+extern const char * GetWeaponAliasFromTranslated(const char *translatedAlias);
+extern bool IsPrimaryWeapon( CSWeaponID id );
+extern bool IsSecondaryWeapon( CSWeaponID id );
+extern int GetShellForAmmoType( const char *ammoname );
+
+#define SHIELD_VIEW_MODEL "models/weapons/v_shield.mdl"
+#define SHIELD_WORLD_MODEL "models/weapons/w_shield.mdl"
+
+class CCSPlayer;
+
+// These are the names of the ammo types that go in the CAmmoDefs and that the
+// weapon script files reference.
+#define BULLET_PLAYER_50AE "BULLET_PLAYER_50AE"
+#define BULLET_PLAYER_762MM "BULLET_PLAYER_762MM"
+#define BULLET_PLAYER_556MM "BULLET_PLAYER_556MM"
+#define BULLET_PLAYER_556MM_BOX "BULLET_PLAYER_556MM_BOX"
+#define BULLET_PLAYER_338MAG "BULLET_PLAYER_338MAG"
+#define BULLET_PLAYER_9MM "BULLET_PLAYER_9MM"
+#define BULLET_PLAYER_BUCKSHOT "BULLET_PLAYER_BUCKSHOT"
+#define BULLET_PLAYER_45ACP "BULLET_PLAYER_45ACP"
+#define BULLET_PLAYER_357SIG "BULLET_PLAYER_357SIG"
+#define BULLET_PLAYER_57MM "BULLET_PLAYER_57MM"
+#define AMMO_TYPE_HEGRENADE "AMMO_TYPE_HEGRENADE"
+#define AMMO_TYPE_FLASHBANG "AMMO_TYPE_FLASHBANG"
+#define AMMO_TYPE_SMOKEGRENADE "AMMO_TYPE_SMOKEGRENADE"
+
+#define CROSSHAIR_CONTRACT_PIXELS_PER_SECOND 7.0f
+
+// Given an ammo type (like from a weapon's GetPrimaryAmmoType()), this compares it
+// against the ammo name you specify.
+// MIKETODO: this should use indexing instead of searching and strcmp()'ing all the time.
+bool IsAmmoType( int iAmmoType, const char *pAmmoName );
+
+enum CSWeaponMode
+{
+ Primary_Mode = 0,
+ Secondary_Mode,
+ WeaponMode_MAX
+};
+
+#if defined( CLIENT_DLL )
+
+ //--------------------------------------------------------------------------------------------------------------
+ /**
+ * Returns the client's ID_* value for the currently owned weapon, or ID_NONE if no weapon is owned
+ */
+ CSWeaponID GetClientWeaponID( bool primary );
+
+#endif
+
+ //--------------------------------------------------------------------------------------------------------------
+ CCSWeaponInfo * GetWeaponInfo( CSWeaponID weaponID );
+
+
+class CWeaponCSBase : public CBaseCombatWeapon
+{
+public:
+ DECLARE_CLASS( CWeaponCSBase, CBaseCombatWeapon );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponCSBase();
+
+ #ifdef GAME_DLL
+ DECLARE_DATADESC();
+
+ virtual void CheckRespawn();
+ virtual CBaseEntity* Respawn();
+
+ virtual const Vector& GetBulletSpread();
+ virtual float GetDefaultAnimSpeed();
+
+ virtual void BulletWasFired( const Vector &vecStart, const Vector &vecEnd );
+ virtual bool ShouldRemoveOnRoundRestart();
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [dwenger] Handle round restart processing for the weapon.
+ //=============================================================================
+
+ virtual void OnRoundRestart();
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ virtual bool DefaultReload( int iClipSize1, int iClipSize2, int iActivity );
+
+ void SendReloadEvents();
+
+ void Materialize();
+ void AttemptToMaterialize();
+ virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+
+ virtual bool IsRemoveable();
+
+ #endif
+
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo );
+ virtual void AddViewmodelBob( CBaseViewModel *viewmodel, Vector &origin, QAngle &angles );
+ virtual float CalcViewmodelBob( void );
+ // All predicted weapons need to implement and return true
+ virtual bool IsPredicted() const;
+
+ // Pistols reset m_iShotsFired to 0 when the attack button is released.
+ bool IsPistol() const;
+
+ virtual bool IsFullAuto() const;
+
+ CCSPlayer* GetPlayerOwner() const;
+
+ virtual float GetMaxSpeed() const; // What's the player's max speed while holding this weapon.
+
+ // Get CS-specific weapon data.
+ CCSWeaponInfo const &GetCSWpnData() const;
+
+ // Get specific CS weapon ID (ie: WEAPON_AK47, etc)
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_NONE; }
+
+ // return true if this weapon is an instance of the given weapon type (ie: "IsA" WEAPON_GLOCK)
+ bool IsA( CSWeaponID id ) const { return GetWeaponID() == id; }
+
+ // return true if this weapon is a kinf of the given weapon type (ie: "IsKindOf" WEAPONTYPE_RIFLE )
+ bool IsKindOf( CSWeaponType type ) const { return GetCSWpnData().m_WeaponType == type; }
+
+ // return true if this weapon has a silencer equipped
+ virtual bool IsSilenced( void ) const { return false; }
+
+ virtual void SetWeaponModelIndex( const char *pName );
+ virtual void OnPickedUp( CBaseCombatCharacter *pNewOwner );
+
+ virtual void OnJump( float fImpulse );
+ virtual void OnLand( float fVelocity );
+
+public:
+ #if defined( CLIENT_DLL )
+
+ virtual void ProcessMuzzleFlashEvent();
+ virtual bool OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options );
+ virtual bool ShouldPredict();
+ virtual void DrawCrosshair();
+ virtual void OnDataChanged( DataUpdateType_t type );
+
+ virtual int GetMuzzleAttachment( void );
+ virtual bool HideViewModelWhenZoomed( void ) { return true; }
+
+ float m_flCrosshairDistance;
+ int m_iAmmoLastCheck;
+ int m_iAlpha;
+ int m_iScopeTextureID;
+ int m_iCrosshairTextureID; // for white additive texture
+
+ virtual int GetMuzzleFlashStyle( void );
+
+ #else
+
+ virtual bool Reload();
+ virtual void Spawn();
+ virtual bool KeyValue( const char *szKeyName, const char *szValue );
+
+ virtual bool PhysicsSplash( const Vector &centerPoint, const Vector &normal, float rawSpeed, float scaledSpeed );
+
+ #endif
+
+ bool IsUseable();
+ virtual bool CanDeploy( void );
+ virtual void UpdateShieldState( void );
+ virtual bool SendWeaponAnim( int iActivity );
+ virtual void SecondaryAttack( void );
+ virtual void Precache( void );
+ virtual bool CanBeSelected( void );
+ virtual Activity GetDeployActivity( void );
+ virtual bool DefaultDeploy( char *szViewModel, char *szWeaponModel, int iActivity, char *szAnimExt );
+ virtual void DefaultTouch( CBaseEntity *pOther ); // default weapon touch
+ virtual bool DefaultPistolReload();
+
+ virtual bool Deploy();
+ virtual void Drop( const Vector &vecVelocity );
+ bool PlayEmptySound();
+ virtual void ItemPostFrame();
+ virtual void ItemBusyFrame();
+ virtual const char *GetViewModel( int viewmodelindex = 0 ) const;
+
+
+ bool m_bDelayFire; // This variable is used to delay the time between subsequent button pressing.
+ float m_flAccuracy;
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [pfreese] new accuracy model
+ //=============================================================================
+
+ CNetworkVar( CSWeaponMode, m_weaponMode);
+
+ virtual float GetInaccuracy() const;
+ virtual float GetSpread() const;
+
+ virtual void UpdateAccuracyPenalty();
+
+ CNetworkVar( float, m_fAccuracyPenalty );
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ void SetExtraAmmoCount( int count ) { m_iExtraPrimaryAmmo = count; }
+ int GetExtraAmmoCount( void ) { return m_iExtraPrimaryAmmo; }
+
+ //=============================================================================
+ // HPE_BEGIN:
+ //=============================================================================
+
+ // [tj] Accessors for the previous owner of the gun
+ void SetPreviousOwner(CCSPlayer* player) { m_prevOwner = player; }
+ CCSPlayer* GetPreviousOwner() { return m_prevOwner; }
+
+ // [tj] Accessors for the donor system
+ void SetDonor(CCSPlayer* player) { m_donor = player; }
+ CCSPlayer* GetDonor() { return m_donor; }
+ void SetDonated(bool donated) { m_donated = true;}
+ bool GetDonated() { return m_donated; }
+
+ //[dwenger] Accessors for the prior owner list
+ void AddToPriorOwnerList(CCSPlayer* pPlayer);
+ bool IsAPriorOwner(CCSPlayer* pPlayer);
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+protected:
+
+ float CalculateNextAttackTime( float flCycleTime );
+
+private:
+
+ float m_flDecreaseShotsFired;
+
+ CWeaponCSBase( const CWeaponCSBase & );
+
+ int m_iExtraPrimaryAmmo;
+
+ float m_nextPrevOwnerTouchTime;
+ CCSPlayer *m_prevOwner;
+
+ int m_iDefaultExtraAmmo;
+
+ //=============================================================================
+ // HPE_BEGIN:
+ //=============================================================================
+
+ // [dwenger] track all prior owners of this weapon
+ CUtlVector< CCSPlayer* > m_PriorOwners;
+
+ // [tj] To keep track of people who drop weapons for teammates during the buy round
+ CHandle<CCSPlayer> m_donor;
+ bool m_donated;
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+};
+
+extern ConVar weapon_accuracy_model;
+
+#endif // WEAPON_CSBASE_H
diff --git a/game/shared/cstrike/weapon_csbasegun.cpp b/game/shared/cstrike/weapon_csbasegun.cpp
new file mode 100644
index 0000000..6ce0f3d
--- /dev/null
+++ b/game/shared/cstrike/weapon_csbasegun.cpp
@@ -0,0 +1,218 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbasegun.h"
+#include "fx_cs_shared.h"
+
+#ifdef CLIENT_DLL
+ #include "c_cs_player.h"
+#else
+ #include "cs_player.h"
+#endif
+
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponCSBaseGun, DT_WeaponCSBaseGun )
+
+BEGIN_NETWORK_TABLE( CWeaponCSBaseGun, DT_WeaponCSBaseGun )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponCSBaseGun )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_csbase_gun, CWeaponCSBaseGun );
+
+
+
+CWeaponCSBaseGun::CWeaponCSBaseGun()
+{
+}
+
+void CWeaponCSBaseGun::Spawn()
+{
+ m_flAccuracy = 0.2;
+ m_bDelayFire = false;
+ m_zoomFullyActiveTime = -1.0f;
+
+ BaseClass::Spawn();
+}
+
+
+bool CWeaponCSBaseGun::Deploy()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return false;
+
+ m_flAccuracy = 0.2;
+ pPlayer->m_iShotsFired = 0;
+ m_bDelayFire = false;
+ m_zoomFullyActiveTime = -1.0f;
+
+ return BaseClass::Deploy();
+}
+
+void CWeaponCSBaseGun::ItemPostFrame()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+
+ if ( !pPlayer )
+ return;
+
+ //GOOSEMAN : Return zoom level back to previous zoom level before we fired a shot. This is used only for the AWP.
+ // And Scout.
+ if ( (m_flNextPrimaryAttack <= gpGlobals->curtime) && (pPlayer->m_bResumeZoom == TRUE) )
+ {
+ pPlayer->m_bResumeZoom = false;
+
+ if ( m_iClip1 != 0 || ( GetWeaponFlags() & ITEM_FLAG_NOAUTORELOAD ) )
+ {
+ m_weaponMode = Secondary_Mode;
+ pPlayer->SetFOV( pPlayer, pPlayer->m_iLastZoom, 0.05f );
+ m_zoomFullyActiveTime = gpGlobals->curtime + 0.05f;// Make sure we think that we are zooming on the server so we don't get instant acc bonus
+ }
+ }
+
+ BaseClass::ItemPostFrame();
+}
+
+
+void CWeaponCSBaseGun::PrimaryAttack()
+{
+ // Derived classes should implement this and call CSBaseGunFire.
+ Assert( false );
+}
+
+bool CWeaponCSBaseGun::CSBaseGunFire( float flCycleTime, CSWeaponMode weaponMode )
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return false;
+
+ const CCSWeaponInfo &pCSInfo = GetCSWpnData();
+
+ m_bDelayFire = true;
+
+ if ( m_iClip1 > 0 )
+ {
+ pPlayer->m_iShotsFired++;
+
+ // These modifications feed back into flSpread eventually.
+ if ( pCSInfo.m_flAccuracyDivisor != -1 )
+ {
+ int iShotsFired = pPlayer->m_iShotsFired;
+
+ if ( pCSInfo.m_bAccuracyQuadratic )
+ iShotsFired = iShotsFired * iShotsFired;
+ else
+ iShotsFired = iShotsFired * iShotsFired * iShotsFired;
+
+ m_flAccuracy = ( iShotsFired / pCSInfo.m_flAccuracyDivisor ) + pCSInfo.m_flAccuracyOffset;
+
+ if ( m_flAccuracy > pCSInfo.m_flMaxInaccuracy )
+ m_flAccuracy = pCSInfo.m_flMaxInaccuracy;
+ }
+ }
+ else
+ {
+ m_flAccuracy = 0;
+
+ if ( m_bFireOnEmpty )
+ {
+ PlayEmptySound();
+
+ // NOTE[pmf]: we don't want to actually play the dry fire animations, as most seem to depict the weapon actually firing.
+ // SendWeaponAnim( ACT_VM_DRYFIRE );
+
+ m_bFireOnEmpty = false;
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.1f;
+ }
+
+ return false;
+ }
+
+ float flCurAttack = CalculateNextAttackTime( flCycleTime );
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+
+ m_iClip1--;
+
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ FX_FireBullets(
+ pPlayer->entindex(),
+ pPlayer->Weapon_ShootPosition(),
+ pPlayer->EyeAngles() + 2.0f * pPlayer->GetPunchAngle(),
+ GetWeaponID(),
+ weaponMode,
+ CBaseEntity::GetPredictionRandomSeed() & 255,
+ GetInaccuracy(),
+ GetSpread(),
+ flCurAttack );
+
+ DoFireEffects();
+
+ SetWeaponIdleTime( gpGlobals->curtime + GetCSWpnData().m_flTimeToIdleAfterFire );
+
+ // update accuracy
+ m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyImpulseFire[weaponMode];
+
+ return true;
+}
+
+
+void CWeaponCSBaseGun::DoFireEffects()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+
+ if ( pPlayer )
+ pPlayer->DoMuzzleFlash();
+}
+
+
+bool CWeaponCSBaseGun::Reload()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return false;
+
+ if (pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) <= 0)
+ return false;
+
+ pPlayer->SetFOV( pPlayer, pPlayer->GetDefaultFOV(), 0.0f );
+
+ int iResult = DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD );
+ if ( !iResult )
+ return false;
+
+ pPlayer->SetAnimation( PLAYER_RELOAD );
+
+ if ((iResult) && (pPlayer->GetFOV() != pPlayer->GetDefaultFOV()))
+ {
+ pPlayer->SetFOV( pPlayer, pPlayer->GetDefaultFOV() );
+ }
+
+ m_flAccuracy = 0.2;
+ pPlayer->m_iShotsFired = 0;
+ m_bDelayFire = false;
+
+ pPlayer->SetShieldDrawnState( false );
+ return true;
+}
+
+void CWeaponCSBaseGun::WeaponIdle()
+{
+ if (m_flTimeWeaponIdle > gpGlobals->curtime)
+ return;
+
+ // only idle if the slid isn't back
+ if ( m_iClip1 != 0 )
+ {
+ SetWeaponIdleTime( gpGlobals->curtime + GetCSWpnData().m_flIdleInterval );
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+}
diff --git a/game/shared/cstrike/weapon_csbasegun.h b/game/shared/cstrike/weapon_csbasegun.h
new file mode 100644
index 0000000..6781340
--- /dev/null
+++ b/game/shared/cstrike/weapon_csbasegun.h
@@ -0,0 +1,58 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef WEAPON_CSBASE_GUN_H
+#define WEAPON_CSBASE_GUN_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "weapon_csbase.h"
+
+
+// This is the base class for pistols and rifles.
+#if defined( CLIENT_DLL )
+
+ #define CWeaponCSBaseGun C_WeaponCSBaseGun
+
+#else
+#endif
+
+
+class CWeaponCSBaseGun : public CWeaponCSBase
+{
+public:
+
+ DECLARE_CLASS( CWeaponCSBaseGun, CWeaponCSBase );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponCSBaseGun();
+
+ virtual void PrimaryAttack();
+ virtual void Spawn();
+ virtual bool Deploy();
+ virtual bool Reload();
+ virtual void WeaponIdle();
+
+ // Derived classes call this to fire a bullet.
+ bool CSBaseGunFire( float flCycleTime, CSWeaponMode weaponMode );
+
+ // Usually plays the shot sound. Guns with silencers can play different sounds.
+ virtual void DoFireEffects();
+ virtual void ItemPostFrame();
+
+protected:
+ float m_zoomFullyActiveTime;
+
+private:
+
+ CWeaponCSBaseGun( const CWeaponCSBaseGun & );
+};
+
+
+#endif // WEAPON_CSBASE_GUN_H
diff --git a/game/shared/cstrike/weapon_deagle.cpp b/game/shared/cstrike/weapon_deagle.cpp
new file mode 100644
index 0000000..1d0d95c
--- /dev/null
+++ b/game/shared/cstrike/weapon_deagle.cpp
@@ -0,0 +1,235 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "decals.h"
+#include "cbase.h"
+#include "shake.h"
+#include "weapon_csbase.h"
+#include "fx_cs_shared.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CDEagle C_DEagle
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+
+#endif
+
+
+
+#define DEAGLE_WEIGHT 7
+#define DEAGLE_MAX_CLIP 7
+
+enum deagle_e {
+ DEAGLE_IDLE1 = 0,
+ DEAGLE_SHOOT1,
+ DEAGLE_SHOOT2,
+ DEAGLE_SHOOT_EMPTY,
+ DEAGLE_RELOAD,
+ DEAGLE_DRAW,
+};
+
+
+
+class CDEagle : public CWeaponCSBase
+{
+public:
+ DECLARE_CLASS( CDEagle, CWeaponCSBase );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CDEagle();
+
+ void Spawn();
+
+ void PrimaryAttack();
+ virtual bool Deploy();
+ bool Reload();
+ void WeaponIdle();
+ void MakeBeam ();
+ void BeamUpdate ();
+ virtual bool UseDecrement() {return true;};
+
+ virtual float GetInaccuracy() const;
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_DEAGLE; }
+
+public:
+ float m_flLastFire;
+
+private:
+ CDEagle( const CDEagle & );
+};
+
+
+
+IMPLEMENT_NETWORKCLASS_ALIASED( DEagle, DT_WeaponDEagle )
+
+BEGIN_NETWORK_TABLE( CDEagle, DT_WeaponDEagle )
+END_NETWORK_TABLE()
+
+#if defined CLIENT_DLL
+BEGIN_PREDICTION_DATA( CDEagle )
+ DEFINE_FIELD( m_flLastFire, FIELD_FLOAT ),
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_deagle, CDEagle );
+PRECACHE_WEAPON_REGISTER( weapon_deagle );
+
+
+
+CDEagle::CDEagle()
+{
+ m_flLastFire = gpGlobals->curtime;
+}
+
+
+void CDEagle::Spawn()
+{
+ BaseClass::Spawn();
+ m_flAccuracy = 0.9;
+}
+
+
+bool CDEagle::Deploy()
+{
+ m_flAccuracy = 0.9;
+ return BaseClass::Deploy();
+}
+
+float CDEagle::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ return 1.5f * (1 - m_flAccuracy);
+
+ else if (pPlayer->GetAbsVelocity().Length2D() > 5)
+ return 0.25f * (1 - m_flAccuracy);
+
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ return 0.115f * (1 - m_flAccuracy);
+
+ else
+ return 0.13f * (1 - m_flAccuracy);
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+void CDEagle::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ // Mark the time of this shot and determine the accuracy modifier based on the last shot fired...
+ m_flAccuracy -= (0.35)*(0.4 - ( gpGlobals->curtime - m_flLastFire ) );
+
+ if (m_flAccuracy > 0.9)
+ m_flAccuracy = 0.9;
+ else if (m_flAccuracy < 0.55)
+ m_flAccuracy = 0.55;
+
+ m_flLastFire = gpGlobals->curtime;
+
+ if (m_iClip1 <= 0)
+ {
+ if ( m_bFireOnEmpty )
+ {
+ PlayEmptySound();
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.1f;
+ m_bFireOnEmpty = false;
+ }
+
+ return;
+ }
+
+ pPlayer->m_iShotsFired++;
+
+ m_iClip1--;
+
+ pPlayer->DoMuzzleFlash();
+
+ if( m_iClip1 > 0 )
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+ else
+ SendWeaponAnim( ACT_VM_DRYFIRE );
+
+ //SetPlayerShieldAnim();
+
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ //pPlayer->m_iWeaponVolume = BIG_EXPLOSION_VOLUME;
+ //pPlayer->m_iWeaponFlash = BRIGHT_GUN_FLASH;
+
+ FX_FireBullets(
+ pPlayer->entindex(),
+ pPlayer->Weapon_ShootPosition(),
+ pPlayer->EyeAngles() + 2.0f * pPlayer->GetPunchAngle(),
+ GetWeaponID(),
+ Primary_Mode,
+ CBaseEntity::GetPredictionRandomSeed() & 255,
+ GetInaccuracy(),
+ GetSpread());
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + GetCSWpnData().m_flCycleTime;
+
+ if ( !m_iClip1 && pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) <= 0 )
+ {
+ // HEV suit - indicate out of ammo condition
+ pPlayer->SetSuitUpdate("!HEV_AMO0", false, 0);
+ }
+
+ SetWeaponIdleTime( gpGlobals->curtime + 1.8 );
+
+ // update accuracy
+ m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyImpulseFire[Primary_Mode];
+
+ QAngle punchAngle = pPlayer->GetPunchAngle();
+ punchAngle.x -= 2;
+ pPlayer->SetPunchAngle( punchAngle );
+
+ //ResetPlayerShieldAnim();
+}
+
+
+bool CDEagle::Reload()
+{
+ if ( !DefaultPistolReload() )
+ return false;
+
+ m_flAccuracy = 0.9;
+ return true;
+}
+
+void CDEagle::WeaponIdle()
+{
+ if ( m_flTimeWeaponIdle > gpGlobals->curtime )
+ return;
+
+ SetWeaponIdleTime( gpGlobals->curtime + 20 );
+
+ if (m_iClip1 != 0)
+ {
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+
+ //if ( FBitSet(m_iWeaponState, WPNSTATE_SHIELD_DRAWN) )
+ // SendWeaponAnim( SHIELDGUN_DRAWN_IDLE, UseDecrement() ? 1:0 );
+}
+
diff --git a/game/shared/cstrike/weapon_elite.cpp b/game/shared/cstrike/weapon_elite.cpp
new file mode 100644
index 0000000..b77b87e
--- /dev/null
+++ b/game/shared/cstrike/weapon_elite.cpp
@@ -0,0 +1,317 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbase.h"
+#include "fx_cs_shared.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponElite C_WeaponElite
+ #include "c_cs_player.h"
+ #include "c_te_effect_dispatch.h"
+
+#else
+
+ #include "cs_player.h"
+
+#endif
+
+
+class CWeaponElite : public CWeaponCSBase
+{
+public:
+ DECLARE_CLASS( CWeaponElite, CWeaponCSBase );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponElite();
+
+ virtual void Spawn();
+ virtual void Precache();
+
+ virtual void PrimaryAttack();
+ virtual bool Deploy();
+
+ virtual bool Reload();
+
+ virtual void WeaponIdle();
+
+ virtual float GetInaccuracy() const;
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_ELITE; }
+
+#ifdef CLIENT_DLL
+ virtual int GetMuzzleAttachment( void );
+ virtual bool OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options );
+#endif
+
+ virtual const char *GetWorldModel( void ) const;
+ virtual int GetWorldModelIndex( void );
+
+protected:
+ bool FiringLeft() const;
+
+private:
+
+ CWeaponElite( const CWeaponElite & );
+ float m_flLastFire;
+
+ int m_droppedModelIndex;
+ bool m_inPrecache;
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponElite, DT_WeaponElite )
+
+BEGIN_NETWORK_TABLE( CWeaponElite, DT_WeaponElite )
+END_NETWORK_TABLE()
+
+#if defined CLIENT_DLL
+BEGIN_PREDICTION_DATA( CWeaponElite )
+ DEFINE_FIELD( m_flLastFire, FIELD_FLOAT ),
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_elite, CWeaponElite );
+PRECACHE_WEAPON_REGISTER( weapon_elite );
+
+CWeaponElite::CWeaponElite()
+{
+ m_flLastFire = gpGlobals->curtime;
+ m_inPrecache = false;
+}
+
+
+void CWeaponElite::Spawn( )
+{
+ m_flAccuracy = 0.88;
+ BaseClass::Spawn();
+}
+
+
+void CWeaponElite::Precache()
+{
+ m_inPrecache = true;
+ BaseClass::Precache();
+
+ PrecacheModel( "models/weapons/w_eq_eholster_elite.mdl" );
+ PrecacheModel( "models/weapons/w_eq_eholster.mdl" );
+ PrecacheModel( "models/weapons/w_pist_elite_single.mdl" );
+ m_droppedModelIndex = CBaseEntity::PrecacheModel( GetCSWpnData().m_szDroppedModel );
+ m_inPrecache = false;
+}
+
+bool CWeaponElite::Deploy( )
+{
+ m_flAccuracy = 0.88;
+ return BaseClass::Deploy();
+}
+
+int CWeaponElite::GetWorldModelIndex( void )
+{
+ if ( GetOwner() || m_inPrecache )
+ {
+ return m_iWorldModelIndex;
+ }
+ else
+ {
+ return m_droppedModelIndex;
+ }
+}
+
+const char * CWeaponElite::GetWorldModel( void ) const
+{
+ if ( GetOwner() || m_inPrecache )
+ {
+ return BaseClass::GetWorldModel();
+ }
+ else
+ {
+ return GetCSWpnData().m_szDroppedModel;
+ }
+}
+
+
+bool CWeaponElite::FiringLeft() const
+{
+ // fire left-hand gun with even number of bullets left
+ return (m_iClip1 & 1) == 0;
+}
+
+
+
+float CWeaponElite::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ return 1.3f * (1 - m_flAccuracy);
+
+ else if (pPlayer->GetAbsVelocity().Length2D() > 5)
+ return 0.175f * (1 - m_flAccuracy);
+
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ return 0.08f * (1 - m_flAccuracy);
+
+ else
+ return 0.1f * (1 - m_flAccuracy);
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+void CWeaponElite::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ // Mark the time of this shot and determine the accuracy modifier based on the last shot fired...
+ m_flAccuracy -= (0.275)*(0.325 - (gpGlobals->curtime - m_flLastFire));
+
+ if (m_flAccuracy > 0.88)
+ m_flAccuracy = 0.88;
+ else if (m_flAccuracy < 0.55)
+ m_flAccuracy = 0.55;
+
+ m_flLastFire = gpGlobals->curtime;
+
+ if (m_iClip1 <= 0)
+ {
+ if ( m_bFireOnEmpty )
+ {
+ PlayEmptySound();
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.1f;
+ m_bFireOnEmpty = false;
+ }
+
+ return;
+ }
+
+ pPlayer->m_iShotsFired++;
+
+ m_iClip1--;
+
+ pPlayer->DoMuzzleFlash();
+
+ //SetPlayerShieldAnim();
+
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ FX_FireBullets(
+ pPlayer->entindex(),
+ pPlayer->Weapon_ShootPosition(),
+ pPlayer->EyeAngles() + 2.0f * pPlayer->GetPunchAngle(),
+ GetWeaponID(),
+ FiringLeft() ? Secondary_Mode : Primary_Mode,
+ CBaseEntity::GetPredictionRandomSeed() & 255,
+ GetInaccuracy(),
+ GetSpread());
+
+ m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + GetCSWpnData().m_flCycleTime;
+
+ if (!m_iClip1 && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0)
+ {
+ // HEV suit - indicate out of ammo condition
+ pPlayer->SetSuitUpdate("!HEV_AMO0", false, 0);
+ }
+
+ SetWeaponIdleTime( gpGlobals->curtime + 2.5 );
+
+ if ( FiringLeft() )
+ {
+ if ( m_iClip1 > 0 )
+ SendWeaponAnim( ACT_VM_SECONDARYATTACK );
+ else
+ SendWeaponAnim( ACT_VM_DRYFIRE );
+ }
+ else
+ {
+ if ( m_iClip1 > 1 )
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+ else
+ SendWeaponAnim( ACT_VM_DRYFIRE_LEFT );
+ }
+
+ // update accuracy
+ m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyImpulseFire[Primary_Mode];
+
+ QAngle punchAngle = pPlayer->GetPunchAngle();
+ punchAngle.x -= 2;
+ pPlayer->SetPunchAngle( punchAngle );
+
+ //ResetPlayerShieldAnim();
+}
+
+
+bool CWeaponElite::Reload()
+{
+ if ( !DefaultPistolReload() )
+ return false;
+
+ m_flAccuracy = 0.88;
+ return true;
+}
+
+void CWeaponElite::WeaponIdle()
+{
+ if (m_flTimeWeaponIdle > gpGlobals->curtime)
+ return;
+
+/*
+ // switching to the idle with the slide back on the right pistol causes animation pops transitioning
+ // from/to the depot/holster animations. The pop transition to the reload is less noticeable, so
+ // we'll live with that one
+
+ if ( m_iClip1 == 1 )
+ {
+ SendWeaponAnim( ACT_VM_IDLE_EMPTY_LEFT );
+ }
+*/
+
+ // only idle if either slide isn't back
+ if ( m_iClip1 >= 2 )
+ {
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+}
+
+#ifdef CLIENT_DLL
+
+ bool CWeaponElite::OnFireEvent( C_BaseViewModel *pViewModel, const Vector& origin, const QAngle& angles, int event, const char *options )
+ {
+ if( event == 5001 )
+ {
+ C_CSPlayer *pPlayer = ToCSPlayer( GetOwner() );
+ if( pPlayer && pPlayer->GetFOV() < pPlayer->GetDefaultFOV() && HideViewModelWhenZoomed() )
+ return true;
+
+ CEffectData data;
+ data.m_fFlags = 0;
+ data.m_hEntity = pViewModel->GetRefEHandle();
+ data.m_nAttachmentIndex = FiringLeft() ? 1 : 2; // toggle muzzle flash
+ data.m_flScale = GetCSWpnData().m_flMuzzleScale;
+
+ DispatchEffect( "CS_MuzzleFlash", data );
+
+ return true;
+ }
+
+ return BaseClass::OnFireEvent( pViewModel, origin, angles, event, options );
+ }
+
+ int CWeaponElite::GetMuzzleAttachment( void )
+ {
+ return LookupAttachment( FiringLeft() ? "muzzle_flash_l" : "muzzle_flash_r" );
+ }
+
+#endif \ No newline at end of file
diff --git a/game/shared/cstrike/weapon_famas.cpp b/game/shared/cstrike/weapon_famas.cpp
new file mode 100644
index 0000000..1f22cad
--- /dev/null
+++ b/game/shared/cstrike/weapon_famas.cpp
@@ -0,0 +1,243 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbasegun.h"
+#include "fx_cs_shared.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponFamas C_WeaponFamas
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+
+#endif
+
+
+class CWeaponFamas : public CWeaponCSBaseGun
+{
+public:
+ DECLARE_CLASS( CWeaponFamas, CWeaponCSBase );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponFamas();
+
+ virtual void PrimaryAttack();
+ virtual void SecondaryAttack();
+ virtual bool Deploy();
+
+ virtual float GetInaccuracy() const;
+
+ virtual void ItemPostFrame();
+
+ void FamasFire( float flSpread, bool bFireBurst );
+ void FireRemaining();
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_FAMAS; }
+
+private:
+
+ CWeaponFamas( const CWeaponFamas & );
+ CNetworkVar( bool, m_bBurstMode );
+ CNetworkVar( int, m_iBurstShotsRemaining );
+ float m_fNextBurstShot; // time to shoot the next bullet in burst fire mode
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponFamas, DT_WeaponFamas )
+
+BEGIN_NETWORK_TABLE( CWeaponFamas, DT_WeaponFamas )
+ #ifdef CLIENT_DLL
+ RecvPropBool( RECVINFO( m_bBurstMode ) ),
+ RecvPropInt( RECVINFO( m_iBurstShotsRemaining ) ),
+ #else
+ SendPropBool( SENDINFO( m_bBurstMode ) ),
+ SendPropInt( SENDINFO( m_iBurstShotsRemaining ) ),
+ #endif
+END_NETWORK_TABLE()
+
+#if defined(CLIENT_DLL)
+BEGIN_PREDICTION_DATA( CWeaponFamas )
+DEFINE_PRED_FIELD( m_iBurstShotsRemaining, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+DEFINE_PRED_FIELD( m_fNextBurstShot, FIELD_FLOAT, 0 ),
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_famas, CWeaponFamas );
+PRECACHE_WEAPON_REGISTER( weapon_famas );
+
+
+const float kFamasBurstCycleTime = 0.075f;
+
+
+CWeaponFamas::CWeaponFamas()
+{
+ m_bBurstMode = false;
+}
+
+
+bool CWeaponFamas::Deploy( )
+{
+ m_iBurstShotsRemaining = 0;
+ m_fNextBurstShot = 0.0f;
+ m_flAccuracy = 0.9f;
+
+ return BaseClass::Deploy();
+}
+
+
+// Secondary attack could be three-round burst mode
+void CWeaponFamas::SecondaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( m_bBurstMode )
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "#Switch_To_FullAuto" );
+ m_bBurstMode = false;
+ m_weaponMode = Primary_Mode;
+ }
+ else
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "#Switch_To_BurstFire" );
+ m_bBurstMode = true;
+ m_weaponMode = Secondary_Mode;
+ }
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.3;
+}
+
+float CWeaponFamas::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ float fAutoPenalty = m_bBurstMode ? 0.0f : 0.01f;
+
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) ) // if player is in air
+ return 0.03f + 0.3f * m_flAccuracy + fAutoPenalty;
+
+ else if ( pPlayer->GetAbsVelocity().Length2D() > 140 ) // if player is moving
+ return 0.03f + 0.07f * m_flAccuracy + fAutoPenalty;
+ /* new code */
+ else
+ return 0.02f * m_flAccuracy + fAutoPenalty;
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+
+void CWeaponFamas::ItemPostFrame()
+{
+ if ( m_iBurstShotsRemaining > 0 && gpGlobals->curtime >= m_fNextBurstShot )
+ FireRemaining();
+
+ BaseClass::ItemPostFrame();
+}
+
+
+
+// GOOSEMAN : FireRemaining used by Glock18
+void CWeaponFamas::FireRemaining()
+{
+ m_iClip1--;
+
+ if (m_iClip1 < 0)
+ {
+ m_iClip1 = 0;
+ m_iBurstShotsRemaining = 0;
+ m_fNextBurstShot = 0.0f;
+ return;
+ }
+
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ Error( "!pPlayer" );
+
+ // Famas burst mode
+ FX_FireBullets(
+ pPlayer->entindex(),
+ pPlayer->Weapon_ShootPosition(),
+ pPlayer->EyeAngles() + 2.0f * pPlayer->GetPunchAngle(),
+ GetWeaponID(),
+ Secondary_Mode,
+ CBaseEntity::GetPredictionRandomSeed() & 255,
+ GetInaccuracy(),
+ GetSpread(),
+ m_fNextBurstShot);
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+
+ pPlayer->DoMuzzleFlash();
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ pPlayer->m_iShotsFired++;
+
+ --m_iBurstShotsRemaining;
+ if ( m_iBurstShotsRemaining > 0 )
+ m_fNextBurstShot += kFamasBurstCycleTime;
+ else
+ m_fNextBurstShot = 0.0f;
+
+ // update accuracy
+ m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyImpulseFire[Secondary_Mode];
+}
+
+
+void CWeaponFamas::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ // don't fire underwater
+ if (pPlayer->GetWaterLevel() == 3)
+ {
+ PlayEmptySound( );
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.15;
+ return;
+ }
+
+ pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ float flCycleTime = GetCSWpnData().m_flCycleTime;
+
+ // change a few things if we're in burst mode
+ if ( m_bBurstMode )
+ {
+ flCycleTime = 0.55f;
+ m_iBurstShotsRemaining = 2;
+ m_fNextBurstShot = gpGlobals->curtime + kFamasBurstCycleTime;
+ }
+
+ if ( !CSBaseGunFire( flCycleTime, m_weaponMode ) )
+ return;
+
+ if ( pPlayer->GetAbsVelocity().Length2D() > 5 )
+ pPlayer->KickBack ( 1, 0.45, 0.275, 0.05, 4, 2.5, 7 );
+
+ else if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ pPlayer->KickBack ( 1.25, 0.45, 0.22, 0.18, 5.5, 4, 5 );
+
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ pPlayer->KickBack ( 0.575, 0.325, 0.2, 0.011, 3.25, 2, 8 );
+
+ else
+ pPlayer->KickBack ( 0.625, 0.375, 0.25, 0.0125, 3.5, 2.25, 8 );
+}
+
+
diff --git a/game/shared/cstrike/weapon_fiveseven.cpp b/game/shared/cstrike/weapon_fiveseven.cpp
new file mode 100644
index 0000000..9b94191
--- /dev/null
+++ b/game/shared/cstrike/weapon_fiveseven.cpp
@@ -0,0 +1,202 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbase.h"
+#include "fx_cs_shared.h"
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponFiveSeven C_WeaponFiveSeven
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+
+#endif
+
+
+class CWeaponFiveSeven : public CWeaponCSBase
+{
+public:
+ DECLARE_CLASS( CWeaponFiveSeven, CWeaponCSBase );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponFiveSeven();
+
+ virtual void Spawn();
+
+ virtual void PrimaryAttack();
+ virtual void SecondaryAttack();
+ virtual bool Deploy();
+
+ virtual bool Reload();
+
+ virtual void WeaponIdle();
+
+ virtual float GetInaccuracy() const;
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_FIVESEVEN; }
+
+private:
+
+ CWeaponFiveSeven( const CWeaponFiveSeven & );
+
+ float m_flLastFire;
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponFiveSeven, DT_WeaponFiveSeven )
+
+BEGIN_NETWORK_TABLE( CWeaponFiveSeven, DT_WeaponFiveSeven )
+END_NETWORK_TABLE()
+
+#if defined CLIENT_DLL
+BEGIN_PREDICTION_DATA( CWeaponFiveSeven )
+ DEFINE_FIELD( m_flLastFire, FIELD_FLOAT ),
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_fiveseven, CWeaponFiveSeven );
+PRECACHE_WEAPON_REGISTER( weapon_fiveseven );
+
+
+
+CWeaponFiveSeven::CWeaponFiveSeven()
+{
+ m_flLastFire = gpGlobals->curtime;
+}
+
+
+void CWeaponFiveSeven::Spawn( )
+{
+ BaseClass::Spawn();
+
+ m_flAccuracy = 0.92;
+}
+
+bool CWeaponFiveSeven::Deploy()
+{
+ m_flAccuracy = 0.92;
+ return BaseClass::Deploy();
+}
+
+
+float CWeaponFiveSeven::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ return 1.5f * (1 - m_flAccuracy);
+ else if (pPlayer->GetAbsVelocity().Length2D() > 5)
+ return 0.255f * (1 - m_flAccuracy);
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ return 0.075f * (1 - m_flAccuracy);
+ else
+ return 0.15f * (1 - m_flAccuracy);
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+void CWeaponFiveSeven::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ // Mark the time of this shot and determine the accuracy modifier based on the last shot fired...
+ m_flAccuracy -= (0.25)*(0.275 - (gpGlobals->curtime - m_flLastFire));
+
+ if (m_flAccuracy > 0.92)
+ m_flAccuracy = 0.92;
+ else if (m_flAccuracy < 0.725)
+ m_flAccuracy = 0.725;
+
+ m_flLastFire = gpGlobals->curtime;
+
+ if (m_iClip1 <= 0)
+ {
+ if ( m_bFireOnEmpty )
+ {
+ PlayEmptySound();
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.1f;
+ m_bFireOnEmpty = false;
+ }
+
+ return;
+ }
+
+ pPlayer->m_iShotsFired++;
+
+ m_iClip1--;
+ pPlayer->DoMuzzleFlash();
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+
+ FX_FireBullets(
+ pPlayer->entindex(),
+ pPlayer->Weapon_ShootPosition(),
+ pPlayer->EyeAngles() + 2.0f * pPlayer->GetPunchAngle(),
+ GetWeaponID(),
+ Primary_Mode,
+ CBaseEntity::GetPredictionRandomSeed() & 255,
+ GetInaccuracy(),
+ GetSpread());
+
+ m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + GetCSWpnData().m_flCycleTime;
+
+ if (!m_iClip1 && pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) <= 0)
+ {
+ // HEV suit - indicate out of ammo condition
+ pPlayer->SetSuitUpdate("!HEV_AMO0", false, 0);
+ }
+
+ SetWeaponIdleTime( gpGlobals->curtime + 2 );
+
+ // update accuracy
+ m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyImpulseFire[Primary_Mode];
+
+ QAngle angle = pPlayer->GetPunchAngle();
+ angle.x -= 2;
+ pPlayer->SetPunchAngle( angle );
+}
+
+
+void CWeaponFiveSeven::SecondaryAttack()
+{
+}
+
+
+bool CWeaponFiveSeven::Reload()
+{
+ if ( !DefaultPistolReload() )
+ return false;
+
+ m_flAccuracy = 0.92;
+ return true;
+}
+
+void CWeaponFiveSeven::WeaponIdle()
+{
+ if (m_flTimeWeaponIdle > gpGlobals->curtime)
+ return;
+
+ // only idle if the slid isn't back
+ if (m_iClip1 != 0)
+ {
+ SetWeaponIdleTime( gpGlobals->curtime + 4 );
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+}
diff --git a/game/shared/cstrike/weapon_flashbang.cpp b/game/shared/cstrike/weapon_flashbang.cpp
new file mode 100644
index 0000000..4ce1fa9
--- /dev/null
+++ b/game/shared/cstrike/weapon_flashbang.cpp
@@ -0,0 +1,60 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbase.h"
+#include "gamerules.h"
+#include "npcevent.h"
+#include "engine/IEngineSound.h"
+#include "weapon_flashbang.h"
+
+
+#ifdef CLIENT_DLL
+
+
+#else
+
+ #include "cs_player.h"
+ #include "items.h"
+ #include "flashbang_projectile.h"
+
+#endif
+
+
+#define GRENADE_TIMER 3.0f //Seconds
+
+
+
+IMPLEMENT_NETWORKCLASS_ALIASED( Flashbang, DT_Flashbang )
+
+BEGIN_NETWORK_TABLE(CFlashbang, DT_Flashbang)
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CFlashbang )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_flashbang, CFlashbang );
+PRECACHE_WEAPON_REGISTER( weapon_flashbang );
+
+
+#ifndef CLIENT_DLL
+
+ BEGIN_DATADESC( CFlashbang )
+ END_DATADESC()
+
+ void CFlashbang::EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer )
+ {
+ CFlashbangProjectile::Create(
+ vecSrc,
+ vecAngles,
+ vecVel,
+ angImpulse,
+ pPlayer );
+ }
+
+#endif
+
+
diff --git a/game/shared/cstrike/weapon_flashbang.h b/game/shared/cstrike/weapon_flashbang.h
new file mode 100644
index 0000000..6f44aae
--- /dev/null
+++ b/game/shared/cstrike/weapon_flashbang.h
@@ -0,0 +1,49 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef WEAPON_FLASHBANG_H
+#define WEAPON_FLASHBANG_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "weapon_basecsgrenade.h"
+
+
+#ifdef CLIENT_DLL
+ #define CFlashbang C_Flashbang
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Fragmentation grenades
+//-----------------------------------------------------------------------------
+class CFlashbang : public CBaseCSGrenade
+{
+public:
+ DECLARE_CLASS( CFlashbang, CBaseCSGrenade );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CFlashbang() {}
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_FLASHBANG; }
+
+
+#ifdef CLIENT_DLL
+
+#else
+ DECLARE_DATADESC();
+
+ virtual void EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer );
+#endif
+
+ CFlashbang( const CFlashbang & ) {}
+};
+
+
+#endif // WEAPON_FLASHBANG_H
diff --git a/game/shared/cstrike/weapon_g3sg1.cpp b/game/shared/cstrike/weapon_g3sg1.cpp
new file mode 100644
index 0000000..25aeca5
--- /dev/null
+++ b/game/shared/cstrike/weapon_g3sg1.cpp
@@ -0,0 +1,213 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbasegun.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponG3SG1 C_WeaponG3SG1
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+ #include "KeyValues.h"
+
+#endif
+
+
+class CWeaponG3SG1 : public CWeaponCSBaseGun
+{
+public:
+ DECLARE_CLASS( CWeaponG3SG1, CWeaponCSBaseGun );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponG3SG1();
+
+ virtual void Spawn();
+ virtual void SecondaryAttack();
+ virtual void PrimaryAttack();
+ virtual bool Reload();
+ virtual bool Deploy();
+
+ virtual float GetInaccuracy() const;
+ virtual float GetMaxSpeed();
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_G3SG1; }
+
+private:
+ CWeaponG3SG1( const CWeaponG3SG1 & );
+
+ float m_flLastFire;
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponG3SG1, DT_WeaponG3SG1 )
+
+BEGIN_NETWORK_TABLE( CWeaponG3SG1, DT_WeaponG3SG1 )
+END_NETWORK_TABLE()
+
+#if defined CLIENT_DLL
+BEGIN_PREDICTION_DATA( CWeaponG3SG1 )
+ DEFINE_FIELD( m_flLastFire, FIELD_FLOAT ),
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_g3sg1, CWeaponG3SG1 );
+PRECACHE_WEAPON_REGISTER( weapon_g3sg1 );
+
+
+
+CWeaponG3SG1::CWeaponG3SG1()
+{
+ m_flLastFire = gpGlobals->curtime;
+}
+
+void CWeaponG3SG1::Spawn()
+{
+ BaseClass::Spawn();
+ m_flAccuracy = 0.98;
+}
+
+
+void CWeaponG3SG1::SecondaryAttack()
+{
+ const float kZoomTime = 0.10f;
+
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( pPlayer->GetFOV() == pPlayer->GetDefaultFOV() )
+ {
+ pPlayer->SetFOV( pPlayer, 40, kZoomTime );
+ m_weaponMode = Secondary_Mode;
+ m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyAltSwitch;
+ }
+ else if (pPlayer->GetFOV() == 40)
+ {
+ pPlayer->SetFOV( pPlayer, 15, kZoomTime );
+ m_weaponMode = Secondary_Mode;
+ }
+ else if (pPlayer->GetFOV() == 15)
+ {
+ pPlayer->SetFOV( pPlayer, pPlayer->GetDefaultFOV(), kZoomTime );
+ m_weaponMode = Primary_Mode;
+ }
+
+#ifndef CLIENT_DLL
+ // If this isn't guarded, the sound will be emitted twice, once by the server and once by the client.
+ // Let the server play it since if only the client plays it, it's liable to get played twice cause of
+ // a prediction error. joy.
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Playing this from the player so that we don't try to play the sound outside the level.
+ //=============================================================================
+ if ( GetPlayerOwner() )
+ {
+ GetPlayerOwner()->EmitSound( "Default.Zoom" ); // zoom sound
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ // let the bots hear the rifle zoom
+ IGameEvent * event = gameeventmanager->CreateEvent( "weapon_zoom" );
+ if( event )
+ {
+ event->SetInt( "userid", pPlayer->GetUserID() );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.3f;
+ m_zoomFullyActiveTime = gpGlobals->curtime + 0.3; // The worst zoom time from above.
+}
+
+float CWeaponG3SG1::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ float fSpread = 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ fSpread = 0.45f * (1.0f - m_flAccuracy);
+ else if (pPlayer->GetAbsVelocity().Length2D() > 5)
+ fSpread = 0.15f;
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ fSpread = 0.035f * (1.0f - m_flAccuracy);
+ else
+ fSpread = 0.055f * (1.0f - m_flAccuracy);
+
+ // If we are not zoomed in, or we have very recently zoomed and are still transitioning, the bullet diverts more.
+ if (pPlayer->GetFOV() == pPlayer->GetDefaultFOV() || (gpGlobals->curtime < m_zoomFullyActiveTime))
+ fSpread += 0.025;
+
+ return fSpread;
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+void CWeaponG3SG1::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ // Mark the time of this shot and determine the accuracy modifier based on the last shot fired...
+ m_flAccuracy = 0.55 + (0.3) * (gpGlobals->curtime - m_flLastFire);
+
+ if (m_flAccuracy > 0.98)
+ m_flAccuracy = 0.98;
+
+ m_flLastFire = gpGlobals->curtime;
+
+ if ( !CSBaseGunFire( GetCSWpnData().m_flCycleTime, m_weaponMode ) )
+ return;
+
+ // Adjust the punch angle.
+ QAngle angle = pPlayer->GetPunchAngle();
+ angle.x -= SharedRandomFloat("G3SG1PunchAngleX", 0.75, 1.75 ) + ( angle.x / 4 );
+ angle.y += SharedRandomFloat("G3SG1PunchAngleY", -0.75, 0.75 );
+ pPlayer->SetPunchAngle( angle );
+}
+
+
+bool CWeaponG3SG1::Reload()
+{
+ bool ret = BaseClass::Reload();
+
+ m_flAccuracy = 0.98;
+ m_weaponMode = Primary_Mode;
+
+ return ret;
+}
+
+bool CWeaponG3SG1::Deploy()
+{
+ bool ret = BaseClass::Deploy();
+
+ m_flAccuracy = 0.98;
+ m_weaponMode = Primary_Mode;
+
+ return ret;
+}
+
+float CWeaponG3SG1::GetMaxSpeed()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( pPlayer && pPlayer->GetFOV() == pPlayer->GetDefaultFOV() )
+ return BaseClass::GetMaxSpeed();
+ else
+ return 150; // zoomed in
+}
diff --git a/game/shared/cstrike/weapon_galil.cpp b/game/shared/cstrike/weapon_galil.cpp
new file mode 100644
index 0000000..961d75a
--- /dev/null
+++ b/game/shared/cstrike/weapon_galil.cpp
@@ -0,0 +1,114 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbasegun.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponGalil C_WeaponGalil
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+
+#endif
+
+
+class CWeaponGalil : public CWeaponCSBaseGun
+{
+public:
+ DECLARE_CLASS( CWeaponGalil, CWeaponCSBaseGun );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponGalil();
+
+ virtual void PrimaryAttack();
+
+ virtual float GetInaccuracy() const;
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_GALIL; }
+
+private:
+
+ CWeaponGalil( const CWeaponGalil & );
+
+ void GalilFire( float flSpread );
+
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponGalil, DT_WeaponGalil )
+
+BEGIN_NETWORK_TABLE( CWeaponGalil, DT_WeaponGalil )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponGalil )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_galil, CWeaponGalil );
+PRECACHE_WEAPON_REGISTER( weapon_galil );
+
+
+
+CWeaponGalil::CWeaponGalil()
+{
+}
+
+float CWeaponGalil::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ return 0.04f + 0.3f * m_flAccuracy;
+ else if (pPlayer->GetAbsVelocity().Length2D() > 140)
+ return 0.04f + 0.07f * m_flAccuracy;
+ else
+ return 0.0375f * m_flAccuracy;
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+void CWeaponGalil::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ // don't fire underwater
+ if (pPlayer->GetWaterLevel() == 3)
+ {
+ PlayEmptySound( );
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.15;
+ return;
+ }
+
+ if ( !CSBaseGunFire( GetCSWpnData().m_flCycleTime, Primary_Mode ) )
+ return;
+
+ // CSBaseGunFire can kill us, forcing us to drop our weapon, if we shoot something that explodes
+ pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if (pPlayer->GetAbsVelocity().Length2D() > 5)
+ pPlayer->KickBack (1.0, 0.45, 0.28, 0.045, 3.75, 3, 7);
+ else if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ pPlayer->KickBack (1.2, 0.5, 0.23, 0.15, 5.5, 3.5, 6);
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ pPlayer->KickBack (0.6, 0.3, 0.2, 0.0125, 3.25, 2, 7);
+ else
+ pPlayer->KickBack (0.65, 0.35, 0.25, 0.015, 3.5, 2.25, 7);
+}
+
+
diff --git a/game/shared/cstrike/weapon_glock.cpp b/game/shared/cstrike/weapon_glock.cpp
new file mode 100644
index 0000000..a094a4c
--- /dev/null
+++ b/game/shared/cstrike/weapon_glock.cpp
@@ -0,0 +1,369 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbase.h"
+#include "fx_cs_shared.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponGlock C_WeaponGlock
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+
+#endif
+
+
+class CWeaponGlock : public CWeaponCSBase
+{
+public:
+ DECLARE_CLASS( CWeaponGlock, CWeaponCSBase );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponGlock();
+
+ virtual void Spawn();
+
+ virtual void PrimaryAttack();
+ virtual void SecondaryAttack();
+ virtual bool Deploy();
+
+ virtual void ItemPostFrame();
+
+ void GlockFire( float fSpread, bool bFireBurst );
+ void FireRemaining( float fSpread );
+
+ virtual bool Reload();
+
+ virtual void WeaponIdle();
+
+ virtual float GetInaccuracy() const;
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_GLOCK; }
+
+private:
+
+ CWeaponGlock( const CWeaponGlock & );
+
+ CNetworkVar( bool, m_bBurstMode );
+ CNetworkVar( int, m_iBurstShotsRemaining ); // used to keep track of the shots fired during the Glock18 burst fire mode.
+ float m_fNextBurstShot; // time to shoot the next bullet in burst fire mode
+ float m_flLastFire;
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponGlock, DT_WeaponGlock )
+
+BEGIN_NETWORK_TABLE( CWeaponGlock, DT_WeaponGlock )
+ #ifdef CLIENT_DLL
+ RecvPropBool( RECVINFO( m_bBurstMode ) ),
+ RecvPropInt( RECVINFO( m_iBurstShotsRemaining ) ),
+ #else
+ SendPropBool( SENDINFO( m_bBurstMode ) ),
+ SendPropInt( SENDINFO( m_iBurstShotsRemaining ) ),
+ #endif
+END_NETWORK_TABLE()
+
+#if defined(CLIENT_DLL)
+BEGIN_PREDICTION_DATA( CWeaponGlock )
+ DEFINE_FIELD( m_flLastFire, FIELD_FLOAT ),
+ DEFINE_PRED_FIELD( m_iBurstShotsRemaining, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_fNextBurstShot, FIELD_FLOAT, 0 ),
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_glock, CWeaponGlock );
+PRECACHE_WEAPON_REGISTER( weapon_glock );
+
+const float kGlockBurstCycleTime = 0.06f;
+
+CWeaponGlock::CWeaponGlock()
+{
+ m_bBurstMode = false;
+ m_flLastFire = gpGlobals->curtime;
+ m_iBurstShotsRemaining = 0;
+ m_fNextBurstShot = 0.0f;
+}
+
+
+void CWeaponGlock::Spawn( )
+{
+ BaseClass::Spawn();
+
+ m_bBurstMode = false;
+ m_iBurstShotsRemaining = 0;
+ m_fNextBurstShot = 0.0f;
+ m_flAccuracy = 0.9f;
+}
+
+bool CWeaponGlock::Deploy( )
+{
+ m_iBurstShotsRemaining = 0;
+ m_fNextBurstShot = 0.0f;
+ m_flAccuracy = 0.9f;
+
+ return BaseClass::Deploy();
+}
+
+void CWeaponGlock::SecondaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( m_bBurstMode )
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "#Switch_To_SemiAuto" );
+ m_bBurstMode = false;
+ m_weaponMode = Primary_Mode;
+ }
+ else
+ {
+ ClientPrint( pPlayer, HUD_PRINTCENTER, "#Switch_To_BurstFire" );
+ m_bBurstMode = true;
+ m_weaponMode = Secondary_Mode;
+ }
+
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.3;
+}
+
+float CWeaponGlock::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ if ( m_bBurstMode )
+ {
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ return 1.2f * (1 - m_flAccuracy);
+
+ else if (pPlayer->GetAbsVelocity().Length2D() > 5)
+ return 0.185f * (1 - m_flAccuracy);
+
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ return 0.095f * (1 - m_flAccuracy);
+
+ else
+ return 0.3f * (1 - m_flAccuracy);
+ }
+ else
+ {
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ return 1.0f * (1 - m_flAccuracy);
+
+ else if (pPlayer->GetAbsVelocity().Length2D() > 5)
+ return 0.165f * (1 - m_flAccuracy);
+
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ return 0.075f * (1 - m_flAccuracy);
+
+ else
+ return 0.1f * (1 - m_flAccuracy);
+ }
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+
+void CWeaponGlock::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ float flCycleTime = m_bBurstMode ? 0.5f : GetCSWpnData().m_flCycleTime;
+
+ // Mark the time of this shot and determine the accuracy modifier based on the last shot fired...
+ m_flAccuracy -= (0.275)*(0.325 - (gpGlobals->curtime - m_flLastFire));
+
+ if (m_flAccuracy > 0.9)
+ m_flAccuracy = 0.9;
+ else if (m_flAccuracy < 0.6)
+ m_flAccuracy = 0.6;
+
+ m_flLastFire = gpGlobals->curtime;
+
+ if (m_iClip1 <= 0)
+ {
+ if ( m_bFireOnEmpty )
+
+ {
+ PlayEmptySound();
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.1f;
+ m_bFireOnEmpty = false;
+ }
+
+ return;
+ }
+
+ pPlayer->m_iShotsFired++;
+
+ m_iClip1--;
+
+ pPlayer->DoMuzzleFlash();
+
+ //SetPlayerShieldAnim();
+
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ // non-silenced
+ //pPlayer->m_iWeaponVolume = NORMAL_GUN_VOLUME;
+ //pPlayer->m_iWeaponFlash = NORMAL_GUN_FLASH;
+
+ FX_FireBullets(
+ pPlayer->entindex(),
+ pPlayer->Weapon_ShootPosition(),
+ pPlayer->EyeAngles() + 2.0f * pPlayer->GetPunchAngle(),
+ GetWeaponID(),
+ Primary_Mode,
+ CBaseEntity::GetPredictionRandomSeed() & 255, // wrap it for network traffic so it's the same between client and server
+ GetInaccuracy(),
+ GetSpread(),
+ gpGlobals->curtime);
+
+ m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + flCycleTime;
+
+ if (!m_iClip1 && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0)
+ {
+ // HEV suit - indicate out of ammo condition
+ pPlayer->SetSuitUpdate("!HEV_AMO0", false, 0);
+ }
+
+ SetWeaponIdleTime( gpGlobals->curtime + 2.5f );
+
+ if ( m_bBurstMode )
+ {
+ // Fire off the next two rounds
+ m_fNextBurstShot = gpGlobals->curtime + kGlockBurstCycleTime;
+ m_iBurstShotsRemaining = 2;
+
+ SendWeaponAnim( ACT_VM_SECONDARYATTACK );
+ }
+ else
+ {
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+ }
+
+ // update accuracy
+ m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyImpulseFire[m_weaponMode];
+
+ //ResetPlayerShieldAnim();
+}
+
+
+// GOOSEMAN : FireRemaining used by Glock18
+
+void CWeaponGlock::FireRemaining( float fSpread )
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ Error( "!pPlayer" );
+
+ if ( m_iBurstShotsRemaining == 0 )
+ return;
+
+ if (m_iClip1 <= 0)
+ {
+ m_iClip1 = 0;
+ m_iBurstShotsRemaining = 0;
+ m_fNextBurstShot = 0.0f;
+ return;
+ }
+ --m_iClip1;
+
+ // TODO FIXME damage = 18, rangemode 0.9
+
+ float fInaccuracy = GetInaccuracy();
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ fInaccuracy = 0.05;
+
+ FX_FireBullets(
+ pPlayer->entindex(),
+ pPlayer->Weapon_ShootPosition(),
+ pPlayer->EyeAngles() + 2.0f * pPlayer->GetPunchAngle(),
+ GetWeaponID(),
+ Secondary_Mode,
+ CBaseEntity::GetPredictionRandomSeed() & 255, // wrap it for network traffic so it's the same between client and server
+ fInaccuracy,
+ GetSpread(),
+ m_fNextBurstShot);
+
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+ pPlayer->m_iShotsFired++;
+
+ --m_iBurstShotsRemaining;
+
+ if ( m_iBurstShotsRemaining > 0 )
+ m_fNextBurstShot += kGlockBurstCycleTime;
+ else
+ m_fNextBurstShot = 0.0;
+
+ // update accuracy
+ m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyImpulseFire[Secondary_Mode];
+}
+
+
+void CWeaponGlock::ItemPostFrame()
+{
+ while ( m_iBurstShotsRemaining > 0 && gpGlobals->curtime >= m_fNextBurstShot )
+ {
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ FireRemaining(0.05f);
+ else
+ FireRemaining(GetSpread());
+ }
+
+ BaseClass::ItemPostFrame();
+}
+
+
+bool CWeaponGlock::Reload()
+{
+ if ( m_iBurstShotsRemaining != 0 )
+ return true;
+
+ if ( !DefaultPistolReload() )
+ return false;
+
+ m_flAccuracy = 0.9;
+ return true;
+}
+
+void CWeaponGlock::WeaponIdle()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if (m_flTimeWeaponIdle > gpGlobals->curtime)
+ return;
+
+ if ( pPlayer->HasShield() )
+ {
+ SetWeaponIdleTime( gpGlobals->curtime + 20 );
+
+ //MIKETODO: shields
+ //if ( FBitSet(m_iWeaponState, WPNSTATE_SHIELD_DRAWN) )
+ // SendWeaponAnim( GLOCK18_SHIELD_IDLE, UseDecrement() ? 1:0 );
+ }
+ else
+ {
+ // only idle if the slid isn't back
+ if (m_iClip1 != 0)
+ {
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+ }
+}
diff --git a/game/shared/cstrike/weapon_hegrenade.cpp b/game/shared/cstrike/weapon_hegrenade.cpp
new file mode 100644
index 0000000..e8303d0
--- /dev/null
+++ b/game/shared/cstrike/weapon_hegrenade.cpp
@@ -0,0 +1,67 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbase.h"
+#include "gamerules.h"
+#include "npcevent.h"
+#include "engine/IEngineSound.h"
+#include "weapon_hegrenade.h"
+
+
+#ifdef CLIENT_DLL
+
+#else
+
+ #include "cs_player.h"
+ #include "items.h"
+ #include "hegrenade_projectile.h"
+
+#endif
+
+
+#define GRENADE_TIMER 3.0f //Seconds
+
+
+
+IMPLEMENT_NETWORKCLASS_ALIASED( HEGrenade, DT_HEGrenade )
+
+BEGIN_NETWORK_TABLE(CHEGrenade, DT_HEGrenade)
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CHEGrenade )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_hegrenade, CHEGrenade );
+PRECACHE_WEAPON_REGISTER( weapon_hegrenade );
+
+
+#ifndef CLIENT_DLL
+
+ BEGIN_DATADESC( CHEGrenade )
+ END_DATADESC()
+
+ void CHEGrenade::EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer )
+ {
+ //=============================================================================
+ // HPE_BEGIN:
+ // [Forrest] Start a new bullet group so that damage dealt by the grenade will be counted as a separate hit from damage previously dealt.
+ //=============================================================================
+#ifdef GAME_DLL
+ CCSPlayer *pCSPlayer = ToCSPlayer( pPlayer );
+ if ( pCSPlayer )
+ {
+ pCSPlayer->StartNewBulletGroup();
+ }
+#endif
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ CHEGrenadeProjectile::Create( vecSrc, vecAngles, vecVel, angImpulse, pPlayer, GRENADE_TIMER );
+ }
+
+#endif
+
diff --git a/game/shared/cstrike/weapon_hegrenade.h b/game/shared/cstrike/weapon_hegrenade.h
new file mode 100644
index 0000000..20e2d99
--- /dev/null
+++ b/game/shared/cstrike/weapon_hegrenade.h
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef WEAPON_HEGRENADE_H
+#define WEAPON_HEGRENADE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "weapon_basecsgrenade.h"
+
+
+#ifdef CLIENT_DLL
+
+ #define CHEGrenade C_HEGrenade
+
+#endif
+
+//-----------------------------------------------------------------------------
+// Fragmentation grenades
+//-----------------------------------------------------------------------------
+class CHEGrenade : public CBaseCSGrenade
+{
+public:
+ DECLARE_CLASS( CHEGrenade, CBaseCSGrenade );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CHEGrenade() {}
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_HEGRENADE; }
+
+#ifdef CLIENT_DLL
+
+#else
+ DECLARE_DATADESC();
+
+ virtual void EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer );
+
+#endif
+
+ CHEGrenade( const CHEGrenade & ) {}
+};
+
+
+#endif // WEAPON_HEGRENADE_H
diff --git a/game/shared/cstrike/weapon_knife.cpp b/game/shared/cstrike/weapon_knife.cpp
new file mode 100644
index 0000000..9bc549b
--- /dev/null
+++ b/game/shared/cstrike/weapon_knife.cpp
@@ -0,0 +1,517 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_knife.h"
+#include "cs_gamerules.h"
+
+#if defined( CLIENT_DLL )
+ #include "c_cs_player.h"
+#else
+ #include "cs_player.h"
+ #include "ilagcompensationmanager.h"
+ #include "cs_gamestats.h"
+#endif
+
+
+#define KNIFE_BODYHIT_VOLUME 128
+#define KNIFE_WALLHIT_VOLUME 512
+
+
+Vector head_hull_mins( -16, -16, -18 );
+Vector head_hull_maxs( 16, 16, 18 );
+
+#ifndef CLIENT_DLL
+ //-----------------------------------------------------------------------------
+ // Purpose: Only send to local player if this weapon is the active weapon
+ // Input : *pStruct -
+ // *pVarData -
+ // *pRecipients -
+ // objectID -
+ // Output : void*
+ //-----------------------------------------------------------------------------
+ void* SendProxy_SendActiveLocalKnifeDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
+ {
+ // Get the weapon entity
+ CBaseCombatWeapon *pWeapon = (CBaseCombatWeapon*)pVarData;
+ if ( pWeapon )
+ {
+ // Only send this chunk of data to the player carrying this weapon
+ CBasePlayer *pPlayer = ToBasePlayer( pWeapon->GetOwner() );
+ if ( pPlayer /*&& pPlayer->GetActiveWeapon() == pWeapon*/ )
+ {
+ pRecipients->SetOnly( pPlayer->GetClientIndex() );
+ return (void*)pVarData;
+ }
+ }
+
+ return NULL;
+ }
+ REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendActiveLocalKnifeDataTable );
+#endif
+
+// ----------------------------------------------------------------------------- //
+// CKnife tables.
+// ----------------------------------------------------------------------------- //
+
+IMPLEMENT_NETWORKCLASS_ALIASED( Knife, DT_WeaponKnife )
+
+BEGIN_NETWORK_TABLE_NOBASE( CKnife, DT_LocalActiveWeaponKnifeData )
+ #if !defined( CLIENT_DLL )
+ SendPropTime( SENDINFO( m_flSmackTime ) ),
+ #else
+ RecvPropTime( RECVINFO( m_flSmackTime ) ),
+ #endif
+END_NETWORK_TABLE()
+
+
+BEGIN_NETWORK_TABLE( CKnife, DT_WeaponKnife )
+ #if !defined( CLIENT_DLL )
+ SendPropDataTable("LocalActiveWeaponKnifeData", 0, &REFERENCE_SEND_TABLE(DT_LocalActiveWeaponKnifeData), SendProxy_SendActiveLocalKnifeDataTable ),
+ #else
+ RecvPropDataTable("LocalActiveWeaponKnifeData", 0, 0, &REFERENCE_RECV_TABLE(DT_LocalActiveWeaponKnifeData)),
+ #endif
+END_NETWORK_TABLE()
+
+
+#if defined CLIENT_DLL
+BEGIN_PREDICTION_DATA( CKnife )
+ DEFINE_PRED_FIELD( m_flSmackTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+END_PREDICTION_DATA()
+#endif
+
+
+LINK_ENTITY_TO_CLASS( weapon_knife, CKnife );
+PRECACHE_WEAPON_REGISTER( weapon_knife );
+
+#ifndef CLIENT_DLL
+
+ BEGIN_DATADESC( CKnife )
+ DEFINE_THINKFUNC( Smack )
+ END_DATADESC()
+
+#endif
+
+// ----------------------------------------------------------------------------- //
+// CKnife implementation.
+// ----------------------------------------------------------------------------- //
+
+CKnife::CKnife()
+{
+}
+
+
+bool CKnife::HasPrimaryAmmo()
+{
+ return true;
+}
+
+
+bool CKnife::CanBeSelected()
+{
+ return true;
+}
+
+void CKnife::Precache()
+{
+ BaseClass::Precache();
+
+ PrecacheScriptSound( "Weapon_Knife.Deploy" );
+ PrecacheScriptSound( "Weapon_Knife.Slash" );
+ PrecacheScriptSound( "Weapon_Knife.Stab" );
+ PrecacheScriptSound( "Weapon_Knife.Hit" );
+}
+
+void CKnife::Spawn()
+{
+ Precache();
+
+ m_iClip1 = -1;
+ BaseClass::Spawn();
+}
+
+
+bool CKnife::Deploy()
+{
+ CPASAttenuationFilter filter( this );
+ filter.UsePredictionRules();
+ EmitSound( filter, entindex(), "Weapon_Knife.Deploy" );
+
+ return BaseClass::Deploy();
+}
+
+void CKnife::Holster( int skiplocal )
+{
+ if ( GetPlayerOwner() )
+ {
+ GetPlayerOwner()->m_flNextAttack = gpGlobals->curtime + 0.5;
+ }
+}
+
+void CKnife::WeaponAnimation ( int iAnimation )
+{
+ /*
+ int flag;
+ #if defined( CLIENT_WEAPONS )
+ flag = FEV_NOTHOST;
+ #else
+ flag = 0;
+ #endif
+
+ PLAYBACK_EVENT_FULL( flag, pPlayer->edict(), m_usKnife,
+ 0.0, (float *)&g_vecZero, (float *)&g_vecZero,
+ 0.0,
+ 0.0,
+ iAnimation, 2, 3, 4 );
+ */
+}
+
+void FindHullIntersection( const Vector &vecSrc, trace_t &tr, const Vector &mins, const Vector &maxs, CBaseEntity *pEntity )
+{
+ int i, j, k;
+ float distance;
+ Vector minmaxs[2] = {mins, maxs};
+ trace_t tmpTrace;
+ Vector vecHullEnd = tr.endpos;
+ Vector vecEnd;
+
+ distance = 1e6f;
+
+ vecHullEnd = vecSrc + ((vecHullEnd - vecSrc)*2);
+ UTIL_TraceLine( vecSrc, vecHullEnd, MASK_SOLID, pEntity, COLLISION_GROUP_NONE, &tmpTrace );
+ if ( tmpTrace.fraction < 1.0 )
+ {
+ tr = tmpTrace;
+ return;
+ }
+
+ for ( i = 0; i < 2; i++ )
+ {
+ for ( j = 0; j < 2; j++ )
+ {
+ for ( k = 0; k < 2; k++ )
+ {
+ vecEnd.x = vecHullEnd.x + minmaxs[i][0];
+ vecEnd.y = vecHullEnd.y + minmaxs[j][1];
+ vecEnd.z = vecHullEnd.z + minmaxs[k][2];
+
+ UTIL_TraceLine( vecSrc, vecEnd, MASK_SOLID, pEntity, COLLISION_GROUP_NONE, &tmpTrace );
+ if ( tmpTrace.fraction < 1.0 )
+ {
+ float thisDistance = (tmpTrace.endpos - vecSrc).Length();
+ if ( thisDistance < distance )
+ {
+ tr = tmpTrace;
+ distance = thisDistance;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void CKnife::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( pPlayer )
+ {
+#if !defined (CLIENT_DLL)
+ // Move other players back to history positions based on local player's lag
+ lagcompensation->StartLagCompensation( pPlayer, pPlayer->GetCurrentCommand() );
+#endif
+ SwingOrStab( false );
+#if !defined (CLIENT_DLL)
+ lagcompensation->FinishLagCompensation( pPlayer );
+#endif
+ }
+}
+
+void CKnife::SecondaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( pPlayer && !pPlayer->m_bIsDefusing && !CSGameRules()->IsFreezePeriod() )
+ {
+#if !defined (CLIENT_DLL)
+ // Move other players back to history positions based on local player's lag
+ lagcompensation->StartLagCompensation( pPlayer, pPlayer->GetCurrentCommand() );
+#endif
+ SwingOrStab( true );
+#if !defined (CLIENT_DLL)
+ lagcompensation->FinishLagCompensation( pPlayer );
+#endif
+ }
+}
+
+#include "effect_dispatch_data.h"
+
+void CKnife::Smack( void )
+{
+ if ( !GetPlayerOwner() )
+ return;
+
+ m_trHit.m_pEnt = m_pTraceHitEnt;
+
+ if ( !m_trHit.m_pEnt || (m_trHit.surface.flags & SURF_SKY) )
+ return;
+
+ if ( m_trHit.fraction == 1.0 )
+ return;
+
+ if ( m_trHit.m_pEnt )
+ {
+ CPASAttenuationFilter filter( this );
+ filter.UsePredictionRules();
+
+ if( m_trHit.m_pEnt->IsPlayer() )
+ {
+ EmitSound( filter, entindex(), m_bStab?"Weapon_Knife.Stab":"Weapon_Knife.Hit" );
+ }
+ else
+ {
+ EmitSound( filter, entindex(), "Weapon_Knife.HitWall" );
+ }
+ }
+
+ CEffectData data;
+ data.m_vOrigin = m_trHit.endpos;
+ data.m_vStart = m_trHit.startpos;
+ data.m_nSurfaceProp = m_trHit.surface.surfaceProps;
+ data.m_nDamageType = DMG_SLASH;
+ data.m_nHitBox = m_trHit.hitbox;
+#ifdef CLIENT_DLL
+ data.m_hEntity = m_trHit.m_pEnt->GetRefEHandle();
+#else
+ data.m_nEntIndex = m_trHit.m_pEnt->entindex();
+#endif
+
+ CPASFilter filter( data.m_vOrigin );
+
+#ifndef CLIENT_DLL
+ filter.RemoveRecipient( GetPlayerOwner() );
+#endif
+
+ data.m_vAngles = GetPlayerOwner()->GetAbsAngles();
+ data.m_fFlags = 0x1; //IMPACT_NODECAL;
+ te->DispatchEffect( filter, 0.0, data.m_vOrigin, "KnifeSlash", data );
+}
+
+void CKnife::WeaponIdle()
+{
+ if (m_flTimeWeaponIdle > gpGlobals->curtime)
+ return;
+
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( pPlayer->IsShieldDrawn() )
+ return;
+
+ SetWeaponIdleTime( gpGlobals->curtime + 20 );
+
+ // only idle if the slid isn't back
+ SendWeaponAnim( ACT_VM_IDLE );
+}
+
+//=============================================================================
+// HPE_BEGIN:
+// [tj] Hacky cheat code to control knife damage
+//=============================================================================
+#ifndef CLIENT_DLL
+ ConVar KnifeDamageScale("knife_damage_scale", "100", FCVAR_DEVELOPMENTONLY);
+#endif
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+
+bool CKnife::SwingOrStab( bool bStab )
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return false;
+
+ float fRange = bStab ? 32 : 48; // knife range
+
+ Vector vForward; AngleVectors( pPlayer->EyeAngles(), &vForward );
+ Vector vecSrc = pPlayer->Weapon_ShootPosition();
+ Vector vecEnd = vecSrc + vForward * fRange;
+
+ trace_t tr;
+ UTIL_TraceLine( vecSrc, vecEnd, MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &tr );
+
+ //check for hitting glass - TODO - fix this hackiness, doesn't always line up with what FindHullIntersection returns
+#ifndef CLIENT_DLL
+ CTakeDamageInfo glassDamage( pPlayer, pPlayer, 42.0f, DMG_BULLET | DMG_NEVERGIB );
+ TraceAttackToTriggers( glassDamage, tr.startpos, tr.endpos, vForward );
+#endif
+
+ if ( tr.fraction >= 1.0 )
+ {
+ UTIL_TraceHull( vecSrc, vecEnd, head_hull_mins, head_hull_maxs, MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &tr );
+ if ( tr.fraction < 1.0 )
+ {
+ // Calculate the point of intersection of the line (or hull) and the object we hit
+ // This is and approximation of the "best" intersection
+ CBaseEntity *pHit = tr.m_pEnt;
+ if ( !pHit || pHit->IsBSPModel() )
+ FindHullIntersection( vecSrc, tr, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, pPlayer );
+ vecEnd = tr.endpos; // This is the point on the actual surface (the hull could have hit space)
+ }
+ }
+
+ bool bDidHit = tr.fraction < 1.0f;
+
+#ifndef CLIENT_DLL
+ bool bFirstSwing = (m_flNextPrimaryAttack + 0.4) < gpGlobals->curtime;
+#endif
+
+ float fPrimDelay, fSecDelay;
+
+ if ( bStab )
+ {
+ SendWeaponAnim( bDidHit ? ACT_VM_HITCENTER : ACT_VM_MISSCENTER );
+
+ fPrimDelay = fSecDelay = bDidHit ? 1.1f : 1.0f;
+
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN_PRIMARY );
+ }
+ else // swing
+ {
+ SendWeaponAnim( bDidHit ? ACT_VM_HITCENTER : ACT_VM_MISSCENTER );
+
+ fPrimDelay = bDidHit ? 0.5f : 0.4f;
+ fSecDelay = bDidHit ? 0.5f : 0.5f;
+
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN_SECONDARY );
+ }
+
+ if ( pPlayer->HasShield() )
+ {
+ fPrimDelay += 0.7f; // 0.7 seconds slower if we carry a shield
+ fSecDelay += 0.7f;
+ }
+
+ m_flNextPrimaryAttack = gpGlobals->curtime + fPrimDelay;
+ m_flNextSecondaryAttack = gpGlobals->curtime + fSecDelay;
+ SetWeaponIdleTime( gpGlobals->curtime + 2 );
+
+ if ( !bDidHit )
+ {
+ // play wiff or swish sound
+ CPASAttenuationFilter filter( this );
+ filter.UsePredictionRules();
+ EmitSound( filter, entindex(), "Weapon_Knife.Slash" );
+ }
+
+#ifndef CLIENT_DLL
+
+ float flDamage = 0.0f;
+ if ( bDidHit )
+ {
+ // play thwack, smack, or dong sound
+
+ CBaseEntity *pEntity = tr.m_pEnt;
+
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ ClearMultiDamage();
+
+ flDamage = 42.0f;
+
+ if ( bStab )
+ {
+ flDamage = 65.0f;
+
+ if ( pEntity && pEntity->IsPlayer() )
+ {
+ Vector vTragetForward;
+
+ AngleVectors( pEntity->GetAbsAngles(), &vTragetForward );
+
+ Vector2D vecLOS = (pEntity->GetAbsOrigin() - pPlayer->GetAbsOrigin()).AsVector2D();
+ Vector2DNormalize( vecLOS );
+
+ float flDot = vecLOS.Dot( vTragetForward.AsVector2D() );
+
+ //Triple the damage if we are stabbing them in the back.
+ if ( flDot > 0.80f )
+ flDamage *= 3;
+ }
+ }
+ else
+ {
+ if ( bFirstSwing )
+ {
+ // first swing does full damage
+ flDamage = 20;
+ }
+ else
+ {
+ // subsequent swings do less
+ flDamage = 15;
+ }
+ }
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Hacky cheat to lower knife damage for testing
+ //=============================================================================
+
+ flDamage *= (KnifeDamageScale.GetInt() / 100.0f);
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+
+ CTakeDamageInfo info( pPlayer, pPlayer, flDamage, DMG_BULLET | DMG_NEVERGIB );
+
+ CalculateMeleeDamageForce( &info, vForward, tr.endpos, 1.0f/flDamage );
+ pEntity->DispatchTraceAttack( info, vForward, &tr );
+ ApplyMultiDamage();
+ }
+
+ CCS_GameStats.Event_KnifeUse( pPlayer, bStab, flDamage );
+
+#endif
+
+ if ( bDidHit )
+ {
+ // delay the decal a bit
+ m_trHit = tr;
+
+ // Store the ent in an EHANDLE, just in case it goes away by the time we get into our think function.
+ m_pTraceHitEnt = tr.m_pEnt;
+
+ m_bStab = bStab; //store this so we know what hit sound to play
+
+ m_flSmackTime = gpGlobals->curtime + (bStab?0.2f:0.1f);
+ }
+
+ return bDidHit;
+}
+
+void CKnife::ItemPostFrame( void )
+{
+ if( m_flSmackTime > 0 && gpGlobals->curtime > m_flSmackTime )
+ {
+ Smack();
+ m_flSmackTime = -1;
+ }
+
+ BaseClass::ItemPostFrame();
+}
+
+bool CKnife::CanDrop()
+{
+ return false;
+}
+
+
diff --git a/game/shared/cstrike/weapon_knife.h b/game/shared/cstrike/weapon_knife.h
new file mode 100644
index 0000000..f7bf851
--- /dev/null
+++ b/game/shared/cstrike/weapon_knife.h
@@ -0,0 +1,81 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef WEAPON_KNIFE_H
+#define WEAPON_KNIFE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "weapon_csbase.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CKnife C_Knife
+
+#endif
+
+
+// ----------------------------------------------------------------------------- //
+// CKnife class definition.
+// ----------------------------------------------------------------------------- //
+
+class CKnife : public CWeaponCSBase
+{
+public:
+ DECLARE_CLASS( CKnife, CWeaponCSBase );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ #ifndef CLIENT_DLL
+ DECLARE_DATADESC();
+ #endif
+
+
+ CKnife();
+
+ // We say yes to this so the weapon system lets us switch to it.
+ virtual bool HasPrimaryAmmo();
+ virtual bool CanBeSelected();
+
+ virtual void Precache();
+
+ void Spawn();
+ void Smack();
+ //void Smack( trace_t *pTr, float delay );
+ bool SwingOrStab( bool bStab );
+ void PrimaryAttack();
+ void SecondaryAttack();
+ void WeaponAnimation( int iAnimation );
+
+ virtual void ItemPostFrame( void );
+
+// virtual float GetSpread() const;
+
+ bool Deploy();
+ void Holster( int skiplocal = 0 );
+ bool CanDrop();
+
+ void WeaponIdle();
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_KNIFE; }
+
+public:
+
+ trace_t m_trHit;
+ EHANDLE m_pTraceHitEnt;
+
+ CNetworkVar( float, m_flSmackTime );
+ bool m_bStab;
+
+private:
+ CKnife( const CKnife & ) {}
+};
+
+
+#endif // WEAPON_KNIFE_H
diff --git a/game/shared/cstrike/weapon_m249.cpp b/game/shared/cstrike/weapon_m249.cpp
new file mode 100644
index 0000000..90ee803
--- /dev/null
+++ b/game/shared/cstrike/weapon_m249.cpp
@@ -0,0 +1,105 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbasegun.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponM249 C_WeaponM249
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+
+#endif
+
+
+class CWeaponM249 : public CWeaponCSBaseGun
+{
+public:
+ DECLARE_CLASS( CWeaponM249, CWeaponCSBaseGun );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponM249();
+
+ virtual void PrimaryAttack();
+
+ virtual float GetInaccuracy() const;
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_M249; }
+
+
+private:
+ CWeaponM249( const CWeaponM249 & );
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponM249, DT_WeaponM249 )
+
+BEGIN_NETWORK_TABLE( CWeaponM249, DT_WeaponM249 )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponM249 )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_m249, CWeaponM249 );
+PRECACHE_WEAPON_REGISTER( weapon_m249 );
+
+
+
+CWeaponM249::CWeaponM249()
+{
+}
+
+float CWeaponM249::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ return 0.045f + 0.5f * m_flAccuracy;
+ else if (pPlayer->GetAbsVelocity().Length2D() > 140)
+ return 0.045f + 0.095f * m_flAccuracy;
+ else
+ return 0.03f * m_flAccuracy;
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+void CWeaponM249::PrimaryAttack( void )
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( !CSBaseGunFire( GetCSWpnData().m_flCycleTime, Primary_Mode ) )
+ return;
+
+ pPlayer = GetPlayerOwner();
+
+ // CSBaseGunFire can kill us, forcing us to drop our weapon, if we shoot something that explodes
+ if ( !pPlayer )
+ return;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ pPlayer->KickBack (1.8, 0.65, 0.45, 0.125, 5, 3.5, 8);
+
+ else if (pPlayer->GetAbsVelocity().Length2D() > 5)
+ pPlayer->KickBack (1.1, 0.5, 0.3, 0.06, 4, 3, 8);
+
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ pPlayer->KickBack (0.75, 0.325, 0.25, 0.025, 3.5, 2.5, 9);
+
+ else
+ pPlayer->KickBack (0.8, 0.35, 0.3, 0.03, 3.75, 3, 9);
+}
diff --git a/game/shared/cstrike/weapon_m3.cpp b/game/shared/cstrike/weapon_m3.cpp
new file mode 100644
index 0000000..d31cc16
--- /dev/null
+++ b/game/shared/cstrike/weapon_m3.cpp
@@ -0,0 +1,293 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbase.h"
+#include "fx_cs_shared.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponM3 C_WeaponM3
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+ #include "te_shotgun_shot.h"
+
+#endif
+
+
+class CWeaponM3 : public CWeaponCSBase
+{
+public:
+ DECLARE_CLASS( CWeaponM3, CWeaponCSBase );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponM3();
+
+ virtual void PrimaryAttack();
+ virtual bool Reload();
+ virtual void WeaponIdle();
+
+ virtual float GetInaccuracy() const;
+ virtual float GetSpread() const;
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_M3; }
+
+private:
+
+ CWeaponM3( const CWeaponM3 & );
+
+ float m_flPumpTime;
+ CNetworkVar( int, m_reloadState );
+
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponM3, DT_WeaponM3 )
+
+BEGIN_NETWORK_TABLE( CWeaponM3, DT_WeaponM3 )
+#ifdef CLIENT_DLL
+ RecvPropInt( RECVINFO( m_reloadState ) )
+#else
+ SendPropInt( SENDINFO( m_reloadState ), 2, SPROP_UNSIGNED )
+#endif
+END_NETWORK_TABLE()
+
+#if defined(CLIENT_DLL)
+BEGIN_PREDICTION_DATA( CWeaponM3 )
+DEFINE_PRED_FIELD( m_reloadState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_m3, CWeaponM3 );
+PRECACHE_WEAPON_REGISTER( weapon_m3 );
+
+
+
+CWeaponM3::CWeaponM3()
+{
+ m_flPumpTime = 0;
+ m_reloadState = 0;
+}
+
+float CWeaponM3::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ return 0.0f;
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+float CWeaponM3::GetSpread() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ return 0.0675f;
+
+ return GetCSWpnData().m_fSpread[Primary_Mode];
+}
+
+void CWeaponM3::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ float flCycleTime = GetCSWpnData().m_flCycleTime;
+
+ // don't fire underwater
+ if (pPlayer->GetWaterLevel() == 3)
+ {
+ PlayEmptySound( );
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.15;
+ return;
+ }
+
+ // Out of ammo?
+ if ( m_iClip1 <= 0 )
+ {
+ Reload();
+ if ( m_iClip1 == 0 )
+ {
+ PlayEmptySound();
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.2;
+ }
+
+ return;
+ }
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+
+ m_iClip1--;
+ pPlayer->DoMuzzleFlash();
+
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ // Dispatch the FX right away with full accuracy.
+ float flCurAttack = CalculateNextAttackTime( flCycleTime );
+ FX_FireBullets(
+ pPlayer->entindex(),
+ pPlayer->Weapon_ShootPosition(),
+ pPlayer->EyeAngles() + 2.0f * pPlayer->GetPunchAngle(),
+ GetWeaponID(),
+ Primary_Mode,
+ CBaseEntity::GetPredictionRandomSeed() & 255, // wrap it for network traffic so it's the same between client and server
+ GetInaccuracy(),
+ GetSpread(),
+ flCurAttack );
+
+ if (!m_iClip1 && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0)
+ {
+ // HEV suit - indicate out of ammo condition
+ pPlayer->SetSuitUpdate("!HEV_AMO0", false, 0);
+ }
+
+ if (m_iClip1 != 0)
+ m_flPumpTime = gpGlobals->curtime + 0.5;
+
+ if (m_iClip1 != 0)
+ SetWeaponIdleTime( gpGlobals->curtime + 2.5 );
+ else
+ SetWeaponIdleTime( gpGlobals->curtime + 0.875 );
+ m_reloadState = 0;
+
+ // update accuracy
+ m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyImpulseFire[Primary_Mode];
+
+ // Update punch angles.
+ QAngle angle = pPlayer->GetPunchAngle();
+
+ if ( pPlayer->GetFlags() & FL_ONGROUND )
+ {
+ angle.x -= SharedRandomInt( "M3PunchAngleGround", 4, 6 );
+ }
+ else
+ {
+ angle.x -= SharedRandomInt( "M3PunchAngleAir", 8, 11 );
+ }
+
+ pPlayer->SetPunchAngle( angle );
+}
+
+
+bool CWeaponM3::Reload()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return false;
+
+ if (pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 || m_iClip1 == GetMaxClip1())
+ return true;
+
+ // don't reload until recoil is done
+ if (m_flNextPrimaryAttack > gpGlobals->curtime)
+ return true;
+
+ // check to see if we're ready to reload
+ if (m_reloadState == 0)
+ {
+ pPlayer->SetAnimation( PLAYER_RELOAD );
+
+ SendWeaponAnim( ACT_SHOTGUN_RELOAD_START );
+ m_reloadState = 1;
+ pPlayer->m_flNextAttack = gpGlobals->curtime + 0.5;
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
+ SetWeaponIdleTime( gpGlobals->curtime + 0.5 );
+
+#ifdef GAME_DLL
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD_START );
+#endif
+
+ return true;
+ }
+ else if (m_reloadState == 1)
+ {
+ if (m_flTimeWeaponIdle > gpGlobals->curtime)
+ return true;
+ // was waiting for gun to move to side
+ m_reloadState = 2;
+
+ SendWeaponAnim( ACT_VM_RELOAD );
+ SetWeaponIdleTime( gpGlobals->curtime + 0.5 );
+#ifdef GAME_DLL
+ if ( m_iClip1 == 7 )
+ {
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD_END );
+ }
+ else
+ {
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD_LOOP );
+ }
+#endif
+ }
+ else
+ {
+ // Add them to the clip
+ m_iClip1 += 1;
+
+#ifdef GAME_DLL
+ SendReloadEvents();
+#endif
+
+ CCSPlayer *pPlayer = GetPlayerOwner();
+
+ if ( pPlayer )
+ pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType );
+
+ m_reloadState = 1;
+ }
+
+ return true;
+}
+
+
+void CWeaponM3::WeaponIdle()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if (m_flPumpTime && m_flPumpTime < gpGlobals->curtime)
+ {
+ // play pumping sound
+ m_flPumpTime = 0;
+ }
+
+ if (m_flTimeWeaponIdle < gpGlobals->curtime)
+ {
+ if (m_iClip1 == 0 && m_reloadState == 0 && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ))
+ {
+ Reload( );
+ }
+ else if (m_reloadState != 0)
+ {
+ if (m_iClip1 != 8 && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ))
+ {
+ Reload( );
+ }
+ else
+ {
+ // reload debounce has timed out
+ //MIKETODO: shotgun anims
+ SendWeaponAnim( ACT_SHOTGUN_RELOAD_FINISH );
+
+ // play cocking sound
+ m_reloadState = 0;
+ SetWeaponIdleTime( gpGlobals->curtime + 1.5 );
+ }
+ }
+ else
+ {
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+ }
+}
diff --git a/game/shared/cstrike/weapon_m4a1.cpp b/game/shared/cstrike/weapon_m4a1.cpp
new file mode 100644
index 0000000..e6ad1aa
--- /dev/null
+++ b/game/shared/cstrike/weapon_m4a1.cpp
@@ -0,0 +1,351 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbasegun.h"
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponM4A1 C_WeaponM4A1
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+
+#endif
+
+
+class CWeaponM4A1 : public CWeaponCSBaseGun
+{
+public:
+ DECLARE_CLASS( CWeaponM4A1, CWeaponCSBaseGun );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponM4A1();
+
+ virtual void Spawn();
+ virtual void Precache();
+
+ virtual void SecondaryAttack();
+ virtual void PrimaryAttack();
+ virtual bool Deploy();
+ virtual bool Reload();
+ virtual void WeaponIdle();
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo );
+ virtual void Drop( const Vector &vecVelocity );
+
+ virtual float GetInaccuracy() const;
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_M4A1; }
+
+ // return true if this weapon has a silencer equipped
+ virtual bool IsSilenced( void ) const { return m_bSilencerOn; }
+
+ virtual Activity GetDeployActivity( void );
+
+#ifdef CLIENT_DLL
+ virtual int GetMuzzleFlashStyle( void );
+#endif
+
+ virtual const char *GetWorldModel( void ) const;
+ virtual int GetWorldModelIndex( void );
+
+private:
+
+ CWeaponM4A1( const CWeaponM4A1 & );
+
+ void DoFireEffects();
+
+ CNetworkVar( bool, m_bSilencerOn );
+ CNetworkVar( float, m_flDoneSwitchingSilencer ); // soonest time switching the silencer will be complete
+
+ int m_silencedModelIndex;
+ bool m_inPrecache;
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponM4A1, DT_WeaponM4A1 )
+
+BEGIN_NETWORK_TABLE( CWeaponM4A1, DT_WeaponM4A1 )
+ #ifdef CLIENT_DLL
+ RecvPropBool( RECVINFO( m_bSilencerOn ) ),
+ RecvPropTime( RECVINFO( m_flDoneSwitchingSilencer ) ),
+ #else
+ SendPropBool( SENDINFO( m_bSilencerOn ) ),
+ SendPropTime( SENDINFO( m_flDoneSwitchingSilencer ) ),
+ #endif
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponM4A1 )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_m4a1, CWeaponM4A1 );
+PRECACHE_WEAPON_REGISTER( weapon_m4a1 );
+
+
+
+CWeaponM4A1::CWeaponM4A1()
+{
+ m_bSilencerOn = false;
+ m_flDoneSwitchingSilencer = 0.0f;
+ m_inPrecache = false;
+}
+
+
+void CWeaponM4A1::Spawn( )
+{
+ BaseClass::Spawn();
+
+ m_bSilencerOn = false;
+ m_weaponMode = Primary_Mode;
+ m_flDoneSwitchingSilencer = 0.0f;
+ m_bDelayFire = true;
+}
+
+
+void CWeaponM4A1::Precache()
+{
+ m_inPrecache = true;
+ BaseClass::Precache();
+
+ m_silencedModelIndex = CBaseEntity::PrecacheModel( GetCSWpnData().m_szSilencerModel );
+ m_inPrecache = false;
+}
+
+
+int CWeaponM4A1::GetWorldModelIndex( void )
+{
+ if ( !m_bSilencerOn || m_inPrecache )
+ {
+ return m_iWorldModelIndex;
+ }
+ else
+ {
+ return m_silencedModelIndex;
+ }
+}
+
+
+const char * CWeaponM4A1::GetWorldModel( void ) const
+{
+ if ( !m_bSilencerOn || m_inPrecache )
+ {
+ return BaseClass::GetWorldModel();
+ }
+ else
+ {
+ return GetCSWpnData().m_szSilencerModel;
+ }
+}
+
+
+bool CWeaponM4A1::Deploy()
+{
+ bool ret = BaseClass::Deploy();
+
+ m_flDoneSwitchingSilencer = 0.0f;
+ m_bDelayFire = true;
+
+ return ret;
+}
+
+Activity CWeaponM4A1::GetDeployActivity( void )
+{
+ if( IsSilenced() )
+ {
+ return ACT_VM_DRAW_SILENCED;
+ }
+ else
+ {
+ return ACT_VM_DRAW;
+ }
+}
+
+bool CWeaponM4A1::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ if ( gpGlobals->curtime < m_flDoneSwitchingSilencer )
+ {
+ // still switching the silencer. Cancel the switch.
+ m_bSilencerOn = !m_bSilencerOn;
+ m_weaponMode = m_bSilencerOn ? Secondary_Mode : Primary_Mode;
+ SetWeaponModelIndex( GetWorldModel() );
+ }
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+void CWeaponM4A1::Drop( const Vector &vecVelocity )
+{
+ if ( gpGlobals->curtime < m_flDoneSwitchingSilencer )
+ {
+ // still switching the silencer. Cancel the switch.
+ m_bSilencerOn = !m_bSilencerOn;
+ m_weaponMode = m_bSilencerOn ? Secondary_Mode : Primary_Mode;
+ SetWeaponModelIndex( GetWorldModel() );
+ }
+
+ BaseClass::Drop( vecVelocity );
+}
+
+void CWeaponM4A1::SecondaryAttack()
+{
+ if ( m_bSilencerOn )
+ {
+ m_bSilencerOn = false;
+ m_weaponMode = Primary_Mode;
+ SendWeaponAnim( ACT_VM_DETACH_SILENCER );
+ }
+ else
+ {
+ m_bSilencerOn = true;
+ m_weaponMode = Secondary_Mode;
+ SendWeaponAnim( ACT_VM_ATTACH_SILENCER );
+ }
+ m_flDoneSwitchingSilencer = gpGlobals->curtime + 2;
+
+ m_flNextSecondaryAttack = gpGlobals->curtime + 2;
+ m_flNextPrimaryAttack = gpGlobals->curtime + 2;
+ SetWeaponIdleTime( gpGlobals->curtime + 2 );
+
+ SetWeaponModelIndex( GetWorldModel() );
+}
+
+float CWeaponM4A1::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ {
+ return 0.035f + 0.4f * m_flAccuracy;
+ }
+ else if (pPlayer->GetAbsVelocity().Length2D() > 140)
+ {
+ return 0.035f + 0.07f * m_flAccuracy;
+ }
+ else
+ {
+ if ( m_bSilencerOn )
+ return 0.025f * m_flAccuracy;
+ else
+ return 0.02f * m_flAccuracy;
+ }
+ }
+ else
+ {
+ return BaseClass::GetInaccuracy();
+ }
+}
+
+
+void CWeaponM4A1::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( !CSBaseGunFire( GetCSWpnData().m_flCycleTime, m_weaponMode ) )
+ return;
+
+ if ( m_bSilencerOn )
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK_SILENCED );
+
+ pPlayer = GetPlayerOwner();
+
+ // CSBaseGunFire can kill us, forcing us to drop our weapon, if we shoot something that explodes
+ if ( !pPlayer )
+ return;
+
+ if (pPlayer->GetAbsVelocity().Length2D() > 5)
+ pPlayer->KickBack (1.0, 0.45, 0.28, 0.045, 3.75, 3, 7);
+ else if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ pPlayer->KickBack (1.2, 0.5, 0.23, 0.15, 5.5, 3.5, 6);
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ pPlayer->KickBack (0.6, 0.3, 0.2, 0.0125, 3.25, 2, 7);
+ else
+ pPlayer->KickBack (0.65, 0.35, 0.25, 0.015, 3.5, 2.25, 7);
+}
+
+
+void CWeaponM4A1::DoFireEffects()
+{
+ if ( !m_bSilencerOn )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( pPlayer )
+ {
+ pPlayer->DoMuzzleFlash();
+ }
+ }
+}
+
+bool CWeaponM4A1::Reload()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return false;
+
+ if (pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) <= 0)
+ return false;
+
+ int iResult = 0;
+
+ if ( m_bSilencerOn )
+ iResult = DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD_SILENCED );
+ else
+ iResult = DefaultReload( GetMaxClip1(), GetMaxClip2(), ACT_VM_RELOAD );
+
+ if ( !iResult )
+ return false;
+
+ pPlayer->SetAnimation( PLAYER_RELOAD );
+
+ if ((iResult) && (pPlayer->GetFOV() != pPlayer->GetDefaultFOV()))
+ {
+ pPlayer->SetFOV( pPlayer, pPlayer->GetDefaultFOV() );
+ }
+
+ m_flAccuracy = 0.2;
+ pPlayer->m_iShotsFired = 0;
+ m_bDelayFire = false;
+ return true;
+}
+
+
+void CWeaponM4A1::WeaponIdle()
+{
+ if (m_flTimeWeaponIdle > gpGlobals->curtime)
+ return;
+
+ // only idle if the slid isn't back
+ if ( m_iClip1 != 0 )
+ {
+ SetWeaponIdleTime( gpGlobals->curtime + GetCSWpnData().m_flIdleInterval );
+ if ( m_bSilencerOn )
+ SendWeaponAnim( ACT_VM_IDLE_SILENCED );
+ else
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+}
+
+
+#ifdef CLIENT_DLL
+int CWeaponM4A1::GetMuzzleFlashStyle( void )
+{
+ if( m_bSilencerOn )
+ {
+ return CS_MUZZLEFLASH_NONE;
+ }
+ else
+ {
+ return CS_MUZZLEFLASH_X;
+ }
+}
+#endif
diff --git a/game/shared/cstrike/weapon_mac10.cpp b/game/shared/cstrike/weapon_mac10.cpp
new file mode 100644
index 0000000..e3979fd
--- /dev/null
+++ b/game/shared/cstrike/weapon_mac10.cpp
@@ -0,0 +1,130 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbasegun.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponMAC10 C_WeaponMAC10
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+
+#endif
+
+
+class CWeaponMAC10 : public CWeaponCSBaseGun
+{
+public:
+ DECLARE_CLASS( CWeaponMAC10, CWeaponCSBaseGun );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponMAC10();
+
+ virtual void Spawn();
+ virtual void PrimaryAttack();
+ virtual bool Deploy();
+ virtual bool Reload();
+
+ virtual float GetInaccuracy() const;
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_MAC10; }
+
+
+private:
+ CWeaponMAC10( const CWeaponMAC10 & );
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponMAC10, DT_WeaponMAC10 )
+
+BEGIN_NETWORK_TABLE( CWeaponMAC10, DT_WeaponMAC10 )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponMAC10 )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_mac10, CWeaponMAC10 );
+PRECACHE_WEAPON_REGISTER( weapon_mac10 );
+
+
+
+CWeaponMAC10::CWeaponMAC10()
+{
+}
+
+
+void CWeaponMAC10::Spawn( )
+{
+ BaseClass::Spawn();
+
+ m_flAccuracy = 0.15;
+}
+
+
+bool CWeaponMAC10::Deploy()
+{
+ bool ret = BaseClass::Deploy();
+
+ m_flAccuracy = 0.15;
+
+ return ret;
+}
+
+bool CWeaponMAC10::Reload()
+{
+ bool ret = BaseClass::Reload();
+
+ m_flAccuracy = 0.15;
+
+ return ret;
+}
+
+
+float CWeaponMAC10::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ return 0.375f * m_flAccuracy;
+ else
+ return 0.03f * m_flAccuracy;
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+void CWeaponMAC10::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( !CSBaseGunFire( GetCSWpnData().m_flCycleTime, Primary_Mode ) )
+ return;
+
+ // CSBaseGunFire can kill us, forcing us to drop our weapon, if we shoot something that explodes
+ pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) ) // jumping
+ pPlayer->KickBack (1.3, 0.55, 0.4, 0.05, 4.75, 3.75, 5);
+ else if (pPlayer->GetAbsVelocity().Length2D() > 5) // running
+ pPlayer->KickBack (0.9, 0.45, 0.25, 0.035, 3.5, 2.75, 7);
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) ) // ducking
+ pPlayer->KickBack (0.75, 0.4, 0.175, 0.03, 2.75, 2.5, 10);
+ else // standing
+ pPlayer->KickBack (0.775, 0.425, 0.2, 0.03, 3, 2.75, 9);
+}
diff --git a/game/shared/cstrike/weapon_mp5navy.cpp b/game/shared/cstrike/weapon_mp5navy.cpp
new file mode 100644
index 0000000..1544550
--- /dev/null
+++ b/game/shared/cstrike/weapon_mp5navy.cpp
@@ -0,0 +1,129 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbasegun.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponMP5Navy C_WeaponMP5Navy
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+
+#endif
+
+
+class CWeaponMP5Navy : public CWeaponCSBaseGun
+{
+public:
+ DECLARE_CLASS( CWeaponMP5Navy, CWeaponCSBaseGun );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponMP5Navy();
+
+ virtual void Spawn();
+ virtual void PrimaryAttack();
+ virtual bool Deploy();
+ virtual bool Reload();
+
+ virtual float GetInaccuracy() const;
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_MP5NAVY; }
+
+
+private:
+ CWeaponMP5Navy( const CWeaponMP5Navy & );
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponMP5Navy, DT_WeaponMP5Navy )
+
+BEGIN_NETWORK_TABLE( CWeaponMP5Navy, DT_WeaponMP5Navy )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponMP5Navy )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_mp5navy, CWeaponMP5Navy );
+PRECACHE_WEAPON_REGISTER( weapon_mp5navy );
+
+
+
+CWeaponMP5Navy::CWeaponMP5Navy()
+{
+}
+
+void CWeaponMP5Navy::Spawn()
+{
+ BaseClass::Spawn();
+
+ m_flAccuracy = 0.0;
+}
+
+
+bool CWeaponMP5Navy::Deploy( )
+{
+ bool ret = BaseClass::Deploy();
+
+ m_flAccuracy = 0.0;
+
+ return ret;
+}
+
+bool CWeaponMP5Navy::Reload( )
+{
+ bool ret = BaseClass::Reload();
+
+ m_flAccuracy = 0.0;
+
+ return ret;
+}
+
+float CWeaponMP5Navy::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ return 0.2f * m_flAccuracy;
+ else
+ return 0.04f * m_flAccuracy;
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+void CWeaponMP5Navy::PrimaryAttack( void )
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( !CSBaseGunFire( GetCSWpnData().m_flCycleTime, Primary_Mode ) )
+ return;
+
+ // CSBaseGunFire can kill us, forcing us to drop our weapon, if we shoot something that explodes
+ pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ // Kick the gun based on the state of the player.
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ pPlayer->KickBack (0.9, 0.475, 0.35, 0.0425, 5, 3, 6);
+ else if (pPlayer->GetAbsVelocity().Length2D() > 5)
+ pPlayer->KickBack (0.5, 0.275, 0.2, 0.03, 3, 2, 10);
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ pPlayer->KickBack (0.225, 0.15, 0.1, 0.015, 2, 1, 10);
+ else
+ pPlayer->KickBack (0.25, 0.175, 0.125, 0.02, 2.25, 1.25, 10);
+}
diff --git a/game/shared/cstrike/weapon_p228.cpp b/game/shared/cstrike/weapon_p228.cpp
new file mode 100644
index 0000000..04bec84
--- /dev/null
+++ b/game/shared/cstrike/weapon_p228.cpp
@@ -0,0 +1,204 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbase.h"
+#include "fx_cs_shared.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponP228 C_WeaponP228
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+
+#endif
+
+
+class CWeaponP228 : public CWeaponCSBase
+{
+public:
+ DECLARE_CLASS( CWeaponP228, CWeaponCSBase );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponP228();
+
+ virtual void Spawn();
+
+ virtual void PrimaryAttack();
+ virtual bool Deploy();
+
+ virtual bool Reload();
+ virtual void WeaponIdle();
+
+ virtual float GetInaccuracy() const;
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_P228; }
+
+private:
+
+ CWeaponP228( const CWeaponP228 & );
+
+ float m_flLastFire;
+};
+
+#if defined CLIENT_DLL
+BEGIN_PREDICTION_DATA( CWeaponP228 )
+ DEFINE_FIELD( m_flLastFire, FIELD_FLOAT ),
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_p228, CWeaponP228 );
+PRECACHE_WEAPON_REGISTER( weapon_p228 );
+
+
+
+CWeaponP228::CWeaponP228()
+{
+ m_flLastFire = gpGlobals->curtime;
+}
+
+
+void CWeaponP228::Spawn( )
+{
+ m_flAccuracy = 0.9;
+
+ BaseClass::Spawn();
+}
+
+
+bool CWeaponP228::Deploy( )
+{
+ m_flAccuracy = 0.9;
+
+ return BaseClass::Deploy();
+}
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponP228, DT_WeaponP228 )
+
+BEGIN_NETWORK_TABLE( CWeaponP228, DT_WeaponP228 )
+END_NETWORK_TABLE()
+
+
+float CWeaponP228::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ return 1.5f * (1 - m_flAccuracy);
+ else if (pPlayer->GetAbsVelocity().Length2D() > 5)
+ return 0.255f * (1 - m_flAccuracy);
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ return 0.075f * (1 - m_flAccuracy);
+ else
+ return 0.15f * (1 - m_flAccuracy);
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+
+void CWeaponP228::PrimaryAttack( void )
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ // Mark the time of this shot and determine the accuracy modifier based on the last shot fired...
+ m_flAccuracy -= (0.3)*(0.325 - (gpGlobals->curtime - m_flLastFire));
+
+ if (m_flAccuracy > 0.9)
+ m_flAccuracy = 0.9;
+ else if (m_flAccuracy < 0.6)
+ m_flAccuracy = 0.6;
+
+ m_flLastFire = gpGlobals->curtime;
+
+ if (m_iClip1 <= 0)
+ {
+ if ( m_bFireOnEmpty )
+ {
+ PlayEmptySound();
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.1f;
+ m_bFireOnEmpty = false;
+ }
+
+ return;
+ }
+
+ pPlayer->m_iShotsFired++;
+
+ m_iClip1--;
+
+ pPlayer->DoMuzzleFlash();
+ //SetPlayerShieldAnim();
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ // Aiming
+ FX_FireBullets(
+ pPlayer->entindex(),
+ pPlayer->Weapon_ShootPosition(),
+ pPlayer->EyeAngles() + 2.0f * pPlayer->GetPunchAngle(),
+ GetWeaponID(),
+ Primary_Mode,
+ CBaseEntity::GetPredictionRandomSeed() & 255,
+ GetInaccuracy(),
+ GetSpread());
+
+ m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + GetCSWpnData().m_flCycleTime;
+
+ if (!m_iClip1 && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0)
+ {
+ // HEV suit - indicate out of ammo condition
+ pPlayer->SetSuitUpdate("!HEV_AMO0", false, 0);
+ }
+
+ SetWeaponIdleTime( gpGlobals->curtime + 2 );
+
+ //ResetPlayerShieldAnim();
+
+ // update accuracy
+ m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyImpulseFire[Primary_Mode];
+
+ QAngle angle = pPlayer->GetPunchAngle();
+ angle.x -= 2;
+ pPlayer->SetPunchAngle( angle );
+}
+
+
+bool CWeaponP228::Reload()
+{
+ if ( !DefaultPistolReload() )
+ return false;
+
+ m_flAccuracy = 0.9;
+ return true;
+}
+
+void CWeaponP228::WeaponIdle()
+{
+ if (m_flTimeWeaponIdle > gpGlobals->curtime)
+ return;
+
+ // only idle if the slid isn't back
+ if (m_iClip1 != 0)
+ {
+ SetWeaponIdleTime( gpGlobals->curtime + 3.0 ) ;
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+}
diff --git a/game/shared/cstrike/weapon_p90.cpp b/game/shared/cstrike/weapon_p90.cpp
new file mode 100644
index 0000000..80ac973
--- /dev/null
+++ b/game/shared/cstrike/weapon_p90.cpp
@@ -0,0 +1,97 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbasegun.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponP90 C_WeaponP90
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+
+#endif
+
+
+class CWeaponP90 : public CWeaponCSBaseGun
+{
+public:
+ DECLARE_CLASS( CWeaponP90, CWeaponCSBaseGun );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponP90();
+
+ virtual void PrimaryAttack();
+
+ virtual float GetInaccuracy() const;
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_P90; }
+
+
+private:
+ CWeaponP90( const CWeaponP90 & );
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponP90, DT_WeaponP90 )
+
+BEGIN_NETWORK_TABLE( CWeaponP90, DT_WeaponP90 )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponP90 )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_p90, CWeaponP90 );
+PRECACHE_WEAPON_REGISTER( weapon_p90 );
+
+
+
+CWeaponP90::CWeaponP90()
+{
+}
+
+float CWeaponP90::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ return 0.3f * m_flAccuracy;
+ else if (pPlayer->GetAbsVelocity().Length2D() > 170)
+ return 0.115f * m_flAccuracy;
+ else
+ return 0.045f * m_flAccuracy;
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+void CWeaponP90::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( !CSBaseGunFire( GetCSWpnData().m_flCycleTime, Primary_Mode ) )
+ return;
+
+ // Kick the gun based on the state of the player.
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ pPlayer->KickBack (0.9, 0.45, 0.35, 0.04, 5.25, 3.5, 4);
+ else if (pPlayer->GetAbsVelocity().Length2D() > 5)
+ pPlayer->KickBack (0.45, 0.3, 0.2, 0.0275, 4, 2.25, 7);
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ pPlayer->KickBack (0.275, 0.2, 0.125, 0.02, 3, 1, 9);
+ else
+ pPlayer->KickBack (0.3, 0.225, 0.125, 0.02, 3.25, 1.25, 8);
+}
diff --git a/game/shared/cstrike/weapon_scout.cpp b/game/shared/cstrike/weapon_scout.cpp
new file mode 100644
index 0000000..8722e48
--- /dev/null
+++ b/game/shared/cstrike/weapon_scout.cpp
@@ -0,0 +1,220 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbasegun.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponScout C_WeaponScout
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+ #include "KeyValues.h"
+
+#endif
+
+const int cScoutMidZoomFOV = 40;
+const int cScoutMaxZoomFOV = 15;
+
+
+class CWeaponScout : public CWeaponCSBaseGun
+{
+public:
+ DECLARE_CLASS( CWeaponScout, CWeaponCSBaseGun );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponScout();
+
+ virtual void PrimaryAttack();
+ virtual void SecondaryAttack();
+
+ virtual float GetInaccuracy() const;
+ virtual float GetMaxSpeed() const;
+ virtual bool Reload();
+ virtual bool Deploy();
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_SCOUT; }
+
+
+private:
+
+ CWeaponScout( const CWeaponScout & );
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponScout, DT_WeaponScout )
+
+BEGIN_NETWORK_TABLE( CWeaponScout, DT_WeaponScout )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponScout )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_scout, CWeaponScout );
+PRECACHE_WEAPON_REGISTER( weapon_scout );
+
+
+
+CWeaponScout::CWeaponScout()
+{
+}
+
+void CWeaponScout::SecondaryAttack()
+{
+ const float kZoomTime = 0.10f;
+
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if (pPlayer == NULL)
+ {
+ Assert(pPlayer != NULL);
+ return;
+ }
+
+ if (pPlayer->GetFOV() == pPlayer->GetDefaultFOV())
+ {
+ pPlayer->SetFOV( pPlayer, cScoutMidZoomFOV, kZoomTime );
+ m_weaponMode = Secondary_Mode;
+ m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyAltSwitch;
+ }
+ else if (pPlayer->GetFOV() == cScoutMidZoomFOV)
+ {
+ pPlayer->SetFOV( pPlayer, cScoutMaxZoomFOV, kZoomTime );
+ m_weaponMode = Secondary_Mode;
+ }
+ else if (pPlayer->GetFOV() == cScoutMaxZoomFOV)
+ {
+ pPlayer->SetFOV( pPlayer, pPlayer->GetDefaultFOV(), kZoomTime );
+ m_weaponMode = Primary_Mode;
+ }
+
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.3f;
+ m_zoomFullyActiveTime = gpGlobals->curtime + 0.15; // The worst zoom time from above.
+
+#ifndef CLIENT_DLL
+ // If this isn't guarded, the sound will be emitted twice, once by the server and once by the client.
+ // Let the server play it since if only the client plays it, it's liable to get played twice cause of
+ // a prediction error. joy.
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Playing this from the player so that we don't try to play the sound outside the level.
+ //=============================================================================
+ if ( GetPlayerOwner() )
+ {
+ GetPlayerOwner()->EmitSound( "Default.Zoom" ); // zoom sound
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ // let the bots hear the rifle zoom
+ IGameEvent * event = gameeventmanager->CreateEvent( "weapon_zoom" );
+ if( event )
+ {
+ event->SetInt( "userid", pPlayer->GetUserID() );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+}
+
+float CWeaponScout::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if (pPlayer == NULL)
+ return 0.0f;
+
+ float fSpread = 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ fSpread = 0.2f;
+ else if (pPlayer->GetAbsVelocity().Length2D() > 170)
+ fSpread = 0.075f;
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ fSpread = 0.0f;
+ else
+ fSpread = 0.007f;
+
+ // If we are not zoomed in, or we have very recently zoomed and are still transitioning, the bullet diverts more.
+ if (pPlayer->GetFOV() == pPlayer->GetDefaultFOV() || (gpGlobals->curtime < m_zoomFullyActiveTime))
+ {
+ fSpread += 0.025;
+ }
+
+ return fSpread;
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+void CWeaponScout::PrimaryAttack( void )
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if (pPlayer == NULL)
+ return;
+
+ if ( !CSBaseGunFire( GetCSWpnData().m_flCycleTime, m_weaponMode ) )
+ return;
+
+ if ( m_weaponMode == Secondary_Mode )
+ {
+ float midFOVdistance = fabs( pPlayer->GetFOV() - (float)cScoutMidZoomFOV );
+ float farFOVdistance = fabs( pPlayer->GetFOV() - (float)cScoutMaxZoomFOV );
+
+ if ( midFOVdistance < farFOVdistance )
+ {
+ pPlayer->m_iLastZoom = cScoutMidZoomFOV;
+ }
+ else
+ {
+ pPlayer->m_iLastZoom = cScoutMaxZoomFOV;
+ }
+
+// #ifndef CLIENT_DLL
+ pPlayer->m_bResumeZoom = true;
+ pPlayer->SetFOV( pPlayer, pPlayer->GetDefaultFOV(), 0.05f );
+ m_weaponMode = Primary_Mode;
+// #endif
+ }
+
+ QAngle angle = pPlayer->GetPunchAngle();
+ angle.x -= 2;
+ pPlayer->SetPunchAngle( angle );
+}
+
+
+float CWeaponScout::GetMaxSpeed() const
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if (pPlayer == NULL)
+ {
+ Assert(pPlayer != NULL);
+ return BaseClass::GetMaxSpeed();
+ }
+
+ if ( pPlayer->GetFOV() == pPlayer->GetDefaultFOV() )
+ return BaseClass::GetMaxSpeed();
+ else
+ return 220; // zoomed in.
+}
+
+
+bool CWeaponScout::Reload()
+{
+ m_weaponMode = Primary_Mode;
+ return BaseClass::Reload();
+
+}
+
+bool CWeaponScout::Deploy()
+{
+ m_weaponMode = Primary_Mode;
+ return BaseClass::Deploy();
+}
diff --git a/game/shared/cstrike/weapon_sg550.cpp b/game/shared/cstrike/weapon_sg550.cpp
new file mode 100644
index 0000000..fc74183
--- /dev/null
+++ b/game/shared/cstrike/weapon_sg550.cpp
@@ -0,0 +1,214 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbasegun.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponSG550 C_WeaponSG550
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+ #include "KeyValues.h"
+
+#endif
+
+
+class CWeaponSG550 : public CWeaponCSBaseGun
+{
+public:
+ DECLARE_CLASS( CWeaponSG550, CWeaponCSBaseGun );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponSG550();
+
+ virtual void Spawn();
+ virtual void SecondaryAttack();
+ virtual void PrimaryAttack();
+ virtual bool Reload();
+ virtual bool Deploy();
+
+ virtual float GetInaccuracy() const;
+ virtual float GetMaxSpeed() const;
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_SG550; }
+
+
+private:
+ CWeaponSG550( const CWeaponSG550 & );
+
+ float m_flLastFire;
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponSG550, DT_WeaponSG550 )
+
+BEGIN_NETWORK_TABLE( CWeaponSG550, DT_WeaponSG550 )
+END_NETWORK_TABLE()
+
+#if defined CLIENT_DLL
+BEGIN_PREDICTION_DATA( CWeaponSG550 )
+ DEFINE_FIELD( m_flLastFire, FIELD_FLOAT ),
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_sg550, CWeaponSG550 );
+PRECACHE_WEAPON_REGISTER( weapon_sg550 );
+
+
+
+CWeaponSG550::CWeaponSG550()
+{
+ m_flLastFire = gpGlobals->curtime;
+}
+
+void CWeaponSG550::Spawn()
+{
+ BaseClass::Spawn();
+ m_flAccuracy = 0.98;
+}
+
+
+void CWeaponSG550::SecondaryAttack()
+{
+ const float kZoomTime = 0.10f;
+
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if (pPlayer->GetFOV() == pPlayer->GetDefaultFOV())
+ {
+ pPlayer->SetFOV( pPlayer, 40, kZoomTime );
+ m_weaponMode = Secondary_Mode;
+ m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyAltSwitch;
+ }
+ else if (pPlayer->GetFOV() == 40)
+ {
+ pPlayer->SetFOV( pPlayer, 15, kZoomTime );
+ m_weaponMode = Secondary_Mode;
+ }
+ else if (pPlayer->GetFOV() == 15)
+ {
+ pPlayer->SetFOV( pPlayer, pPlayer->GetDefaultFOV(), kZoomTime );
+ m_weaponMode = Primary_Mode;
+ }
+
+
+#ifndef CLIENT_DLL
+ // If this isn't guarded, the sound will be emitted twice, once by the server and once by the client.
+ // Let the server play it since if only the client plays it, it's liable to get played twice cause of
+ // a prediction error. joy.
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj] Playing this from the player so that we don't try to play the sound outside the level.
+ //=============================================================================
+ if ( GetPlayerOwner() )
+ {
+ GetPlayerOwner()->EmitSound( "Default.Zoom" ); // zoom sound.
+ }
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ // let the bots hear the rifle zoom
+ IGameEvent * event = gameeventmanager->CreateEvent( "weapon_zoom" );
+ if( event )
+ {
+ event->SetInt( "userid", pPlayer->GetUserID() );
+ gameeventmanager->FireEvent( event );
+ }
+#endif
+
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.3f;
+ m_zoomFullyActiveTime = gpGlobals->curtime + 0.3; // The worst zoom time from above.
+}
+
+float CWeaponSG550::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ float fSpread = 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ fSpread = 0.45f * (1 - m_flAccuracy);
+ else if (pPlayer->GetAbsVelocity().Length2D() > 5)
+ fSpread = 0.15f;
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ fSpread = 0.04f * (1 - m_flAccuracy);
+ else
+ fSpread = 0.05f * (1 - m_flAccuracy);
+
+ // If we are not zoomed in, or we have very recently zoomed and are still transitioning, the bullet diverts more.
+ if (pPlayer->GetFOV() == pPlayer->GetDefaultFOV() || (gpGlobals->curtime < m_zoomFullyActiveTime))
+ fSpread += 0.025;
+
+ return fSpread;
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+void CWeaponSG550::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ // Mark the time of this shot and determine the accuracy modifier based on the last shot fired...
+ m_flAccuracy = 0.65 + (0.35) * (gpGlobals->curtime - m_flLastFire);
+
+ if (m_flAccuracy > 0.98)
+ m_flAccuracy = 0.98;
+
+ m_flLastFire = gpGlobals->curtime;
+
+ if ( !CSBaseGunFire( GetCSWpnData().m_flCycleTime, m_weaponMode ) )
+ return;
+
+ QAngle angle = pPlayer->GetPunchAngle();
+ angle.x -= SharedRandomFloat("SG550PunchAngleX", 0.75, 1.25 ) + ( angle.x / 4 );
+ angle.y += SharedRandomFloat("SG550PunchAngleY", -0.75, 0.75 );
+ pPlayer->SetPunchAngle( angle );
+}
+
+bool CWeaponSG550::Reload()
+{
+ bool ret = BaseClass::Reload();
+
+ m_flAccuracy = 0.98;
+ m_weaponMode = Primary_Mode;
+
+ return ret;
+}
+
+bool CWeaponSG550::Deploy()
+{
+ bool ret = BaseClass::Deploy();
+
+ m_flAccuracy = 0.98;
+ m_weaponMode = Primary_Mode;
+
+ return ret;
+}
+
+float CWeaponSG550::GetMaxSpeed() const
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+
+ if ( !pPlayer || pPlayer->GetFOV() == 90 )
+ return BaseClass::GetMaxSpeed();
+ else
+ return 150; // zoomed in
+}
diff --git a/game/shared/cstrike/weapon_sg552.cpp b/game/shared/cstrike/weapon_sg552.cpp
new file mode 100644
index 0000000..ab84095
--- /dev/null
+++ b/game/shared/cstrike/weapon_sg552.cpp
@@ -0,0 +1,170 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbasegun.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponSG552 C_WeaponSG552
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+
+#endif
+
+
+class CWeaponSG552 : public CWeaponCSBaseGun
+{
+public:
+ DECLARE_CLASS( CWeaponSG552, CWeaponCSBaseGun );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponSG552();
+
+ virtual void SecondaryAttack();
+ virtual void PrimaryAttack();
+
+ virtual float GetInaccuracy() const;
+ virtual float GetMaxSpeed() const;
+ virtual bool Reload();
+ virtual bool Deploy();
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_SG552; }
+
+#ifdef CLIENT_DLL
+ virtual bool HideViewModelWhenZoomed( void ) { return false; }
+#endif
+
+private:
+
+ CWeaponSG552( const CWeaponSG552 & );
+
+ void SG552Fire( float flSpread, bool bZoomed );
+
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponSG552, DT_WeaponSG552 )
+
+BEGIN_NETWORK_TABLE( CWeaponSG552, DT_WeaponSG552 )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponSG552 )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_sg552, CWeaponSG552 );
+PRECACHE_WEAPON_REGISTER( weapon_sg552 );
+
+
+
+CWeaponSG552::CWeaponSG552()
+{
+}
+
+
+void CWeaponSG552::SecondaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if (pPlayer->GetFOV() == pPlayer->GetDefaultFOV())
+ {
+ pPlayer->SetFOV( pPlayer, 55, 0.2f );
+ m_weaponMode = Secondary_Mode;
+ }
+ else if (pPlayer->GetFOV() == 55)
+ {
+ pPlayer->SetFOV( pPlayer, 0, 0.15f );
+ m_weaponMode = Secondary_Mode;
+ }
+ else
+ {
+ //FIXME: This seems wrong
+ pPlayer->SetFOV( pPlayer, pPlayer->GetDefaultFOV() );
+ m_weaponMode = Primary_Mode;
+ }
+
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.3;
+}
+
+float CWeaponSG552::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ return 0.035f + 0.45f * m_flAccuracy;
+ else if (pPlayer->GetAbsVelocity().Length2D() > 140)
+ return 0.035f + 0.075f * m_flAccuracy;
+ else
+ return 0.02f * m_flAccuracy;
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+void CWeaponSG552::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ bool bZoomed = pPlayer->GetFOV() < pPlayer->GetDefaultFOV();
+
+ float flCycleTime = GetCSWpnData().m_flCycleTime;
+
+ if ( bZoomed )
+ flCycleTime = 0.135f;
+
+ if ( !CSBaseGunFire( flCycleTime, m_weaponMode ) )
+ return;
+
+ // CSBaseGunFire can kill us, forcing us to drop our weapon, if we shoot something that explodes
+ pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if (pPlayer->GetAbsVelocity().Length2D() > 5)
+ pPlayer->KickBack (1, 0.45, 0.28, 0.04, 4.25, 2.5, 7);
+ else if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ pPlayer->KickBack (1.25, 0.45, 0.22, 0.18, 6, 4, 5);
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ pPlayer->KickBack (0.6, 0.35, 0.2, 0.0125, 3.7, 2, 10);
+ else
+ pPlayer->KickBack (0.625, 0.375, 0.25, 0.0125, 4, 2.25, 9);
+}
+
+
+float CWeaponSG552::GetMaxSpeed() const
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+
+ if ( !pPlayer || pPlayer->GetFOV() == pPlayer->GetDefaultFOV() )
+ return BaseClass::GetMaxSpeed();
+ else
+ return 200; // zoomed in.
+}
+
+
+bool CWeaponSG552::Reload()
+{
+ m_weaponMode = Primary_Mode;
+ return BaseClass::Reload();
+}
+
+bool CWeaponSG552::Deploy()
+{
+ m_weaponMode = Primary_Mode;
+ return BaseClass::Deploy();
+}
diff --git a/game/shared/cstrike/weapon_smokegrenade.cpp b/game/shared/cstrike/weapon_smokegrenade.cpp
new file mode 100644
index 0000000..1703fbf
--- /dev/null
+++ b/game/shared/cstrike/weapon_smokegrenade.cpp
@@ -0,0 +1,49 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbase.h"
+#include "gamerules.h"
+#include "npcevent.h"
+#include "engine/IEngineSound.h"
+#include "weapon_smokegrenade.h"
+
+
+#ifdef CLIENT_DLL
+
+#else
+
+ #include "cs_player.h"
+ #include "items.h"
+ #include "smokegrenade_projectile.h"
+
+#endif
+
+
+IMPLEMENT_NETWORKCLASS_ALIASED( SmokeGrenade, DT_SmokeGrenade )
+
+BEGIN_NETWORK_TABLE(CSmokeGrenade, DT_SmokeGrenade)
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CSmokeGrenade )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_smokegrenade, CSmokeGrenade );
+PRECACHE_WEAPON_REGISTER( weapon_smokegrenade );
+
+
+#ifndef CLIENT_DLL
+
+ BEGIN_DATADESC( CSmokeGrenade )
+ END_DATADESC()
+
+ void CSmokeGrenade::EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer )
+ {
+ CSmokeGrenadeProjectile::Create( vecSrc, vecAngles, vecVel, angImpulse, pPlayer );
+ }
+
+#endif
+
diff --git a/game/shared/cstrike/weapon_smokegrenade.h b/game/shared/cstrike/weapon_smokegrenade.h
new file mode 100644
index 0000000..8d654cf
--- /dev/null
+++ b/game/shared/cstrike/weapon_smokegrenade.h
@@ -0,0 +1,52 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef WEAPON_SMOKEGRENADE_H
+#define WEAPON_SMOKEGRENADE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "weapon_basecsgrenade.h"
+
+
+#ifdef CLIENT_DLL
+
+ #define CSmokeGrenade C_SmokeGrenade
+
+#endif
+
+
+//-----------------------------------------------------------------------------
+// Smoke grenades
+//-----------------------------------------------------------------------------
+class CSmokeGrenade : public CBaseCSGrenade
+{
+public:
+ DECLARE_CLASS( CSmokeGrenade, CBaseCSGrenade );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CSmokeGrenade() {}
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_SMOKEGRENADE; }
+
+#ifdef CLIENT_DLL
+
+#else
+ DECLARE_DATADESC();
+
+ void EmitGrenade( Vector vecSrc, QAngle vecAngles, Vector vecVel, AngularImpulse angImpulse, CBasePlayer *pPlayer );
+
+#endif
+
+ CSmokeGrenade( const CSmokeGrenade & ) {}
+};
+
+
+#endif // WEAPON_SMOKEGRENADE_H
diff --git a/game/shared/cstrike/weapon_tmp.cpp b/game/shared/cstrike/weapon_tmp.cpp
new file mode 100644
index 0000000..b750d84
--- /dev/null
+++ b/game/shared/cstrike/weapon_tmp.cpp
@@ -0,0 +1,106 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbasegun.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponTMP C_WeaponTMP
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+
+#endif
+
+
+class CWeaponTMP : public CWeaponCSBaseGun
+{
+public:
+ DECLARE_CLASS( CWeaponTMP, CWeaponCSBaseGun );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponTMP();
+
+ virtual void PrimaryAttack();
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_TMP; }
+ virtual bool IsSilenced( void ) const { return true; }
+
+ virtual float GetInaccuracy() const;
+
+private:
+
+ CWeaponTMP( const CWeaponTMP & );
+
+ void DoFireEffects( void );
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponTMP, DT_WeaponTMP )
+
+BEGIN_NETWORK_TABLE( CWeaponTMP, DT_WeaponTMP )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponTMP )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_tmp, CWeaponTMP );
+PRECACHE_WEAPON_REGISTER( weapon_tmp );
+
+
+CWeaponTMP::CWeaponTMP()
+{
+}
+
+
+float CWeaponTMP::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ return 0.25f * m_flAccuracy;
+ else
+ return 0.03f * m_flAccuracy;
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+void CWeaponTMP::PrimaryAttack( void )
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( !CSBaseGunFire( GetCSWpnData().m_flCycleTime, Primary_Mode ) )
+ return;
+
+ // CSBaseGunFire can kill us, forcing us to drop our weapon, if we shoot something that explodes
+ pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ pPlayer->KickBack (1.1, 0.5, 0.35, 0.045, 4.5, 3.5, 6);
+ else if (pPlayer->GetAbsVelocity().Length2D() > 5)
+ pPlayer->KickBack (0.8, 0.4, 0.2, 0.03, 3, 2.5, 7);
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ pPlayer->KickBack (0.7, 0.35, 0.125, 0.025, 2.5, 2, 10);
+ else
+ pPlayer->KickBack (0.725, 0.375, 0.15, 0.025, 2.75, 2.25, 9);
+}
+
+void CWeaponTMP::DoFireEffects( void )
+{
+ // TMP is silenced, so do nothing
+}
diff --git a/game/shared/cstrike/weapon_ump45.cpp b/game/shared/cstrike/weapon_ump45.cpp
new file mode 100644
index 0000000..8bcc3e0
--- /dev/null
+++ b/game/shared/cstrike/weapon_ump45.cpp
@@ -0,0 +1,132 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbasegun.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponUMP45 C_WeaponUMP45
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+
+#endif
+
+
+class CWeaponUMP45 : public CWeaponCSBaseGun
+{
+public:
+ DECLARE_CLASS( CWeaponUMP45, CWeaponCSBaseGun );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponUMP45();
+
+ virtual void Spawn();
+ virtual void PrimaryAttack();
+ virtual bool Deploy();
+ virtual bool Reload();
+
+ virtual float GetInaccuracy() const;
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_UMP45; }
+
+
+private:
+
+ CWeaponUMP45( const CWeaponUMP45 & );
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponUMP45, DT_WeaponUMP45 )
+
+BEGIN_NETWORK_TABLE( CWeaponUMP45, DT_WeaponUMP45 )
+END_NETWORK_TABLE()
+
+BEGIN_PREDICTION_DATA( CWeaponUMP45 )
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( weapon_ump45, CWeaponUMP45 );
+PRECACHE_WEAPON_REGISTER( weapon_ump45 );
+
+
+
+CWeaponUMP45::CWeaponUMP45()
+{
+}
+
+
+void CWeaponUMP45::Spawn()
+{
+ BaseClass::Spawn();
+
+ m_flAccuracy = 0.0;
+}
+
+
+bool CWeaponUMP45::Deploy()
+{
+ bool ret = BaseClass::Deploy();
+
+ m_flAccuracy = 0.0;
+
+ return ret;
+}
+
+bool CWeaponUMP45::Reload()
+{
+ bool ret = BaseClass::Reload();
+
+ m_flAccuracy = 0.0;
+
+ return ret;
+}
+
+float CWeaponUMP45::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ return 0.24f * m_flAccuracy;
+ else
+ return 0.04f * m_flAccuracy;
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+void CWeaponUMP45::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if ( !CSBaseGunFire( GetCSWpnData().m_flCycleTime, Primary_Mode ) )
+ return;
+
+ // CSBaseGunFire can kill us, forcing us to drop our weapon, if we shoot something that explodes
+ pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ // Kick the gun based on the state of the player.
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ pPlayer->KickBack (0.125, 0.65, 0.55, 0.0475, 5.5, 4, 10);
+ else if (pPlayer->GetAbsVelocity().Length2D() > 5)
+ pPlayer->KickBack (0.55, 0.3, 0.225, 0.03, 3.5, 2.5, 10);
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ pPlayer->KickBack (0.25, 0.175, 0.125, 0.02, 2.25, 1.25, 10);
+ else
+ pPlayer->KickBack (0.275, 0.2, 0.15, 0.0225, 2.5, 1.5, 10);
+}
+
diff --git a/game/shared/cstrike/weapon_usp.cpp b/game/shared/cstrike/weapon_usp.cpp
new file mode 100644
index 0000000..6a99f02
--- /dev/null
+++ b/game/shared/cstrike/weapon_usp.cpp
@@ -0,0 +1,395 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbase.h"
+#include "fx_cs_shared.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponUSP C_WeaponUSP
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+
+#endif
+
+
+class CWeaponUSP : public CWeaponCSBase
+{
+public:
+ DECLARE_CLASS( CWeaponUSP, CWeaponCSBase );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponUSP();
+
+ virtual void Spawn();
+ virtual void Precache();
+
+ virtual void PrimaryAttack();
+ virtual void SecondaryAttack();
+ virtual bool Deploy();
+ virtual bool Holster( CBaseCombatWeapon *pSwitchingTo );
+ virtual void Drop( const Vector &vecVelocity );
+
+ virtual float GetInaccuracy() const;
+
+ virtual bool Reload();
+ virtual void WeaponIdle();
+
+ // We overload this so we can translate all weapon activities to silenced versions.
+ virtual bool SendWeaponAnim( int iActivity );
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_USP; }
+
+ // return true if this weapon has a silencer equipped
+ virtual bool IsSilenced( void ) const { return m_bSilencerOn; }
+
+ virtual Activity GetDeployActivity( void );
+
+#ifdef CLIENT_DLL
+ virtual int GetMuzzleFlashStyle( void );
+#endif
+
+ virtual const char *GetWorldModel( void ) const;
+ virtual int GetWorldModelIndex( void );
+
+private:
+ CWeaponUSP( const CWeaponUSP & );
+
+ CNetworkVar( bool, m_bSilencerOn );
+ CNetworkVar( float, m_flDoneSwitchingSilencer ); // soonest time switching the silencer will be complete
+ float m_flLastFire;
+
+ int m_silencedModelIndex;
+ bool m_inPrecache;
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponUSP, DT_WeaponUSP )
+
+BEGIN_NETWORK_TABLE( CWeaponUSP, DT_WeaponUSP )
+#ifdef CLIENT_DLL
+ RecvPropBool( RECVINFO( m_bSilencerOn ) ),
+ RecvPropTime( RECVINFO( m_flDoneSwitchingSilencer ) ),
+#else
+ SendPropBool( SENDINFO( m_bSilencerOn ) ),
+ SendPropTime( SENDINFO( m_flDoneSwitchingSilencer ) ),
+#endif
+END_NETWORK_TABLE()
+
+#ifdef CLIENT_DLL
+BEGIN_PREDICTION_DATA( CWeaponUSP )
+ DEFINE_PRED_FIELD( m_bSilencerOn, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_FIELD( m_flLastFire, FIELD_FLOAT ),
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_usp, CWeaponUSP );
+PRECACHE_WEAPON_REGISTER( weapon_usp );
+
+
+Activity g_SilencedTranslations[][2] =
+{
+ { ACT_VM_RELOAD, ACT_VM_RELOAD_SILENCED },
+ { ACT_VM_PRIMARYATTACK, ACT_VM_PRIMARYATTACK_SILENCED },
+ { ACT_VM_DRAW, ACT_VM_DRAW_SILENCED },
+};
+
+
+
+CWeaponUSP::CWeaponUSP()
+{
+ m_flLastFire = gpGlobals->curtime;
+ m_bSilencerOn = false;
+ m_flDoneSwitchingSilencer = 0.0f;
+ m_inPrecache = false;
+}
+
+
+void CWeaponUSP::Spawn()
+{
+ //m_iDefaultAmmo = 12;
+ m_flAccuracy = 0.92;
+ m_bSilencerOn = false;
+ m_weaponMode = Primary_Mode;
+ m_flDoneSwitchingSilencer = 0.0f;
+
+ //FallInit();// get ready to fall down.
+ BaseClass::Spawn();
+}
+
+
+void CWeaponUSP::Precache()
+{
+ m_inPrecache = true;
+ BaseClass::Precache();
+
+ m_silencedModelIndex = CBaseEntity::PrecacheModel( GetCSWpnData().m_szSilencerModel );
+ m_inPrecache = false;
+}
+
+
+int CWeaponUSP::GetWorldModelIndex( void )
+{
+ if ( !m_bSilencerOn || m_inPrecache )
+ {
+ return m_iWorldModelIndex;
+ }
+ else
+ {
+ return m_silencedModelIndex;
+ }
+}
+
+
+const char * CWeaponUSP::GetWorldModel( void ) const
+{
+ if ( !m_bSilencerOn || m_inPrecache )
+ {
+ return BaseClass::GetWorldModel();
+ }
+ else
+ {
+ return GetCSWpnData().m_szSilencerModel;
+ }
+}
+
+
+bool CWeaponUSP::Deploy()
+{
+ m_flAccuracy = 0.92;
+ m_flDoneSwitchingSilencer = 0.0f;
+
+ return BaseClass::Deploy();
+}
+
+bool CWeaponUSP::Holster( CBaseCombatWeapon *pSwitchingTo )
+{
+ if ( gpGlobals->curtime < m_flDoneSwitchingSilencer )
+ {
+ // still switching the silencer. Cancel the switch.
+ m_bSilencerOn = !m_bSilencerOn;
+ m_weaponMode = m_bSilencerOn ? Secondary_Mode : Primary_Mode;
+ SetWeaponModelIndex( GetWorldModel() );
+ }
+
+ return BaseClass::Holster( pSwitchingTo );
+}
+
+void CWeaponUSP::Drop( const Vector &vecVelocity )
+{
+ if ( gpGlobals->curtime < m_flDoneSwitchingSilencer )
+ {
+ // still switching the silencer. Cancel the switch.
+ m_bSilencerOn = !m_bSilencerOn;
+ m_weaponMode = m_bSilencerOn ? Secondary_Mode : Primary_Mode;
+ SetWeaponModelIndex( GetWorldModel() );
+ }
+
+ BaseClass::Drop( vecVelocity );
+}
+
+Activity CWeaponUSP::GetDeployActivity( void )
+{
+ if( IsSilenced() )
+ {
+ return ACT_VM_DRAW_SILENCED;
+ }
+ else
+ {
+ return ACT_VM_DRAW;
+ }
+}
+
+void CWeaponUSP::SecondaryAttack()
+{
+ if ( m_bSilencerOn )
+ {
+ SendWeaponAnim( ACT_VM_DETACH_SILENCER );
+ }
+ else
+ {
+ SendWeaponAnim( ACT_VM_ATTACH_SILENCER );
+ }
+ m_bSilencerOn = !m_bSilencerOn;
+ m_weaponMode = m_bSilencerOn ? Secondary_Mode : Primary_Mode;
+ m_flDoneSwitchingSilencer = gpGlobals->curtime + 3;
+
+ m_flNextSecondaryAttack = gpGlobals->curtime + 3;
+ m_flNextPrimaryAttack = gpGlobals->curtime + 3;
+ SetWeaponIdleTime( gpGlobals->curtime + 3 );
+
+ SetWeaponModelIndex( GetWorldModel() );
+}
+
+
+float CWeaponUSP::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ {
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return 0.0f;
+
+ if ( m_bSilencerOn )
+ {
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ return 1.3f * (1 - m_flAccuracy);
+ else if (pPlayer->GetAbsVelocity().Length2D() > 5)
+ return 0.25f * (1 - m_flAccuracy);
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ return 0.125f * (1 - m_flAccuracy);
+ else
+ return 0.15f * (1 - m_flAccuracy);
+ }
+ else
+ {
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ return 1.2f * (1 - m_flAccuracy );
+ else if (pPlayer->GetAbsVelocity().Length2D() > 5)
+ return 0.225f * (1 - m_flAccuracy);
+ else if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING ) )
+ return 0.08f * (1 - m_flAccuracy);
+ else
+ return 0.1f * (1 - m_flAccuracy);
+ }
+ }
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+
+void CWeaponUSP::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ float flCycleTime = GetCSWpnData().m_flCycleTime;
+
+ // Mark the time of this shot and determine the accuracy modifier based on the last shot fired...
+ m_flAccuracy -= (0.275)*(0.3 - (gpGlobals->curtime - m_flLastFire));
+
+ if (m_flAccuracy > 0.92)
+ m_flAccuracy = 0.92;
+ else if (m_flAccuracy < 0.6)
+ m_flAccuracy = 0.6;
+
+ m_flLastFire = gpGlobals->curtime;
+
+ if (m_iClip1 <= 0)
+ {
+ if ( m_bFireOnEmpty )
+ {
+ PlayEmptySound();
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.2;
+ m_bFireOnEmpty = false;
+ }
+
+ return;
+ }
+
+ pPlayer->m_iShotsFired++;
+
+ m_flNextPrimaryAttack = m_flNextSecondaryAttack = gpGlobals->curtime + flCycleTime;
+
+ m_iClip1--;
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+
+ if ( !m_bSilencerOn )
+ {
+ pPlayer->DoMuzzleFlash();
+ }
+
+ FX_FireBullets(
+ pPlayer->entindex(),
+ pPlayer->Weapon_ShootPosition(),
+ pPlayer->EyeAngles() + 2.0f * pPlayer->GetPunchAngle(),
+ GetWeaponID(),
+ m_weaponMode,
+ CBaseEntity::GetPredictionRandomSeed() & 255,
+ GetInaccuracy(),
+ GetSpread());
+
+ if (!m_iClip1 && pPlayer->GetAmmoCount( GetPrimaryAmmoType() ) <= 0)
+ {
+ // HEV suit - indicate out of ammo condition
+ pPlayer->SetSuitUpdate("!HEV_AMO0", false, 0);
+ }
+
+ SetWeaponIdleTime( gpGlobals->curtime + 2 );
+
+ // update accuracy
+ m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyImpulseFire[m_weaponMode];
+
+ QAngle angle = pPlayer->GetPunchAngle();
+ angle.x -= 2;
+ pPlayer->SetPunchAngle( angle );
+}
+
+
+bool CWeaponUSP::Reload()
+{
+ if ( !DefaultPistolReload() )
+ return false;
+
+ m_flAccuracy = 0.92;
+ return true;
+}
+
+void CWeaponUSP::WeaponIdle()
+{
+ if (m_flTimeWeaponIdle > gpGlobals->curtime)
+ return;
+
+ // only idle if the slid isn't back
+ if (m_iClip1 != 0)
+ {
+ SetWeaponIdleTime( gpGlobals->curtime + 6.0 );
+ }
+}
+
+bool CWeaponUSP::SendWeaponAnim( int iActivity )
+{
+ // Translate the activity?
+ if ( m_bSilencerOn )
+ {
+ for ( int i=0; i < ARRAYSIZE( g_SilencedTranslations ); i++ )
+ {
+ if ( g_SilencedTranslations[i][0] == iActivity )
+ {
+ iActivity = g_SilencedTranslations[i][1];
+ break;
+ }
+ }
+ }
+
+ return BaseClass::SendWeaponAnim( iActivity );
+}
+
+
+#ifdef CLIENT_DLL
+int CWeaponUSP::GetMuzzleFlashStyle( void )
+{
+ if( m_bSilencerOn )
+ {
+ return CS_MUZZLEFLASH_NONE;
+ }
+ else
+ {
+ return CS_MUZZLEFLASH_NORM;
+ }
+}
+#endif
diff --git a/game/shared/cstrike/weapon_xm1014.cpp b/game/shared/cstrike/weapon_xm1014.cpp
new file mode 100644
index 0000000..4cead32
--- /dev/null
+++ b/game/shared/cstrike/weapon_xm1014.cpp
@@ -0,0 +1,304 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "weapon_csbase.h"
+#include "fx_cs_shared.h"
+
+
+#if defined( CLIENT_DLL )
+
+ #define CWeaponXM1014 C_WeaponXM1014
+ #include "c_cs_player.h"
+
+#else
+
+ #include "cs_player.h"
+ #include "te_shotgun_shot.h"
+
+#endif
+
+
+class CWeaponXM1014 : public CWeaponCSBase
+{
+public:
+ DECLARE_CLASS( CWeaponXM1014, CWeaponCSBase );
+ DECLARE_NETWORKCLASS();
+ DECLARE_PREDICTABLE();
+
+ CWeaponXM1014();
+
+ virtual void Spawn();
+ virtual void PrimaryAttack();
+ virtual bool Reload();
+ virtual void WeaponIdle();
+
+ virtual float GetInaccuracy() const;
+ virtual float GetSpread() const;
+
+ virtual CSWeaponID GetWeaponID( void ) const { return WEAPON_XM1014; }
+
+private:
+
+ CWeaponXM1014( const CWeaponXM1014 & );
+
+ float m_flPumpTime;
+ CNetworkVar( int, m_reloadState ); // special reload state for shotgun
+
+};
+
+IMPLEMENT_NETWORKCLASS_ALIASED( WeaponXM1014, DT_WeaponXM1014 )
+
+BEGIN_NETWORK_TABLE( CWeaponXM1014, DT_WeaponXM1014 )
+#ifdef CLIENT_DLL
+ RecvPropInt( RECVINFO( m_reloadState ) )
+#else
+ SendPropInt( SENDINFO( m_reloadState ), 2, SPROP_UNSIGNED )
+#endif
+END_NETWORK_TABLE()
+
+#if defined(CLIENT_DLL)
+BEGIN_PREDICTION_DATA( CWeaponXM1014 )
+ DEFINE_PRED_FIELD( m_reloadState, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+END_PREDICTION_DATA()
+#endif
+
+LINK_ENTITY_TO_CLASS( weapon_xm1014, CWeaponXM1014 );
+PRECACHE_WEAPON_REGISTER( weapon_xm1014 );
+
+
+CWeaponXM1014::CWeaponXM1014()
+{
+ m_flPumpTime = 0;
+ m_reloadState = 0;
+}
+
+void CWeaponXM1014::Spawn()
+{
+ //m_iDefaultAmmo = M3_DEFAULT_GIVE;
+ //FallInit();// get ready to fall
+ BaseClass::Spawn();
+}
+
+float CWeaponXM1014::GetInaccuracy() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ return 0.0f;
+ else
+ return BaseClass::GetInaccuracy();
+}
+
+float CWeaponXM1014::GetSpread() const
+{
+ if ( weapon_accuracy_model.GetInt() == 1 )
+ return 0.0725f;
+
+ return GetCSWpnData().m_fSpread[Primary_Mode];
+}
+
+void CWeaponXM1014::PrimaryAttack()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ float flCycleTime = GetCSWpnData().m_flCycleTime;
+
+ // don't fire underwater
+ if (pPlayer->GetWaterLevel() == 3)
+ {
+ PlayEmptySound( );
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.15;
+ return;
+ }
+
+ if (m_iClip1 <= 0)
+ {
+ Reload();
+
+ if (m_iClip1 == 0)
+ {
+ PlayEmptySound( );
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.25;
+ }
+
+ return;
+ }
+
+ SendWeaponAnim( ACT_VM_PRIMARYATTACK );
+
+ //pPlayer->m_iWeaponVolume = LOUD_GUN_VOLUME;
+ //pPlayer->m_iWeaponFlash = BRIGHT_GUN_FLASH;
+
+ m_iClip1--;
+ pPlayer->DoMuzzleFlash();
+
+ // player "shoot" animation
+ pPlayer->SetAnimation( PLAYER_ATTACK1 );
+
+ // Dispatch the FX right away with full accuracy.
+ float flCurAttack = CalculateNextAttackTime( flCycleTime );
+ FX_FireBullets(
+ pPlayer->entindex(),
+ pPlayer->Weapon_ShootPosition(),
+ pPlayer->EyeAngles() + 2.0f * pPlayer->GetPunchAngle(),
+ GetWeaponID(),
+ Primary_Mode,
+ CBaseEntity::GetPredictionRandomSeed() & 255, // wrap it for network traffic so it's the same between client and server
+ GetInaccuracy(),
+ GetSpread(), // flSpread
+ flCurAttack );
+
+ if (!m_iClip1 && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0)
+ {
+ // HEV suit - indicate out of ammo condition
+ pPlayer->SetSuitUpdate("!HEV_AMO0", false, 0);
+ }
+
+ if (m_iClip1 != 0)
+ m_flPumpTime = gpGlobals->curtime + 0.5;
+
+ if (m_iClip1 != 0)
+ SetWeaponIdleTime( gpGlobals->curtime + 2.5 );
+ else
+ SetWeaponIdleTime( gpGlobals->curtime + 0.25 );
+ m_reloadState = 0;
+
+ // update accuracy
+ m_fAccuracyPenalty += GetCSWpnData().m_fInaccuracyImpulseFire[Primary_Mode];
+
+ // Update punch angles.
+ QAngle angle = pPlayer->GetPunchAngle();
+
+ if ( pPlayer->GetFlags() & FL_ONGROUND )
+ {
+ angle.x -= SharedRandomInt( "XM1014PunchAngleGround", 3, 5 );
+ }
+ else
+ {
+ angle.x -= SharedRandomInt( "XM1014PunchAngleAir", 7, 10 );
+ }
+
+ pPlayer->SetPunchAngle( angle );
+}
+
+
+bool CWeaponXM1014::Reload()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return false;
+
+ if (pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) <= 0 || m_iClip1 == GetMaxClip1())
+ return true;
+
+ // don't reload until recoil is done
+ if (m_flNextPrimaryAttack > gpGlobals->curtime)
+ return true;
+
+ //MIKETODO: shotgun reloading (wait until we get content)
+
+ // check to see if we're ready to reload
+ if (m_reloadState == 0)
+ {
+ pPlayer->SetAnimation( PLAYER_RELOAD );
+
+ SendWeaponAnim( ACT_SHOTGUN_RELOAD_START );
+ m_reloadState = 1;
+ pPlayer->m_flNextAttack = gpGlobals->curtime + 0.5;
+ SetWeaponIdleTime( gpGlobals->curtime + 0.5 );
+ m_flNextPrimaryAttack = gpGlobals->curtime + 0.5;
+ m_flNextSecondaryAttack = gpGlobals->curtime + 0.5;
+
+#ifdef GAME_DLL
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD_START );
+#endif
+
+ return true;
+ }
+ else if (m_reloadState == 1)
+ {
+ if (m_flTimeWeaponIdle > gpGlobals->curtime)
+ return true;
+ // was waiting for gun to move to side
+ m_reloadState = 2;
+
+ SendWeaponAnim( ACT_VM_RELOAD );
+ SetWeaponIdleTime( gpGlobals->curtime + 0.5 );
+#ifdef GAME_DLL
+ if ( m_iClip1 == 6 )
+ {
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD_END );
+ }
+ else
+ {
+ pPlayer->DoAnimationEvent( PLAYERANIMEVENT_RELOAD_LOOP );
+ }
+#endif
+ }
+ else
+ {
+ // Add them to the clip
+ m_iClip1 += 1;
+
+#ifdef GAME_DLL
+ SendReloadEvents();
+#endif
+
+ CCSPlayer *pPlayer = GetPlayerOwner();
+
+ if ( pPlayer )
+ pPlayer->RemoveAmmo( 1, m_iPrimaryAmmoType );
+
+ m_reloadState = 1;
+ }
+
+
+ return true;
+}
+
+
+void CWeaponXM1014::WeaponIdle()
+{
+ CCSPlayer *pPlayer = GetPlayerOwner();
+ if ( !pPlayer )
+ return;
+
+ if (m_flPumpTime && m_flPumpTime < gpGlobals->curtime)
+ {
+ // play pumping sound
+ m_flPumpTime = 0;
+ }
+
+ if (m_flTimeWeaponIdle < gpGlobals->curtime)
+ {
+ if (m_iClip1 == 0 && m_reloadState == 0 && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ))
+ {
+ Reload( );
+ }
+ else if (m_reloadState != 0)
+ {
+ if (m_iClip1 != 7 && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ))
+ {
+ Reload( );
+ }
+ else
+ {
+ // reload debounce has timed out
+ //MIKETODO: shotgun anims
+ SendWeaponAnim( ACT_SHOTGUN_RELOAD_FINISH );
+
+ // play cocking sound
+ m_reloadState = 0;
+ SetWeaponIdleTime( gpGlobals->curtime + 1.5 );
+ }
+ }
+ else
+ {
+ SendWeaponAnim( ACT_VM_IDLE );
+ }
+ }
+}