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.
188 lines
4.9 KiB
188 lines
4.9 KiB
//====== Copyright © Valve Corporation, All rights reserved. =================
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=============================================================================
|
|
#include "cbase.h"
|
|
#include "cs_gameplay_hints.h"
|
|
#include "keyvalues.h"
|
|
#include "fmtstr.h"
|
|
#include "filesystem.h"
|
|
#include "vgui/ILocalize.h"
|
|
#include "gametypes/igametypes.h"
|
|
#include "cs_gamerules.h"
|
|
|
|
struct CSGameplayHint_t
|
|
{
|
|
CSGameplayHint_t( const char* pszToken, uint32 flags ):
|
|
m_pszLocToken( pszToken ), m_nRequiredContextFlags( flags ), m_nDisplayCount( 0 ) {}
|
|
const char* m_pszLocToken;
|
|
uint32 m_nRequiredContextFlags;
|
|
uint32 m_nDisplayCount;
|
|
};
|
|
|
|
CCSGameplayHints::CCSGameplayHints()
|
|
{
|
|
m_pHintKV = NULL;
|
|
}
|
|
|
|
CCSGameplayHints::~CCSGameplayHints()
|
|
{
|
|
Cleanup();
|
|
}
|
|
|
|
CCSGameplayHints g_CSGameplayHints;
|
|
|
|
|
|
uint32 CCSGameplayHints::GetCurrentContextFlags( void )
|
|
{
|
|
uint32 flags = 0;
|
|
|
|
if ( CSGameRules() && CSGameRules()->IsBombDefuseMap() )
|
|
flags |= HINT_CONTEXT_BOMB_MAP;
|
|
if ( CSGameRules() && CSGameRules()->IsHostageRescueMap() )
|
|
flags |= HINT_CONTEXT_HOSTAGE_MAP;
|
|
if ( CSGameRules() && CSGameRules()->IsPlayingGunGame() )
|
|
flags |= HINT_CONTEXT_GUNGAME;
|
|
|
|
return flags;
|
|
}
|
|
|
|
uint32 ContextEntryToBitFlag( const char* szName )
|
|
{
|
|
if ( V_stricmp( szName, "bomb_map_only" ) == 0 )
|
|
{
|
|
return CCSGameplayHints::HINT_CONTEXT_BOMB_MAP;
|
|
}
|
|
else if ( V_stricmp( szName, "hostage_map_only" ) == 0 )
|
|
{
|
|
return CCSGameplayHints::HINT_CONTEXT_HOSTAGE_MAP;
|
|
}
|
|
else if ( V_stricmp( szName, "gungame_map_only" ) == 0 )
|
|
{
|
|
return CCSGameplayHints::HINT_CONTEXT_GUNGAME;
|
|
}
|
|
else
|
|
{
|
|
Warning( "HintConfig.txt contains invalid context setting: %s\n", szName );
|
|
Assert( 0 );
|
|
}
|
|
|
|
// Show it as a failsafe.
|
|
return CCSGameplayHints::HINT_CONTEXT_ALWAYS_SHOW;
|
|
}
|
|
|
|
uint32 BuildContextFlags( KeyValues *pContextKeys )
|
|
{
|
|
if ( pContextKeys == NULL || pContextKeys->GetFirstSubKey() == NULL )
|
|
return CCSGameplayHints::HINT_CONTEXT_ALWAYS_SHOW;
|
|
|
|
uint32 nFlags = 0;
|
|
for ( KeyValues *entry = pContextKeys->GetFirstSubKey(); entry != NULL; entry = entry->GetNextKey() )
|
|
{
|
|
if ( entry->GetInt() > 0 )
|
|
{
|
|
nFlags |= ContextEntryToBitFlag( entry->GetName() );
|
|
}
|
|
}
|
|
return nFlags;
|
|
}
|
|
|
|
void CCSGameplayHints::PostInit()
|
|
{
|
|
KeyValues *m_pHintKV = new KeyValues( "HintConfig.txt" );
|
|
if ( m_pHintKV->LoadFromFile( g_pFullFileSystem, "resource/HintConfig.txt" ) )
|
|
{
|
|
KeyValues *hints = m_pHintKV->FindKey( "hints" );
|
|
if ( hints )
|
|
{
|
|
for ( KeyValues *entry = hints->GetFirstSubKey(); entry != NULL; entry = entry->GetNextKey() )
|
|
{
|
|
const char* szLocToken = entry->GetString( "locToken", NULL );
|
|
if ( szLocToken )
|
|
{
|
|
const wchar_t *wszText = g_pVGuiLocalize->Find( szLocToken );
|
|
Assert( wszText );
|
|
// Sanity check length here
|
|
if ( wszText )
|
|
{
|
|
uint32 nContextFlags = BuildContextFlags( entry->FindKey( "context", NULL ) );
|
|
m_HintList.AddToTail( new CSGameplayHint_t( szLocToken, nContextFlags ) );
|
|
}
|
|
else
|
|
{
|
|
Warning( "Bad localization token in resource/HintConfig.txt: '%s'\n", szLocToken );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Assert( m_HintList.Count() > 0 );
|
|
}
|
|
|
|
void CCSGameplayHints::Cleanup( void )
|
|
{
|
|
m_HintList.PurgeAndDeleteElements();
|
|
if ( m_pHintKV )
|
|
{
|
|
m_pHintKV->deleteThis();
|
|
m_pHintKV = NULL;
|
|
}
|
|
}
|
|
void CCSGameplayHints::Shutdown()
|
|
{
|
|
Cleanup();
|
|
}
|
|
|
|
int CompareHintsByDisplayCount( CSGameplayHint_t* const* lhs, CSGameplayHint_t* const* rhs )
|
|
{
|
|
if ( (*lhs)->m_nDisplayCount > (*rhs)->m_nDisplayCount )
|
|
return 1;
|
|
else if ( (*lhs)->m_nDisplayCount < (*rhs)->m_nDisplayCount )
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
const char* CCSGameplayHints::GetRandomLeastPlayedHint( void )
|
|
{
|
|
if ( m_HintList.Count() == 0 )
|
|
return NULL;
|
|
|
|
// Sort by times 'used' and pick among the lowest counts. Counts aren't stored and will
|
|
// re-zero every time this the CCSGameplayHints class is created (so, every app launch).
|
|
m_HintList.InPlaceQuickSort( CompareHintsByDisplayCount );
|
|
|
|
uint32 contextFlags = GetCurrentContextFlags();
|
|
|
|
CUtlVector<CSGameplayHint_t*> validHints;
|
|
// Filter out based on current context flags
|
|
FOR_EACH_VEC( m_HintList, i )
|
|
{
|
|
CSGameplayHint_t* hint = m_HintList[i];
|
|
|
|
// If bits are set for required context, make sure all of them are currently met.
|
|
if ( hint->m_nRequiredContextFlags == HINT_CONTEXT_ALWAYS_SHOW ||
|
|
( contextFlags & hint->m_nRequiredContextFlags ) == hint->m_nRequiredContextFlags )
|
|
{
|
|
// Early out if we found at least one valid hint and we've moved
|
|
// on to checking hints that have been displayed more often.
|
|
// After the sort, the first found valid hint should have the lowest display count.
|
|
if ( validHints.Count() > 0 && validHints[0]->m_nDisplayCount < hint->m_nDisplayCount )
|
|
break;
|
|
|
|
validHints.AddToTail( hint );
|
|
}
|
|
}
|
|
|
|
int pick = RandomInt( 0, validHints.Count()-1 );
|
|
CSGameplayHint_t *hint = validHints[pick];
|
|
hint->m_nDisplayCount++;
|
|
return hint->m_pszLocToken;
|
|
}
|
|
/*
|
|
CON_COMMAND( pick_hint, "" )
|
|
{
|
|
Msg( "Hint: '%s'\n", g_CSGameplayHints.GetRandomLeastPlayedHint() );
|
|
}
|
|
*/
|