]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/mapvoting.qc
Apply a patch by Melanosuchus, fixing issues noted in http://dev.xonotic.org/issues...
[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_choosenmap;
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(gametypevote && mapvote_choosenmap != "" )
324         {
325                 pos_x = center - stringwidth(mapvote_choosenmap, false, hud_fontsize);
326                 drawstring(pos, mapvote_choosenmap, hud_fontsize*2, '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                 // read map name in case we have nextmap set
539                 mapvote_choosenmap = strzone(ReadString());
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 }