Mervill
2022-11-07 18:18:21 -08:00
committed by GitHub
parent 01e0f60f81
commit 40b556d206
12 changed files with 181 additions and 99 deletions

View File

@@ -43,11 +43,9 @@ public static class PoolManager
(CCVars.DatabaseSqliteDelay.Name, "0", false), (CCVars.DatabaseSqliteDelay.Name, "0", false),
(CCVars.HolidaysEnabled.Name, "false", false), (CCVars.HolidaysEnabled.Name, "false", false),
(CCVars.GameMap.Name, "Empty", true), (CCVars.GameMap.Name, "Empty", true),
(CCVars.GameMapForced.Name, "true", true),
(CCVars.AdminLogsQueueSendDelay.Name, "0", true), (CCVars.AdminLogsQueueSendDelay.Name, "0", true),
(CCVars.NetPVS.Name, "false", true), (CCVars.NetPVS.Name, "false", true),
(CCVars.NPCMaxUpdates.Name, "999999", true), (CCVars.NPCMaxUpdates.Name, "999999", true),
(CCVars.GameMapForced.Name, "true", true),
(CCVars.SysWinTickPeriod.Name, "0", true), (CCVars.SysWinTickPeriod.Name, "0", true),
(CCVars.ContactMinimumThreads.Name, "1", true), (CCVars.ContactMinimumThreads.Name, "1", true),
(CCVars.ContactMultithreadThreshold.Name, "999", true), (CCVars.ContactMultithreadThreshold.Name, "999", true),

View File

@@ -1,7 +1,9 @@
using System.Linq; using System.Linq;
using Content.Server.Administration; using Content.Server.Administration;
using Content.Server.Maps; using Content.Server.Maps;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.CCVar;
using Robust.Shared.Configuration;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -10,6 +12,8 @@ namespace Content.Server.GameTicking.Commands
[AdminCommand(AdminFlags.Round)] [AdminCommand(AdminFlags.Round)]
sealed class ForceMapCommand : IConsoleCommand sealed class ForceMapCommand : IConsoleCommand
{ {
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
public string Command => "forcemap"; public string Command => "forcemap";
public string Description => Loc.GetString("forcemap-command-description"); public string Description => Loc.GetString("forcemap-command-description");
public string Help => Loc.GetString("forcemap-command-help"); public string Help => Loc.GetString("forcemap-command-help");
@@ -25,7 +29,7 @@ namespace Content.Server.GameTicking.Commands
var gameMap = IoCManager.Resolve<IGameMapManager>(); var gameMap = IoCManager.Resolve<IGameMapManager>();
var name = args[0]; var name = args[0];
gameMap.ForceSelectMap(name); _configurationManager.SetCVar(CCVars.GameMap, name);
shell.WriteLine(Loc.GetString("forcemap-command-success", ("map", name))); shell.WriteLine(Loc.GetString("forcemap-command-success", ("map", name)));
} }

View File

@@ -1,8 +1,11 @@
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Content.Server.Station.Systems;
using Content.Server.Station.Components;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Network; using Robust.Shared.Network;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Players; using Robust.Shared.Players;
using System.Text;
namespace Content.Server.GameTicking namespace Content.Server.GameTicking
{ {
@@ -39,13 +42,33 @@ namespace Content.Server.GameTicking
{ {
return string.Empty; return string.Empty;
} }
var playerCount = $"{_playerManager.PlayerCount}"; var playerCount = $"{_playerManager.PlayerCount}";
var map = _gameMapManager.GetSelectedMap();
var mapName = map?.MapName ?? Loc.GetString("game-ticker-no-map-selected"); StringBuilder stationNames = new StringBuilder();
if (_stationSystem.Stations.Count != 0)
{
foreach (EntityUid entUID in _stationSystem.Stations)
{
StationDataComponent? stationData = null;
MetaDataComponent? metaData = null;
if (Resolve(entUID, ref stationData, ref metaData, logMissing: true))
{
if (stationNames.Length > 0)
stationNames.Append('\n');
stationNames.Append(metaData.EntityName);
}
}
}
else
{
stationNames.Append(Loc.GetString("game-ticker-no-map-selected"));
}
var gmTitle = Loc.GetString(Preset.ModeTitle); var gmTitle = Loc.GetString(Preset.ModeTitle);
var desc = Loc.GetString(Preset.Description); var desc = Loc.GetString(Preset.Description);
return Loc.GetString("game-ticker-get-info-text",("roundId", RoundId), ("playerCount", playerCount),("mapName", mapName),("gmTitle", gmTitle),("desc", desc)); return Loc.GetString("game-ticker-get-info-text",("roundId", RoundId), ("playerCount", playerCount),("mapName", stationNames.ToString()),("gmTitle", gmTitle),("desc", desc));
} }
private TickerLobbyReadyEvent GetStatusSingle(ICommonSession player, PlayerGameStatus gameStatus) private TickerLobbyReadyEvent GetStatusSingle(ICommonSession player, PlayerGameStatus gameStatus)

View File

@@ -81,7 +81,29 @@ namespace Content.Server.GameTicking
DefaultMap = _mapManager.CreateMap(); DefaultMap = _mapManager.CreateMap();
_mapManager.AddUninitializedMap(DefaultMap); _mapManager.AddUninitializedMap(DefaultMap);
var startTime = _gameTiming.RealTime; var startTime = _gameTiming.RealTime;
var maps = new List<GameMapPrototype>() { _gameMapManager.GetSelectedMapChecked(true, true) };
var maps = new List<GameMapPrototype>();
// the map might have been force-set by something
// (i.e. votemap or forcemap)
var mainStationMap = _gameMapManager.GetSelectedMap();
if (mainStationMap == null)
{
// otherwise set the map using the config rules
_gameMapManager.SelectMapByConfigRules();
mainStationMap = _gameMapManager.GetSelectedMap();
}
// Small chance the above could return no map.
// ideally SelectMapByConfigRules will always find a valid map
if (mainStationMap != null)
{
maps.Add(mainStationMap);
}
else
{
throw new Exception("invalid config; couldn't select a valid station map!");
}
// Let game rules dictate what maps we should load. // Let game rules dictate what maps we should load.
RaiseLocalEvent(new LoadingMapsEvent(maps)); RaiseLocalEvent(new LoadingMapsEvent(maps));
@@ -149,6 +171,10 @@ namespace Content.Server.GameTicking
LoadMaps(); LoadMaps();
// map has been selected so update the lobby info text
// applies to players who didn't ready up
UpdateInfoText();
StartGamePresetRules(); StartGamePresetRules();
RoundLengthMetric.Set(0); RoundLengthMetric.Set(0);
@@ -423,6 +449,8 @@ namespace Content.Server.GameTicking
_roleBanManager.Restart(); _roleBanManager.Restart();
_gameMapManager.ClearSelectedMap();
// Clear up any game rules. // Clear up any game rules.
ClearGameRules(); ClearGameRules();

View File

@@ -1,6 +1,6 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Content.Server.Chat.Managers;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
@@ -15,29 +15,38 @@ public sealed class GameMapManager : IGameMapManager
[Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IChatManager _chatManager = default!;
[ViewVariables(VVAccess.ReadOnly)]
[ViewVariables]
private readonly Queue<string> _previousMaps = new(); private readonly Queue<string> _previousMaps = new();
[ViewVariables] [ViewVariables(VVAccess.ReadOnly)]
private GameMapPrototype _currentMap = default!; private GameMapPrototype? _configSelectedMap = default;
[ViewVariables] [ViewVariables(VVAccess.ReadOnly)]
private bool _currentMapForced; private GameMapPrototype? _selectedMap = default; // Don't change this value during a round!
[ViewVariables] [ViewVariables(VVAccess.ReadOnly)]
private bool _mapRotationEnabled; private bool _mapRotationEnabled;
[ViewVariables] [ViewVariables(VVAccess.ReadOnly)]
private int _mapQueueDepth = 1; private int _mapQueueDepth = 1;
public void Initialize() public void Initialize()
{ {
_configurationManager.OnValueChanged(CCVars.GameMap, value => _configurationManager.OnValueChanged(CCVars.GameMap, value =>
{ {
if (TryLookupMap(value, out var map)) if (TryLookupMap(value, out GameMapPrototype? map))
_currentMap = map; {
_configSelectedMap = map;
}
else else
throw new ArgumentException($"Unknown map prototype {value} was selected!"); {
if (string.IsNullOrEmpty(value))
{
_configSelectedMap = default!;
}
else
{
Logger.ErrorS("mapsel", $"Unknown map prototype {value} was selected!");
}
}
}, true); }, true);
_configurationManager.OnValueChanged(CCVars.GameMapForced, value => _currentMapForced = value, true);
_configurationManager.OnValueChanged(CCVars.GameMapRotation, value => _mapRotationEnabled = value, true); _configurationManager.OnValueChanged(CCVars.GameMapRotation, value => _mapRotationEnabled = value, true);
_configurationManager.OnValueChanged(CCVars.GameMapMemoryDepth, value => _configurationManager.OnValueChanged(CCVars.GameMapMemoryDepth, value =>
{ {
@@ -62,7 +71,6 @@ public sealed class GameMapManager : IGameMapManager
public IEnumerable<GameMapPrototype> CurrentlyEligibleMaps() public IEnumerable<GameMapPrototype> CurrentlyEligibleMaps()
{ {
var maps = AllVotableMaps().Where(IsMapEligible).ToArray(); var maps = AllVotableMaps().Where(IsMapEligible).ToArray();
return maps.Length == 0 ? AllMaps().Where(x => x.Fallback) : maps; return maps.Length == 0 ? AllMaps().Where(x => x.Fallback) : maps;
} }
@@ -91,64 +99,59 @@ public sealed class GameMapManager : IGameMapManager
return _prototypeManager.EnumeratePrototypes<GameMapPrototype>(); return _prototypeManager.EnumeratePrototypes<GameMapPrototype>();
} }
public bool TrySelectMap(string gameMap) public GameMapPrototype? GetSelectedMap()
{ {
if (!TryLookupMap(gameMap, out var map) || !IsMapEligible(map)) return false; return _configSelectedMap ?? _selectedMap;
_currentMap = map;
_currentMapForced = false;
var ticker = EntitySystem.Get<GameTicking.GameTicker>();
ticker.UpdateInfoText();
return true;
} }
public void ForceSelectMap(string gameMap) public void ClearSelectedMap()
{
_selectedMap = default!;
}
public bool TrySelectMapIfEligible(string gameMap)
{
if (!TryLookupMap(gameMap, out var map) || !IsMapEligible(map))
return false;
_selectedMap = map;
return true;
}
public void SelectMap(string gameMap)
{ {
if (!TryLookupMap(gameMap, out var map)) if (!TryLookupMap(gameMap, out var map))
throw new ArgumentException($"The map \"{gameMap}\" is invalid!"); throw new ArgumentException($"The map \"{gameMap}\" is invalid!");
_currentMap = map; _selectedMap = map;
_currentMapForced = true;
var ticker = EntitySystem.Get<GameTicking.GameTicker>();
ticker.UpdateInfoText();
} }
public void SelectRandomMap() public void SelectMapRandom()
{ {
var maps = CurrentlyEligibleMaps().ToList(); var maps = CurrentlyEligibleMaps().ToList();
_currentMap = _random.Pick(maps); _selectedMap = _random.Pick(maps);
_currentMapForced = false;
var ticker = EntitySystem.Get<GameTicking.GameTicker>();
ticker.UpdateInfoText();
} }
public GameMapPrototype GetSelectedMap() public void SelectMapFromRotationQueue(bool markAsPlayed = false)
{ {
if (!_mapRotationEnabled || _currentMapForced) var map = GetFirstInRotationQueue();
return _currentMap;
return SelectMapInQueue() ?? CurrentlyEligibleMaps().First();
}
public GameMapPrototype GetSelectedMapChecked(bool loud = false, bool markAsPlayed = false) _selectedMap = map;
{
if (!_currentMapForced && !IsMapEligible(GetSelectedMap()))
{
var oldMap = GetSelectedMap().MapName;
SelectRandomMap();
if (loud)
{
_chatManager.DispatchServerAnnouncement(
Loc.GetString("gamemap-could-not-use-map-error",
("oldMap", oldMap), ("newMap", GetSelectedMap().MapName)
));
}
}
var map = GetSelectedMap();
if (markAsPlayed) if (markAsPlayed)
EnqueueMap(map.ID); EnqueueMap(map.ID);
return map; }
public void SelectMapByConfigRules()
{
if (_mapRotationEnabled)
{
Logger.InfoS("mapsel", "selecting the next map from the rotation queue");
SelectMapFromRotationQueue(true);
}
else
{
Logger.InfoS("mapsel", "selecting a random map");
SelectMapRandom();
}
} }
public bool CheckMapExists(string gameMap) public bool CheckMapExists(string gameMap)
@@ -168,7 +171,7 @@ public sealed class GameMapManager : IGameMapManager
return _prototypeManager.TryIndex(gameMap, out map); return _prototypeManager.TryIndex(gameMap, out map);
} }
public int GetMapQueuePriority(string gameMapProtoName) private int GetMapRotationQueuePriority(string gameMapProtoName)
{ {
var i = 0; var i = 0;
foreach (var map in _previousMaps.Reverse()) foreach (var map in _previousMaps.Reverse())
@@ -177,22 +180,28 @@ public sealed class GameMapManager : IGameMapManager
return i; return i;
i++; i++;
} }
return _mapQueueDepth; return _mapQueueDepth;
} }
public GameMapPrototype? SelectMapInQueue() private GameMapPrototype GetFirstInRotationQueue()
{ {
Logger.InfoS("mapsel", string.Join(", ", _previousMaps)); Logger.InfoS("mapsel", $"map queue: {string.Join(", ", _previousMaps)}");
var eligible = CurrentlyEligibleMaps() var eligible = CurrentlyEligibleMaps()
.Select(x => (proto: x, weight: GetMapQueuePriority(x.ID))) .Select(x => (proto: x, weight: GetMapRotationQueuePriority(x.ID)))
.OrderByDescending(x => x.weight).ToArray(); .OrderByDescending(x => x.weight)
Logger.InfoS("mapsel", string.Join(", ", eligible.Select(x => (x.proto.ID, x.weight)))); .ToArray();
if (eligible.Length is 0)
return null; Logger.InfoS("mapsel", $"eligible queue: {string.Join(", ", eligible.Select(x => (x.proto.ID, x.weight)))}");
// YML "should" be configured with at least one fallback map
Debug.Assert(eligible.Length != 0, $"couldn't select a map with {nameof(GetFirstInRotationQueue)}()! No eligible maps and no fallback maps!");
var weight = eligible[0].weight; var weight = eligible[0].weight;
return eligible.Where(x => x.Item2 == weight).OrderBy(x => x.proto.ID).First().proto; return eligible.Where(x => x.Item2 == weight)
.OrderBy(x => x.proto.ID)
.First()
.proto;
} }
private void EnqueueMap(string mapProtoName) private void EnqueueMap(string mapProtoName)

View File

@@ -2,6 +2,7 @@ using Content.Server.Station;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using System.Diagnostics;
namespace Content.Server.Maps; namespace Content.Server.Maps;
@@ -13,6 +14,7 @@ namespace Content.Server.Maps;
/// Make a new partial for your fancy new feature, it'll save you time later. /// Make a new partial for your fancy new feature, it'll save you time later.
/// </remarks> /// </remarks>
[Prototype("gameMap"), PublicAPI] [Prototype("gameMap"), PublicAPI]
[DebuggerDisplay("GameMapPrototype [{ID} - {MapName}]")]
public sealed partial class GameMapPrototype : IPrototype public sealed partial class GameMapPrototype : IPrototype
{ {
/// <inheritdoc/> /// <inheritdoc/>

View File

@@ -26,35 +26,45 @@ public interface IGameMapManager
IEnumerable<GameMapPrototype> AllMaps(); IEnumerable<GameMapPrototype> AllMaps();
/// <summary> /// <summary>
/// Attempts to select the given map. /// Gets the currently selected map
/// </summary>
/// <returns>selected map</returns>
GameMapPrototype? GetSelectedMap();
/// <summary>
/// Clears the selected map, if any
/// </summary>
void ClearSelectedMap();
/// <summary>
/// Attempts to select the given map, checking eligibility criteria
/// </summary> /// </summary>
/// <param name="gameMap">map prototype</param> /// <param name="gameMap">map prototype</param>
/// <returns>success or failure</returns> /// <returns>success or failure</returns>
bool TrySelectMap(string gameMap); bool TrySelectMapIfEligible(string gameMap);
/// <summary> /// <summary>
/// Forces the given map, making sure the game map manager won't reselect if conditions are no longer met at round restart. /// Select the given map regardless of eligibility
/// </summary> /// </summary>
/// <param name="gameMap">map prototype</param> /// <param name="gameMap">map prototype</param>
/// <returns>success or failure</returns> /// <returns>success or failure</returns>
void ForceSelectMap(string gameMap); void SelectMap(string gameMap);
/// <summary> /// <summary>
/// Selects a random map. /// Selects a random map eligible map
/// </summary> /// </summary>
void SelectRandomMap(); void SelectMapRandom();
/// <summary> /// <summary>
/// Gets the currently selected map, without double-checking if it can be used. /// Selects the map at the front of the rotation queue
/// </summary> /// </summary>
/// <returns>selected map</returns> /// <returns>selected map</returns>
GameMapPrototype GetSelectedMap(); void SelectMapFromRotationQueue(bool markAsPlayed = false);
/// <summary> /// <summary>
/// Gets the currently selected map, double-checking if it can be used. /// Selects the map by following rules set in the config
/// </summary> /// </summary>
/// <returns>selected map</returns> public void SelectMapByConfigRules();
GameMapPrototype GetSelectedMapChecked(bool loud = false, bool markAsPlayed = false);
/// <summary> /// <summary>
/// Checks if the given map exists /// Checks if the given map exists

View File

@@ -36,7 +36,7 @@ namespace Content.Server.Voting.Managers
default: default:
throw new ArgumentOutOfRangeException(nameof(voteType), voteType, null); throw new ArgumentOutOfRangeException(nameof(voteType), voteType, null);
} }
var ticker = EntitySystem.Get<GameTicker>(); var ticker = _entityManager.EntitySysManager.GetEntitySystem<GameTicker>();
ticker.UpdateInfoText(); ticker.UpdateInfoText();
TimeoutStandardVote(voteType); TimeoutStandardVote(voteType);
} }
@@ -76,7 +76,8 @@ namespace Content.Server.Voting.Managers
if (total > 0 && votesYes / (float) total >= ratioRequired) if (total > 0 && votesYes / (float) total >= ratioRequired)
{ {
_chatManager.DispatchServerAnnouncement(Loc.GetString("ui-vote-restart-succeeded")); _chatManager.DispatchServerAnnouncement(Loc.GetString("ui-vote-restart-succeeded"));
EntitySystem.Get<RoundEndSystem>().EndRound(); var roundEnd = _entityManager.EntitySysManager.GetEntitySystem<RoundEndSystem>();
roundEnd.EndRound();
} }
else else
{ {
@@ -141,8 +142,8 @@ namespace Content.Server.Voting.Managers
_chatManager.DispatchServerAnnouncement( _chatManager.DispatchServerAnnouncement(
Loc.GetString("ui-vote-gamemode-win", ("winner", Loc.GetString(presets[picked])))); Loc.GetString("ui-vote-gamemode-win", ("winner", Loc.GetString(presets[picked]))));
} }
var ticker = _entityManager.EntitySysManager.GetEntitySystem<GameTicker>();
EntitySystem.Get<GameTicker>().SetGamePreset(picked); ticker.SetGamePreset(picked);
}; };
} }
@@ -187,7 +188,18 @@ namespace Content.Server.Voting.Managers
Loc.GetString("ui-vote-map-win", ("winner", maps[picked]))); Loc.GetString("ui-vote-map-win", ("winner", maps[picked])));
} }
_gameMapManager.TrySelectMap(picked.ID); var ticker = _entityManager.EntitySysManager.GetEntitySystem<GameTicker>();
if (ticker.RunLevel == GameRunLevel.PreRoundLobby)
{
if (_gameMapManager.TrySelectMapIfEligible(picked.ID))
{
ticker.UpdateInfoText();
}
}
else
{
_chatManager.DispatchServerAnnouncement(Loc.GetString("ui-vote-map-notlobby"));
}
}; };
} }

View File

@@ -34,6 +34,7 @@ namespace Content.Server.Voting.Managers
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IGameMapManager _gameMapManager = default!; [Dependency] private readonly IGameMapManager _gameMapManager = default!;
[Dependency] private readonly IEntityManager _entityManager = default!;
private int _nextVoteId = 1; private int _nextVoteId = 1;

View File

@@ -188,7 +188,7 @@ namespace Content.Shared.CCVar
/// Controls the game map prototype to load. SS14 stores these prototypes in Prototypes/Maps. /// Controls the game map prototype to load. SS14 stores these prototypes in Prototypes/Maps.
/// </summary> /// </summary>
public static readonly CVarDef<string> public static readonly CVarDef<string>
GameMap = CVarDef.Create("game.map", "Saltern", CVar.SERVERONLY); GameMap = CVarDef.Create("game.map", string.Empty, CVar.SERVERONLY);
/// <summary> /// <summary>
/// Prototype to use for map pool. /// Prototype to use for map pool.
@@ -196,12 +196,6 @@ namespace Content.Shared.CCVar
public static readonly CVarDef<string> public static readonly CVarDef<string>
GameMapPool = CVarDef.Create("game.map_pool", "DefaultMapPool", CVar.SERVERONLY); GameMapPool = CVarDef.Create("game.map_pool", "DefaultMapPool", CVar.SERVERONLY);
/// <summary>
/// Controls if the game should obey map criteria or not. Overriden if a map vote or similar occurs.
/// </summary>
public static readonly CVarDef<bool>
GameMapForced = CVarDef.Create("game.mapforced", false, CVar.SERVERONLY);
/// <summary> /// <summary>
/// The depth of the queue used to calculate which map is next in rotation. /// The depth of the queue used to calculate which map is next in rotation.
/// This is how long the game "remembers" that some map was put in play. Default is 16 rounds. /// This is how long the game "remembers" that some map was put in play. Default is 16 rounds.

View File

@@ -13,7 +13,7 @@ game-ticker-get-info-text = Hi and welcome to [color=white]Space Station 14![/co
The current map is: [color=white]{$mapName}[/color] The current map is: [color=white]{$mapName}[/color]
The current game mode is: [color=white]{$gmTitle}[/color] The current game mode is: [color=white]{$gmTitle}[/color]
>[color=yellow]{$desc}[/color] >[color=yellow]{$desc}[/color]
game-ticker-no-map-selected = [color=red]No map selected![/color] game-ticker-no-map-selected = [color=yellow]Map not yet selected![/color]
game-ticker-player-no-jobs-available-when-joining = When attempting to join to the game, no jobs were available. game-ticker-player-no-jobs-available-when-joining = When attempting to join to the game, no jobs were available.
# Displayed in chat to admins when a player joins # Displayed in chat to admins when a player joins

View File

@@ -17,3 +17,4 @@ ui-vote-gamemode-win = { $winner } won the gamemode vote!
ui-vote-map-title = Next map ui-vote-map-title = Next map
ui-vote-map-tie = Tie for map vote! Picking... { $picked } ui-vote-map-tie = Tie for map vote! Picking... { $picked }
ui-vote-map-win = { $winner } won the map vote! ui-vote-map-win = { $winner } won the map vote!
ui-vote-map-notlobby = Voting for maps is only valid in the pre-round lobby!