2015-05-19 22:51:02 +00:00
|
|
|
#include <engine/shared/config.h>
|
|
|
|
#include <engine/textrender.h>
|
|
|
|
#include <engine/graphics.h>
|
|
|
|
#include <engine/serverbrowser.h>
|
2017-04-26 03:10:31 +00:00
|
|
|
#include <engine/storage.h>
|
2015-05-19 22:51:02 +00:00
|
|
|
#include <game/generated/client_data.h>
|
|
|
|
#include <game/client/gameclient.h>
|
|
|
|
#include <game/client/animstate.h>
|
2015-05-20 16:22:04 +00:00
|
|
|
#include <game/client/components/motd.h>
|
2015-05-21 09:41:59 +00:00
|
|
|
#include <game/client/components/statboard.h>
|
2015-05-19 22:51:02 +00:00
|
|
|
|
2015-05-21 09:41:59 +00:00
|
|
|
CStatboard::CStatboard()
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2015-05-20 16:22:04 +00:00
|
|
|
m_Active = false;
|
2015-05-19 22:51:02 +00:00
|
|
|
m_ScreenshotTaken = false;
|
|
|
|
m_ScreenshotTime = -1;
|
2017-05-08 19:55:03 +00:00
|
|
|
m_pCSVstr = 0;
|
2015-05-19 22:51:02 +00:00
|
|
|
}
|
|
|
|
|
2015-05-21 09:41:59 +00:00
|
|
|
void CStatboard::OnReset()
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
|
|
|
for(int i=0; i<MAX_CLIENTS; i++)
|
|
|
|
m_pClient->m_aStats[i].Reset();
|
2015-05-20 16:22:04 +00:00
|
|
|
m_Active = false;
|
2015-05-19 22:51:02 +00:00
|
|
|
m_ScreenshotTaken = false;
|
|
|
|
m_ScreenshotTime = -1;
|
|
|
|
}
|
|
|
|
|
2015-07-09 18:38:23 +00:00
|
|
|
void CStatboard::OnRelease()
|
|
|
|
{
|
|
|
|
m_Active = false;
|
|
|
|
}
|
|
|
|
|
2015-05-21 09:41:59 +00:00
|
|
|
void CStatboard::ConKeyStats(IConsole::IResult *pResult, void *pUserData)
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2015-05-21 09:41:59 +00:00
|
|
|
((CStatboard *)pUserData)->m_Active = pResult->GetInteger(0) != 0;
|
2015-05-19 22:51:02 +00:00
|
|
|
}
|
|
|
|
|
2015-05-21 09:41:59 +00:00
|
|
|
void CStatboard::OnConsoleInit()
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2015-05-20 20:23:58 +00:00
|
|
|
Console()->Register("+statboard", "", CFGFLAG_CLIENT, ConKeyStats, this, "Show stats");
|
2015-05-19 22:51:02 +00:00
|
|
|
}
|
|
|
|
|
2015-05-21 09:41:59 +00:00
|
|
|
bool CStatboard::IsActive()
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2015-05-20 16:22:04 +00:00
|
|
|
return m_Active;
|
2015-05-19 22:51:02 +00:00
|
|
|
}
|
|
|
|
|
2015-05-21 09:41:59 +00:00
|
|
|
void CStatboard::OnMessage(int MsgType, void *pRawMsg)
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
|
|
|
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_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]++;
|
|
|
|
}
|
|
|
|
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)
|
|
|
|
{
|
2019-04-16 00:24:24 +00:00
|
|
|
const char *p, *t;
|
2015-05-19 22:51:02 +00:00
|
|
|
const char *pLookFor = "flag was captured by '";
|
2019-04-15 23:47:12 +00:00
|
|
|
if((p = str_find(pMsg->m_pMessage, pLookFor)))
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2019-04-15 23:47:12 +00:00
|
|
|
char aName[MAX_NAME_LENGTH];
|
2015-05-19 22:51:02 +00:00
|
|
|
p += str_length(pLookFor);
|
2019-04-16 00:24:24 +00:00
|
|
|
t = str_rchr(p, '\'');
|
2019-04-15 23:47:12 +00:00
|
|
|
|
2019-04-16 00:24:24 +00:00
|
|
|
if(t <= p)
|
2019-04-15 23:47:12 +00:00
|
|
|
return;
|
2019-04-16 06:10:02 +00:00
|
|
|
str_truncate(aName, sizeof(aName), p, t - p);
|
2015-05-19 22:51:02 +00:00
|
|
|
|
|
|
|
for(int i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
{
|
2017-02-15 15:43:45 +00:00
|
|
|
if(!m_pClient->m_aStats[i].IsActive())
|
2015-05-19 22:51:02 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if(str_comp(m_pClient->m_aClients[i].m_aName, aName) == 0)
|
|
|
|
{
|
|
|
|
m_pClient->m_aStats[i].m_FlagCaptures++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-21 09:41:59 +00:00
|
|
|
void CStatboard::OnRender()
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2017-04-26 03:10:31 +00:00
|
|
|
if((g_Config.m_ClAutoStatboardScreenshot||g_Config.m_ClAutoCSV) && Client()->State() != IClient::STATE_DEMOPLAYBACK)
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
|
|
|
if(m_ScreenshotTime < 0 && m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER)
|
2015-05-20 23:06:31 +00:00
|
|
|
m_ScreenshotTime = time_get() + time_freq() * 3;
|
2015-05-19 22:51:02 +00:00
|
|
|
if(m_ScreenshotTime > -1 && m_ScreenshotTime < time_get())
|
2015-05-20 16:22:04 +00:00
|
|
|
m_Active = true;
|
2015-05-20 23:06:31 +00:00
|
|
|
if(!m_ScreenshotTaken && m_ScreenshotTime > -1 && m_ScreenshotTime + time_freq() / 5 < time_get())
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2017-04-26 03:10:31 +00:00
|
|
|
if(g_Config.m_ClAutoStatboardScreenshot)
|
|
|
|
AutoStatScreenshot();
|
|
|
|
if(g_Config.m_ClAutoCSV)
|
|
|
|
AutoStatCSV();
|
2015-05-19 22:51:02 +00:00
|
|
|
m_ScreenshotTaken = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-02 16:55:05 +00:00
|
|
|
if(IsActive())
|
2015-05-20 16:22:04 +00:00
|
|
|
RenderGlobalStats();
|
2015-05-19 22:51:02 +00:00
|
|
|
}
|
|
|
|
|
2015-05-21 09:41:59 +00:00
|
|
|
void CStatboard::RenderGlobalStats()
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2015-06-14 11:41:03 +00:00
|
|
|
const float StatboardWidth = 400*3.0f*Graphics()->ScreenAspect();
|
|
|
|
const float StatboardHeight = 400*3.0f;
|
2015-07-10 20:12:20 +00:00
|
|
|
float StatboardContentWidth = 260.0f;
|
2015-06-14 11:41:03 +00:00
|
|
|
float StatboardContentHeight = 750.0f;
|
2015-05-19 22:51:02 +00:00
|
|
|
|
|
|
|
const CNetObj_PlayerInfo *apPlayers[MAX_CLIENTS] = {0};
|
|
|
|
int NumPlayers = 0;
|
|
|
|
|
|
|
|
// sort red or dm players by score
|
2015-06-14 11:41:03 +00:00
|
|
|
for(int i = 0; i < MAX_CLIENTS; i++)
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
|
|
|
const CNetObj_PlayerInfo *pInfo = m_pClient->m_Snap.m_paInfoByScore[i];
|
2017-02-15 15:43:45 +00:00
|
|
|
if(!pInfo || !m_pClient->m_aStats[pInfo->m_ClientID].IsActive() || m_pClient->m_aClients[pInfo->m_ClientID].m_Team != TEAM_RED)
|
2015-05-19 22:51:02 +00:00
|
|
|
continue;
|
|
|
|
apPlayers[NumPlayers] = pInfo;
|
|
|
|
NumPlayers++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// sort blue players by score after
|
2015-06-14 15:20:40 +00:00
|
|
|
if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS)
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2015-06-14 11:41:03 +00:00
|
|
|
for(int i = 0; i < MAX_CLIENTS; i++)
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
|
|
|
const CNetObj_PlayerInfo *pInfo = m_pClient->m_Snap.m_paInfoByScore[i];
|
2017-02-15 15:43:45 +00:00
|
|
|
if(!pInfo || !m_pClient->m_aStats[pInfo->m_ClientID].IsActive() || m_pClient->m_aClients[pInfo->m_ClientID].m_Team != TEAM_BLUE)
|
2015-05-19 22:51:02 +00:00
|
|
|
continue;
|
|
|
|
apPlayers[NumPlayers] = pInfo;
|
|
|
|
NumPlayers++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-21 14:27:26 +00:00
|
|
|
// Dirty hack. Do not show scoreboard if there are more than 16 players
|
|
|
|
// remove as soon as support of more than 16 players is required
|
|
|
|
if(NumPlayers > 16)
|
|
|
|
return;
|
|
|
|
|
|
|
|
//clear motd if it is active
|
|
|
|
if(m_pClient->m_pMotd->IsActive())
|
|
|
|
m_pClient->m_pMotd->Clear();
|
|
|
|
|
2017-03-21 10:24:44 +00:00
|
|
|
bool GameWithFlags = m_pClient->m_Snap.m_pGameInfoObj &&
|
2015-06-12 10:33:42 +00:00
|
|
|
m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_FLAGS;
|
|
|
|
|
2015-07-09 18:33:16 +00:00
|
|
|
StatboardContentWidth += 7 * 85 + 95; // Suicides 95; other labels 85
|
2015-05-19 22:51:02 +00:00
|
|
|
|
2017-03-21 10:24:44 +00:00
|
|
|
if(GameWithFlags)
|
2015-07-10 20:12:20 +00:00
|
|
|
StatboardContentWidth += 150; // Grabs & Flags
|
2015-05-19 22:51:02 +00:00
|
|
|
|
2015-05-21 09:41:59 +00:00
|
|
|
bool aDisplayWeapon[NUM_WEAPONS] = {false};
|
2015-06-14 11:41:03 +00:00
|
|
|
for(int i = 0; i < NumPlayers; i++)
|
2015-05-21 09:41:59 +00:00
|
|
|
{
|
2017-02-15 15:43:45 +00:00
|
|
|
const CGameClient::CClientStats *pStats = &m_pClient->m_aStats[apPlayers[i]->m_ClientID];
|
2015-05-21 09:41:59 +00:00
|
|
|
for(int j=0; j<NUM_WEAPONS; j++)
|
2017-02-15 15:43:45 +00:00
|
|
|
aDisplayWeapon[j] = aDisplayWeapon[j] || pStats->m_aFragsWith[j] || pStats->m_aDeathsFrom[j];
|
2015-05-21 09:41:59 +00:00
|
|
|
}
|
2015-06-14 11:41:03 +00:00
|
|
|
for(int i = 0; i < NUM_WEAPONS; i++)
|
2015-05-21 09:41:59 +00:00
|
|
|
if(aDisplayWeapon[i])
|
2015-06-14 11:41:03 +00:00
|
|
|
StatboardContentWidth += 80;
|
2015-05-19 22:51:02 +00:00
|
|
|
|
2015-06-14 11:41:03 +00:00
|
|
|
float x = StatboardWidth/2-StatboardContentWidth/2;
|
2015-05-19 22:51:02 +00:00
|
|
|
float y = 200.0f;
|
|
|
|
|
2015-06-14 11:41:03 +00:00
|
|
|
Graphics()->MapScreen(0, 0, StatboardWidth, StatboardHeight);
|
2015-05-19 22:51:02 +00:00
|
|
|
|
|
|
|
Graphics()->BlendNormal();
|
2012-08-12 10:41:50 +00:00
|
|
|
Graphics()->TextureClear();
|
2015-05-19 22:51:02 +00:00
|
|
|
Graphics()->QuadsBegin();
|
|
|
|
Graphics()->SetColor(0,0,0,0.5f);
|
2015-06-14 11:41:03 +00:00
|
|
|
RenderTools()->DrawRoundRect(x-10.f, y-10.f, StatboardContentWidth, StatboardContentHeight, 17.0f);
|
2015-05-19 22:51:02 +00:00
|
|
|
Graphics()->QuadsEnd();
|
|
|
|
|
|
|
|
float tw;
|
|
|
|
int px = 325;
|
|
|
|
|
2015-07-09 18:33:16 +00:00
|
|
|
TextRender()->Text(0, x+10, y-5, 22.0f, Localize("Name"), -1);
|
2015-06-14 11:41:03 +00:00
|
|
|
const char *apHeaders[] = {
|
|
|
|
Localize("Frags"), Localize("Deaths"), Localize("Suicides"),
|
|
|
|
Localize("Ratio"), Localize("Net"), Localize("FPM"),
|
2015-07-09 18:33:16 +00:00
|
|
|
Localize("Spree"), Localize("Best"), Localize("Grabs")
|
2015-06-14 11:41:03 +00:00
|
|
|
};
|
|
|
|
for(int i = 0; i < 9; i++)
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2015-05-21 17:21:13 +00:00
|
|
|
if(i == 2)
|
|
|
|
px += 10.0f; // Suicides
|
2017-03-21 10:24:44 +00:00
|
|
|
if(i == 8 && !GameWithFlags) // Don't draw "Grabs" in game with no flag
|
2015-06-12 10:33:42 +00:00
|
|
|
continue;
|
2020-07-15 19:10:13 +00:00
|
|
|
tw = TextRender()->TextWidth(0, 22.0f, apHeaders[i], -1, -1.0f);
|
2015-07-09 18:33:16 +00:00
|
|
|
TextRender()->Text(0, x+px-tw, y-5, 22.0f, apHeaders[i], -1);
|
|
|
|
px += 85;
|
2015-05-20 23:06:31 +00:00
|
|
|
}
|
2015-05-19 22:51:02 +00:00
|
|
|
|
2015-05-20 23:06:31 +00:00
|
|
|
Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id);
|
|
|
|
Graphics()->QuadsBegin();
|
2015-06-14 11:41:03 +00:00
|
|
|
px -= 40;
|
|
|
|
for(int i = 0; i < NUM_WEAPONS; i++)
|
2015-05-20 23:06:31 +00:00
|
|
|
{
|
2015-05-21 09:41:59 +00:00
|
|
|
if(!aDisplayWeapon[i])
|
|
|
|
continue;
|
2015-05-20 23:06:31 +00:00
|
|
|
RenderTools()->SelectSprite(g_pData->m_Weapons.m_aId[i].m_pSpriteBody);
|
|
|
|
if(i == 0)
|
2019-07-08 21:08:42 +00:00
|
|
|
RenderTools()->DrawSprite(x+px, y+10, g_pData->m_Weapons.m_aId[i].m_VisualSize*0.8f);
|
2015-05-20 23:06:31 +00:00
|
|
|
else
|
|
|
|
RenderTools()->DrawSprite(x+px, y+10, g_pData->m_Weapons.m_aId[i].m_VisualSize);
|
|
|
|
px += 80;
|
2015-05-19 22:51:02 +00:00
|
|
|
}
|
2015-05-20 23:06:31 +00:00
|
|
|
Graphics()->QuadsEnd();
|
2015-05-19 22:51:02 +00:00
|
|
|
|
2017-03-21 10:24:44 +00:00
|
|
|
if(GameWithFlags)
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
|
|
|
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();
|
|
|
|
}
|
2015-06-14 11:41:03 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
px += 40;
|
|
|
|
}
|
2015-05-19 22:51:02 +00:00
|
|
|
|
|
|
|
y += 29.0f;
|
|
|
|
|
2015-07-09 18:33:16 +00:00
|
|
|
float FontSize = 24.0f;
|
2015-05-19 22:51:02 +00:00
|
|
|
float LineHeight = 50.0f;
|
2015-07-09 18:33:16 +00:00
|
|
|
float TeeSizemod = 0.8f;
|
2015-05-19 22:51:02 +00:00
|
|
|
float TeeOffset = 0.0f;
|
|
|
|
|
|
|
|
if(NumPlayers > 14)
|
|
|
|
{
|
2015-07-09 18:33:16 +00:00
|
|
|
FontSize = 24.0f;
|
2015-05-19 22:51:02 +00:00
|
|
|
LineHeight = 40.0f;
|
2015-07-09 18:33:16 +00:00
|
|
|
TeeSizemod = 0.7f;
|
2015-05-19 22:51:02 +00:00
|
|
|
TeeOffset = -5.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
for(int j = 0; j < NumPlayers; j++)
|
|
|
|
{
|
|
|
|
const CNetObj_PlayerInfo *pInfo = apPlayers[j];
|
2017-02-15 15:43:45 +00:00
|
|
|
const CGameClient::CClientStats *pStats = &m_pClient->m_aStats[pInfo->m_ClientID];
|
2015-05-19 22:51:02 +00:00
|
|
|
|
2015-06-14 11:41:03 +00:00
|
|
|
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))
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
|
|
|
// background so it's easy to find the local player
|
2012-08-12 10:41:50 +00:00
|
|
|
Graphics()->TextureClear();
|
2015-05-19 22:51:02 +00:00
|
|
|
Graphics()->QuadsBegin();
|
|
|
|
Graphics()->SetColor(1,1,1,0.25f);
|
2015-06-14 11:41:03 +00:00
|
|
|
RenderTools()->DrawRoundRect(x, y, StatboardContentWidth-20, LineHeight*0.95f, 17.0f);
|
2015-05-19 22:51:02 +00:00
|
|
|
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];
|
|
|
|
CTextCursor Cursor;
|
2020-07-15 19:10:13 +00:00
|
|
|
tw = TextRender()->TextWidth(0, FontSize, m_pClient->m_aClients[pInfo->m_ClientID].m_aName, -1, -1.0f);
|
2018-04-04 00:40:30 +00:00
|
|
|
TextRender()->SetCursor(&Cursor, x+64, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END);
|
2015-05-19 22:51:02 +00:00
|
|
|
Cursor.m_LineWidth = 220;
|
|
|
|
TextRender()->TextEx(&Cursor, m_pClient->m_aClients[pInfo->m_ClientID].m_aName, -1);
|
|
|
|
|
|
|
|
px = 325;
|
|
|
|
|
2015-06-14 11:41:03 +00:00
|
|
|
// FRAGS
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2017-02-15 15:43:45 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%d", pStats->m_Frags);
|
2020-07-15 19:10:13 +00:00
|
|
|
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
|
2018-04-04 00:40:30 +00:00
|
|
|
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
|
2015-07-09 18:33:16 +00:00
|
|
|
px += 85;
|
2015-05-19 22:51:02 +00:00
|
|
|
}
|
2015-06-14 11:41:03 +00:00
|
|
|
// DEATHS
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2017-02-15 15:43:45 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%d", pStats->m_Deaths);
|
2020-07-15 19:10:13 +00:00
|
|
|
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
|
2018-04-04 00:40:30 +00:00
|
|
|
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
|
2015-07-09 18:33:16 +00:00
|
|
|
px += 85;
|
2015-05-19 22:51:02 +00:00
|
|
|
}
|
2015-06-14 11:41:03 +00:00
|
|
|
// SUICIDES
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2015-05-21 17:21:13 +00:00
|
|
|
px += 10;
|
2017-02-15 15:43:45 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%d", pStats->m_Suicides);
|
2020-07-15 19:10:13 +00:00
|
|
|
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
|
2018-04-04 00:40:30 +00:00
|
|
|
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
|
2015-07-09 18:33:16 +00:00
|
|
|
px += 85;
|
2015-05-19 22:51:02 +00:00
|
|
|
}
|
2015-06-14 11:41:03 +00:00
|
|
|
// RATIO
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2017-02-15 15:43:45 +00:00
|
|
|
if(pStats->m_Deaths == 0)
|
2015-05-19 22:51:02 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "--");
|
|
|
|
else
|
2017-02-15 15:43:45 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%.2f", (float)(pStats->m_Frags)/pStats->m_Deaths);
|
2020-07-15 19:10:13 +00:00
|
|
|
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
|
2018-04-04 00:40:30 +00:00
|
|
|
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
|
2015-07-09 18:33:16 +00:00
|
|
|
px += 85;
|
2015-05-19 22:51:02 +00:00
|
|
|
}
|
2015-06-14 11:41:03 +00:00
|
|
|
// NET
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2017-02-15 15:43:45 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%+d", pStats->m_Frags-pStats->m_Deaths);
|
2020-07-15 19:10:13 +00:00
|
|
|
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
|
2018-04-04 00:40:30 +00:00
|
|
|
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
|
2015-07-09 18:33:16 +00:00
|
|
|
px += 85;
|
2015-05-19 22:51:02 +00:00
|
|
|
}
|
2015-06-14 11:41:03 +00:00
|
|
|
// FPM
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2020-02-19 10:24:58 +00:00
|
|
|
float Fpm = pStats->GetFPM(Client()->GameTick(g_Config.m_ClDummy), Client()->GameTickSpeed());
|
2015-05-19 22:51:02 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%.1f", Fpm);
|
2020-07-15 19:10:13 +00:00
|
|
|
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
|
2018-04-04 00:40:30 +00:00
|
|
|
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
|
2015-07-09 18:33:16 +00:00
|
|
|
px += 85;
|
2015-05-19 22:51:02 +00:00
|
|
|
}
|
2015-06-14 11:41:03 +00:00
|
|
|
// SPREE
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2017-02-15 15:43:45 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%d", pStats->m_CurrentSpree);
|
2020-07-15 19:10:13 +00:00
|
|
|
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
|
2018-04-04 00:40:30 +00:00
|
|
|
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
|
2015-07-09 18:33:16 +00:00
|
|
|
px += 85;
|
2015-05-19 22:51:02 +00:00
|
|
|
}
|
2015-06-14 11:41:03 +00:00
|
|
|
// BEST SPREE
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2017-02-15 15:43:45 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%d", pStats->m_BestSpree);
|
2020-07-15 19:10:13 +00:00
|
|
|
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
|
2018-04-04 00:40:30 +00:00
|
|
|
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
|
2015-07-09 18:33:16 +00:00
|
|
|
px += 85;
|
2015-05-19 22:51:02 +00:00
|
|
|
}
|
2015-06-14 11:41:03 +00:00
|
|
|
// GRABS
|
2017-03-21 10:24:44 +00:00
|
|
|
if(GameWithFlags)
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2017-02-15 15:43:45 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%d", pStats->m_FlagGrabs);
|
2020-07-15 19:10:13 +00:00
|
|
|
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
|
2018-04-04 00:40:30 +00:00
|
|
|
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
|
2015-07-09 18:33:16 +00:00
|
|
|
px += 85;
|
2015-05-19 22:51:02 +00:00
|
|
|
}
|
2015-06-14 11:41:03 +00:00
|
|
|
// WEAPONS
|
|
|
|
px -= 40;
|
|
|
|
for(int i = 0; i < NUM_WEAPONS; i++)
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2015-05-21 09:41:59 +00:00
|
|
|
if(!aDisplayWeapon[i])
|
|
|
|
continue;
|
|
|
|
|
2017-02-15 15:43:45 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%d/%d", pStats->m_aFragsWith[i], pStats->m_aDeathsFrom[i]);
|
2020-07-15 19:10:13 +00:00
|
|
|
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
|
2018-04-04 00:40:30 +00:00
|
|
|
TextRender()->Text(0, x+px-tw/2, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
|
2015-05-19 22:51:02 +00:00
|
|
|
px += 80;
|
|
|
|
}
|
2015-06-14 11:41:03 +00:00
|
|
|
// FLAGS
|
2017-03-21 10:24:44 +00:00
|
|
|
if(GameWithFlags)
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
2017-02-15 15:43:45 +00:00
|
|
|
str_format(aBuf, sizeof(aBuf), "%d", pStats->m_FlagCaptures);
|
2020-07-15 19:10:13 +00:00
|
|
|
tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f);
|
2018-04-04 00:40:30 +00:00
|
|
|
TextRender()->Text(0, x-tw+px, y + (LineHeight*0.95f - FontSize) / 2.f, FontSize, aBuf, -1);
|
2015-07-09 18:33:16 +00:00
|
|
|
px += 85;
|
2015-05-19 22:51:02 +00:00
|
|
|
}
|
|
|
|
y += LineHeight;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-21 09:41:59 +00:00
|
|
|
void CStatboard::AutoStatScreenshot()
|
2015-05-19 22:51:02 +00:00
|
|
|
{
|
|
|
|
if(Client()->State() != IClient::STATE_DEMOPLAYBACK)
|
|
|
|
Client()->AutoStatScreenshot_Start();
|
|
|
|
}
|
2017-04-26 03:10:31 +00:00
|
|
|
|
|
|
|
void CStatboard::AutoStatCSV()
|
|
|
|
{
|
2017-05-02 16:55:05 +00:00
|
|
|
if(Client()->State() != IClient::STATE_DEMOPLAYBACK)
|
2017-04-26 03:10:31 +00:00
|
|
|
{
|
|
|
|
char aDate[20], aFilename[128];
|
|
|
|
str_timestamp(aDate, sizeof(aDate));
|
|
|
|
str_format(aFilename, sizeof(aFilename), "screenshots/auto/stats_%s.csv", aDate);
|
|
|
|
IOHANDLE File = Storage()->OpenFile(aFilename, IOFLAG_WRITE, IStorage::TYPE_ALL);
|
|
|
|
|
2017-05-02 16:27:04 +00:00
|
|
|
FormatStats();
|
|
|
|
|
2017-04-26 03:10:31 +00:00
|
|
|
if(File)
|
|
|
|
{
|
2018-04-09 09:56:39 +00:00
|
|
|
io_write(File, m_pCSVstr, str_length(m_pCSVstr));
|
2017-04-26 03:10:31 +00:00
|
|
|
io_close(File);
|
|
|
|
}
|
|
|
|
|
|
|
|
Client()->AutoCSV_Start();
|
2017-07-08 11:38:27 +00:00
|
|
|
}
|
2017-04-26 03:10:31 +00:00
|
|
|
}
|
|
|
|
|
2017-05-02 16:27:04 +00:00
|
|
|
char* CStatboard::ReplaceCommata(char* pStr)
|
2017-04-26 03:10:31 +00:00
|
|
|
{
|
2017-05-02 16:55:05 +00:00
|
|
|
if(!str_find(pStr, ","))
|
2017-05-02 16:27:04 +00:00
|
|
|
return pStr;
|
|
|
|
|
|
|
|
char aBuf[64];
|
|
|
|
str_format(aBuf, sizeof(aBuf), "%s", pStr);
|
|
|
|
|
|
|
|
char aOutbuf[256];
|
|
|
|
mem_zero(aOutbuf, sizeof(aOutbuf));
|
|
|
|
|
2017-05-02 16:55:05 +00:00
|
|
|
for(int i = 0, skip = 0; i < 64; i++)
|
2017-04-26 03:10:31 +00:00
|
|
|
{
|
2017-05-02 16:55:05 +00:00
|
|
|
if(aBuf[i] == ',')
|
2017-05-02 16:27:04 +00:00
|
|
|
{
|
|
|
|
aOutbuf[i + skip++] = '%';
|
|
|
|
aOutbuf[i + skip++] = '2';
|
|
|
|
aOutbuf[i + skip] = 'C';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
aOutbuf[i + skip] = aBuf[i];
|
2017-04-26 03:10:31 +00:00
|
|
|
}
|
|
|
|
|
2017-05-02 16:27:04 +00:00
|
|
|
unsigned int len = str_length(aOutbuf);
|
|
|
|
char* buf = new char[len];
|
2017-05-02 16:40:54 +00:00
|
|
|
mem_copy(buf, aOutbuf, len);
|
2017-05-02 16:27:04 +00:00
|
|
|
return buf;
|
2017-04-26 03:10:31 +00:00
|
|
|
}
|
|
|
|
|
2017-05-02 16:27:04 +00:00
|
|
|
void CStatboard::FormatStats()
|
2017-04-26 03:10:31 +00:00
|
|
|
{
|
|
|
|
// server stats
|
|
|
|
CServerInfo CurrentServerInfo;
|
|
|
|
Client()->GetServerInfo(&CurrentServerInfo);
|
|
|
|
char aServerStats[1024];
|
2017-05-02 16:27:04 +00:00
|
|
|
str_format(aServerStats, sizeof(aServerStats), "Servername,Game-type,Map\n%s,%s,%s", ReplaceCommata(CurrentServerInfo.m_aName), CurrentServerInfo.m_aGameType, CurrentServerInfo.m_aMap);
|
2017-04-26 03:10:31 +00:00
|
|
|
|
|
|
|
|
|
|
|
// player stats
|
|
|
|
|
|
|
|
// sort players
|
|
|
|
const CNetObj_PlayerInfo *apPlayers[MAX_CLIENTS] = {0};
|
|
|
|
int NumPlayers = 0;
|
|
|
|
|
|
|
|
// sort red or dm players by score
|
|
|
|
for (int i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
{
|
|
|
|
const CNetObj_PlayerInfo *pInfo = m_pClient->m_Snap.m_paInfoByScore[i];
|
2017-05-02 16:55:05 +00:00
|
|
|
if(!pInfo || !m_pClient->m_aStats[pInfo->m_ClientID].IsActive() || m_pClient->m_aClients[pInfo->m_ClientID].m_Team != TEAM_RED)
|
2017-04-26 03:10:31 +00:00
|
|
|
continue;
|
|
|
|
apPlayers[NumPlayers] = pInfo;
|
|
|
|
NumPlayers++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// sort blue players by score after
|
2017-05-02 16:55:05 +00:00
|
|
|
if(m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_TEAMS)
|
2017-04-26 03:10:31 +00:00
|
|
|
{
|
|
|
|
for (int i = 0; i < MAX_CLIENTS; i++)
|
|
|
|
{
|
|
|
|
const CNetObj_PlayerInfo *pInfo = m_pClient->m_Snap.m_paInfoByScore[i];
|
2017-05-02 16:55:05 +00:00
|
|
|
if(!pInfo || !m_pClient->m_aStats[pInfo->m_ClientID].IsActive() || m_pClient->m_aClients[pInfo->m_ClientID].m_Team != TEAM_BLUE)
|
2017-04-26 03:10:31 +00:00
|
|
|
continue;
|
|
|
|
apPlayers[NumPlayers] = pInfo;
|
|
|
|
NumPlayers++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
char aPlayerStats[1024 * VANILLA_MAX_CLIENTS];
|
2019-11-22 14:37:18 +00:00
|
|
|
str_format(aPlayerStats, sizeof(aPlayerStats), "Local-player,Team,Name,Clan,Score,Frags,Deaths,Suicides,F/D-ratio,Net,FPM,Spree,Best,Hammer-F/D,Gun-F/D,Shotgun-F/D,Grenade-F/D,Laser-F/D,Ninja-F/D,GameWithFlags,Flag-grabs,Flag-captures\n");
|
2017-04-28 09:28:18 +00:00
|
|
|
for (int i = 0; i < NumPlayers; i++)
|
2017-04-26 03:10:31 +00:00
|
|
|
{
|
2017-04-28 09:28:18 +00:00
|
|
|
const CNetObj_PlayerInfo *pInfo = apPlayers[i];
|
2017-04-26 03:10:31 +00:00
|
|
|
const CGameClient::CClientStats *pStats = &m_pClient->m_aStats[pInfo->m_ClientID];
|
2017-07-24 13:27:30 +00:00
|
|
|
|
|
|
|
// Pre-formatting
|
2017-04-26 03:10:31 +00:00
|
|
|
|
|
|
|
// Weapons frags/deaths
|
|
|
|
char aWeaponFD[64 * NUM_WEAPONS];
|
2017-04-28 09:28:18 +00:00
|
|
|
for (int j = 0; j < NUM_WEAPONS; j++)
|
2017-04-26 03:10:31 +00:00
|
|
|
{
|
2017-05-02 16:55:05 +00:00
|
|
|
if(j == 0)
|
2017-04-28 09:28:18 +00:00
|
|
|
str_format(aWeaponFD, sizeof(aWeaponFD), "%d/%d", pStats->m_aFragsWith[j], pStats->m_aDeathsFrom[j]);
|
2017-04-26 03:10:31 +00:00
|
|
|
else
|
2017-04-28 09:28:18 +00:00
|
|
|
str_format(aWeaponFD, sizeof(aWeaponFD), "%s,%d/%d", aWeaponFD, pStats->m_aFragsWith[j], pStats->m_aDeathsFrom[j]);
|
2017-04-26 03:10:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Frag/Death ratio
|
|
|
|
float fdratio=0.0f;
|
2017-05-02 16:55:05 +00:00
|
|
|
if(pStats->m_Deaths != 0)
|
2017-04-26 03:10:31 +00:00
|
|
|
fdratio = (float) (pStats->m_Frags) / pStats->m_Deaths;
|
|
|
|
|
|
|
|
// Local player
|
|
|
|
bool localPlayer = (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));
|
|
|
|
|
|
|
|
// Game with flags
|
|
|
|
bool GameWithFlags = (m_pClient->m_Snap.m_pGameInfoObj && m_pClient->m_Snap.m_pGameInfoObj->m_GameFlags&GAMEFLAG_FLAGS);
|
|
|
|
|
2017-07-24 13:27:30 +00:00
|
|
|
char aBuf[1024];
|
|
|
|
str_format(aBuf, sizeof(aBuf), "%d,%d,%s,%s,%d,%d,%d,%d,%.2f,%i,%.1f,%d,%d,%s,%d,%d,%d\n",
|
2017-04-26 03:10:31 +00:00
|
|
|
localPlayer?1:0, // Local player
|
|
|
|
m_pClient->m_aClients[pInfo->m_ClientID].m_Team, // Team
|
2017-05-02 16:55:05 +00:00
|
|
|
ReplaceCommata(m_pClient->m_aClients[pInfo->m_ClientID].m_aName), // Name
|
|
|
|
ReplaceCommata(m_pClient->m_aClients[pInfo->m_ClientID].m_aClan), // Clan
|
2017-04-26 03:10:31 +00:00
|
|
|
clamp(pInfo->m_Score, -999, 999), // Score
|
|
|
|
pStats->m_Frags, // Frags
|
|
|
|
pStats->m_Deaths, // Deaths
|
|
|
|
pStats->m_Suicides, // Suicides
|
|
|
|
fdratio, // fdratio
|
|
|
|
pStats->m_Frags - pStats->m_Deaths, // Net
|
2020-02-19 10:24:58 +00:00
|
|
|
pStats->GetFPM(Client()->GameTick(g_Config.m_ClDummy), Client()->GameTickSpeed()), // FPM
|
2017-04-26 03:10:31 +00:00
|
|
|
pStats->m_CurrentSpree, // CurSpree
|
|
|
|
pStats->m_BestSpree, // BestSpree
|
|
|
|
aWeaponFD, // WeaponFD
|
|
|
|
GameWithFlags?1:0, // GameWithFlags
|
|
|
|
pStats->m_FlagGrabs, // Flag grabs
|
|
|
|
pStats->m_FlagCaptures); // Flag captures
|
|
|
|
|
2017-07-24 13:27:30 +00:00
|
|
|
str_append(aPlayerStats, aBuf, sizeof(aPlayerStats));
|
2017-04-26 03:10:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
char aStats[1024*(VANILLA_MAX_CLIENTS+1)];
|
2017-05-02 16:40:54 +00:00
|
|
|
str_format(aStats, sizeof(aStats), "%s\n\n%s", aServerStats, aPlayerStats);
|
2017-04-26 03:10:31 +00:00
|
|
|
|
2018-04-09 09:56:39 +00:00
|
|
|
unsigned int Len = str_length(aStats);
|
|
|
|
m_pCSVstr = (char *)malloc(Len);
|
|
|
|
str_copy(m_pCSVstr, aStats, Len);
|
2017-04-26 03:10:31 +00:00
|
|
|
}
|