]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/mapvoting.qc
Transifex autosync
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / mapvoting.qc
index 9242a5b6f4d057aa99e95f356cf80098eb2f6a12..b9d19f611dfdd61f6beae00b05181161cd7872d6 100644 (file)
@@ -12,7 +12,6 @@
 #include <server/command/cmd.qh>
 #include <server/command/getreplies.qh>
 #include <server/gamelog.qh>
-#include <server/intermission.qh>
 #include <server/world.qh>
 
 // definitions
@@ -48,10 +47,10 @@ entity mapvote_ent;
  */
 Gametype GameTypeVote_Type_FromString(string type_name)
 {
-       Gametype type = MapInfo_Type_FromString(type_name, false);
+       Gametype type = MapInfo_Type_FromString(type_name, false, false);
        if (type == NULL)
                type = MapInfo_Type_FromString(cvar_string(
-                       strcat("sv_vote_gametype_",type_name,"_type")), false);
+                       strcat("sv_vote_gametype_",type_name,"_type")), false, false);
        return type;
 }
 
@@ -59,11 +58,11 @@ int GameTypeVote_AvailabilityStatus(string type_name)
 {
        int flag = GTV_FORBIDDEN;
 
-       Gametype type = MapInfo_Type_FromString(type_name, false);
+       Gametype type = MapInfo_Type_FromString(type_name, false, false);
        if ( type == NULL )
        {
                type = MapInfo_Type_FromString(cvar_string(
-                       strcat("sv_vote_gametype_",type_name,"_type")), false);
+                       strcat("sv_vote_gametype_",type_name,"_type")), false, false);
                flag |= GTV_CUSTOM;
        }
 
@@ -89,6 +88,10 @@ int GameTypeVote_GetMask()
        gametype_mask = 0;
        for(j = 0; j < n; ++j)
                gametype_mask |= GameTypeVote_Type_FromString(argv(j)).m_flags;
+
+       if (gametype_mask == 0)
+               gametype_mask |= MapInfo_CurrentGametype().m_flags;
+
        return gametype_mask;
 }
 
@@ -197,9 +200,26 @@ void MapVote_AddVotable(string nextMap, bool isSuggestion)
        mapvote_count += 1;
 }
 
+void MapVote_AddVotableMaps(int nmax, int smax)
+{
+       int available_maps = Maplist_Init();
+       int max_attempts = available_maps;
+       if (available_maps >= 2)
+               max_attempts = min(available_maps * 5, 100);
+
+       if (smax && mapvote_suggestion_ptr)
+               for(int i = 0; i < max_attempts && mapvote_count < smax; ++i)
+                       MapVote_AddVotable(mapvote_suggestions[floor(random() * mapvote_suggestion_ptr)], true);
+
+       for (int i = 0; i < max_attempts && mapvote_count < nmax; ++i)
+               MapVote_AddVotable(GetNextMap(), false);
+}
+
+string voted_gametype_string;
+Gametype voted_gametype;
+Gametype match_gametype;
 void MapVote_Init()
 {
-       int i;
        int nmax, smax;
 
        MapVote_ClearAllVotes();
@@ -220,26 +240,10 @@ void MapVote_Init()
        if(mapvote_screenshot_dirs_count == 0)
                mapvote_screenshot_dirs_count = tokenize_console("maps levelshots");
        mapvote_screenshot_dirs_count = min(mapvote_screenshot_dirs_count, MAPVOTE_SCREENSHOT_DIRS_COUNT);
-       for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
+       for(int i = 0; i < mapvote_screenshot_dirs_count; ++i)
                mapvote_screenshot_dirs[i] = strzone(argv(i));
 
-       if(mapvote_suggestion_ptr)
-               for(i = 0; i < 100 && mapvote_count < smax; ++i)
-                       MapVote_AddVotable(mapvote_suggestions[floor(random() * mapvote_suggestion_ptr)], true);
-
-       for(i = 0; i < 100 && mapvote_count < nmax; ++i)
-               MapVote_AddVotable(GetNextMap(), false);
-
-       if(mapvote_count == 0)
-       {
-               bprint( "Maplist contains no single playable map!  Resetting it to default map list.\n" );
-               cvar_set("g_maplist", MapInfo_ListAllowedMaps(MapInfo_CurrentGametype(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags()));
-               if(autocvar_g_maplist_shuffle)
-                       ShuffleMaplist();
-               localcmd("\nmenu_cmd sync\n");
-               for(i = 0; i < 100 && mapvote_count < nmax; ++i)
-                       MapVote_AddVotable(GetNextMap(), false);
-       }
+       MapVote_AddVotableMaps(nmax, smax);
 
        mapvote_count_real = mapvote_count;
        if(mapvote_abstain)
@@ -253,6 +257,16 @@ void MapVote_Init()
                mapvote_keeptwotime = 0;
 
        MapVote_Spawn();
+
+       // If match_gametype is set it means voted_gametype has just been applied (on game type vote end).
+       // In this case apply back match_gametype here so that the "restart" command, if called,
+       // properly restarts the map applying the current game type.
+       // Applying voted_gametype before map vote start is needed to properly initialize map vote.
+       string gametype_custom_string = "";
+       if (gametype_custom_enabled)
+               gametype_custom_string = loaded_gametype_custom_string;
+       if (match_gametype)
+               GameTypeVote_SetGametype(match_gametype, gametype_custom_string, true);
 }
 
 void MapVote_SendPicture(entity to, int id)
@@ -337,6 +351,8 @@ void GameTypeVote_SendOption(int i)
        }
 }
 
+int mapvote_winner;
+float mapvote_winner_time;
 bool MapVote_SendEntity(entity this, entity to, int sf)
 {
        int i;
@@ -344,6 +360,9 @@ bool MapVote_SendEntity(entity this, entity to, int sf)
        if(sf & 1)
                sf &= ~2; // if we send 1, we don't need to also send 2
 
+       if (!mapvote_winner_time)
+               sf &= ~8; // no winner yet
+
        WriteHeader(MSG_ENTITY, ENT_CLIENT_MAPVOTE);
        WriteByte(MSG_ENTITY, sf);
 
@@ -361,14 +380,19 @@ bool MapVote_SendEntity(entity this, entity to, int sf)
                if ( gametypevote )
                {
                        // gametype vote
-                       WriteByte(MSG_ENTITY, 1);
+                       WriteByte(MSG_ENTITY, BIT(0)); // gametypevote_flags
                        WriteString(MSG_ENTITY, autocvar_nextmap);
                }
                else if ( autocvar_sv_vote_gametype )
                {
                        // map vote but gametype has been chosen via voting screen
-                       WriteByte(MSG_ENTITY, 2);
-                       WriteString(MSG_ENTITY, MapInfo_Type_ToText(MapInfo_CurrentGametype()));
+                       WriteByte(MSG_ENTITY, BIT(1)); // gametypevote_flags
+                       string voted_gametype_name;
+                       if (voted_gametype_string == MapInfo_Type_ToString(voted_gametype))
+                               voted_gametype_name = MapInfo_Type_ToText(voted_gametype);
+                       else
+                               voted_gametype_name = cvar_string(strcat("sv_vote_gametype_", voted_gametype_string, "_name"));
+                       WriteString(MSG_ENTITY, voted_gametype_name);
                }
                else
                        WriteByte(MSG_ENTITY, 0); // map vote
@@ -401,6 +425,11 @@ bool MapVote_SendEntity(entity this, entity to, int sf)
                WriteByte(MSG_ENTITY, to.mapvote);
        }
 
+       if(sf & 8)
+       {
+               WriteByte(MSG_ENTITY, mapvote_winner + 1);
+       }
+
        return true;
 }
 
@@ -419,6 +448,13 @@ void MapVote_TouchVotes(entity voter)
        mapvote_ent.SendFlags |= 4;
 }
 
+void MapVote_Winner(int mappos)
+{
+       mapvote_ent.SendFlags |= 8;
+       mapvote_winner_time = time;
+       mapvote_winner = mappos;
+}
+
 bool MapVote_Finished(int mappos)
 {
        if(alreadychangedlevel)
@@ -462,6 +498,7 @@ bool MapVote_Finished(int mappos)
                                Map_Goto_SetStr(autocvar_nextmap);
                                Map_Goto(0);
                                alreadychangedlevel = true;
+                               strfree(voted_gametype_string);
                                return true;
                        }
                        else
@@ -470,8 +507,7 @@ bool MapVote_Finished(int mappos)
                return false;
        }
 
-       Map_Goto_SetStr(mapvote_maps[mappos]);
-       Map_Goto(0);
+       MapVote_Winner(mappos);
        alreadychangedlevel = true;
 
        return true;
@@ -516,18 +552,23 @@ bool MapVote_CheckRules_2()
        RandomSelection_Init();
        currentPlace = 0;
        currentVotes = -1;
+       string current_gametype_string;
+       if (gametype_custom_enabled)
+               current_gametype_string = loaded_gametype_custom_string;
+       else
+               current_gametype_string = MapInfo_Type_ToString(MapInfo_CurrentGametype());
        for(i = 0; i < mapvote_count_real; ++i)
                if ( mapvote_maps_flags[i] & GTV_AVAILABLE )
                {
                        RandomSelection_AddFloat(i, 1, mapvote_selections[i]);
-                       if ( gametypevote &&  mapvote_maps[i] == MapInfo_Type_ToString(MapInfo_CurrentGametype()) )
+                       if ( gametypevote && mapvote_maps[i] == current_gametype_string )
                        {
                                currentVotes = mapvote_selections[i];
                                currentPlace = i;
                        }
                }
        firstPlaceVotes = RandomSelection_best_priority;
-       if ( autocvar_sv_vote_gametype_default_current && firstPlaceVotes == 0 )
+       if (gametypevote && autocvar_sv_vote_gametype_default_current && firstPlaceVotes == 0)
                firstPlace = currentPlace;
        else
                firstPlace = RandomSelection_chosen_float;
@@ -548,8 +589,12 @@ bool MapVote_CheckRules_2()
        if(firstPlace == -1)
                error("No first place in map vote... WTF?");
 
-       if(secondPlace == -1 || time > mapvote_timeout || (mapvote_voters_real - firstPlaceVotes) < firstPlaceVotes)
+       if(secondPlace == -1 || time > mapvote_timeout
+               || (mapvote_voters_real - firstPlaceVotes) < firstPlaceVotes
+               || mapvote_selections[mapvote_count - 1] == mapvote_voters)
+       {
                return MapVote_Finished(firstPlace);
+       }
 
        if(mapvote_keeptwotime)
                if(time > mapvote_keeptwotime || (mapvote_voters_real - firstPlaceVotes - secondPlaceVotes) < secondPlaceVotes)
@@ -585,7 +630,6 @@ bool MapVote_CheckRules_2()
 
 void MapVote_Tick()
 {
-
        MapVote_CheckRules_1(); // count
        if(MapVote_CheckRules_2()) // decide
                return;
@@ -647,6 +691,25 @@ void MapVote_Think()
        if(!mapvote_run)
                return;
 
+       if (mapvote_winner_time)
+       {
+               if (time > mapvote_winner_time + 1)
+               {
+                       if (voted_gametype)
+                       {
+                               // clear match_gametype so that GameTypeVote_SetGametype
+                               // prints the game type switch message
+                               match_gametype = NULL;
+                               GameTypeVote_SetGametype(voted_gametype, voted_gametype_string, true);
+                       }
+
+                       Map_Goto_SetStr(mapvote_maps[mapvote_winner]);
+                       Map_Goto(0);
+                       strfree(voted_gametype_string);
+               }
+               return;
+       }
+
        if(alreadychangedlevel)
                return;
 
@@ -655,6 +718,8 @@ void MapVote_Think()
        //dprint("tick\n");
 
        mapvote_nextthink = time + 0.5;
+       if (mapvote_nextthink > mapvote_timeout - 0.1) // make sure there's no delay when map vote times out
+               mapvote_nextthink = mapvote_timeout + 0.001;
 
        if(!mapvote_initialized)
        {
@@ -694,8 +759,25 @@ void MapVote_Think()
        MapVote_Tick();
 }
 
-bool GameTypeVote_SetGametype(Gametype type)
+// if gametype_string is "" then gametype_string is inferred from Gametype type
+// otherwise gametype_string is the string (short name) of a custom gametype
+bool GameTypeVote_SetGametype(Gametype type, string gametype_string, bool call_hooks)
 {
+       if (!call_hooks)
+       {
+               // custom gametype is disabled because gametype hooks can't be executed
+               gametype_custom_enabled = false;
+       }
+       else
+       {
+               if (gametype_string == "")
+                       gametype_string = MapInfo_Type_ToString(type);
+               gametype_custom_enabled = (gametype_string != MapInfo_Type_ToString(type));
+
+               localcmd("sv_vote_gametype_hook_all\n");
+               localcmd("sv_vote_gametype_hook_", gametype_string, "\n");
+       }
+
        if (MapInfo_CurrentGametype() == type)
                return true;
 
@@ -710,7 +792,9 @@ bool GameTypeVote_SetGametype(Gametype type)
                // update lsmaps in case the gametype changed, this way people can easily list maps for it
                if(lsmaps_reply != "") { strunzone(lsmaps_reply); }
                lsmaps_reply = strzone(getlsmaps());
-               bprint("Game type successfully switched to ", MapInfo_Type_ToString(type), "\n");
+
+               if (!match_gametype) // don't show this msg if we are temporarily switching game type
+                       bprint("Game type successfully switched to ", MapInfo_Type_ToString(type), "\n");
        }
        else
        {
@@ -720,11 +804,7 @@ bool GameTypeVote_SetGametype(Gametype type)
                return false;
        }
 
-       //localcmd("gametype ", MapInfo_Type_ToString(type), "\n");
-
        cvar_set("g_maplist", MapInfo_ListAllowedMaps(type, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags()) );
-       if(autocvar_g_maplist_shuffle)
-               ShuffleMaplist();
 
        return true;
 }
@@ -735,13 +815,16 @@ bool GameTypeVote_Finished(int pos)
        if(!gametypevote || gametypevote_finished)
                return false;
 
-       localcmd("sv_vote_gametype_hook_all\n");
-       localcmd("sv_vote_gametype_hook_", mapvote_maps[pos], "\n");
+       match_gametype = MapInfo_CurrentGametype();
+       voted_gametype = GameTypeVote_Type_FromString(mapvote_maps[pos]);
+       strcpy(voted_gametype_string, mapvote_maps[pos]);
 
-       if ( !GameTypeVote_SetGametype(GameTypeVote_Type_FromString(mapvote_maps[pos])) )
-       {
-               LOG_TRACE("Selected gametype is not supported by any map");
-       }
+       GameTypeVote_SetGametype(voted_gametype, voted_gametype_string, true);
+
+       // save to a cvar so it can be applied back when gametype is temporary
+       // changed on gametype vote end of the next game
+       if (mapvote_maps_flags[pos] & GTV_CUSTOM)
+               cvar_set("_sv_vote_gametype_custom", voted_gametype_string);
 
        gametypevote_finished = true;
 
@@ -798,13 +881,18 @@ bool GameTypeVote_Start()
 
        mapvote_count_real = mapvote_count;
 
-       gametypevote = 1;
+       gametypevote = true;
 
        if ( really_available == 0 )
        {
                if ( mapvote_count > 0 )
                        strunzone(mapvote_maps[0]);
-               mapvote_maps[0] = strzone(MapInfo_Type_ToString(MapInfo_CurrentGametype()));
+               string current_gametype_string;
+               if (gametype_custom_enabled)
+                       current_gametype_string = loaded_gametype_custom_string;
+               else
+                       current_gametype_string = MapInfo_Type_ToString(MapInfo_CurrentGametype());
+               mapvote_maps[0] = strzone(current_gametype_string);
                //GameTypeVote_Finished(0);
                MapVote_Finished(0);
                return false;