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