Merge branch 'master' into terencehill/music_player
[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 float mv_avail[MAPVOTE_COUNT];
10 float mv_avail_start[MAPVOTE_COUNT];
11 entity mv_pk3list;
12 float mv_abstain;
13 float mv_ownvote;
14 float mv_detail;
15 float mv_timeout;
16 float mv_top2_time;
17 float mv_top2_alpha;
18
19 vector mv_mousepos;
20 float mv_selection;
21 float mv_columns;
22 float mv_mouse_selection;
23 float mv_selection_keyboard;
24
25 float gametypevote;
26 string mapvote_chosenmap;
27 vector gtv_text_size;
28 vector gtv_text_size_small;
29
30 string MapVote_FormatMapItem(float id, string map, float count, float maxwidth, vector fontsize)
31 {
32         string pre, post;
33         pre = sprintf("%d. ", id+1);
34         if(mv_detail)
35         {
36                 if(count == 1)
37                         post = _(" (1 vote)");
38                 else if(count >= 0 && mv_avail[id] == GTV_AVAILABLE)
39                         post = sprintf(_(" (%d votes)"), count);
40                 else
41                         post = "";
42         }
43         else
44                 post = "";
45         maxwidth -= stringwidth(pre, FALSE, fontsize) + stringwidth(post, FALSE, fontsize);
46         map = textShortenToWidth(map, maxwidth, fontsize, stringwidth_nocolors);
47         return strcat(pre, map, post);
48 }
49
50 string GameTypeVote_DescriptionByID(float id)
51 {
52         return MapInfo_Type_Description(MapInfo_Type_FromString(mv_maps[id]));
53 }
54
55 vector MapVote_RGB(float id)
56 {
57         if(mv_avail[id] != GTV_AVAILABLE)
58                 return '1 1 1';
59         if(id == mv_ownvote)
60                 return '0 1 0';
61         else if (id == mv_selection)
62                 return '1 1 0';
63         else
64                 return '1 1 1';
65 }
66
67 void GameTypeVote_DrawGameTypeItem(vector pos, float maxh, float tsize, string gtype, string pic, float count, float id)
68 {
69         float alpha;
70         float desc_padding = gtv_text_size_x * 3;
71         float rect_margin = hud_fontsize_y / 2;
72         vector rect_pos = pos - '0.5 0.5 0' * rect_margin;
73         vector rect_size = '1 1 0';
74         rect_size_x = tsize + rect_margin;
75         rect_size_y = maxh + rect_margin;
76         vector rgb = MapVote_RGB(id);
77         vector offset = pos;
78         float nlines = 0;
79         
80         if(mv_avail_start[id] != GTV_AVAILABLE)
81                 alpha = 0.2;
82         else if ( mv_avail[id] != GTV_AVAILABLE && mv_top2_alpha)
83                 alpha = mv_top2_alpha;
84         else
85                 alpha = 1;
86         
87         if(id == mv_selection && mv_avail[id] == GTV_AVAILABLE)
88         {
89                 drawfill(rect_pos, rect_size, '1 1 1', 0.1, DRAWFLAG_NORMAL);
90         }
91         if(id == mv_ownvote)
92         {
93                 drawfill(rect_pos, rect_size, rgb, 0.1*alpha, DRAWFLAG_NORMAL);
94                 drawborderlines(autocvar_scoreboard_border_thickness, rect_pos, rect_size, rgb, alpha, DRAWFLAG_NORMAL);
95         }
96         
97         entity title;
98         title = spawn();
99         title.message = MapVote_FormatMapItem(id, MapInfo_Type_ToText(MapInfo_Type_FromString(gtype)), 
100                                                                                   count, tsize, gtv_text_size);
101         title.origin = pos-offset;
102         
103         pos_y += gtv_text_size_small_y;
104         pos_y += gtv_text_size_y/2;
105         
106         maxh -= gtv_text_size_y;
107         
108         entity picent = spawn();
109         picent.origin = pos-offset;
110         picent.maxs = '1 1 0 ' * min(maxh, desc_padding) * 0.8;
111         
112         pos_x += desc_padding;
113         tsize -= desc_padding;
114         
115         string thelabel = GameTypeVote_DescriptionByID(id), ts;
116         entity last = title;
117         entity next = world;
118         if( thelabel != "") 
119         {
120                 float i,n = tokenizebyseparator(thelabel, "\n");
121                 for(i = 0; i < n && maxh > (nlines+1)*gtv_text_size_small_y; ++i)
122                 {
123                         getWrappedLine_remaining = argv(i);
124                         while(getWrappedLine_remaining && maxh > (nlines+1)*gtv_text_size_small_y)
125                         {
126                                 ts = getWrappedLine(tsize, gtv_text_size_small, stringwidth_colors);
127                                 if (ts != "")
128                                 {
129                                         next = spawn();
130                                         next.message = ts;
131                                         next.origin = pos-offset;
132                                         last.chain = next;
133                                         last = next;
134                                         pos_y += gtv_text_size_small_y;
135                                         nlines++;
136                                 }
137                         }
138                 }
139         }
140         
141         maxh -= max(nlines*gtv_text_size_small_y,picent.maxs_y);
142         if ( maxh > 0 )
143                 offset_y += maxh/2;
144         drawstring(title.origin+offset, title.message, gtv_text_size, rgb, alpha, DRAWFLAG_NORMAL); 
145         
146         if(pic != "")
147                 drawpic(picent.origin+offset, pic, picent.maxs, '1 1 1', alpha, DRAWFLAG_NORMAL);
148         
149         for ( last = title.chain; last ; )
150         {
151                 drawstring(last.origin+offset, last.message, gtv_text_size_small, '1 1 1', alpha, DRAWFLAG_NORMAL);
152                 next = last;
153                 last = last.chain;
154                 remove(next);
155         }
156         
157         remove(picent);
158         remove(title);
159 }
160
161 void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, string pic, float count, float id)
162 {
163         vector img_size = '0 0 0';
164         vector rgb;
165         string label;
166         float text_size;
167
168         isize -= hud_fontsize_y; // respect the text when calculating the image size
169
170         rgb = MapVote_RGB(id);
171
172         img_size_y = isize;
173         img_size_x = isize / 0.75; // 4:3 x can be stretched easily, height is defined in isize
174
175         pos_y = pos_y + img_size_y;
176
177         label = MapVote_FormatMapItem(id, map, count, tsize, hud_fontsize);
178
179         text_size = stringwidth(label, false, hud_fontsize);
180
181         float theAlpha;
182         if (mv_avail[id] != GTV_AVAILABLE && mv_top2_alpha)
183                 theAlpha = mv_top2_alpha;
184         else
185                 theAlpha = 1;
186
187         pos_x -= text_size*0.5;
188         drawstring(pos, label, hud_fontsize, rgb, theAlpha, DRAWFLAG_NORMAL);
189
190         pos_x = pos_x + text_size*0.5 - img_size_x*0.5;
191         pos_y = pos_y - img_size_y;
192
193         pos += autocvar_scoreboard_border_thickness * '1 1 0';
194         img_size -= (autocvar_scoreboard_border_thickness * 2) * '1 1 0';
195         if(pic == "")
196         {
197                 drawfill(pos, img_size, '.5 .5 .5', .7 * theAlpha, DRAWFLAG_NORMAL);
198         }
199         else
200         {
201                 if(drawgetimagesize(pic) == '0 0 0')
202                         drawpic(pos, draw_UseSkinFor("nopreview_map"), img_size, '1 1 1', theAlpha, DRAWFLAG_NORMAL);
203                 else
204                         drawpic(pos, pic, img_size, '1 1 1', theAlpha, DRAWFLAG_NORMAL);
205         }
206
207         if(id == mv_ownvote)
208                 drawborderlines(autocvar_scoreboard_border_thickness, pos, img_size, rgb, theAlpha, DRAWFLAG_NORMAL);
209         else
210                 drawborderlines(autocvar_scoreboard_border_thickness, pos, img_size, '0 0 0', theAlpha, DRAWFLAG_NORMAL);
211
212         if(id == mv_selection && mv_avail[id] == GTV_AVAILABLE)
213                 drawfill(pos, img_size, '1 1 1', 0.1, DRAWFLAG_NORMAL);
214 }
215
216 void MapVote_DrawAbstain(vector pos, float isize, float tsize, float count, float id)
217 {
218         vector rgb;
219         float text_size;
220         string label;
221
222         rgb = MapVote_RGB(id);
223
224         pos_y = pos_y + hud_fontsize_y;
225
226         label = MapVote_FormatMapItem(id, _("Don't care"), count, tsize, hud_fontsize);
227
228         text_size = stringwidth(label, false, hud_fontsize);
229
230         pos_x -= text_size*0.5;
231         drawstring(pos, label, hud_fontsize, rgb, 1, DRAWFLAG_NORMAL);
232 }
233
234 vector MapVote_GridVec(vector gridspec, float i, float m)
235 {
236         float r;
237         r = mod(i, m);
238         return
239                 '1 0 0' * (gridspec_x * r)
240                 +
241                 '0 1 0' * (gridspec_y * (i - r) / m);
242 }
243
244 float MapVote_Selection(vector topleft, vector cellsize, float rows, float columns)
245 {
246
247         float c, r;
248
249         mv_mouse_selection = -1;
250
251         for (r = 0; r < rows; ++r)
252                 for (c = 0; c < columns; ++c)
253                 {
254                         if (mv_mousepos_x >= topleft_x + cellsize_x *  c &&
255                                 mv_mousepos_x <= topleft_x + cellsize_x * (c + 1) &&
256                                 mv_mousepos_y >= topleft_y + cellsize_y *  r &&
257                                 mv_mousepos_y <= topleft_y + cellsize_y * (r + 1))
258                         {
259                                 mv_mouse_selection = r * columns + c;
260                                 break;
261                         }
262                 }
263
264         if (mv_mouse_selection >= mv_num_maps)
265                 mv_mouse_selection = -1;
266
267         if (mv_abstain && mv_mouse_selection < 0)
268                 mv_mouse_selection = mv_num_maps;
269
270         if ( mv_selection_keyboard )
271                 return mv_selection;
272         
273         return mv_mouse_selection;
274 }
275
276 void MapVote_Draw()
277 {
278         string map;
279         float i, tmp;
280         vector pos;
281         float isize;
282         float center;
283         float rows;
284         float tsize;
285         vector dist = '0 0 0';
286
287         if(!mv_active)
288                 return;
289
290         if (!autocvar_hud_cursormode)
291         {
292                 vector mpos = mv_mousepos + getmousepos();
293                 mpos_x = bound(0, mpos_x, vid_conwidth);
294                 mpos_y = bound(0, mpos_y, vid_conheight);
295                 
296                 if ( mpos_x != mv_mousepos_x || mpos_y != mv_mousepos_y )
297                         mv_selection_keyboard = 0;
298                 mv_mousepos = mpos;
299
300         }
301
302         center = (vid_conwidth - 1)/2;
303         xmin = vid_conwidth*0.05; // 5% border must suffice
304         xmax = vid_conwidth - xmin;
305         ymin = 20;
306         i = autocvar_con_chatpos; //*autocvar_con_chatsize;
307         if(i < 0)
308                 ymax = vid_conheight + (i - autocvar_con_chat) * autocvar_con_chatsize;
309         if(i >= 0 || ymax < (vid_conheight*0.5))
310                 ymax = vid_conheight - ymin;
311
312         hud_fontsize = HUD_GetFontsize("hud_fontsize");
313
314         pos_y = ymin;
315         pos_z = 0;
316
317         draw_beginBoldFont();
318         map = ((gametypevote) ? _("Decide the gametype") : _("Vote for a map"));
319         pos_x = center - stringwidth(map, false, '12 0 0');
320         drawstring(pos, map, '24 24 0', '1 1 1', 1, DRAWFLAG_NORMAL);
321         pos_y += 26;
322
323         if( mapvote_chosenmap != "" )
324         {
325                 pos_x = center - stringwidth(mapvote_chosenmap, false, hud_fontsize*1.5/2);
326                 drawstring(pos, mapvote_chosenmap, hud_fontsize*1.5, '1 1 1', 1, DRAWFLAG_NORMAL);
327                 pos_y += hud_fontsize_y*2;
328         }
329
330         i = ceil(max(0, mv_timeout - time));
331         map = sprintf(_("%d seconds left"), i);
332         pos_x = center - stringwidth(map, false, '8 0 0');
333         drawstring(pos, map, '16 16 0', '0 1 0', 1, DRAWFLAG_NORMAL);
334         pos_y += 22;
335         pos_x = xmin;
336         draw_endBoldFont();
337
338         // base for multi-column stuff...
339         ymin = pos_y;
340         if(mv_abstain)
341                 mv_num_maps -= 1;
342
343         rows = ceil(mv_num_maps / mv_columns);
344
345         dist_x = (xmax - xmin) / mv_columns;
346         dist_y = (ymax - pos_y) / rows;
347
348         if ( gametypevote )
349         {
350                 tsize = dist_x - hud_fontsize_y;
351                 isize = dist_y;
352                 float maxheight = (ymax - pos_y) / 3;
353                 if ( isize > maxheight )
354                 {
355                         pos_x += (isize - maxheight)/2;
356                         isize = maxheight;
357                 }
358                 else
359                         dist_y += hud_fontsize_y;
360                 pos_x = ( vid_conwidth - dist_x * mv_columns ) / 2;
361         }
362         else
363         {
364                 tsize = dist_x - 10;
365                 isize = min(dist_y - 10, 0.75 * tsize);
366         }
367
368         mv_selection = MapVote_Selection(pos, dist, rows, mv_columns);
369
370         if ( !gametypevote )
371                 pos_x += dist_x / 2;
372         pos_y += (dist_y - isize) / 2;
373         ymax -= isize;
374
375         if (mv_top2_time)
376                 mv_top2_alpha = max(0.2, 1 - (time - mv_top2_time)*(time - mv_top2_time));
377
378         void (vector, float, float, string, string, float, float) DrawItem;
379
380         if(gametypevote)
381                 DrawItem = GameTypeVote_DrawGameTypeItem;
382         else
383                 DrawItem = MapVote_DrawMapItem;
384
385         for(i = 0; i < mv_num_maps; ++i)
386         {
387                 tmp = mv_votes[i]; // FTEQCC bug: too many array accesses in the function call screw it up
388                 map = mv_maps[i];
389                 if(mv_preview[i])
390                         DrawItem(pos + MapVote_GridVec(dist, i, mv_columns), isize, tsize, map, mv_pics[i], tmp, i);
391                 else
392                         DrawItem(pos + MapVote_GridVec(dist, i, mv_columns), isize, tsize, map, "", tmp, i);
393         }
394
395         if(mv_abstain)
396                 ++mv_num_maps;
397
398         if(mv_abstain && i < mv_num_maps) {
399                 tmp = mv_votes[i];
400                 pos_y = ymax + isize - hud_fontsize_y;
401                 pos_x = (xmax+xmin)*0.5;
402                 MapVote_DrawAbstain(pos, isize, xmax - xmin, tmp, i);
403         }
404
405         drawpic(mv_mousepos, strcat("gfx/menu/", autocvar_menu_skin, "/cursor.tga"), '32 32 0', '1 1 1', 1 - autocvar__menu_alpha, DRAWFLAG_NORMAL);
406 }
407
408 void Cmd_MapVote_MapDownload(float argc)
409 {
410         float id;
411         entity pak;
412
413         if(argc != 2 || !mv_pk3list)
414         {
415                 print(_("mv_mapdownload: ^3You're not supposed to use this command on your own!\n"));
416                 return;
417         }
418
419         id = stof(argv(1));
420         for(pak = mv_pk3list; pak; pak = pak.chain)
421                 if(pak.sv_entnum == id)
422                         break;
423
424         if(!pak || pak.sv_entnum != id) {
425                 print(_("^1Error:^7 Couldn't find pak index.\n"));
426                 return;
427         }
428
429         if(PreviewExists(pak.message))
430         {
431                 mv_preview[id] = true;
432                 return;
433         } else {
434                 print(_("Requesting preview...\n"));
435                 localcmd(strcat("\ncmd mv_getpicture ", ftos(id), "\n"));
436         }
437 }
438
439 void MapVote_CheckPK3(string pic, string pk3, float id)
440 {
441         entity pak;
442         pak = spawn();
443         pak.netname = pk3;
444         pak.message = pic;
445         pak.sv_entnum = id;
446
447         pak.chain = mv_pk3list;
448         mv_pk3list = pak;
449
450         if(pk3 != "")
451         {
452                 localcmd(strcat("\ncurl --pak ", pk3, "; wait; cl_cmd mv_download ", ftos(id), "\n"));
453         }
454         else
455         {
456                 Cmd_MapVote_MapDownload(tokenize_console(strcat("mv_download ", ftos(id))));
457         }
458 }
459
460 void MapVote_CheckPic(string pic, string pk3, float id)
461 {
462         // never try to retrieve a pic for the "don't care" 'map'
463         if(mv_abstain && id == mv_num_maps - 1)
464                 return;
465
466         if(PreviewExists(pic))
467         {
468                 mv_preview[id] = true;
469                 return;
470         }
471         MapVote_CheckPK3(pic, pk3, id);
472 }
473
474 void MapVote_ReadMask()
475 {
476         float i;
477         if ( mv_num_maps < 24 )
478         {
479                 float mask, power;
480                 if(mv_num_maps < 8)
481                         mask = ReadByte();
482                 else if(mv_num_maps < 16)
483                         mask = ReadShort();
484                 else
485                         mask = ReadLong();
486                 
487                 for(i = 0, power = 1; i < mv_num_maps; ++i, power *= 2)
488                         mv_avail[i] = (mask & power) ? GTV_AVAILABLE : GTV_FORBIDDEN;
489         }
490         else
491         {
492                 for(i = 0; i < mv_num_maps; ++i )
493                         mv_avail[i] = ReadByte();
494         }
495 }
496
497 #define NUM_SSDIRS 4
498 string ssdirs[NUM_SSDIRS];
499 float n_ssdirs;
500 void MapVote_Init()
501 {
502         float i, j;
503         string map, pk3, s;
504
505         precache_sound ("misc/invshot.wav");
506
507         mv_active = 1;
508         if(autocvar_hud_cursormode) { setcursormode(1); }
509         else { mv_mousepos = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight; }
510         mv_selection = -1;
511         mv_selection_keyboard = 0;
512
513         for(n_ssdirs = 0; ; ++n_ssdirs)
514         {
515                 s = ReadString();
516                 if(s == "")
517                         break;
518                 if(n_ssdirs < NUM_SSDIRS)
519                         ssdirs[n_ssdirs] = s;
520         }
521         n_ssdirs = min(n_ssdirs, NUM_SSDIRS);
522
523         mv_num_maps = min(MAPVOTE_COUNT, ReadByte());
524         mv_abstain = ReadByte();
525         if(mv_abstain)
526                 mv_abstain = 1; // must be 1 for bool-true, makes stuff easier
527         mv_detail = ReadByte();
528
529         mv_ownvote = -1;
530         mv_timeout = ReadCoord();
531
532         gametypevote = ReadByte();
533         
534         float mv_real_num_maps = mv_num_maps - mv_abstain;
535
536         if(gametypevote)
537         {
538                 mapvote_chosenmap = strzone(ReadString());
539                 if ( gametypevote == 2 )
540                         gametypevote = 0;
541
542                 gtv_text_size = hud_fontsize*1.4;
543                 gtv_text_size_small = hud_fontsize*1.1;
544                 
545                 if (mv_real_num_maps > 8 )
546                         mv_columns = 3;
547                 else
548                         mv_columns = min(2, mv_real_num_maps);
549     }
550     else
551         {
552                 if (mv_real_num_maps > 16)
553                         mv_columns = 5;
554                 else if (mv_real_num_maps > 9)
555                         mv_columns = 4;
556                 else if(mv_real_num_maps > 3)
557                         mv_columns = 3;
558                 else
559                         mv_columns = mv_real_num_maps;
560         }
561
562         MapVote_ReadMask();
563         for(i = 0; i < mv_num_maps; ++i )
564                 mv_avail_start[i] = mv_avail[i];
565
566         // Assume mv_pk3list is world, there should only be 1 mapvote per round
567         mv_pk3list = world; // I'm still paranoid!
568
569         for(i = 0; i < mv_num_maps; ++i)
570         {
571                 mv_votes[i] = 0;
572
573                 map = strzone(ReadString());
574                 pk3 = strzone(ReadString());
575                 j = bound(0, ReadByte(), n_ssdirs - 1);
576
577                 mv_maps[i] = map;
578                 mv_pk3[i] = pk3;
579                 mv_avail[i] = ReadByte();
580
581                 if(gametypevote)
582                 {
583                         //map = strzone(strcat("gfx/menu/default/gametype_", map));
584                         //map = strzone(sprintf("gfx/menu/%s/gametype_%s", autocvar_menu_skin, map));
585                         string mv_picpath = sprintf("gfx/menu/%s/gametype_%s", autocvar_menu_skin, map);
586                         if(precache_pic(mv_picpath) == "")
587                                 mv_picpath = strcat("gfx/menu/default/gametype_", map);
588                         map = strzone(mv_picpath);
589                         mv_pics[i] = map;
590                         mv_preview[i] = PreviewExists(map);
591                 }
592                 else
593                 {
594                         map = strzone(strcat(ssdirs[j], "/", map));
595                         mv_pics[i] = map;
596                         mv_preview[i] = false;
597                         MapVote_CheckPic(map, pk3, i);
598                 }
599         }
600
601         for(i = 0; i < n_ssdirs; ++i)
602                 ssdirs[n_ssdirs] = string_null;
603         n_ssdirs = 0;
604 }
605
606 void MapVote_SendChoice(float index)
607 {
608         localcmd(strcat("\nimpulse ", ftos(index+1), "\n"));
609 }
610
611 float MapVote_MoveLeft(float pos)
612 {
613         float imp;
614         if ( pos < 0 ) 
615                 imp = mv_num_maps - 1;
616         else
617                 imp = pos < 1 ? mv_num_maps - 1 : pos - 1;
618         if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
619                 imp = MapVote_MoveLeft(imp);
620         return imp;
621 }
622 float MapVote_MoveRight(float pos)
623 {
624         float imp;
625         if ( pos < 0 ) 
626                 imp = 0;
627         else
628                 imp = pos >= mv_num_maps - 1 ? 0 : pos + 1;
629         if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
630                 imp = MapVote_MoveRight(imp);
631         return imp;
632 }
633 float MapVote_MoveUp(float pos)
634 {
635         float imp;
636         if ( pos < 0 ) 
637                 imp = mv_num_maps - 1;
638         else
639         {
640                 imp = pos - mv_columns;
641                 if ( imp < 0 )
642                 {
643                         imp = floor(mv_num_maps/mv_columns)*mv_columns + pos % mv_columns;
644                         if ( imp >= mv_num_maps )
645                                 imp -= mv_columns;
646                 }
647         }
648         if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
649                 imp = MapVote_MoveUp(imp);
650         return imp;
651 }
652 float MapVote_MoveDown(float pos)
653 {
654         float imp;
655         if ( pos < 0 ) 
656                 imp = 0;
657         else
658         {
659                 imp = pos + mv_columns;
660                 if ( imp >= mv_num_maps )
661                         imp = imp % mv_columns;
662         }
663         if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
664                 imp = MapVote_MoveDown(imp);
665         return imp;
666 }
667
668 float MapVote_InputEvent(float bInputType, float nPrimary, float nSecondary)
669 {
670         float imp;
671
672         if (!mv_active)
673                 return false;
674
675         if(bInputType == 3)
676         {
677                 mv_mousepos_x = nPrimary;
678                 mv_mousepos_y = nSecondary;
679                 mv_selection_keyboard = 0;
680                 return true;
681         }
682
683         if (bInputType != 0)
684                 return false;
685
686         if ('0' <= nPrimary && nPrimary <= '9')
687         {
688                 imp = nPrimary - '0';
689                 if (imp == 0) imp = 10;
690                 localcmd(strcat("\nimpulse ", ftos(imp), "\n"));
691                 return true;
692         }
693         switch(nPrimary)
694         {
695                 case K_KP_1: localcmd("\nimpulse 1\n"); return true;
696                 case K_KP_2: localcmd("\nimpulse 2\n"); return true;
697                 case K_KP_3: localcmd("\nimpulse 3\n"); return true;
698                 case K_KP_4: localcmd("\nimpulse 4\n"); return true;
699                 case K_KP_5: localcmd("\nimpulse 5\n"); return true;
700                 case K_KP_6: localcmd("\nimpulse 6\n"); return true;
701                 case K_KP_7: localcmd("\nimpulse 7\n"); return true;
702                 case K_KP_8: localcmd("\nimpulse 8\n"); return true;
703                 case K_KP_9: localcmd("\nimpulse 9\n"); return true;
704                 case K_KP_0: localcmd("\nimpulse 10\n"); return true;
705
706                 case K_RIGHTARROW:
707                         mv_selection_keyboard = 1;
708                         mv_selection = MapVote_MoveRight(mv_selection);
709                         return true;
710                 case K_LEFTARROW:
711                         mv_selection_keyboard = 1;
712                         mv_selection = MapVote_MoveLeft(mv_selection);
713                         return true;
714                 case K_DOWNARROW:
715                         mv_selection_keyboard = 1;
716                         mv_selection = MapVote_MoveDown(mv_selection);
717                         return true;
718                 case K_UPARROW:
719                         mv_selection_keyboard = 1;
720                         mv_selection = MapVote_MoveUp(mv_selection);
721                         return true;
722                 case K_KP_ENTER:
723                 case K_ENTER:
724                 case K_SPACE:
725                         if ( mv_selection_keyboard )
726                                 MapVote_SendChoice(mv_selection);
727                         return true;
728         }
729
730         if (nPrimary == K_MOUSE1)
731         {
732                 mv_selection_keyboard = 0;
733                 mv_selection = mv_mouse_selection;
734                 if (mv_selection >= 0)
735                 {
736                         imp = min(mv_selection + 1, mv_num_maps);
737                         localcmd(strcat("\nimpulse ", ftos(imp), "\n"));
738                         return true;
739                 }
740         }
741
742         return false;
743 }
744
745 void MapVote_UpdateMask()
746 {
747         MapVote_ReadMask();
748         mv_top2_time = time;
749 }
750
751 void MapVote_UpdateVotes()
752 {
753         float i;
754         for(i = 0; i < mv_num_maps; ++i)
755         {
756                 if(mv_avail[i] == GTV_AVAILABLE)
757                 {
758                         if(mv_detail)
759                                 mv_votes[i] = ReadByte();
760                         else
761                                 mv_votes[i] = 0;
762                 }
763                 else
764                         mv_votes[i] = -1;
765         }
766
767         mv_ownvote = ReadByte()-1;
768 }
769
770 void Ent_MapVote()
771 {
772         float sf;
773
774         sf = ReadByte();
775
776         if(sf & 1)
777                 MapVote_Init();
778
779         if(sf & 2)
780                 MapVote_UpdateMask();
781
782         if(sf & 4)
783                 MapVote_UpdateVotes();
784 }
785
786 void Net_MapVote_Picture()
787 {
788         float type;
789         type = ReadByte();
790         mv_preview[type] = true;
791         mv_pics[type] = strzone(ReadPicture());
792 }