CSGO: Custom Event Logging
So, since event logs have become a topic of discussion recently, and more specifically, how to draw them without prefixes or setting convars, I decided to write a small logging function separate from the game’s developer print filtering so you can send any messages to it, prefixed or not.
I’ve heavily commented each code snippet in hopes that people will learn from this, and not blindly paste it, but I’ve accepted that the blind pasting is an inevitable part of releasing anything publicly.
Let’s get to it.
Azsry DEFINES UNKNOWNCHEATSAzsry DEFINES UNKNOWNCHEATSAzsry DEFINES UNKNOWNCHEATSAzsry DEFINES UNKNOWNCHEATSAzsry DEFINES UNKNOWNCHEATSAzsry DEFINES UNKNOWNCHEATSAzsry DEFINES UNKNOWNCHEATSAzsry DEFINES UNKNOWNCHEATSAzsry DEFINES UNKNOWNCHEATSAzsry DEFINES UNKNOWNCHEATSAzsry DEFINES UNKNOWNCHEATS
Custom Event Logging
So, since event logs have become a topic of discussion recently, and more specifically, how to draw them without prefixes or setting convars, I decided to write a small logging function separate from the game’s developer print filtering so you can send any messages to it, prefixed or not.
I’ve heavily commented each code snippet in hopes that people will learn from this, and not blindly paste it, but I’ve accepted that the blind pasting is an inevitable part of releasing anything publicly.
Let’s get to it.
Definitions:
#define EVENTLOG_DURATION 3.f
struct EventLog_t {
// Apparently structs don't like being defined without default constructors
EventLog_t( ) : m_printed( false ), m_draw_time( 0.0f ), m_text( nullptr ), m_text_colour( Color( 255, 255, 255 ) ), m_font( D::ESP ) { }
// Generic constructor for white text
EventLog_t( const char* text ) : m_printed( false ), m_draw_time( 0.0f ), m_text( text ), m_text_colour( Color( 255, 255, 255 ) ), m_font( D::ESP ) { }
// Specific constructor for font-swapped text
EventLog_t( const char* text, HFont font ) : m_printed( false ), m_draw_time( 0.0f ), m_text( text ), m_text_colour( Color( 255, 255, 255 ) ), m_font( font ) { }
// Specific constructor for coloured text
EventLog_t( const char* text, Color col ) : m_printed( false ), m_draw_time( 0.0f ), m_text( text ), m_text_colour( col ), m_font( D::ESP ) { }
// Specific constructor for coloured, font-swapped text
EventLog_t( const char* text, Color col, HFont font ) : m_printed( false ), m_draw_time( 0.0f ), m_text( text ), m_text_colour( col ), m_font( font ) { }
bool m_printed;
float m_draw_time,
m_death_time;
std::string m_text;
Color m_text_colour;
HFont m_font;
};
class CESP {
public:
...
// There's no 'null' colour, so we'll just use a black with 0 alpha as the default
// Use -1 as a default font because it's never used
void AddToLog( const char* text, Color col = Color( 0, 0, 0, 0 ), HFont font = -1 );
...
private:
// Used to store the current Y offset for the next line to be drawn
float flLogOffset = 0.f;
// Container for event messages.
std::vector m_event_log;
...
};
void CESP::AddToLog( const char * text, Color col, HFont font ) {
// No colour specified, just use white
if ( col == Color( 0, 0, 0, 0 ) ) {
// No font specified, use default font
if ( font == -1 )
m_event_log.push_back( EventLog_t( text ) );
else
m_event_log.push_back( EventLog_t( text, font ) );
}
// Colour specified
else {
// No font specified, use default font
if ( font == -1 )
m_event_log.push_back( EventLog_t( text, col ) );
else
m_event_log.push_back( EventLog_t( text, col, font ) );
}
}
At the top of where you draw your ESP:
if ( !Vars.Visuals.Active )
return;
if ( !g_Engine->IsInGame( ) || !g_Engine->IsConnected( ) )
return;
flLogOffset = 0.f;
DrawLog( );
...
The meat of the log:
void CESP::DrawLog( ) {
// Init variables once so we don't spam inits later :))
int w = 0, h = 0;
// Iterate through each active message in the event log.
// NOTE: We do not have an increment condition here
// because we might need to remove the message later
// on. Using vector::erase will invalidate all existing
// iterator references, so we need to deal with it accordingly
for ( auto it = m_event_log.begin( ); it != m_event_log.end( ); ) {
// Init each message's draw and death times
if ( !it->m_printed ) {
// Set the time this message was drawn to (((now))) (stops it from drawing after we start a new match)
it->m_draw_time = g_Globals->curtime;
// Set the time this message should be removed from the log
it->m_death_time = it->m_draw_time + EVENTLOG_DURATION;
// Set the printed bool to true so we don't constantly init the message
it->m_printed = true;
}
// Get the size of our text. We only need height for this, but I didn't want to write a function just to extract the height component
// if we'd need to call it anyway
g_Surface->GetTextSize( it->m_font, U::StringToWstring( it->m_text.c_str( ) ), w, h );
// If we've drawn it for long enough, or for some reason curtime is behind when it should be drawn, remove it from the queue.
if ( g_Globals->curtime >= it->m_death_time || g_Globals->curtime < it->m_draw_time ) {
// Decrement the text offset so all our messages shift up one line. 0.8 is a magic number for my D::ESP because += h left a bit of space in between each message
flLogOffset -= h * 0.8f;
// Fix the iterator after ::erase invalidates the existing one
it = m_event_log.erase( it );
}
// Nothing to draw. Just leave the function.
if ( m_event_log.empty( ) )
return;
// Get the percentage of completion for this message
auto duration_percent = ( it->m_death_time - g_Globals->curtime ) / EVENTLOG_DURATION;
// Draw the message to our log with the proper alpha fading
D::Text( Vector2D( 10, 10 + flLogOffset ), it->m_text.c_str( ), it->m_font, Color( it->m_text_colour.r( ), it->m_text_colour.g( ), it->m_text_colour.b( ), ( int ) ( 255 * duration_percent ) ) );
// Allow subsequent calls to be drawn below the current one.
flLogOffset += h * 0.8f;
// We successfully printed this one. Increment the iterator and continue to the next message
++it;
}
}
Usage (sending hits to log, with headshots showing in a different colour and font):
auto name = event->GetName( );
if ( !strcmp( name, "player_hurt" ) ) {
int attacker = event->GetInt( "attacker" );
int target = event->GetInt( "userid" );
int hitgroup = event->GetInt( "hitgroup" );
int health = event->GetInt( "health" );
if ( g_Engine->GetPlayerForUserID( attacker ) == g_Engine->GetLocalPlayer( ) ) {
auto uid = g_Engine->GetPlayerForUserID( target );
auto _target = g_EntList->GetClientEntity( uid );
player_info_t target_info;
g_Engine->GetPlayerInfo( _target->GetIndex( ), &target_info );
char buffer[ 512 ];
sprintf_s( buffer, "Hit %s: %i health remaining.", target_info.name, health );
if ( health == 0 )
g_ESP->AddToLog( buffer, Color::Red( 4 ), D::Bold );
else
g_ESP->AddToLog( buffer );
}
}
Using this method, you gain more flexibility than using the game’s console filtering. For example, you can print in different colours for different severity levels, or even in different fonts if you wanted to. The function will automatically align subsequent lines to be below the current one, regardless of font.
|