Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
cl_mapvoting.qc
Go to the documentation of this file.
1#include "cl_mapvoting.qh"
2
3#include "net.qh"
4
5#include <client/draw.qh>
6#include <client/hud/_mod.qh>
8#include <common/util.qh>
9
10// MapVote (#21)
11
13{
14 // allow saving cvars that aesthetically change the panel into hud skin files
15 HUD_Write_Cvar("hud_panel_mapvote_highlight_border");
16}
17
18float xmin, xmax, ymin, ymax;
19
20const float MV_FADETIME = 0.2;
21
22bool PreviewExists(string name)
23{
25 return false;
26
27 if (fexists(strcat(name, ".tga"))
28 || fexists(strcat(name, ".png"))
29 || fexists(strcat(name, ".jpg"))
30 || fexists(strcat(name, ".pcx")))
31 return true;
32
33 return false;
34}
35
36string MapVote_FormatItem(int id, string text, int _count, float maxwidth, vector fontsize)
37{
38 TC(int, id);
39 string post, pre = strcat(itos(id + 1), ". ");
40 if (mv_detail)
41 {
42 if (_count == 1)
43 post = _(" (1 vote)");
44 else if (_count >= 0 && (mv_flags[id] & GTV_AVAILABLE))
45 post = sprintf(_(" (%d votes)"), _count);
46 else
47 post = "";
48 }
49 else
50 post = "";
51
52 maxwidth -= stringwidth_nocolors(pre, fontsize) + stringwidth_nocolors(post, fontsize);
53 text = textShortenToWidth(text, maxwidth, fontsize, stringwidth_nocolors);
54 return strcat(pre, text, post);
55}
56
58{
59 TC(int, id);
60
61 if (!(mv_flags[id] & GTV_AVAILABLE))
62 return '1 1 1';
63 if (id == mv_ownvote)
64 return '0 1 0';
65 if (id == mv_selection)
66 return '1 1 0';
67
68 float time_frac = (time - mv_select_lasttime[id]) / MV_FADETIME;
69 return '1 1 0' + eZ * (time_frac >= 1 ? 1 : sqrt(time_frac));
70}
71
72void GameTypeVote_DrawGameTypeItem(vector pos, float maxh, float tsize, string gtype /* unused */, string pic, int _count, int id, int most_votes)
73{
74 TC(int, id);
75
76 // Find the correct alpha
77 float alpha;
78 if (!(mv_flags_start[id] & GTV_AVAILABLE))
79 alpha = 0.2; // The gametype isn't supported by the map
80 else if (!(mv_flags[id] & GTV_AVAILABLE) && mv_reduce_alpha)
81 alpha = mv_reduce_alpha; // Fade away if not one of the top 2 choice
82 else
83 alpha = 1; // Normal, full alpha
85
86 // Bounding box details
87 float rect_margin = hud_fontsize.y * 0.5;
88
91 maxh -= 2 * (rect_margin + autocvar_hud_panel_mapvote_highlight_border);
92 tsize -= 2 * (rect_margin + autocvar_hud_panel_mapvote_highlight_border);
93
94 vector rect_pos = pos - '0.5 0.5 0' * rect_margin;
95 vector rect_size = '1 1 0';
96 rect_size.x = tsize + rect_margin;
97 rect_size.y = maxh + rect_margin;
98
99 vector rgb = MapVote_RGB(id);
100 float time_frac;
101 if (id == mv_ownvote)
102 {
103 // Highlight current vote
104 time_frac = 0;
105 mv_select_lasttime[id] = time; // pretend it's selected, to also enlarge icon
106 drawfill(rect_pos, rect_size, rgb, 0.1 * alpha, DRAWFLAG_NORMAL);
108 }
109 else if (mv_flags[id] & GTV_AVAILABLE)
110 {
111 // Highlight selected item
112 if (id == mv_selection)
113 time_frac = 0;
114 else
115 time_frac = (time - mv_select_lasttime[id]) / MV_FADETIME;
116 if (time_frac < 1)
117 drawfill(rect_pos, rect_size, '1 1 1', 0.1 * (1 - sqrt(time_frac)) * panel_fg_alpha, DRAWFLAG_NORMAL);
118 }
119 else
120 time_frac = 1;
121
122 vector offset = pos;
123
124 float title_gap = gtv_text_size.y * 1.4; // distance between the title and the description
125 pos.y += title_gap;
126 maxh -= title_gap;
127
128 // Evaluate the image size
129 vector image_size = '3 3 0' * gtv_text_size.x;
130 if (maxh < image_size.y)
131 image_size = '1 1 0' * maxh;
132 image_size *= 0.8;
133 float desc_padding = gtv_text_size.x * 0.6;
134 pos.x += image_size.x + desc_padding;
135
136 // Split the description into lines, count lines
137 float desc_tsize = tsize - (image_size.x + desc_padding);
138 string the_label = mv_desc[id];
139 int nlines = 0;
140 if (the_label != "")
141 {
142 string ts;
143 int n = tokenizebyseparator(the_label, "\n");
144 for (int i = 0; i < n && maxh > (nlines + 1) * gtv_text_size_small.y; ++i)
145 {
147 while (getWrappedLine_remaining && maxh > (nlines + 1) * gtv_text_size_small.y)
148 if ((ts = getWrappedLine(desc_tsize, gtv_text_size_small, stringwidth_colors)) != "")
149 ++nlines;
150 }
151 }
152
153 // Center the contents in the bounding box
154 vector old_offset = offset;
155 float old_maxh = maxh;
156 maxh -= max(nlines * gtv_text_size_small.y, image_size.y);
157 if (maxh > 0)
158 offset.y += maxh * 0.5;
159
160 // Draw the title
161 bool is_bold = ((mv_flags[id] & GTV_AVAILABLE)
162 && (mv_tie_winner == id || (mv_tie_winner == -2 && _count == most_votes)));
163 if (is_bold)
165 string title = MapVote_FormatItem(id, mv_data[id], _count, tsize, gtv_text_size);
166 drawstring(offset, title, gtv_text_size, rgb, alpha, DRAWFLAG_NORMAL);
167 if (is_bold)
169
170 // Draw the icon
171 if (pic != "")
172 {
173 vector offset_padding;
174 if (time_frac >= 0 && time_frac < 1)
175 {
176 float extra_padding = min(desc_padding, title_gap);
177 float transition_progress = 1 - sqrt(time_frac);
178 image_size *= transition_progress * (extra_padding / image_size.x) + 1;
179 offset_padding = transition_progress * extra_padding * '-0.5 -0.5 0';
180 }
181 else
182 offset_padding = '0 0 0';
183 drawpic(eY * title_gap + eX * 0.5 * desc_padding + offset + offset_padding, pic, image_size, '1 1 1', alpha, DRAWFLAG_NORMAL);
184 }
185
186 // Draw the description, line by line
187 if (the_label == "")
188 return;
189
190 string ts;
191 int n = tokenizebyseparator(the_label, "\n");
192 nlines = 0;
193 maxh = old_maxh;
194 offset -= old_offset;
195 for (int i = 0; i < n && maxh > (nlines + 1) * gtv_text_size_small.y; ++i)
196 {
198 while (getWrappedLine_remaining && maxh > (nlines + 1) * gtv_text_size_small.y)
199 if ((ts = getWrappedLine(desc_tsize, gtv_text_size_small, stringwidth_colors)) != "")
200 {
201 drawstring(pos + offset, ts, gtv_text_size_small, '1 1 1', alpha, DRAWFLAG_NORMAL);
202 pos.y += gtv_text_size_small.y;
203 ++nlines;
204 }
205 }
206}
207
208void MapVote_DrawMapPicture(string pic, vector pos, vector img_size, float theAlpha)
209{
210 if (pic == "")
211 drawfill(pos, img_size, '0.5 0.5 0.5', 0.7 * theAlpha, DRAWFLAG_NORMAL);
212 else
213 {
214 if (drawgetimagesize(pic) == '0 0 0')
215 drawpic(pos, draw_UseSkinFor("nopreview_map"), img_size, '1 1 1', theAlpha, DRAWFLAG_NORMAL);
216 else
217 drawpic(pos, pic, img_size, '1 1 1', theAlpha, DRAWFLAG_NORMAL);
218 }
219}
220
221void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, string pic, int _count, int id, int most_votes)
222{
223 TC(int, id);
224
225 float rect_margin = hud_fontsize.y * 0.5;
226 pos.x += rect_margin + autocvar_hud_panel_mapvote_highlight_border;
227 pos.y += rect_margin + autocvar_hud_panel_mapvote_highlight_border;
228 isize -= 2 * (rect_margin + autocvar_hud_panel_mapvote_highlight_border);
229 tsize -= 2 * (rect_margin + autocvar_hud_panel_mapvote_highlight_border);
230
231 vector rect_pos = pos - '0.5 0.5 0' * rect_margin;
232 vector rect_size = '1 1 0';
233 rect_size.x = tsize + rect_margin;
234 rect_size.y = isize + rect_margin;
235
236 const float img_ar = 4/3;
237 vector img_size = '0 0 0';
238 img_size.x = min(tsize, isize * img_ar);
239 img_size.y = img_size.x / img_ar;
240 img_size.y -= hud_fontsize.y;
241 img_size.x = img_size.y * img_ar;
242
243 pos.y += (isize - img_size.y - hud_fontsize.y) * 0.5;
244
245 bool is_bold = ((mv_flags[id] & GTV_AVAILABLE)
246 && (mv_tie_winner == id || (mv_tie_winner == -2 && _count == most_votes)));
247 if (is_bold)
249 string label = MapVote_FormatItem(id, map, _count, tsize, hud_fontsize);
250 vector rgb = MapVote_RGB(id);
251
252 float text_size = stringwidth_nocolors(label, hud_fontsize);
253
254 float save_rect_sizex = rect_size.x;
255 rect_size.x = max(img_size.x, text_size) + rect_margin;
256 rect_pos.x += (save_rect_sizex - rect_size.x) * 0.5;
257
258 vector text_pos;
259 text_pos.x = pos.x + (tsize - text_size) * 0.5;
260 text_pos.y = pos.y + img_size.y;
261 text_pos.z = 0;
262
263 pos.x += (tsize - img_size.x) * 0.5;
264
265 float theAlpha = panel_fg_alpha;
266 if (!(mv_flags[id] & GTV_AVAILABLE) && mv_reduce_alpha)
267 theAlpha *= mv_reduce_alpha;
268 else if (mv_winner && mv_winner_alpha)
269 theAlpha *= mv_winner_alpha;
270
271 if (!mv_winner)
272 {
273 if (id == mv_ownvote)
274 {
275 // Highlight current vote
276 drawfill(rect_pos, rect_size, rgb, 0.1 * theAlpha, DRAWFLAG_NORMAL);
278 }
279 else if (mv_flags[id] & GTV_AVAILABLE)
280 {
281 // Highlight selected item
282 float time_frac;
283 if (id == mv_selection)
284 time_frac = 0;
285 else
286 time_frac = (time - mv_select_lasttime[id]) / MV_FADETIME;
287 if (time_frac < 1)
288 drawfill(rect_pos, rect_size, '1 1 1', 0.1 * (1 - sqrt(time_frac)) * panel_fg_alpha, DRAWFLAG_NORMAL);
289 }
290 }
291
292 MapVote_DrawMapPicture(pic, pos, img_size, theAlpha);
293
294 drawstring(text_pos, label, hud_fontsize, rgb, theAlpha, DRAWFLAG_NORMAL);
295 if (is_bold)
297}
298
299void MapVote_DrawAbstain(vector pos, float tsize, int _count, int id)
300{
301 string label = MapVote_FormatItem(id, _("Don't care"), _count, tsize, hud_fontsize);
302
303 float theAlpha = (mv_winner && mv_winner_alpha)
305 : 1;
306 theAlpha *= panel_fg_alpha;
307 pos.x -= 0.5 * stringwidth(label, false, hud_fontsize);
308 drawstring(pos, label, hud_fontsize, MapVote_RGB(id), theAlpha, DRAWFLAG_NORMAL);
309}
310
312{
313 int id = (mv_winner ? mv_winner - 1 : mv_selection);
314
315 float theAlpha = 0;
316 if (id != -1 && (mv_flags[id] & GTV_AVAILABLE)) // if the map is valid, update cache and time
317 {
318 if (mv_suggester_cache == "" && mv_suggester[id] == "")
319 return;
320 if (mv_suggester[id] != "") // update cache to current, set time
321 {
324 theAlpha = 1;
325 }
326 }
327 if (mv_suggester_cache != "" && theAlpha != 1) // figure out alpha for cache if it's partly-faded
328 {
329 float time_frac = (time - mv_suggester_cachetime) / MV_FADETIME;
330 if (time_frac >= 1) // finished fading out, delete cache
331 {
333 return;
334 }
335 theAlpha = 1 - sqrt(time_frac);
336 }
337 if (!theAlpha)
338 return;
339
340 string label = sprintf(_("Suggested by: %s"), ColorTranslateRGB(mv_suggester_cache));
341
342 theAlpha *= panel_fg_alpha;
343 pos.x -= 0.5 * stringwidth(label, true, hud_fontsize);
344 drawcolorcodedstring(pos, label, hud_fontsize, theAlpha, DRAWFLAG_NORMAL);
345}
346
347vector MapVote_GridVec(vector gridspec, int i, int m)
348{
349 TC(int, i); TC(int, m);
350 int r = i % m;
351 return
352 eX * (gridspec.x * r)
353 +
354 eY * (gridspec.y * (i - r) / m);
355}
356
357int MapVote_Selection(vector topleft, vector cellsize, int rows, int columns)
358{
359 if (mv_winner)
360 return -1;
361
363
364 for (int c, r = 0; r < rows; ++r)
365 for (c = 0; c < columns; ++c)
366 if (mousepos.x >= topleft.x + cellsize.x * c
367 && mousepos.x <= topleft.x + cellsize.x * (c + 1)
368 && mousepos.y >= topleft.y + cellsize.y * r
369 && mousepos.y <= topleft.y + cellsize.y * (r + 1))
370 {
371 mv_mouse_selection = r * columns + c;
372 break;
373 }
374
379
381 return mv_selection;
382 return mv_mouse_selection;
383}
384
386void MapVote_Draw(bool should_draw)
387{
388 if (!should_draw)
389 return;
390 //if (intermission != 2) return;
391 if (!mv_active)
392 return;
393
395
396 float center = (vid_conwidth - 1) * 0.5;
397 xmin = vid_conwidth * (gametypevote ? 0.08 : 0.1);
399 ymin = 20;
401
402 if (chat_posy + chat_sizey * 0.5 < vid_conheight * 0.5)
403 ymin += chat_sizey;
404 else
405 ymax -= chat_sizey;
406
407 hud_fontsize = HUD_GetFontsize("hud_fontsize");
408 if (gametypevote)
409 {
412 }
413
414 vector pos;
415 pos.y = ymin;
416 pos.z = 0;
417
420
421 string map = (gametypevote) ? _("Decide the gametype") : _("Vote for a map");
422 if (!mv_winner)
423 {
424 pos.x = center - stringwidth(map, false, hud_fontsize * 2) * 0.5;
425 drawstring(pos, map, hud_fontsize * 2, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
426 }
427 pos.y += hud_fontsize.y * 2;
428
429 if (mv_chosenmap != "")
430 {
431 pos.y += hud_fontsize.y * 0.25;
432 pos.x = center - stringwidth(mv_chosenmap, false, hud_fontsize * 1.5) * 0.5;
434 pos.y += hud_fontsize.y * 1.5;
435 }
436 pos.y += hud_fontsize.y * 0.5;
437
439
440 int i;
441 if (mv_winner)
442 map = mv_entries[mv_winner - 1];
443 else
444 {
445 i = ceil(max(1, mv_timeout - time)); // make sure 0 seconds left never shows up, not even for a frame
446 map = count_seconds(i);
447 }
448 pos.x = center - stringwidth(map, false, hud_fontsize * 1.5) * 0.5;
449 drawstring(pos, map, hud_fontsize * 1.5, '0 1 0', panel_fg_alpha, DRAWFLAG_NORMAL);
450 pos.y += hud_fontsize.y * 1.5;
451 pos.y += hud_fontsize.y * 0.5;
452
453 // base for multi-column stuff...
454 pos.y += hud_fontsize.y;
455 pos.x = xmin;
456 ymin = pos.y;
457 float abstain_spacing = panel_bg_border + hud_fontsize.y;
458 float suggester_spacing = hud_fontsize.y * 1.5;
459 if (mv_abstain)
460 ymax -= abstain_spacing;
461 bool were_suggesters = (mv_suggester[0] != ""); // suggestions are placed at the start
462 if (were_suggesters)
463 ymax -= suggester_spacing;
464
465 // higher than the image itself ratio for mapvote items to reserve space for long map names
466 float item_aspect = (gametypevote) ? 3/1 : 5/3;
467 vector table_size = HUD_GetTableSize_BestItemAR(mv_count_real, vec2(xmax - xmin, ymax - ymin), item_aspect);
468 mv_columns = table_size.x;
469 int rows = table_size.y;
470
471 vector dist = vec2((xmax - xmin) / mv_columns, (ymax - pos.y) / rows);
472
473 // reduce size of too wide items
474 float tmp = vid_conwidth / 3; // max width
475 if (dist.x > tmp)
476 {
477 dist.x = tmp;
478 dist.y = min(dist.y, dist.x / item_aspect);
479 }
480 tmp = vid_conheight / 3; // max height
481 if (dist.y > tmp)
482 {
483 dist.y = tmp;
484 dist.x = min(dist.x, dist.y * item_aspect);
485 }
486
487 // reduce size to fix aspect ratio
488 if (dist.x / dist.y > item_aspect)
489 dist.x = dist.y * item_aspect;
490 else
491 dist.y = dist.x / item_aspect;
492
493 // adjust table pos and size according to the new size
494 float offset = ((xmax - pos.x) - dist.x * mv_columns) * 0.5;
495 xmin = pos.x += offset;
496 xmax -= offset;
497 offset = ((ymax - pos.y) - dist.y * rows) * 0.5;
498 ymax -= 2 * offset;
499
500 // override panel_pos and panel_size
501 panel_pos.x = pos.x;
502 panel_pos.y = pos.y;
503 panel_size.x = xmax - xmin;
504 panel_size.y = ymax - ymin;
506
508 {
509 // FIXME item AR gets slightly changed here...
510 // it's rather hard to avoid it at this point
511 dist.x -= 2 * panel_bg_padding / mv_columns;
512 dist.y -= 2 * panel_bg_padding / rows;
513 xmin = pos.x += panel_bg_padding;
514 ymin = pos.y += panel_bg_padding;
515 xmax -= 2 * panel_bg_padding;
516 ymax -= 2 * panel_bg_padding;
517 }
518
519 mv_selection = MapVote_Selection(pos, dist, rows, mv_columns);
520 if (mv_selection != -1)
522
523 if (mv_reduce_time)
524 mv_reduce_alpha = max(0.2, 1 - (time - mv_reduce_time) ** 2);
525
526 if (mv_winner_time)
527 mv_winner_alpha = max(0.2, 1 - sqrt(max(0, time - mv_winner_time)));
528
529 int most_votes = -1;
530 if (mv_tie_winner == -2)
531 for (i = 0; i < mv_count_real; ++i)
532 if (mv_votes[i] > most_votes)
533 most_votes = mv_votes[i];
534
535 void(vector, float, float, string, string, float, int, int) DrawItem;
536 if (gametypevote)
538 else
539 DrawItem = MapVote_DrawMapItem;
540
541 for (i = 0; i < mv_count_real; ++i)
542 {
543 tmp = mv_votes[i]; // FTEQCC bug: too many array accesses in the function call screw it up
544 map = mv_entries[i];
545 string pic = (mv_preview[i] ? mv_pics[i] : "");
546 DrawItem(pos + MapVote_GridVec(dist, i, mv_columns), dist.y, dist.x, map, pic, tmp, i, most_votes);
547 }
548
549 pos.y = ymax + abstain_spacing;
550 if (mv_abstain)
551 {
552 tmp = mv_votes[i];
553 pos.x = center;
554 MapVote_DrawAbstain(pos, xmax - xmin, tmp, i);
555 pos.y += suggester_spacing;
556 }
557
558 if (mv_winner)
559 {
560 // expand winner map image
561 vector startsize;
562 startsize.z = 0;
563 startsize.y = vid_conheight * 0.1;
564 startsize.x = startsize.y * 4/3;
565 vector startpos = panel_pos + (panel_size - startsize) * 0.5;
566
567 vector endsize;
568 endsize.z = 0;
569 endsize.y = vid_conheight * 0.5;
570 endsize.x = endsize.y * 4/3;
571 vector endpos;
572 endpos.z = 0;
573 endpos.y = panel_pos.y;
574 endpos.x = (vid_conwidth - endsize.x) * 0.5;
575
576 float f = bound(0, sqrt((time - mv_winner_time) * 2), 1);
577 float theAlpha = f;
578 f = min(0.1 + f, 1);
579 vector img_size = endsize * f + startsize * (1 - f);
580 vector img_pos = endpos * f + startpos * (1 - f);
581
582 MapVote_DrawMapPicture(mv_pics[mv_winner - 1], img_pos, img_size, theAlpha);
583
584 if (were_suggesters)
585 {
586 float suggester_startposy = pos.y;
587 float suggester_endposy = panel_pos.y + endsize.y + hud_fontsize.y;
588 pos.y = suggester_endposy * f + suggester_startposy * (1 - f);
589 }
590 }
591
592 if (were_suggesters)
593 {
594 pos.x = center;
596 }
597}
598
600{
601 TC(int, argc);
602
603 if (argc != 2 || !mv_pk3list)
604 {
605 LOG_INFO(_("mv_mapdownload: ^3You're not supposed to use this command on your own!"));
606 return;
607 }
608
609 entity pak;
610 int id = stoi(argv(1));
611 for (pak = mv_pk3list; pak; pak = pak.chain)
612 if (pak.sv_entnum == id)
613 break;
614
615 if (!pak || pak.sv_entnum != id)
616 {
617 LOG_INFO(_("^1Error:^7 Couldn't find pak index."));
618 return;
619 }
620
621 if (PreviewExists(pak.message))
622 {
623 mv_preview[id] = true;
624 return;
625 }
626 else
627 {
628 LOG_INFO(_("Requesting preview..."));
629 localcmd("\ncmd mv_getpicture ", ftos(id), "\n");
630 }
631}
632
633void MapVote_CheckPK3(string pic, string pk3, int id)
634{
635 TC(int, id);
636 entity pak = spawn();
637 pak.message = pic;
638 pak.netname = pk3;
639 pak.sv_entnum = id;
640
641 pak.chain = mv_pk3list;
642 mv_pk3list = pak;
643
644 if (pk3 != "")
645 localcmd("\ncurl --pak ", pk3, "; wait; cl_cmd mv_download ", itos(id), "\n");
646 else
648}
649
650void MapVote_CheckPic(string pic, string pk3, int id)
651{
652 TC(int, id);
653 // never try to retrieve a pic for the "don't care" 'map'
654 if (mv_abstain && id == mv_count - 1)
655 return;
656
657 if (PreviewExists(pic))
658 {
659 mv_preview[id] = true;
660 return;
661 }
662 MapVote_CheckPK3(pic, pk3, id);
663}
664
666{
667 TC(int, pos);
668 int imp;
669 if (pos < 0)
670 imp = mv_count - 1;
671 else
672 imp = pos < 1 ? mv_count - 1 : pos - 1;
673 if (!(mv_flags[imp] & GTV_AVAILABLE) && imp != mv_ownvote)
675 return imp;
676}
678{
679 TC(int, pos);
680 int imp;
681 if (pos < 0)
682 imp = 0;
683 else
684 imp = pos >= mv_count - 1 ? 0 : pos + 1;
685 if (!(mv_flags[imp] & GTV_AVAILABLE) && imp != mv_ownvote)
687 return imp;
688}
689int MapVote_MoveUp(int pos)
690{
691 TC(int, pos);
692 int imp;
693 if (pos < 0)
694 imp = mv_count - 1;
695 else
696 {
697 imp = pos - mv_columns;
698 if (imp < 0)
699 {
700 int mv_rows = ceil(mv_count / mv_columns);
701 if (imp == -mv_columns) // pos == 0
702 imp = mv_columns * mv_rows - 1;
703 else
704 imp += mv_columns * mv_rows - 1;
705 }
706 }
707 if (!(mv_flags[imp] & GTV_AVAILABLE) && imp != mv_ownvote)
709 return imp;
710}
712{
713 TC(int, pos);
714 int imp;
715 if (pos < 0)
716 imp = 0;
717 else
718 {
719 imp = pos + mv_columns;
720 if (imp >= mv_count)
721 {
722 int col = imp % mv_columns;
723 if (col == mv_columns - 1)
724 imp = 0;
725 else
726 imp = col + 1;
727 }
728 }
729 if (!(mv_flags[imp] & GTV_AVAILABLE) && imp != mv_ownvote)
731 return imp;
732}
733
734float MapVote_InputEvent(int bInputType, float nPrimary, float nSecondary)
735{
736 TC(int, bInputType);
737
738 static int first_digit = 0;
739 if (!mv_active || isdemo())
740 return false;
741
742 if (bInputType == 3)
743 {
744 mousepos.x = nPrimary;
745 mousepos.y = nSecondary;
746 mv_selection_keyboard = false;
747 return true;
748 }
749
750 if (bInputType == 2)
751 {
752 mv_selection_keyboard = false;
753 return false;
754 }
755
756 // at this point bInputType can only be 0 or 1 (key pressed or released)
757 bool key_pressed = (bInputType == 0);
758
759 if (key_pressed)
760 {
761 if (nPrimary == K_ALT) hudShiftState |= S_ALT;
762 if (nPrimary == K_CTRL) hudShiftState |= S_CTRL;
763 if (nPrimary == K_SHIFT) hudShiftState |= S_SHIFT;
764 }
765 else
766 {
767 if (nPrimary == K_ALT) hudShiftState &= ~S_ALT;
768 if (nPrimary == K_CTRL) hudShiftState &= ~S_CTRL;
769 if (nPrimary == K_SHIFT) hudShiftState &= ~S_SHIFT;
770
771 if (nPrimary == K_CTRL)
772 first_digit = 0;
773 }
774
775 /* Key release events must be handled by the engine otherwise the on-press command such as +jump
776 * executed by pressing SPACE before entering the map voting screen won't be followed by the
777 * on-release command (-jump) on key release once entered the map voting screen, causing +jump
778 * to stay active even on the next map and automatically forcing the player to join
779 */
780 if (!key_pressed)
781 return false;
782
783 int imp = 0;
784 switch (nPrimary)
785 {
786 #define IMPULSE_ARROW_CASE(key, func) \
787 case key: \
788 if (!mv_winner) \
789 { \
790 mv_selection_keyboard = true; \
791 mv_selection = func(mv_selection); \
792 } \
793 return true
798 #undef IMPULSE_ARROW_CASE
799 case K_KP_ENTER:
800 case K_ENTER:
801 case K_SPACE:
804 return true;
805 #define IMPULSE_NUM_CASE(num, imp_num) \
806 case '0' + num: \
807 case K_KP_##num: \
808 imp = imp_num; \
809 break
810 IMPULSE_NUM_CASE(1, 1);
811 IMPULSE_NUM_CASE(2, 2);
812 IMPULSE_NUM_CASE(3, 3);
813 IMPULSE_NUM_CASE(4, 4);
814 IMPULSE_NUM_CASE(5, 5);
815 IMPULSE_NUM_CASE(6, 6);
816 IMPULSE_NUM_CASE(7, 7);
817 IMPULSE_NUM_CASE(8, 8);
818 IMPULSE_NUM_CASE(9, 9);
819 IMPULSE_NUM_CASE(0, 10);
820 #undef IMPULSE_NUM_CASE
821 }
822
823 if (imp && hudShiftState & S_CTRL)
824 {
825 if (!first_digit)
826 {
827 first_digit = imp % 10;
828 return true;
829 }
830 else
831 imp = first_digit * 10 + (imp % 10);
832 }
833
834 if (nPrimary == K_MOUSE1)
835 {
836 mv_selection_keyboard = false;
838 if (mv_selection >= 0)
840 }
841
842 if (nPrimary == K_MOUSE2)
843 return true; // do nothing
844
845 if (imp)
846 {
847 if (!mv_winner && imp <= mv_count)
849 return true;
850 }
851
852 return false;
853}
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
float xmin
void Cmd_MapVote_MapDownload(int argc)
void MapVote_Draw_Export(int fh)
void GameTypeVote_DrawGameTypeItem(vector pos, float maxh, float tsize, string gtype, string pic, int _count, int id, int most_votes)
void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, string pic, int _count, int id, int most_votes)
int MapVote_MoveRight(int pos)
vector MapVote_GridVec(vector gridspec, int i, int m)
void MapVote_CheckPic(string pic, string pk3, int id)
void MapVote_DrawMapPicture(string pic, vector pos, vector img_size, float theAlpha)
float ymin
void MapVote_CheckPK3(string pic, string pk3, int id)
#define IMPULSE_ARROW_CASE(key, func)
bool PreviewExists(string name)
int MapVote_Selection(vector topleft, vector cellsize, int rows, int columns)
string MapVote_FormatItem(int id, string text, int _count, float maxwidth, vector fontsize)
int MapVote_MoveLeft(int pos)
float xmax
float MapVote_InputEvent(int bInputType, float nPrimary, float nSecondary)
vector MapVote_RGB(int id)
void MapVote_DrawSuggester(vector pos)
int MapVote_MoveUp(int pos)
void MapVote_DrawAbstain(vector pos, float tsize, int _count, int id)
#define IMPULSE_NUM_CASE(num, imp_num)
float ymax
int MapVote_MoveDown(int pos)
const float MV_FADETIME
void MapVote_Draw(bool should_draw)
Draws map vote or gametype vote.
int mv_columns
bool mv_selection_keyboard
int mv_tie_winner
int mv_selection
float mv_select_lasttime[MAPVOTE_COUNT]
vector gtv_text_size_small
int mv_flags_start[MAPVOTE_COUNT]
(gt) mv_flags initially
bool autocvar_cl_readpicture_force
int mv_mouse_selection
float mv_winner_time
float mv_reduce_alpha
string mv_chosenmap
bool mv_active
float mv_suggester_cachetime
string mv_suggester_cache
string mv_desc[MAPVOTE_COUNT]
(gt) gametype description
entity mv_pk3list
vector gtv_text_size
int mv_ownvote
string mv_data[MAPVOTE_COUNT]
(shared) map pk3 name/gametype human readable name
bool gametypevote
float autocvar_hud_panel_mapvote_highlight_border
float mv_winner_alpha
float mv_preview[MAPVOTE_COUNT]
(shared) whether there is a pic
string mv_pics[MAPVOTE_COUNT]
(shared) pic file location
void drawborderlines(float thickness, vector pos, vector dim, vector color, float theAlpha, float drawflag)
Definition draw.qc:5
#define drawcolorcodedstring(position, text, scale, alpha, flag)
Definition draw.qh:30
#define drawstring(position, text, scale, rgb, alpha, flag)
Definition draw.qh:27
#define drawpic(position, pic, size, rgb, alpha, flag)
Definition draw.qh:21
#define draw_beginBoldFont()
Definition draw.qh:4
#define drawfill(position, size, rgb, alpha, flag)
Definition draw.qh:36
#define draw_endBoldFont()
Definition draw.qh:5
float alpha
Definition items.qc:13
vector hud_fontsize
Definition main.qh:77
string textShortenToWidth(string theText, float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
Definition util.qc:1183
string getWrappedLine(float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
Definition util.qc:1143
string getWrappedLine_remaining
Definition util.qh:147
#define count_seconds(time)
Definition counting.qh:56
const float DRAWFLAG_NORMAL
vector drawgetimagesize(string pic)
float time
#define stringwidth
#define spawn
#define tokenize_console
#define tokenizebyseparator
ERASEABLE bool fexists(string f)
Definition file.qh:4
void HUD_Panel_LoadCvars()
Definition hud.qc:215
vector HUD_GetFontsize(string cvarname)
Definition hud.qc:112
vector HUD_GetTableSize_BestItemAR(int item_count, vector psize, float item_aspect)
Definition hud.qc:172
void HUD_Scale_Disable()
Definition hud.qc:84
vector panel_size
Definition hud.qh:163
float panel_fg_alpha
Definition hud.qh:169
const int S_SHIFT
Definition hud.qh:129
int hudShiftState
Definition hud.qh:128
const int S_ALT
Definition hud.qh:131
float panel_bg_padding
Definition hud.qh:174
float panel_bg_border
Definition hud.qh:172
float chat_sizey
Definition hud.qh:183
#define HUD_Panel_DrawBg()
Definition hud.qh:55
vector panel_pos
Definition hud.qh:162
float chat_posy
Definition hud.qh:182
vector mousepos
Definition hud.qh:103
const int S_CTRL
Definition hud.qh:130
#define HUD_Write_Cvar(cvar)
Definition hud_config.qh:40
#define stoi(s)
Definition int.qh:4
#define itos(i)
Definition int.qh:6
float K_SHIFT
Definition keycodes.qc:22
float K_UPARROW
Definition keycodes.qc:15
float K_DOWNARROW
Definition keycodes.qc:16
float K_MOUSE1
Definition keycodes.qc:129
float K_CTRL
Definition keycodes.qc:21
float K_RIGHTARROW
Definition keycodes.qc:18
float K_SPACE
Definition keycodes.qc:10
float K_ALT
Definition keycodes.qc:20
float K_ENTER
Definition keycodes.qc:8
float K_LEFTARROW
Definition keycodes.qc:17
float K_MOUSE2
Definition keycodes.qc:130
float K_KP_ENTER
Definition keycodes.qc:74
#define TC(T, sym)
Definition _all.inc:82
noref float vid_conwidth
Definition draw.qh:7
noref float vid_conheight
Definition draw.qh:8
#define LOG_INFO(...)
Definition log.qh:62
void MapVote_WritePlayerVote(int index)
Definition net.qc:43
bool mv_abstain
(shared) if abstaining is possible, false in gametype voting
Definition mapvoting.qh:21
int mv_count
(shared) number of maps/gametypes
Definition mapvoting.qh:14
int mv_flags[MAPVOTE_COUNT]
(shared) map/gametype flags
Definition mapvoting.qh:18
string mv_entries[MAPVOTE_COUNT]
(shared) map name or gametype name
Definition mapvoting.qh:16
int mv_detail
(shared) how much information about the votes/results is revealed
Definition mapvoting.qh:20
float mv_reduce_time
Definition mapvoting.qh:22
int mv_winner
Definition mapvoting.qh:24
int mv_count_real
(shared) number of maps/gametypes, excluding abstain
Definition mapvoting.qh:15
const int GTV_AVAILABLE
Can be voted.
Definition mapvoting.qh:28
int mv_votes[MAPVOTE_COUNT]
(shared) number of votes for the map/gametype
Definition mapvoting.qh:19
string mv_suggester[MAPVOTE_COUNT]
(maps) .netname of the player who suggested the map
Definition mapvoting.qh:17
float mv_timeout
Definition mapvoting.qh:23
string name
Definition menu.qh:30
void localcmd(string command,...)
float isdemo()
float ceil(float f)
float bound(float min, float value, float max)
float sqrt(float f)
float min(float f,...)
string ftos(float f)
string argv(float n)
float max(float f,...)
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
vector
Definition self.qh:96
void
Definition self.qh:76
int int int imp
Definition impulse.qc:90
ERASEABLE string ColorTranslateRGB(string s)
Definition string.qh:194
float stringwidth_nocolors(string s, vector theSize)
Definition string.qh:35
float stringwidth_colors(string s, vector theSize)
Definition string.qh:30
const vector eY
Definition vector.qh:44
const vector eZ
Definition vector.qh:45
const vector eX
Definition vector.qh:43
#define vec2(...)
Definition vector.qh:95