diff options
Diffstat (limited to 'game/shared/tf/quest_objective_trackers.cpp')
| -rw-r--r-- | game/shared/tf/quest_objective_trackers.cpp | 550 |
1 files changed, 550 insertions, 0 deletions
diff --git a/game/shared/tf/quest_objective_trackers.cpp b/game/shared/tf/quest_objective_trackers.cpp new file mode 100644 index 0000000..3f21d4e --- /dev/null +++ b/game/shared/tf/quest_objective_trackers.cpp @@ -0,0 +1,550 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "quest_objective_manager.h" +#include "gcsdk/gcclient.h" +#include "gc_clientsystem.h" +#include "econ_quests.h" +#include "tf_gamerules.h" +#include "schemainitutils.h" +#include "econ_item_system.h" +#ifdef CLIENT_DLL + #include "quest_log_panel.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +#if defined( DEBUG ) || defined( STAGING_ONLY ) +ConVar tf_quests_commit_every_point( "tf_quests_commit_every_point", "0", FCVAR_REPLICATED ); +ConVar tf_quests_progress_enabled( "tf_quests_progress_enabled", "1", FCVAR_REPLICATED ); +#endif + + +CQuestObjectiveManager *QuestObjectiveManager( void ) +{ + static CQuestObjectiveManager g_QuestObjectiveManager; + return &g_QuestObjectiveManager; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CBaseQuestObjectiveTracker::CBaseQuestObjectiveTracker( const CTFQuestObjectiveDefinition* pObjective, CQuestItemTracker* pParent ) + : m_nObjectiveDefIndex( pObjective->GetDefinitionIndex() ) + , m_pParent( pParent ) + , m_pEvaluator( NULL ) +{ + KeyValues *pKVConditions = pObjective->GetConditionsKeyValues(); + + AssertMsg( !m_pEvaluator, "%s", CFmtStr( "Too many input for operator '%s'.", GetConditionName() ).Get() ); + + const char *pszType = pKVConditions->GetString( "type" ); + m_pEvaluator = CreateEvaluatorByName( pszType, this ); + AssertMsg( m_pEvaluator != NULL, "%s", CFmtStr( "Failed to create quest condition name '%s' for '%s'", pszType, GetConditionName() ).Get() ); + + SO_TRACKER_SPEW( CFmtStr( "Creating objective tracker def %d for quest def %d on item %llu for user %s\n", + pObjective->GetDefinitionIndex(), + pParent->GetItem()->GetItemDefinition()->GetDefinitionIndex(), + pParent->GetItem()->GetID(), + pParent->GetOwnerSteamID().Render() ), + SO_TRACKER_SPEW_OBJECTIVE_TRACKER_MANAGEMENT ); + + if ( !m_pEvaluator->BInitFromKV( pKVConditions, NULL ) ) + { + AssertMsg( false, "Failed to init from KeyValues" ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CBaseQuestObjectiveTracker::~CBaseQuestObjectiveTracker() +{ + if ( m_pEvaluator ) + { + delete m_pEvaluator; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CBaseQuestObjectiveTracker::IsValidForPlayer( const CTFPlayer *pOwner, InvalidReasonsContainer_t& invalidReasons ) const +{ + return m_pEvaluator->IsValidForPlayer( pOwner, invalidReasons ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const CTFPlayer *CBaseQuestObjectiveTracker::GetQuestOwner() const +{ + return GetTrackedPlayer(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseQuestObjectiveTracker::EvaluateCondition( CTFQuestEvaluator *pSender, int nScore ) +{ +#ifdef GAME_DLL + // tracker should be the root + Assert( !GetParent() ); + IncrementCount( nScore ); + ResetCondition(); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseQuestObjectiveTracker::ResetCondition() +{ + m_pEvaluator->ResetCondition(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CBaseQuestObjectiveTracker::UpdateConditions() +{ + const CTFQuestObjectiveDefinition *pObjective = (CTFQuestObjectiveDefinition*)ItemSystem()->GetItemSchema()->GetQuestObjectiveByDefIndex( m_nObjectiveDefIndex ); + if ( !pObjective ) + return false; + + // clean up previous evaluator + if ( m_pEvaluator ) + { + delete m_pEvaluator; + m_pEvaluator = NULL; + } + + CUtlVector< CUtlString > vecErrors; + return BInitFromKV( pObjective->GetConditionsKeyValues(), &vecErrors ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const CTFPlayer* CBaseQuestObjectiveTracker::GetTrackedPlayer() const +{ +#ifdef CLIENT_DLL + return ToTFPlayer( C_BasePlayer::GetLocalPlayer() ); +#else + return m_pParent->GetTrackedPlayer(); +#endif +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseQuestObjectiveTracker::IncrementCount( int nIncrementValue ) +{ + const CTFQuestObjectiveDefinition *pObjective = (CTFQuestObjectiveDefinition*)ItemSystem()->GetItemSchema()->GetQuestObjectiveByDefIndex( m_nObjectiveDefIndex ); + Assert( pObjective ); + if ( !pObjective ) + return; + + uint32 nPointsToAdd = nIncrementValue * pObjective->GetPoints(); + m_pParent->IncrementCount( nPointsToAdd, pObjective ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CQuestItemTracker::CQuestItemTracker( const CSharedObject* pItem, CSteamID SteamIDOwner, CSOTrackerManager* pManager ) + : CBaseSOTracker( pItem, SteamIDOwner, pManager ) + , m_pItem( NULL ) + , m_nStandardPoints( 0 ) + , m_nBonusPoints( 0 ) +#ifdef GAME_DLL + , m_nStartingStandardPoints( 0 ) + , m_nStartingBonusPoints( 0 ) +#endif +{ + m_pItem = assert_cast< const CEconItem* >( pItem ); + // Retrieve starting numbers + UpdatePointsFromSOItem(); + + SO_TRACKER_SPEW( CFmtStr( "Creating tracker for quest %d on item %llu for user %s with %dsp and %dbp\n", + GetItem()->GetItemDefinition()->GetDefinitionIndex(), + GetItem()->GetID(), + GetOwnerSteamID().Render(), + GetEarnedStandardPoints(), + GetEarnedBonusPoints() ), + SO_TRACKER_SPEW_ITEM_TRACKER_MANAGEMENT ); + + // Create trackers for each objective + QuestObjectiveDefVec_t vecChosenObjectives; + m_pItem->GetItemDefinition()->GetQuestDef()->GetRolledObjectivesForItem( vecChosenObjectives, m_pItem ); + FOR_EACH_VEC( vecChosenObjectives, i ) + { + if ( !DoesObjectiveNeedToBeTracked( vecChosenObjectives[i] ) ) + continue; + + CBaseQuestObjectiveTracker* pNewTracker = new CBaseQuestObjectiveTracker( vecChosenObjectives[i], this ); + m_vecObjectiveTrackers.AddToTail( pNewTracker ); + } + + if ( m_vecObjectiveTrackers.IsEmpty() ) + { + SO_TRACKER_SPEW( CFmtStr( "Did not create any objective trackers for quest %d on item %llu for user %s with %dsp and %dbp\n", + GetItem()->GetItemDefinition()->GetDefinitionIndex(), + GetItem()->GetID(), + GetOwnerSteamID().Render(), + GetEarnedStandardPoints(), + GetEarnedBonusPoints() ), + SO_TRACKER_SPEW_OBJECTIVE_TRACKER_MANAGEMENT ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CQuestItemTracker::~CQuestItemTracker() +{ +#ifdef CLIENT_DLL + SO_TRACKER_SPEW( CFmtStr( "Deleting tracker for quest %u on item %llu with %usp and %ubp\n", + m_pItem->GetItemDefinition()->GetDefinitionIndex(), + m_pItem->GetItemID(), + m_nStandardPoints, + m_nBonusPoints ), + SO_TRACKER_SPEW_ITEM_TRACKER_MANAGEMENT ); +#else + SO_TRACKER_SPEW( CFmtStr( "Deleting tracker for quest %u on item %llu with %usp %ussp %ubp %usbp\n", + m_pItem->GetItemDefinition()->GetDefinitionIndex(), + m_pItem->GetItemID(), + m_nStandardPoints, + m_nStartingStandardPoints, + m_nBonusPoints, + m_nStartingBonusPoints ), + SO_TRACKER_SPEW_ITEM_TRACKER_MANAGEMENT ); +#endif + m_vecObjectiveTrackers.PurgeAndDeleteElements(); +} + +//----------------------------------------------------------------------------- +// Purpose: Take a look at our item and update what we think our points are +// based on the attributes on the item IF they are greater. We NEVER +// want to lose progress for any reason. +//----------------------------------------------------------------------------- +void CQuestItemTracker::UpdatePointsFromSOItem() +{ + uint32 nNewPoints = 0; + static CSchemaAttributeDefHandle pAttribDef_EarnedStandardPoints( "quest earned standard points" ); + m_pItem->FindAttribute( pAttribDef_EarnedStandardPoints, &nNewPoints ); +#ifdef GAME_DLL + m_nStartingStandardPoints = Max( nNewPoints, m_nStartingStandardPoints ); +#else + m_nStandardPoints = Max( nNewPoints, m_nStandardPoints ); +#endif + + nNewPoints = 0; + static CSchemaAttributeDefHandle pAttribDef_EarnedBonusPoints( "quest earned bonus points" ); + m_pItem->FindAttribute( pAttribDef_EarnedBonusPoints, &nNewPoints ); +#ifdef GAME_DLL + m_nStartingBonusPoints = Max( nNewPoints, m_nStartingBonusPoints ); +#else + m_nBonusPoints = Max( nNewPoints, m_nBonusPoints ); +#endif + +#ifdef GAME_DLL + SendUpdateToClient( NULL ); + + SO_TRACKER_SPEW( CFmtStr( "Updated points from item. CS:%d S:%d CB:%d B:%d\n", m_nStandardPoints, m_nStartingStandardPoints, m_nBonusPoints, m_nStartingBonusPoints ), SO_TRACKER_SPEW_OBJECTIVES ); +#else + SO_TRACKER_SPEW( CFmtStr( "Updated points from item. S:%d B:%d\n", m_nStandardPoints, m_nBonusPoints ), SO_TRACKER_SPEW_OBJECTIVES ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +const CBaseQuestObjectiveTracker* CQuestItemTracker::FindTrackerForDefIndex( uint32 nDefIndex ) const +{ + FOR_EACH_VEC( m_vecObjectiveTrackers, i ) + { + if ( m_vecObjectiveTrackers[ i ]->GetObjectiveDefIndex() == nDefIndex ) + { + return m_vecObjectiveTrackers[ i ]; + } + } + + return NULL; +} + +uint32 CQuestItemTracker::GetEarnedStandardPoints() const +{ +#ifdef GAME_DLL + return m_nStartingStandardPoints + m_nStandardPoints; +#else + return m_nStandardPoints; +#endif +} +uint32 CQuestItemTracker::GetEarnedBonusPoints() const +{ +#ifdef GAME_DLL + return m_nStartingBonusPoints + m_nBonusPoints; +#else + return m_nBonusPoints; +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CQuestItemTracker::IncrementCount( uint32 nIncrementValue, const CQuestObjectiveDefinition* pObjective ) +{ +#if defined( DEBUG ) || defined( STAGING_ONLY ) + if ( !tf_quests_progress_enabled.GetBool() ) + return; +#endif + +#ifdef GAME_DLL + Assert( pObjective ); + Assert( m_pItem ); + if ( !pObjective || !m_pItem ) + return; + + auto pQuestDef = m_pItem->GetItemDefinition()->GetQuestDef(); + Assert( pQuestDef ); + if ( !pQuestDef ) + return; + + if ( g_pVGuiLocalize && ( g_nQuestSpewFlags & SO_TRACKER_SPEW_OBJECTIVES ) ) + { + locchar_t loc_IntermediateName[ MAX_ITEM_NAME_LENGTH ]; + locchar_t locValue[ MAX_ITEM_NAME_LENGTH ]; + loc_sprintf_safe( locValue, LOCCHAR( "%d" ), pObjective->GetPoints() ); + loc_scpy_safe( loc_IntermediateName, CConstructLocalizedString( g_pVGuiLocalize->Find( pObjective->GetDescriptionToken() ), locValue ) ); + char szTempObjectiveName[256]; + ::ILocalize::ConvertUnicodeToANSI( loc_IntermediateName, szTempObjectiveName, sizeof( szTempObjectiveName )); + + SO_TRACKER_SPEW( CFmtStr( "Increment for quest: %llu Objective: \"%s\" %d->%d (+%d)\n" + , m_pItem->GetItemID() + , szTempObjectiveName + , m_nStandardPoints + m_nBonusPoints + , m_nStandardPoints + m_nBonusPoints + nIncrementValue + , nIncrementValue ), SO_TRACKER_SPEW_OBJECTIVES ); + } + + // Regardless of standard or bonus, we fill the standard gauge first + uint32 nMaxStandardPoints = pQuestDef->GetMaxStandardPoints() - GetEarnedStandardPoints(); + int nAmountToAdd = Min( nMaxStandardPoints, nIncrementValue ); + m_nStandardPoints += nAmountToAdd; + nIncrementValue -= nAmountToAdd; + + // If any bonus points left, fill in bonus points + if ( pObjective->IsAdvanced() && nIncrementValue > 0 ) + { + uint32 nMaxBonusPoints = pQuestDef->GetMaxBonusPoints() + pQuestDef->GetMaxStandardPoints() - GetEarnedStandardPoints() - GetEarnedBonusPoints(); + m_nBonusPoints += Min( nMaxBonusPoints, nIncrementValue ); + } + + bool bShouldCommit = IsQuestItemReadyToTurnIn( m_pItem ); +#if defined( DEBUG ) || defined( STAGING_ONLY ) + bShouldCommit |= tf_quests_commit_every_point.GetBool(); +#endif + + // Once we're over the turn-in threshhold, we need to record every point made. + if ( bShouldCommit ) + { + CommitChangesToDB(); + } + + SendUpdateToClient( pObjective ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: Remove and delete any objective trackers that are no longer needed. +// One is considered not needed if it's a tracker for a "standard" +// objective and we're done getting standard points, or if we're at +// full bonus points, then there's no way for us to get points anymore +//----------------------------------------------------------------------------- +void CQuestItemTracker::OnUpdate() +{ + FOR_EACH_VEC_BACK( m_vecObjectiveTrackers, i ) + { + const CQuestObjectiveDefinition *pObjective = GEconItemSchema().GetQuestObjectiveByDefIndex( m_vecObjectiveTrackers[ i ]->GetObjectiveDefIndex() ); + Assert( pObjective ); + if ( !pObjective || !DoesObjectiveNeedToBeTracked( pObjective ) ) + { + delete m_vecObjectiveTrackers[ i ]; + m_vecObjectiveTrackers.Remove( i ); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CQuestItemTracker::OnRemove() +{ +#ifdef GAME_DLL + CommitRecord_t* pRecord = m_pManager->GetCommitRecord( m_pItem->GetItemID() ); + if ( pRecord ) + { + CMsgGCQuestObjective_PointsChange* pProto = assert_cast< CMsgGCQuestObjective_PointsChange* >( pRecord->m_pProtoMsg ); + pProto->set_update_base_points( true ); + } +#endif +} + +void CQuestItemTracker::Spew() const +{ + CBaseSOTracker::Spew(); + + FOR_EACH_VEC( m_vecObjectiveTrackers, i ) + { + DevMsg( "Tracking objective: %d\n", m_vecObjectiveTrackers[ i ]->GetObjectiveDefIndex() ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CQuestItemTracker::DoesObjectiveNeedToBeTracked( const CQuestObjectiveDefinition* pObjective ) const +{ + auto pQuestDef = m_pItem->GetItemDefinition()->GetQuestDef(); + + Assert( pObjective ); + if ( pObjective && pQuestDef ) + { + // If there's standard points to be earned, all objectives need to be tracked + if ( pQuestDef->GetMaxStandardPoints() > 0 && GetEarnedStandardPoints() < pQuestDef->GetMaxStandardPoints() ) + { + return true; + } + + // If this objective is advanced, only track it if there's bonus points to be earned + if ( pObjective->IsAdvanced() ) + { + return pQuestDef->GetMaxBonusPoints() > 0 && GetEarnedBonusPoints() < pQuestDef->GetMaxBonusPoints(); + } + } + + return false; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CQuestItemTracker::CommitChangesToDB() +{ +#ifdef CLIENT_DLL + if ( GetQuestLog() && GetTrackedPlayer() == C_TFPlayer::GetLocalTFPlayer() ) + { + GetQuestLog()->MarkQuestsDirty(); + } +#else // GAME_DLL + + // Nothing to commit? Bail + if ( m_nStandardPoints == 0 && m_nBonusPoints == 0 ) + return; + + SO_TRACKER_SPEW( CFmtStr( "CommitChangesToDB: %llu S:%d B:%d\n" + , m_pItem->GetItemID() + , GetEarnedStandardPoints() + , GetEarnedBonusPoints() ), 0 ); + + CSteamID ownerSteamID( m_pItem->GetAccountID(), GetUniverse(), k_EAccountTypeIndividual ); + + CMsgGCQuestObjective_PointsChange record; + + // Cook up our message + record.set_owner_steamid( ownerSteamID.ConvertToUint64() ); + record.set_quest_item_id( m_pItem->GetItemID() ); + record.set_standard_points( GetEarnedStandardPoints() ); + record.set_bonus_points( GetEarnedBonusPoints() ); // Here's the meat + + m_pManager->AddCommitRecord( &record, record.quest_item_id(), true ); +#endif +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CQuestItemTracker::IsValidForPlayer( const CTFPlayer *pOwner, InvalidReasonsContainer_t& invalidReasons ) const +{ + int nNumInvalid = 0; + FOR_EACH_VEC( m_vecObjectiveTrackers, i ) + { + m_vecObjectiveTrackers[ i ]->IsValidForPlayer( pOwner, invalidReasons ); + + if ( !invalidReasons.IsValid() ) + { + ++nNumInvalid; + } + } + + return nNumInvalid; +} + +#ifdef CLIENT_DLL +//----------------------------------------------------------------------------- +// Purpose: The server has changed scores. Apply those changes here +//----------------------------------------------------------------------------- +void CQuestItemTracker::UpdateFromServer( uint32 nStandardPoints, uint32 nBonusPoints ) +{ + SO_TRACKER_SPEW( CFmtStr( "Updating \"%s's\" standard points: %d->%d bonus points: %d->%d\n" + , m_pItem->GetItemDefinition()->GetQuestDef()->GetRolledNameForItem( m_pItem ) + , m_nStandardPoints + , nStandardPoints + , m_nBonusPoints + , nBonusPoints ) + , SO_TRACKER_SPEW_OBJECTIVES ); + + m_nStandardPoints = nStandardPoints; + m_nBonusPoints = nBonusPoints; +} +#else +void CQuestItemTracker::SendUpdateToClient( const CQuestObjectiveDefinition* pObjective ) +{ + const CTFPlayer* pPlayer = GetTrackedPlayer(); + + // They might've disconnected, so let's check if they're still around + if ( pPlayer ) + { + // Update the user on their progress + CSingleUserRecipientFilter filter( GetTrackedPlayer() ); + filter.MakeReliable(); + UserMessageBegin( filter, "QuestObjectiveCompleted" ); + itemid_t nID = m_pItem->GetItemID(); + WRITE_BITS( &nID, 64 ); + WRITE_WORD( GetEarnedStandardPoints() ); + WRITE_WORD( GetEarnedBonusPoints() ); + WRITE_WORD( pObjective ? pObjective->GetDefinitionIndex() : (uint32)-1 ); + MessageEnd(); + } +} +#endif + +#if defined( DEBUG ) || defined( STAGING_ONLY ) +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CQuestItemTracker::DBG_CompleteQuest() +{ +#ifdef GAME_DLL + + auto pQuestDef = m_pItem->GetItemDefinition()->GetQuestDef(); + uint32 nStandardPointsDelta = pQuestDef->GetMaxStandardPoints() - GetEarnedStandardPoints(); + + // Cheat! + if ( m_vecObjectiveTrackers.Count() ) + { + const_cast< CBaseQuestObjectiveTracker* >( m_vecObjectiveTrackers[0] )->EvaluateCondition( NULL, nStandardPointsDelta ); + } + + CommitChangesToDB(); +#endif +} +#endif |