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