📄 tf_playerclass.cpp
字号:
/***
*
* Copyright (c) 1999, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
* Use, distribution, and modification of this source code and/or resulting
* object code is restricted to non-commercial enhancements to products from
* Valve LLC. All other use, distribution, or modification is prohibited
* without written permission from Valve LLC.
*
****/
/*
===== tf_playerclass.cpp ========================================================
functions dealing with the TF playerclasses
*/
#include "cbase.h"
#include "player.h"
#include "basecombatweapon.h"
#include "tf_player.h"
#include "tf_obj.h"
#include "weapon_builder.h"
#include "tf_team.h"
#include "tf_func_resource.h"
#include "orders.h"
#include "order_repair.h"
#include "engine/IEngineSound.h"
#include "vstdlib/strtools.h"
#include "textstatsmgr.h"
#include "ammodef.h"
#include "weapon_objectselection.h"
#include "vcollide_parse.h"
#include "weapon_combatshield.h"
#include "tf_vehicle_tank.h"
#include "tf_obj_manned_plasmagun.h"
#include "tf_obj_manned_missilelauncher.h"
extern char *g_pszEMPPulseStart;
extern ConVar tf_fastbuild;
// Stat groupings
enum
{
STATS_TANK = TFCLASS_CLASS_COUNT,
STATS_MANNEDGUN_PLASMA,
STATS_MANNEDGUN_ROCKET,
STATS_NUM_GROUPS,
};
char *sNonClassStatNames[] =
{
"Tanks",
"Manned Plasmaguns",
"Manned Rocket launchers",
};
// Stats between player classes (like how many snipers killed recons).
class CInterClassStats
{
public:
CInterClassStats()
{
m_flTotalDamageInflicted = 0;
m_nKills = 0;
m_flTotalEngagementDist = 0;
m_nEngagements = 0;
m_flTotalNormalizedEngagementDist = 0;
m_nNormalizedEngagements = 0;
}
public:
//
// Note: "containing class" refers to the class in the CPlayerClassStats that owns this CInterClassStats.
// So if there's a CPlayerClassStats for a recon, and it has a CInterClassStats for a sniper,
// then "containing class" refers to the recon. In this example, m_flTotalDamageInflicted representss
// how much damage the recon players inflicted on the snipers.
//
double m_flTotalDamageInflicted; // Total damage inflicted on this class by the containing class.
double m_flTotalEngagementDist; // Used to get the average engagement distance.
int m_nEngagements;
double m_flTotalNormalizedEngagementDist;
int m_nNormalizedEngagements;
int m_nKills; // Now many times the containing class killed this one.
};
class CPlayerClassStats
{
public:
CPlayerClassStats()
{
m_flPlayerTime = 0;
}
public:
CInterClassStats m_InterClassStats[STATS_NUM_GROUPS];
double m_flPlayerTime; // How much player time was spent in this class.
};
ConVar tf2_object_hard_limits( "tf2_object_hard_limits","0", FCVAR_NONE, "If true, use hard object limits instead of resource costs" );
CPlayerClassStats g_PlayerClassStats[STATS_NUM_GROUPS];
void AddPlayerClassTime( int classnum, float seconds )
{
g_PlayerClassStats[ classnum ].m_flPlayerTime += seconds;
}
int GetStatGroupFor( CBaseTFPlayer *pPlayer )
{
// In a vehicle?
if ( pPlayer->IsInAVehicle() )
{
if ( dynamic_cast<CVehicleTank*>( pPlayer->GetVehicle() ) )
return STATS_TANK;
if ( dynamic_cast<CObjectMannedMissileLauncher*>( pPlayer->GetVehicle() ) )
return STATS_MANNEDGUN_ROCKET;
if ( dynamic_cast<CObjectMannedPlasmagun*>( pPlayer->GetVehicle() ) )
return STATS_MANNEDGUN_PLASMA;
}
// Otherwise, use the playerclass
return pPlayer->GetPlayerClass()->GetTFClass();
}
const char* GetGroupNameFor( int iStatGroup )
{
Assert( iStatGroup > TFCLASS_UNDECIDED && iStatGroup < STATS_NUM_GROUPS );
if ( iStatGroup < TFCLASS_CLASS_COUNT )
return GetTFClassInfo( iStatGroup )->m_pClassName;
else
return sNonClassStatNames[ iStatGroup - TFCLASS_CLASS_COUNT ];
}
void PrintPlayerClassStats()
{
IFileSystem *pFileSys = filesystem;
FileHandle_t hFile = pFileSys->Open( "class_stats.txt", "wt", "LOGDIR" );
if ( hFile == FILESYSTEM_INVALID_HANDLE )
return;
pFileSys->FPrintf( hFile, "Class\tPlayer Time (minutes)\tAvg Engagement Dist\t(OLD) Engagement Dist\n" );
for ( int i=TFCLASS_UNDECIDED+1; i < STATS_NUM_GROUPS; i++ )
{
CPlayerClassStats *pStats = &g_PlayerClassStats[i];
// Figure out the average engagement distance across all classes.
int j;
double flAvgEngagementDist = 0;
int nTotalEngagements = 0;
double flTotalEngagementDist = 0;
double flTotalNormalizedEngagementDist = 0;
int nTotalNormalizedEngagements = 0;
for ( j=TFCLASS_UNDECIDED+1; j < STATS_NUM_GROUPS; j++ )
{
CInterClassStats *pInter = &g_PlayerClassStats[i].m_InterClassStats[j];
nTotalEngagements += pInter->m_nEngagements;
flTotalEngagementDist += pInter->m_flTotalEngagementDist;
nTotalNormalizedEngagements += pInter->m_nNormalizedEngagements;
flTotalNormalizedEngagementDist += pInter->m_flTotalNormalizedEngagementDist;
}
flAvgEngagementDist = nTotalEngagements ? ( flTotalEngagementDist / nTotalEngagements ) : 0;
double flAvgNormalizedEngagementDist = nTotalNormalizedEngagements ? (flTotalNormalizedEngagementDist / nTotalNormalizedEngagements) : 0;
pFileSys->FPrintf( hFile, "%s", GetGroupNameFor( i ) );
pFileSys->FPrintf( hFile, "\t%.1f", (pStats->m_flPlayerTime / 60.0f) );
pFileSys->FPrintf( hFile, "\t%d", (int)flAvgNormalizedEngagementDist );
pFileSys->FPrintf( hFile, "\t%d", (int)flAvgEngagementDist );
pFileSys->FPrintf( hFile, "\n" );
}
pFileSys->FPrintf( hFile, "\n" );
pFileSys->FPrintf( hFile, "\n" );
pFileSys->FPrintf( hFile, "Class\tTarget Class\tTotal Damage\tKills\tAvg Engagement Dist\t(OLD) Engagement Dist\n" );
for ( i=TFCLASS_UNDECIDED+1; i < STATS_NUM_GROUPS; i++ )
{
CPlayerClassStats *pStats = &g_PlayerClassStats[i];
// Print the inter-class stats.
for ( int j=TFCLASS_UNDECIDED+1; j < STATS_NUM_GROUPS; j++ )
{
CInterClassStats *pInter = &pStats->m_InterClassStats[j];
pFileSys->FPrintf( hFile, "%s", GetGroupNameFor( i ) );
pFileSys->FPrintf( hFile, "\t%s", GetGroupNameFor( j ) );
pFileSys->FPrintf( hFile, "\t%d", (int)pInter->m_flTotalDamageInflicted );
pFileSys->FPrintf( hFile, "\t%d", pInter->m_nKills );
pFileSys->FPrintf( hFile, "\t%d", (int)(pInter->m_nNormalizedEngagements ? (pInter->m_flTotalNormalizedEngagementDist / pInter->m_nNormalizedEngagements) : 0) );
pFileSys->FPrintf( hFile, "\t%d", (int)(pInter->m_nEngagements ? (pInter->m_flTotalEngagementDist / pInter->m_nEngagements) : 0) );
pFileSys->FPrintf( hFile, "\n" );
}
}
pFileSys->Close( hFile );
}
CTextStatFile g_PlayerClassStatsOutput( PrintPlayerClassStats );
// ---------------------------------------------------------------------------------------------------------------- //
// Detailed stats output.
// ---------------------------------------------------------------------------------------------------------------- //
ConVar tf_DetailedStats( "tf_DetailedStats", "1", 0, "Prints extensive detailed gameplay stats into detailed_stats.txt" );
class CShotInfo
{
public:
float m_flDistance;
int m_nDamage;
};
CUtlLinkedList<CShotInfo,int> g_ClassShotInfos[STATS_NUM_GROUPS];
void PrintDetailedPlayerClassStats()
{
if ( !tf_DetailedStats.GetInt() )
return;
IFileSystem *pFileSys = filesystem;
FileHandle_t hFile = pFileSys->Open( "class_stats_detailed.txt", "wt", "LOGDIR" );
if ( hFile != FILESYSTEM_INVALID_HANDLE )
{
// Print the header.
for ( int i=TFCLASS_UNDECIDED+1; i < STATS_NUM_GROUPS; i++ )
{
pFileSys->FPrintf( hFile, "%s dist\t%s dmg\t", GetGroupNameFor( i ), GetGroupNameFor( i ) );
}
pFileSys->FPrintf( hFile, "\n" );
// Write out each column.
int iterators[STATS_NUM_GROUPS];
for ( i=TFCLASS_UNDECIDED+1; i < STATS_NUM_GROUPS; i++ )
iterators[i] = g_ClassShotInfos[i].Head();
bool bWroteAnything;
do
{
bWroteAnything = false;
for ( int i=TFCLASS_UNDECIDED+1; i < STATS_NUM_GROUPS; i++ )
{
if ( iterators[i] == g_ClassShotInfos[i].InvalidIndex() )
{
pFileSys->FPrintf( hFile, "\t\t" );
}
else
{
CShotInfo *pInfo = &g_ClassShotInfos[i][iterators[i]];
iterators[i] = g_ClassShotInfos[i].Next( iterators[i] );
pFileSys->FPrintf( hFile, "%.2f\t%d\t", pInfo->m_flDistance, pInfo->m_nDamage );
bWroteAnything = true;
}
}
pFileSys->FPrintf( hFile, "\n" );
} while ( bWroteAnything );
pFileSys->Close( hFile );
}
}
CTextStatFile g_PlayerClassStatsDetailedOutput( PrintDetailedPlayerClassStats );
//=====================================================================
// PLAYER CLASS HANDLING
//=====================================================================
// Base PlayerClass
CPlayerClass::CPlayerClass( CBaseTFPlayer *pPlayer, TFClass iClass )
: m_TFClass( iClass )
{
m_pPlayer = pPlayer;
for (int i = 0; i <= MAX_TF_TEAMS; ++i)
{
m_sClassModel[i] = NULL_STRING;
}
m_iNumWeaponTechAssociations = 0;
m_bTechAssociationsSet = false;
m_flNormalizedEngagementNextTime = -1;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CPlayerClass::~CPlayerClass()
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClass::ClassActivate( void )
{
// Setup the default player movement variables.
SetupMoveData();
AddWeaponTechAssociations();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClass::ClassDeactivate( void )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClass::AddWeaponTechAssociations( void )
{
ClearAllWeaponTechAssoc();
// Iterate tech tree for this class and find
Assert( m_pPlayer );
CTechnologyTree *tree = m_pPlayer->GetTechTree();
Assert( tree );
// Loop through all of the techs to see if any of them say yes to being applicable to
// this class specifically
for ( int i = 0 ; i < tree->GetNumberTechnologies(); i++ )
{
CBaseTechnology *tech = tree->GetTechnology( i );
if ( !tech )
continue;
if ( !tech->GetAssociateWeaponsForClass( GetTFClass() ) )
continue;
// Associate weapon tech name with class
AddWeaponTechAssoc( (char *)tech->GetName() );
}
}
void CPlayerClass::NetworkStateChanged()
{
if ( m_pPlayer )
m_pPlayer->NetworkStateChanged();
}
//-----------------------------------------------------------------------------
// Purpose: Setup the default movement parameters, quite a few of these will be
// overridden with class specific values.
//-----------------------------------------------------------------------------
void CPlayerClass::SetupMoveData( void )
{
// Set the default walking and sprinting speeds.
m_flMaxWalkingSpeed = 120;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CPlayerClass::SetupSizeData( void )
{
// Initially set the player to the base player class standing hull size.
m_pPlayer->SetCollisionBounds( PLAYERCLASS_HULL_STAND_MIN, PLAYERCLASS_HULL_STAND_MAX );
m_pPlayer->SetViewOffset( PLAYERCLASS_VIEWOFFSET_STAND );
m_pPlayer->m_Local.m_flStepSize = PLAYERCLASS_STEPSIZE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -