Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
mapvoting.qc
Go to the documentation of this file.
1#include "mapvoting.qh"
2
3#include <common/constants.qh>
4#include <common/mapinfo.qh>
7#include <common/state.qh>
8#include <common/stats.qh>
9#include <common/util.qh>
11#include <server/client.qh>
12#include <server/command/cmd.qh>
14#include <server/gamelog.qh>
15#include <server/world.qh>
16
17// declarations
18
26
27int mapvote_count; // (shared) number of maps/gametypes
28int mapvote_count_real; // (shared) number of maps/gametypes, excluding abstain
29string mapvote_maps[MAPVOTE_COUNT]; // (shared) name of the map/gametype
30int mapvote_maps_screenshot_dir[MAPVOTE_COUNT]; // (shared) where to look for screenshots, set to 0 for gametype voting
31string mapvote_maps_pakfile[MAPVOTE_COUNT]; // (maps) pk3
32string mapvote_maps_suggesters[MAPVOTE_COUNT]; // (maps) netname of the person who suggested the map
33string mapvote_maps_suggestions[MAPVOTE_COUNT]; // (maps) name of the suggested map, later copied into mapvote_maps
34int mapvote_suggestion_ptr; // (maps) index of where the next suggestion should be (starts as 0)
35int mapvote_voters; // (shared) number of human voters present
36int mapvote_selections[MAPVOTE_COUNT]; // (shared) number of votes for the map/gametype
37int mapvote_maps_flags[MAPVOTE_COUNT]; // (shared) map/gametype flags
38int mapvote_ranked[MAPVOTE_COUNT]; // (shared) maps/gametypes ranked by most votes, first = most
39float mapvote_rng[MAPVOTE_COUNT]; // (shared) random() value for each map/gametype to determine tiebreakers
40// Suggestions need mapvote_maps_suggestions (can't use mapvote_maps, since it's shared by gametype voting so will be overridden)
41/* NOTE: mapvote_rng array can be replaced with a randomly selected index of the tie-winner.
42 * If the tie-winner isn't included in the tie, choose the nearest index that is included.
43 * This would use less storage but isn't truly random and can sometimes be predictable.
44 */
46int mapvote_detail; // (shared)
47bool mapvote_abstain; // (shared) set to false in gametype voting
48bool mapvote_show_suggester; // (maps) not read in gametype voting
50
52
53// Returns the gamtype ID from its name, if type_name isn't a real gametype it checks for sv_vote_gametype_(type_name)_type
55{
56 Gametype type = MapInfo_Type_FromString(type_name, false, false);
57 if (type == NULL)
59 strcat("sv_vote_gametype_", type_name, "_type")), false, false);
60 return type;
61}
62
64{
65 int flag = GTV_FORBIDDEN;
66
67 Gametype type = MapInfo_Type_FromString(type_name, false, false);
68 if (type == NULL)
69 {
71 strcat("sv_vote_gametype_", type_name, "_type")), false, false);
72 flag |= GTV_CUSTOM;
73 }
74
75 if (type == NULL)
76 return flag;
77
78 if (get_nextmap() != "")
79 {
80 if (!MapInfo_Get_ByName(get_nextmap(), false, NULL))
81 return flag;
82 if (!(MapInfo_Map_supportedGametypes & type.gametype_flags))
83 return flag;
84 }
85
86 return flag | GTV_AVAILABLE;
87}
88
90{
91 vector gametype_mask = '0 0 0';
93 n = min(MAPVOTE_COUNT, n);
94 for (int i = 0; i < n; ++i)
95 gametype_mask |= GameTypeVote_Type_FromString(argv(i)).gametype_flags;
96
97 if (gametype_mask == '0 0 0')
98 gametype_mask |= MapInfo_CurrentGametype().gametype_flags;
99
100 return gametype_mask;
101}
102
112
114{
115 FOREACH_CLIENT(true, {
116 it.mapvote = 0;
117 });
118}
119
121{
122 for (int i = 0; i < mapvote_count; ++i)
123 {
126 // mapvote_maps_suggesters is set to the netname of the suggester, so can't & doesn't need to be unzoned
127 }
128}
129
130string MapVote_Suggest(entity this, string m)
131{
132 if (m == "")
133 return "That's not how to use this command.";
135 return "Suggestions are not accepted on this server.";
137 return "Can't suggest, voting is already in progress.";
139 if (!m)
140 return "The map you suggested is not available on this server.";
142 return "This server does not allow for recent maps to be played again. Please be patient for some rounds.";
143
145 return "The map you suggested does not support the current gametype.";
146 int i;
147 for (i = 0; i < mapvote_suggestion_ptr; ++i)
148 if (mapvote_maps_suggestions[i] == m)
149 return "This map was already suggested.";
152 else
153 {
156 }
157
158 if (mapvote_maps_suggestions[i] != "")
162
164 GameLogEcho(strcat(":vote:suggested:", m, ":", itos(this.playerid)));
165 return strcat("Suggestion of ", m, " accepted.");
166}
167
168bool MapVote_AddVotable(int from_i)
169{
170 string next_map;
171 if (from_i == -1) // GetNextMap
172 next_map = GetNextMap();
173 else if (from_i == -2) // abstain
174 next_map = "don't care";
175 else
176 next_map = mapvote_maps_suggestions[from_i];
177 if (next_map == "")
178 return false;
179
180 const string suggester = (from_i >= 0 ? mapvote_maps_suggesters[from_i] : "");
181 int i;
182 if (suggester == "")
183 {
184 for (i = 0; i < mapvote_count; ++i)
185 if (mapvote_maps[i] == next_map)
186 return false;
187 }
188 else if (!MapInfo_CheckMap(next_map))
189 {
190 // suggestions might be no longer valid/allowed after gametype switch!
191 return false;
192 }
193
194 string pakfile = string_null;
195 for (i = 0; i < mapvote_screenshot_dirs_count; ++i)
196 {
197 const string mapfile = strcat(mapvote_screenshot_dirs[i], "/", next_map);
198 pakfile = whichpack(strcat(mapfile, ".tga"));
199 if (pakfile == "")
200 pakfile = whichpack(strcat(mapfile, ".jpg"));
201 if (pakfile == "")
202 pakfile = whichpack(strcat(mapfile, ".png"));
203 if (pakfile != "")
204 break;
205 }
207 i = 0; // FIXME maybe network this error case, as that means there is no mapshot on th=e server?
208 for (int o = strstrofs(pakfile, "/", 0) + 1; o > 0; o = strstrofs(pakfile, "/", 0) + 1)
209 pakfile = substring(pakfile, o, -1);
210
216 if (suggester != "" && from_i != mapvote_count)
217 {
218 // swap accepted suggestion with wherever it came from
223 }
224
226 return true;
227}
228
229void MapVote_AddVotableMaps(int nmax, int smax)
230{
231 const int available_maps = Maplist_Init();
232 const int max_attempts = available_maps < 2
233 ? available_maps
234 : min(available_maps * 5, 100);
235
243 if (smax && mapvote_suggestion_ptr)
244 {
245 int i, last_i = mapvote_suggestion_ptr - 1;
247 {
248 // mapvote_suggestion_ptr <= smax, so mapvote_count + fails < mapvote_suggestion_ptr
249 i = floor(random() * (last_i - mapvote_count + 1)) + mapvote_count;
250 if (!MapVote_AddVotable(i))
251 {
252 // replace failed suggestion with the one at the end of the list, and shorten the list
255 mapvote_maps_suggestions[last_i] = "";
256 mapvote_maps_suggesters[last_i] = "";
257 --last_i;
258 }
259 }
260 for (i = mapvote_count; i <= last_i; ++i) // may need to clear unused suggestions,
261 { // if g_maplist_votable_suggestions < mapvote_suggestion_ptr
264 }
265 }
266 for (int att = 0; att < max_attempts && mapvote_count < nmax; ++att)
267 MapVote_AddVotable(-1); // GetNextMap
268
269 mapvote_ranked[0] = 0;
270
272}
273
279{
282
283 mapvote_count = 0;
288
289 const int nmax = (mapvote_abstain)
293
294 // we need this for AddVotable, as that cycles through the screenshot dirs
299 for (int i = 0; i < mapvote_screenshot_dirs_count; ++i)
301
302 MapVote_AddVotableMaps(nmax, smax);
303
305 if (mapvote_abstain)
306 MapVote_AddVotable(-2); // abstain
307
308 //dprint("mapvote count is ", itos(mapvote_count), "\n");
309
315
317
318 /* If match_gametype is set it means voted_gametype has just been applied (on gametype vote end).
319 * In this case apply back match_gametype here so that the "restart" command, if called,
320 * properly restarts the map applying the current gametype.
321 * Applying voted_gametype before map vote start is needed to properly initialize map vote.
322 */
323 if (match_gametype)
324 {
325 const string gametype_custom_string = (gametype_custom_enabled)
327 : "";
328 GameTypeVote_SetGametype(match_gametype, gametype_custom_string, true);
329 }
330}
331
333{
334 msg_entity = to;
335 WriteHeader(MSG_ONE, TE_CSQC_PICTURE);
336 WriteByte(MSG_ONE, id);
338}
339
340
342{
343 int i;
344 if (mapvote_count < 24)
345 {
346 int mask = 0;
347 for (i = 0; i < mapvote_count; ++i)
349 mask |= BIT(i);
350
351 if (mapvote_count < 8)
352 WriteByte(MSG_ENTITY, mask);
353 else if (mapvote_count < 16)
355 else
356 WriteLong(MSG_ENTITY, mask);
357 }
358 else
359 {
360 for (i = 0; i < mapvote_count; ++i)
362 }
363}
364
365// Sends a single map vote option to the client
367{
368 // abstain
369 if (mapvote_abstain && i == mapvote_count - 1)
370 {
371 WriteString(MSG_ENTITY, ""); // abstain needs no text
372 WriteString(MSG_ENTITY, ""); // abstain needs no pack
373 WriteString(MSG_ENTITY, ""); // abstain has no suggester
374 WriteByte(MSG_ENTITY, 0); // abstain needs no screenshot dir
375 }
376 else
377 {
382 }
383}
384
385// Sends a single gametype vote option to the client
387{
388 // abstain
389 if (mapvote_abstain && i == mapvote_count - 1)
390 {
391 WriteString(MSG_ENTITY, ""); // abstain needs no text
393 }
394 else
395 {
396 const string type_name = mapvote_maps[i];
397 WriteString(MSG_ENTITY, type_name);
400 {
402 strcat("sv_vote_gametype_", type_name, "_name")));
404 strcat("sv_vote_gametype_", type_name, "_description")));
406 strcat("sv_vote_gametype_", type_name, "_type")));
407 }
408 }
409}
410
413bool MapVote_SendEntity(entity this, entity to, int sf)
414{
415 int i;
416
417 if (sf & BIT(0))
418 sf &= ~BIT(1); // if we send 1, we don't need to also send 2
419
421 sf &= ~BIT(3); // no winner yet
422
423 WriteHeader(MSG_ENTITY, ENT_CLIENT_MAPVOTE);
425
426 if (sf & BIT(0))
427 {
428 // flag 1 == initialization
429 for (i = 0; i < mapvote_screenshot_dirs_count; ++i)
436
437 if (gametypevote)
438 {
439 // gametype vote
440 WriteByte(MSG_ENTITY, BIT(0)); // gametypevote_flags
442 }
444 {
445 // map vote but gametype has been chosen via voting screen
446 WriteByte(MSG_ENTITY, BIT(1)); // gametypevote_flags
447 const string voted_gametype_name = (voted_gametype_string == MapInfo_Type_ToString(voted_gametype))
449 : cvar_string(strcat("sv_vote_gametype_", voted_gametype_string, "_name"));
450 WriteString(MSG_ENTITY, voted_gametype_name);
451 }
452 else
453 WriteByte(MSG_ENTITY, 0); // map vote
454
456
457 // Send data for the vote options
458 for (i = 0; i < mapvote_count; ++i)
459 {
460 if (gametypevote)
462 else
464 }
465 }
466
467 if (sf & BIT(1)) // flag 2 == update of mask
469
470 if (sf & BIT(2))
471 {
472 if (mapvote_detail)
473 {
474 for (i = 0; i < mapvote_count; ++i)
477
478 if (mapvote_detail == 2) // tell the client who the tie winner will be
480 else if (mapvote_selections[mapvote_ranked[0]] == 0) // no votes yet, don't draw a winner (-1)
482 else // figure out winners yourself (-2)
484 }
485
486 WriteByte(MSG_ENTITY, to.mapvote);
487 }
488
489 if (sf & BIT(3))
491
492 return true;
493}
494
499
501{
502 mapvote_ent.SendFlags |= BIT(1);
503}
504
506{
507 mapvote_ent.SendFlags |= BIT(2);
508}
509
510void MapVote_Winner(int mappos)
511{
512 mapvote_ent.SendFlags |= BIT(3);
514 mapvote_winner = mappos;
515}
516
517bool MapVote_Finished(int mappos)
518{
520 return false;
521
523 {
524 string result = strcat(":vote:finished:", mapvote_maps[mappos],
525 ":", itos(mapvote_selections[mappos]), "::");
526 int didnt_vote = mapvote_voters;
527 for (int i = 0; i < mapvote_count; ++i)
529 {
530 didnt_vote -= mapvote_selections[i];
531 if (i != mappos)
532 result = strcat(result, ":", mapvote_maps[i],
533 ":", itos(mapvote_selections[i]));
534 }
535 result = strcat(result, ":didn't vote:", itos(didnt_vote));
536
538 if (!gametypevote && mapvote_maps_suggesters[mappos] != "")
539 GameLogEcho(strcat(":vote:suggestion_accepted:", mapvote_maps[mappos]));
540 }
541
543 FixClientCvars(it);
544 });
545
546 if (gametypevote)
547 {
548 if (GameTypeVote_Finished(mappos))
549 {
550 gametypevote = false;
551 if (get_nextmap() != "")
552 {
554 Map_Goto(0);
555 alreadychangedlevel = true;
557 return true;
558 }
559 else
560 MapVote_Init();
561 }
562 return false;
563 }
564
565 MapVote_Winner(mappos);
566 alreadychangedlevel = true;
567
568 return true;
569}
570
571void MapVote_ranked_swap(int i, int j, entity pass)
572{
573 TC(int, i); TC(int, j);
574 const int tmp = mapvote_ranked[i];
576 mapvote_ranked[j] = tmp;
577}
579{
580 TC(int, i); TC(int, j);
581 const int ri = mapvote_ranked[i];
582 const int rj = mapvote_ranked[j];
583 const bool avail_i = mapvote_maps_flags[ri] & GTV_AVAILABLE;
584 const bool avail_j = mapvote_maps_flags[rj] & GTV_AVAILABLE;
585 if (avail_j && !avail_i) // i isn't votable, just move it to the end
586 return 1;
587 if (avail_i && !avail_j) // j isn't votable, just move it to the end
588 return -1;
589 if (!avail_i && !avail_j)
590 return 0;
591
592 const int votes_i = mapvote_selections[ri];
593 const int votes_j = mapvote_selections[rj];
594 if (votes_i <= 0 && rj == current_gametype_index) // j is the current and should be used
595 return 1;
596 if (votes_j <= 0 && ri == current_gametype_index) // i is the current and should be used
597 return -1;
598 if (votes_i == votes_j) // randomly choose which goes first
599 return (mapvote_rng[rj] > mapvote_rng[ri]) ? 1 : -1;
600 return votes_j - votes_i; // descending order
601}
602
604{
605 int i;
606 for (i = 0; i < mapvote_count; ++i) // reset all votes
608 {
609 //dprint("Map ", itos(i), ": "); dprint(mapvote_maps[i], "\n");
610 mapvote_selections[i] = 0;
611 }
612
613 mapvote_voters = 0;
614 FOREACH_CLIENT(IS_REAL_CLIENT(it), { // add votes
616 if (it.mapvote)
617 {
618 const int idx = it.mapvote - 1;
619 //dprint("Player ", it.netname, " vote = ", itos(idx), "\n");
620 ++mapvote_selections[idx];
621 }
622 });
623
624 for (i = 0; i < mapvote_count; ++i) // sort by most votes, for any ties choose randomly
625 mapvote_ranked[i] = i; // populate up to mapvote_count, only bother sorting up to mapvote_count_real
627}
628
630{
631 if (mapvote_count_real == 1)
632 return MapVote_Finished(0);
633
634 int mapvote_voters_real = mapvote_voters;
635 if (mapvote_abstain)
636 mapvote_voters_real -= mapvote_selections[mapvote_count - 1]; // excluding abstainers
637
638 //dprint("1st place index: ", itos(mapvote_ranked[0]), "\n");
639 //dprint("1st place votes: ", itos(mapvote_selections[mapvote_ranked[0]]), "\n");
640 //dprint("2nd place index: ", itos(mapvote_ranked[1]), "\n");
641 //dprint("2nd place votes: ", itos(mapvote_selections[mapvote_ranked[1]]), "\n");
642
643 // these are used to check whether even if everyone else all voted for one map,
644 // ... it wouldn't be enough to push it into the top `reduce_count` maps
645 // i.e. reducing can start early
646 int votes_recent = mapvote_selections[mapvote_ranked[0]];
647 int votes_running_total = votes_recent;
648
650 || (mapvote_voters_real - votes_running_total) < votes_recent
651 || mapvote_voters_real == 0) // all abstained
652 return MapVote_Finished(mapvote_ranked[0]); // choose best
653
654 // if mapvote_reduce_count is >= 2, we reduce to the top `reduce_count`, keeping exactly that many
655 // if it's < 2, we keep all maps that received at least 1 vote, as long as there's at least 2
656 int ri, i;
657 const bool keep_exactly = (mapvote_reduce_count >= 2);
658#define REDUCE_REMOVE_THIS(idx) (keep_exactly \
659 ? (idx >= mapvote_reduce_count) \
660 : (mapvote_selections[mapvote_ranked[idx]] <= 0))
661 for (ri = 1; ri < mapvote_count; ++ri)
662 {
663 i = mapvote_ranked[ri];
664 if (REDUCE_REMOVE_THIS(ri))
665 break;
666 votes_recent = mapvote_selections[i];
667 votes_running_total += votes_recent;
668 }
669
671 if ((time > mapvote_reduce_time && (keep_exactly || ri >= 2))
672 || (mapvote_voters_real - votes_running_total) < votes_recent)
673 {
676 string result = ":vote:reduce";
677 int didnt_vote = mapvote_voters;
678 bool remove = false;
679 for (ri = 0; ri < mapvote_count; ++ri)
680 {
681 i = mapvote_ranked[ri];
682 didnt_vote -= mapvote_selections[i];
683 result = strcat(result, ":", mapvote_maps[i],
684 ":", itos(mapvote_selections[i]));
685 if (!remove && REDUCE_REMOVE_THIS(ri))
686 {
687 result = strcat(result, "::"); // separator between maps kept and maps removed
688 remove = true;
689 }
690 if (remove && i < mapvote_count_real)
691 mapvote_maps_flags[i] &= ~GTV_AVAILABLE; // make it not votable
692 }
693 result = strcat(result, ":didn't vote:", itos(didnt_vote));
696 }
697#undef REDUCE_REMOVE_THIS
698
699 return false;
700}
701
703{
706 return;
707
708 FOREACH_CLIENT(true, {
709 if (!IS_REAL_CLIENT(it))
710 {
711 // apply the same special health value to bots too for consistency's sake
712 if (GetResource(it, RES_HEALTH) != 2342)
713 SetResourceExplicit(it, RES_HEALTH, 2342);
714 continue;
715 }
716 // hide scoreboard again
717 if (GetResource(it, RES_HEALTH) != 2342)
718 {
719 SetResourceExplicit(it, RES_HEALTH, 2342); // health in the voting phase
720 CS(it).impulse = 0;
721
722 msg_entity = it;
724 WriteString(MSG_ONE, "");
725 }
726
727 // clear possibly invalid votes
728 if (!(mapvote_maps_flags[it.mapvote-1] & GTV_AVAILABLE))
729 it.mapvote = 0;
730 // use impulses as new vote
731 if (CS(it).impulse >= 1 && CS(it).impulse <= mapvote_count)
733 {
734 it.mapvote = CS(it).impulse;
736 }
737 CS(it).impulse = 0;
738 });
739
740 MapVote_CheckRules_count(); // just count
741}
742
744{
745 // if mapvote is already running, don't do this initialization again
746 if (mapvote_run)
747 return;
748
749 // don't start mapvote until after playerstats gamereport is sent
751 return;
752
755 mapvote_run = true;
756}
757
759{
760 if (!mapvote_run)
761 return;
762
764 {
765 if (time > mapvote_winner_time + 1)
766 {
767 if (voted_gametype)
768 {
769 // clear match_gametype so that GameTypeVote_SetGametype
770 // prints the gametype switch message
773 }
774
776 Map_Goto(0);
778 }
779 return;
780 }
781
783 return;
784
786 return;
787 //dprint("tick\n");
788
789 mapvote_nextthink = time + 0.5;
790 if (mapvote_nextthink > mapvote_timeout - 0.1) // make sure there's no delay when map vote times out
792
794 {
796 {
797 cvar_set("rescan_pending", "2");
798 localcmd("\nfs_rescan\nrescan_pending 3\n");
799 return;
800 }
801 else if (autocvar_rescan_pending == 2)
802 {
803 return;
804 }
805 else if (autocvar_rescan_pending == 3)
806 {
807 // now build missing mapinfo files
809 return;
810
811 // we're done, start the timer
812 cvar_set("rescan_pending", "0");
813 }
814
815 mapvote_initialized = true;
816 if (DoNextMapOverride(0))
817 return;
819 {
820 GotoNextMap(0);
821 return;
822 }
823
826 else if (get_nextmap() == "")
827 MapVote_Init();
828 }
829
830 MapVote_Tick();
831}
832
833// if gametype_string is "" then gametype_string is inferred from Gametype type
834// otherwise gametype_string is the string (short name) of a custom gametype
835bool GameTypeVote_SetGametype(Gametype type, string gametype_string, bool call_hooks)
836{
837 if (gametype_string == "")
838 gametype_string = MapInfo_Type_ToString(type);
839 if (!call_hooks)
840 {
841 // custom gametype is disabled because gametype hooks can't be executed
843 }
844 else
845 {
846 gametype_custom_enabled = (gametype_string != MapInfo_Type_ToString(type));
847
848 localcmd("\nsv_vote_gametype_hook_all\n");
849 localcmd("\nsv_vote_gametype_hook_", gametype_string, "\n");
850 }
851
852 if (MapInfo_CurrentGametype() == type)
853 return true;
854
855 const Gametype tsave = MapInfo_CurrentGametype();
856
858
861 if (MapInfo_count > 0)
862 {
863 // update lsmaps in case the gametype changed, this way people can easily list maps for it
864 if (lsmaps_reply != "")
867
868 if (!match_gametype) // don't show this msg if we are temporarily switching gametype
869 bprint("Gametype successfully switched to ", MapInfo_Type_ToString(type), "\n");
870 }
871 else
872 {
873 bprint("Cannot use this gametype: no map for it found\n");
876 return false;
877 }
878
880 const string gvmaplist = strcat("sv_vote_gametype_", gametype_string, "_maplist");
881 if (cvar_type(gvmaplist) & CVAR_TYPEFLAG_EXISTS)
882 {
883 // force a reset if the provided list is empty
884 if (cvar_string(gvmaplist) == "")
885 do_reset = true;
886 else
887 cvar_set("g_maplist", cvar_string(gvmaplist));
888 }
889 if (do_reset)
891
892 return true;
893}
894
897{
899 return false;
900
904
906
907 // save to a cvar so it can be applied back when gametype is temporary
908 // changed on gametype vote end of the next game
910 cvar_set("_sv_vote_gametype_custom", voted_gametype_string);
911
913
914 return true;
915}
916
917bool GameTypeVote_AddVotable(string nextMode)
918{
919 if (nextMode == "" || GameTypeVote_Type_FromString(nextMode) == NULL)
920 return false;
921
922 for (int i = 0; i < mapvote_count; ++i)
923 if (mapvote_maps[i] == nextMode)
924 return false;
925
928 // suggester will be uninitialized, but it's never read until map voting, before which it has been set
930
932
933 return true;
934}
935
937{
940
941 mapvote_count = 0;
943 mapvote_abstain = false;
945
947 n = min(MAPVOTE_COUNT, n);
948
949 int really_available = 0;
950 int which_available = -1;
951 int i;
952 for (i = 0; i < n; ++i)
953 {
956 {
957 ++really_available;
958 which_available = i;
959 }
960 }
961
963
964 gametypevote = true;
965
966 const string current_gametype_string = (gametype_custom_enabled)
969
970 if (really_available == 0)
971 {
972 if (mapvote_count > 0)
974 mapvote_maps[0] = strzone(current_gametype_string);
976 //GameTypeVote_Finished(0);
978 return false;
979 }
980 if (really_available == 1)
981 {
982 current_gametype_index = which_available;
983 //GameTypeVote_Finished(which_available);
985 return false;
986 }
988 if (autocvar_sv_vote_gametype_default_current) // find current gametype index
989 for (i = 0; i < mapvote_count_real; ++i)
990 if (mapvote_maps[i] == current_gametype_string)
991 {
993 break;
994 }
995
997
1002
1003 MapVote_Spawn();
1004
1005 return true;
1006}
int player_count
Definition api.qh:103
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
Definition bits.qh:8
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
bool SetResourceExplicit(entity e, Resource res_type, float amount)
Sets the resource amount of an entity without calling any hooks.
string netname
Definition powerups.qc:20
void MapVote_Init()
Definition mapvoting.qc:795
bool gametypevote
Definition mapvoting.qc:48
string lsmaps_reply
Definition util.qh:161
const int GTV_CUSTOM
Definition constants.qh:65
const int GTV_FORBIDDEN
Definition constants.qh:63
const int MAPVOTE_COUNT
Definition constants.qh:52
const int GTV_AVAILABLE
Definition constants.qh:64
float CVAR_TYPEFLAG_EXISTS
float time
#define strstrofs
#define tokenize_console
#define tokenizebyseparator
#define pass(name, colormin, colormax)
void GameLogEcho(string s)
Definition gamelog.qc:15
bool autocvar_sv_eventlog
Definition gamelog.qh:3
string getlsmaps()
#define itos(i)
Definition int.qh:6
void Maplist_Close()
void Map_Goto_SetStr(string nextmapname)
bool Map_IsRecent(string m)
int Maplist_Init(void)
void Map_Goto(float reinit)
string GetNextMap(void)
float DoNextMapOverride(float reinit)
void GotoNextMap(float reinit)
bool alreadychangedlevel
#define get_nextmap()
#define TC(T, sym)
Definition _all.inc:82
const int MSG_ENTITY
Definition net.qh:115
#define WriteHeader(to, id)
Definition net.qh:221
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:123
float MapInfo_CheckMap(string s)
Definition mapinfo.qc:1510
float MapInfo_FilterGametype(Gametype pGametype, int pFeatures, int pFlagsRequired, int pFlagsForbidden, bool pAbortOnGenerate)
Definition mapinfo.qc:177
int MapInfo_RequiredFlags()
Definition mapinfo.qc:1679
string MapInfo_Type_ToString(Gametype t)
Definition mapinfo.qc:656
float _MapInfo_FilterGametype(vector pGametype, int pFeatures, int pFlagsRequired, int pFlagsForbidden, bool pAbortOnGenerate)
Definition mapinfo.qc:181
Gametype MapInfo_CurrentGametype()
Definition mapinfo.qc:1490
int MapInfo_Get_ByName(string pFilename, float pAllowGenerate, Gametype pGametypeToSet)
Definition mapinfo.qc:1390
int MapInfo_ForbiddenFlags()
Definition mapinfo.qc:1664
int MapInfo_CurrentFeatures()
Definition mapinfo.qc:1480
string MapInfo_Type_ToText(Gametype t)
Definition mapinfo.qc:661
string MapInfo_ListAllowedMaps(Gametype type, float pRequiredFlags, float pForbiddenFlags)
Definition mapinfo.qc:1542
void MapInfo_SwitchGameType(Gametype t)
Definition mapinfo.qc:1518
Gametype MapInfo_Type_FromString(string gtype, bool dowarn, bool is_q3compat)
Definition mapinfo.qc:621
void MapInfo_Enumerate()
Definition mapinfo.qc:134
string MapInfo_FixName(string s)
Definition mapinfo.qc:1474
vector MapInfo_Map_supportedGametypes
Definition mapinfo.qh:13
float MapInfo_count
Definition mapinfo.qh:166
void localcmd(string command,...)
void cvar_set(string name, string value)
float MSG_ONE
Definition menudefs.qc:56
string substring(string s, float start, float length)
void WriteLong(float data, float dest, float desto)
void WriteString(string data, float dest, float desto)
void WriteChar(float data, float dest, float desto)
float random(void)
void bprint(string text,...)
const string cvar_string(string name)
void WriteShort(float data, float dest, float desto)
void WriteCoord(float data, float dest, float desto)
float min(float f,...)
void strunzone(string s)
void WriteByte(float data, float dest, float desto)
float floor(float f)
string strzone(string s)
string argv(float n)
string string_null
Definition nil.qh:9
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
bool PlayerStats_GameReport_DelayMapVote
#define NULL
Definition post.qh:14
#define remove
Definition pre.qh:9
entity msg_entity
Definition progsdefs.qc:63
float impulse
Definition progsdefs.qc:158
float SVC_FINALE
Definition progsdefs.qc:341
entity result
Definition promise.qc:43
vector
Definition self.qh:92
void FixClientCvars(entity e)
Definition client.qc:997
int playerid
Definition client.qh:82
int mapvote_maps_screenshot_dir[MAPVOTE_COUNT]
Definition mapvoting.qc:30
bool GameTypeVote_Finished(int pos)
Definition mapvoting.qc:896
int mapvote_count_real
Definition mapvoting.qc:28
Gametype match_gametype
Definition mapvoting.qc:276
Gametype voted_gametype
Definition mapvoting.qc:275
string MapVote_Suggest(entity this, string m)
Definition mapvoting.qc:130
int mapvote_winner
Definition mapvoting.qc:411
string GameTypeVote_MapInfo_FixName(string m)
Definition mapvoting.qc:103
const int MAPVOTE_SCREENSHOT_DIRS_COUNT
Definition mapvoting.qc:23
bool MapVote_SendEntity(entity this, entity to, int sf)
Definition mapvoting.qc:413
float mapvote_reduce_time
Definition mapvoting.qc:20
void MapVote_AddVotableMaps(int nmax, int smax)
Definition mapvoting.qc:229
int current_gametype_index
Definition mapvoting.qc:277
bool GameTypeVote_SetGametype(Gametype type, string gametype_string, bool call_hooks)
Definition mapvoting.qc:835
vector GameTypeVote_GetMask()
Definition mapvoting.qc:89
float mapvote_winner_time
Definition mapvoting.qc:412
void MapVote_ClearAllVotes()
Definition mapvoting.qc:113
void MapVote_TouchMask()
Definition mapvoting.qc:500
int mapvote_screenshot_dirs_count
Definition mapvoting.qc:25
string mapvote_maps_pakfile[MAPVOTE_COUNT]
Definition mapvoting.qc:31
int mapvote_detail
Definition mapvoting.qc:46
bool MapVote_CheckRules_decide()
Definition mapvoting.qc:629
void MapVote_Winner(int mappos)
Definition mapvoting.qc:510
bool MapVote_Finished(int mappos)
Definition mapvoting.qc:517
void MapVote_SendOption(int i)
Definition mapvoting.qc:366
void MapVote_Think()
Definition mapvoting.qc:758
string voted_gametype_string
Definition mapvoting.qc:274
void MapVote_Tick()
Definition mapvoting.qc:702
int mapvote_suggestion_ptr
Definition mapvoting.qc:34
int mapvote_count
Definition mapvoting.qc:27
int mapvote_reduce_count
Definition mapvoting.qc:21
float mapvote_timeout
Definition mapvoting.qc:22
bool mapvote_abstain
Definition mapvoting.qc:47
void MapVote_SendPicture(entity to, int id)
Definition mapvoting.qc:332
bool GameTypeVote_Start()
Definition mapvoting.qc:936
bool gametypevote_finished
Definition mapvoting.qc:895
void MapVote_Spawn()
Definition mapvoting.qc:495
void MapVote_UnzoneStrings()
Definition mapvoting.qc:120
void MapVote_TouchVotes(entity voter)
Definition mapvoting.qc:505
void GameTypeVote_SendOption(int i)
Definition mapvoting.qc:386
float mapvote_rng[MAPVOTE_COUNT]
Definition mapvoting.qc:39
Gametype GameTypeVote_Type_FromString(string type_name)
Definition mapvoting.qc:54
int GameTypeVote_AvailabilityStatus(string type_name)
Definition mapvoting.qc:63
string mapvote_maps[MAPVOTE_COUNT]
Definition mapvoting.qc:29
string mapvote_maps_suggestions[MAPVOTE_COUNT]
Definition mapvoting.qc:33
void MapVote_CheckRules_count()
Definition mapvoting.qc:603
bool mapvote_run
Definition mapvoting.qc:45
string mapvote_screenshot_dirs[MAPVOTE_SCREENSHOT_DIRS_COUNT]
Definition mapvoting.qc:24
float mapvote_nextthink
Definition mapvoting.qc:19
bool MapVote_AddVotable(int from_i)
Definition mapvoting.qc:168
bool mapvote_show_suggester
Definition mapvoting.qc:48
entity mapvote_ent
Definition mapvoting.qc:51
int mapvote_voters
Definition mapvoting.qc:35
int mapvote_maps_flags[MAPVOTE_COUNT]
Definition mapvoting.qc:37
int mapvote
Definition mapvoting.qc:49
void MapVote_Start()
Definition mapvoting.qc:743
void MapVote_WriteMask()
Definition mapvoting.qc:341
bool GameTypeVote_AddVotable(string nextMode)
Definition mapvoting.qc:917
int mapvote_selections[MAPVOTE_COUNT]
Definition mapvoting.qc:36
int mapvote_ranked[MAPVOTE_COUNT]
Definition mapvoting.qc:38
string mapvote_maps_suggesters[MAPVOTE_COUNT]
Definition mapvoting.qc:32
void MapVote_ranked_swap(int i, int j, entity pass)
Definition mapvoting.qc:571
int MapVote_ranked_cmp(int i, int j, entity pass)
Definition mapvoting.qc:578
#define REDUCE_REMOVE_THIS(idx)
#define autocvar_g_maplist_votable
Definition mapvoting.qh:10
bool autocvar_sv_vote_gametype
Definition mapvoting.qh:24
int autocvar_g_maplist_votable_detail
Definition mapvoting.qh:15
float autocvar_sv_vote_gametype_reduce_time
Definition mapvoting.qh:27
string autocvar_g_maplist_votable_screenshot_dir
Definition mapvoting.qh:16
float autocvar_sv_vote_gametype_timeout
Definition mapvoting.qh:25
bool autocvar_g_maplist_votable_suggestions_override_mostrecent
Definition mapvoting.qh:18
bool autocvar_g_maplist_votable_show_suggester
Definition mapvoting.qh:12
bool autocvar_sv_vote_gametype_maplist_reset
Definition mapvoting.qh:31
string autocvar_sv_vote_gametype_options
Definition mapvoting.qh:26
float autocvar_g_maplist_votable_timeout
Definition mapvoting.qh:19
bool autocvar_g_maplist_votable_suggestions
Definition mapvoting.qh:17
bool autocvar_sv_vote_gametype_default_current
Definition mapvoting.qh:30
int autocvar_g_maplist_votable_reduce_count
Definition mapvoting.qh:14
bool autocvar_g_maplist_votable_abstain
Definition mapvoting.qh:11
int autocvar_rescan_pending
Definition mapvoting.qh:23
int autocvar_sv_vote_gametype_reduce_count
Definition mapvoting.qh:28
float mapvote_initialized
Definition mapvoting.qh:46
float autocvar_g_maplist_votable_reduce_time
Definition mapvoting.qh:13
int autocvar_sv_vote_gametype_detail
Definition mapvoting.qh:29
ERASEABLE void heapsort(int n, swapfunc_t swap, comparefunc_t cmp, entity pass)
Definition sort.qh:9
ClientState CS(Client this)
Definition state.qh:47
#define strfree(this)
Definition string.qh:59
#define strcpy(this, s)
Definition string.qh:52
#define IS_REAL_CLIENT(v)
Definition utils.qh:17
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:50
string loaded_gametype_custom_string
Definition world.qh:53
bool gametype_custom_enabled
Definition world.qh:52