//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======= // // Purpose: An entity that networks the state of the game's objectives. // //============================================================================= #include "cbase.h" #include "c_team_objectiveresource.h" #include "igameevents.h" #include "teamplayroundbased_gamerules.h" #include "c_baseplayer.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #define RESOURCE_THINK_TIME 0.1 extern ConVar mp_capstyle; extern ConVar mp_capdeteriorate_time; //----------------------------------------------------------------------------- // Purpose: Owner recv proxy //----------------------------------------------------------------------------- void RecvProxy_Owner( const CRecvProxyData *pData, void *pStruct, void *pOut ) { // hacks? Not sure how else to get the index of the integer that is // being transmitted. int index = pData->m_pRecvProp->GetOffset() / sizeof(int); ObjectiveResource()->SetOwningTeam( index, pData->m_Value.m_Int ); } //----------------------------------------------------------------------------- // Purpose: capper recv proxy //----------------------------------------------------------------------------- void RecvProxy_CappingTeam( const CRecvProxyData *pData, void *pStruct, void *pOut ) { int index = pData->m_pRecvProp->GetOffset() / sizeof(int); ObjectiveResource()->SetCappingTeam( index, pData->m_Value.m_Int ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void RecvProxy_CapLayout( const CRecvProxyData *pData, void *pStruct, void *pOut ) { ObjectiveResource()->SetCapLayout( pData->m_Value.m_pString ); } IMPLEMENT_CLIENTCLASS_DT_NOBASE(C_BaseTeamObjectiveResource, DT_BaseTeamObjectiveResource, CBaseTeamObjectiveResource) RecvPropInt( RECVINFO(m_iTimerToShowInHUD) ), RecvPropInt( RECVINFO(m_iStopWatchTimer) ), RecvPropInt( RECVINFO(m_iNumControlPoints) ), RecvPropBool( RECVINFO(m_bPlayingMiniRounds) ), RecvPropBool( RECVINFO(m_bControlPointsReset) ), RecvPropInt( RECVINFO(m_iUpdateCapHudParity) ), RecvPropArray( RecvPropVector(RECVINFO(m_vCPPositions[0])), m_vCPPositions), RecvPropArray3( RECVINFO_ARRAY(m_bCPIsVisible), RecvPropInt( RECVINFO(m_bCPIsVisible[0]) ) ), RecvPropArray3( RECVINFO_ARRAY(m_flLazyCapPerc), RecvPropFloat( RECVINFO(m_flLazyCapPerc[0]) ) ), RecvPropArray3( RECVINFO_ARRAY(m_iTeamIcons), RecvPropInt( RECVINFO(m_iTeamIcons[0]) ) ), RecvPropArray3( RECVINFO_ARRAY(m_iTeamOverlays), RecvPropInt( RECVINFO(m_iTeamOverlays[0]) ) ), RecvPropArray3( RECVINFO_ARRAY(m_iTeamReqCappers), RecvPropInt( RECVINFO(m_iTeamReqCappers[0]) ) ), RecvPropArray3( RECVINFO_ARRAY(m_flTeamCapTime), RecvPropTime( RECVINFO(m_flTeamCapTime[0]) ) ), RecvPropArray3( RECVINFO_ARRAY(m_iPreviousPoints), RecvPropInt( RECVINFO(m_iPreviousPoints[0]) ) ), RecvPropArray3( RECVINFO_ARRAY(m_bTeamCanCap), RecvPropBool( RECVINFO(m_bTeamCanCap[0]) ) ), RecvPropArray3( RECVINFO_ARRAY(m_iTeamBaseIcons), RecvPropInt( RECVINFO(m_iTeamBaseIcons[0]) ) ), RecvPropArray3( RECVINFO_ARRAY(m_iBaseControlPoints), RecvPropInt( RECVINFO(m_iBaseControlPoints[0]) ) ), RecvPropArray3( RECVINFO_ARRAY(m_bInMiniRound), RecvPropBool( RECVINFO(m_bInMiniRound[0]) ) ), RecvPropArray3( RECVINFO_ARRAY(m_iWarnOnCap), RecvPropInt( RECVINFO(m_iWarnOnCap[0]) ) ), RecvPropArray( RecvPropString( RECVINFO( m_iszWarnSound[0]) ), m_iszWarnSound ), RecvPropArray3( RECVINFO_ARRAY(m_flPathDistance), RecvPropFloat( RECVINFO(m_flPathDistance[0]) ) ), // state variables RecvPropArray3( RECVINFO_ARRAY(m_iNumTeamMembers), RecvPropInt( RECVINFO(m_iNumTeamMembers[0]) ) ), RecvPropArray3( RECVINFO_ARRAY(m_iCappingTeam), RecvPropInt( RECVINFO(m_iCappingTeam[0]), 0, RecvProxy_CappingTeam ) ), RecvPropArray3( RECVINFO_ARRAY(m_iTeamInZone), RecvPropInt( RECVINFO(m_iTeamInZone[0]) ) ), RecvPropArray3( RECVINFO_ARRAY(m_bBlocked), RecvPropInt( RECVINFO(m_bBlocked[0]) ) ), RecvPropArray3( RECVINFO_ARRAY(m_iOwner), RecvPropInt( RECVINFO(m_iOwner[0]), 0, RecvProxy_Owner ) ), RecvPropString( RECVINFO(m_pszCapLayoutInHUD), 0, RecvProxy_CapLayout ), END_RECV_TABLE() C_BaseTeamObjectiveResource *g_pObjectiveResource = NULL; //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- C_BaseTeamObjectiveResource::C_BaseTeamObjectiveResource() { m_iNumControlPoints = 0; m_iPrevNumControlPoints = 0; m_pszCapLayoutInHUD[0] = 0; m_iUpdateCapHudParity = 0; m_bControlPointsReset = false; for ( int i=0; i < MAX_CONTROL_POINTS; i++ ) { m_flCapTimeLeft[i] = 0; m_flCapLastThinkTime[i] = 0; m_flLastCapWarningTime[i] = 0; m_bWarnedOnFinalCap[i] = false; // have we warned m_iWarnOnCap[i] = CP_WARN_NORMAL; // should we warn m_iszWarnSound[i][0] = 0; // what sound should be played m_flLazyCapPerc[i] = 0.0; for ( int team = 0; team < MAX_CONTROL_POINT_TEAMS; team++ ) { int iTeamIndex = TEAM_ARRAY( i, team ); m_iTeamIcons[ iTeamIndex ] = 0; m_iTeamOverlays[ iTeamIndex ] = 0; m_iTeamReqCappers[ iTeamIndex ] = 0; m_flTeamCapTime[ iTeamIndex ] = 0.0f; m_iNumTeamMembers[ iTeamIndex ] = 0; for ( int ipoint = 0; ipoint < MAX_PREVIOUS_POINTS; ipoint++ ) { int iIntIndex = ipoint + (i * MAX_PREVIOUS_POINTS) + (team * MAX_CONTROL_POINTS * MAX_PREVIOUS_POINTS); m_iPreviousPoints[ iIntIndex ] = -1; } } } for ( int team = 0; team < MAX_CONTROL_POINT_TEAMS; team++ ) { m_iTeamBaseIcons[team] = 0; } g_pObjectiveResource = this; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- C_BaseTeamObjectiveResource::~C_BaseTeamObjectiveResource() { g_pObjectiveResource = NULL; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_BaseTeamObjectiveResource::OnPreDataChanged( DataUpdateType_t updateType ) { BaseClass::OnPreDataChanged( updateType ); m_iPrevNumControlPoints = m_iNumControlPoints; m_iOldUpdateCapHudParity = m_iUpdateCapHudParity; m_bOldControlPointsReset = m_bControlPointsReset; memcpy( m_flOldLazyCapPerc, m_flLazyCapPerc, sizeof(float)*m_iNumControlPoints ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_BaseTeamObjectiveResource::OnDataChanged( DataUpdateType_t updateType ) { BaseClass::OnDataChanged( updateType ); if ( m_bOldControlPointsReset != m_bControlPointsReset || m_iNumControlPoints != m_iPrevNumControlPoints ) { // Tell everyone we know how many control points we have IGameEvent *event = gameeventmanager->CreateEvent( "controlpoint_initialized" ); if ( event ) { gameeventmanager->FireEventClientSide( event ); } } if ( m_iUpdateCapHudParity != m_iOldUpdateCapHudParity ) { UpdateControlPoint( "controlpoint_updateimages" ); } for ( int i = 0; i < m_iNumControlPoints; i++ ) { if ( m_flOldLazyCapPerc[i] != m_flLazyCapPerc[i] ) { m_flCapTimeLeft[i] = m_flLazyCapPerc[i] * m_flTeamCapTime[ TEAM_ARRAY(i,m_iCappingTeam[i]) ]; } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_BaseTeamObjectiveResource::UpdateControlPoint( const char *pszEvent, int index ) { IGameEvent *event = gameeventmanager->CreateEvent( pszEvent ); if ( event ) { event->SetInt( "index", index ); gameeventmanager->FireEventClientSide( event ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- float C_BaseTeamObjectiveResource::GetCPCapPercentage( int index ) { Assert( 0 <= index && index <= m_iNumControlPoints ); float flCapLength = m_flTeamCapTime[ TEAM_ARRAY(index,m_iCappingTeam[index]) ]; if( flCapLength <= 0 ) return 0.0f; float flElapsedTime = flCapLength - m_flCapTimeLeft[index]; if( flElapsedTime > flCapLength ) return 1.0f; return ( flElapsedTime / flCapLength ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int C_BaseTeamObjectiveResource::GetNumControlPointsOwned( void ) { C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); if ( !pPlayer ) return 0; int iTeam = pPlayer->GetTeamNumber(); int nOwned = 0; for ( int i = 0; i < GetNumControlPoints(); ++i ) { if ( GetOwningTeam( i ) == iTeam ) { ++nOwned; } } return nOwned; } //----------------------------------------------------------------------------- // Purpose: // team - //----------------------------------------------------------------------------- void C_BaseTeamObjectiveResource::SetOwningTeam( int index, int team ) { if ( team == m_iCappingTeam[index] ) { // successful cap, reset things m_iCappingTeam[index] = TEAM_UNASSIGNED; m_flCapTimeLeft[index] = 0.0f; m_flCapLastThinkTime[index] = 0; } m_iOwner[index] = team; UpdateControlPoint( "controlpoint_updateowner", index ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_BaseTeamObjectiveResource::SetCappingTeam( int index, int team ) { if ( team != GetOwningTeam( index ) && ( team > LAST_SHARED_TEAM ) ) { m_flCapTimeLeft[index] = m_flTeamCapTime[ TEAM_ARRAY(index,team) ]; } else { m_flCapTimeLeft[index] = 0.0; } m_iCappingTeam[index] = team; m_bWarnedOnFinalCap[index] = false; m_flCapLastThinkTime[index] = gpGlobals->curtime; SetNextClientThink( gpGlobals->curtime + RESOURCE_THINK_TIME ); UpdateControlPoint( "controlpoint_updatecapping", index ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_BaseTeamObjectiveResource::SetCapLayout( const char *pszLayout ) { Q_strncpy( m_pszCapLayoutInHUD, pszLayout, MAX_CAPLAYOUT_LENGTH ); UpdateControlPoint( "controlpoint_updatelayout" ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool C_BaseTeamObjectiveResource::CapIsBlocked( int index ) { Assert( 0 <= index && index <= m_iNumControlPoints ); if ( m_flCapTimeLeft[index] ) { // Blocked caps have capping teams & cap times, but no players on the point if ( GetNumPlayersInArea( index, m_iCappingTeam[index] ) == 0 ) return true; } return false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void C_BaseTeamObjectiveResource::ClientThink() { BaseClass::ClientThink(); for ( int i = 0; i < MAX_CONTROL_POINTS; i++ ) { if ( m_flCapTimeLeft[i] ) { if ( !IsCPBlocked(i) ) { bool bDeteriorateNormally = true; // Make sure there is only 1 team on the cap int iPlayersCapping = GetNumPlayersInArea( i, GetTeamInZone(i) ); if ( iPlayersCapping > 0 ) { float flReduction = gpGlobals->curtime - m_flCapLastThinkTime[i]; if ( mp_capstyle.GetInt() == 1 ) { // Diminishing returns for successive players. for ( int iPlayer = 1; iPlayer < iPlayersCapping; iPlayer++ ) { flReduction += ((gpGlobals->curtime - m_flCapLastThinkTime[i]) / (float)(iPlayer+1)); } } if ( GetTeamInZone(i) == m_iCappingTeam[i] ) { bDeteriorateNormally = false; m_flCapTimeLeft[i] -= flReduction; if ( !m_bWarnedOnFinalCap[i] ) { FOR_EACH_VALID_SPLITSCREEN_PLAYER( hh ) { ACTIVE_SPLITSCREEN_PLAYER_GUARD( hh ); // If this the local player's team, warn him C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer(); if ( pPlayer ) { if ( m_iCappingTeam[i] != TEAM_UNASSIGNED && pPlayer->GetTeamNumber() != m_iCappingTeam[i] && GetCapWarningLevel( i ) == CP_WARN_FINALCAP ) { // Prevent spam if ( gpGlobals->curtime > ( m_flLastCapWarningTime[i] + 5 ) ) { pPlayer->EmitSound( GetWarnSound( i ) ); m_bWarnedOnFinalCap[i] = true; m_flLastCapWarningTime[i] = gpGlobals->curtime; } } } } } } else if ( GetOwningTeam(i) == TEAM_UNASSIGNED && GetTeamInZone(i) != TEAM_UNASSIGNED ) { bDeteriorateNormally = false; m_flCapTimeLeft[i] += flReduction; } } if ( bDeteriorateNormally ) { // Caps deteriorate over time // If we're not cappable at all right now, wipe all progress if ( TeamplayRoundBasedRules() && TeamplayRoundBasedRules()->TeamMayCapturePoint(m_iCappingTeam[i],i) ) { float flCapLength = m_flTeamCapTime[ TEAM_ARRAY(i,m_iCappingTeam[i]) ]; float flDecrease = (flCapLength / mp_capdeteriorate_time.GetFloat()) * (gpGlobals->curtime - m_flCapLastThinkTime[i]); if ( TeamplayRoundBasedRules() && TeamplayRoundBasedRules()->InOvertime() ) { flDecrease *= 6; } m_flCapTimeLeft[i] += flDecrease; } else { m_flCapTimeLeft[i] = 0.0; } m_bWarnedOnFinalCap[i] = false; } } UpdateControlPoint( "controlpoint_updatelayout", i ); m_flCapLastThinkTime[i] = gpGlobals->curtime; } } SetNextClientThink( gpGlobals->curtime + RESOURCE_THINK_TIME ); }