/* Copyright (c) 2002-2012 Croteam Ltd. This program is free software; you can redistribute it and/or modify it under the terms of version 2 of the GNU General Public License as published by the Free Software Foundation This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "SeriousSam/StdH.h" #include #include #include #include "MGServerList.h" #include "SeriousSam/GUI/Components/MGEdit.h" extern CSoundData *_psdSelect; extern CSoundData *_psdPress; FLOATaabbox2D GetBoxPartHoriz(const FLOATaabbox2D &box, FLOAT fMin, FLOAT fMax) { FLOAT fBoxMin = box.Min()(1); FLOAT fBoxSize = box.Size()(1); return FLOATaabbox2D( FLOAT2D(fBoxMin + fBoxSize*fMin, box.Min()(2)), FLOAT2D(fBoxMin + fBoxSize*fMax, box.Max()(2))); } void PrintInBox(CDrawPort *pdp, PIX pixI, PIX pixJ, PIX pixSizeI, CTString str, COLOR col) { str = str.Undecorated(); PIX pixCharSize = pdp->dp_pixTextCharSpacing + pdp->dp_FontData->fd_pixCharWidth; str.TrimRight(pixSizeI / pixCharSize); // print text pdp->PutText(str, pixI, pixJ, col); } CMGServerList::CMGServerList() { mg_iSelected = 0; mg_iFirstOnScreen = 0; mg_ctOnScreen = 10; mg_pixMinI = 0; mg_pixMaxI = 0; mg_pixListMinJ = 0; mg_pixListStepJ = 0; mg_pixDragJ = -1; mg_iDragLine = -1; mg_pixMouseDrag = -1; // by default, sort by ping, best on top mg_iSort = 2; mg_bSortDown = FALSE; } void CMGServerList::AdjustFirstOnScreen(void) { INDEX ctSessions = _lhServers.Count(); mg_iSelected = Clamp(mg_iSelected, 0L, ClampDn(ctSessions - 1L, 0L)); mg_iFirstOnScreen = Clamp(mg_iFirstOnScreen, 0L, ClampDn(ctSessions - mg_ctOnScreen, 0L)); if (mg_iSelected= mg_iFirstOnScreen + mg_ctOnScreen) { mg_iFirstOnScreen = ClampDn(mg_iSelected - mg_ctOnScreen + 1L, 0L); } } BOOL _iSort = 0; BOOL _bSortDown = FALSE; int CompareSessions(const void *pv0, const void *pv1) { const CNetworkSession &ns0 = **(const CNetworkSession **)pv0; const CNetworkSession &ns1 = **(const CNetworkSession **)pv1; int iResult = 0; switch (_iSort) { case 0: iResult = stricmp(ns0.ns_strSession, ns1.ns_strSession); break; case 1: iResult = stricmp(ns0.ns_strWorld, ns1.ns_strWorld); break; case 2: iResult = Sgn(ns0.ns_tmPing - ns1.ns_tmPing); break; case 3: iResult = Sgn(ns0.ns_ctPlayers - ns1.ns_ctPlayers); break; case 4: iResult = stricmp(ns0.ns_strGameType, ns1.ns_strGameType); break; case 5: iResult = stricmp(ns0.ns_strMod, ns1.ns_strMod); break; case 6: iResult = stricmp(ns0.ns_strVer, ns1.ns_strVer); break; } if (iResult == 0) { // make sure we always have unique order when resorting return stricmp(ns0.ns_strAddress, ns1.ns_strAddress);; } return _bSortDown ? -iResult : iResult; } extern CMGButton mgServerColumn[7]; extern CMGEdit mgServerFilter[7]; void SortAndFilterServers(void) { {FORDELETELIST(CNetworkSession, ns_lnNode, _lhServers, itns) { delete &*itns; }} {FOREACHINLIST(CNetworkSession, ns_lnNode, _pNetwork->ga_lhEnumeratedSessions, itns) { CNetworkSession &ns = *itns; extern CTString _strServerFilter[7]; if (_strServerFilter[0] != "" && !ns.ns_strSession.Matches("*" + _strServerFilter[0] + "*")) continue; if (_strServerFilter[1] != "" && !ns.ns_strWorld.Matches("*" + _strServerFilter[1] + "*")) continue; if (_strServerFilter[2] != "") { char strCompare[3] = { 0, 0, 0 }; int iPing = 0; _strServerFilter[2].ScanF("%2[<>=]%d", strCompare, &iPing); if (strcmp(strCompare, "<") == 0 && !(int(ns.ns_tmPing * 1000)< iPing)) continue; if (strcmp(strCompare, "<=") == 0 && !(int(ns.ns_tmPing * 1000) <= iPing)) continue; if (strcmp(strCompare, ">") == 0 && !(int(ns.ns_tmPing * 1000)> iPing)) continue; if (strcmp(strCompare, ">=") == 0 && !(int(ns.ns_tmPing * 1000) >= iPing)) continue; if (strcmp(strCompare, "=") == 0 && !(int(ns.ns_tmPing * 1000) == iPing)) continue; } if (_strServerFilter[3] != "") { char strCompare[3] = { 0, 0, 0 }; int iPlayers = 0; _strServerFilter[3].ScanF("%2[<>=]%d", strCompare, &iPlayers); if (strcmp(strCompare, "<") == 0 && !(ns.ns_ctPlayers< iPlayers)) continue; if (strcmp(strCompare, "<=") == 0 && !(ns.ns_ctPlayers <= iPlayers)) continue; if (strcmp(strCompare, ">") == 0 && !(ns.ns_ctPlayers> iPlayers)) continue; if (strcmp(strCompare, ">=") == 0 && !(ns.ns_ctPlayers >= iPlayers)) continue; if (strcmp(strCompare, "=") == 0 && !(ns.ns_ctPlayers == iPlayers)) continue; } if (_strServerFilter[4] != "" && !ns.ns_strGameType.Matches("*" + _strServerFilter[4] + "*")) continue; if (_strServerFilter[5] != "" && !ns.ns_strMod.Matches("*" + _strServerFilter[5] + "*")) continue; if (_strServerFilter[6] != "" && !ns.ns_strVer.Matches("*" + _strServerFilter[6] + "*")) continue; CNetworkSession *pnsNew = new CNetworkSession; pnsNew->Copy(*itns); _lhServers.AddTail(pnsNew->ns_lnNode); } } _lhServers.Sort(CompareSessions, offsetof(CNetworkSession, ns_lnNode)); } void CMGServerList::Render(CDrawPort *pdp) { _iSort = mg_iSort; _bSortDown = mg_bSortDown; SortAndFilterServers(); SetFontSmall(pdp); BOOL bFocusedBefore = mg_bFocused; mg_bFocused = FALSE; PIXaabbox2D box = FloatBoxToPixBox(pdp, mg_boxOnScreen); COLOR col = GetCurrentColor(); PIX pixDPSizeI = pdp->GetWidth(); PIX pixDPSizeJ = pdp->GetHeight(); PIX pixCharSizeI = pdp->dp_pixTextCharSpacing + pdp->dp_FontData->fd_pixCharWidth; PIX pixCharSizeJ = pdp->dp_pixTextLineSpacing + pdp->dp_FontData->fd_pixCharHeight + 1; PIX pixLineSize = 1; PIX pixSliderSizeI = 10; PIX pixOuterMargin = 20; INDEX ctSessions = _lhServers.Count(); INDEX iSession = 0; INDEX ctColumns[7]; {for (INDEX i = 0; iDrawLine(apixSeparatorI[i], pixTopJ, apixSeparatorI[i], pixBottomJ, col | CT_OPAQUE); } pdp->DrawLine(apixSeparatorI[0], pixTopJ, apixSeparatorI[8], pixTopJ, col | CT_OPAQUE); pdp->DrawLine(apixSeparatorI[0], pixListTopJ - pixLineSize, apixSeparatorI[8], pixListTopJ - pixLineSize, col | CT_OPAQUE); pdp->DrawLine(apixSeparatorI[0], pixBottomJ, apixSeparatorI[8], pixBottomJ, col | CT_OPAQUE); PIXaabbox2D boxHandle = GetScrollBarHandleBox(); pdp->Fill(boxHandle.Min()(1) + 2, boxHandle.Min()(2) + 2, boxHandle.Size()(1) - 3, boxHandle.Size()(2) - 3, col | CT_OPAQUE); PIX pixJ = pixTopJ + pixLineSize * 2 + 1; mg_ctOnScreen = ctSessionsOnScreen; AdjustFirstOnScreen(); if (_lhServers.Count() == 0) { if (_pNetwork->ga_strEnumerationStatus != "") { mg_bFocused = TRUE; COLOR colItem = GetCurrentColor(); PrintInBox(pdp, apixSeparatorI[0] + pixCharSizeI, pixListTopJ + pixCharSizeJ + pixLineSize + 1, apixSeparatorI[1] - apixSeparatorI[0], TRANS("searching..."), colItem); } } else { FOREACHINLIST(CNetworkSession, ns_lnNode, _lhServers, itns) { CNetworkSession &ns = *itns; if (iSession= mg_iFirstOnScreen + ctSessionsOnScreen) { iSession++; continue; } PIX pixJ = pixListTopJ + (iSession - mg_iFirstOnScreen)*pixCharSizeJ + pixLineSize + 1; mg_bFocused = bFocusedBefore&&iSession == mg_iSelected; COLOR colItem = GetCurrentColor(); if (ns.ns_strVer != _SE_VER_STRING) { colItem = MulColors(colItem, 0xA0A0A0FF); } CTString strPing(0, "%4d", INDEX(ns.ns_tmPing * 1000)); CTString strPlayersCt(0, "%2d/%2d", ns.ns_ctPlayers, ns.ns_ctMaxPlayers); CTString strMod = ns.ns_strMod; if (strMod == "") { strMod = "SeriousSam"; } PrintInBox(pdp, apixSeparatorI[0] + pixCharSizeI / 2, pixJ, apixSeparatorI[1] - apixSeparatorI[0] - pixCharSizeI, ns.ns_strSession, colItem); PrintInBox(pdp, apixSeparatorI[1] + pixCharSizeI / 2, pixJ, apixSeparatorI[2] - apixSeparatorI[1] - pixCharSizeI, TranslateConst(ns.ns_strWorld), colItem); PrintInBox(pdp, apixSeparatorI[2] + pixCharSizeI / 2, pixJ, apixSeparatorI[3] - apixSeparatorI[2] - pixCharSizeI, strPing, colItem); PrintInBox(pdp, apixSeparatorI[3] + pixCharSizeI / 2, pixJ, apixSeparatorI[4] - apixSeparatorI[3] - pixCharSizeI, strPlayersCt, colItem); PrintInBox(pdp, apixSeparatorI[4] + pixCharSizeI / 2, pixJ, apixSeparatorI[5] - apixSeparatorI[4] - pixCharSizeI, TranslateConst(ns.ns_strGameType), colItem); PrintInBox(pdp, apixSeparatorI[5] + pixCharSizeI / 2, pixJ, apixSeparatorI[6] - apixSeparatorI[5] - pixCharSizeI, TranslateConst(strMod), colItem); PrintInBox(pdp, apixSeparatorI[6] + pixCharSizeI / 2, pixJ, apixSeparatorI[7] - apixSeparatorI[6] - pixCharSizeI, ns.ns_strVer, colItem); iSession++; } } mg_bFocused = bFocusedBefore; } static INDEX SliderPixToIndex(PIX pixOffset, INDEX iVisible, INDEX iTotal, PIXaabbox2D boxFull) { FLOAT fSize = ClampUp(FLOAT(iVisible) / iTotal, 1.0f); PIX pixFull = boxFull.Size()(2); PIX pixSize = PIX(pixFull*fSize); if (pixSize >= boxFull.Size()(2)) { return 0; } return (iTotal*pixOffset) / pixFull; } static PIXaabbox2D GetSliderBox(INDEX iFirst, INDEX iVisible, INDEX iTotal, PIXaabbox2D boxFull) { if (iTotal <= 0) { return boxFull; } FLOAT fSize = ClampUp(FLOAT(iVisible) / iTotal, 1.0f); PIX pixFull = boxFull.Size()(2); PIX pixSize = PIX(pixFull*fSize); pixSize = ClampDn(pixSize, boxFull.Size()(1)); PIX pixTop = pixFull*(FLOAT(iFirst) / iTotal) + boxFull.Min()(2); PIX pixI0 = boxFull.Min()(1); PIX pixI1 = boxFull.Max()(1); return PIXaabbox2D(PIX2D(pixI0, pixTop), PIX2D(pixI1, pixTop + pixSize)); } PIXaabbox2D CMGServerList::GetScrollBarFullBox(void) { return PIXaabbox2D(PIX2D(mg_pixSBMinI, mg_pixSBMinJ), PIX2D(mg_pixSBMaxI, mg_pixSBMaxJ)); } PIXaabbox2D CMGServerList::GetScrollBarHandleBox(void) { return GetSliderBox(mg_iFirstOnScreen, mg_ctOnScreen, _lhServers.Count(), GetScrollBarFullBox()); } void CMGServerList::OnMouseOver(PIX pixI, PIX pixJ) { mg_pixMouseI = pixI; mg_pixMouseJ = pixJ; if (!(GetKeyState(VK_LBUTTON) & 0x8000)) { mg_pixDragJ = -1; } BOOL bInSlider = (pixI >= mg_pixSBMinI && pixI <= mg_pixSBMaxI && pixJ >= mg_pixSBMinJ && pixJ <= mg_pixSBMaxJ); if (mg_pixDragJ >= 0 && bInSlider) { PIX pixDelta = pixJ - mg_pixDragJ; INDEX ctSessions = _lhServers.Count(); INDEX iWantedLine = mg_iDragLine + SliderPixToIndex(pixDelta, mg_ctOnScreen, ctSessions, GetScrollBarFullBox()); mg_iFirstOnScreen = Clamp(iWantedLine, 0L, ClampDn(ctSessions - mg_ctOnScreen, 0L)); mg_iSelected = Clamp(mg_iSelected, mg_iFirstOnScreen, mg_iFirstOnScreen + mg_ctOnScreen - 1L); // AdjustFirstOnScreen(); return; } // if some server is selected if (pixI >= mg_pixMinI && pixI <= mg_pixMaxI) { INDEX iOnScreen = (pixJ - mg_pixListMinJ) / mg_pixListStepJ; if (iOnScreen >= 0 && iOnScreen=mg_pixHeaderMinJ && mg_pixMouseJ<=mg_pixHeaderMidJ && mg_pixMouseI>=mg_pixHeaderI[0] && mg_pixMouseI<=mg_pixHeaderI[7]) { INDEX iNewSort = mg_iSort; if (mg_pixMouseI<=mg_pixHeaderI[1]) { iNewSort = 0; } else if (mg_pixMouseI<=mg_pixHeaderI[2]) { iNewSort = 1; } else if (mg_pixMouseI<=mg_pixHeaderI[3]) { iNewSort = 2; } else if (mg_pixMouseI<=mg_pixHeaderI[4]) { iNewSort = 3; } else if (mg_pixMouseI<=mg_pixHeaderI[5]) { iNewSort = 4; } else if (mg_pixMouseI<=mg_pixHeaderI[6]) { iNewSort = 5; } else if (mg_pixMouseI<=mg_pixHeaderI[7]) { iNewSort = 6; } if (iNewSort==mg_iSort) { mg_bSortDown = !mg_bSortDown; } else { mg_bSortDown = FALSE; } mg_iSort = iNewSort; break; } else */if (mg_pixMouseDrag >= 0) { mg_pixDragJ = mg_pixMouseDrag; mg_iDragLine = mg_iFirstOnScreen; break; } case VK_RETURN: PlayMenuSound(_psdPress); IFeel_PlayEffect("Menu_press"); {INDEX i = 0; FOREACHINLIST(CNetworkSession, ns_lnNode, _lhServers, itns) { if (i == mg_iSelected) { char strAddress[256]; int iPort; itns->ns_strAddress.ScanF("%200[^:]:%d", &strAddress, &iPort); _pGame->gam_strJoinAddress = strAddress; _pShell->SetINDEX("net_iPort", iPort); extern void StartSelectPlayersMenuFromServers(void); StartSelectPlayersMenuFromServers(); return TRUE; } i++; }} break; default: return FALSE; } return TRUE; } void CMGServerList::OnSetFocus(void) { mg_bFocused = TRUE; } void CMGServerList::OnKillFocus(void) { mg_bFocused = FALSE; }