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