]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/mapvoting.qc
Fix some issues mentioned by divVerent and add Melanosuchus to credits
[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                 WriteByte(MSG_ENTITY, gametypevote);
260
261                 if(gametypevote)
262                         WriteString(MSG_ENTITY, autocvar_nextmap);
263
264                 MapVote_WriteMask();
265
266                 for(i = 0; i < mapvote_count; ++i)
267                 {
268                         if(mapvote_abstain && i == mapvote_count - 1)
269                         {
270                                 WriteString(MSG_ENTITY, ""); // abstain needs no text
271                                 WriteString(MSG_ENTITY, ""); // abstain needs no pack
272                                 WriteByte(MSG_ENTITY, 0); // abstain needs no screenshot dir
273                                 WriteByte(MSG_ENTITY, GTV_AVAILABLE);
274                         }
275                         else
276                         {
277                                 WriteString(MSG_ENTITY, mapvote_maps[i]);
278                                 WriteString(MSG_ENTITY, mapvote_maps_pakfile[i]);
279                                 WriteByte(MSG_ENTITY, mapvote_maps_screenshot_dir[i]);
280                                 WriteByte(MSG_ENTITY, mapvote_maps_availability[i]);
281                         }
282                 }
283         }
284
285         if(sf & 2)
286         {
287                 // flag 2 == update of mask
288                 MapVote_WriteMask();
289         }
290
291         if(sf & 4)
292         {
293                 if(mapvote_detail)
294                         for(i = 0; i < mapvote_count; ++i)
295                                 if ( mapvote_maps_availability[i] == GTV_AVAILABLE )
296                                         WriteByte(MSG_ENTITY, mapvote_selections[i]);
297
298                 WriteByte(MSG_ENTITY, to.mapvote);
299         }
300
301         return TRUE;
302 }
303
304 void MapVote_Spawn()
305 {
306         Net_LinkEntity(mapvote_ent = spawn(), FALSE, 0, MapVote_SendEntity);
307 }
308
309 void MapVote_TouchMask()
310 {
311         mapvote_ent.SendFlags |= 2;
312 }
313
314 void MapVote_TouchVotes(entity voter)
315 {
316         mapvote_ent.SendFlags |= 4;
317 }
318
319 float MapVote_Finished(float mappos)
320 {
321         if(alreadychangedlevel)
322                 return FALSE;
323
324         string result;
325         float i;
326         float didntvote;
327
328         if(autocvar_sv_eventlog)
329         {
330                 result = strcat(":vote:finished:", mapvote_maps[mappos]);
331                 result = strcat(result, ":", ftos(mapvote_selections[mappos]), "::");
332                 didntvote = mapvote_voters;
333                 for(i = 0; i < mapvote_count; ++i)
334                         if(mapvote_maps_availability[i] == GTV_AVAILABLE )
335                         {
336                                 didntvote -= mapvote_selections[i];
337                                 if(i != mappos)
338                                 {
339                                         result = strcat(result, ":", mapvote_maps[i]);
340                                         result = strcat(result, ":", ftos(mapvote_selections[i]));
341                                 }
342                         }
343                 result = strcat(result, ":didn't vote:", ftos(didntvote));
344
345                 GameLogEcho(result);
346                 if(mapvote_maps_suggested[mappos])
347                         GameLogEcho(strcat(":vote:suggestion_accepted:", mapvote_maps[mappos]));
348         }
349
350         FOR_EACH_REALCLIENT(other)
351                 FixClientCvars(other);
352
353         if(gametypevote)
354         {
355                 if ( GameTypeVote_Finished(mappos) )
356                 {
357                         gametypevote = FALSE;
358                         if(autocvar_nextmap != "")
359                         {
360                                 Map_Goto_SetStr(autocvar_nextmap);
361                                 Map_Goto(0);
362                                 alreadychangedlevel = TRUE;
363                                 return TRUE;
364                         }
365                         else
366                                 MapVote_Init();
367                 }
368                 return FALSE;
369         }
370         
371         Map_Goto_SetStr(mapvote_maps[mappos]);
372         Map_Goto(0);
373         alreadychangedlevel = TRUE;
374         
375         return TRUE;
376 }
377
378 void MapVote_CheckRules_1()
379 {
380         float i;
381
382         for(i = 0; i < mapvote_count; ++i) 
383                 if( mapvote_maps_availability[i] == GTV_AVAILABLE )
384                 {
385                         //dprint("Map ", ftos(i), ": "); dprint(mapvote_maps[i], "\n");
386                         mapvote_selections[i] = 0;
387                 }
388
389         mapvote_voters = 0;
390         FOR_EACH_REALCLIENT(other)
391         {
392                 ++mapvote_voters;
393                 if(other.mapvote)
394                 {
395                         i = other.mapvote - 1;
396                         //dprint("Player ", other.netname, " vote = ", ftos(other.mapvote - 1), "\n");
397                         mapvote_selections[i] = mapvote_selections[i] + 1;
398                 }
399         }
400 }
401
402 float MapVote_CheckRules_2()
403 {
404         float i;
405         float firstPlace, secondPlace, currentPlace;
406         float firstPlaceVotes, secondPlaceVotes, currentVotes;
407         float mapvote_voters_real;
408         string result;
409
410         if(mapvote_count_real == 1)
411                 return MapVote_Finished(0);
412
413         mapvote_voters_real = mapvote_voters;
414         if(mapvote_abstain)
415                 mapvote_voters_real -= mapvote_selections[mapvote_count - 1];
416
417         RandomSelection_Init();
418         currentPlace = 0;
419         currentVotes = -1;
420         for(i = 0; i < mapvote_count_real; ++i) 
421                 if ( mapvote_maps_availability[i] == GTV_AVAILABLE )
422                 {
423                         RandomSelection_Add(world, i, string_null, 1, mapvote_selections[i]);
424                         if ( gametypevote &&  mapvote_maps[i] == MapInfo_Type_ToString(MapInfo_CurrentGametype()) )
425                         {
426                                 currentVotes = mapvote_selections[i];
427                                 currentPlace = i;
428                         }
429                 }
430         firstPlaceVotes = RandomSelection_best_priority;
431         if ( autocvar_sv_vote_gametype_default_current && currentVotes == firstPlaceVotes )
432                 firstPlace = currentPlace;
433         else
434                 firstPlace = RandomSelection_chosen_float;
435         
436         //dprint("First place: ", ftos(firstPlace), "\n");
437         //dprint("First place votes: ", ftos(firstPlaceVotes), "\n");
438
439         RandomSelection_Init();
440         for(i = 0; i < mapvote_count_real; ++i)
441                 if(i != firstPlace)
442                 if ( mapvote_maps_availability[i] == GTV_AVAILABLE )
443                         RandomSelection_Add(world, i, string_null, 1, mapvote_selections[i]);
444         secondPlace = RandomSelection_chosen_float;
445         secondPlaceVotes = RandomSelection_best_priority;
446         //dprint("Second place: ", ftos(secondPlace), "\n");
447         //dprint("Second place votes: ", ftos(secondPlaceVotes), "\n");
448
449         if(firstPlace == -1)
450                 error("No first place in map vote... WTF?");
451
452         if(secondPlace == -1 || time > mapvote_timeout || (mapvote_voters_real - firstPlaceVotes) < firstPlaceVotes)
453                 return MapVote_Finished(firstPlace);
454
455         if(mapvote_keeptwotime)
456                 if(time > mapvote_keeptwotime || (mapvote_voters_real - firstPlaceVotes - secondPlaceVotes) < secondPlaceVotes)
457                 {
458                         float didntvote;
459                         MapVote_TouchMask();
460                         mapvote_message = "Now decide between the TOP TWO!";
461                         mapvote_keeptwotime = 0;
462                         result = strcat(":vote:keeptwo:", mapvote_maps[firstPlace]);
463                         result = strcat(result, ":", ftos(firstPlaceVotes));
464                         result = strcat(result, ":", mapvote_maps[secondPlace]);
465                         result = strcat(result, ":", ftos(secondPlaceVotes), "::");
466                         didntvote = mapvote_voters;
467                         for(i = 0; i < mapvote_count; ++i)
468                         {
469                                 didntvote -= mapvote_selections[i];
470                                 if(i != firstPlace)
471                                         if(i != secondPlace)
472                                         {
473                                                 result = strcat(result, ":", mapvote_maps[i]);
474                                                 result = strcat(result, ":", ftos(mapvote_selections[i]));
475                                                 if(i < mapvote_count_real)
476                                                 {
477                                                         mapvote_maps_availability[i] = GTV_FORBIDDEN;
478                                                 }
479                                         }
480                         }
481                         result = strcat(result, ":didn't vote:", ftos(didntvote));
482                         if(autocvar_sv_eventlog)
483                                 GameLogEcho(result);
484                 }
485
486         return FALSE;
487 }
488
489 void MapVote_Tick()
490 {
491         float keeptwo;
492         float totalvotes;
493
494         keeptwo = mapvote_keeptwotime;
495         MapVote_CheckRules_1(); // count
496         if(MapVote_CheckRules_2()) // decide
497                 return;
498
499         totalvotes = 0;
500         FOR_EACH_REALCLIENT(other)
501         {
502                 // hide scoreboard again
503                 if(other.health != 2342)
504                 {
505                         other.health = 2342;
506                         other.impulse = 0;
507                         if(IS_REAL_CLIENT(other))
508                         {
509                                 msg_entity = other;
510                                 WriteByte(MSG_ONE, SVC_FINALE);
511                                 WriteString(MSG_ONE, "");
512                         }
513                 }
514
515                 // clear possibly invalid votes
516                 if ( mapvote_maps_availability[other.mapvote-1] != GTV_AVAILABLE )
517                         other.mapvote = 0;
518                 // use impulses as new vote
519                 if(other.impulse >= 1 && other.impulse <= mapvote_count)
520                         if( mapvote_maps_availability[other.impulse - 1] == GTV_AVAILABLE )
521                         {
522                                 other.mapvote = other.impulse;
523                                 MapVote_TouchVotes(other);
524                         }
525                 other.impulse = 0;
526
527                 if(other.mapvote)
528                         ++totalvotes;
529         }
530
531         MapVote_CheckRules_1(); // just count
532 }
533
534 void MapVote_Start()
535 {
536         if(mapvote_run)
537                 return;
538
539         // wait for stats to be sent first
540         if(!playerstats_waitforme)
541                 return;
542
543         MapInfo_Enumerate();
544         if(MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1))
545                 mapvote_run = TRUE;
546 }
547
548 void MapVote_Think()
549 {
550         if(!mapvote_run)
551                 return;
552
553         if(alreadychangedlevel)
554                 return;
555
556         if(time < mapvote_nextthink)
557                 return;
558         //dprint("tick\n");
559
560         mapvote_nextthink = time + 0.5;
561
562         if(!mapvote_initialized)
563         {
564                 if(autocvar_rescan_pending == 1)
565                 {
566                         cvar_set("rescan_pending", "2");
567                         localcmd("fs_rescan\nrescan_pending 3\n");
568                         return;
569                 }
570                 else if(autocvar_rescan_pending == 2)
571                 {
572                         return;
573                 }
574                 else if(autocvar_rescan_pending == 3)
575                 {
576                         // now build missing mapinfo files
577                         if(!MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1))
578                                 return;
579
580                         // we're done, start the timer
581                         cvar_set("rescan_pending", "0");
582                 }
583
584                 mapvote_initialized = TRUE;
585                 if(DoNextMapOverride(0))
586                         return;
587                 if(!autocvar_g_maplist_votable || player_count <= 0)
588                 {
589                         GotoNextMap(0);
590                         return;
591                 }
592                 
593                 if(autocvar_sv_vote_gametype) { GameTypeVote_Start(); }
594                 else if(autocvar_nextmap == "") { MapVote_Init(); }
595         }
596
597         MapVote_Tick();
598 }
599
600 float GameTypeVote_SetGametype(float type)
601 {
602         if (MapInfo_CurrentGametype() == type)
603                 return TRUE;
604                 
605         float tsave = MapInfo_CurrentGametype();
606
607         MapInfo_SwitchGameType(type);
608
609         MapInfo_Enumerate();
610         MapInfo_FilterGametype(type, MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
611         if(MapInfo_count > 0)
612         {
613                 // update lsmaps in case the gametype changed, this way people can easily list maps for it
614                 if(lsmaps_reply != "") { strunzone(lsmaps_reply); }
615                 lsmaps_reply = strzone(getlsmaps());
616                 bprint("Game type successfully switched to ", MapInfo_Type_ToString(type), "\n");
617         }
618         else
619         {
620                 bprint("Cannot use this game type: no map for it found\n");
621                 MapInfo_SwitchGameType(tsave);
622                 MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
623                 return FALSE;
624         }
625
626         //localcmd("gametype ", MapInfo_Type_ToString(type), "\n");
627
628         cvar_set("g_maplist", MapInfo_ListAllowedMaps(type, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags()) );
629         if(autocvar_g_maplist_shuffle)
630                 ShuffleMaplist();
631
632         return TRUE;
633 }
634
635 float gametypevote_finished;
636 float GameTypeVote_Finished(float pos)
637 {
638         if(!gametypevote || gametypevote_finished)
639                 return FALSE;
640         
641         if ( !GameTypeVote_SetGametype(MapInfo_Type_FromString(mapvote_maps[pos])) )
642         {
643                 dprint("Selected gametype is not supported by any map");
644         }
645         
646         localcmd("sv_vote_gametype_hook_all\n");
647         localcmd("sv_vote_gametype_hook_", mapvote_maps[pos], "\n");
648         
649         gametypevote_finished = TRUE;
650         
651         return TRUE;
652 }
653
654 float GameTypeVote_AddVotable(string nextMode)
655 {
656         float j;
657         if ( nextMode == "" || MapInfo_Type_FromString(nextMode) == 0 )
658                 return FALSE;
659         for(j = 0; j < mapvote_count; ++j)
660                 if(mapvote_maps[j] == nextMode)
661                         return FALSE;
662         
663         mapvote_maps[mapvote_count] = strzone(nextMode);
664         mapvote_maps_suggested[mapvote_count] = FALSE;
665
666         mapvote_maps_screenshot_dir[mapvote_count] = 0;
667         mapvote_maps_pakfile[mapvote_count] = strzone("");
668         mapvote_maps_availability[mapvote_count] = GameTypeVote_AvailabilityStatus(nextMode);
669
670         mapvote_count += 1;
671         
672         return TRUE;
673         
674 }
675
676 float GameTypeVote_Start()
677 {
678         float j;
679         MapVote_ClearAllVotes();
680         MapVote_UnzoneStrings();
681         
682         mapvote_count = 0;
683         mapvote_timeout = time + autocvar_sv_vote_gametype_timeout;
684         mapvote_abstain = 0;
685         mapvote_detail = !autocvar_g_maplist_votable_nodetail;
686         
687         float n = tokenizebyseparator(autocvar_sv_vote_gametype_options, " ");
688         n = min(MAPVOTE_COUNT, n);
689         
690         float really_available, which_available;
691         really_available = 0;
692         which_available = -1;
693         for(j = 0; j < n; ++j)
694         {
695                 if ( GameTypeVote_AddVotable(argv(j)) )
696                 if ( mapvote_maps_availability[j] == GTV_AVAILABLE )
697                 {
698                         really_available++;
699                         which_available = j;
700                 }
701         }
702
703         mapvote_count_real = mapvote_count;
704         
705         gametypevote = 1;
706         
707         if ( really_available == 0 )
708         {
709                 if ( mapvote_count > 0 )
710                         strunzone(mapvote_maps[0]);
711                 mapvote_maps[0] = strzone(MapInfo_Type_ToString(MapInfo_CurrentGametype()));
712                 //GameTypeVote_Finished(0);
713                 MapVote_Finished(0);
714                 return FALSE;
715         }
716         if ( really_available == 1 )
717         {
718                 //GameTypeVote_Finished(which_available);
719                 MapVote_Finished(which_available);
720                 return FALSE;
721         }
722         
723         mapvote_count_real = mapvote_count;
724
725         mapvote_keeptwotime = time + autocvar_sv_vote_gametype_keeptwotime;
726         if(mapvote_count_real < 3 || mapvote_keeptwotime <= time)
727                 mapvote_keeptwotime = 0;
728         
729         MapVote_Spawn();
730         
731         return TRUE;
732 }