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