Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1684 lines
50 KiB

  1. //========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Contains the implementation of game rules for multiplayer.
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "multiplay_gamerules.h"
  9. #include "viewport_panel_names.h"
  10. #include "gameeventdefs.h"
  11. #include <keyvalues.h>
  12. #include "filesystem.h"
  13. #include "mp_shareddefs.h"
  14. #include "gametypes/igametypes.h"
  15. #ifdef CLIENT_DLL
  16. #else
  17. #include "eventqueue.h"
  18. #include "player.h"
  19. #include "basecombatweapon.h"
  20. #include "gamerules.h"
  21. #include "game.h"
  22. #include "items.h"
  23. #include "entitylist.h"
  24. #include "in_buttons.h"
  25. #include <ctype.h>
  26. #include "voice_gamemgr.h"
  27. #include "iscorer.h"
  28. #include "hltvdirector.h"
  29. #if defined( REPLAY_ENABLED )
  30. #include "replaydirector.h"
  31. #endif
  32. #include "ai_criteria.h"
  33. #include "sceneentity.h"
  34. #include "team.h"
  35. #include "usermessages.h"
  36. #include "tier0/icommandline.h"
  37. #include "basemultiplayerplayer.h"
  38. #endif
  39. // memdbgon must be the last include file in a .cpp file!!!
  40. #include "tier0/memdbgon.h"
  41. extern ConVar mp_verbose_changelevel_spew;
  42. extern ConVar sv_kick_ban_duration;
  43. extern ConVar mp_autokick;
  44. REGISTER_GAMERULES_CLASS( CMultiplayRules );
  45. ConVar mp_match_restart_delay(
  46. "mp_match_restart_delay",
  47. "15",
  48. FCVAR_REPLICATED | FCVAR_RELEASE,
  49. "Time (in seconds) until a match restarts.",
  50. true, 1,
  51. true, 120 );
  52. ConVar mapcycledisabled(
  53. "mapcycledisabled",
  54. "0",
  55. FCVAR_REPLICATED | FCVAR_RELEASE,
  56. "repeats the same map after each match instead of using the map cycle");
  57. #ifdef GAME_DLL
  58. void MPTimeLimitCallback( IConVar *var, const char *pOldString, float flOldValue )
  59. {
  60. if ( mp_timelimit.GetInt() < 0 )
  61. {
  62. mp_timelimit.SetValue( 0 );
  63. }
  64. if ( MultiplayRules() )
  65. {
  66. MultiplayRules()->HandleTimeLimitChange();
  67. }
  68. }
  69. #endif
  70. ConVar mp_timelimit( "mp_timelimit", "5", FCVAR_NOTIFY | FCVAR_REPLICATED | FCVAR_RELEASE, "game time per map in minutes"
  71. #ifdef GAME_DLL
  72. , MPTimeLimitCallback
  73. #endif
  74. );
  75. ConVar fraglimit( "mp_fraglimit","0", FCVAR_NOTIFY|FCVAR_REPLICATED, "The number of kills at which the map ends" );
  76. #ifdef GAME_DLL
  77. ConVar tv_delaymapchange( "tv_delaymapchange", "1", FCVAR_RELEASE, "Delays map change until broadcast is complete" );
  78. ConVar mp_restartgame( "mp_restartgame", "0", FCVAR_GAMEDLL | FCVAR_RELEASE, "If non-zero, game will restart in the specified number of seconds" );
  79. void cc_SkipNextMapInCycle()
  80. {
  81. if ( !UTIL_IsCommandIssuedByServerAdmin() )
  82. return;
  83. if ( MultiplayRules() )
  84. {
  85. MultiplayRules()->SkipNextMapInCycle();
  86. }
  87. }
  88. ConCommand skip_next_map( "skip_next_map", cc_SkipNextMapInCycle, "Skips the next map in the map rotation for the server." );
  89. #ifndef TF_DLL // TF overrides the default value of this convar
  90. ConVar mp_waitingforplayers_time( "mp_waitingforplayers_time", "0", FCVAR_GAMEDLL, "WaitingForPlayers time length in seconds" );
  91. #endif
  92. ConVar mp_waitingforplayers_restart( "mp_waitingforplayers_restart", "0", FCVAR_GAMEDLL, "Set to 1 to start or restart the WaitingForPlayers period." );
  93. ConVar mp_waitingforplayers_cancel( "mp_waitingforplayers_cancel", "0", FCVAR_GAMEDLL, "Set to 1 to end the WaitingForPlayers period." );
  94. ConVar mp_clan_readyrestart( "mp_clan_readyrestart", "0", FCVAR_GAMEDLL, "If non-zero, game will restart once someone from each team gives the ready signal" );
  95. ConVar mp_clan_ready_signal( "mp_clan_ready_signal", "ready", FCVAR_GAMEDLL, "Text that team leader from each team must speak for the match to begin" );
  96. #endif // GAME_DLL
  97. #ifdef GAME_DLL
  98. ConVar nextmap_print_enabled( "nextmap_print_enabled", "0", FCVAR_GAMEDLL | FCVAR_RELEASE, "When enabled prints next map to clients" );
  99. #endif
  100. ConVar nextlevel( "nextlevel",
  101. "",
  102. FCVAR_NOTIFY | FCVAR_RELEASE | FCVAR_REPLICATED,
  103. #if defined( CSTRIKE_DLL ) || defined( TF_DLL )
  104. "If set to a valid map name, will trigger a changelevel to the specified map at the end of the round"
  105. );
  106. #else
  107. "If set to a valid map name, will change to this map during the next changelevel" );
  108. #endif // CSTRIKE_DLL || TF_DLL
  109. #ifndef CLIENT_DLL
  110. int CMultiplayRules::m_nMapCycleTimeStamp = 0;
  111. int CMultiplayRules::m_nMapCycleindex = 0;
  112. CUtlStringList CMultiplayRules::m_MapList;
  113. #endif
  114. //=========================================================
  115. //=========================================================
  116. bool CMultiplayRules::IsMultiplayer( void )
  117. {
  118. return true;
  119. }
  120. //-----------------------------------------------------------------------------
  121. // Purpose:
  122. //-----------------------------------------------------------------------------
  123. int CMultiplayRules::Damage_GetTimeBased( void )
  124. {
  125. int iDamage = ( DMG_PARALYZE | DMG_NERVEGAS | DMG_POISON | DMG_RADIATION | DMG_DROWNRECOVER | DMG_ACID | DMG_SLOWBURN );
  126. return iDamage;
  127. }
  128. //-----------------------------------------------------------------------------
  129. // Purpose:
  130. //-----------------------------------------------------------------------------
  131. int CMultiplayRules::Damage_GetShouldGibCorpse( void )
  132. {
  133. int iDamage = ( DMG_CRUSH | DMG_FALL | DMG_BLAST | DMG_SONIC | DMG_CLUB );
  134. return iDamage;
  135. }
  136. //-----------------------------------------------------------------------------
  137. // Purpose:
  138. //-----------------------------------------------------------------------------
  139. int CMultiplayRules::Damage_GetShowOnHud( void )
  140. {
  141. int iDamage = ( DMG_POISON | DMG_ACID | DMG_DROWN | DMG_BURN | DMG_SLOWBURN | DMG_NERVEGAS | DMG_RADIATION | DMG_SHOCK );
  142. return iDamage;
  143. }
  144. //-----------------------------------------------------------------------------
  145. // Purpose:
  146. //-----------------------------------------------------------------------------
  147. int CMultiplayRules::Damage_GetNoPhysicsForce( void )
  148. {
  149. int iTimeBasedDamage = Damage_GetTimeBased();
  150. int iDamage = ( DMG_FALL | DMG_BURN | DMG_PLASMA | DMG_DROWN | iTimeBasedDamage | DMG_CRUSH | DMG_PHYSGUN | DMG_PREVENT_PHYSICS_FORCE );
  151. return iDamage;
  152. }
  153. //-----------------------------------------------------------------------------
  154. // Purpose:
  155. //-----------------------------------------------------------------------------
  156. int CMultiplayRules::Damage_GetShouldNotBleed( void )
  157. {
  158. int iDamage = ( DMG_POISON | DMG_ACID );
  159. return iDamage;
  160. }
  161. //-----------------------------------------------------------------------------
  162. // Purpose:
  163. // Input : iDmgType -
  164. // Output : Returns true on success, false on failure.
  165. //-----------------------------------------------------------------------------
  166. bool CMultiplayRules::Damage_IsTimeBased( int iDmgType )
  167. {
  168. // Damage types that are time-based.
  169. return ( ( iDmgType & ( DMG_PARALYZE | DMG_NERVEGAS | DMG_POISON | DMG_RADIATION | DMG_DROWNRECOVER | DMG_ACID | DMG_SLOWBURN ) ) != 0 );
  170. }
  171. //-----------------------------------------------------------------------------
  172. // Purpose:
  173. // Input : iDmgType -
  174. // Output : Returns true on success, false on failure.
  175. //-----------------------------------------------------------------------------
  176. bool CMultiplayRules::Damage_ShouldGibCorpse( int iDmgType )
  177. {
  178. // Damage types that gib the corpse.
  179. return ( ( iDmgType & ( DMG_CRUSH | DMG_FALL | DMG_BLAST | DMG_SONIC | DMG_CLUB ) ) != 0 );
  180. }
  181. //-----------------------------------------------------------------------------
  182. // Purpose:
  183. // Input : iDmgType -
  184. // Output : Returns true on success, false on failure.
  185. //-----------------------------------------------------------------------------
  186. bool CMultiplayRules::Damage_ShowOnHUD( int iDmgType )
  187. {
  188. // Damage types that have client HUD art.
  189. return ( ( iDmgType & ( DMG_POISON | DMG_ACID | DMG_DROWN | DMG_BURN | DMG_SLOWBURN | DMG_NERVEGAS | DMG_RADIATION | DMG_SHOCK ) ) != 0 );
  190. }
  191. //-----------------------------------------------------------------------------
  192. // Purpose:
  193. // Input : iDmgType -
  194. // Output : Returns true on success, false on failure.
  195. //-----------------------------------------------------------------------------
  196. bool CMultiplayRules::Damage_NoPhysicsForce( int iDmgType )
  197. {
  198. // Damage types that don't have to supply a physics force & position.
  199. int iTimeBasedDamage = Damage_GetTimeBased();
  200. return ( ( iDmgType & ( DMG_FALL | DMG_BURN | DMG_PLASMA | DMG_DROWN | iTimeBasedDamage | DMG_CRUSH | DMG_PHYSGUN | DMG_PREVENT_PHYSICS_FORCE ) ) != 0 );
  201. }
  202. //-----------------------------------------------------------------------------
  203. // Purpose:
  204. // Input : iDmgType -
  205. // Output : Returns true on success, false on failure.
  206. //-----------------------------------------------------------------------------
  207. bool CMultiplayRules::Damage_ShouldNotBleed( int iDmgType )
  208. {
  209. // Damage types that don't make the player bleed.
  210. return ( ( iDmgType & ( DMG_POISON | DMG_ACID ) ) != 0 );
  211. }
  212. //*********************************************************
  213. // Rules for the half-life multiplayer game.
  214. //*********************************************************
  215. CMultiplayRules::CMultiplayRules()
  216. {
  217. #ifndef CLIENT_DLL
  218. #ifdef CSTRIKE15
  219. // before we exec ANY cfg files or apply any convars, go through the bspconvar whitelist and set all convars in that list to their default value
  220. KeyValues::AutoDelete pKV_wl( "convars" );
  221. if ( pKV_wl->LoadFromFile( g_pFullFileSystem, "bspconvar_whitelist.txt", "GAME" ) )
  222. {
  223. for ( KeyValues *pKey = pKV_wl->GetFirstSubKey(); pKey != NULL; pKey = pKey->GetNextKey() )
  224. {
  225. //save the name of this outfit
  226. const char *szConVarName = pKey->GetName();
  227. ConVarRef convar( szConVarName );
  228. if ( convar.IsValid() )
  229. {
  230. convar.SetValue( convar.GetDefault() );
  231. Msg( "%s - %s\n", szConVarName, convar.GetDefault() );
  232. }
  233. }
  234. }
  235. #endif
  236. // 11/8/98
  237. // Modified by YWB: Server .cfg file is now a cvar, so that
  238. // server ops can run multiple game servers, with different server .cfg files,
  239. // from a single installed directory.
  240. // Mapcyclefile is already a cvar.
  241. // 3/31/99
  242. // Added lservercfg file cvar, since listen and dedicated servers should not
  243. // share a single config file. (sjb)
  244. if ( engine->IsDedicatedServer() )
  245. {
  246. // dedicated server
  247. const char *cfgfile = servercfgfile.GetString();
  248. if ( cfgfile && cfgfile[0] )
  249. {
  250. char szCommand[256];
  251. Msg( "Executing dedicated server config file\n" );
  252. Q_snprintf( szCommand,sizeof(szCommand), "exec %s\n", cfgfile );
  253. engine->ServerCommand( szCommand );
  254. }
  255. }
  256. else
  257. {
  258. // listen server
  259. const char *cfgfile = lservercfgfile.GetString();
  260. if ( cfgfile && cfgfile[0] )
  261. {
  262. char szCommand[256];
  263. Msg( "Executing listen server config file\n" );
  264. Q_snprintf( szCommand,sizeof(szCommand), "exec %s\n", cfgfile );
  265. engine->ServerCommand( szCommand );
  266. }
  267. }
  268. nextlevel.SetValue( "" );
  269. #endif
  270. LoadVoiceCommandScript();
  271. }
  272. #ifdef CLIENT_DLL
  273. #else
  274. extern bool g_fGameOver;
  275. #define ITEM_RESPAWN_TIME 30
  276. #define WEAPON_RESPAWN_TIME 20
  277. #define AMMO_RESPAWN_TIME 20
  278. //=========================================================
  279. //=========================================================
  280. void CMultiplayRules::RefreshSkillData( bool forceUpdate )
  281. {
  282. // load all default values
  283. BaseClass::RefreshSkillData( forceUpdate );
  284. // override some values for multiplay.
  285. // suitcharger
  286. #if !defined( TF_DLL ) && !defined( CSTRIKE_DLL )
  287. ConVarRef suitcharger( "sk_suitcharger" );
  288. suitcharger.SetValue( 30 );
  289. #endif
  290. }
  291. ConVar spec_replay_bot( "spec_replay_bot", "0", FCVAR_RELEASE, "Enable Spectator Hltv Replay when killed by bot" );
  292. //=========================================================
  293. //
  294. // WARNING - this function is NOT called in CS:GO
  295. //
  296. // CCSGameRules (which has CMultiplayRules as a baseclass) ::Think() calls
  297. // CGameRules::Think() directly, bypassing CTeamplayRules::Think() and
  298. // CMultiplayRules::Think()
  299. //
  300. // Code placed in here is likely to NEVER be called
  301. //=========================================================
  302. void CMultiplayRules::Think ( void )
  303. {
  304. BaseClass::Think();
  305. ///// Check game rules /////
  306. if ( g_fGameOver ) // someone else quit the game already
  307. {
  308. ChangeLevel(); // intermission is over
  309. return;
  310. }
  311. float flTimeLimit = mp_timelimit.GetFloat() * 60;
  312. float flFragLimit = fraglimit.GetFloat();
  313. if ( flTimeLimit != 0 && gpGlobals->curtime >= flTimeLimit )
  314. {
  315. GoToIntermission();
  316. return;
  317. }
  318. if ( flFragLimit )
  319. {
  320. // check if any player is over the frag limit
  321. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  322. {
  323. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  324. if ( pPlayer && pPlayer->FragCount() >= flFragLimit )
  325. {
  326. GoToIntermission();
  327. return;
  328. }
  329. }
  330. }
  331. }
  332. //=========================================================
  333. //=========================================================
  334. bool CMultiplayRules::IsDeathmatch( void )
  335. {
  336. return true;
  337. }
  338. //=========================================================
  339. //=========================================================
  340. bool CMultiplayRules::IsCoOp( void )
  341. {
  342. return false;
  343. }
  344. //=========================================================
  345. //=========================================================
  346. bool CMultiplayRules::FShouldSwitchWeapon( CBasePlayer *pPlayer, CBaseCombatWeapon *pWeapon )
  347. {
  348. if ( !pPlayer->Weapon_CanSwitchTo( pWeapon ) )
  349. {
  350. // Can't switch weapons for some reason.
  351. return false;
  352. }
  353. if ( !pPlayer->GetActiveWeapon() )
  354. {
  355. // Player doesn't have an active item, might as well switch.
  356. return true;
  357. }
  358. if ( !pWeapon->AllowsAutoSwitchTo() )
  359. {
  360. // The given weapon should not be auto switched to from another weapon.
  361. return false;
  362. }
  363. if ( !pPlayer->GetActiveWeapon()->AllowsAutoSwitchFrom() )
  364. {
  365. // The active weapon does not allow autoswitching away from it.
  366. return false;
  367. }
  368. if ( pWeapon->GetWeight() > pPlayer->GetActiveWeapon()->GetWeight() )
  369. {
  370. return true;
  371. }
  372. return false;
  373. }
  374. //-----------------------------------------------------------------------------
  375. // Purpose: Returns the weapon in the player's inventory that would be better than
  376. // the given weapon.
  377. //-----------------------------------------------------------------------------
  378. CBaseCombatWeapon *CMultiplayRules::GetNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon )
  379. {
  380. CBaseCombatWeapon *pCheck;
  381. CBaseCombatWeapon *pBest;// this will be used in the event that we don't find a weapon in the same category.
  382. int iCurrentWeight = -1;
  383. int iBestWeight = -1;// no weapon lower than -1 can be autoswitched to
  384. pBest = NULL;
  385. // If I have a weapon, make sure I'm allowed to holster it
  386. if ( pCurrentWeapon )
  387. {
  388. if ( !pCurrentWeapon->AllowsAutoSwitchFrom() || !pCurrentWeapon->CanHolster() )
  389. {
  390. // Either this weapon doesn't allow autoswitching away from it or I
  391. // can't put this weapon away right now, so I can't switch.
  392. return NULL;
  393. }
  394. iCurrentWeight = pCurrentWeapon->GetWeight();
  395. }
  396. for ( int i = 0 ; i < pPlayer->WeaponCount(); ++i )
  397. {
  398. pCheck = pPlayer->GetWeapon( i );
  399. if ( !pCheck )
  400. continue;
  401. // If we have an active weapon and this weapon doesn't allow autoswitching away
  402. // from another weapon, skip it.
  403. if ( pCurrentWeapon && !pCheck->AllowsAutoSwitchTo() )
  404. continue;
  405. if ( pCheck->GetWeight() > -1 && pCheck->GetWeight() == iCurrentWeight && pCheck != pCurrentWeapon )
  406. {
  407. // this weapon is from the same category.
  408. if ( pCheck->HasAnyAmmo() )
  409. {
  410. if ( pPlayer->Weapon_CanSwitchTo( pCheck ) )
  411. {
  412. return pCheck;
  413. }
  414. }
  415. }
  416. else if ( pCheck->GetWeight() > iBestWeight && pCheck != pCurrentWeapon )// don't reselect the weapon we're trying to get rid of
  417. {
  418. //Msg( "Considering %s\n", STRING( pCheck->GetClassname() );
  419. // we keep updating the 'best' weapon just in case we can't find a weapon of the same weight
  420. // that the player was using. This will end up leaving the player with his heaviest-weighted
  421. // weapon.
  422. if ( pCheck->HasAnyAmmo() )
  423. {
  424. // if this weapon is useable, flag it as the best
  425. iBestWeight = pCheck->GetWeight();
  426. pBest = pCheck;
  427. }
  428. }
  429. }
  430. // if we make it here, we've checked all the weapons and found no useable
  431. // weapon in the same catagory as the current weapon.
  432. // if pBest is null, we didn't find ANYTHING. Shouldn't be possible- should always
  433. // at least get the crowbar, but ya never know.
  434. return pBest;
  435. }
  436. //-----------------------------------------------------------------------------
  437. // Purpose:
  438. // Output : Returns true on success, false on failure.
  439. //-----------------------------------------------------------------------------
  440. bool CMultiplayRules::SwitchToNextBestWeapon( CBaseCombatCharacter *pPlayer, CBaseCombatWeapon *pCurrentWeapon )
  441. {
  442. CBaseCombatWeapon *pWeapon = GetNextBestWeapon( pPlayer, pCurrentWeapon );
  443. if ( pWeapon != NULL )
  444. return pPlayer->Weapon_Switch( pWeapon );
  445. return false;
  446. }
  447. //=========================================================
  448. //=========================================================
  449. bool CMultiplayRules::ClientConnected( edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen )
  450. {
  451. GetVoiceGameMgr()->ClientConnected( pEntity );
  452. /*
  453. CBasePlayer *pl = ToBasePlayer( GetContainingEntity( pEntity ) );
  454. if ( pl && ( engine->IsSplitScreenPlayer( pl->entindex() ) )
  455. {
  456. Msg( "%s is a split screen player\n", pszName );
  457. }
  458. */
  459. return true;
  460. }
  461. void CMultiplayRules::InitHUD( CBasePlayer *pl )
  462. {
  463. }
  464. //=========================================================
  465. //=========================================================
  466. void CMultiplayRules::ClientDisconnected( edict_t *pClient )
  467. {
  468. if ( pClient )
  469. {
  470. CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pClient );
  471. if ( pPlayer )
  472. {
  473. FireTargets( "game_playerleave", pPlayer, pPlayer, USE_TOGGLE, 0 );
  474. pPlayer->RemoveAllItems( true );// destroy all of the players weapons and items
  475. // Kill off view model entities
  476. pPlayer->DestroyViewModels();
  477. pPlayer->SetConnected( PlayerDisconnected );
  478. }
  479. }
  480. }
  481. //=========================================================
  482. //=========================================================
  483. float CMultiplayRules::FlPlayerFallDamage( CBasePlayer *pPlayer )
  484. {
  485. int iFallDamage = (int)falldamage.GetFloat();
  486. switch ( iFallDamage )
  487. {
  488. case 1://progressive
  489. pPlayer->m_Local.m_flFallVelocity -= PLAYER_MAX_SAFE_FALL_SPEED;
  490. return pPlayer->m_Local.m_flFallVelocity * DAMAGE_FOR_FALL_SPEED;
  491. break;
  492. default:
  493. case 0:// fixed
  494. return 10;
  495. break;
  496. }
  497. }
  498. //=========================================================
  499. //=========================================================
  500. bool CMultiplayRules::AllowDamage( CBaseEntity *pVictim, const CTakeDamageInfo &info )
  501. {
  502. return true;
  503. }
  504. //=========================================================
  505. //=========================================================
  506. bool CMultiplayRules::FPlayerCanTakeDamage( CBasePlayer *pPlayer, CBaseEntity *pAttacker )
  507. {
  508. return true;
  509. }
  510. //=========================================================
  511. //=========================================================
  512. void CMultiplayRules::PlayerThink( CBasePlayer *pPlayer )
  513. {
  514. if ( g_fGameOver )
  515. {
  516. // clear attack/use commands from player
  517. pPlayer->m_afButtonPressed = 0;
  518. pPlayer->m_nButtons = 0;
  519. pPlayer->m_afButtonReleased = 0;
  520. }
  521. }
  522. //=========================================================
  523. //=========================================================
  524. void CMultiplayRules::PlayerSpawn( CBasePlayer *pPlayer )
  525. {
  526. bool addDefault;
  527. CBaseEntity *pWeaponEntity = NULL;
  528. pPlayer->EquipSuit();
  529. addDefault = true;
  530. while ( (pWeaponEntity = gEntList.FindEntityByClassname( pWeaponEntity, "game_player_equip" )) != NULL)
  531. {
  532. pWeaponEntity->Touch( pPlayer );
  533. addDefault = false;
  534. }
  535. }
  536. //=========================================================
  537. //=========================================================
  538. bool CMultiplayRules::FPlayerCanRespawn( CBasePlayer *pPlayer )
  539. {
  540. return true;
  541. }
  542. //=========================================================
  543. //=========================================================
  544. float CMultiplayRules::FlPlayerSpawnTime( CBasePlayer *pPlayer )
  545. {
  546. return gpGlobals->curtime;//now!
  547. }
  548. bool CMultiplayRules::AllowAutoTargetCrosshair( void )
  549. {
  550. return ( aimcrosshair.GetInt() != 0 );
  551. }
  552. //=========================================================
  553. // IPointsForKill - how many points awarded to anyone
  554. // that kills this player?
  555. //=========================================================
  556. int CMultiplayRules::IPointsForKill( CBasePlayer *pAttacker, CBasePlayer *pKilled )
  557. {
  558. return 1;
  559. }
  560. //-----------------------------------------------------------------------------
  561. // Purpose:
  562. //-----------------------------------------------------------------------------
  563. CBasePlayer *CMultiplayRules::GetDeathScorer( CBaseEntity *pKiller, CBaseEntity *pInflictor )
  564. {
  565. if ( pKiller)
  566. {
  567. if ( pKiller->Classify() == CLASS_PLAYER )
  568. return (CBasePlayer*)pKiller;
  569. // Killing entity might be specifying a scorer player
  570. IScorer *pScorerInterface = dynamic_cast<IScorer*>( pKiller );
  571. if ( pScorerInterface )
  572. {
  573. CBasePlayer *pPlayer = pScorerInterface->GetScorer();
  574. if ( pPlayer )
  575. return pPlayer;
  576. }
  577. // Inflicting entity might be specifying a scoring player
  578. pScorerInterface = dynamic_cast<IScorer*>( pInflictor );
  579. if ( pScorerInterface )
  580. {
  581. CBasePlayer *pPlayer = pScorerInterface->GetScorer();
  582. if ( pPlayer )
  583. return pPlayer;
  584. }
  585. }
  586. return NULL;
  587. }
  588. //-----------------------------------------------------------------------------
  589. // Purpose: Returns player who should receive credit for kill
  590. //-----------------------------------------------------------------------------
  591. CBasePlayer *CMultiplayRules::GetDeathScorer( CBaseEntity *pKiller, CBaseEntity *pInflictor, CBaseEntity *pVictim )
  592. {
  593. // if this method not overridden by subclass, just call our default implementation
  594. return GetDeathScorer( pKiller, pInflictor );
  595. }
  596. //=========================================================
  597. // PlayerKilled - someone/something killed this player
  598. //=========================================================
  599. void CMultiplayRules::PlayerKilled( CBasePlayer *pVictim, const CTakeDamageInfo &info )
  600. {
  601. // Find the killer & the scorer
  602. CBaseEntity *pInflictor = info.GetInflictor();
  603. CBaseEntity *pKiller = info.GetAttacker();
  604. CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor, pVictim );
  605. pVictim->IncrementDeathCount( 1 );
  606. // dvsents2: uncomment when removing all FireTargets
  607. // variant_t value;
  608. // g_EventQueue.AddEvent( "game_playerdie", "Use", value, 0, pVictim, pVictim );
  609. FireTargets( "game_playerdie", pVictim, pVictim, USE_TOGGLE, 0 );
  610. // Did the player kill himself?
  611. if ( pVictim == pScorer )
  612. {
  613. if ( UseSuicidePenalty() )
  614. {
  615. // Players lose a frag for killing themselves
  616. pVictim->IncrementFragCount( -1 );
  617. }
  618. }
  619. else if ( pScorer )
  620. {
  621. // if a player dies in a deathmatch game and the killer is a client, award the killer some points
  622. int numPointsPerKill = IPointsForKill( pScorer, pVictim );
  623. int numHeadshots = ( ( numPointsPerKill == 1 ) && ( info.GetDamageType() & /*DMG_HEADSHOT*/(DMG_LASTGENERICFLAG<<1) ) ) ? 1 : 0;
  624. pScorer->IncrementFragCount( numPointsPerKill, numHeadshots );
  625. if ( numHeadshots > 0 )
  626. {
  627. pVictim->m_iDeathPostEffect = 15; // POST_EFFECT_DEATH_CAM_HEADSHOT
  628. }
  629. else
  630. {
  631. pVictim->m_iDeathPostEffect = 14; // POST_EFFECT_DEATH_CAM_BODYSHOT
  632. }
  633. // Allow the scorer to immediately paint a decal
  634. pScorer->AllowImmediateDecalPainting();
  635. // dvsents2: uncomment when removing all FireTargets
  636. //variant_t value;
  637. //g_EventQueue.AddEvent( "game_playerkill", "Use", value, 0, pScorer, pScorer );
  638. FireTargets( "game_playerkill", pScorer, pScorer, USE_TOGGLE, 0 );
  639. }
  640. else
  641. {
  642. if ( UseSuicidePenalty() )
  643. {
  644. // Players lose a frag for letting the world kill them
  645. pVictim->IncrementFragCount( -1 );
  646. }
  647. }
  648. DeathNotice( pVictim, info );
  649. }
  650. //=========================================================
  651. // Deathnotice.
  652. //=========================================================
  653. void CMultiplayRules::DeathNotice( CBasePlayer *pVictim, const CTakeDamageInfo &info )
  654. {
  655. // Work out what killed the player, and send a message to all clients about it
  656. const char *killer_weapon_name = "world"; // by default, the player is killed by the world
  657. int killer_ID = 0;
  658. // Find the killer & the scorer
  659. CBaseEntity *pInflictor = info.GetInflictor();
  660. CBaseEntity *pKiller = info.GetAttacker();
  661. CBasePlayer *pScorer = GetDeathScorer( pKiller, pInflictor, pVictim );
  662. // Custom damage type?
  663. if ( info.GetDamageCustom() )
  664. {
  665. killer_weapon_name = GetDamageCustomString( info );
  666. if ( pScorer )
  667. {
  668. killer_ID = pScorer->GetUserID();
  669. }
  670. }
  671. else
  672. {
  673. // Is the killer a client?
  674. if ( pScorer )
  675. {
  676. killer_ID = pScorer->GetUserID();
  677. if ( pInflictor )
  678. {
  679. if ( pInflictor == pScorer )
  680. {
  681. // If the inflictor is the killer, then it must be their current weapon doing the damage
  682. if ( pScorer->GetActiveWeapon() )
  683. {
  684. killer_weapon_name = pScorer->GetActiveWeapon()->GetDeathNoticeName();
  685. }
  686. }
  687. else
  688. {
  689. killer_weapon_name = STRING( pInflictor->m_iClassname ); // it's just that easy
  690. }
  691. }
  692. }
  693. else
  694. {
  695. killer_weapon_name = STRING( pInflictor->m_iClassname );
  696. }
  697. // strip the NPC_* or weapon_* from the inflictor's classname
  698. if ( IsWeaponClassname( killer_weapon_name ) )
  699. {
  700. killer_weapon_name += WEAPON_CLASSNAME_PREFIX_LENGTH;
  701. }
  702. else if ( StringHasPrefixCaseSensitive( killer_weapon_name, "NPC_" ) )
  703. {
  704. killer_weapon_name += V_strlen( "NPC_" );
  705. }
  706. else if ( StringHasPrefixCaseSensitive( killer_weapon_name, "func_" ) )
  707. {
  708. killer_weapon_name += V_strlen( "func_" );
  709. }
  710. }
  711. IGameEvent * event = gameeventmanager->CreateEvent( "player_death" );
  712. if ( event )
  713. {
  714. event->SetInt("userid", pVictim->GetUserID() );
  715. event->SetInt("attacker", killer_ID );
  716. event->SetInt("customkill", info.GetDamageCustom() );
  717. event->SetInt("priority", 7 ); // HLTV event priority, not transmitted
  718. if( !OnReplayPrompt( pVictim, pScorer ) )
  719. event->SetBool( "noreplay", true );
  720. gameeventmanager->FireEvent( event );
  721. }
  722. }
  723. bool CMultiplayRules::OnReplayPrompt( CBasePlayer *pVictim, CBasePlayer *pScorer )
  724. {
  725. if( !spec_replay_bot.GetBool() && pScorer && pScorer->IsBot() )
  726. {
  727. return false; // don't replay kills by bots if spec_replay_bot == 0
  728. }
  729. if( !engine->HasHltvReplay( ) )
  730. return false; // cannot replay if the engine doesn't have hltv replay working
  731. // ok we are ready to replay. Let's replay it to other people, too!
  732. return true;
  733. }
  734. //=========================================================
  735. // FlWeaponRespawnTime - what is the time in the future
  736. // at which this weapon may spawn?
  737. //=========================================================
  738. float CMultiplayRules::FlWeaponRespawnTime( CBaseCombatWeapon *pWeapon )
  739. {
  740. if ( weaponstay.GetInt() > 0 )
  741. {
  742. // make sure it's only certain weapons
  743. if ( !(pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) )
  744. {
  745. return gpGlobals->curtime + 0; // weapon respawns almost instantly
  746. }
  747. }
  748. return gpGlobals->curtime + WEAPON_RESPAWN_TIME;
  749. }
  750. // when we are within this close to running out of entities, items
  751. // marked with the ITEM_FLAG_LIMITINWORLD will delay their respawn
  752. #define ENTITY_INTOLERANCE 100
  753. //=========================================================
  754. // FlWeaponRespawnTime - Returns 0 if the weapon can respawn
  755. // now, otherwise it returns the time at which it can try
  756. // to spawn again.
  757. //=========================================================
  758. float CMultiplayRules::FlWeaponTryRespawn( CBaseCombatWeapon *pWeapon )
  759. {
  760. if ( pWeapon && (pWeapon->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD) )
  761. {
  762. if ( gEntList.NumberOfEntities() < (gpGlobals->maxEntities - ENTITY_INTOLERANCE) )
  763. return 0;
  764. // we're past the entity tolerance level, so delay the respawn
  765. return FlWeaponRespawnTime( pWeapon );
  766. }
  767. return 0;
  768. }
  769. //=========================================================
  770. // VecWeaponRespawnSpot - where should this weapon spawn?
  771. // Some game variations may choose to randomize spawn locations
  772. //=========================================================
  773. Vector CMultiplayRules::VecWeaponRespawnSpot( CBaseCombatWeapon *pWeapon )
  774. {
  775. return pWeapon->GetAbsOrigin();
  776. }
  777. //=========================================================
  778. // WeaponShouldRespawn - any conditions inhibiting the
  779. // respawning of this weapon?
  780. //=========================================================
  781. int CMultiplayRules::WeaponShouldRespawn( CBaseCombatWeapon *pWeapon )
  782. {
  783. if ( pWeapon->HasSpawnFlags( SF_NORESPAWN ) )
  784. {
  785. return GR_WEAPON_RESPAWN_NO;
  786. }
  787. return GR_WEAPON_RESPAWN_YES;
  788. }
  789. //=========================================================
  790. // CanHaveWeapon - returns false if the player is not allowed
  791. // to pick up this weapon
  792. //=========================================================
  793. bool CMultiplayRules::CanHavePlayerItem( CBasePlayer *pPlayer, CBaseCombatWeapon *pItem )
  794. {
  795. if ( weaponstay.GetInt() > 0 )
  796. {
  797. if ( pItem->GetWeaponFlags() & ITEM_FLAG_LIMITINWORLD )
  798. return BaseClass::CanHavePlayerItem( pPlayer, pItem );
  799. // check if the player already has this weapon
  800. for ( int i = 0 ; i < pPlayer->WeaponCount() ; i++ )
  801. {
  802. if ( pPlayer->GetWeapon(i) == pItem )
  803. {
  804. return false;
  805. }
  806. }
  807. }
  808. return BaseClass::CanHavePlayerItem( pPlayer, pItem );
  809. }
  810. //=========================================================
  811. //=========================================================
  812. bool CMultiplayRules::CanHaveItem( CBasePlayer *pPlayer, CItem *pItem )
  813. {
  814. return true;
  815. }
  816. //=========================================================
  817. //=========================================================
  818. void CMultiplayRules::PlayerGotItem( CBasePlayer *pPlayer, CItem *pItem )
  819. {
  820. }
  821. //=========================================================
  822. //=========================================================
  823. int CMultiplayRules::ItemShouldRespawn( CItem *pItem )
  824. {
  825. if ( pItem->HasSpawnFlags( SF_NORESPAWN ) )
  826. {
  827. return GR_ITEM_RESPAWN_NO;
  828. }
  829. return GR_ITEM_RESPAWN_YES;
  830. }
  831. //=========================================================
  832. // At what time in the future may this Item respawn?
  833. //=========================================================
  834. float CMultiplayRules::FlItemRespawnTime( CItem *pItem )
  835. {
  836. return gpGlobals->curtime + ITEM_RESPAWN_TIME;
  837. }
  838. //=========================================================
  839. // Where should this item respawn?
  840. // Some game variations may choose to randomize spawn locations
  841. //=========================================================
  842. Vector CMultiplayRules::VecItemRespawnSpot( CItem *pItem )
  843. {
  844. return pItem->GetAbsOrigin();
  845. }
  846. //=========================================================
  847. // What angles should this item use to respawn?
  848. //=========================================================
  849. QAngle CMultiplayRules::VecItemRespawnAngles( CItem *pItem )
  850. {
  851. return pItem->GetAbsAngles();
  852. }
  853. //=========================================================
  854. //=========================================================
  855. void CMultiplayRules::PlayerGotAmmo( CBaseCombatCharacter *pPlayer, char *szName, int iCount )
  856. {
  857. }
  858. //=========================================================
  859. //=========================================================
  860. bool CMultiplayRules::IsAllowedToSpawn( CBaseEntity *pEntity )
  861. {
  862. // if ( pEntity->GetFlags() & FL_NPC )
  863. // return false;
  864. return true;
  865. }
  866. //=========================================================
  867. //=========================================================
  868. float CMultiplayRules::FlHealthChargerRechargeTime( void )
  869. {
  870. return 60;
  871. }
  872. float CMultiplayRules::FlHEVChargerRechargeTime( void )
  873. {
  874. return 30;
  875. }
  876. //=========================================================
  877. //=========================================================
  878. int CMultiplayRules::DeadPlayerWeapons( CBasePlayer *pPlayer )
  879. {
  880. return GR_PLR_DROP_GUN_ACTIVE;
  881. }
  882. //=========================================================
  883. //=========================================================
  884. int CMultiplayRules::DeadPlayerAmmo( CBasePlayer *pPlayer )
  885. {
  886. return GR_PLR_DROP_AMMO_ACTIVE;
  887. }
  888. CBaseEntity *CMultiplayRules::GetPlayerSpawnSpot( CBasePlayer *pPlayer )
  889. {
  890. CBaseEntity *pentSpawnSpot = BaseClass::GetPlayerSpawnSpot( pPlayer );
  891. //!! replace this with an Event
  892. /*
  893. if ( IsMultiplayer() && pentSpawnSpot->m_target )
  894. {
  895. FireTargets( STRING(pentSpawnSpot->m_target), pPlayer, pPlayer, USE_TOGGLE, 0 ); // dvsents2: what is this code supposed to do?
  896. }
  897. */
  898. return pentSpawnSpot;
  899. }
  900. //=========================================================
  901. //=========================================================
  902. bool CMultiplayRules::PlayerCanHearChat( CBasePlayer *pListener, CBasePlayer *pSpeaker, bool bTeamOnly )
  903. {
  904. return !bTeamOnly || PlayerRelationship( pListener, pSpeaker ) == GR_TEAMMATE;
  905. }
  906. int CMultiplayRules::PlayerRelationship( CBaseEntity *pPlayer, CBaseEntity *pTarget )
  907. {
  908. // half life deathmatch has only enemies
  909. return GR_NOTTEAMMATE;
  910. }
  911. bool CMultiplayRules::PlayFootstepSounds( CBasePlayer *pl )
  912. {
  913. if ( footsteps.GetInt() == 0 )
  914. return false;
  915. if ( pl->IsOnLadder() || pl->GetAbsVelocity().Length2D() > 220 )
  916. return true; // only make step sounds in multiplayer if the player is moving fast enough
  917. return false;
  918. }
  919. bool CMultiplayRules::FAllowFlashlight( void )
  920. {
  921. return flashlight.GetInt() != 0;
  922. }
  923. //=========================================================
  924. //=========================================================
  925. bool CMultiplayRules::FAllowNPCs( void )
  926. {
  927. return true; // E3 hack
  928. return ( allowNPCs.GetInt() != 0 );
  929. }
  930. //=========================================================
  931. //======== CMultiplayRules private functions ===========
  932. float CMultiplayRules::GetIntermissionDuration() const
  933. {
  934. float flWaitTime = mp_match_restart_delay.GetInt();
  935. if ( tv_delaymapchange.GetBool() )
  936. {
  937. if ( HLTVDirector() && HLTVDirector()->IsActive() )
  938. {
  939. CEngineHltvInfo_t engineHltv;
  940. if ( engine->GetEngineHltvInfo( engineHltv ) &&
  941. engineHltv.m_bBroadcastActive && ( engineHltv.m_numClients > 0 ) )
  942. {
  943. flWaitTime = MAX ( flWaitTime, HLTVDirector()->GetDelay() + 5.0f );
  944. }
  945. }
  946. #if defined( REPLAY_ENABLED )
  947. else if ( ReplayDirector()->IsActive() )
  948. flWaitTime = MAX ( flWaitTime, ReplayDirector()->GetDelay() + 5.0f );
  949. #endif
  950. }
  951. return flWaitTime;
  952. }
  953. void CMultiplayRules::GoToIntermission( void )
  954. {
  955. if ( g_fGameOver )
  956. return;
  957. g_fGameOver = true;
  958. m_flIntermissionStartTime = gpGlobals->curtime;
  959. IGameEvent * event = gameeventmanager->CreateEvent( "cs_intermission" );
  960. if ( event )
  961. {
  962. gameeventmanager->FireEvent( event );
  963. }
  964. #if !defined( CSTRIKE15 )
  965. for ( int i = 1; i <= MAX_PLAYERS; i++ )
  966. {
  967. CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
  968. if ( !pPlayer )
  969. continue;
  970. pPlayer->ShowViewPortPanel( PANEL_SCOREBOARD );
  971. }
  972. #endif
  973. }
  974. void StripChar(char *szBuffer, const char cWhiteSpace )
  975. {
  976. while ( char *pSpace = strchr( szBuffer, cWhiteSpace ) )
  977. {
  978. char *pNextChar = pSpace + sizeof(char);
  979. V_strcpy( pSpace, pNextChar );
  980. }
  981. }
  982. void CMultiplayRules::GetNextLevelName( char *pszNextMap, int bufsize, bool bRandom /* = false */ )
  983. {
  984. const char *mapGroupName = NULL;
  985. mapGroupName = STRING( gpGlobals->mapGroupName );
  986. if ( mp_verbose_changelevel_spew.GetBool() )
  987. {
  988. Msg( "CHANGELEVEL: Looking for next level in mapgroup '%s'\n", mapGroupName );
  989. }
  990. // If mapcycling is disabled, just return the same map name and bail.
  991. if ( mapcycledisabled.GetBool() )
  992. {
  993. Q_strncpy( pszNextMap, STRING( gpGlobals->mapname ), bufsize );
  994. if ( mp_verbose_changelevel_spew.GetBool() && pszNextMap )
  995. {
  996. Msg( "CHANGELEVEL: Map cycle disabled (due to convar '%s') -- not using map group, reloading current map '%s'\n", mapcycledisabled.GetName(), pszNextMap );
  997. }
  998. return;
  999. }
  1000. const char* nextMapName = NULL;
  1001. if ( bRandom )
  1002. {
  1003. nextMapName = g_pGameTypes->GetRandomMap( mapGroupName );
  1004. if ( mp_verbose_changelevel_spew.GetBool() && nextMapName )
  1005. {
  1006. Msg( "CHANGELEVEL: Random map request, choosing '%s'\n", nextMapName );
  1007. }
  1008. }
  1009. else
  1010. {
  1011. if ( m_szNextLevelName && m_szNextLevelName[0] )
  1012. {
  1013. nextMapName = m_szNextLevelName;
  1014. Assert( 0 ); // Suspect this is a dead code path... remove me if possible
  1015. }
  1016. else
  1017. {
  1018. const char* szPrevMap = STRING( gpGlobals->mapname );
  1019. nextMapName = g_pGameTypes->GetNextMap( mapGroupName, szPrevMap );
  1020. if ( mp_verbose_changelevel_spew.GetBool() )
  1021. {
  1022. if ( nextMapName && szPrevMap )
  1023. Msg( "CHANGELEVEL: Choosing map '%s' (previous was %s)\n", nextMapName, szPrevMap );
  1024. else
  1025. Msg( "CHANGELEVEL: GetNextMap failed for mapgroup '%s', map group invalid or empty\n", mapGroupName);
  1026. }
  1027. }
  1028. }
  1029. if ( nextMapName )
  1030. {
  1031. // we have a valid map name from the mapgroup info
  1032. V_strncpy( pszNextMap, nextMapName, bufsize );
  1033. return;
  1034. }
  1035. // we were not given a mapgroup name or we were given a mapname that was not in the mapgroup, so we fall back to the old method of cycling maps
  1036. const char *mapcfile = mapcyclefile.GetString();
  1037. Assert( mapcfile != NULL );
  1038. // Check the time of the mapcycle file and re-populate the list of level names if the file has been modified
  1039. const int nMapCycleTimeStamp = filesystem->GetPathTime( mapcfile, "GAME" );
  1040. if ( 0 == nMapCycleTimeStamp )
  1041. {
  1042. // Map cycle file does not exist, make a list containing only the current map
  1043. char *szCurrentMapName = new char[MAX_PATH];
  1044. V_strncpy( szCurrentMapName, STRING(gpGlobals->mapname), MAX_PATH );
  1045. m_MapList.AddToTail( szCurrentMapName );
  1046. if ( mp_verbose_changelevel_spew.GetBool() && szCurrentMapName )
  1047. {
  1048. Msg( "CHANGELEVEL: No maycycle file, using current map '%s'\n", szCurrentMapName );
  1049. }
  1050. }
  1051. else
  1052. {
  1053. // If map cycle file has changed or this is the first time through ...
  1054. if ( m_nMapCycleTimeStamp != nMapCycleTimeStamp )
  1055. {
  1056. // Reset map index and map cycle timestamp
  1057. m_nMapCycleTimeStamp = nMapCycleTimeStamp;
  1058. m_nMapCycleindex = 0;
  1059. // Clear out existing map list. Not using Purge() because I don't think that it will do a 'delete []'
  1060. for ( int i = 0; i < m_MapList.Count(); i++ )
  1061. {
  1062. delete [] m_MapList[i];
  1063. }
  1064. m_MapList.RemoveAll();
  1065. // Repopulate map list from mapcycle file
  1066. int nFileLength;
  1067. char *aFileList = (char*)UTIL_LoadFileForMe( mapcfile, &nFileLength );
  1068. if ( aFileList && nFileLength )
  1069. {
  1070. V_SplitString( aFileList, "\n", m_MapList );
  1071. for ( int i = 0; i < m_MapList.Count(); i++ )
  1072. {
  1073. bool bIgnore = false;
  1074. // Strip out the spaces in the name
  1075. StripChar( m_MapList[i] , '\r');
  1076. StripChar( m_MapList[i] , ' ');
  1077. if ( !engine->IsMapValid( m_MapList[i] ) )
  1078. {
  1079. bIgnore = true;
  1080. // If the engine doesn't consider it a valid map remove it from the lists
  1081. Warning( "Invalid map '%s' included in map cycle file. Ignored.\n", m_MapList[i] );
  1082. }
  1083. else if ( StringHasPrefixCaseSensitive( m_MapList[i], "//" ) )
  1084. {
  1085. bIgnore = true;
  1086. }
  1087. if ( bIgnore )
  1088. {
  1089. delete [] m_MapList[i];
  1090. m_MapList.Remove( i );
  1091. --i;
  1092. }
  1093. }
  1094. UTIL_FreeFile( (byte *)aFileList );
  1095. }
  1096. // If the current map selection is in the list, set m_nMapCycleindex to the map that follows it.
  1097. for ( int i = 0; i < m_MapList.Count(); i++ )
  1098. {
  1099. if ( V_strcmp( STRING( gpGlobals->mapname ), m_MapList[i] ) == 0 )
  1100. {
  1101. m_nMapCycleindex = i;
  1102. IncrementMapCycleIndex();
  1103. break;
  1104. }
  1105. }
  1106. }
  1107. }
  1108. // If somehow we have no maps in the list then add the current one
  1109. if ( 0 == m_MapList.Count() )
  1110. {
  1111. char *szDefaultMapName = new char[MAX_PATH];
  1112. V_strncpy( szDefaultMapName, STRING(gpGlobals->mapname), MAX_PATH );
  1113. m_MapList.AddToTail( szDefaultMapName );
  1114. if ( mp_verbose_changelevel_spew.GetBool() && szDefaultMapName )
  1115. {
  1116. Msg( "CHANGELEVEL: Map list empty or failed to parse, using current map '%s'\n", szDefaultMapName );
  1117. }
  1118. }
  1119. if ( bRandom )
  1120. {
  1121. m_nMapCycleindex = RandomInt( 0, m_MapList.Count() - 1 );
  1122. }
  1123. // Here's the return value
  1124. Q_strncpy( pszNextMap, m_MapList[m_nMapCycleindex], bufsize);
  1125. }
  1126. void CMultiplayRules::ChangeLevel( void )
  1127. {
  1128. char szNextMap[MAX_PATH];
  1129. if ( nextlevel.GetString() && *nextlevel.GetString() && engine->IsMapValid( nextlevel.GetString() ) )
  1130. {
  1131. Q_strncpy( szNextMap, nextlevel.GetString(), sizeof( szNextMap ) );
  1132. }
  1133. else
  1134. {
  1135. GetNextLevelName( szNextMap, sizeof(szNextMap) );
  1136. IncrementMapCycleIndex();
  1137. }
  1138. g_fGameOver = true;
  1139. Msg( "CHANGE LEVEL: %s\n", szNextMap );
  1140. engine->ChangeLevel( szNextMap, NULL );
  1141. }
  1142. #endif
  1143. //-----------------------------------------------------------------------------
  1144. // Purpose: Shared script resource of voice menu commands and hud strings
  1145. //-----------------------------------------------------------------------------
  1146. void CMultiplayRules::LoadVoiceCommandScript( void )
  1147. {
  1148. KeyValues *pKV = new KeyValues( "VoiceCommands" );
  1149. if ( pKV->LoadFromFile( filesystem, "scripts/voicecommands.txt", "GAME" ) )
  1150. {
  1151. for ( KeyValues *menu = pKV->GetFirstSubKey(); menu != NULL; menu = menu->GetNextKey() )
  1152. {
  1153. int iMenuIndex = m_VoiceCommandMenus.AddToTail();
  1154. int iNumItems = 0;
  1155. // for each subkey of this menu, add a menu item
  1156. for ( KeyValues *menuitem = menu->GetFirstSubKey(); menuitem != NULL; menuitem = menuitem->GetNextKey() )
  1157. {
  1158. iNumItems++;
  1159. if ( iNumItems > 9 )
  1160. {
  1161. Warning( "Trying to load more than 9 menu items in voicecommands.txt, extras ignored" );
  1162. continue;
  1163. }
  1164. VoiceCommandMenuItem_t item;
  1165. #ifndef CLIENT_DLL
  1166. int iConcept = GetMPConceptIndexFromString( menuitem->GetString( "concept", "" ) );
  1167. if ( iConcept == MP_CONCEPT_NONE )
  1168. {
  1169. Warning( "Voicecommand script attempting to use unknown concept. Need to define new concepts in code. ( %s )\n", menuitem->GetString( "concept", "" ) );
  1170. }
  1171. item.m_iConcept = iConcept;
  1172. item.m_bShowSubtitle = ( menuitem->GetInt( "show_subtitle", 0 ) > 0 );
  1173. item.m_bDistanceBasedSubtitle = ( menuitem->GetInt( "distance_check_subtitle", 0 ) > 0 );
  1174. Q_strncpy( item.m_szGestureActivity, menuitem->GetString( "activity", "" ), sizeof( item.m_szGestureActivity ) );
  1175. #else
  1176. Q_strncpy( item.m_szSubtitle, menuitem->GetString( "subtitle", "" ), MAX_VOICE_COMMAND_SUBTITLE );
  1177. Q_strncpy( item.m_szMenuLabel, menuitem->GetString( "menu_label", "" ), MAX_VOICE_COMMAND_SUBTITLE );
  1178. #endif
  1179. m_VoiceCommandMenus.Element( iMenuIndex ).AddToTail( item );
  1180. }
  1181. }
  1182. }
  1183. pKV->deleteThis();
  1184. }
  1185. #ifndef CLIENT_DLL
  1186. void CMultiplayRules::SkipNextMapInCycle()
  1187. {
  1188. char szSkippedMap[MAX_PATH];
  1189. char szNextMap[MAX_PATH];
  1190. GetNextLevelName( szSkippedMap, sizeof( szSkippedMap ) );
  1191. IncrementMapCycleIndex();
  1192. GetNextLevelName( szNextMap, sizeof( szNextMap ) );
  1193. Msg( "Skipping: %s\tNext map: %s\n", szSkippedMap, szNextMap );
  1194. if ( nextlevel.GetString() && *nextlevel.GetString() && engine->IsMapValid( nextlevel.GetString() ) )
  1195. {
  1196. Msg( "Warning! \"nextlevel\" is set to \"%s\" and will override the next map to be played.\n", nextlevel.GetString() );
  1197. }
  1198. }
  1199. void CMultiplayRules::IncrementMapCycleIndex()
  1200. {
  1201. // Reset index if we've passed the end of the map list
  1202. if ( ++m_nMapCycleindex >= m_MapList.Count() )
  1203. {
  1204. m_nMapCycleindex = 0;
  1205. }
  1206. }
  1207. bool CMultiplayRules::ClientCommand( CBaseEntity *pEdict, const CCommand &args )
  1208. {
  1209. CBasePlayer *pPlayer = ToBasePlayer( pEdict );
  1210. const char *pcmd = args[0];
  1211. if ( FStrEq( pcmd, "voicemenu" ) )
  1212. {
  1213. if ( args.ArgC() < 3 )
  1214. return true;
  1215. CBaseMultiplayerPlayer *pMultiPlayerPlayer = dynamic_cast< CBaseMultiplayerPlayer * >( pPlayer );
  1216. if ( pMultiPlayerPlayer )
  1217. {
  1218. int iMenu = atoi( args[1] );
  1219. int iItem = atoi( args[2] );
  1220. VoiceCommand( pMultiPlayerPlayer, iMenu, iItem );
  1221. }
  1222. return true;
  1223. }
  1224. #if 0 // disabled this in CS:GO due to exploit
  1225. else if ( FStrEq( pcmd, "achievement_earned" ) )
  1226. {
  1227. CBaseMultiplayerPlayer *pPlayer = static_cast<CBaseMultiplayerPlayer*>( pEdict );
  1228. if ( pPlayer && pPlayer->ShouldAnnounceAchievement() )
  1229. {
  1230. // let's check this came from the client .dll and not the console
  1231. unsigned short mask = UTIL_GetAchievementEventMask();
  1232. int iPlayerID = pPlayer->GetUserID();
  1233. int iAchievement = atoi( args[1] ) ^ mask;
  1234. int code = ( iPlayerID ^ iAchievement ) ^ mask;
  1235. if ( code == atoi( args[2] ) )
  1236. {
  1237. IGameEvent * event = gameeventmanager->CreateEvent( "achievement_earned" );
  1238. if ( event )
  1239. {
  1240. event->SetInt( "player", pEdict->entindex() );
  1241. event->SetInt( "achievement", iAchievement );
  1242. gameeventmanager->FireEvent( event );
  1243. }
  1244. pPlayer->OnAchievementEarned( iAchievement );
  1245. }
  1246. }
  1247. return true;
  1248. }
  1249. #endif
  1250. return BaseClass::ClientCommand( pEdict, args );
  1251. }
  1252. VoiceCommandMenuItem_t *CMultiplayRules::VoiceCommand( CBaseMultiplayerPlayer *pPlayer, int iMenu, int iItem )
  1253. {
  1254. // have the player speak the concept that is in a particular menu slot
  1255. if ( !pPlayer )
  1256. return NULL;
  1257. if ( iMenu < 0 || iMenu >= m_VoiceCommandMenus.Count() )
  1258. return NULL;
  1259. if ( iItem < 0 || iItem >= m_VoiceCommandMenus.Element( iMenu ).Count() )
  1260. return NULL;
  1261. VoiceCommandMenuItem_t *pItem = &m_VoiceCommandMenus.Element( iMenu ).Element( iItem );
  1262. Assert( pItem );
  1263. char szResponse[AI_Response::MAX_RESPONSE_NAME];
  1264. if ( pPlayer->CanSpeakVoiceCommand() )
  1265. {
  1266. CMultiplayer_Expresser *pExpresser = pPlayer->GetMultiplayerExpresser();
  1267. Assert( pExpresser );
  1268. pExpresser->AllowMultipleScenes();
  1269. if ( pPlayer->SpeakConceptIfAllowed( pItem->m_iConcept, NULL, szResponse, AI_Response::MAX_RESPONSE_NAME ) )
  1270. {
  1271. // show a subtitle if we need to
  1272. if ( pItem->m_bShowSubtitle )
  1273. {
  1274. CRecipientFilter filter;
  1275. if ( pItem->m_bDistanceBasedSubtitle )
  1276. {
  1277. filter.AddRecipientsByPAS( pPlayer->WorldSpaceCenter() );
  1278. // further reduce the range to a certain radius
  1279. int i;
  1280. for ( i = filter.GetRecipientCount()-1; i >= 0; i-- )
  1281. {
  1282. int index = filter.GetRecipientIndex(i);
  1283. CBasePlayer *pListener = UTIL_PlayerByIndex( index );
  1284. if ( pListener && pListener != pPlayer )
  1285. {
  1286. float flDist = ( pListener->WorldSpaceCenter() - pPlayer->WorldSpaceCenter() ).Length2D();
  1287. if ( flDist > VOICE_COMMAND_MAX_SUBTITLE_DIST )
  1288. filter.RemoveRecipientByPlayerIndex( index );
  1289. }
  1290. }
  1291. }
  1292. else
  1293. {
  1294. filter.AddAllPlayers();
  1295. }
  1296. // if we aren't a disguised spy
  1297. if ( !pPlayer->ShouldShowVoiceSubtitleToEnemy() )
  1298. {
  1299. // remove players on other teams
  1300. filter.RemoveRecipientsNotOnTeam( pPlayer->GetTeam() );
  1301. }
  1302. // Register this event in the mod-specific usermessages .cpp file if you hit this assert
  1303. // Gurjeets - Commented this Assert out when converting messages to protobuf. There is no instance in code
  1304. // of user message "VoiceSubtitle" ever being sent
  1305. // Assert( usermessages->LookupUserMessage( "VoiceSubtitle" ) != -1 );
  1306. }
  1307. pPlayer->NoteSpokeVoiceCommand( szResponse );
  1308. }
  1309. else
  1310. {
  1311. pItem = NULL;
  1312. }
  1313. pExpresser->DisallowMultipleScenes();
  1314. return pItem;
  1315. }
  1316. return NULL;
  1317. }
  1318. bool CMultiplayRules::IsLoadingBugBaitReport()
  1319. {
  1320. return ( !engine->IsDedicatedServer()&& CommandLine()->CheckParm( "-bugbait" ) && sv_cheats->GetBool() );
  1321. }
  1322. void CMultiplayRules::HaveAllPlayersSpeakConceptIfAllowed( int iConcept )
  1323. {
  1324. CBaseMultiplayerPlayer *pPlayer;
  1325. for ( int i = 1; i <= gpGlobals->maxClients; i++ )
  1326. {
  1327. pPlayer = ToBaseMultiplayerPlayer( UTIL_PlayerByIndex( i ) );
  1328. if ( !pPlayer )
  1329. continue;
  1330. pPlayer->SpeakConceptIfAllowed( iConcept );
  1331. }
  1332. }
  1333. void CMultiplayRules::GetTaggedConVarList( KeyValues *pCvarTagList )
  1334. {
  1335. BaseClass::GetTaggedConVarList( pCvarTagList );
  1336. KeyValues *pGravity = new KeyValues( "sv_gravity" );
  1337. pGravity->SetString( "convar", "sv_gravity" );
  1338. pGravity->SetString( "tag", "gravity" );
  1339. pCvarTagList->AddSubKey( pGravity );
  1340. }
  1341. #else
  1342. const char *CMultiplayRules::GetVoiceCommandSubtitle( int iMenu, int iItem )
  1343. {
  1344. Assert( iMenu >= 0 && iMenu < m_VoiceCommandMenus.Count() );
  1345. if ( iMenu < 0 || iMenu >= m_VoiceCommandMenus.Count() )
  1346. return "";
  1347. Assert( iItem >= 0 && iItem < m_VoiceCommandMenus.Element( iMenu ).Count() );
  1348. if ( iItem < 0 || iItem >= m_VoiceCommandMenus.Element( iMenu ).Count() )
  1349. return "";
  1350. VoiceCommandMenuItem_t *pItem = &m_VoiceCommandMenus.Element( iMenu ).Element( iItem );
  1351. Assert( pItem );
  1352. return pItem->m_szSubtitle;
  1353. }
  1354. // Returns false if no such menu is declared or if it's an empty menu
  1355. bool CMultiplayRules::GetVoiceMenuLabels( int iMenu, KeyValues *pKV )
  1356. {
  1357. Assert( iMenu >= 0 && iMenu < m_VoiceCommandMenus.Count() );
  1358. if ( iMenu < 0 || iMenu >= m_VoiceCommandMenus.Count() )
  1359. return false;
  1360. int iNumItems = m_VoiceCommandMenus.Element( iMenu ).Count();
  1361. for ( int i=0; i<iNumItems; i++ )
  1362. {
  1363. VoiceCommandMenuItem_t *pItem = &m_VoiceCommandMenus.Element( iMenu ).Element( i );
  1364. KeyValues *pLabelKV = new KeyValues( pItem->m_szMenuLabel );
  1365. pKV->AddSubKey( pLabelKV );
  1366. }
  1367. return iNumItems > 0;
  1368. }
  1369. #endif
  1370. //-----------------------------------------------------------------------------
  1371. // Purpose: Sort function for sorting players by time spent connected ( user ID )
  1372. //-----------------------------------------------------------------------------
  1373. bool CSameTeamGroup::Less( const CSameTeamGroup &p1, const CSameTeamGroup &p2 )
  1374. {
  1375. // sort by score
  1376. return ( p1.Score() > p2.Score() );
  1377. }
  1378. CSameTeamGroup::CSameTeamGroup() :
  1379. m_nScore( INT_MIN )
  1380. {
  1381. }
  1382. CSameTeamGroup::CSameTeamGroup( const CSameTeamGroup &src )
  1383. {
  1384. m_nScore = src.m_nScore;
  1385. m_Players = src.m_Players;
  1386. }
  1387. int CSameTeamGroup::Score() const
  1388. {
  1389. return m_nScore;
  1390. }
  1391. CBasePlayer *CSameTeamGroup::GetPlayer( int idx )
  1392. {
  1393. return m_Players[ idx ];
  1394. }
  1395. int CSameTeamGroup::Count() const
  1396. {
  1397. return m_Players.Count();
  1398. }