]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/menu/nexuiz/serverlist.c
only update the bookmark/remove button when really needed
[divverent/nexuiz.git] / data / qcsrc / menu / nexuiz / serverlist.c
1 #ifdef INTERFACE
2 CLASS(NexuizServerList) EXTENDS(NexuizListBox)
3         METHOD(NexuizServerList, configureNexuizServerList, void(entity))
4         ATTRIB(NexuizServerList, rowsPerItem, float, 1)
5         METHOD(NexuizServerList, draw, void(entity))
6         METHOD(NexuizServerList, drawListBoxItem, void(entity, float, vector, float))
7         METHOD(NexuizServerList, clickListBoxItem, void(entity, float, vector))
8         METHOD(NexuizServerList, resizeNotify, void(entity, vector, vector, vector, vector))
9         METHOD(NexuizServerList, keyDown, float(entity, float, float, float))
10
11         ATTRIB(NexuizServerList, realFontSize, vector, '0 0 0')
12         ATTRIB(NexuizServerList, realUpperMargin, float, 0)
13         ATTRIB(NexuizServerList, columnPingOrigin, float, 0)
14         ATTRIB(NexuizServerList, columnPingSize, float, 0)
15         ATTRIB(NexuizServerList, columnNameOrigin, float, 0)
16         ATTRIB(NexuizServerList, columnNameSize, float, 0)
17         ATTRIB(NexuizServerList, columnMapOrigin, float, 0)
18         ATTRIB(NexuizServerList, columnMapSize, float, 0)
19         ATTRIB(NexuizServerList, columnTypeOrigin, float, 0)
20         ATTRIB(NexuizServerList, columnTypeSize, float, 0)
21         ATTRIB(NexuizServerList, columnPlayersOrigin, float, 0)
22         ATTRIB(NexuizServerList, columnPlayersSize, float, 0)
23
24         ATTRIB(NexuizServerList, selectedServer, string, string_null) // to restore selected server when needed
25         METHOD(NexuizServerList, setSelected, void(entity, float))
26         METHOD(NexuizServerList, setSortOrder, void(entity, float, float))
27         ATTRIB(NexuizServerList, filterShowEmpty, float, 1)
28         ATTRIB(NexuizServerList, filterShowFull, float, 1)
29         ATTRIB(NexuizServerList, filterString, string, string_null)
30         ATTRIB(NexuizServerList, controlledTextbox, entity, NULL)
31         ATTRIB(NexuizServerList, ipAddressBox, entity, NULL)
32         ATTRIB(NexuizServerList, favoriteButton, entity, NULL)
33         ATTRIB(NexuizServerList, nextRefreshTime, float, 0)
34         METHOD(NexuizServerList, refreshServerList, void(entity, float)) // refresh mode: 0 = just reparametrize, 1 = send new requests, 2 = clear
35         ATTRIB(NexuizServerList, needsRefresh, float, 1)
36         METHOD(NexuizServerList, focusEnter, void(entity))
37         METHOD(NexuizServerList, positionSortButton, void(entity, entity, float, float, string, void(entity, entity)))
38         ATTRIB(NexuizServerList, sortButton1, entity, NULL)
39         ATTRIB(NexuizServerList, sortButton2, entity, NULL)
40         ATTRIB(NexuizServerList, sortButton3, entity, NULL)
41         ATTRIB(NexuizServerList, sortButton4, entity, NULL)
42         ATTRIB(NexuizServerList, sortButton5, entity, NULL)
43         ATTRIB(NexuizServerList, connectButton, entity, NULL)
44         ATTRIB(NexuizServerList, infoButton, entity, NULL)
45         ATTRIB(NexuizServerList, currentSortOrder, float, 0)
46         ATTRIB(NexuizServerList, currentSortField, float, -1)
47         ATTRIB(NexuizServerList, lastClickedServer, float, -1)
48         ATTRIB(NexuizServerList, lastClickedTime, float, 0)
49
50         ATTRIB(NexuizServerList, ipAddressBoxFocused, float, -1)
51 ENDCLASS(NexuizServerList)
52 entity makeNexuizServerList();
53 void ServerList_Connect_Click(entity btn, entity me);
54 void ServerList_ShowEmpty_Click(entity box, entity me);
55 void ServerList_ShowFull_Click(entity box, entity me);
56 void ServerList_Filter_Change(entity box, entity me);
57 void ServerList_Favorite_Click(entity btn, entity me);
58 void ServerList_Info_Click(entity btn, entity me);
59 #endif
60
61 #ifdef IMPLEMENTATION
62 float SLIST_FIELD_CNAME;
63 float SLIST_FIELD_PING;
64 float SLIST_FIELD_GAME;
65 float SLIST_FIELD_MOD;
66 float SLIST_FIELD_MAP;
67 float SLIST_FIELD_NAME;
68 float SLIST_FIELD_MAXPLAYERS;
69 float SLIST_FIELD_NUMPLAYERS;
70 float SLIST_FIELD_NUMHUMANS;
71 float SLIST_FIELD_NUMBOTS;
72 float SLIST_FIELD_PROTOCOL;
73 float SLIST_FIELD_FREESLOTS;
74 float SLIST_FIELD_PLAYERS;
75 float SLIST_FIELD_QCSTATUS;
76 float SLIST_FIELD_ISFAVORITE;
77 void ServerList_UpdateFieldIDs()
78 {
79         SLIST_FIELD_CNAME = gethostcacheindexforkey( "cname" );
80         SLIST_FIELD_PING = gethostcacheindexforkey( "ping" );
81         SLIST_FIELD_GAME = gethostcacheindexforkey( "game" );
82         SLIST_FIELD_MOD = gethostcacheindexforkey( "mod" );
83         SLIST_FIELD_MAP = gethostcacheindexforkey( "map" );
84         SLIST_FIELD_NAME = gethostcacheindexforkey( "name" );
85         SLIST_FIELD_MAXPLAYERS = gethostcacheindexforkey( "maxplayers" );
86         SLIST_FIELD_NUMPLAYERS = gethostcacheindexforkey( "numplayers" );
87         SLIST_FIELD_NUMHUMANS = gethostcacheindexforkey( "numhumans" );
88         SLIST_FIELD_NUMBOTS = gethostcacheindexforkey( "numbots" );
89         SLIST_FIELD_PROTOCOL = gethostcacheindexforkey( "protocol" );
90         SLIST_FIELD_FREESLOTS = gethostcacheindexforkey( "freeslots" );
91         SLIST_FIELD_PLAYERS = gethostcacheindexforkey( "players" );
92         SLIST_FIELD_QCSTATUS = gethostcacheindexforkey( "qcstatus" );
93         SLIST_FIELD_ISFAVORITE = gethostcacheindexforkey( "isfavorite" );
94 }
95
96 float IsFavorite(string srv)
97 {
98         float i, n;
99         srv = netaddress_resolve(srv, 26000);
100         n = tokenize_console(cvar_string("net_slist_favorites"));
101         for(i = 0; i < n; ++i)
102                 if(srv == netaddress_resolve(argv(i), 26000))
103                         return TRUE;
104         return FALSE;
105 }
106
107 void ToggleFavorite(string srv)
108 {
109         string s, s0, s1, s2, srv_resolved;
110         float i, n;
111         srv_resolved = netaddress_resolve(srv, 26000);
112         s = cvar_string("net_slist_favorites");
113         n = tokenize_console(s);
114         for(i = 0; i < n; ++i)
115                 if(srv_resolved == netaddress_resolve(argv(i), 26000))
116                 {
117                         s0 = s1 = s2 = "";
118                         if(i > 0)
119                                 s0 = substring(s, 0, argv_end_index(i - 1));
120                         if(i < n-1)
121                                 s2 = substring(s, argv_start_index(i + 1), -1);
122                         if(s0 != "" && s2 != "")
123                                 s1 = " ";
124                         print("s0 = >>", s0, "<<\ns1 = >>", s1, "<<\ns2 = >>", s2, "<<\n");
125                         cvar_set("net_slist_favorites", strcat(s0, s1, s2));
126                         return;
127                 }
128         
129         s1 = "";
130         if(s != "")
131                 s1 = " ";
132         cvar_set("net_slist_favorites", strcat(s, " ", srv));
133
134         resorthostcache();
135 }
136
137 entity makeNexuizServerList()
138 {
139         entity me;
140         me = spawnNexuizServerList();
141         me.configureNexuizServerList(me);
142         return me;
143 }
144 void configureNexuizServerListNexuizServerList(entity me)
145 {
146         me.configureNexuizListBox(me);
147
148         ServerList_UpdateFieldIDs();
149
150         me.nItems = 0;
151 }
152 void setSelectedNexuizServerList(entity me, float i)
153 {
154         float save;
155         save = me.selectedItem;
156         setSelectedListBox(me, i);
157         /*
158         if(me.selectedItem == save)
159                 return;
160         */
161         if(me.nItems == 0)
162                 return;
163         if(gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT) != me.nItems)
164                 return; // sorry, it would be wrong
165
166         if(me.selectedServer)
167                 strunzone(me.selectedServer);
168         me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
169
170         me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
171         me.ipAddressBox.cursorPos = strlen(me.selectedServer);
172 }
173 void refreshServerListNexuizServerList(entity me, float mode)
174 {
175         // 0: just reparametrize
176         // 1: also ask for new servers
177         // 2: clear
178         //print("refresh of type ", ftos(mode), "\n");
179         /* if(mode == 2) // borken
180         {
181                 // clear list
182                 localcmd("net_slist\n");
183                 me.needsRefresh = 1; // net_slist kills sort order, so we need to restore it later
184         }
185         else */
186         {
187                 float m, o;
188                 string s, typestr, modstr;
189                 s = me.filterString;
190
191                 m = strstrofs(s, ":", 0);
192                 if(m >= 0)
193                 {
194                         typestr = substring(s, 0, m);
195                         s = substring(s, m + 1, strlen(s) - m - 1);
196                         while(substring(s, 0, 1) == " ")
197                                 s = substring(s, 1, strlen(s) - 1);
198                 }
199                 else
200                         typestr = "";
201
202                 modstr = cvar_string("menu_slist_modfilter");
203
204                 m = SLIST_MASK_AND - 1;
205                 resethostcachemasks();
206                 if(!me.filterShowFull)
207                         sethostcachemasknumber(++m, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL);
208                 if(!me.filterShowEmpty)
209                         sethostcachemasknumber(++m, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
210                 if(typestr != "")
211                         sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(typestr, ":"), SLIST_TEST_STARTSWITH);
212                 if(modstr != "")
213                 {
214                         if(substring(modstr, 0, 1) == "!")
215                                 sethostcachemaskstring(++m, SLIST_FIELD_MOD, substring(modstr, 1, strlen(modstr) - 1), SLIST_TEST_NOTEQUAL);
216                         else
217                                 sethostcachemaskstring(++m, SLIST_FIELD_MOD, modstr, SLIST_TEST_EQUAL);
218                 }
219                 m = SLIST_MASK_OR - 1;
220                 if(s != "")
221                 {
222                         sethostcachemaskstring(++m, SLIST_FIELD_NAME, s, SLIST_TEST_CONTAINS);
223                         sethostcachemaskstring(++m, SLIST_FIELD_MAP, s, SLIST_TEST_CONTAINS);
224                         sethostcachemaskstring(++m, SLIST_FIELD_PLAYERS, s, SLIST_TEST_CONTAINS);
225                         sethostcachemaskstring(++m, SLIST_FIELD_QCSTATUS, strcat(s, ":"), SLIST_TEST_STARTSWITH);
226                 }
227                 o = 2; // favorites first
228                 if(me.currentSortOrder < 0)
229                         o |= 1; // descending
230                 sethostcachesort(me.currentSortField, o);
231                 resorthostcache();
232                 if(mode >= 1)
233                         refreshhostcache();
234         }
235 }
236 void focusEnterNexuizServerList(entity me)
237 {
238         if(time < me.nextRefreshTime)
239         {
240                 //print("sorry, no refresh yet\n");
241                 return;
242         }
243         me.nextRefreshTime = time + 10;
244         me.refreshServerList(me, 1);
245 }
246 void drawNexuizServerList(entity me)
247 {
248         float i, found, owned;
249
250         if(me.currentSortField == -1)
251         {
252                 me.setSortOrder(me, SLIST_FIELD_PING, +1);
253                 me.refreshServerList(me, 2);
254         }
255         else if(me.needsRefresh == 1)
256         {
257                 me.needsRefresh = 2; // delay by one frame to make sure "slist" has been executed
258         }
259         else if(me.needsRefresh == 2)
260         {
261                 me.needsRefresh = 0;
262                 me.refreshServerList(me, 0);
263         }
264
265         owned = ((me.selectedServer == me.ipAddressBox.text) && (me.ipAddressBox.text != ""));
266
267         me.nItems = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
268
269         me.connectButton.disabled = ((me.nItems == 0) && (me.ipAddressBox.text == ""));
270         me.infoButton.disabled = ((me.nItems == 0) || !owned);
271
272         found = 0;
273         if(me.selectedServer)
274         {
275                 for(i = 0; i < me.nItems; ++i)
276                         if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer)
277                         {
278                                 if(i != me.selectedItem)
279                                 {
280                                         me.lastClickedServer = -1;
281                                         me.selectedItem = i;
282                                 }
283                                 found = 1;
284                                 break;
285                         }
286         }
287         if(!found)
288                 if(me.nItems > 0)
289                 {
290                         if(me.selectedItem >= me.nItems)
291                                 me.selectedItem = me.nItems - 1;
292                         if(me.selectedServer)
293                                 strunzone(me.selectedServer);
294                         me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
295                 }
296
297         if(owned)
298         {
299                 me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer);
300                 me.ipAddressBox.cursorPos = strlen(me.selectedServer);
301         }
302
303         if(me.ipAddressBoxFocused != me.ipAddressBox.focused)
304         {
305                 me.ipAddressBoxFocused = me.ipAddressBox.focused;
306                 if(me.ipAddressBoxFocused)
307                 {
308                         if(IsFavorite(me.ipAddressBox.text))
309                                 me.favoriteButton.setText(me.favoriteButton, "Remove");
310                         else
311                                 me.favoriteButton.setText(me.favoriteButton, "Bookmark");
312                 }
313         }
314
315         drawListBox(me);
316 }
317 void ServerList_PingSort_Click(entity btn, entity me)
318 {
319         me.setSortOrder(me, SLIST_FIELD_PING, +1);
320 }
321 void ServerList_NameSort_Click(entity btn, entity me)
322 {
323         me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
324 }
325 void ServerList_MapSort_Click(entity btn, entity me)
326 {
327         me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
328 }
329 void ServerList_PlayerSort_Click(entity btn, entity me)
330 {
331         me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
332 }
333 void ServerList_TypeSort_Click(entity btn, entity me)
334 {
335         string s, t;
336         float i, m;
337         s = me.filterString;
338         m = strstrofs(s, ":", 0);
339         if(m >= 0)
340         {
341                 s = substring(s, 0, m);
342                 while(substring(s, m+1, 1) == " ") // skip spaces
343                         ++m;
344         }
345         else
346                 s = "";
347
348         for(i = 1; ; ++i) // 20 modes ought to be enough for anyone
349         {
350                 t = GametypeNameFromType(i);
351                 if(i > 1)
352                         if(t == GametypeNameFromType(0)) // it repeats (default case)
353                         {
354                                 // no type was found
355                                 // choose the first one
356                                 s = t;
357                                 break;
358                         }
359                 if(s == GametypeNameFromType(i))
360                 {
361                         // the type was found
362                         // choose the next one
363                         s = GametypeNameFromType(i + 1);
364                         if(s == GametypeNameFromType(0))
365                                 s = "";
366                         break;
367                 }
368         }
369
370         if(s != "")
371                 s = strcat(s, ":");
372         s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1));
373
374         me.controlledTextbox.setText(me.controlledTextbox, s);
375         me.controlledTextbox.keyDown(me.controlledTextbox, K_END, 0, 0);
376         me.controlledTextbox.keyUp(me.controlledTextbox, K_END, 0, 0);
377         //ServerList_Filter_Change(me.controlledTextbox, me);
378 }
379 void ServerList_Filter_Change(entity box, entity me)
380 {
381         if(me.filterString)
382                 strunzone(me.filterString);
383         if(box.text != "")
384                 me.filterString = strzone(box.text);
385         else
386                 me.filterString = string_null;
387         me.refreshServerList(me, 0);
388
389         me.ipAddressBox.setText(me.ipAddressBox, "");
390         me.ipAddressBox.cursorPos = 0;
391 }
392 void ServerList_ShowEmpty_Click(entity box, entity me)
393 {
394         box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
395         me.refreshServerList(me, 0);
396
397         me.ipAddressBox.setText(me.ipAddressBox, "");
398         me.ipAddressBox.cursorPos = 0;
399 }
400 void ServerList_ShowFull_Click(entity box, entity me)
401 {
402         box.setChecked(box, me.filterShowFull = !me.filterShowFull);
403         me.refreshServerList(me, 0);
404
405         me.ipAddressBox.setText(me.ipAddressBox, "");
406         me.ipAddressBox.cursorPos = 0;
407 }
408 void setSortOrderNexuizServerList(entity me, float field, float direction)
409 {
410         if(me.currentSortField == field)
411                 direction = -me.currentSortOrder;
412         me.currentSortOrder = direction;
413         me.currentSortField = field;
414         me.sortButton1.forcePressed = (field == SLIST_FIELD_PING);
415         me.sortButton2.forcePressed = (field == SLIST_FIELD_NAME);
416         me.sortButton3.forcePressed = (field == SLIST_FIELD_MAP);
417         me.sortButton4.forcePressed = 0;
418         me.sortButton5.forcePressed = (field == SLIST_FIELD_NUMHUMANS);
419         me.selectedItem = 0;
420         if(me.selectedServer)
421                 strunzone(me.selectedServer);
422         me.selectedServer = string_null;
423         me.refreshServerList(me, 0);
424 }
425 void positionSortButtonNexuizServerList(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
426 {
427         vector originInLBSpace, sizeInLBSpace;
428         originInLBSpace = eY * (-me.itemHeight);
429         sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
430
431         vector originInDialogSpace, sizeInDialogSpace;
432         originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
433         sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
434
435         btn.Container_origin_x = originInDialogSpace_x + sizeInDialogSpace_x * theOrigin;
436         btn.Container_size_x   =                         sizeInDialogSpace_x * theSize;
437         btn.setText(btn, theTitle);
438         btn.onClick = theFunc;
439         btn.onClickEntity = me;
440         btn.resized = 1;
441 }
442 void resizeNotifyNexuizServerList(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
443 {
444         resizeNotifyNexuizListBox(me, relOrigin, relSize, absOrigin, absSize);
445
446         me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight);
447         me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth));
448         me.realUpperMargin = 0.5 * (1 - me.realFontSize_y);
449
450         me.columnPingOrigin = 0;
451         me.columnPingSize = me.realFontSize_x * 4;
452         me.columnMapSize = me.realFontSize_x * 12;
453         me.columnTypeSize = me.realFontSize_x * 4;
454         me.columnPlayersSize = me.realFontSize_x * 6;
455         me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - me.columnTypeSize - 4 * me.realFontSize_x;
456         me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize_x;
457         me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize_x;
458         me.columnTypeOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize_x;
459         me.columnPlayersOrigin = me.columnTypeOrigin + me.columnTypeSize + me.realFontSize_x;
460
461         me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, "Ping", ServerList_PingSort_Click);
462         me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, "Host name", ServerList_NameSort_Click);
463         me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, "Map", ServerList_MapSort_Click);
464         me.positionSortButton(me, me.sortButton4, me.columnTypeOrigin, me.columnTypeSize, "Type", ServerList_TypeSort_Click);
465         me.positionSortButton(me, me.sortButton5, me.columnPlayersOrigin, me.columnPlayersSize, "Players", ServerList_PlayerSort_Click);
466
467         float f;
468         f = me.currentSortField;
469         if(f >= 0)
470         {
471                 me.currentSortField = -1;
472                 me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
473         }
474 }
475 void ServerList_Connect_Click(entity btn, entity me)
476 {
477         if(me.ipAddressBox.text == "")
478                 localcmd("connect ", me.selectedServer, "\n");
479         else
480                 localcmd("connect ", me.ipAddressBox.text, "\n");
481 }
482 void ServerList_Favorite_Click(entity btn, entity me)
483 {
484         string ipstr;
485         ipstr = netaddress_resolve(me.ipAddressBox.text, 26000);
486         if(ipstr != "")
487         {
488                 ToggleFavorite(me.ipAddressBox.text);
489                 me.ipAddressBoxFocused = -1;
490         }
491 }
492 void ServerList_Info_Click(entity btn, entity me)
493 {
494         main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
495         DialogOpenButton_Click(me, main.serverInfoDialog);
496 }
497 void clickListBoxItemNexuizServerList(entity me, float i, vector where)
498 {
499         if(i == me.lastClickedServer)
500                 if(time < me.lastClickedTime + 0.3)
501                 {
502                         // DOUBLE CLICK!
503                         ServerList_Connect_Click(NULL, me);
504                 }
505         me.lastClickedServer = i;
506         me.lastClickedTime = time;
507 }
508 void drawListBoxItemNexuizServerList(entity me, float i, vector absSize, float isSelected)
509 {
510         // layout: Ping, Server name, Map name, NP, TP, MP
511         string s;
512         float p;
513         vector theColor;
514         float theAlpha;
515
516         if(isSelected)
517                 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
518
519         if(gethostcachenumber(SLIST_FIELD_NUMPLAYERS, i) >= gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i))
520                 theAlpha = SKINALPHA_SERVERLIST_FULL;
521         else if not(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i))
522                 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
523         else
524                 theAlpha = 1;
525
526         p = gethostcachenumber(SLIST_FIELD_PING, i);
527 #define PING_LOW 75
528 #define PING_MED 200
529 #define PING_HIGH 500
530         if(p < PING_LOW)
531                 theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * (p / PING_LOW);
532         else if(p < PING_MED)
533                 theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((p - PING_LOW) / (PING_MED - PING_LOW));
534         else if(p < PING_HIGH)
535         {
536                 theColor = SKINCOLOR_SERVERLIST_HIGHPING;
537                 theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((p - PING_MED) / (PING_HIGH - PING_MED));
538         }
539         else
540         {
541                 theColor = eX;
542                 theAlpha *= SKINALPHA_SERVERLIST_HIGHPING;
543         }
544
545         if(gethostcachenumber(SLIST_FIELD_ISFAVORITE, i))
546         {
547                 theColor = theColor * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINCOLOR_SERVERLIST_FAVORITE * SKINALPHA_SERVERLIST_FAVORITE;
548                 theAlpha = theAlpha * (1 - SKINALPHA_SERVERLIST_FAVORITE) + SKINALPHA_SERVERLIST_FAVORITE;
549         }
550
551         s = ftos(p);
552         draw_Text(me.realUpperMargin * eY + (me.columnPingSize - draw_TextWidth(s, 0) * me.realFontSize_x) * eX, s, me.realFontSize, theColor, theAlpha, 0);
553         s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize / me.realFontSize_x, 0);
554         draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
555         s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize / me.realFontSize_x, 0);
556         draw_Text(me.realUpperMargin * eY + (me.columnMapOrigin + (me.columnMapSize - draw_TextWidth(s, 0) * me.realFontSize_x) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
557         s = gethostcachestring(SLIST_FIELD_QCSTATUS, i);
558         p = strstrofs(s, ":", 0);
559         if(p >= 0)
560                 s = substring(s, 0, p);
561         else
562                 s = "";
563         s = draw_TextShortenToWidth(s, me.columnMapSize / me.realFontSize_x, 0);
564         draw_Text(me.realUpperMargin * eY + (me.columnTypeOrigin + (me.columnTypeSize - draw_TextWidth(s, 0) * me.realFontSize_x) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
565         s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i)));
566         draw_Text(me.realUpperMargin * eY + (me.columnPlayersOrigin + (me.columnPlayersSize - draw_TextWidth(s, 0) * me.realFontSize_x) * 0.5) * eX, s, me.realFontSize, theColor, theAlpha, 0);
567 }
568
569 float keyDownNexuizServerList(entity me, float scan, float ascii, float shift)
570 {
571         float i;
572         vector org, sz;
573
574         org = boxToGlobal(eY * (me.selectedItem * me.itemHeight - me.scrollPos), me.origin, me.size);
575         sz = boxToGlobalSize(eY * me.itemHeight + eX * (1 - me.controlWidth), me.size);
576
577         if(scan == K_ENTER)
578         {
579                 ServerList_Connect_Click(NULL, me);
580                 return 1;
581         }
582         else if(scan == K_MOUSE2 || scan == K_SPACE)
583         {
584                 main.serverInfoDialog.loadServerInfo(main.serverInfoDialog, me.selectedItem);
585                 DialogOpenButton_Click_withCoords(me, main.serverInfoDialog, org, sz);
586         }
587         else if(scan == K_INS || scan == K_MOUSE3)
588         {
589                 i = me.selectedItem;
590                 if(i < me.nItems)
591                 {
592                         ToggleFavorite(me.selectedServer);
593                         me.ipAddressBoxFocused = -1;
594                 }
595         }
596         else if(keyDownListBox(me, scan, ascii, shift))
597                 return 1;
598         else if(!me.controlledTextbox)
599                 return 0;
600         else
601                 return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);
602 }
603 #endif