void Save( ISave *pSave )
	{
		pSave->WriteBool( &g_bInCommentaryMode );
		if ( IsInCommentaryMode() )
		{
			pSave->WriteAll( &g_CommentarySystem, g_CommentarySystem.GetDataDescMap() );
			pSave->WriteInt( &CAI_BaseNPC::m_nDebugBits );
		}
	}
	virtual void LevelInitPostEntity( void )
	{
		if ( !IsInCommentaryMode() )
			return;

		// Don't spawn commentary entities when loading a savegame
		if ( gpGlobals->eLoadType == MapLoad_LoadGame || gpGlobals->eLoadType == MapLoad_Background )
			return;

		m_bCommentaryEnabledMidGame = false;
		InitCommentary();
	}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTeamRoundTimer::ClientThink()
{
	if ( IsDisabled() || m_bTimerPaused || IsInCommentaryMode() )
		return;

	if ( IsStopWatchTimer() == true && IsWatchingTimeStamps() == true )
		return;

	float flTime = GetTimeRemaining();

	if ( flTime <= 61.0 && m_bFire1MinRemain )
	{
		m_bFire1MinRemain = false;
		SendTimeWarning( RT_WARNING_60SECS );
	}
	else if ( flTime <= 31.0 && m_bFire30SecRemain )
	{
		m_bFire30SecRemain = false;
		SendTimeWarning( RT_WARNING_30SECS );
	}
	else if ( flTime <= 11.0 && m_bFire10SecRemain )
	{
		m_bFire10SecRemain = false;
		SendTimeWarning( RT_WARNING_10SECS );
	}
	else if ( flTime <= 6.0 && m_bFire5SecRemain )
	{
		m_bFire5SecRemain = false;
		SendTimeWarning( RT_WARNING_5SECS );
	}
	else if ( flTime <= 5.0 && m_bFire4SecRemain )
	{
		m_bFire4SecRemain = false;
		SendTimeWarning( RT_WARNING_4SECS );
	}
	else if ( flTime <= 4.0 && m_bFire3SecRemain )
	{
		m_bFire3SecRemain = false;
		SendTimeWarning( RT_WARNING_3SECS );
	}
	else if ( flTime <= 3.0 && m_bFire2SecRemain )
	{
		m_bFire2SecRemain = false;
		SendTimeWarning( RT_WARNING_2SECS );
	}
	else if ( flTime <= 2.0 && m_bFire1SecRemain )
	{
		m_bFire1SecRemain = false;
		SendTimeWarning( RT_WARNING_1SECS );
	}
}
	void SetCommentaryMode( bool bCommentaryMode )
	{
		g_bInCommentaryMode = bCommentaryMode;
		CalculateCommentaryState();

		// If we're turning on commentary, create all the entities.
		if ( IsInCommentaryMode() )
		{
			m_bCommentaryEnabledMidGame = true;
			InitCommentary();
		}
		else
		{
			ShutDownCommentary();
		}
	}
	void OnRestore( void )
	{
		cvar->InstallGlobalChangeCallback( NULL );

		if ( !IsInCommentaryMode() )
			return;

		// Set any convars that have already been changed by the commentary before the save
		for ( int i = 0; i < m_ModifiedConvars.Count(); i++ )
		{
			ConVar *pConVar = (ConVar *)cvar->FindVar( m_ModifiedConvars[i].pszConvar );
			if ( pConVar )
			{
				//Msg("    Restoring Convar %s: value %s (org %s)\n", m_ModifiedConvars[i].pszConvar, m_ModifiedConvars[i].pszCurrentValue, m_ModifiedConvars[i].pszOrgValue );
				pConVar->SetValue( m_ModifiedConvars[i].pszCurrentValue );
			}
		}

		// Install the global cvar callback
		cvar->InstallGlobalChangeCallback( CV_GlobalChange_Commentary );
	}
//-----------------------------------------------------------------------------
// Look for a target
//-----------------------------------------------------------------------------
bool CObjectSentrygun::FindTarget()
{
	// Disable the sentry guns for ifm.
	if ( tf_sentrygun_notarget.GetBool() )
		return false;

	if ( IsInCommentaryMode() )
		return false;

	// Sapper, etc.
	if ( IsDisabled() )
		return false;

	// Loop through players within 1100 units (sentry range).
	Vector vecSentryOrigin = EyePosition();

	// Find the opposing team list.
	CTFPlayer *pPlayer = ToTFPlayer( GetOwner() );
	CUtlVector<CTFTeam *> pTeamList;
	CTFTeam *pTeam = NULL;

	//CTFTeam *pTeam = pPlayer->GetOpposingTFTeam();
	//if ( !pTeam )
	//	return false;

	if ( pPlayer )
	{
		// Try builder's team.
		pTeam = pPlayer->GetTFTeam();
	}
	else
	{
		// If we have no builder use our own team number instead.
		pTeam = GetTFTeam();
	}

	if ( pTeam )
		pTeam->GetOpposingTFTeamList( &pTeamList );
	else
		return false;

	// If we have an enemy get his minimum distance to check against.
	Vector vecSegment;
	Vector vecTargetCenter;
	float flMinDist2 = 1100.0f * 1100.0f;
	CBaseEntity *pTargetCurrent = NULL;
	CBaseEntity *pTargetOld = m_hEnemy.Get();
	float flOldTargetDist2 = FLT_MAX;

	// Sentries will try to target players first, then objects.  However, if the enemy held was an object it will continue
	// to try and attack it first.

	for (int i = 0; i < pTeamList.Size(); i++)
	{
		int nTeamCount = pTeamList[i]->GetNumPlayers();
		for (int iPlayer = 0; iPlayer < nTeamCount; ++iPlayer)
		{
			CTFPlayer *pTargetPlayer = static_cast<CTFPlayer*>(pTeamList[i]->GetPlayer(iPlayer));
			if (pTargetPlayer == NULL)
				continue;

			// Make sure the player is alive.
			if (!pTargetPlayer->IsAlive())
				continue;

			if (pTargetPlayer->GetFlags() & FL_NOTARGET)
				continue;

			vecTargetCenter = pTargetPlayer->GetAbsOrigin();
			vecTargetCenter += pTargetPlayer->GetViewOffset();
			VectorSubtract(vecTargetCenter, vecSentryOrigin, vecSegment);
			float flDist2 = vecSegment.LengthSqr();

			// Store the current target distance if we come across it
			if (pTargetPlayer == pTargetOld)
			{
				flOldTargetDist2 = flDist2;
			}

			// Check to see if the target is closer than the already validated target.
			if (flDist2 > flMinDist2)
				continue;

			// It is closer, check to see if the target is valid.
			if (ValidTargetPlayer(pTargetPlayer, vecSentryOrigin, vecTargetCenter))
			{
				flMinDist2 = flDist2;
				pTargetCurrent = pTargetPlayer;
			}
		}

		// If we already have a target, don't check objects.
		if (pTargetCurrent == NULL)
		{
			int nTeamObjectCount = pTeamList[i]->GetNumObjects();
			for (int iObject = 0; iObject < nTeamObjectCount; ++iObject)
			{
				CBaseObject *pTargetObject = pTeamList[i]->GetObject(iObject);
				if (!pTargetObject)
					continue;

				vecTargetCenter = pTargetObject->GetAbsOrigin();
				vecTargetCenter += pTargetObject->GetViewOffset();
				VectorSubtract(vecTargetCenter, vecSentryOrigin, vecSegment);
				float flDist2 = vecSegment.LengthSqr();

				// Store the current target distance if we come across it
				if (pTargetObject == pTargetOld)
				{
					flOldTargetDist2 = flDist2;
				}

				// Check to see if the target is closer than the already validated target.
				if (flDist2 > flMinDist2)
					continue;

				// It is closer, check to see if the target is valid.
				if (ValidTargetObject(pTargetObject, vecSentryOrigin, vecTargetCenter))
				{
					flMinDist2 = flDist2;
					pTargetCurrent = pTargetObject;
				}
			}
		}
		// We have a target.
		if (pTargetCurrent)
		{
			if (pTargetCurrent != pTargetOld)
			{
				// flMinDist2 is the new target's distance
				// flOldTargetDist2 is the old target's distance
				// Don't switch unless the new target is closer by some percentage
				if (flMinDist2 < (flOldTargetDist2 * 0.75f))
				{
					FoundTarget(pTargetCurrent, vecSentryOrigin);
				}
			}
			return true;
		}
	}

	return false;
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTeamRoundTimer::RoundTimerThink( void )
{
	if ( TeamplayRoundBasedRules()->IsInPreMatch() == true && IsDisabled() == false )
	{
		inputdata_t data;
		InputDisable( data );
	}

	if ( IsDisabled() || m_bTimerPaused || IsInCommentaryMode() || gpGlobals->eLoadType == MapLoad_Background )
	{
		SetContextThink( &CTeamRoundTimer::RoundTimerThink, gpGlobals->curtime + 0.05, ROUND_TIMER_THINK );
		return;
	}

	// Don't do anything when the game has been won or if we're loading a bugbait report
	if ( TeamplayRoundBasedRules()->RoundHasBeenWon() ||
		 TeamplayRoundBasedRules()->IsLoadingBugBaitReport() )
	{
		// We want to stop timers when the round has been won, but we don't want to 
		// force mapmakers to deal with having to unpause it. This little hack works around that.
		if ( !m_bTimerPaused )
		{
			PauseTimer();
			m_bPauseDueToWin = true;
		}

		SetContextThink( &CTeamRoundTimer::RoundTimerThink, gpGlobals->curtime + 0.05, ROUND_TIMER_THINK );
		return;
	}
	else if ( m_bPauseDueToWin )
	{
		ResumeTimer();
		m_bPauseDueToWin = false;
	}

	float flTime = GetTimeRemaining();

	if ( flTime > 0 && ShowInHud() ) // is this the timer we're showing in the HUD?
	{
		TeamplayRoundBasedRules()->SetOvertime( false );
	}

	if ( flTime <= 0.0f && m_bFireFinished )
	{
		// Allow the gamerules to prevent timer expiration (i.e. while a control point is contested)
		if ( !TeamplayGameRules()->TimerMayExpire() )
		{
			// we don't want the timer to keep going (negative time)
			m_flTimerEndTime = gpGlobals->curtime;

			// is this the timer we're showing in the HUD?
			if ( ShowInHud() )
			{
				TeamplayRoundBasedRules()->SetOvertime( true );
			}

			// Think slower
			SetContextThink( &CTeamRoundTimer::RoundTimerThink, gpGlobals->curtime + 1.0, ROUND_TIMER_THINK );
			return;
		}

		m_OnFinished.FireOutput( this, this );
		m_bFireFinished = false;
	}
	else if ( flTime <= 300.0 && m_bFire5MinRemain )
	{
		m_On5MinRemain.FireOutput( this, this );
		m_bFire5MinRemain = false;
	}
	else if ( flTime <= 240.0 && m_bFire4MinRemain )
	{
		m_On4MinRemain.FireOutput( this, this );
		m_bFire4MinRemain = false;
	}
	else if ( flTime <= 180.0 && m_bFire3MinRemain )
	{
		m_On3MinRemain.FireOutput( this, this );
		m_bFire3MinRemain = false;
	}
	else if ( flTime <= 120.0 && m_bFire2MinRemain )
	{
		m_On2MinRemain.FireOutput( this, this );
		m_bFire2MinRemain = false;
	}
	else if ( flTime <= 60.0 && m_bFire1MinRemain )
	{
		m_On1MinRemain.FireOutput( this, this );
		m_bFire1MinRemain = false;
	}
	else if ( flTime <= 30.0 && m_bFire30SecRemain )
	{
		m_On30SecRemain.FireOutput( this, this );
		m_bFire30SecRemain = false;
	}
	else if ( flTime <= 10.0 && m_bFire10SecRemain )
	{
		m_On10SecRemain.FireOutput( this, this );
		m_bFire10SecRemain = false;
	}
	else if ( flTime <= 5.0 && m_bFire5SecRemain )
	{
		m_On5SecRemain.FireOutput( this, this );
		m_bFire5SecRemain = false;
	}
	else if ( flTime <= 4.0 && m_bFire4SecRemain )
	{
		m_On4SecRemain.FireOutput( this, this );
		m_bFire4SecRemain = false;
	}
	else if ( flTime <= 3.0 && m_bFire3SecRemain )
	{
		m_On3SecRemain.FireOutput( this, this );
		m_bFire3SecRemain = false;
	}
	else if ( flTime <= 2.0 && m_bFire2SecRemain )
	{
		m_On2SecRemain.FireOutput( this, this );
		m_bFire2SecRemain = false;
	}
	else if ( flTime <= 1.0 && m_bFire1SecRemain )
	{
		m_On1SecRemain.FireOutput( this, this );
		m_bFire1SecRemain = false;
	}

	SetContextThink( &CTeamRoundTimer::RoundTimerThink, gpGlobals->curtime + 0.05, ROUND_TIMER_THINK );
}
	virtual void FrameUpdatePrePlayerRunCommand( void )
	{
		if ( !IsInCommentaryMode() )
			return;

		CPointCommentaryNode *pCurrentNode = GetNodeUnderCrosshair();

		// Changed nodes?
 		if ( m_hCurrentNode != pCurrentNode )
		{
			// Stop animating the old one
 			if ( m_hCurrentNode.Get() )
			{
				m_hCurrentNode->SetUnderCrosshair( false );
			}

			// Start animating the new one
			if ( pCurrentNode )
			{
				pCurrentNode->SetUnderCrosshair( true );
			}

			m_hCurrentNode = pCurrentNode;
		}

		// Check for commentary node activations
		CBasePlayer *pPlayer = IGameSystem::RunCommandPlayer();
		CUserCmd *pUserCmds = IGameSystem::RunCommandUserCmd();
		if ( pPlayer )
		{
			// Has the player pressed down an attack button?
			int buttonsChanged = m_afPlayersLastButtons ^ pUserCmds->buttons;
			int buttonsPressed = buttonsChanged & pUserCmds->buttons;
			m_afPlayersLastButtons = pUserCmds->buttons;

			if ( !(pUserCmds->buttons & COMMENTARY_BUTTONS) )
			{
				m_iClearPressedButtons &= ~COMMENTARY_BUTTONS;
			}

			// Detect press events to start/stop commentary nodes
			if (buttonsPressed & COMMENTARY_BUTTONS) 
			{
 				// Looking at a node?
				if ( m_hCurrentNode )
				{
					// Ignore input while an unstoppable node is playing
					if ( !GetActiveNode() || !GetActiveNode()->CannotBeStopped() )
					{
						// If we have an active node already, stop it
						if ( GetActiveNode() && GetActiveNode() != m_hCurrentNode )
						{
							GetActiveNode()->StopPlaying();
 						}

						m_hCurrentNode->PlayerActivated();
					}

					// Prevent weapon firing when toggling nodes
					pUserCmds->buttons &= ~COMMENTARY_BUTTONS;
					m_iClearPressedButtons |= (buttonsPressed & COMMENTARY_BUTTONS);
				}
				else if ( GetActiveNode() && GetActiveNode()->HasViewTarget() )
				{
					if ( !GetActiveNode()->CannotBeStopped() )
					{
						GetActiveNode()->StopPlaying();
					}

					// Prevent weapon firing when toggling nodes
					pUserCmds->buttons &= ~COMMENTARY_BUTTONS;
					m_iClearPressedButtons |= (buttonsPressed & COMMENTARY_BUTTONS);
				}
			}

			if ( GetActiveNode() && GetActiveNode()->PreventsMovement() )
			{
 				pUserCmds->buttons &= ~(IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT | IN_JUMP | IN_DUCK );
				pUserCmds->upmove = 0;
				pUserCmds->sidemove = 0;
				pUserCmds->forwardmove = 0;
			}

			// When we swallow button down events, we have to keep clearing that button
			// until the player releases the button. Otherwise, the frame after we swallow
			// it, the code detects the button down and goes ahead as normal.
			pUserCmds->buttons &= ~m_iClearPressedButtons;
		}
	}