#include "cbase.h" #include "usermessages.h" #include "funfactmgr_cs.h" #include "cs_shareddefs.h" const float kCooldownRatePlayer = 0.5f; const float kCooldownRateFunFact = 0.2f; const float kWeightPlayerCooldown = 0.7f; const float kWeightFunFactCooldown = 1.0f; const float kWeightCoolness = 1.2f; const float kWeightRarity = 1.0f; #define DEBUG_FUNFACT_SCORING 0 #if DEBUG_FUNFACT_SCORING #include "cs_player.h" #endif //----------------------------------------------------------------------------- // Purpose: constructor //----------------------------------------------------------------------------- CCSFunFactMgr::CCSFunFactMgr() : CAutoGameSystemPerFrame( "CCSFunFactMgr" ), m_funFactDatabase(0, 100, DefLessFunc(int) ) { for ( int i = 0; i < ARRAYSIZE(m_playerCooldown); ++i ) { m_playerCooldown[i] = 0.0f; } } CCSFunFactMgr::~CCSFunFactMgr() { Shutdown(); } //----------------------------------------------------------------------------- // Purpose: Initializes the fun fact manager //----------------------------------------------------------------------------- bool CCSFunFactMgr::Init() { ListenForGameEvent( "player_connect" ); CFunFactHelper *pFunFactHelper = CFunFactHelper::s_pFirst; // create database of all fun fact evaluators (and initial usage metrics) while ( pFunFactHelper ) { FunFactDatabaseEntry entry; entry.fCooldown = 0.0f; entry.iOccurrences = 0; entry.pEvaluator = pFunFactHelper->m_pfnCreate(); m_funFactDatabase.Insert(entry.pEvaluator->GetId(), entry); pFunFactHelper = pFunFactHelper->m_pNext; } for (int i = 0; i < ARRAYSIZE(m_playerCooldown); ++i) { m_playerCooldown[i] = 0.0f; } m_numRounds = 0; return true; } //----------------------------------------------------------------------------- // Purpose: Shuts down the fun fact manager //----------------------------------------------------------------------------- void CCSFunFactMgr::Shutdown() { FOR_EACH_MAP( m_funFactDatabase, iter ) { delete m_funFactDatabase[iter].pEvaluator; } m_funFactDatabase.RemoveAll(); } //----------------------------------------------------------------------------- // Purpose: Per frame processing //----------------------------------------------------------------------------- void CCSFunFactMgr::Update( float frametime ) { } //----------------------------------------------------------------------------- // Purpose: Listens for game events. Clears out map based stats and player based stats when necessary //----------------------------------------------------------------------------- void CCSFunFactMgr::FireGameEvent( IGameEvent *event ) { const char *eventname = event->GetName(); if ( Q_strcmp( "player_connect", eventname ) == 0 ) { int index = event->GetInt("index");// player slot (entity index-1) ASSERT( index >= 0 && index <= MAX_PLAYERS ); if( index >= 0 && index <= MAX_PLAYERS ) { m_playerCooldown[index] = 0.0f; } } } //----------------------------------------------------------------------------- // Purpose: Finds the best fun fact to display and returns all necessary information through the parameters //----------------------------------------------------------------------------- bool CCSFunFactMgr::GetRoundEndFunFact( int iWinningTeam, e_RoundEndReason iRoundResult, FunFact& funfact ) { //No fun fact for surrender if ( iRoundResult == CTs_Surrender || iRoundResult == Terrorists_Surrender ) { return false; } FunFactVector validFunFacts; // Generate a vector of all valid fun facts for this round FOR_EACH_MAP( m_funFactDatabase, i ) { FunFact funFact; if ( m_funFactDatabase[i].pEvaluator->Evaluate(iRoundResult, validFunFacts) ) { m_funFactDatabase[i].iOccurrences++; } } m_numRounds++; if (validFunFacts.Count() == 0) return false; // pick the fun fact with the highest score float fBestScore = -FLT_MAX; int iFunFactIndex = -1; #if DEBUG_FUNFACT_SCORING Msg("Scoring fun facts:\n"); #endif FOR_EACH_VEC(validFunFacts, i) { float fScore = ScoreFunFact(validFunFacts[i]); #if DEBUG_FUNFACT_SCORING char szPlayerName[64]; const FunFact& funfact = validFunFacts[i]; if (funfact.iPlayer > 0) V_strncpy(szPlayerName, ToCSPlayer(UTIL_PlayerByIndex(funfact.iPlayer))->GetPlayerName(), sizeof(szPlayerName)); else V_strcpy(szPlayerName, ""); Msg("(%5.4f) %s, %s, %i, %i, %i\n", fScore, funfact.szLocalizationToken, szPlayerName, funfact.iData1, funfact.iData2, funfact.iData3); #endif if (fScore > fBestScore) { fBestScore = fScore; iFunFactIndex = i; } } if (iFunFactIndex < 0) return false; funfact = validFunFacts[iFunFactIndex]; // decay player cooldowns for (int i = 0; i < ARRAYSIZE(m_playerCooldown); ++i ) { m_playerCooldown[i] *= (1.0f - kCooldownRatePlayer); } // decay funfact cooldowns FOR_EACH_MAP(m_funFactDatabase, i) { m_funFactDatabase[i].fCooldown *= (1.0f - kCooldownRateFunFact); } // set player cooldown for player in funfact m_playerCooldown[funfact.iPlayer] = 1.0f; // set funfact cooldown for current funfact m_funFactDatabase[m_funFactDatabase.Find(funfact.id)].fCooldown = 1.0f; return true; } float CCSFunFactMgr::ScoreFunFact( const FunFact& funfact ) { float fScore = 0.0f; const FunFactDatabaseEntry& dbEntry = m_funFactDatabase[m_funFactDatabase.Find(funfact.id)]; // add the coolness score for the funfact fScore += kWeightCoolness * dbEntry.pEvaluator->GetCoolness() * (1.0f + funfact.fMagnitude); // subtract the cooldown for the funfact fScore -= kWeightFunFactCooldown * dbEntry.fCooldown; // subtract the cooldown for the player fScore -= kWeightPlayerCooldown * m_playerCooldown[funfact.iPlayer]; // add the rarity bonus fScore += kWeightRarity * powf((1.0f - (float)dbEntry.iOccurrences / m_numRounds), 4.0f); return fScore; }