]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/mapvoting.qc
Turn mapvote availability into a flag
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mapvoting.qc
1 #include "mapvoting.qh"
2 #include "_all.qh"
3
4 #include "g_world.qh"
5 #include "command/cmd.qh"
6 #include "command/getreplies.qh"
7 #include "../common/constants.qh"
8 #include "../common/mapinfo.qh"
9 #include "../common/playerstats.qh"
10 #include "../common/util.qh"
11
12
13 // definitions
14
15 float mapvote_nextthink;
16 float mapvote_keeptwotime;
17 float mapvote_timeout;
18 string mapvote_message;
19 const float MAPVOTE_SCREENSHOT_DIRS_COUNT = 4;
20 string mapvote_screenshot_dirs[MAPVOTE_SCREENSHOT_DIRS_COUNT];
21 float mapvote_screenshot_dirs_count;
22
23 float mapvote_count;
24 float mapvote_count_real;
25 string mapvote_maps[MAPVOTE_COUNT];
26 float mapvote_maps_screenshot_dir[MAPVOTE_COUNT];
27 string mapvote_maps_pakfile[MAPVOTE_COUNT];
28 float mapvote_maps_suggested[MAPVOTE_COUNT];
29 string mapvote_suggestions[MAPVOTE_COUNT];
30 float mapvote_suggestion_ptr;
31 float mapvote_voters;
32 float mapvote_selections[MAPVOTE_COUNT];
33 float mapvote_maps_flags[MAPVOTE_COUNT];
34 float mapvote_run;
35 float mapvote_detail;
36 float mapvote_abstain;
37 .float mapvote;
38
39 entity mapvote_ent;
40
41 /**
42  * Returns the gamtype ID from its name, if type_name isn't a real gametype it
43  * checks for sv_vote_gametype_(type_name)_type
44  */
45 float GameTypeVote_Type_FromString(string type_name)
46 {
47         float type = MapInfo_Type_FromString(type_name);
48         if ( type == 0 )
49                 type = MapInfo_Type_FromString(cvar_string(
50                         strcat("sv_vote_gametype_",type_name,"_type")));
51         return type;
52 }
53
54 int GameTypeVote_AvailabilityStatus(string gtname)
55 {
56         float type = GameTypeVote_Type_FromString(gtname);
57         if( type == 0 )
58                 return GTV_FORBIDDEN;
59
60         if ( autocvar_nextmap != "" )
61         {
62                 if ( !MapInfo_Get_ByName(autocvar_nextmap, false, 0) )
63                         return GTV_FORBIDDEN;
64                 if (!(MapInfo_Map_supportedGametypes & type))
65                         return GTV_FORBIDDEN;
66         }
67
68         return GTV_AVAILABLE;
69 }
70
71 float GameTypeVote_GetMask()
72 {
73         float n, j, gametype_mask;
74         n = tokenizebyseparator(autocvar_sv_vote_gametype_options, " ");
75         n = min(MAPVOTE_COUNT, n);
76         gametype_mask = 0;
77         for(j = 0; j < n; ++j)
78                 gametype_mask |= GameTypeVote_Type_FromString(argv(j));
79         return gametype_mask;
80 }
81
82 string GameTypeVote_MapInfo_FixName(string m)
83 {
84         if ( autocvar_sv_vote_gametype )
85         {
86                 MapInfo_Enumerate();
87                 MapInfo_FilterGametype(GameTypeVote_GetMask(), 0, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
88         }
89         return MapInfo_FixName(m);
90 }
91
92 void MapVote_ClearAllVotes()
93 {
94         FOR_EACH_CLIENT(other)
95                 other.mapvote = 0;
96 }
97
98 void MapVote_UnzoneStrings()
99 {
100         float j;
101         for(j = 0; j < mapvote_count; ++j)
102         {
103                 if ( mapvote_maps[j] )
104                 {
105                         strunzone(mapvote_maps[j]);
106                         mapvote_maps[j] = string_null;
107                 }
108                 if ( mapvote_maps_pakfile[j] )
109                 {
110                         strunzone(mapvote_maps_pakfile[j]);
111                         mapvote_maps_pakfile[j] = string_null;
112                 }
113         }
114 }
115
116 string MapVote_Suggest(string m)
117 {
118         float i;
119         if(m == "")
120                 return "That's not how to use this command.";
121         if(!autocvar_g_maplist_votable_suggestions)
122                 return "Suggestions are not accepted on this server.";
123         if(mapvote_initialized)
124         if(!gametypevote)
125                 return "Can't suggest - voting is already in progress!";
126         m = GameTypeVote_MapInfo_FixName(m);
127         if (!m)
128                 return "The map you suggested is not available on this server.";
129         if(!autocvar_g_maplist_votable_suggestions_override_mostrecent)
130                 if(Map_IsRecent(m))
131                         return "This server does not allow for recent maps to be played again. Please be patient for some rounds.";
132
133         if (!autocvar_sv_vote_gametype)
134         if(!MapInfo_CheckMap(m))
135                 return "The map you suggested does not support the current game mode.";
136         for(i = 0; i < mapvote_suggestion_ptr; ++i)
137                 if(mapvote_suggestions[i] == m)
138                         return "This map was already suggested.";
139         if(mapvote_suggestion_ptr >= MAPVOTE_COUNT)
140         {
141                 i = floor(random() * mapvote_suggestion_ptr);
142         }
143         else
144         {
145                 i = mapvote_suggestion_ptr;
146                 mapvote_suggestion_ptr += 1;
147         }
148         if(mapvote_suggestions[i] != "")
149                 strunzone(mapvote_suggestions[i]);
150         mapvote_suggestions[i] = strzone(m);
151         if(autocvar_sv_eventlog)
152                 GameLogEcho(strcat(":vote:suggested:", m, ":", ftos(self.playerid)));
153         return strcat("Suggestion of ", m, " accepted.");
154 }
155
156 void MapVote_AddVotable(string nextMap, float isSuggestion)
157 {
158         float j, i, o;
159         string pakfile, mapfile;
160
161         if(nextMap == "")
162                 return;
163         for(j = 0; j < mapvote_count; ++j)
164                 if(mapvote_maps[j] == nextMap)
165                         return;
166         // suggestions might be no longer valid/allowed after gametype switch!
167         if(isSuggestion)
168                 if(!MapInfo_CheckMap(nextMap))
169                         return;
170         mapvote_maps[mapvote_count] = strzone(nextMap);
171         mapvote_maps_suggested[mapvote_count] = isSuggestion;
172
173         pakfile = string_null;
174         for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
175         {
176                 mapfile = strcat(mapvote_screenshot_dirs[i], "/", mapvote_maps[i]);
177                 pakfile = whichpack(strcat(mapfile, ".tga"));
178                 if(pakfile == "")
179                         pakfile = whichpack(strcat(mapfile, ".jpg"));
180                 if(pakfile == "")
181                         pakfile = whichpack(strcat(mapfile, ".png"));
182                 if(pakfile != "")
183                         break;
184         }
185         if(i >= mapvote_screenshot_dirs_count)
186                 i = 0; // FIXME maybe network this error case, as that means there is no mapshot on the server?
187         for(o = strstr(pakfile, "/", 0)+1; o > 0; o = strstr(pakfile, "/", 0)+1)
188                 pakfile = substring(pakfile, o, -1);
189
190         mapvote_maps_screenshot_dir[mapvote_count] = i;
191         mapvote_maps_pakfile[mapvote_count] = strzone(pakfile);
192         mapvote_maps_flags[mapvote_count] = GTV_AVAILABLE;
193
194         mapvote_count += 1;
195 }
196
197 void MapVote_Init()
198 {
199         float i;
200         float nmax, smax;
201
202         MapVote_ClearAllVotes();
203         MapVote_UnzoneStrings();
204
205         mapvote_count = 0;
206         mapvote_detail = !autocvar_g_maplist_votable_nodetail;
207         mapvote_abstain = autocvar_g_maplist_votable_abstain;
208
209         if(mapvote_abstain)
210                 nmax = min(MAPVOTE_COUNT - 1, autocvar_g_maplist_votable);
211         else
212                 nmax = min(MAPVOTE_COUNT, autocvar_g_maplist_votable);
213         smax = min3(nmax, autocvar_g_maplist_votable_suggestions, mapvote_suggestion_ptr);
214
215         // we need this for AddVotable, as that cycles through the screenshot dirs
216         mapvote_screenshot_dirs_count = tokenize_console(autocvar_g_maplist_votable_screenshot_dir);
217         if(mapvote_screenshot_dirs_count == 0)
218                 mapvote_screenshot_dirs_count = tokenize_console("maps levelshots");
219         mapvote_screenshot_dirs_count = min(mapvote_screenshot_dirs_count, MAPVOTE_SCREENSHOT_DIRS_COUNT);
220         for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
221                 mapvote_screenshot_dirs[i] = strzone(argv(i));
222
223         if(mapvote_suggestion_ptr)
224                 for(i = 0; i < 100 && mapvote_count < smax; ++i)
225                         MapVote_AddVotable(mapvote_suggestions[floor(random() * mapvote_suggestion_ptr)], true);
226
227         for(i = 0; i < 100 && mapvote_count < nmax; ++i)
228                 MapVote_AddVotable(GetNextMap(), false);
229
230         if(mapvote_count == 0)
231         {
232                 bprint( "Maplist contains no single playable map!  Resetting it to default map list.\n" );
233                 cvar_set("g_maplist", MapInfo_ListAllowedMaps(MapInfo_CurrentGametype(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags()));
234                 if(autocvar_g_maplist_shuffle)
235                         ShuffleMaplist();
236                 localcmd("\nmenu_cmd sync\n");
237                 for(i = 0; i < 100 && mapvote_count < nmax; ++i)
238                         MapVote_AddVotable(GetNextMap(), false);
239         }
240
241         mapvote_count_real = mapvote_count;
242         if(mapvote_abstain)
243                 MapVote_AddVotable("don't care", 0);
244
245         //dprint("mapvote count is ", ftos(mapvote_count), "\n");
246
247         mapvote_keeptwotime = time + autocvar_g_maplist_votable_keeptwotime;
248         mapvote_timeout = time + autocvar_g_maplist_votable_timeout;
249         if(mapvote_count_real < 3 || mapvote_keeptwotime <= time)
250                 mapvote_keeptwotime = 0;
251         mapvote_message = "Choose a map and press its key!";
252
253         MapVote_Spawn();
254 }
255
256 void MapVote_SendPicture(float id)
257 {
258         msg_entity = self;
259         WriteByte(MSG_ONE, SVC_TEMPENTITY);
260         WriteByte(MSG_ONE, TE_CSQC_PICTURE);
261         WriteByte(MSG_ONE, id);
262         WritePicture(MSG_ONE, strcat(mapvote_screenshot_dirs[mapvote_maps_screenshot_dir[id]], "/", mapvote_maps[id]), 3072);
263 }
264
265
266 void MapVote_WriteMask()
267 {
268         float i;
269         if ( mapvote_count < 24 )
270         {
271                 float mask,power;
272                 mask = 0;
273                 for(i = 0, power = 1; i < mapvote_count; ++i, power *= 2)
274                         if(mapvote_maps_flags[i] & GTV_AVAILABLE )
275                                 mask |= power;
276
277                 if(mapvote_count < 8)
278                         WriteByte(MSG_ENTITY, mask);
279                 else if (mapvote_count < 16)
280                         WriteShort(MSG_ENTITY,mask);
281                 else
282                         WriteLong(MSG_ENTITY, mask);
283         }
284         else
285         {
286                 for ( i = 0; i < mapvote_count; ++i )
287                         WriteByte(MSG_ENTITY, mapvote_maps_flags[i]);
288         }
289 }
290
291 float MapVote_SendEntity(entity to, int sf)
292 {
293         float i;
294
295         if(sf & 1)
296                 sf &= ~2; // if we send 1, we don't need to also send 2
297
298         WriteByte(MSG_ENTITY, ENT_CLIENT_MAPVOTE);
299         WriteByte(MSG_ENTITY, sf);
300
301         if(sf & 1)
302         {
303                 // flag 1 == initialization
304                 for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
305                         WriteString(MSG_ENTITY, mapvote_screenshot_dirs[i]);
306                 WriteString(MSG_ENTITY, "");
307                 WriteByte(MSG_ENTITY, mapvote_count);
308                 WriteByte(MSG_ENTITY, mapvote_abstain);
309                 WriteByte(MSG_ENTITY, mapvote_detail);
310                 WriteCoord(MSG_ENTITY, mapvote_timeout);
311
312                 if ( gametypevote )
313                 {
314                         // gametype vote
315                         WriteByte(MSG_ENTITY, 1);
316                         WriteString(MSG_ENTITY, autocvar_nextmap);
317                 }
318                 else if ( autocvar_sv_vote_gametype )
319                 {
320                          // map vote but gametype has been chosen via voting screen
321                         WriteByte(MSG_ENTITY, 2);
322                         WriteString(MSG_ENTITY, MapInfo_Type_ToText(MapInfo_CurrentGametype()));
323                 }
324                 else
325                         WriteByte(MSG_ENTITY, 0); // map vote
326
327                 MapVote_WriteMask();
328
329                 // Send data for the vote options
330                 for(i = 0; i < mapvote_count; ++i)
331                 {
332                         if(mapvote_abstain && i == mapvote_count - 1)
333                         {
334                                 WriteString(MSG_ENTITY, ""); // abstain needs no text
335                                 WriteString(MSG_ENTITY, ""); // abstain needs no pack
336                                 WriteByte(MSG_ENTITY, 0); // abstain needs no screenshot dir
337                                 WriteByte(MSG_ENTITY, GTV_AVAILABLE);
338                         }
339                         else
340                         {
341                                 WriteString(MSG_ENTITY, mapvote_maps[i]);
342                                 WriteString(MSG_ENTITY, mapvote_maps_pakfile[i]);
343                                 WriteByte(MSG_ENTITY, mapvote_maps_screenshot_dir[i]);
344                                 WriteByte(MSG_ENTITY, mapvote_maps_flags[i]);
345                         }
346                 }
347         }
348
349         if(sf & 2)
350         {
351                 // flag 2 == update of mask
352                 MapVote_WriteMask();
353         }
354
355         if(sf & 4)
356         {
357                 if(mapvote_detail)
358                         for(i = 0; i < mapvote_count; ++i)
359                                 if ( mapvote_maps_flags[i] == GTV_AVAILABLE )
360                                         WriteByte(MSG_ENTITY, mapvote_selections[i]);
361
362                 WriteByte(MSG_ENTITY, to.mapvote);
363         }
364
365         return true;
366 }
367
368 void MapVote_Spawn()
369 {
370         Net_LinkEntity(mapvote_ent = spawn(), false, 0, MapVote_SendEntity);
371 }
372
373 void MapVote_TouchMask()
374 {
375         mapvote_ent.SendFlags |= 2;
376 }
377
378 void MapVote_TouchVotes(entity voter)
379 {
380         mapvote_ent.SendFlags |= 4;
381 }
382
383 float MapVote_Finished(float mappos)
384 {
385         if(alreadychangedlevel)
386                 return false;
387
388         string result;
389         float i;
390         float didntvote;
391
392         if(autocvar_sv_eventlog)
393         {
394                 result = strcat(":vote:finished:", mapvote_maps[mappos]);
395                 result = strcat(result, ":", ftos(mapvote_selections[mappos]), "::");
396                 didntvote = mapvote_voters;
397                 for(i = 0; i < mapvote_count; ++i)
398                         if(mapvote_maps_flags[i] & GTV_AVAILABLE )
399                         {
400                                 didntvote -= mapvote_selections[i];
401                                 if(i != mappos)
402                                 {
403                                         result = strcat(result, ":", mapvote_maps[i]);
404                                         result = strcat(result, ":", ftos(mapvote_selections[i]));
405                                 }
406                         }
407                 result = strcat(result, ":didn't vote:", ftos(didntvote));
408
409                 GameLogEcho(result);
410                 if(mapvote_maps_suggested[mappos])
411                         GameLogEcho(strcat(":vote:suggestion_accepted:", mapvote_maps[mappos]));
412         }
413
414         FOR_EACH_REALCLIENT(other)
415                 FixClientCvars(other);
416
417         if(gametypevote)
418         {
419                 if ( GameTypeVote_Finished(mappos) )
420                 {
421                         gametypevote = false;
422                         if(autocvar_nextmap != "")
423                         {
424                                 Map_Goto_SetStr(autocvar_nextmap);
425                                 Map_Goto(0);
426                                 alreadychangedlevel = true;
427                                 return true;
428                         }
429                         else
430                                 MapVote_Init();
431                 }
432                 return false;
433         }
434
435         Map_Goto_SetStr(mapvote_maps[mappos]);
436         Map_Goto(0);
437         alreadychangedlevel = true;
438
439         return true;
440 }
441
442 void MapVote_CheckRules_1()
443 {
444         float i;
445
446         for(i = 0; i < mapvote_count; ++i)
447                 if( mapvote_maps_flags[i] & GTV_AVAILABLE )
448                 {
449                         //dprint("Map ", ftos(i), ": "); dprint(mapvote_maps[i], "\n");
450                         mapvote_selections[i] = 0;
451                 }
452
453         mapvote_voters = 0;
454         FOR_EACH_REALCLIENT(other)
455         {
456                 ++mapvote_voters;
457                 if(other.mapvote)
458                 {
459                         i = other.mapvote - 1;
460                         //dprint("Player ", other.netname, " vote = ", ftos(other.mapvote - 1), "\n");
461                         mapvote_selections[i] = mapvote_selections[i] + 1;
462                 }
463         }
464 }
465
466 float MapVote_CheckRules_2()
467 {
468         float i;
469         float firstPlace, secondPlace, currentPlace;
470         float firstPlaceVotes, secondPlaceVotes, currentVotes;
471         float mapvote_voters_real;
472         string result;
473
474         if(mapvote_count_real == 1)
475                 return MapVote_Finished(0);
476
477         mapvote_voters_real = mapvote_voters;
478         if(mapvote_abstain)
479                 mapvote_voters_real -= mapvote_selections[mapvote_count - 1];
480
481         RandomSelection_Init();
482         currentPlace = 0;
483         currentVotes = -1;
484         for(i = 0; i < mapvote_count_real; ++i)
485                 if ( mapvote_maps_flags[i] & GTV_AVAILABLE )
486                 {
487                         RandomSelection_Add(world, i, string_null, 1, mapvote_selections[i]);
488                         if ( gametypevote &&  mapvote_maps[i] == MapInfo_Type_ToString(MapInfo_CurrentGametype()) )
489                         {
490                                 currentVotes = mapvote_selections[i];
491                                 currentPlace = i;
492                         }
493                 }
494         firstPlaceVotes = RandomSelection_best_priority;
495         if ( autocvar_sv_vote_gametype_default_current && currentVotes == firstPlaceVotes )
496                 firstPlace = currentPlace;
497         else
498                 firstPlace = RandomSelection_chosen_float;
499
500         //dprint("First place: ", ftos(firstPlace), "\n");
501         //dprint("First place votes: ", ftos(firstPlaceVotes), "\n");
502
503         RandomSelection_Init();
504         for(i = 0; i < mapvote_count_real; ++i)
505                 if(i != firstPlace)
506                 if ( mapvote_maps_flags[i] & GTV_AVAILABLE )
507                         RandomSelection_Add(world, i, string_null, 1, mapvote_selections[i]);
508         secondPlace = RandomSelection_chosen_float;
509         secondPlaceVotes = RandomSelection_best_priority;
510         //dprint("Second place: ", ftos(secondPlace), "\n");
511         //dprint("Second place votes: ", ftos(secondPlaceVotes), "\n");
512
513         if(firstPlace == -1)
514                 error("No first place in map vote... WTF?");
515
516         if(secondPlace == -1 || time > mapvote_timeout || (mapvote_voters_real - firstPlaceVotes) < firstPlaceVotes)
517                 return MapVote_Finished(firstPlace);
518
519         if(mapvote_keeptwotime)
520                 if(time > mapvote_keeptwotime || (mapvote_voters_real - firstPlaceVotes - secondPlaceVotes) < secondPlaceVotes)
521                 {
522                         float didntvote;
523                         MapVote_TouchMask();
524                         mapvote_message = "Now decide between the TOP TWO!";
525                         mapvote_keeptwotime = 0;
526                         result = strcat(":vote:keeptwo:", mapvote_maps[firstPlace]);
527                         result = strcat(result, ":", ftos(firstPlaceVotes));
528                         result = strcat(result, ":", mapvote_maps[secondPlace]);
529                         result = strcat(result, ":", ftos(secondPlaceVotes), "::");
530                         didntvote = mapvote_voters;
531                         for(i = 0; i < mapvote_count; ++i)
532                         {
533                                 didntvote -= mapvote_selections[i];
534                                 if(i != firstPlace)
535                                         if(i != secondPlace)
536                                         {
537                                                 result = strcat(result, ":", mapvote_maps[i]);
538                                                 result = strcat(result, ":", ftos(mapvote_selections[i]));
539                                                 if(i < mapvote_count_real)
540                                                 {
541                                                         mapvote_maps_flags[i] &= ~GTV_AVAILABLE;
542                                                 }
543                                         }
544                         }
545                         result = strcat(result, ":didn't vote:", ftos(didntvote));
546                         if(autocvar_sv_eventlog)
547                                 GameLogEcho(result);
548                 }
549
550         return false;
551 }
552
553 void MapVote_Tick()
554 {
555         float keeptwo;
556         float totalvotes;
557
558         keeptwo = mapvote_keeptwotime;
559         MapVote_CheckRules_1(); // count
560         if(MapVote_CheckRules_2()) // decide
561                 return;
562
563         totalvotes = 0;
564         FOR_EACH_REALCLIENT(other)
565         {
566                 // hide scoreboard again
567                 if(other.health != 2342)
568                 {
569                         other.health = 2342;
570                         other.impulse = 0;
571                         if(IS_REAL_CLIENT(other))
572                         {
573                                 msg_entity = other;
574                                 WriteByte(MSG_ONE, SVC_FINALE);
575                                 WriteString(MSG_ONE, "");
576                         }
577                 }
578
579                 // clear possibly invalid votes
580                 if ( !(mapvote_maps_flags[other.mapvote-1] & GTV_AVAILABLE) )
581                         other.mapvote = 0;
582                 // use impulses as new vote
583                 if(other.impulse >= 1 && other.impulse <= mapvote_count)
584                         if( mapvote_maps_flags[other.impulse - 1] & GTV_AVAILABLE )
585                         {
586                                 other.mapvote = other.impulse;
587                                 MapVote_TouchVotes(other);
588                         }
589                 other.impulse = 0;
590
591                 if(other.mapvote)
592                         ++totalvotes;
593         }
594
595         MapVote_CheckRules_1(); // just count
596 }
597
598 void MapVote_Start()
599 {
600         // if mapvote is already running, don't do this initialization again
601         if(mapvote_run) { return; }
602
603         // don't start mapvote until after playerstats gamereport is sent
604         if(PlayerStats_GameReport_DelayMapVote) { return; }
605
606         MapInfo_Enumerate();
607         if(MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1))
608                 mapvote_run = true;
609 }
610
611 void MapVote_Think()
612 {
613         if(!mapvote_run)
614                 return;
615
616         if(alreadychangedlevel)
617                 return;
618
619         if(time < mapvote_nextthink)
620                 return;
621         //dprint("tick\n");
622
623         mapvote_nextthink = time + 0.5;
624
625         if(!mapvote_initialized)
626         {
627                 if(autocvar_rescan_pending == 1)
628                 {
629                         cvar_set("rescan_pending", "2");
630                         localcmd("fs_rescan\nrescan_pending 3\n");
631                         return;
632                 }
633                 else if(autocvar_rescan_pending == 2)
634                 {
635                         return;
636                 }
637                 else if(autocvar_rescan_pending == 3)
638                 {
639                         // now build missing mapinfo files
640                         if(!MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1))
641                                 return;
642
643                         // we're done, start the timer
644                         cvar_set("rescan_pending", "0");
645                 }
646
647                 mapvote_initialized = true;
648                 if(DoNextMapOverride(0))
649                         return;
650                 if(!autocvar_g_maplist_votable || player_count <= 0)
651                 {
652                         GotoNextMap(0);
653                         return;
654                 }
655
656                 if(autocvar_sv_vote_gametype) { GameTypeVote_Start(); }
657                 else if(autocvar_nextmap == "") { MapVote_Init(); }
658         }
659
660         MapVote_Tick();
661 }
662
663 float GameTypeVote_SetGametype(float type)
664 {
665         if (MapInfo_CurrentGametype() == type)
666                 return true;
667
668         float tsave = MapInfo_CurrentGametype();
669
670         MapInfo_SwitchGameType(type);
671
672         MapInfo_Enumerate();
673         MapInfo_FilterGametype(type, MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
674         if(MapInfo_count > 0)
675         {
676                 // update lsmaps in case the gametype changed, this way people can easily list maps for it
677                 if(lsmaps_reply != "") { strunzone(lsmaps_reply); }
678                 lsmaps_reply = strzone(getlsmaps());
679                 bprint("Game type successfully switched to ", MapInfo_Type_ToString(type), "\n");
680         }
681         else
682         {
683                 bprint("Cannot use this game type: no map for it found\n");
684                 MapInfo_SwitchGameType(tsave);
685                 MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
686                 return false;
687         }
688
689         //localcmd("gametype ", MapInfo_Type_ToString(type), "\n");
690
691         cvar_set("g_maplist", MapInfo_ListAllowedMaps(type, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags()) );
692         if(autocvar_g_maplist_shuffle)
693                 ShuffleMaplist();
694
695         return true;
696 }
697
698 float gametypevote_finished;
699 float GameTypeVote_Finished(float pos)
700 {
701         if(!gametypevote || gametypevote_finished)
702                 return false;
703
704         if ( !GameTypeVote_SetGametype(GameTypeVote_Type_FromString(mapvote_maps[pos])) )
705         {
706                 dprint("Selected gametype is not supported by any map");
707         }
708
709         localcmd("sv_vote_gametype_hook_all\n");
710         localcmd("sv_vote_gametype_hook_", mapvote_maps[pos], "\n");
711
712         gametypevote_finished = true;
713
714         return true;
715 }
716
717 float GameTypeVote_AddVotable(string nextMode)
718 {
719         float j;
720         if ( nextMode == "" || GameTypeVote_Type_FromString(nextMode) == 0 )
721                 return false;
722         for(j = 0; j < mapvote_count; ++j)
723                 if(mapvote_maps[j] == nextMode)
724                         return false;
725
726         mapvote_maps[mapvote_count] = strzone(nextMode);
727         mapvote_maps_suggested[mapvote_count] = false;
728
729         mapvote_maps_screenshot_dir[mapvote_count] = 0;
730         mapvote_maps_pakfile[mapvote_count] = strzone("");
731         mapvote_maps_flags[mapvote_count] = GameTypeVote_AvailabilityStatus(nextMode);
732
733         mapvote_count += 1;
734
735         return true;
736
737 }
738
739 float GameTypeVote_Start()
740 {
741         float j;
742         MapVote_ClearAllVotes();
743         MapVote_UnzoneStrings();
744
745         mapvote_count = 0;
746         mapvote_timeout = time + autocvar_sv_vote_gametype_timeout;
747         mapvote_abstain = 0;
748         mapvote_detail = !autocvar_g_maplist_votable_nodetail;
749
750         float n = tokenizebyseparator(autocvar_sv_vote_gametype_options, " ");
751         n = min(MAPVOTE_COUNT, n);
752
753         float really_available, which_available;
754         really_available = 0;
755         which_available = -1;
756         for(j = 0; j < n; ++j)
757         {
758                 if ( GameTypeVote_AddVotable(argv(j)) )
759                 if ( mapvote_maps_flags[j] & GTV_AVAILABLE )
760                 {
761                         really_available++;
762                         which_available = j;
763                 }
764         }
765
766         mapvote_count_real = mapvote_count;
767
768         gametypevote = 1;
769
770         if ( really_available == 0 )
771         {
772                 if ( mapvote_count > 0 )
773                         strunzone(mapvote_maps[0]);
774                 mapvote_maps[0] = strzone(MapInfo_Type_ToString(MapInfo_CurrentGametype()));
775                 //GameTypeVote_Finished(0);
776                 MapVote_Finished(0);
777                 return false;
778         }
779         if ( really_available == 1 )
780         {
781                 //GameTypeVote_Finished(which_available);
782                 MapVote_Finished(which_available);
783                 return false;
784         }
785
786         mapvote_count_real = mapvote_count;
787
788         mapvote_keeptwotime = time + autocvar_sv_vote_gametype_keeptwotime;
789         if(mapvote_count_real < 3 || mapvote_keeptwotime <= time)
790                 mapvote_keeptwotime = 0;
791
792         MapVote_Spawn();
793
794         return true;
795 }