2 #include "../dpdefs/csprogsdefs.qh"
4 #include "../dpdefs/keycodes.qh"
5 #include "../common/constants.qh"
6 #include "../common/util.qh"
7 #include "../common/mapinfo.qh"
8 #include "autocvars.qh"
18 string mv_maps[MAPVOTE_COUNT];
19 string mv_pics[MAPVOTE_COUNT];
20 string mv_pk3[MAPVOTE_COUNT];
21 float mv_preview[MAPVOTE_COUNT];
22 float mv_votes[MAPVOTE_COUNT];
23 float mv_avail[MAPVOTE_COUNT];
24 float mv_avail_start[MAPVOTE_COUNT];
36 int mv_mouse_selection;
37 int mv_selection_keyboard;
40 string mapvote_chosenmap;
42 vector gtv_text_size_small;
44 string MapVote_FormatMapItem(int id, string map, float count, float maxwidth, vector fontsize)
47 pre = sprintf("%d. ", id+1);
51 post = _(" (1 vote)");
52 else if(count >= 0 && mv_avail[id] == GTV_AVAILABLE)
53 post = sprintf(_(" (%d votes)"), count);
59 maxwidth -= stringwidth(pre, false, fontsize) + stringwidth(post, false, fontsize);
60 map = textShortenToWidth(map, maxwidth, fontsize, stringwidth_nocolors);
61 return strcat(pre, map, post);
64 string GameTypeVote_DescriptionByID(float id)
66 return MapInfo_Type_Description(MapInfo_Type_FromString(mv_maps[id]));
69 vector MapVote_RGB(int id)
71 if(mv_avail[id] != GTV_AVAILABLE)
75 else if (id == mv_selection)
81 void GameTypeVote_DrawGameTypeItem(vector pos, float maxh, float tsize, string gtype, string pic, float count, int id)
84 float desc_padding = gtv_text_size.x * 3;
85 float rect_margin = hud_fontsize.y / 2;
86 vector rect_pos = pos - '0.5 0.5 0' * rect_margin;
87 vector rect_size = '1 1 0';
88 rect_size_x = tsize + rect_margin;
89 rect_size_y = maxh + rect_margin;
90 vector rgb = MapVote_RGB(id);
94 if(mv_avail_start[id] != GTV_AVAILABLE)
96 else if ( mv_avail[id] != GTV_AVAILABLE && mv_top2_alpha)
97 alpha = mv_top2_alpha;
101 if(id == mv_selection && mv_avail[id] == GTV_AVAILABLE)
103 drawfill(rect_pos, rect_size, '1 1 1', 0.1, DRAWFLAG_NORMAL);
107 drawfill(rect_pos, rect_size, rgb, 0.1*alpha, DRAWFLAG_NORMAL);
108 drawborderlines(autocvar_scoreboard_border_thickness, rect_pos, rect_size, rgb, alpha, DRAWFLAG_NORMAL);
113 title.message = MapVote_FormatMapItem(id, MapInfo_Type_ToText(MapInfo_Type_FromString(gtype)),
114 count, tsize, gtv_text_size);
115 title.origin = pos-offset;
117 pos.y += gtv_text_size_small.y;
118 pos.y += gtv_text_size.y/2;
120 maxh -= gtv_text_size.y;
122 entity picent = spawn();
123 picent.origin = pos-offset;
124 picent.maxs = '1 1 0 ' * min(maxh, desc_padding) * 0.8;
126 pos.x += desc_padding;
127 tsize -= desc_padding;
129 string thelabel = GameTypeVote_DescriptionByID(id), ts;
134 float i,n = tokenizebyseparator(thelabel, "\n");
135 for(i = 0; i < n && maxh > (nlines+1)*gtv_text_size_small.y; ++i)
137 getWrappedLine_remaining = argv(i);
138 while(getWrappedLine_remaining && maxh > (nlines+1)*gtv_text_size_small.y)
140 ts = getWrappedLine(tsize, gtv_text_size_small, stringwidth_colors);
145 next.origin = pos-offset;
148 pos.y += gtv_text_size_small.y;
155 maxh -= max(nlines*gtv_text_size_small.y,picent.maxs.y);
158 drawstring(title.origin+offset, title.message, gtv_text_size, rgb, alpha, DRAWFLAG_NORMAL);
161 drawpic(picent.origin+offset, pic, picent.maxs, '1 1 1', alpha, DRAWFLAG_NORMAL);
163 for ( last = title.chain; last ; )
165 drawstring(last.origin+offset, last.message, gtv_text_size_small, '1 1 1', alpha, DRAWFLAG_NORMAL);
175 void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, string pic, float count, int id)
177 vector img_size = '0 0 0';
182 isize -= hud_fontsize.y; // respect the text when calculating the image size
184 rgb = MapVote_RGB(id);
187 img_size_x = isize / 0.75; // 4:3 x can be stretched easily, height is defined in isize
189 pos_y = pos.y + img_size.y;
191 label = MapVote_FormatMapItem(id, map, count, tsize, hud_fontsize);
193 text_size = stringwidth(label, false, hud_fontsize);
196 if (mv_avail[id] != GTV_AVAILABLE && mv_top2_alpha)
197 theAlpha = mv_top2_alpha;
201 pos.x -= text_size*0.5;
202 drawstring(pos, label, hud_fontsize, rgb, theAlpha, DRAWFLAG_NORMAL);
204 pos_x = pos.x + text_size*0.5 - img_size.x*0.5;
205 pos_y = pos.y - img_size.y;
207 pos += autocvar_scoreboard_border_thickness * '1 1 0';
208 img_size -= (autocvar_scoreboard_border_thickness * 2) * '1 1 0';
211 drawfill(pos, img_size, '.5 .5 .5', .7 * theAlpha, DRAWFLAG_NORMAL);
215 if(drawgetimagesize(pic) == '0 0 0')
216 drawpic(pos, draw_UseSkinFor("nopreview_map"), img_size, '1 1 1', theAlpha, DRAWFLAG_NORMAL);
218 drawpic(pos, pic, img_size, '1 1 1', theAlpha, DRAWFLAG_NORMAL);
222 drawborderlines(autocvar_scoreboard_border_thickness, pos, img_size, rgb, theAlpha, DRAWFLAG_NORMAL);
224 drawborderlines(autocvar_scoreboard_border_thickness, pos, img_size, '0 0 0', theAlpha, DRAWFLAG_NORMAL);
226 if(id == mv_selection && mv_avail[id] == GTV_AVAILABLE)
227 drawfill(pos, img_size, '1 1 1', 0.1, DRAWFLAG_NORMAL);
230 void MapVote_DrawAbstain(vector pos, float isize, float tsize, float count, int id)
236 rgb = MapVote_RGB(id);
238 pos_y = pos.y + hud_fontsize.y;
240 label = MapVote_FormatMapItem(id, _("Don't care"), count, tsize, hud_fontsize);
242 text_size = stringwidth(label, false, hud_fontsize);
244 pos.x -= text_size*0.5;
245 drawstring(pos, label, hud_fontsize, rgb, 1, DRAWFLAG_NORMAL);
248 vector MapVote_GridVec(vector gridspec, int i, int m)
252 '1 0 0' * (gridspec.x * r)
254 '0 1 0' * (gridspec.y * (i - r) / m);
257 float MapVote_Selection(vector topleft, vector cellsize, float rows, float columns)
262 mv_mouse_selection = -1;
264 for (r = 0; r < rows; ++r)
265 for (c = 0; c < columns; ++c)
267 if (mv_mousepos.x >= topleft.x + cellsize.x * c &&
268 mv_mousepos.x <= topleft.x + cellsize.x * (c + 1) &&
269 mv_mousepos.y >= topleft.y + cellsize.y * r &&
270 mv_mousepos.y <= topleft.y + cellsize.y * (r + 1))
272 mv_mouse_selection = r * columns + c;
277 if (mv_mouse_selection >= mv_num_maps)
278 mv_mouse_selection = -1;
280 if (mv_abstain && mv_mouse_selection < 0)
281 mv_mouse_selection = mv_num_maps;
283 if ( mv_selection_keyboard )
286 return mv_mouse_selection;
299 vector dist = '0 0 0';
304 if (!autocvar_hud_cursormode)
306 vector mpos = mv_mousepos + getmousepos();
307 mpos_x = bound(0, mpos.x, vid_conwidth);
308 mpos_y = bound(0, mpos.y, vid_conheight);
310 if ( mpos.x != mv_mousepos.x || mpos.y != mv_mousepos.y )
311 mv_selection_keyboard = 0;
316 center = (vid_conwidth - 1)/2;
317 xmin = vid_conwidth*0.05; // 5% border must suffice
318 xmax = vid_conwidth - xmin;
320 i = autocvar_con_chatpos; //*autocvar_con_chatsize;
322 ymax = vid_conheight + (i - autocvar_con_chat) * autocvar_con_chatsize;
323 if(i >= 0 || ymax < (vid_conheight*0.5))
324 ymax = vid_conheight - ymin;
326 hud_fontsize = HUD_GetFontsize("hud_fontsize");
331 draw_beginBoldFont();
332 map = ((gametypevote) ? _("Decide the gametype") : _("Vote for a map"));
333 pos_x = center - stringwidth(map, false, '12 0 0');
334 drawstring(pos, map, '24 24 0', '1 1 1', 1, DRAWFLAG_NORMAL);
337 if( mapvote_chosenmap != "" )
339 pos_x = center - stringwidth(mapvote_chosenmap, false, hud_fontsize*1.5/2);
340 drawstring(pos, mapvote_chosenmap, hud_fontsize*1.5, '1 1 1', 1, DRAWFLAG_NORMAL);
341 pos.y += hud_fontsize.y*2;
344 i = ceil(max(0, mv_timeout - time));
345 map = sprintf(_("%d seconds left"), i);
346 pos_x = center - stringwidth(map, false, '8 0 0');
347 drawstring(pos, map, '16 16 0', '0 1 0', 1, DRAWFLAG_NORMAL);
352 // base for multi-column stuff...
357 rows = ceil(mv_num_maps / mv_columns);
359 dist_x = (xmax - xmin) / mv_columns;
360 dist_y = (ymax - pos.y) / rows;
364 tsize = dist.x - hud_fontsize.y;
366 float maxheight = (ymax - pos.y) / 3;
367 if ( isize > maxheight )
369 pos.x += (isize - maxheight)/2;
373 dist.y += hud_fontsize.y;
374 pos_x = ( vid_conwidth - dist.x * mv_columns ) / 2;
379 isize = min(dist.y - 10, 0.75 * tsize);
382 mv_selection = MapVote_Selection(pos, dist, rows, mv_columns);
386 pos.y += (dist.y - isize) / 2;
390 mv_top2_alpha = max(0.2, 1 - (time - mv_top2_time)*(time - mv_top2_time));
392 void (vector, float, float, string, string, float, float) DrawItem;
395 DrawItem = GameTypeVote_DrawGameTypeItem;
397 DrawItem = MapVote_DrawMapItem;
399 for(i = 0; i < mv_num_maps; ++i)
401 tmp = mv_votes[i]; // FTEQCC bug: too many array accesses in the function call screw it up
404 DrawItem(pos + MapVote_GridVec(dist, i, mv_columns), isize, tsize, map, mv_pics[i], tmp, i);
406 DrawItem(pos + MapVote_GridVec(dist, i, mv_columns), isize, tsize, map, "", tmp, i);
412 if(mv_abstain && i < mv_num_maps) {
414 pos_y = ymax + isize - hud_fontsize.y;
415 pos_x = (xmax+xmin)*0.5;
416 MapVote_DrawAbstain(pos, isize, xmax - xmin, tmp, i);
419 drawpic(mv_mousepos, strcat("gfx/menu/", autocvar_menu_skin, "/cursor.tga"), '32 32 0', '1 1 1', 1 - autocvar__menu_alpha, DRAWFLAG_NORMAL);
422 void Cmd_MapVote_MapDownload(float argc)
427 if(argc != 2 || !mv_pk3list)
429 print(_("mv_mapdownload: ^3You're not supposed to use this command on your own!\n"));
434 for(pak = mv_pk3list; pak; pak = pak.chain)
435 if(pak.sv_entnum == id)
438 if(!pak || pak.sv_entnum != id) {
439 print(_("^1Error:^7 Couldn't find pak index.\n"));
443 if(PreviewExists(pak.message))
445 mv_preview[id] = true;
448 print(_("Requesting preview...\n"));
449 localcmd(strcat("\ncmd mv_getpicture ", ftos(id), "\n"));
453 void MapVote_CheckPK3(string pic, string pk3, int id)
461 pak.chain = mv_pk3list;
466 localcmd(strcat("\ncurl --pak ", pk3, "; wait; cl_cmd mv_download ", ftos(id), "\n"));
470 Cmd_MapVote_MapDownload(tokenize_console(strcat("mv_download ", ftos(id))));
474 void MapVote_CheckPic(string pic, string pk3, int id)
476 // never try to retrieve a pic for the "don't care" 'map'
477 if(mv_abstain && id == mv_num_maps - 1)
480 if(PreviewExists(pic))
482 mv_preview[id] = true;
485 MapVote_CheckPK3(pic, pk3, id);
488 void MapVote_ReadMask()
491 if ( mv_num_maps < 24 )
496 else if(mv_num_maps < 16)
501 for(i = 0, power = 1; i < mv_num_maps; ++i, power *= 2)
502 mv_avail[i] = (mask & power) ? GTV_AVAILABLE : GTV_FORBIDDEN;
506 for(i = 0; i < mv_num_maps; ++i )
507 mv_avail[i] = ReadByte();
511 const int NUM_SSDIRS = 4;
512 string ssdirs[NUM_SSDIRS];
519 precache_sound ("misc/invshot.wav");
522 if(autocvar_hud_cursormode) { setcursormode(1); }
523 else { mv_mousepos = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight; }
525 mv_selection_keyboard = 0;
527 for(n_ssdirs = 0; ; ++n_ssdirs)
532 if(n_ssdirs < NUM_SSDIRS)
533 ssdirs[n_ssdirs] = s;
535 n_ssdirs = min(n_ssdirs, NUM_SSDIRS);
537 mv_num_maps = min(MAPVOTE_COUNT, ReadByte());
538 mv_abstain = ReadByte();
540 mv_abstain = 1; // must be 1 for bool-true, makes stuff easier
541 mv_detail = ReadByte();
544 mv_timeout = ReadCoord();
546 gametypevote = ReadByte();
548 float mv_real_num_maps = mv_num_maps - mv_abstain;
552 mapvote_chosenmap = strzone(ReadString());
553 if ( gametypevote == 2 )
556 gtv_text_size = hud_fontsize*1.4;
557 gtv_text_size_small = hud_fontsize*1.1;
559 if (mv_real_num_maps > 8 )
562 mv_columns = min(2, mv_real_num_maps);
566 if (mv_real_num_maps > 16)
568 else if (mv_real_num_maps > 9)
570 else if(mv_real_num_maps > 3)
573 mv_columns = mv_real_num_maps;
577 for(i = 0; i < mv_num_maps; ++i )
578 mv_avail_start[i] = mv_avail[i];
580 // Assume mv_pk3list is world, there should only be 1 mapvote per round
581 mv_pk3list = world; // I'm still paranoid!
583 for(i = 0; i < mv_num_maps; ++i)
587 map = strzone(ReadString());
588 pk3 = strzone(ReadString());
589 j = bound(0, ReadByte(), n_ssdirs - 1);
593 mv_avail[i] = ReadByte();
597 //map = strzone(strcat("gfx/menu/default/gametype_", map));
598 //map = strzone(sprintf("gfx/menu/%s/gametype_%s", autocvar_menu_skin, map));
599 string mv_picpath = sprintf("gfx/menu/%s/gametype_%s", autocvar_menu_skin, map);
600 if(precache_pic(mv_picpath) == "")
601 mv_picpath = strcat("gfx/menu/default/gametype_", map);
602 map = strzone(mv_picpath);
604 mv_preview[i] = PreviewExists(map);
608 map = strzone(strcat(ssdirs[j], "/", map));
610 mv_preview[i] = false;
611 MapVote_CheckPic(map, pk3, i);
615 for(i = 0; i < n_ssdirs; ++i)
616 ssdirs[n_ssdirs] = string_null;
620 void MapVote_SendChoice(float index)
622 localcmd(strcat("\nimpulse ", ftos(index+1), "\n"));
625 int MapVote_MoveLeft(int pos)
629 imp = mv_num_maps - 1;
631 imp = pos < 1 ? mv_num_maps - 1 : pos - 1;
632 if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
633 imp = MapVote_MoveLeft(imp);
636 int MapVote_MoveRight(int pos)
642 imp = pos >= mv_num_maps - 1 ? 0 : pos + 1;
643 if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
644 imp = MapVote_MoveRight(imp);
647 int MapVote_MoveUp(int pos)
651 imp = mv_num_maps - 1;
654 imp = pos - mv_columns;
657 imp = floor(mv_num_maps/mv_columns)*mv_columns + pos % mv_columns;
658 if ( imp >= mv_num_maps )
662 if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
663 imp = MapVote_MoveUp(imp);
666 int MapVote_MoveDown(int pos)
673 imp = pos + mv_columns;
674 if ( imp >= mv_num_maps )
675 imp = imp % mv_columns;
677 if ( mv_avail[imp] != GTV_AVAILABLE && imp != mv_ownvote )
678 imp = MapVote_MoveDown(imp);
682 float MapVote_InputEvent(float bInputType, float nPrimary, float nSecondary)
691 mv_mousepos_x = nPrimary;
692 mv_mousepos_y = nSecondary;
693 mv_selection_keyboard = 0;
700 if ('0' <= nPrimary && nPrimary <= '9')
702 imp = nPrimary - '0';
703 if (imp == 0) imp = 10;
704 localcmd(strcat("\nimpulse ", ftos(imp), "\n"));
709 case K_KP_1: localcmd("\nimpulse 1\n"); return true;
710 case K_KP_2: localcmd("\nimpulse 2\n"); return true;
711 case K_KP_3: localcmd("\nimpulse 3\n"); return true;
712 case K_KP_4: localcmd("\nimpulse 4\n"); return true;
713 case K_KP_5: localcmd("\nimpulse 5\n"); return true;
714 case K_KP_6: localcmd("\nimpulse 6\n"); return true;
715 case K_KP_7: localcmd("\nimpulse 7\n"); return true;
716 case K_KP_8: localcmd("\nimpulse 8\n"); return true;
717 case K_KP_9: localcmd("\nimpulse 9\n"); return true;
718 case K_KP_0: localcmd("\nimpulse 10\n"); return true;
721 mv_selection_keyboard = 1;
722 mv_selection = MapVote_MoveRight(mv_selection);
725 mv_selection_keyboard = 1;
726 mv_selection = MapVote_MoveLeft(mv_selection);
729 mv_selection_keyboard = 1;
730 mv_selection = MapVote_MoveDown(mv_selection);
733 mv_selection_keyboard = 1;
734 mv_selection = MapVote_MoveUp(mv_selection);
739 if ( mv_selection_keyboard )
740 MapVote_SendChoice(mv_selection);
744 if (nPrimary == K_MOUSE1)
746 mv_selection_keyboard = 0;
747 mv_selection = mv_mouse_selection;
748 if (mv_selection >= 0)
750 imp = min(mv_selection + 1, mv_num_maps);
751 localcmd(strcat("\nimpulse ", ftos(imp), "\n"));
759 void MapVote_UpdateMask()
765 void MapVote_UpdateVotes()
768 for(i = 0; i < mv_num_maps; ++i)
770 if(mv_avail[i] == GTV_AVAILABLE)
773 mv_votes[i] = ReadByte();
781 mv_ownvote = ReadByte()-1;
792 MapVote_UpdateMask();
795 MapVote_UpdateVotes();
798 void Net_MapVote_Picture()
800 int type = ReadByte();
801 mv_preview[type] = true;
802 mv_pics[type] = strzone(ReadPicture());