Allow gamemodes to specify custom map pools (#18429)
* Allow game presets to require certain maps * make preset maps ignore the game map pool * make it use a map pool prototype * Typo --------- Co-authored-by: Kevin Zheng <kevinz5000@gmail.com>
This commit is contained in:
@@ -3,6 +3,7 @@ using System.Linq;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.GameTicking.Presets;
|
using Content.Server.GameTicking.Presets;
|
||||||
using Content.Server.Ghost.Components;
|
using Content.Server.Ghost.Components;
|
||||||
|
using Content.Server.Maps;
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Damage.Prototypes;
|
using Content.Shared.Damage.Prototypes;
|
||||||
@@ -17,8 +18,16 @@ namespace Content.Server.GameTicking
|
|||||||
{
|
{
|
||||||
public const float PresetFailedCooldownIncrease = 30f;
|
public const float PresetFailedCooldownIncrease = 30f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The selected preset that will be used at the start of the next round.
|
||||||
|
/// </summary>
|
||||||
public GamePresetPrototype? Preset { get; private set; }
|
public GamePresetPrototype? Preset { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The preset that's currently active.
|
||||||
|
/// </summary>
|
||||||
|
public GamePresetPrototype? CurrentPreset { get; private set; }
|
||||||
|
|
||||||
private bool StartPreset(IPlayerSession[] origReadyPlayers, bool force)
|
private bool StartPreset(IPlayerSession[] origReadyPlayers, bool force)
|
||||||
{
|
{
|
||||||
var startAttempt = new RoundStartAttemptEvent(origReadyPlayers, force);
|
var startAttempt = new RoundStartAttemptEvent(origReadyPlayers, force);
|
||||||
@@ -27,7 +36,7 @@ namespace Content.Server.GameTicking
|
|||||||
if (!startAttempt.Cancelled)
|
if (!startAttempt.Cancelled)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var presetTitle = Preset != null ? Loc.GetString(Preset.ModeTitle) : string.Empty;
|
var presetTitle = CurrentPreset != null ? Loc.GetString(CurrentPreset.ModeTitle) : string.Empty;
|
||||||
|
|
||||||
void FailedPresetRestart()
|
void FailedPresetRestart()
|
||||||
{
|
{
|
||||||
@@ -93,6 +102,7 @@ namespace Content.Server.GameTicking
|
|||||||
|
|
||||||
Preset = preset;
|
Preset = preset;
|
||||||
UpdateInfoText();
|
UpdateInfoText();
|
||||||
|
ValidateMap();
|
||||||
|
|
||||||
if (force)
|
if (force)
|
||||||
{
|
{
|
||||||
@@ -131,12 +141,39 @@ namespace Content.Server.GameTicking
|
|||||||
return prototype != null;
|
return prototype != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsMapEligible(GameMapPrototype map)
|
||||||
|
{
|
||||||
|
if (Preset == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (Preset.MapPool == null || !_prototypeManager.TryIndex<GameMapPoolPrototype>(Preset.MapPool, out var pool))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return pool.Maps.Contains(map.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ValidateMap()
|
||||||
|
{
|
||||||
|
if (Preset == null || _gameMapManager.GetSelectedMap() is not { } map)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Preset.MapPool == null ||
|
||||||
|
!_prototypeManager.TryIndex<GameMapPoolPrototype>(Preset.MapPool, out var pool))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (pool.Maps.Contains(map.ID))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_gameMapManager.SelectMapRandom();
|
||||||
|
}
|
||||||
|
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
private bool AddGamePresetRules()
|
private bool AddGamePresetRules()
|
||||||
{
|
{
|
||||||
if (DummyTicker || Preset == null)
|
if (DummyTicker || Preset == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
CurrentPreset = Preset;
|
||||||
foreach (var rule in Preset.Rules)
|
foreach (var rule in Preset.Rules)
|
||||||
{
|
{
|
||||||
AddGameRule(rule);
|
AddGameRule(rule);
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ namespace Content.Server.GameTicking
|
|||||||
|
|
||||||
private string GetInfoText()
|
private string GetInfoText()
|
||||||
{
|
{
|
||||||
if (Preset == null)
|
var preset = CurrentPreset ?? Preset;
|
||||||
|
if (preset == null)
|
||||||
{
|
{
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
@@ -72,8 +73,8 @@ namespace Content.Server.GameTicking
|
|||||||
stationNames.Append(Loc.GetString("game-ticker-no-map-selected"));
|
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(RunLevel == GameRunLevel.PreRoundLobby ? "game-ticker-get-info-preround-text" : "game-ticker-get-info-text",
|
return Loc.GetString(RunLevel == GameRunLevel.PreRoundLobby ? "game-ticker-get-info-preround-text" : "game-ticker-get-info-text",
|
||||||
("roundId", RoundId), ("playerCount", playerCount), ("readyCount", readyCount), ("mapName", stationNames.ToString()),("gmTitle", gmTitle),("desc", desc));
|
("roundId", RoundId), ("playerCount", playerCount), ("readyCount", readyCount), ("mapName", stationNames.ToString()),("gmTitle", gmTitle),("desc", desc));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,6 +114,17 @@ namespace Content.Server.GameTicking
|
|||||||
throw new Exception("invalid config; couldn't select a valid station map!");
|
throw new Exception("invalid config; couldn't select a valid station map!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (CurrentPreset?.MapPool != null &&
|
||||||
|
_prototypeManager.TryIndex<GameMapPoolPrototype>(CurrentPreset.MapPool, out var pool) &&
|
||||||
|
pool.Maps.Contains(mainStationMap.ID))
|
||||||
|
{
|
||||||
|
var msg = Loc.GetString("game-ticker-start-round-invalid-map",
|
||||||
|
("map", mainStationMap.MapName),
|
||||||
|
("mode", Loc.GetString(CurrentPreset.ModeTitle)));
|
||||||
|
Log.Debug(msg);
|
||||||
|
SendServerMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
// 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));
|
||||||
|
|
||||||
@@ -292,7 +303,7 @@ namespace Content.Server.GameTicking
|
|||||||
_adminLogger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Round ended, showing summary");
|
_adminLogger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Round ended, showing summary");
|
||||||
|
|
||||||
//Tell every client the round has ended.
|
//Tell every client the round has ended.
|
||||||
var gamemodeTitle = Preset != null ? Loc.GetString(Preset.ModeTitle) : string.Empty;
|
var gamemodeTitle = CurrentPreset != null ? Loc.GetString(CurrentPreset.ModeTitle) : string.Empty;
|
||||||
|
|
||||||
// Let things add text here.
|
// Let things add text here.
|
||||||
var textEv = new RoundEndTextAppendEvent();
|
var textEv = new RoundEndTextAppendEvent();
|
||||||
@@ -306,7 +317,7 @@ namespace Content.Server.GameTicking
|
|||||||
//Generate a list of basic player info to display in the end round summary.
|
//Generate a list of basic player info to display in the end round summary.
|
||||||
var listOfPlayerInfo = new List<RoundEndMessageEvent.RoundEndPlayerInfo>();
|
var listOfPlayerInfo = new List<RoundEndMessageEvent.RoundEndPlayerInfo>();
|
||||||
// Grab the great big book of all the Minds, we'll need them for this.
|
// Grab the great big book of all the Minds, we'll need them for this.
|
||||||
var allMinds = Get<MindTrackerSystem>().AllMinds;
|
var allMinds = _mindTracker.AllMinds;
|
||||||
foreach (var mind in allMinds)
|
foreach (var mind in allMinds)
|
||||||
{
|
{
|
||||||
// TODO don't list redundant observer roles?
|
// TODO don't list redundant observer roles?
|
||||||
@@ -447,6 +458,7 @@ namespace Content.Server.GameTicking
|
|||||||
|
|
||||||
// Clear up any game rules.
|
// Clear up any game rules.
|
||||||
ClearGameRules();
|
ClearGameRules();
|
||||||
|
CurrentPreset = null;
|
||||||
|
|
||||||
_allPreviousGameRules.Clear();
|
_allPreviousGameRules.Clear();
|
||||||
|
|
||||||
@@ -514,7 +526,7 @@ namespace Content.Server.GameTicking
|
|||||||
|
|
||||||
private void AnnounceRound()
|
private void AnnounceRound()
|
||||||
{
|
{
|
||||||
if (Preset == null) return;
|
if (CurrentPreset == null) return;
|
||||||
|
|
||||||
var options = _prototypeManager.EnumeratePrototypes<RoundAnnouncementPrototype>().ToList();
|
var options = _prototypeManager.EnumeratePrototypes<RoundAnnouncementPrototype>().ToList();
|
||||||
|
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ namespace Content.Server.GameTicking
|
|||||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
[Dependency] private readonly GhostSystem _ghost = default!;
|
[Dependency] private readonly GhostSystem _ghost = default!;
|
||||||
[Dependency] private readonly MindSystem _mind = default!;
|
[Dependency] private readonly MindSystem _mind = default!;
|
||||||
|
[Dependency] private readonly MindTrackerSystem _mindTracker = default!;
|
||||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||||
|
|
||||||
[ViewVariables] private bool _initialized;
|
[ViewVariables] private bool _initialized;
|
||||||
@@ -92,7 +93,8 @@ namespace Content.Server.GameTicking
|
|||||||
|
|
||||||
private void SendServerMessage(string message)
|
private void SendServerMessage(string message)
|
||||||
{
|
{
|
||||||
_chatManager.ChatMessageToAll(ChatChannel.Server, message, "", default, false, true);
|
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", message));
|
||||||
|
_chatManager.ChatMessageToAll(ChatChannel.Server, message, wrappedMessage, default, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
|
||||||
|
using Content.Server.Maps;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
||||||
|
|
||||||
namespace Content.Server.GameTicking.Presets
|
namespace Content.Server.GameTicking.Presets
|
||||||
@@ -33,5 +35,12 @@ namespace Content.Server.GameTicking.Presets
|
|||||||
|
|
||||||
[DataField("rules", customTypeSerializer: typeof(PrototypeIdListSerializer<EntityPrototype>))]
|
[DataField("rules", customTypeSerializer: typeof(PrototypeIdListSerializer<EntityPrototype>))]
|
||||||
public IReadOnlyList<string> Rules { get; } = Array.Empty<string>();
|
public IReadOnlyList<string> Rules { get; } = Array.Empty<string>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If specified, the gamemode will only be run with these maps.
|
||||||
|
/// If none are elligible, the global fallback will be used.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("supportedMaps", customTypeSerializer: typeof(PrototypeIdSerializer<GameMapPoolPrototype>))]
|
||||||
|
public readonly string? MapPool;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Content.Server.GameTicking;
|
||||||
using Content.Shared.CCVar;
|
using Content.Shared.CCVar;
|
||||||
using Robust.Server.Player;
|
using Robust.Server.Player;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
@@ -11,24 +12,29 @@ namespace Content.Server.Maps;
|
|||||||
|
|
||||||
public sealed class GameMapManager : IGameMapManager
|
public sealed class GameMapManager : IGameMapManager
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[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!;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadOnly)]
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
private readonly Queue<string> _previousMaps = new();
|
private readonly Queue<string> _previousMaps = new();
|
||||||
[ViewVariables(VVAccess.ReadOnly)]
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
private GameMapPrototype? _configSelectedMap = default;
|
private GameMapPrototype? _configSelectedMap;
|
||||||
[ViewVariables(VVAccess.ReadOnly)]
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
private GameMapPrototype? _selectedMap = default; // Don't change this value during a round!
|
private GameMapPrototype? _selectedMap; // Don't change this value during a round!
|
||||||
[ViewVariables(VVAccess.ReadOnly)]
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
private bool _mapRotationEnabled;
|
private bool _mapRotationEnabled;
|
||||||
[ViewVariables(VVAccess.ReadOnly)]
|
[ViewVariables(VVAccess.ReadOnly)]
|
||||||
private int _mapQueueDepth = 1;
|
private int _mapQueueDepth = 1;
|
||||||
|
|
||||||
|
private ISawmill _log = default!;
|
||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
|
_log = Logger.GetSawmill("mapsel");
|
||||||
|
|
||||||
_configurationManager.OnValueChanged(CCVars.GameMap, value =>
|
_configurationManager.OnValueChanged(CCVars.GameMap, value =>
|
||||||
{
|
{
|
||||||
if (TryLookupMap(value, out GameMapPrototype? map))
|
if (TryLookupMap(value, out GameMapPrototype? map))
|
||||||
@@ -43,7 +49,7 @@ public sealed class GameMapManager : IGameMapManager
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.ErrorS("mapsel", $"Unknown map prototype {value} was selected!");
|
_log.Error($"Unknown map prototype {value} was selected!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
@@ -76,21 +82,25 @@ public sealed class GameMapManager : IGameMapManager
|
|||||||
|
|
||||||
public IEnumerable<GameMapPrototype> AllVotableMaps()
|
public IEnumerable<GameMapPrototype> AllVotableMaps()
|
||||||
{
|
{
|
||||||
|
var poolPrototype = _entityManager.System<GameTicker>().Preset?.MapPool ??
|
||||||
|
_configurationManager.GetCVar(CCVars.GameMapPool);
|
||||||
|
|
||||||
if (_prototypeManager.TryIndex<GameMapPoolPrototype>(_configurationManager.GetCVar(CCVars.GameMapPool), out var pool))
|
if (_prototypeManager.TryIndex<GameMapPoolPrototype>(_configurationManager.GetCVar(CCVars.GameMapPool), out var pool))
|
||||||
{
|
{
|
||||||
foreach (var map in pool.Maps)
|
foreach (var map in pool.Maps)
|
||||||
{
|
{
|
||||||
if (!_prototypeManager.TryIndex<GameMapPrototype>(map, out var mapProto))
|
if (!_prototypeManager.TryIndex<GameMapPrototype>(map, out var mapProto))
|
||||||
{
|
{
|
||||||
Logger.Error("Couldn't index map " + map + " in pool " + pool.ID);
|
_log.Error($"Couldn't index map {map} in pool {poolPrototype}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
yield return mapProto;
|
yield return mapProto;
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
throw new Exception("Could not index map pool prototype " + _configurationManager.GetCVar(CCVars.GameMapPool) + "!");
|
throw new Exception($"Could not index map pool prototype {poolPrototype}!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,12 +154,12 @@ public sealed class GameMapManager : IGameMapManager
|
|||||||
{
|
{
|
||||||
if (_mapRotationEnabled)
|
if (_mapRotationEnabled)
|
||||||
{
|
{
|
||||||
Logger.InfoS("mapsel", "selecting the next map from the rotation queue");
|
_log.Info("selecting the next map from the rotation queue");
|
||||||
SelectMapFromRotationQueue(true);
|
SelectMapFromRotationQueue(true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Logger.InfoS("mapsel", "selecting a random map");
|
_log.Info("selecting a random map");
|
||||||
SelectMapRandom();
|
SelectMapRandom();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,7 +173,8 @@ public sealed class GameMapManager : IGameMapManager
|
|||||||
{
|
{
|
||||||
return map.MaxPlayers >= _playerManager.PlayerCount &&
|
return map.MaxPlayers >= _playerManager.PlayerCount &&
|
||||||
map.MinPlayers <= _playerManager.PlayerCount &&
|
map.MinPlayers <= _playerManager.PlayerCount &&
|
||||||
map.Conditions.All(x => x.Check(map));
|
map.Conditions.All(x => x.Check(map)) &&
|
||||||
|
_entityManager.System<GameTicker>().IsMapEligible(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryLookupMap(string gameMap, [NotNullWhen(true)] out GameMapPrototype? map)
|
private bool TryLookupMap(string gameMap, [NotNullWhen(true)] out GameMapPrototype? map)
|
||||||
@@ -185,23 +196,22 @@ public sealed class GameMapManager : IGameMapManager
|
|||||||
|
|
||||||
private GameMapPrototype GetFirstInRotationQueue()
|
private GameMapPrototype GetFirstInRotationQueue()
|
||||||
{
|
{
|
||||||
Logger.InfoS("mapsel", $"map queue: {string.Join(", ", _previousMaps)}");
|
_log.Info($"map queue: {string.Join(", ", _previousMaps)}");
|
||||||
|
|
||||||
var eligible = CurrentlyEligibleMaps()
|
var eligible = CurrentlyEligibleMaps()
|
||||||
.Select(x => (proto: x, weight: GetMapRotationQueuePriority(x.ID)))
|
.Select(x => (proto: x, weight: GetMapRotationQueuePriority(x.ID)))
|
||||||
.OrderByDescending(x => x.weight)
|
.OrderByDescending(x => x.weight)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
|
|
||||||
Logger.InfoS("mapsel", $"eligible queue: {string.Join(", ", eligible.Select(x => (x.proto.ID, x.weight)))}");
|
_log.Info($"eligible queue: {string.Join(", ", eligible.Select(x => (x.proto.ID, x.weight)))}");
|
||||||
|
|
||||||
// YML "should" be configured with at least one fallback map
|
// 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!");
|
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)
|
return eligible.Where(x => x.Item2 == weight)
|
||||||
.OrderBy(x => x.proto.ID)
|
.MinBy(x => x.proto.ID)
|
||||||
.First()
|
.proto;
|
||||||
.proto;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnqueueMap(string mapProtoName)
|
private void EnqueueMap(string mapProtoName)
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using System.Linq;
|
|||||||
using Content.Server.Administration;
|
using Content.Server.Administration;
|
||||||
using Content.Server.Administration.Logs;
|
using Content.Server.Administration.Logs;
|
||||||
using Content.Server.Administration.Managers;
|
using Content.Server.Administration.Managers;
|
||||||
using Content.Server.Afk;
|
|
||||||
using Content.Server.Chat.Managers;
|
using Content.Server.Chat.Managers;
|
||||||
using Content.Server.GameTicking;
|
using Content.Server.GameTicking;
|
||||||
using Content.Server.Maps;
|
using Content.Server.Maps;
|
||||||
@@ -58,13 +57,15 @@ namespace Content.Server.Voting.Managers
|
|||||||
_playerManager.PlayerStatusChanged += PlayerManagerOnPlayerStatusChanged;
|
_playerManager.PlayerStatusChanged += PlayerManagerOnPlayerStatusChanged;
|
||||||
_adminMgr.OnPermsChanged += AdminPermsChanged;
|
_adminMgr.OnPermsChanged += AdminPermsChanged;
|
||||||
|
|
||||||
_cfg.OnValueChanged(CCVars.VoteEnabled, value => {
|
_cfg.OnValueChanged(CCVars.VoteEnabled, _ =>
|
||||||
|
{
|
||||||
DirtyCanCallVoteAll();
|
DirtyCanCallVoteAll();
|
||||||
});
|
});
|
||||||
|
|
||||||
foreach (var kvp in _voteTypesToEnableCVars)
|
foreach (var kvp in _voteTypesToEnableCVars)
|
||||||
{
|
{
|
||||||
_cfg.OnValueChanged(kvp.Value, value => {
|
_cfg.OnValueChanged(kvp.Value, _ =>
|
||||||
|
{
|
||||||
DirtyCanCallVoteAll();
|
DirtyCanCallVoteAll();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -294,7 +295,7 @@ namespace Content.Server.Voting.Managers
|
|||||||
var votesUnavailable = new List<(StandardVoteType, TimeSpan)>();
|
var votesUnavailable = new List<(StandardVoteType, TimeSpan)>();
|
||||||
foreach (var v in _standardVoteTypeValues)
|
foreach (var v in _standardVoteTypeValues)
|
||||||
{
|
{
|
||||||
if (CanCallVote(player, v, out var _isAdmin, out var typeTimeSpan))
|
if (CanCallVote(player, v, out _, out var typeTimeSpan))
|
||||||
continue;
|
continue;
|
||||||
votesUnavailable.Add((v, typeTimeSpan));
|
votesUnavailable.Add((v, typeTimeSpan));
|
||||||
}
|
}
|
||||||
@@ -324,7 +325,7 @@ namespace Content.Server.Voting.Managers
|
|||||||
if (!_cfg.GetCVar(CCVars.VoteEnabled))
|
if (!_cfg.GetCVar(CCVars.VoteEnabled))
|
||||||
return false;
|
return false;
|
||||||
// Specific standard vote types can be disabled with cvars.
|
// Specific standard vote types can be disabled with cvars.
|
||||||
if ((voteType != null) && _voteTypesToEnableCVars.TryGetValue(voteType.Value, out var cvar) && !_cfg.GetCVar(cvar))
|
if (voteType != null && _voteTypesToEnableCVars.TryGetValue(voteType.Value, out var cvar) && !_cfg.GetCVar(cvar))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Cannot start vote if vote is already active (as non-admin).
|
// Cannot start vote if vote is already active (as non-admin).
|
||||||
@@ -345,7 +346,7 @@ namespace Content.Server.Voting.Managers
|
|||||||
if (voteType == StandardVoteType.Preset)
|
if (voteType == StandardVoteType.Preset)
|
||||||
{
|
{
|
||||||
var presets = GetGamePresets();
|
var presets = GetGamePresets();
|
||||||
if (presets.Count() == 1 && presets.Select(x => x.Key).Single() == EntitySystem.Get<GameTicker>().Preset?.ID)
|
if (presets.Count == 1 && presets.Select(x => x.Key).Single() == _entityManager.System<GameTicker>().Preset?.ID)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ game-ticker-restart-round = Restarting round...
|
|||||||
game-ticker-start-round = The round is starting now...
|
game-ticker-start-round = The round is starting now...
|
||||||
game-ticker-start-round-cannot-start-game-mode-fallback = Failed to start {$failedGameMode} mode! Defaulting to {$fallbackMode}...
|
game-ticker-start-round-cannot-start-game-mode-fallback = Failed to start {$failedGameMode} mode! Defaulting to {$fallbackMode}...
|
||||||
game-ticker-start-round-cannot-start-game-mode-restart = Failed to start {$failedGameMode} mode! Restarting round...
|
game-ticker-start-round-cannot-start-game-mode-restart = Failed to start {$failedGameMode} mode! Restarting round...
|
||||||
|
game-ticker-start-round-invalid-map = Selected map {$map} is inelligible for gamemode {$mode}. Gamemode may not function as intended...
|
||||||
game-ticker-unknown-role = Unknown
|
game-ticker-unknown-role = Unknown
|
||||||
game-ticker-delay-start = Round start has been delayed for {$seconds} seconds.
|
game-ticker-delay-start = Round start has been delayed for {$seconds} seconds.
|
||||||
game-ticker-pause-start = Round start has been paused.
|
game-ticker-pause-start = Round start has been paused.
|
||||||
|
|||||||
Reference in New Issue
Block a user