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