warning fix only
[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, columnPlayersOrigin, float, 0)
20         ATTRIB(NexuizServerList, columnPlayersSize, float, 0)
21
22         ATTRIB(NexuizServerList, selectedServer, string, string_null) // to restore selected server when needed
23         METHOD(NexuizServerList, setSelected, void(entity, float))
24         METHOD(NexuizServerList, setSortOrder, void(entity, float, float))
25         ATTRIB(NexuizServerList, filterShowEmpty, float, 1)
26         ATTRIB(NexuizServerList, filterShowFull, float, 1)
27         ATTRIB(NexuizServerList, filterString, string, string_null)
28         ATTRIB(NexuizServerList, controlledTextbox, entity, NULL)
29         ATTRIB(NexuizServerList, nextRefreshTime, float, 0)
30         METHOD(NexuizServerList, refreshServerList, void(entity, float)) // refresh mode: 0 = just reparametrize, 1 = send new requests, 2 = clear
31         ATTRIB(NexuizServerList, needsRefresh, float, 1)
32         METHOD(NexuizServerList, focusEnter, void(entity))
33         METHOD(NexuizServerList, positionSortButton, void(entity, entity, float, float, string, void(entity, entity)))
34         ATTRIB(NexuizServerList, sortButton1, entity, NULL)
35         ATTRIB(NexuizServerList, sortButton2, entity, NULL)
36         ATTRIB(NexuizServerList, sortButton3, entity, NULL)
37         ATTRIB(NexuizServerList, sortButton4, entity, NULL)
38         ATTRIB(NexuizServerList, connectButton, entity, NULL)
39         ATTRIB(NexuizServerList, currentSortOrder, float, 0)
40         ATTRIB(NexuizServerList, currentSortField, float, -1)
41         ATTRIB(NexuizServerList, lastClickedServer, float, -1)
42         ATTRIB(NexuizServerList, lastClickedTime, float, 0)
43 ENDCLASS(NexuizServerList)
44 entity makeNexuizServerList();
45 void ServerList_Connect_Click(entity btn, entity me);
46 void ServerList_ShowEmpty_Click(entity box, entity me);
47 void ServerList_ShowFull_Click(entity box, entity me);
48 void ServerList_Filter_Change(entity box, entity me);
49 #endif
50
51 #ifdef IMPLEMENTATION
52 float SLIST_FIELD_CNAME;
53 float SLIST_FIELD_PING;
54 float SLIST_FIELD_GAME;
55 float SLIST_FIELD_MOD;
56 float SLIST_FIELD_MAP;
57 float SLIST_FIELD_NAME;
58 float SLIST_FIELD_MAXPLAYERS;
59 float SLIST_FIELD_NUMPLAYERS;
60 float SLIST_FIELD_NUMHUMANS;
61 float SLIST_FIELD_NUMBOTS;
62 float SLIST_FIELD_PROTOCOL;
63 float SLIST_FIELD_FREESLOTS;
64 void ServerList_UpdateFieldIDs()
65 {
66         SLIST_FIELD_CNAME = gethostcacheindexforkey( "cname" );
67         SLIST_FIELD_PING = gethostcacheindexforkey( "ping" );
68         SLIST_FIELD_GAME = gethostcacheindexforkey( "game" );
69         SLIST_FIELD_MOD = gethostcacheindexforkey( "mod" );
70         SLIST_FIELD_MAP = gethostcacheindexforkey( "map" );
71         SLIST_FIELD_NAME = gethostcacheindexforkey( "name" );
72         SLIST_FIELD_MAXPLAYERS = gethostcacheindexforkey( "maxplayers" );
73         SLIST_FIELD_NUMPLAYERS = gethostcacheindexforkey( "numplayers" );
74         SLIST_FIELD_NUMHUMANS = gethostcacheindexforkey( "numhumans" );
75         SLIST_FIELD_NUMBOTS = gethostcacheindexforkey( "numbots" );
76         SLIST_FIELD_PROTOCOL = gethostcacheindexforkey( "protocol" );
77         SLIST_FIELD_FREESLOTS = gethostcacheindexforkey( "freeslots" );
78 }
79
80 entity makeNexuizServerList()
81 {
82         entity me;
83         me = spawnNexuizServerList();
84         me.configureNexuizServerList(me);
85         return me;
86 }
87 void configureNexuizServerListNexuizServerList(entity me)
88 {
89         me.configureNexuizListBox(me);
90
91         ServerList_UpdateFieldIDs();
92
93         me.nItems = 0;
94 }
95 void setSelectedNexuizServerList(entity me, float i)
96 {
97         float save;
98         save = me.selectedItem;
99         setSelectedListBox(me, i);
100         /*
101         if(me.selectedItem == save)
102                 return;
103         */
104         if(me.nItems == 0)
105                 return;
106         if(gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT) != me.nItems)
107                 return; // sorry, it would be wrong
108         if(me.selectedServer)
109                 strunzone(me.selectedServer);
110         me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
111 }
112 void refreshServerListNexuizServerList(entity me, float mode)
113 {
114         // 0: just reparametrize
115         // 1: also ask for new servers
116         // 2: clear
117         //print("refresh of type ", ftos(mode), "\n");
118         /* if(mode == 2) // borken
119         {
120                 // clear list
121                 localcmd("net_slist\n");
122                 me.needsRefresh = 1; // net_slist kills sort order, so we need to restore it later
123         }
124         else */
125         {
126                 float m;
127                 m = SLIST_MASK_AND - 1;
128                 resethostcachemasks();
129                 if(!me.filterShowFull)
130                         sethostcachemasknumber(++m, SLIST_FIELD_FREESLOTS, 1, SLIST_TEST_GREATEREQUAL);
131                 if(!me.filterShowEmpty)
132                         sethostcachemasknumber(++m, SLIST_FIELD_NUMHUMANS, 1, SLIST_TEST_GREATEREQUAL);
133                 m = SLIST_MASK_OR - 1;
134                 if(me.filterString)
135                 {
136                         sethostcachemaskstring(++m, SLIST_FIELD_NAME, me.filterString, SLIST_TEST_CONTAINS);
137                         sethostcachemaskstring(++m, SLIST_FIELD_MAP, me.filterString, SLIST_TEST_CONTAINS);
138                 }
139                 sethostcachesort(me.currentSortField, me.currentSortOrder < 0);
140                 resorthostcache();
141                 if(mode >= 1)
142                         refreshhostcache();
143         }
144 }
145 void focusEnterNexuizServerList(entity me)
146 {
147         if(time < me.nextRefreshTime)
148         {
149                 //print("sorry, no refresh yet\n");
150                 return;
151         }
152         me.nextRefreshTime = time + 10;
153         me.refreshServerList(me, 1);
154 }
155 void drawNexuizServerList(entity me)
156 {
157         float i, found;
158
159         if(me.currentSortField == -1)
160         {
161                 me.setSortOrder(me, SLIST_FIELD_PING, +1);
162                 me.refreshServerList(me, 2);
163         }
164         else if(me.needsRefresh == 1)
165         {
166                 me.needsRefresh = 2; // delay by one frame to make sure "slist" has been executed
167         }
168         else if(me.needsRefresh == 2)
169         {
170                 me.needsRefresh = 0;
171                 me.refreshServerList(me, 0);
172         }
173
174         me.nItems = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT);
175         me.connectButton.disabled = (me.nItems == 0);
176
177         found = 0;
178         if(me.selectedServer)
179         {
180                 for(i = 0; i < me.nItems; ++i)
181                         if(gethostcachestring(SLIST_FIELD_CNAME, i) == me.selectedServer)
182                         {
183                                 if(i != me.selectedItem)
184                                 {
185                                         me.lastClickedServer = -1;
186                                         me.selectedItem = i;
187                                 }
188                                 found = 1;
189                                 break;
190                         }
191         }
192         if(!found)
193                 if(me.nItems > 0)
194                 {
195                         if(me.selectedItem >= me.nItems)
196                                 me.selectedItem = me.nItems - 1;
197                         if(me.selectedServer)
198                                 strunzone(me.selectedServer);
199                         me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem));
200                 }
201
202         drawListBox(me);
203 }
204 void ServerList_PingSort_Click(entity btn, entity me)
205 {
206         me.setSortOrder(me, SLIST_FIELD_PING, +1);
207 }
208 void ServerList_NameSort_Click(entity btn, entity me)
209 {
210         me.setSortOrder(me, SLIST_FIELD_NAME, -1); // why?
211 }
212 void ServerList_MapSort_Click(entity btn, entity me)
213 {
214         me.setSortOrder(me, SLIST_FIELD_MAP, -1); // why?
215 }
216 void ServerList_PlayerSort_Click(entity btn, entity me)
217 {
218         me.setSortOrder(me, SLIST_FIELD_NUMHUMANS, -1);
219 }
220 void ServerList_Filter_Change(entity box, entity me)
221 {
222         if(me.filterString)
223                 strunzone(me.filterString);
224         if(box.text != "")
225                 me.filterString = strzone(box.text);
226         else
227                 me.filterString = string_null;
228         me.refreshServerList(me, 0);
229 }
230 void ServerList_ShowEmpty_Click(entity box, entity me)
231 {
232         box.setChecked(box, me.filterShowEmpty = !me.filterShowEmpty);
233         me.refreshServerList(me, 0);
234 }
235 void ServerList_ShowFull_Click(entity box, entity me)
236 {
237         box.setChecked(box, me.filterShowFull = !me.filterShowFull);
238         me.refreshServerList(me, 0);
239 }
240 void setSortOrderNexuizServerList(entity me, float field, float direction)
241 {
242         if(me.currentSortField == field)
243                 direction = -me.currentSortOrder;
244         me.currentSortOrder = direction;
245         me.currentSortField = field;
246         me.sortButton1.forcePressed = (field == SLIST_FIELD_PING);
247         me.sortButton2.forcePressed = (field == SLIST_FIELD_NAME);
248         me.sortButton3.forcePressed = (field == SLIST_FIELD_MAP);
249         me.sortButton4.forcePressed = (field == SLIST_FIELD_NUMHUMANS);
250         me.selectedItem = 0;
251         if(me.selectedServer)
252                 strunzone(me.selectedServer);
253         me.selectedServer = string_null;
254         me.refreshServerList(me, 0);
255 }
256 void positionSortButtonNexuizServerList(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc)
257 {
258         vector originInLBSpace, sizeInLBSpace;
259         originInLBSpace = eY * (-me.itemHeight);
260         sizeInLBSpace = eY * me.itemHeight + eX * (1 - me.controlWidth);
261
262         vector originInDialogSpace, sizeInDialogSpace;
263         originInDialogSpace = boxToGlobal(originInLBSpace, me.Container_origin, me.Container_size);
264         sizeInDialogSpace = boxToGlobalSize(sizeInLBSpace, me.Container_size);
265
266         btn.Container_origin_x = originInDialogSpace_x + sizeInDialogSpace_x * theOrigin;
267         btn.Container_size_x   =                         sizeInDialogSpace_x * theSize;
268         btn.setText(btn, theTitle);
269         btn.onClick = theFunc;
270         btn.onClickEntity = me;
271         btn.resized = 1;
272 }
273 void resizeNotifyNexuizServerList(entity me, vector relOrigin, vector relSize, vector absOrigin, vector absSize)
274 {
275         resizeNotifyNexuizListBox(me, relOrigin, relSize, absOrigin, absSize);
276
277         me.realFontSize_y = me.fontSize / (absSize_y * me.itemHeight);
278         me.realFontSize_x = me.fontSize / (absSize_x * (1 - me.controlWidth));
279         me.realUpperMargin = 0.5 * (1 - me.realFontSize_y);
280
281         me.columnPingOrigin = 0;
282         me.columnPingSize = me.realFontSize_x * 4;
283         me.columnMapSize = me.realFontSize_x * 12;
284         me.columnPlayersSize = me.realFontSize_x * 6;
285         me.columnNameSize = 1 - me.columnPlayersSize - me.columnMapSize - me.columnPingSize - 3 * me.realFontSize_x;
286         me.columnNameOrigin = me.columnPingOrigin + me.columnPingSize + me.realFontSize_x;
287         me.columnMapOrigin = me.columnNameOrigin + me.columnNameSize + me.realFontSize_x;
288         me.columnPlayersOrigin = me.columnMapOrigin + me.columnMapSize + me.realFontSize_x;
289
290         me.positionSortButton(me, me.sortButton1, me.columnPingOrigin, me.columnPingSize, "Ping", ServerList_PingSort_Click);
291         me.positionSortButton(me, me.sortButton2, me.columnNameOrigin, me.columnNameSize, "Host name", ServerList_NameSort_Click);
292         me.positionSortButton(me, me.sortButton3, me.columnMapOrigin, me.columnMapSize, "Map", ServerList_MapSort_Click);
293         me.positionSortButton(me, me.sortButton4, me.columnPlayersOrigin, me.columnPlayersSize, "Players", ServerList_PlayerSort_Click);
294
295         float f;
296         f = me.currentSortField;
297         if(f >= 0)
298         {
299                 me.currentSortField = -1;
300                 me.setSortOrder(me, f, me.currentSortOrder); // force resetting the sort order
301         }
302 }
303 void ServerList_Connect_Click(entity btn, entity me)
304 {
305         if(me.nItems > 0)
306                 localcmd("connect ", me.selectedServer, "\n");
307 }
308 void clickListBoxItemNexuizServerList(entity me, float i, vector where)
309 {
310         if(i == me.lastClickedServer)
311                 if(time < me.lastClickedTime + 0.3)
312                 {
313                         // DOUBLE CLICK!
314                         ServerList_Connect_Click(NULL, me);
315                 }
316         me.lastClickedServer = i;
317         me.lastClickedTime = time;
318 }
319 void drawListBoxItemNexuizServerList(entity me, float i, vector absSize, float isSelected)
320 {
321         // layout: Ping, Server name, Map name, NP, TP, MP
322         string s;
323         float p;
324         vector theColor;
325         float theAlpha;
326
327         if(isSelected)
328                 draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED);
329
330         if(gethostcachenumber(SLIST_FIELD_NUMPLAYERS, i) >= gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i))
331                 theAlpha = SKINALPHA_SERVERLIST_FULL;
332         else if not(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i))
333                 theAlpha = SKINALPHA_SERVERLIST_EMPTY;
334         else
335                 theAlpha = 1;
336         
337         p = gethostcachenumber(SLIST_FIELD_PING, i);
338 #define PING_LOW 75
339 #define PING_MED 200
340 #define PING_HIGH 500
341         if(p < PING_LOW)
342                 theColor = SKINCOLOR_SERVERLIST_LOWPING + (SKINCOLOR_SERVERLIST_MEDPING - SKINCOLOR_SERVERLIST_LOWPING) * (p / PING_LOW);
343         else if(p < PING_MED)
344                 theColor = SKINCOLOR_SERVERLIST_MEDPING + (SKINCOLOR_SERVERLIST_HIGHPING - SKINCOLOR_SERVERLIST_MEDPING) * ((p - PING_LOW) / (PING_MED - PING_LOW));
345         else if(p < PING_HIGH)
346         {
347                 theColor = SKINCOLOR_SERVERLIST_HIGHPING;
348                 theAlpha *= 1 + (SKINALPHA_SERVERLIST_HIGHPING - 1) * ((p - PING_MED) / (PING_HIGH - PING_MED));
349         }
350         else
351         {
352                 theColor = eX;
353                 theAlpha *= SKINALPHA_SERVERLIST_HIGHPING;
354         }
355         
356         s = ftos(p);
357         draw_Text(me.realUpperMargin * eY + (me.columnPingSize - draw_TextWidth(s, 0) * me.realFontSize_x) * eX, s, me.realFontSize, theColor, theAlpha, 0);
358         s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_NAME, i), me.columnNameSize / me.realFontSize_x, 0);
359         draw_Text(me.realUpperMargin * eY + me.columnNameOrigin * eX, s, me.realFontSize, theColor, theAlpha, 0);
360         s = draw_TextShortenToWidth(gethostcachestring(SLIST_FIELD_MAP, i), me.columnMapSize / me.realFontSize_x, 0);
361         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);
362         s = strcat(ftos(gethostcachenumber(SLIST_FIELD_NUMHUMANS, i)), "/", ftos(gethostcachenumber(SLIST_FIELD_MAXPLAYERS, i)));
363         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);
364 }
365
366 float keyDownNexuizServerList(entity me, float scan, float ascii, float shift)
367 {
368         if(scan == K_ENTER)
369         {
370                 ServerList_Connect_Click(NULL, me);
371                 return 1;
372         }
373         else if(keyDownListBox(me, scan, ascii, shift))
374                 return 1;
375         else if(!me.controlledTextbox)
376                 return 0;
377         else
378                 return me.controlledTextbox.keyDown(me.controlledTextbox, scan, ascii, shift);
379 }
380 #endif