// ========================================================== // given a start node and a list of goal nodes // calculate the distance between each // ========================================================== void CTeamTrainWatcher::WatcherActivate( void ) { m_flRecedeTime = 0; m_bWaitingToRecede = false; m_bCapBlocked = false; m_flNextSpeakForwardConceptTime = 0; m_hAreaCap = NULL; m_flTrainDistanceFromStart = 0.0f; m_bAlarmPlayed = false; m_Sparks.Purge(); StopCaptureAlarm(); // init our train m_hTrain = dynamic_cast<CFuncTrackTrain*>( gEntList.FindEntityByName( NULL, m_iszTrain ) ); if ( !m_hTrain ) { Warning("%s failed to find train named '%s'\n", GetClassname(), STRING( m_iszTrain ) ); } // find the trigger area that will give us movement updates and find the sparks (if we're going to handle the train movement) if ( m_bHandleTrainMovement ) { if ( m_hTrain ) { CTriggerAreaCapture *pArea = dynamic_cast<CTriggerAreaCapture *>( gEntList.FindEntityByClassname( NULL, "trigger_capture_area" ) ); while( pArea ) { if ( pArea->GetParent() == m_hTrain.Get() ) { // this is the capture area we care about, so let it know that we want updates on the capture numbers pArea->SetTrainWatcher( this ); break; } pArea = dynamic_cast<CTriggerAreaCapture *>( gEntList.FindEntityByClassname( pArea, "trigger_capture_area" ) ); } } // init the sprites (if any) CEnvSpark *pSpark = dynamic_cast<CEnvSpark*>( gEntList.FindEntityByName( NULL, m_iszSparkName ) ); while ( pSpark ) { m_Sparks.AddToTail( pSpark ); pSpark = dynamic_cast<CEnvSpark*>( gEntList.FindEntityByName( pSpark, m_iszSparkName ) ); } } // init our array of path_tracks linked to control points m_iNumCPLinks = 0; int i; for ( i = 0 ; i < MAX_CONTROL_POINTS ; i++ ) { CPathTrack *pPathTrack = dynamic_cast<CPathTrack*>( gEntList.FindEntityByName( NULL, m_iszLinkedPathTracks[i] ) ); CTeamControlPoint *pCP = dynamic_cast<CTeamControlPoint*>( gEntList.FindEntityByName( NULL, m_iszLinkedCPs[i] ) ); if ( pPathTrack && pCP ) { m_CPLinks[m_iNumCPLinks].hPathTrack = pPathTrack; m_CPLinks[m_iNumCPLinks].hCP = pCP; m_CPLinks[m_iNumCPLinks].flDistanceFromStart = 0; // filled in when we parse the nodes m_CPLinks[m_iNumCPLinks].bAlertPlayed = false; m_iNumCPLinks++; } } // init our start and goal nodes m_hStartNode = dynamic_cast<CPathTrack*>( gEntList.FindEntityByName( NULL, m_iszStartNode ) ); if ( !m_hStartNode ) { Warning("%s failed to find path_track named '%s'\n", GetClassname(), STRING(m_iszStartNode) ); } m_hGoalNode = dynamic_cast<CPathTrack*>( gEntList.FindEntityByName( NULL, m_iszGoalNode ) ); if ( !m_hGoalNode ) { Warning("%s failed to find path_track named '%s'\n", GetClassname(), STRING(m_iszGoalNode) ); } m_flTotalPathDistance = 0.0f; CUtlVector< float > hillData; bool bOnHill = false; bool bDownHillData[TEAM_TRAIN_MAX_HILLS]; Q_memset( bDownHillData, 0, sizeof( bDownHillData ) ); int iHillCount = 0; if( m_hStartNode.Get() && m_hGoalNode.Get() ) { CPathTrack *pNode = m_hStartNode; CPathTrack *pPrev = pNode; CPathTrack *pHillStart = NULL; pNode = pNode->GetNext(); int iHillType = HILL_TYPE_NONE; // don't check the start node for links. If it's linked, it will have 0 distance anyway while ( pNode ) { Vector dir = pNode->GetLocalOrigin() - pPrev->GetLocalOrigin(); float length = dir.Length(); m_flTotalPathDistance += length; // gather our hill data for the HUD if ( pNode->GetHillType() != iHillType ) { if ( !bOnHill ) // we're at the start of a hill { hillData.AddToTail( m_flTotalPathDistance ); bOnHill = true; pHillStart = pNode; if ( iHillCount < TEAM_TRAIN_MAX_HILLS ) { bDownHillData[iHillCount] = pNode->IsDownHill() ? true : false; iHillCount++; } } else // we're at the end of a hill { float flDistance = m_flTotalPathDistance - length; // subtract length because the prev node was the end of the hill (not this one) if ( pHillStart && ( pHillStart == pPrev ) ) { flDistance = m_flTotalPathDistance; // we had a single node marked as a hill, so we'll use the current distance as the next marker } hillData.AddToTail( flDistance ); // is our current node the start of another hill? if ( pNode->GetHillType() != HILL_TYPE_NONE ) { hillData.AddToTail( m_flTotalPathDistance ); bOnHill = true; pHillStart = pNode; if ( iHillCount < TEAM_TRAIN_MAX_HILLS ) { bDownHillData[iHillCount] = pNode->IsDownHill() ? true : false; iHillCount++; } } else { bOnHill = false; pHillStart = NULL; } } iHillType = pNode->GetHillType(); } // if pNode is one of our cp nodes, store its distance from m_hStartNode for ( i = 0 ; i < m_iNumCPLinks ; i++ ) { if ( m_CPLinks[i].hPathTrack == pNode ) { m_CPLinks[i].flDistanceFromStart = m_flTotalPathDistance; break; } } if ( pNode == m_hGoalNode ) break; pPrev = pNode; pNode = pNode->GetNext(); } } // if we don't have an even number of entries in our hill data (beginning/end) add the final distance if ( ( hillData.Count() % 2 ) != 0 ) { hillData.AddToTail( m_flTotalPathDistance ); } if ( ObjectiveResource() ) { ObjectiveResource()->ResetHillData( GetTeamNumber() ); // convert our hill data into 0-1 percentages for networking if ( m_flTotalPathDistance > 0 && hillData.Count() > 0 ) { i = 0; while ( i < hillData.Count() ) { if ( i < TEAM_TRAIN_HILLS_ARRAY_SIZE - 1 ) // - 1 because we want to use 2 entries { // add/subtract to the hill start/end to fix rounding errors in the HUD when the train // stops at the bottom/top of a hill but the HUD thinks the train is still on the hill ObjectiveResource()->SetHillData( GetTeamNumber(), (hillData[i] / m_flTotalPathDistance) + 0.005f, (hillData[i+1] / m_flTotalPathDistance) - 0.005f, bDownHillData[i/2] ); } i = i + 2; } } } // We have total distance and increments in our links array for ( i=0;i<m_iNumCPLinks;i++ ) { int iCPIndex = m_CPLinks[i].hCP.Get()->GetPointIndex(); // This can be pulled once DoD includes team_objectiveresource.* and c_team_objectiveresource.* #ifndef DOD_DLL ObjectiveResource()->SetTrainPathDistance( iCPIndex, m_CPLinks[i].flDistanceFromStart / m_flTotalPathDistance ); #endif } #ifdef GLOWS_ENABLE FindGlowEntity(); #endif // GLOWS_ENABLE InternalSetSpeedForwardModifier( m_flSpeedForwardModifier ); SetContextThink( &CTeamTrainWatcher::WatcherThink, gpGlobals->curtime + 0.1, TW_THINK ); }
void CTeamTrainWatcher::FireGameEvent( IGameEvent *event ) { if ( IsDisabled() || !m_bHandleTrainMovement ) return; const char *pszEventName = event->GetName(); if ( FStrEq( pszEventName, "path_track_passed" ) ) { int iIndex = event->GetInt( "index" ); CPathTrack *pNode = dynamic_cast< CPathTrack* >( UTIL_EntityByIndex( iIndex ) ); if ( pNode ) { bool bHandleEvent = false; CPathTrack *pTempNode = m_hStartNode.Get(); // is this a node in the track we're watching? while ( pTempNode ) { if ( pTempNode == pNode ) { bHandleEvent = true; break; } pTempNode = pTempNode->GetNext(); } if ( bHandleEvent ) { // If we're receding and we've hit a node but the next node (going backwards) is disabled // the train is going to stop (like at the base of a downhill section) when we start forward // again we won't pass this node again so don't change our hill state based on this node. if ( m_bReceding ) { if ( pNode->GetPrevious() && pNode->GetPrevious()->IsDisabled() ) { return; } } int iHillType = pNode->GetHillType(); bool bUpdate = ( m_iCurrentHillType != iHillType ); if ( !bUpdate ) { // the hill settings are the same, but are we leaving an uphill or downhill segment? if ( m_iCurrentHillType != HILL_TYPE_NONE ) { // let's peek at the next node CPathTrack *pNextNode = pNode->GetNext(); if ( m_flCurrentSpeed < 0 ) { // we're going backwards pNextNode = pNode->GetPrevious(); } if ( pNextNode ) { int iNextHillType = pNextNode->GetHillType(); if ( m_iCurrentHillType != iNextHillType ) { // we're leaving an uphill or downhill segment...so reset our state until we pass the next node bUpdate = true; iHillType = HILL_TYPE_NONE; } } } } if ( bUpdate ) { m_iCurrentHillType = iHillType; HandleTrainMovement(); } } } } }