diff --git a/data/icons/info.png b/data/icons/info.png new file mode 100644 index 000000000..fee86dfce Binary files /dev/null and b/data/icons/info.png differ diff --git a/datasrc/content.py b/datasrc/content.py index 452368671..67e049330 100644 --- a/datasrc/content.py +++ b/datasrc/content.py @@ -234,6 +234,7 @@ image_guibuttons = Image("guibuttons", "gui_buttons.png") image_guiicons = Image("guiicons", "gui_icons.png") image_menuicons = Image("menuicons", "icons/menu.png") image_toolicons = Image("toolicons", "icons/tools.png") +image_infoicons = Image("infoicons", "icons/info.png") container.images.Add(image_null) container.images.Add(image_game) @@ -253,6 +254,7 @@ container.images.Add(Image("no_skinpart", "no_skinpart.png")) container.images.Add(Image("hlpicker", "hlpicker.png")) container.images.Add(image_menuicons) container.images.Add(image_toolicons) +container.images.Add(image_infoicons) container.pickups.Add(Pickup("health")) container.pickups.Add(Pickup("armor")) @@ -277,6 +279,7 @@ set_guibuttons = SpriteSet("guibuttons", image_guibuttons, 12, 4) set_guiicons = SpriteSet("guiicons", image_guiicons, 8, 2) set_menuicons = SpriteSet("menuicons", image_menuicons, 2, 2) set_toolicons = SpriteSet("toolicons", image_toolicons, 4, 2) +set_infoicons = SpriteSet("infoicons", image_infoicons, 1, 2) container.spritesets.Add(set_particles) container.spritesets.Add(set_game) @@ -294,6 +297,7 @@ container.spritesets.Add(set_guibuttons) container.spritesets.Add(set_guiicons) container.spritesets.Add(set_menuicons) container.spritesets.Add(set_toolicons) +container.spritesets.Add(set_infoicons) container.sprites.Add(Sprite("part_slice", set_particles, 0,0,1,1)) container.sprites.Add(Sprite("part_ball", set_particles, 1,0,1,1)) @@ -458,6 +462,9 @@ container.sprites.Add(Sprite("tool_edit_b", set_toolicons, 2,1,1,1)) container.sprites.Add(Sprite("tool_x_a", set_toolicons, 3,0,1,1)) container.sprites.Add(Sprite("tool_x_b", set_toolicons, 3,1,1,1)) +container.sprites.Add(Sprite("info_a", set_infoicons, 0,0,1,1)) +container.sprites.Add(Sprite("info_b", set_infoicons, 0,1,1,1)) + anim = Animation("base") anim.body.frames.Add(AnimKeyframe(0, 0, -4, 0)) diff --git a/src/game/client/components/menus.h b/src/game/client/components/menus.h index e49bdc3bf..ce5ff8573 100644 --- a/src/game/client/components/menus.h +++ b/src/game/client/components/menus.h @@ -324,6 +324,7 @@ class CMenus : public CComponent { OVERLAY_SERVERINFO=0, OVERLAY_HEADERINFO, + OVERLAY_PLAYERSINFO, }; int m_Type; diff --git a/src/game/client/components/menus_browser.cpp b/src/game/client/components/menus_browser.cpp index 87566b613..42d77eb7e 100644 --- a/src/game/client/components/menus_browser.cpp +++ b/src/game/client/components/menus_browser.cpp @@ -20,7 +20,7 @@ CMenus::CColumn CMenus::ms_aCols[] = { {COL_FLAG, -1, " ", -1, 87.0f, 0, {0}, {0}}, // Localize - these strings are localized within CLocConstString - {COL_NAME, IServerBrowser::SORT_NAME, "Name", 0, 300.0f, 0, {0}, {0}}, + {COL_NAME, IServerBrowser::SORT_NAME, "Server", 0, 300.0f, 0, {0}, {0}}, {COL_GAMETYPE, IServerBrowser::SORT_GAMETYPE, "Type", 1, 70.0f, 0, {0}, {0}}, {COL_MAP, IServerBrowser::SORT_MAP, "Map", 1, 100.0f, 0, {0}, {0}}, {COL_PLAYERS, IServerBrowser::SORT_NUMPLAYERS, "Players", 1, 60.0f, 0, {0}, {0}}, @@ -321,6 +321,16 @@ int CMenus::DoBrowserEntry(const void *pID, CUIRect *pRect, const CServerInfo *p } else if(ID == COL_PLAYERS) { + // handle mouse over + if(UI()->MouseInside(&Button)) + { + // overlay + SetOverlay(CInfoOverlay::OVERLAY_PLAYERSINFO, UI()->MouseX(), UI()->MouseY(), pEntry); + + // rect + RenderTools()->DrawUIRect(&Button, vec4(0.973f, 0.863f, 0.207, 1.0f), CUI::CORNER_ALL, 5.0f); + } + TextRender()->TextColor(TextBaseColor.r, TextBaseColor.g, TextBaseColor.b, TextAplpha); if(g_Config.m_BrFilterSpectators) @@ -336,24 +346,33 @@ int CMenus::DoBrowserEntry(const void *pID, CUIRect *pRect, const CServerInfo *p { int Ping = pEntry->m_Latency; - vec4 StartColor; - vec4 EndColor; - float MixVal; - if(Ping <= 125) + vec4 Color; + if(Selected || Inside) { - StartColor = vec4(0.0f, 1.0f, 0.0f, TextAplpha); - EndColor = vec4(1.0f, 1.0f, 0.0f, TextAplpha); - - MixVal = (Ping-50.0f)/75.0f; + Color = vec4(TextBaseColor.r, TextBaseColor.g, TextBaseColor.b, TextAplpha); } else { - StartColor = vec4(1.0f, 1.0f, 0.0f, TextAplpha); - EndColor = vec4(1.0f, 0.0f, 0.0f, TextAplpha); - - MixVal = (Ping-125.0f)/75.0f; + vec4 StartColor; + vec4 EndColor; + float MixVal; + if(Ping <= 125) + { + StartColor = vec4(0.0f, 1.0f, 0.0f, TextAplpha); + EndColor = vec4(1.0f, 1.0f, 0.0f, TextAplpha); + + MixVal = (Ping-50.0f)/75.0f; + } + else + { + StartColor = vec4(1.0f, 1.0f, 0.0f, TextAplpha); + EndColor = vec4(1.0f, 0.0f, 0.0f, TextAplpha); + + MixVal = (Ping-125.0f)/75.0f; + } + Color = mix(StartColor, EndColor, MixVal); } - vec4 Color = mix(StartColor, EndColor, MixVal); + str_format(aTemp, sizeof(aTemp), "%d", pEntry->m_Latency); TextRender()->TextColor(Color.r, Color.g, Color.b, Color.a); Button.y += 2.0f; @@ -418,8 +437,9 @@ bool CMenus::RenderFilterHeader(CUIRect View, int FilterIndex) View.VSplitLeft(Spacing, 0, &View); View.VSplitRight((ButtonHeight+Spacing)*4.0f, &View, &EditButtons); + View.VSplitLeft(20.0f, 0, &View); // little space View.y += 2.0f; - UI()->DoLabel(&View, pFilter->Name(), ButtonHeight*ms_FontmodHeight*0.8f, 0); + UI()->DoLabel(&View, pFilter->Name(), ButtonHeight*ms_FontmodHeight*0.8f, -1); /*if(pFilter->Custom() <= CBrowserFilter::FILTER_ALL) UI()->DoLabel(&View, pFilter->Name(), 12.0f, -1); @@ -542,6 +562,137 @@ void CMenus::RenderServerbrowserOverlay() RenderServerbrowserServerDetail(View, pInfo); } + else if(Type == CInfoOverlay::OVERLAY_PLAYERSINFO) + { + const CServerInfo *pInfo = (CServerInfo*)m_InfoOverlay.m_pData; + + CUIRect Screen = *UI()->Screen(); + float ButtonHeight = 20.0f; + + TextRender()->TextOutlineColor(1.0f, 1.0f, 1.0f, 0.25f); + TextRender()->TextColor(0.0f, 0.0f, 0.0f, 1.0f); + + if(pInfo && pInfo->m_NumClients) + { + // get position + CUIRect View = { m_InfoOverlay.m_X+25.0f, m_InfoOverlay.m_Y, 250.0f, pInfo->m_NumClients*ButtonHeight }; + if(View.x+View.w > Screen.w-5.0f) + { + View.y += 25.0f; + View.x -= View.x+View.w-Screen.w+5.0f; + } + if(View.y+View.h >= 590.0f) + View.y -= View.y+View.h - 590.0f; + + // render background + RenderTools()->DrawUIRect(&View, vec4(1.0f, 1.0f, 1.0f, 0.75f), CUI::CORNER_ALL, 6.0f); + + CUIRect ServerScoreBoard = View; + CTextCursor Cursor; + const float FontSize = 12.0f; + for (int i = 0; i < pInfo->m_NumClients; i++) + { + CUIRect Name, Clan, Score, Flag; + ServerScoreBoard.HSplitTop(ButtonHeight, &Name, &ServerScoreBoard); + if(UI()->DoButtonLogic(&pInfo->m_aClients[i], "", 0, &Name)) + { + if(pInfo->m_aClients[i].m_FriendState == IFriends::FRIEND_PLAYER) + m_pClient->Friends()->RemoveFriend(pInfo->m_aClients[i].m_aName, pInfo->m_aClients[i].m_aClan); + else + m_pClient->Friends()->AddFriend(pInfo->m_aClients[i].m_aName, pInfo->m_aClients[i].m_aClan); + FriendlistOnUpdate(); + Client()->ServerBrowserUpdate(); + } + + vec4 Colour = pInfo->m_aClients[i].m_FriendState == IFriends::FRIEND_NO ? vec4(1.0f, 1.0f, 1.0f, (i%2+1)*0.05f) : + vec4(0.5f, 1.0f, 0.5f, 0.15f+(i%2+1)*0.05f); + RenderTools()->DrawUIRect(&Name, Colour, CUI::CORNER_ALL, 4.0f); + Name.VSplitLeft(5.0f, 0, &Name); + Name.VSplitLeft(30.0f, &Score, &Name); + Name.VSplitRight(34.0f, &Name, &Flag); + Flag.HMargin(4.0f, &Flag); + Name.VSplitRight(40.0f, &Name, &Clan); + + // score + if(pInfo->m_aClients[i].m_Player) + { + char aTemp[16]; + str_format(aTemp, sizeof(aTemp), "%d", pInfo->m_aClients[i].m_Score); + TextRender()->SetCursor(&Cursor, Score.x, Score.y+(Score.h-FontSize)/4.0f, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); + Cursor.m_LineWidth = Score.w; + TextRender()->TextEx(&Cursor, aTemp, -1); + } + + // name + TextRender()->SetCursor(&Cursor, Name.x, Name.y+(Name.h-FontSize)/4.0f, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); + Cursor.m_LineWidth = Name.w; + const char *pName = pInfo->m_aClients[i].m_aName; + if(g_Config.m_BrFilterString[0]) + { + // highlight the parts that matches + const char *s = str_find_nocase(pName, g_Config.m_BrFilterString); + if(s) + { + TextRender()->TextEx(&Cursor, pName, (int)(s-pName)); + TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1.0f); + TextRender()->TextEx(&Cursor, s, str_length(g_Config.m_BrFilterString)); + TextRender()->TextColor(0.0f, 0.0f, 0.0f, 1.0f); + TextRender()->TextEx(&Cursor, s+str_length(g_Config.m_BrFilterString), -1); + } + else + TextRender()->TextEx(&Cursor, pName, -1); + } + else + TextRender()->TextEx(&Cursor, pName, -1); + + // clan + TextRender()->SetCursor(&Cursor, Clan.x, Clan.y+(Clan.h-FontSize)/4.0f, FontSize, TEXTFLAG_RENDER|TEXTFLAG_STOP_AT_END); + Cursor.m_LineWidth = Clan.w; + const char *pClan = pInfo->m_aClients[i].m_aClan; + if(g_Config.m_BrFilterString[0]) + { + // highlight the parts that matches + const char *s = str_find_nocase(pClan, g_Config.m_BrFilterString); + if(s) + { + TextRender()->TextEx(&Cursor, pClan, (int)(s-pClan)); + TextRender()->TextColor(0.4f, 0.4f, 1.0f, 1.0f); + TextRender()->TextEx(&Cursor, s, str_length(g_Config.m_BrFilterString)); + TextRender()->TextColor(0.0f, 0.0f, 0.0f, 1.0f); + TextRender()->TextEx(&Cursor, s+str_length(g_Config.m_BrFilterString), -1); + } + else + TextRender()->TextEx(&Cursor, pClan, -1); + } + else + TextRender()->TextEx(&Cursor, pClan, -1); + + // flag + vec4 Color(1.0f, 1.0f, 1.0f, 0.75f); + m_pClient->m_pCountryFlags->Render(pInfo->m_aClients[i].m_Country, &Color, Flag.x, Flag.y, Flag.w, Flag.h); + } + } + else + { + CUIRect View = { m_InfoOverlay.m_X+25.0f, m_InfoOverlay.m_Y, 150.0f, ButtonHeight }; + if(View.x+View.w > Screen.w-5.0f) + { + View.y += 25.0f; + View.x -= View.x+View.w-Screen.w+5.0f; + } + if(View.y+View.h >= 590.0f) + View.y -= View.y+View.h - 590.0f; + + // render background + RenderTools()->DrawUIRect(&View, vec4(1.0f, 1.0f, 1.0f, 0.75f), CUI::CORNER_ALL, 6.0f); + + View.y += 2.0f; + UI()->DoLabel(&View, Localize("no players"), View.h*ms_FontmodHeight*0.8f, 0); + } + + TextRender()->TextColor(1.0f, 1.0f, 1.0f, 1.0f); + TextRender()->TextOutlineColor(0.0f, 0.0f, 0.0f, 0.3f); + } // deactivate it m_InfoOverlayActive = false; @@ -549,8 +700,7 @@ void CMenus::RenderServerbrowserOverlay() void CMenus::RenderServerbrowserServerList(CUIRect View) { - CUIRect Headers; - CUIRect Status; + CUIRect Headers, Status, InfoButton; float SpacingH = 2.0f; float ButtonHeight = 20.0f; @@ -561,7 +711,7 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) View.HSplitTop(ms_ListheaderHeight, &Headers, &View); View.HSplitBottom(ButtonHeight*3.0f+SpacingH*2.0f, &View, &Status); - Headers.VSplitRight(20.0f, &Headers, 0); + Headers.VSplitRight(ms_ListheaderHeight, &Headers, &InfoButton); // split for info button // do layout for(int i = 0; i < NUM_COLS; i++) @@ -634,6 +784,16 @@ void CMenus::RenderServerbrowserServerList(CUIRect View) } } + // do info icon at the end of the list header + { + InfoButton.Margin(2.0f, &InfoButton); + static int s_InfoButton = 0; + if(DoButton_SpriteCleanID(&s_InfoButton, IMAGE_INFOICONS, SPRITE_INFO_A, &InfoButton)) + { + + } + } + // split scrollbar from view CUIRect Scroll; View.VSplitRight(20.0f, &View, &Scroll);