]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/mapvoting.qc
The PanelHud. Now in Xonotic git.
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / mapvoting.qc
1 float mv_num_maps;
2
3 float mv_active;
4 string mv_maps[MAPVOTE_COUNT];
5 string mv_pics[MAPVOTE_COUNT];
6 string mv_pk3[MAPVOTE_COUNT];
7 float mv_preview[MAPVOTE_COUNT];
8 float mv_votes[MAPVOTE_COUNT];
9 entity mv_pk3list;
10 float mv_abstain;
11 float mv_ownvote;
12 float mv_detail;
13 float mv_timeout;
14 float mv_maps_mask;
15
16 vector mv_mousepos;
17 float mv_selection;
18
19 string MapVote_FormatMapItem(float id, string map, float count, float maxwidth, vector fontsize)
20 {
21         string pre, post;
22         pre = strcat(ftos(id+1), ". ");
23         if(mv_detail)
24         {
25                 if(count == 1)
26                         post = strcat(" (1 vote)");
27                 else
28                         post = strcat(" (", ftos(count), " votes)");
29         }
30         else
31                 post = "";
32         maxwidth -= stringwidth(pre, FALSE, fontsize) + stringwidth(post, FALSE, fontsize);
33         map = textShortenToWidth(map, maxwidth, fontsize, stringwidth_nocolors);
34         return strcat(pre, map, post);
35 }
36
37 vector MapVote_RGB(float id)
38 {
39         if(id == mv_ownvote)
40                 return '0 1 0';
41         else if (id == mv_selection)
42                 return '1 1 0';
43         else
44                 return '1 1 1';
45 }
46
47 void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, string pic, float count, float id)
48 {
49         vector img_size;
50         vector rgb;
51         string label;
52         float text_size;
53         
54         isize -= sbar_fontsize_y; // respect the text when calculating the image size
55
56         rgb = MapVote_RGB(id);
57         
58         img_size_y = isize;
59         img_size_x = isize / 0.75; // 4:3 x can be stretched easily, height is defined in isize
60
61         drawfont = sbar_font;
62         pos_y = pos_y + img_size_y;
63         
64         label = MapVote_FormatMapItem(id, map, count, tsize, sbar_fontsize);
65
66         text_size = stringwidth(label, false, sbar_fontsize);
67         
68         pos_x -= text_size*0.5;
69         drawstring(pos, label, sbar_fontsize, rgb, 1, DRAWFLAG_NORMAL);
70         
71         pos_x = pos_x + text_size*0.5 - img_size_x*0.5;
72         pos_y = pos_y - img_size_y;
73
74         pos += hud_border_thickness * '1 1 0';
75         img_size -= (hud_border_thickness * 2) * '1 1 0';
76         if(pic == "")
77         {
78                 drawfill(pos, img_size, '.5 .5 .5', .7, DRAWFLAG_NORMAL);
79         }
80         else
81         {
82                 drawpic(pos, pic, img_size, '1 1 1', 1, DRAWFLAG_NORMAL);
83         }
84         
85         drawpic(pos + '1 0 0', strcat("gfx/hud/num_", ftos(id+1)), (img_size_y / 5) * '1 1 0', '1 1 1', 0.6, DRAWFLAG_NORMAL);
86
87         if(id == mv_ownvote || pic == "")
88         {
89                 drawborderlines(hud_border_thickness, pos, img_size, rgb, 1, DRAWFLAG_NORMAL);
90                 drawpic(pos + '1 0 0', strcat("gfx/hud/num_", ftos(id+1)), (img_size_y / 5) * '1 1 0', rgb, 0.6, DRAWFLAG_NORMAL);
91         }
92         else
93         {
94                 drawborderlines(hud_border_thickness, pos, img_size, '0 0 0', 1, DRAWFLAG_NORMAL);
95                 drawpic(pos + '1 0 0', strcat("gfx/hud/num_", ftos(id+1)), (img_size_y / 5) * '1 1 0', '1 1 1', 0.6, DRAWFLAG_NORMAL);
96         }
97 }
98
99 void MapVote_DrawAbstain(vector pos, float isize, float tsize, float count, float id)
100 {
101         vector rgb;
102         float text_size;
103         string label;
104         
105         rgb = MapVote_RGB(id);
106
107         drawfont = sbar_font;
108         pos_y = pos_y + sbar_fontsize_y;
109         
110         label = MapVote_FormatMapItem(id, "Don't care", count, tsize, sbar_fontsize);
111
112         text_size = stringwidth(label, false, sbar_fontsize);
113         
114         pos_x -= text_size*0.5;
115         drawstring(pos, label, sbar_fontsize, rgb, 1, DRAWFLAG_NORMAL);
116 }
117
118 vector MapVote_GridVec(vector gridspec, float i, float m)
119 {
120         float r;
121         r = mod(i, m);
122         return
123                 '1 0 0' * (gridspec_x * r)
124                 +
125                 '0 1 0' * (gridspec_y * (i - r) / m);
126 }
127
128 float MapVote_Selection(vector topleft, vector cellsize, float rows, float columns)
129 {
130         float cell;
131         float c, r;
132
133         cell = -1;
134
135         for (r = 0; r < rows; ++r)
136                 for (c = 0; c < columns; ++c)
137                 {
138                         if (mv_mousepos_x >= topleft_x + cellsize_x *  c &&
139                                 mv_mousepos_x <= topleft_x + cellsize_x * (c + 1) &&
140                                 mv_mousepos_y >= topleft_y + cellsize_y *  r &&
141                                 mv_mousepos_y <= topleft_y + cellsize_y * (r + 1))
142                         {
143                                 cell = r * columns + c;
144                                 break;
145                         }
146                 }
147
148         if (cell >= mv_num_maps)
149                 cell = -1;
150
151         if (mv_abstain && cell < 0)
152                 return mv_num_maps;
153
154         return cell;
155 }
156
157 void MapVote_Draw()
158 {
159         string map;
160         float i, tmp;
161         vector pos;
162         float isize;
163         float center;
164         float columns, rows;
165         float tsize;
166         vector dist;
167
168         if(!mv_active)
169                 return;
170         
171         mv_mousepos = mv_mousepos + getmousepos();
172
173         mv_mousepos_x = bound(0, mv_mousepos_x, vid_conwidth);
174         mv_mousepos_y = bound(0, mv_mousepos_y, vid_conheight);
175
176         center = (vid_conwidth - 1)/2;
177         xmin = vid_conwidth*0.05; // 5% border must suffice
178         xmax = vid_conwidth - xmin;
179         ymin = 20;
180         i = cvar("con_chatpos"); //*cvar("con_chatsize");
181         if(i < 0)
182                 ymax = vid_conheight + (i - cvar("con_chat")) * cvar("con_chatsize");
183         if(i >= 0 || ymax < (vid_conheight*0.5))
184                 ymax = vid_conheight - ymin;
185
186         drawfont = sbar_bigfont;
187         sbar_fontsize = Sbar_GetFontsize("sbar_fontsize");
188
189         pos_y = ymin;
190         pos_z = 0;
191         //pos_x = center - stringwidth("Vote for a map", false) * 0.5 * 24;
192         pos_x = center - stringwidth("Vote for a map", false, '12 0 0');
193         drawstring(pos, "Vote for a map", '24 24 0', '1 1 1', 1, DRAWFLAG_NORMAL);
194         pos_y += 26;
195
196         i = ceil(max(0, mv_timeout - time));
197         map = strcat(ftos(i), " seconds left");
198         //pos_x = center - stringwidth(map, false) * 0.5 * 16;
199         pos_x = center - stringwidth(map, false, '8 0 0');
200         drawstring(pos, map, '16 16 0', '0 1 0', 1, DRAWFLAG_NORMAL);
201         pos_y += 22;
202         pos_x = xmin;
203
204         drawfont = sbar_font;
205         
206         // base for multi-column stuff...
207         ymin = pos_y;
208         if(mv_abstain)
209                 mv_num_maps -= 1;
210         
211         if(mv_num_maps > 3)
212         {
213                 columns = 3;
214         } else {
215                 columns = mv_num_maps;
216         }
217         rows = ceil(mv_num_maps / columns);
218
219         dist_x = (xmax - xmin) / columns;
220         dist_y = (ymax - pos_y) / rows;
221         tsize = dist_x - 10;
222         isize = min(dist_y - 10, 0.75 * tsize);
223
224         mv_selection = MapVote_Selection(pos, dist, rows, columns);
225
226         pos_x += (xmax - xmin) / (2 * columns);
227         pos_y += (dist_y - isize) / 2;
228         ymax -= isize;
229
230         for(i = 0; i < mv_num_maps; ++i)
231         {
232                 tmp = mv_votes[i]; // FTEQCC bug: too many array accesses in the function call screw it up
233                 if(tmp < 0)
234                         continue;
235                 map = mv_maps[i];
236                 if(mv_preview[i])
237                         MapVote_DrawMapItem(pos + MapVote_GridVec(dist, i, columns), isize, tsize, map, mv_pics[i], tmp, i);
238                 else
239                         MapVote_DrawMapItem(pos + MapVote_GridVec(dist, i, columns), isize, tsize, map, "", tmp, i);
240         }
241
242         if(mv_abstain)
243                 ++mv_num_maps;
244         
245         if(mv_abstain && i < mv_num_maps) {
246                 tmp = mv_votes[i];
247                 pos_y = ymax + isize - sbar_fontsize_y;
248                 pos_x = (xmax+xmin)*0.5;
249                 MapVote_DrawAbstain(pos, isize, xmax - xmin, tmp, i);
250         }
251
252         drawpic(mv_mousepos, strcat("gfx/menu/", cvar_string("menu_skin"), "/cursor.tga"), '32 32 0', '1 1 1', hud_alpha_fg, DRAWFLAG_NORMAL);
253 }
254
255 void Cmd_MapVote_MapDownload(float argc)
256 {
257         float id;
258         entity pak;
259
260         if(argc != 2 || !mv_pk3list)
261         {
262                 print("mv_mapdownload: ^3You're not supposed to use this command on your own!\n");
263                 return;
264         }
265         
266         id = stof(argv(1));
267         for(pak = mv_pk3list; pak; pak = pak.chain)
268                 if(pak.sv_entnum == id)
269                         break;
270         
271         if(!pak || pak.sv_entnum != id) {
272                 print("^1Error:^7 Couldn't find pak index.\n");
273                 return;
274         }
275
276         //print(strcat("^3Adding: ", ftos(id), " - ", pak.message, " - "));
277         
278         if(PreviewExists(pak.message))
279         {
280                 mv_preview[id] = true;
281                 //print("^2Found...\n");
282                 return;
283         } else {
284                 print("Requesting preview...\n");
285                 localcmd(strcat("\ncmd mv_getpic ", ftos(id), "\n"));
286         }
287 }
288
289 void MapVote_CheckPK3(string pic, string pk3, float id)
290 {
291         entity pak;
292         pak = spawn();
293         pak.netname = pk3;
294         pak.message = pic;
295         pak.sv_entnum = id;
296         
297         pak.chain = mv_pk3list;
298         mv_pk3list = pak;
299         
300         if(pk3 != "")
301         {
302                 localcmd(strcat("\ncurl --pak ", pk3, "; wait; cl_cmd mv_download ", ftos(id), "\n"));
303         }
304         else
305         {
306                 Cmd_MapVote_MapDownload(tokenize_console(strcat("mv_download ", ftos(id))));
307         }
308 }
309
310 void MapVote_CheckPic(string pic, string pk3, float id)
311 {
312         // never try to retrieve a pic for the "don't care" 'map'
313         if(mv_abstain && id == mv_num_maps - 1)
314                 return;
315
316         if(PreviewExists(pic))
317         {
318                 mv_preview[id] = true;
319                 return;
320         }
321         MapVote_CheckPK3(pic, pk3, id);
322 }
323
324 #define NUM_SSDIRS 4
325 string ssdirs[NUM_SSDIRS];
326 float n_ssdirs;
327 void MapVote_Init()
328 {
329         float i, j, power;
330         string map, pk3, s;
331
332         precache_sound ("misc/invshot.wav");
333
334         registercmd("+showscores");
335         registercmd("-showscores");
336
337         mv_active = 1;
338
339         mv_mousepos = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
340         mv_selection = -1;
341
342         for(n_ssdirs = 0; ; ++n_ssdirs)
343         {
344                 s = ReadString();
345                 if(s == "")
346                         break;
347                 if(n_ssdirs < NUM_SSDIRS)
348                         ssdirs[n_ssdirs] = s;
349         }
350         n_ssdirs = min(n_ssdirs, NUM_SSDIRS);
351
352         mv_num_maps = min(MAPVOTE_COUNT, ReadByte());
353         mv_abstain = ReadByte();
354         if(mv_abstain)
355                 mv_abstain = 1; // must be 1 for bool-true, makes stuff easier
356         mv_detail = ReadByte();
357
358         mv_ownvote = -1;
359         mv_timeout = ReadCoord();
360
361         if(mv_num_maps <= 8)
362                 mv_maps_mask = ReadByte();
363         else
364                 mv_maps_mask = ReadShort();
365         
366         // Assume mv_pk3list is NULL, there should only be 1 mapvote per round
367         mv_pk3list = NULL; // I'm still paranoid!
368         
369         for(i = 0, power = 1; i < mv_num_maps; ++i, power *= 2)
370         {
371                 mv_votes[i] = 0;
372
373                 if(mv_maps_mask & power)
374                 {
375                         map = strzone(ReadString());
376                         pk3 = strzone(ReadString());
377                         j = bound(0, ReadByte(), n_ssdirs - 1);
378         
379                         mv_maps[i] = map;
380                         mv_pk3[i] = pk3;
381                         map = strzone(strcat(ssdirs[j], "/", map));
382                         mv_pics[i] = map;
383
384                         mv_preview[i] = false;
385
386                         //print(strcat("RECV: ", map, " in ", pk3, "\n"));
387                         MapVote_CheckPic(map, pk3, i);
388                 }
389                 else
390                 {
391                         mv_maps[i] = strzone("if-you-see-this-the-code-is-broken");
392                         mv_pk3[i] = strzone("if-you-see-this-the-code-is-broken");
393                         mv_pics[i] = strzone("if-you-see-this-the-code-is-broken");
394                         mv_preview[i] = false;
395                 }
396         }
397
398         for(i = 0; i < n_ssdirs; ++i)
399                 ssdirs[n_ssdirs] = string_null;
400         n_ssdirs = 0;
401 }
402
403 float MapVote_InputEvent(float bInputType, float nPrimary, float nSecondary)
404 {
405         float imp;
406
407         if (!mv_active)
408                 return false;
409
410         if (bInputType != 0)
411                 return false;
412
413         if ('0' <= nPrimary && nPrimary <= '9')
414         {
415                 imp = nPrimary - '0';
416                 if (imp == 0) imp = 10;
417                 localcmd(strcat("\nimpulse ", ftos(imp), "\n"));
418                 return true;
419         }
420
421         if (nPrimary == K_MOUSE1)
422                 if (mv_selection >= 0)
423                 {
424                         imp = min(mv_selection + 1, mv_num_maps);
425                         localcmd(strcat("\nimpulse ", ftos(imp), "\n"));
426                         return true;
427                 }
428
429         return false;
430 }
431
432 void MapVote_UpdateMask()
433 {
434         float i, power;
435         float oldmask;
436
437         oldmask = mv_maps_mask;
438         if(mv_num_maps <= 8)
439                 mv_maps_mask = ReadByte();
440         else
441                 mv_maps_mask = ReadShort();
442
443         if(oldmask & mv_maps_mask != oldmask)
444                 if(oldmask & mv_maps_mask == mv_maps_mask)
445                          sound(world, CHAN_AUTO, "misc_invshot.wav", VOL_BASE, ATTN_NONE);
446
447         // remove votes that no longer apply
448         for(i = 0, power = 1; i < mv_num_maps; ++i, power *= 2)
449                 if not(mv_maps_mask & power)
450                         mv_votes[i] = -1;
451 }
452
453 void MapVote_UpdateVotes()
454 {
455         float i, power;
456         for(i = 0, power = 1; i < mv_num_maps; ++i, power *= 2)
457         {
458                 if(mv_maps_mask & power)
459                 {
460                         if(mv_detail)
461                                 mv_votes[i] = ReadByte();
462                         else
463                                 mv_votes[i] = 0;
464                 }
465                 else
466                         mv_votes[i] = -1;
467         }
468
469         mv_ownvote = ReadByte()-1;
470 }
471
472 void Ent_MapVote()
473 {
474         float sf;
475
476         sf = ReadByte();
477
478         if(sf & 1)
479                 MapVote_Init();
480
481         if(sf & 2)
482                 MapVote_UpdateMask();
483
484         if(sf & 4)
485                 MapVote_UpdateVotes();
486 }
487
488 void Net_MapVote_Picture()
489 {
490         float type;
491         type = ReadByte();
492         mv_preview[type] = true;
493         mv_pics[type] = strzone(ReadPicture());
494 }