//////////////////////////////////////////////////////////// /// Create a new shape made of a single rectangle //////////////////////////////////////////////////////////// sfShape* sfShape_CreateRectangle(float P1X, float P1Y, float P2X, float P2Y, sfColor Col, float Outline, sfColor OutlineCol) { sf::Color Color(Col.r, Col.g, Col.b, Col.a); sf::Color OutlineColor(OutlineCol.r, OutlineCol.g, OutlineCol.b, OutlineCol.a); sfShape* Shape = new sfShape; Shape->This = sf::Shape::Rectangle(P1X, P1Y, P2X, P2Y, Color, Outline, OutlineColor); return Shape; }
//////////////////////////////////////////////////////////// /// Create a new shape made of a single circle //////////////////////////////////////////////////////////// sfShape* sfShape_CreateCircle(float X, float Y, float Radius, sfColor Col, float Outline, sfColor OutlineCol) { sf::Color Color(Col.r, Col.g, Col.b, Col.a); sf::Color OutlineColor(OutlineCol.r, OutlineCol.g, OutlineCol.b, OutlineCol.a); sfShape* Shape = new sfShape; Shape->This = sf::Shape::Circle(X, Y, Radius, Color, Outline, OutlineColor); return Shape; }
float CScoreboard::RenderScoreboard(float x, float y, float w, int Team, const char *pTitle, int Align) { if(Team == TEAM_SPECTATORS) return 0.0f; // ready mode const CGameClient::CSnapState& Snap = m_pClient->m_Snap; const bool ReadyMode = Snap.m_pGameData && (Snap.m_pGameData->m_GameStateFlags&(GAMESTATEFLAG_STARTCOUNTDOWN|GAMESTATEFLAG_PAUSED|GAMESTATEFLAG_WARMUP)) && Snap.m_pGameData->m_GameStateEndTick == 0; float HeadlineHeight = 40.0f; float TitleFontsize = 20.0f; float HeadlineFontsize = 12.0f; float LineHeight = 20.0f; float TeeSizeMod = 1.0f; float Spacing = 2.0f; float PingOffset = x+Spacing, PingLength = 35.0f; float CountryFlagOffset = PingOffset+PingLength, CountryFlagLength = 20.f; float IdSize = g_Config.m_ClShowUserId ? LineHeight : 0.0f; float ReadyLength = ReadyMode ? 10.f : 0.f; float TeeOffset = CountryFlagOffset+CountryFlagLength+4.0f, TeeLength = 25*TeeSizeMod; float NameOffset = CountryFlagOffset+CountryFlagLength+IdSize, NameLength = 128.0f-IdSize/2-ReadyLength; float ClanOffset = NameOffset+NameLength+ReadyLength, ClanLength = 88.0f-IdSize/2; float KillOffset = ClanOffset+ClanLength, KillLength = 24.0f; float DeathOffset = KillOffset+KillLength, DeathLength = 24.0f; float ScoreOffset = DeathOffset+DeathLength, ScoreLength = 35.0f; float tw = 0.0f; bool NoTitle = pTitle? false : true; // count players dbg_assert(Team == TEAM_RED || Team == TEAM_BLUE, "Unknown team id"); int NumPlayers = m_pClient->m_GameInfo.m_aTeamSize[Team]; m_PlayerLines = max(m_PlayerLines, NumPlayers); // clamp to 16 if(m_PlayerLines > 16) m_PlayerLines = 16; char aBuf[128] = {0}; // background Graphics()->BlendNormal(); vec4 Color; if(Team == TEAM_RED && m_pClient->m_GameInfo.m_GameFlags&GAMEFLAG_TEAMS) Color = vec4(0.975f, 0.17f, 0.17f, 0.75f); else if(Team == TEAM_BLUE) Color = vec4(0.17f, 0.46f, 0.975f, 0.75f); else Color = vec4(0.0f, 0.0f, 0.0f, 0.5f); CUIRect Rect = {x, y, w, HeadlineHeight}; RenderTools()->DrawRoundRect(&Rect, Color, 5.0f); // render title if(NoTitle) { if(m_pClient->m_Snap.m_pGameData->m_GameStateFlags&GAMESTATEFLAG_GAMEOVER) pTitle = Localize("Game over"); else if(m_pClient->m_Snap.m_pGameData->m_GameStateFlags&GAMESTATEFLAG_ROUNDOVER) { pTitle = Localize("Round over"); m_SkipPlayerStatsReset = true; } else pTitle = Localize("Scoreboard"); } else { if(Team == TEAM_BLUE) str_format(aBuf, sizeof(aBuf), "(%d) %s", NumPlayers, pTitle); else str_format(aBuf, sizeof(aBuf), "%s (%d)", pTitle, NumPlayers); } if(Align == -1) { tw = TextRender()->TextWidth(0, TitleFontsize, pTitle, -1, -1.0f); TextRender()->Text(0, x+20.0f, y+5.0f, TitleFontsize, pTitle, -1.0f); if(!NoTitle) { str_format(aBuf, sizeof(aBuf), " (%d)", NumPlayers); TextRender()->TextColor(1.0f, 1.0f, 1.0f, 0.5f); TextRender()->Text(0, x+20.0f+tw, y+5.0f, TitleFontsize, aBuf, -1.0f); TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); } } else { tw = TextRender()->TextWidth(0, TitleFontsize, pTitle, -1, -1.0f); if(!NoTitle) { str_format(aBuf, sizeof(aBuf), "(%d) ", NumPlayers); float PlayersTextWidth = TextRender()->TextWidth(0, TitleFontsize, aBuf, -1, -1.0f); TextRender()->TextColor(1.0f, 1.0f, 1.0f, 0.5f); TextRender()->Text(0, x+w-tw-PlayersTextWidth-20.0f, y+5.0f, TitleFontsize, aBuf, -1.0f); TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); } tw = TextRender()->TextWidth(0, TitleFontsize, pTitle, -1, -1.0f); TextRender()->Text(0, x+w-tw-20.0f, y+5.0f, TitleFontsize, pTitle, -1.0f); } if(m_pClient->m_GameInfo.m_GameFlags&GAMEFLAG_TEAMS) { int Score = Team == TEAM_RED ? m_pClient->m_Snap.m_pGameDataTeam->m_TeamscoreRed : m_pClient->m_Snap.m_pGameDataTeam->m_TeamscoreBlue; str_format(aBuf, sizeof(aBuf), "%d", Score); } else { if(m_pClient->m_Snap.m_SpecInfo.m_Active && m_pClient->m_Snap.m_SpecInfo.m_SpectatorID >= 0 && m_pClient->m_Snap.m_paPlayerInfos[m_pClient->m_Snap.m_SpecInfo.m_SpectatorID]) { int Score = m_pClient->m_Snap.m_paPlayerInfos[m_pClient->m_Snap.m_SpecInfo.m_SpectatorID]->m_Score; str_format(aBuf, sizeof(aBuf), "%d", Score); } else if(m_pClient->m_Snap.m_pLocalInfo) { int Score = m_pClient->m_Snap.m_pLocalInfo->m_Score; str_format(aBuf, sizeof(aBuf), "%d", Score); } } if(Align == -1) { tw = TextRender()->TextWidth(0, TitleFontsize, aBuf, -1, -1.0f); TextRender()->Text(0, x+w-tw-20.0f, y+5.0f, TitleFontsize, aBuf, -1.0f); } else TextRender()->Text(0, x+20.0f, y+5.0f, TitleFontsize, aBuf, -1.0f); // render headlines y += HeadlineHeight; Graphics()->BlendNormal(); { CUIRect Rect = {x, y, w, LineHeight*(m_PlayerLines+1)}; RenderTools()->DrawRoundRect(&Rect, vec4(0.0f, 0.0f, 0.0f, 0.25f), 5.0f); } if(m_PlayerLines) { CUIRect Rect = {x, y+LineHeight, w, LineHeight*(m_PlayerLines)}; RenderTools()->DrawRoundRect(&Rect, vec4(0.0f, 0.0f, 0.0f, 0.25f), 5.0f); } TextRender()->TextColor(1.0f, 1.0f, 1.0f, 0.5f); tw = TextRender()->TextWidth(0, HeadlineFontsize, Localize("Ping"), -1, -1.0f); TextRender()->Text(0, PingOffset+PingLength-tw, y+Spacing, HeadlineFontsize, Localize("Ping"), -1.0f); TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); TextRender()->Text(0, NameOffset+ TeeLength, y+Spacing, HeadlineFontsize, Localize("Name"), -1.0f); tw = TextRender()->TextWidth(0, HeadlineFontsize, Localize("Clan"), -1, -1.0f); TextRender()->Text(0, ClanOffset+ClanLength/2-tw/2, y+Spacing, HeadlineFontsize, Localize("Clan"), -1.0f); TextRender()->TextColor(1.0f, 1.0f, 1.0f, 0.5f); tw = TextRender()->TextWidth(0, HeadlineFontsize, "K", -1, -1.0f); TextRender()->Text(0, KillOffset+KillLength/2-tw/2, y+Spacing, HeadlineFontsize, "K", -1.0f); tw = TextRender()->TextWidth(0, HeadlineFontsize, "D", -1, -1.0f); TextRender()->Text(0, DeathOffset+DeathLength/2-tw/2, y+Spacing, HeadlineFontsize, "D", -1.0f); TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); tw = TextRender()->TextWidth(0, HeadlineFontsize, Localize("Score"), -1, -1.0f); TextRender()->Text(0, ScoreOffset+ScoreLength/2-tw/2, y+Spacing, HeadlineFontsize, Localize("Score"), -1.0f); TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); // render player entries y += LineHeight; float FontSize = HeadlineFontsize; CTextCursor Cursor; const int MAX_IDS = 16; int RenderScoreIDs[MAX_IDS]; int NumRenderScoreIDs = 0; int HoleSizes[2]; for(int i = 0; i < MAX_IDS; ++i) RenderScoreIDs[i] = -1; // Non vanilla scoreboard, for now, some parts of the scoreboard are omitted if(NumPlayers > MAX_IDS) { for(int RenderDead = 0; RenderDead < 2 && NumRenderScoreIDs < MAX_IDS-1; ++RenderDead) { for(int i = 0; i < MAX_CLIENTS && NumRenderScoreIDs < MAX_IDS-1; i++) { // make sure that we render the correct team const CGameClient::CPlayerInfoItem *pInfo = &m_pClient->m_Snap.m_aInfoByScore[i]; if(!pInfo->m_pPlayerInfo || m_pClient->m_aClients[pInfo->m_ClientID].m_Team != Team || (!RenderDead && (pInfo->m_pPlayerInfo->m_PlayerFlags&PLAYERFLAG_DEAD)) || (RenderDead && !(pInfo->m_pPlayerInfo->m_PlayerFlags&PLAYERFLAG_DEAD))) continue; RenderScoreIDs[NumRenderScoreIDs] = i; NumRenderScoreIDs++; } } NumRenderScoreIDs = MAX_IDS; RenderScoreIDs[MAX_IDS-1] = -1; HoleSizes[0] = m_pClient->m_GameInfo.m_aTeamSize[Team] - (MAX_IDS-1); if(m_pClient->m_LocalClientID != -1 && (m_pClient->m_aClients[m_pClient->m_LocalClientID].m_Team == Team || m_pClient->m_Snap.m_SpecInfo.m_Active)) { int Classment = -1; int TeamScoreIDs[MAX_CLIENTS]; for(int RenderDead = 0, j = 0; RenderDead < 2; ++RenderDead) { for(int i = 0; i < MAX_CLIENTS; i++) { // make sure that we render the correct team const CGameClient::CPlayerInfoItem *pInfo = &m_pClient->m_Snap.m_aInfoByScore[i]; if(!pInfo->m_pPlayerInfo || m_pClient->m_aClients[pInfo->m_ClientID].m_Team != Team || (!RenderDead && (pInfo->m_pPlayerInfo->m_PlayerFlags&PLAYERFLAG_DEAD)) || (RenderDead && !(pInfo->m_pPlayerInfo->m_PlayerFlags&PLAYERFLAG_DEAD))) continue; if(m_pClient->m_LocalClientID == pInfo->m_ClientID || (m_pClient->m_Snap.m_SpecInfo.m_Active && pInfo->m_ClientID == m_pClient->m_Snap.m_SpecInfo.m_SpectatorID)) Classment = j; TeamScoreIDs[j] = i; j++; } } if(Classment < MAX_IDS-1) {} else if(Classment == m_pClient->m_GameInfo.m_aTeamSize[Team] - 1) { HoleSizes[0] = Classment - MAX_IDS-2; RenderScoreIDs[MAX_IDS-3] = -1; RenderScoreIDs[MAX_IDS-2] = TeamScoreIDs[Classment-1]; RenderScoreIDs[MAX_IDS-1] = TeamScoreIDs[Classment]; } else if(Classment == m_pClient->m_GameInfo.m_aTeamSize[Team] - 2) { HoleSizes[0] = Classment - MAX_IDS-3; RenderScoreIDs[MAX_IDS-4] = -1; RenderScoreIDs[MAX_IDS-3] = TeamScoreIDs[Classment-1]; RenderScoreIDs[MAX_IDS-2] = TeamScoreIDs[Classment]; RenderScoreIDs[MAX_IDS-1] = TeamScoreIDs[Classment+1]; } else if(Classment == m_pClient->m_GameInfo.m_aTeamSize[Team] - 3) { HoleSizes[0] = Classment - MAX_IDS-4; RenderScoreIDs[MAX_IDS-5] = -1; RenderScoreIDs[MAX_IDS-4] = TeamScoreIDs[Classment-1]; RenderScoreIDs[MAX_IDS-3] = TeamScoreIDs[Classment]; RenderScoreIDs[MAX_IDS-2] = TeamScoreIDs[Classment+1]; RenderScoreIDs[MAX_IDS-1] = TeamScoreIDs[Classment+2]; } else if(Classment < m_pClient->m_GameInfo.m_aTeamSize[Team] - 3) { HoleSizes[0] = Classment - MAX_IDS-4; RenderScoreIDs[MAX_IDS-5] = -1; RenderScoreIDs[MAX_IDS-4] = TeamScoreIDs[Classment-1]; RenderScoreIDs[MAX_IDS-3] = TeamScoreIDs[Classment]; RenderScoreIDs[MAX_IDS-2] = TeamScoreIDs[Classment+1]; HoleSizes[1] = m_pClient->m_GameInfo.m_aTeamSize[Team] - Classment - 2; RenderScoreIDs[MAX_IDS-1] = -2; } } } else // Normal scoreboard { for(int RenderDead = 0; RenderDead < 2; ++RenderDead) { for(int i = 0; i < MAX_CLIENTS && NumRenderScoreIDs < MAX_IDS; i++) { // make sure that we render the correct team const CGameClient::CPlayerInfoItem *pInfo = &m_pClient->m_Snap.m_aInfoByScore[i]; if(!pInfo->m_pPlayerInfo || m_pClient->m_aClients[pInfo->m_ClientID].m_Team != Team || (!RenderDead && (pInfo->m_pPlayerInfo->m_PlayerFlags&PLAYERFLAG_DEAD)) || (RenderDead && !(pInfo->m_pPlayerInfo->m_PlayerFlags&PLAYERFLAG_DEAD))) continue; RenderScoreIDs[NumRenderScoreIDs] = i; NumRenderScoreIDs++; } } } for(int i = 0 ; i < NumRenderScoreIDs ; i++) { if(RenderScoreIDs[i] >= 0) { const CGameClient::CPlayerInfoItem *pInfo = &m_pClient->m_Snap.m_aInfoByScore[RenderScoreIDs[i]]; bool RenderDead = pInfo->m_pPlayerInfo->m_PlayerFlags&PLAYERFLAG_DEAD; float ColorAlpha = RenderDead ? 0.5f : 1.0f; TextRender()->TextColor(1.0f, 1.0f, 1.0f, ColorAlpha); // color for text vec3 TextColor = vec3(1.0f, 1.0f, 1.0f); vec4 OutlineColor(0.0f, 0.0f, 0.0f, 0.3f); const bool HighlightedLine = m_pClient->m_LocalClientID == pInfo->m_ClientID || (Snap.m_SpecInfo.m_Active && pInfo->m_ClientID == Snap.m_SpecInfo.m_SpectatorID); // background so it's easy to find the local player or the followed one in spectator mode if(HighlightedLine) { CUIRect Rect = {x, y, w, LineHeight}; RenderTools()->DrawRoundRect(&Rect, vec4(1.0f, 1.0f, 1.0f, 0.75f*ColorAlpha), 5.0f); // make color for own entry black TextColor = vec3(0.0f, 0.0f, 0.0f); OutlineColor = vec4(1.0f, 1.0f, 1.0f, 0.25f); } else OutlineColor = vec4(0.0f, 0.0f, 0.0f, 0.3f); // set text color TextRender()->TextColor(TextColor.r, TextColor.g, TextColor.b, ColorAlpha); TextRender()->TextOutlineColor(OutlineColor.r, OutlineColor.g, OutlineColor.b, OutlineColor.a); // ping TextRender()->TextColor(TextColor.r, TextColor.g, TextColor.b, 0.5f*ColorAlpha); str_format(aBuf, sizeof(aBuf), "%d", clamp(pInfo->m_pPlayerInfo->m_Latency, 0, 999)); tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f); TextRender()->SetCursor(&Cursor, PingOffset+PingLength-tw, y+Spacing, FontSize, TEXTFLAG_RENDER); Cursor.m_LineWidth = PingLength; TextRender()->TextEx(&Cursor, aBuf, -1); TextRender()->TextColor(TextColor.r, TextColor.g, TextColor.b, ColorAlpha); // country flag const vec4 CFColor(1, 1, 1, 0.75f * ColorAlpha); m_pClient->m_pCountryFlags->Render(m_pClient->m_aClients[pInfo->m_ClientID].m_Country, &CFColor, CountryFlagOffset, y + 3.0f, 30.0f, LineHeight-5.0f); // flag if(m_pClient->m_GameInfo.m_GameFlags&GAMEFLAG_FLAGS && m_pClient->m_Snap.m_pGameDataFlag && (m_pClient->m_Snap.m_pGameDataFlag->m_FlagCarrierRed == pInfo->m_ClientID || m_pClient->m_Snap.m_pGameDataFlag->m_FlagCarrierBlue == pInfo->m_ClientID)) { Graphics()->BlendNormal(); Graphics()->TextureSet(g_pData->m_aImages[IMAGE_GAME].m_Id); Graphics()->QuadsBegin(); RenderTools()->SelectSprite(m_pClient->m_aClients[pInfo->m_ClientID].m_Team==TEAM_RED ? SPRITE_FLAG_BLUE : SPRITE_FLAG_RED, SPRITE_FLAG_FLIP_X); float Size = LineHeight; IGraphics::CQuadItem QuadItem(TeeOffset+4.0f, y-2.0f-Spacing/2.0f, Size/2.0f, Size); Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); } // avatar if(RenderDead) { Graphics()->BlendNormal(); Graphics()->TextureSet(g_pData->m_aImages[IMAGE_DEADTEE].m_Id); Graphics()->QuadsBegin(); if(m_pClient->m_GameInfo.m_GameFlags&GAMEFLAG_TEAMS) { vec4 Color = m_pClient->m_pSkins->GetColorV4(m_pClient->m_pSkins->GetTeamColor(true, 0, m_pClient->m_aClients[pInfo->m_ClientID].m_Team, SKINPART_BODY), false); Graphics()->SetColor(Color.r, Color.g, Color.b, Color.a); } IGraphics::CQuadItem QuadItem(TeeOffset+TeeLength/2 - 10*TeeSizeMod, y-2.0f+Spacing, 20*TeeSizeMod, 20*TeeSizeMod); Graphics()->QuadsDrawTL(&QuadItem, 1); Graphics()->QuadsEnd(); } else { CTeeRenderInfo TeeInfo = m_pClient->m_aClients[pInfo->m_ClientID].m_RenderInfo; TeeInfo.m_Size = 20*TeeSizeMod; RenderTools()->RenderTee(CAnimState::GetIdle(), &TeeInfo, EMOTE_NORMAL, vec2(1.0f, 0.0f), vec2(TeeOffset+TeeLength/2, y+LineHeight/2+Spacing)); } // TODO: make an eye icon or something if(RenderDead && pInfo->m_pPlayerInfo->m_PlayerFlags&PLAYERFLAG_WATCHING) TextRender()->TextColor(1.0f, 1.0f, 0.0f, ColorAlpha); // id if(g_Config.m_ClShowUserId) { TextRender()->SetCursor(&Cursor, NameOffset+TeeLength-IdSize+Spacing, y+Spacing, FontSize, TEXTFLAG_RENDER); RenderTools()->DrawClientID(TextRender(), &Cursor, pInfo->m_ClientID); } // name TextRender()->SetCursor(&Cursor, NameOffset+TeeLength, y+Spacing, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); Cursor.m_LineWidth = NameLength-TeeLength; TextRender()->TextEx(&Cursor, m_pClient->m_aClients[pInfo->m_ClientID].m_aName, str_length(m_pClient->m_aClients[pInfo->m_ClientID].m_aName)); // ready / watching if(ReadyMode && (pInfo->m_pPlayerInfo->m_PlayerFlags&PLAYERFLAG_READY)) { if(HighlightedLine) TextRender()->TextOutlineColor(0.0f, 0.1f, 0.0f, 0.5f); TextRender()->TextColor(0.1f, 1.0f, 0.1f, ColorAlpha); TextRender()->SetCursor(&Cursor, Cursor.m_X, y+Spacing, FontSize, TEXTFLAG_RENDER); TextRender()->TextEx(&Cursor, "\xE2\x9C\x93", str_length("\xE2\x9C\x93")); } TextRender()->TextColor(TextColor.r, TextColor.g, TextColor.b, ColorAlpha); TextRender()->TextOutlineColor(OutlineColor.r, OutlineColor.g, OutlineColor.b, OutlineColor.a); // clan tw = TextRender()->TextWidth(0, FontSize, m_pClient->m_aClients[pInfo->m_ClientID].m_aClan, -1, -1.0f); TextRender()->SetCursor(&Cursor, ClanOffset+ClanLength/2-tw/2, y+Spacing, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); Cursor.m_LineWidth = ClanLength; TextRender()->TextEx(&Cursor, m_pClient->m_aClients[pInfo->m_ClientID].m_aClan, -1); // K TextRender()->TextColor(TextColor.r, TextColor.g, TextColor.b, 0.5f*ColorAlpha); str_format(aBuf, sizeof(aBuf), "%d", clamp(m_aPlayerStats[pInfo->m_ClientID].m_Kills, 0, 999)); tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f); TextRender()->SetCursor(&Cursor, KillOffset+KillLength/2-tw/2, y+Spacing, FontSize, TEXTFLAG_RENDER); Cursor.m_LineWidth = KillLength; TextRender()->TextEx(&Cursor, aBuf, -1); // D str_format(aBuf, sizeof(aBuf), "%d", clamp(m_aPlayerStats[pInfo->m_ClientID].m_Deaths, 0, 999)); tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f); TextRender()->SetCursor(&Cursor, DeathOffset+DeathLength/2-tw/2, y+Spacing, FontSize, TEXTFLAG_RENDER); Cursor.m_LineWidth = DeathLength; TextRender()->TextEx(&Cursor, aBuf, -1); // score TextRender()->TextColor(TextColor.r, TextColor.g, TextColor.b, ColorAlpha); str_format(aBuf, sizeof(aBuf), "%d", clamp(pInfo->m_pPlayerInfo->m_Score, -999, 999)); tw = TextRender()->TextWidth(0, FontSize, aBuf, -1, -1.0f); TextRender()->SetCursor(&Cursor, ScoreOffset+ScoreLength/2-tw/2, y+Spacing, FontSize, TEXTFLAG_RENDER); Cursor.m_LineWidth = ScoreLength; TextRender()->TextEx(&Cursor, aBuf, -1); y += LineHeight; } else { int HoleSize = HoleSizes[-1-RenderScoreIDs[i]]; TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); TextRender()->SetCursor(&Cursor, NameOffset+TeeLength, y+Spacing, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); Cursor.m_LineWidth = NameLength; char aBuf[64], aBuf2[64]; str_format(aBuf, sizeof(aBuf), Localize("%d other players"), HoleSize); str_format(aBuf2, sizeof(aBuf2), "\xe2\x8b\x85\xe2\x8b\x85\xe2\x8b\x85 %s", aBuf); TextRender()->TextEx(&Cursor, aBuf2, -1); y += LineHeight; } } TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); TextRender()->TextOutlineColor(0.0f, 0.0f, 0.0f, 0.3f); return HeadlineHeight+LineHeight*(m_PlayerLines+1); }