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