#include #include #include #include #include #include #include #include CDetailedStats::CDetailedStats() { m_Mode = 0; m_StatsClientID = -1; m_ScreenshotTaken = false; m_ScreenshotTime = -1; } void CDetailedStats::OnReset() { for(int i=0; im_aStats[i].Reset(); m_Mode = 0; m_StatsClientID = -1; m_ScreenshotTaken = false; m_ScreenshotTime = -1; } void CDetailedStats::ConKeyStats(IConsole::IResult *pResult, void *pUserData) { if(pResult->GetInteger(0) != 0) ((CDetailedStats *)pUserData)->m_Mode = pResult->GetInteger(1); else ((CDetailedStats *)pUserData)->m_Mode = 0; } void CDetailedStats::ConKeyNext(IConsole::IResult *pResult, void *pUserData) { CDetailedStats *pStats = (CDetailedStats *)pUserData; if(pStats->m_Mode != 2) return; if(pResult->GetInteger(0) == 0) { pStats->m_StatsClientID++; pStats->m_StatsClientID %= MAX_CLIENTS; pStats->CheckStatsClientID(); } } void CDetailedStats::OnConsoleInit() { Console()->Register("+stats", "i", CFGFLAG_CLIENT, ConKeyStats, this, "Show stats"); Console()->Register("+next_stats", "", CFGFLAG_CLIENT, ConKeyNext, this, "Next player Stats"); } bool CDetailedStats::IsActive() { return (m_Mode > 0); } void CDetailedStats::CheckStatsClientID() { if(m_StatsClientID == -1) m_StatsClientID = m_pClient->m_Snap.m_LocalClientID; int Prev = m_StatsClientID; while(!m_pClient->m_aStats[m_StatsClientID].m_Active) { m_StatsClientID++; m_StatsClientID %= MAX_CLIENTS; if(m_StatsClientID == Prev) { m_StatsClientID = -1; m_Mode = 0; break; } } } void CDetailedStats::OnMessage(int MsgType, void *pRawMsg) { if(MsgType == NETMSGTYPE_SV_KILLMSG) { CNetMsg_Sv_KillMsg *pMsg = (CNetMsg_Sv_KillMsg *)pRawMsg; CGameClient::CClientStats *pStats = m_pClient->m_aStats; pStats[pMsg->m_Victim].m_Deaths++; pStats[pMsg->m_Victim].m_CurrentSpree = 0; if(pMsg->m_Weapon >= 0) pStats[pMsg->m_Victim].m_aDeathsFrom[pMsg->m_Weapon]++; if(pMsg->m_ModeSpecial & 1) pStats[pMsg->m_Victim].m_DeathsCarrying++; if(pMsg->m_Victim != pMsg->m_Killer) { pStats[pMsg->m_Killer].m_Frags++; pStats[pMsg->m_Killer].m_CurrentSpree++; if(pStats[pMsg->m_Killer].m_CurrentSpree > pStats[pMsg->m_Killer].m_BestSpree) pStats[pMsg->m_Killer].m_BestSpree = pStats[pMsg->m_Killer].m_CurrentSpree; if(pMsg->m_Weapon >= 0) pStats[pMsg->m_Killer].m_aFragsWith[pMsg->m_Weapon]++; if(pMsg->m_ModeSpecial & 1) pStats[pMsg->m_Killer].m_CarriersKilled++; if(pMsg->m_ModeSpecial & 2) pStats[pMsg->m_Killer].m_KillsCarrying++; } else pStats[pMsg->m_Victim].m_Suicides++; } else if(MsgType == NETMSGTYPE_SV_CHAT) { CNetMsg_Sv_Chat *pMsg = (CNetMsg_Sv_Chat *)pRawMsg; if(pMsg->m_ClientID < 0) { const char *p; const char *pLookFor = "flag was captured by '"; if(str_find(pMsg->m_pMessage, pLookFor) != 0) { // server info CServerInfo CurrentServerInfo; Client()->GetServerInfo(&CurrentServerInfo); p = str_find(pMsg->m_pMessage, pLookFor); char aName[64]; p += str_length(pLookFor); str_copy(aName, p, sizeof(aName)); // remove capture time if(str_comp(aName+str_length(aName)-9, " seconds)") == 0) { char *c = aName+str_length(aName)-10; while(c > aName) { c--; if(*c == '(') { *(c-1) = 0; break; } } } // remove the ' at the end aName[str_length(aName)-1] = 0; for(int i = 0; i < MAX_CLIENTS; i++) { if(!m_pClient->m_aStats[i].m_Active) continue; if(str_comp(m_pClient->m_aClients[i].m_aName, aName) == 0) { m_pClient->m_aStats[i].m_FlagCaptures++; break; } } } } } } void CDetailedStats::OnRender() { // auto stat screenshot stuff if(g_Config.m_ClDsStatScreenshot) { if(m_ScreenshotTime < 0 && m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER) m_ScreenshotTime = time_get()+time_freq()*3; if(m_ScreenshotTime > -1 && m_ScreenshotTime < time_get()) m_Mode = 1; if(!m_ScreenshotTaken && m_ScreenshotTime > -1 && m_ScreenshotTime+time_freq()/5 < time_get()) { AutoStatScreenshot(); m_ScreenshotTaken = true; } } switch(m_Mode) { case 1: RenderGlobalStats(); break; case 2: RenderIndividualStats(); break; } } void CDetailedStats::RenderGlobalStats() { if(m_Mode != 1) return; float Width = 400*3.0f*Graphics()->ScreenAspect(); float Height = 400*3.0f; float w = 250.0f; float h = 750.0f; const CNetObj_PlayerInfo *apPlayers[MAX_CLIENTS] = {0}; int NumPlayers = 0; int i = 0; // sort red or dm players by score for(i = 0; i < MAX_CLIENTS; i++) { const CNetObj_PlayerInfo *pInfo = m_pClient->m_Snap.m_paInfoByScore[i]; if(!pInfo || !m_pClient->m_aStats[pInfo->m_ClientID].m_Active || m_pClient->m_aClients[pInfo->m_ClientID].m_Team != TEAM_RED) continue; apPlayers[NumPlayers] = pInfo; NumPlayers++; } // sort blue players by score after if(m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS) { for(i = 0; i < MAX_CLIENTS; i++) { const CNetObj_PlayerInfo *pInfo = m_pClient->m_Snap.m_paInfoByScore[i]; if(!pInfo || !m_pClient->m_aStats[pInfo->m_ClientID].m_Active || m_pClient->m_aClients[pInfo->m_ClientID].m_Team != TEAM_BLUE) continue; apPlayers[NumPlayers] = pInfo; NumPlayers++; } } for(i = 0; i < 9; i++) if(g_Config.m_ClDsStatboardInfos & (1<m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_FLAGS && g_Config.m_ClDsStatboardInfos&DETAILED_STATS_FLAGCAPTURES) w += 100; bool aDisplayWeapon[NUM_WEAPONS] = {false}; if(g_Config.m_ClDsStatboardInfos & DETAILED_STATS_WEAPS) { w += 10; for(i=0; im_aStats[apPlayers[i]->m_ClientID]; for(int j=0; jMapScreen(0, 0, Width, Height); Graphics()->BlendNormal(); Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(0,0,0,0.5f); RenderTools()->DrawRoundRect(x-10.f, y-10.f, w, h, 17.0f); Graphics()->QuadsEnd(); float tw; int px = 325; TextRender()->Text(0, x+10, y-5, 24.0f, Localize("Name"), -1); const char *apHeaders[] = { Localize("Frags"), Localize("Deaths"), Localize("Suicides"), Localize("Ratio"), Localize("Net"), Localize("FPM"), Localize("Spree"), Localize("Best spree"), Localize("Grabs") }; for(i = 0; i < 9; i++) if(g_Config.m_ClDsStatboardInfos & (1<TextWidth(0, 24.0f, apHeaders[i], -1); TextRender()->Text(0, x+px-tw, y-5, 24.0f, apHeaders[i], -1); px += 100; } if(g_Config.m_ClDsStatboardInfos & DETAILED_STATS_WEAPS) { Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); for(i = 0, px-=40; i < NUM_WEAPONS; i++) { if(!aDisplayWeapon[i]) continue; RenderTools()->SelectSprite(g_pData->m_Weapons.m_aId[i].m_pSpriteBody); if(i == 0) RenderTools()->DrawSprite(x+px, y+10, g_pData->m_Weapons.m_aId[i].m_VisualSize*0.8); else RenderTools()->DrawSprite(x+px, y+10, g_pData->m_Weapons.m_aId[i].m_VisualSize); px += 80; } Graphics()->QuadsEnd(); px += 40; } if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_FLAGS && g_Config.m_ClDsStatboardInfos&DETAILED_STATS_FLAGCAPTURES) { px -= 40; Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); Graphics()->QuadsSetRotation(0.78f); RenderTools()->SelectSprite(SPRITE_FLAG_RED); RenderTools()->DrawSprite(x+px, y+15, 48); Graphics()->QuadsEnd(); } y += 29.0f; float FontSize = 30.0f; float LineHeight = 50.0f; float TeeSizemod = 1.0f; float TeeOffset = 0.0f; if(NumPlayers > 14) { FontSize = 30.0f; LineHeight = 40.0f; TeeSizemod = 0.8f; TeeOffset = -5.0f; } for(int j = 0; j < NumPlayers; j++) { const CNetObj_PlayerInfo *pInfo = apPlayers[j]; const CGameClient::CClientStats Stats = m_pClient->m_aStats[pInfo->m_ClientID]; if(m_pClient->m_Snap.m_LocalClientID == pInfo->m_ClientID || (m_pClient->m_Snap.m_SpecInfo.m_Active && pInfo->m_ClientID == m_pClient->m_Snap.m_SpecInfo.m_SpectatorID)) { // background so it's easy to find the local player Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(1,1,1,0.25f); RenderTools()->DrawRoundRect(x, y, w-20, LineHeight*0.95f, 17.0f); Graphics()->QuadsEnd(); } CTeeRenderInfo Teeinfo = m_pClient->m_aClients[pInfo->m_ClientID].m_RenderInfo; Teeinfo.m_Size *= TeeSizemod; RenderTools()->RenderTee(CAnimState::GetIdle(), &Teeinfo, EMOTE_NORMAL, vec2(1,0), vec2(x+28, y+28+TeeOffset)); char aBuf[128]; if(g_Config.m_ClDsStatId) { str_format(aBuf, sizeof(aBuf), "%d", pInfo->m_ClientID); TextRender()->Text(0, x, y, FontSize, aBuf, -1); } CTextCursor Cursor; tw = TextRender()->TextWidth(0, FontSize, m_pClient->m_aClients[pInfo->m_ClientID].m_aName, -1); TextRender()->SetCursor(&Cursor, x+64, y, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); Cursor.m_LineWidth = 220; TextRender()->TextEx(&Cursor, m_pClient->m_aClients[pInfo->m_ClientID].m_aName, -1); //TextRender()->Text(0, x+64, y, FontSize, m_pClient->m_aClients[pInfo->m_ClientID].m_aName, -1); px = 325; if(g_Config.m_ClDsStatboardInfos & DETAILED_STATS_FRAGS) { str_format(aBuf, sizeof(aBuf), "%d", Stats.m_Frags); tw = TextRender()->TextWidth(0, FontSize, aBuf, -1); TextRender()->Text(0, x-tw+px, y, FontSize, aBuf, -1); px += 100; } if(g_Config.m_ClDsStatboardInfos & DETAILED_STATS_DEATHS) { str_format(aBuf, sizeof(aBuf), "%d", Stats.m_Deaths); tw = TextRender()->TextWidth(0, FontSize, aBuf, -1); TextRender()->Text(0, x-tw+px, y, FontSize, aBuf, -1); px += 100; } if(g_Config.m_ClDsStatboardInfos & DETAILED_STATS_SUICIDES) { str_format(aBuf, sizeof(aBuf), "%d", Stats.m_Suicides); tw = TextRender()->TextWidth(0, FontSize, aBuf, -1); TextRender()->Text(0, x-tw+px, y, FontSize, aBuf, -1); px += 100; } if(g_Config.m_ClDsStatboardInfos & DETAILED_STATS_RATIO) { if(Stats.m_Deaths == 0) str_format(aBuf, sizeof(aBuf), "--"); else str_format(aBuf, sizeof(aBuf), "%.2f", (float)(Stats.m_Frags)/Stats.m_Deaths); tw = TextRender()->TextWidth(0, FontSize, aBuf, -1); TextRender()->Text(0, x-tw+px, y, FontSize, aBuf, -1); px += 100; } if(g_Config.m_ClDsStatboardInfos & DETAILED_STATS_NET) { str_format(aBuf, sizeof(aBuf), "%+d", Stats.m_Frags-Stats.m_Deaths); tw = TextRender()->TextWidth(0, FontSize, aBuf, -1); TextRender()->Text(0, x-tw+px, y, FontSize, aBuf, -1); px += 100; } if(g_Config.m_ClDsStatboardInfos & DETAILED_STATS_FPM) { float Fpm = (float)(Stats.m_Frags*60)/((float)(Client()->GameTick()-Stats.m_JoinDate)/Client()->GameTickSpeed()); str_format(aBuf, sizeof(aBuf), "%.1f", Fpm); tw = TextRender()->TextWidth(0, FontSize, aBuf, -1); TextRender()->Text(0, x-tw+px, y, FontSize, aBuf, -1); px += 100; } if(g_Config.m_ClDsStatboardInfos & DETAILED_STATS_SPREE) { str_format(aBuf, sizeof(aBuf), "%d", Stats.m_CurrentSpree); tw = TextRender()->TextWidth(0, FontSize, aBuf, -1); TextRender()->Text(0, x-tw+px, y, FontSize, aBuf, -1); px += 100; } if(g_Config.m_ClDsStatboardInfos & DETAILED_STATS_BESTSPREE) { px += 40; str_format(aBuf, sizeof(aBuf), "%d", Stats.m_BestSpree); tw = TextRender()->TextWidth(0, FontSize, aBuf, -1); TextRender()->Text(0, x-tw+px, y, FontSize, aBuf, -1); px += 100; } if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_FLAGS && g_Config.m_ClDsStatboardInfos&DETAILED_STATS_FLAGGRABS) { str_format(aBuf, sizeof(aBuf), "%d", Stats.m_FlagGrabs); tw = TextRender()->TextWidth(0, FontSize, aBuf, -1); TextRender()->Text(0, x-tw+px, y, FontSize, aBuf, -1); px += 100; } for(i = 0, px=px-40; i < NUM_WEAPONS; i++) { if(!aDisplayWeapon[i]) continue; str_format(aBuf, sizeof(aBuf), "%d/%d", Stats.m_aFragsWith[i], Stats.m_aDeathsFrom[i]); tw = TextRender()->TextWidth(0, FontSize, aBuf, -1); TextRender()->Text(0, x+px-tw/2, y, FontSize, aBuf, -1); px += 80; } if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_FLAGS && g_Config.m_ClDsStatboardInfos&DETAILED_STATS_FLAGCAPTURES) { str_format(aBuf, sizeof(aBuf), "%d", Stats.m_FlagCaptures); tw = TextRender()->TextWidth(0, FontSize, aBuf, -1); TextRender()->Text(0, x-tw+px, y, FontSize, aBuf, -1); px += 100; } y += LineHeight; } } void CDetailedStats::RenderIndividualStats() { if(m_Mode != 2) return; CheckStatsClientID(); if(m_Mode != 2) return; int m_ClientID = m_StatsClientID; float Width = 400*3.0f*Graphics()->ScreenAspect(); float Height = 400*3.0f; float w = 1200.0f; float x = Width/2-w/2; float y = 100.0f; float xo = 200.0f; float FontSize = 30.0f; float LineHeight = 40.0f; const CGameClient::CClientStats m_aStats = m_pClient->m_aStats[m_ClientID]; Graphics()->MapScreen(0, 0, Width, Height); // header with name and score Graphics()->BlendNormal(); Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(0,0,0,0.5f); RenderTools()->DrawRoundRect(x-10.f, y-10.f, w, 120.0f, 17.0f); Graphics()->QuadsEnd(); CTeeRenderInfo Teeinfo = m_pClient->m_aClients[m_ClientID].m_RenderInfo; Teeinfo.m_Size *= 1.5f; RenderTools()->RenderTee(CAnimState::GetIdle(), &Teeinfo, EMOTE_NORMAL, vec2(1,0), vec2(x+xo+32, y+36)); TextRender()->Text(0, x+xo+128, y, 48.0f, m_pClient->m_aClients[m_ClientID].m_aName, -1); char aBuf[64]; if(g_Config.m_ClDsStatId) { str_format(aBuf, sizeof(aBuf), "%d", m_ClientID); TextRender()->Text(0, x+xo, y, FontSize, aBuf, -1); } str_format(aBuf, sizeof(aBuf), "%s: %d", Localize("Score"), m_pClient->m_Snap.m_paPlayerInfos[m_ClientID]->m_Score); TextRender()->Text(0, x+xo, y+64, FontSize, aBuf, -1); int Seconds = (float)(Client()->GameTick()-m_aStats.m_JoinDate)/Client()->GameTickSpeed(); str_format(aBuf, sizeof(aBuf), "%s: %02d:%02d", Localize("Time played"), Seconds/60, Seconds%60); TextRender()->Text(0, x+xo+256, y+64, FontSize, aBuf, -1); y += 150.0f; // Frags, etc. stats Graphics()->BlendNormal(); Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(0,0,0,0.5f); RenderTools()->DrawRoundRect(x-10.f, y-10.f, w, 100.0f, 17.0f); Graphics()->QuadsEnd(); Graphics()->TextureSet(g_pData->m_aImages[IMAGE_EMOTICONS].m_Id); Graphics()->QuadsBegin(); Graphics()->SetColor(1.0f,1.0f,1.0f,1.0f); RenderTools()->SelectSprite(SPRITE_EYES); IGraphics::CQuadItem QuadItem(x+xo/2, y+40, 128, 128); Graphics()->QuadsDraw(&QuadItem, 1); Graphics()->QuadsEnd(); str_format(aBuf, sizeof(aBuf), "%s: %d", Localize("Frags"), m_aStats.m_Frags); TextRender()->Text(0, x+xo, y, FontSize, aBuf, -1); str_format(aBuf, sizeof(aBuf), "%s: %d", Localize("Deaths"), m_aStats.m_Deaths); TextRender()->Text(0, x+xo+200.0f, y, FontSize, aBuf, -1); str_format(aBuf, sizeof(aBuf), "%s: %d", Localize("Suicides"), m_aStats.m_Suicides); TextRender()->Text(0, x+xo+400.0f, y, FontSize, aBuf, -1); str_format(aBuf, sizeof(aBuf), "%s: %d", Localize("Spree"), m_aStats.m_CurrentSpree); TextRender()->Text(0, x+xo+600.0f, y, FontSize, aBuf, -1); y += LineHeight; if(m_aStats.m_Deaths == 0) str_format(aBuf, sizeof(aBuf), "%s: --", Localize("Ratio")); else str_format(aBuf, sizeof(aBuf), "%s: %.2f", Localize("Ratio"), (float)(m_aStats.m_Frags)/m_aStats.m_Deaths); TextRender()->Text(0, x+xo, y, FontSize, aBuf, -1); str_format(aBuf, sizeof(aBuf), "%s: %d", Localize("Net"), m_aStats.m_Frags-m_aStats.m_Deaths); TextRender()->Text(0, x+xo+200.0f, y, FontSize, aBuf, -1); float Fpm = (float)(m_aStats.m_Frags*60)/((float)(Client()->GameTick()-m_aStats.m_JoinDate)/Client()->GameTickSpeed()); str_format(aBuf, sizeof(aBuf), "%s: %.1f", Localize("FPM"), Fpm); TextRender()->Text(0, x+xo+400.0f, y, FontSize, aBuf, -1); str_format(aBuf, sizeof(aBuf), "%s: %d", Localize("Best spree"), m_aStats.m_BestSpree); TextRender()->Text(0, x+xo+600.0f, y, FontSize, aBuf, -1); y+= LineHeight + 30.0f; // Weapon stats bool aDisplayWeapon[NUM_WEAPONS] = {false}; int NumWeaps = 0; for(int i=0; iBlendNormal(); Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(0,0,0,0.5f); RenderTools()->DrawRoundRect(x-10.f, y-10.f, w, LineHeight*(1+NumWeaps)+20.0f, 17.0f); Graphics()->QuadsEnd(); TextRender()->Text(0, x+xo, y, FontSize, Localize("Frags"), -1); TextRender()->Text(0, x+xo+200.0f, y, FontSize, Localize("Deaths"), -1); y += LineHeight; for(int i=0; iTextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); RenderTools()->SelectSprite(g_pData->m_Weapons.m_aId[i].m_pSpriteBody, 0); RenderTools()->DrawSprite(x+xo/2, y+24, g_pData->m_Weapons.m_aId[i].m_VisualSize); Graphics()->QuadsEnd(); str_format(aBuf, sizeof(aBuf), "%d", m_aStats.m_aFragsWith[i]); TextRender()->Text(0, x+xo, y, FontSize, aBuf, -1); str_format(aBuf, sizeof(aBuf), "%d", m_aStats.m_aDeathsFrom[i]); TextRender()->Text(0, x+xo+200.0f, y, FontSize, aBuf, -1); y += LineHeight; } y += 30.0f; } // Flag stats if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_FLAGS) { Graphics()->BlendNormal(); Graphics()->TextureSet(-1); Graphics()->QuadsBegin(); Graphics()->SetColor(0,0,0,0.5f); RenderTools()->DrawRoundRect(x-10.f, y-10.f, w, LineHeight*5+20.0f, 17.0f); Graphics()->QuadsEnd(); Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); RenderTools()->SelectSprite(SPRITE_FLAG_RED); RenderTools()->DrawSprite(x+xo/2, y+100.0f, 192); Graphics()->QuadsEnd(); str_format(aBuf, sizeof(aBuf), "%s: %d", Localize("Grabs"), m_aStats.m_FlagGrabs); TextRender()->Text(0, x+xo, y, FontSize, aBuf, -1); y += LineHeight; str_format(aBuf, sizeof(aBuf), "%s: %d", Localize("Captures"), m_aStats.m_FlagCaptures); TextRender()->Text(0, x+xo, y, FontSize, aBuf, -1); y += LineHeight; str_format(aBuf, sizeof(aBuf), "%s: %d", Localize("Kills holding flag"), m_aStats.m_KillsCarrying); TextRender()->Text(0, x+xo, y, FontSize, aBuf, -1); y += LineHeight; str_format(aBuf, sizeof(aBuf), "%s: %d", Localize("Deaths with flag"), m_aStats.m_DeathsCarrying); TextRender()->Text(0, x+xo, y, FontSize, aBuf, -1); y += LineHeight; str_format(aBuf, sizeof(aBuf), "%s: %d", Localize("Carriers killed"), m_aStats.m_CarriersKilled); TextRender()->Text(0, x+xo, y, FontSize, aBuf, -1); y += LineHeight; } } void CDetailedStats::AutoStatScreenshot() { if(Client()->State() != IClient::STATE_DEMOPLAYBACK) Client()->AutoStatScreenshot_Start(); }