]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Fool players with decoy presets (#40053)
authorSamuka-C <47865393+Samuka-C@users.noreply.github.com>
Thu, 4 Sep 2025 13:03:47 +0000 (10:03 -0300)
committerGitHub <noreply@github.com>
Thu, 4 Sep 2025 13:03:47 +0000 (15:03 +0200)
* added secret gamepresets

* cut down on alias

* remove all secret presets

* change the command to allow for a secret argument

* update test

* moved the secret argument after the number of rounds argument

* added completions

* localization and use of CompletionHelper.Booleans

* command now has a option for a decoy preset

* fixed decoy message in the end

* ops

* clean up

* hint 2

* improve localization

---------

Co-authored-by: Errant <35878406+Errant-4@users.noreply.github.com>
Content.Server/GameTicking/Commands/SetGamePresetCommand.cs
Content.Server/GameTicking/GameTicker.GamePreset.cs
Content.Server/GameTicking/GameTicker.Lobby.cs
Resources/Locale/en-US/game-ticking/set-game-preset-command.ftl
Resources/Prototypes/game_presets.yml

index 78e2b452b7bedbe9bd2549fefe7c12501ae323ec..6114a4ca0d32af5de094c1bee68a563218782991 100644 (file)
@@ -20,9 +20,9 @@ namespace Content.Server.GameTicking.Commands
 
         public void Execute(IConsoleShell shell, string argStr, string[] args)
         {
-            if (!args.Length.InRange(1, 2))
+            if (!args.Length.InRange(1, 3))
             {
-                shell.WriteError(Loc.GetString("shell-need-between-arguments", ("lower", 1), ("upper", 2), ("currentAmount", args.Length)));
+                shell.WriteError(Loc.GetString("shell-need-between-arguments", ("lower", 1), ("upper", 3), ("currentAmount", args.Length)));
                 return;
             }
 
@@ -36,32 +36,39 @@ namespace Content.Server.GameTicking.Commands
 
             var rounds = 1;
 
-            if (args.Length == 2 && !int.TryParse(args[1], out rounds))
+            if (args.Length >= 2 && !int.TryParse(args[1], out rounds))
             {
                 shell.WriteError(Loc.GetString("set-game-preset-optional-argument-not-integer"));
                 return;
             }
 
-            ticker.SetGamePreset(preset, false, rounds);
-            shell.WriteLine(Loc.GetString("set-game-preset-preset-set-finite", ("preset", preset.ID), ("rounds", rounds.ToString())));
+            GamePresetPrototype? decoy = null;
+
+            if (args.Length == 3 && !ticker.TryFindGamePreset(args[2], out decoy))
+            {
+                shell.WriteError(Loc.GetString("set-game-preset-decoy-error", ("preset", args[2])));
+                return;
+            }
+
+            ticker.SetGamePreset(preset, false, decoy, rounds);
+            if (decoy == null)
+                shell.WriteLine(Loc.GetString("set-game-preset-preset-set-finite", ("preset", preset.ID), ("rounds", rounds.ToString())));
+            else
+                shell.WriteLine(Loc.GetString("set-game-preset-preset-set-finite-with-decoy", ("preset", preset.ID), ("rounds", rounds.ToString()), ("decoy", decoy.ID)));
         }
 
         public CompletionResult GetCompletion(IConsoleShell shell, string[] args)
         {
-            if (args.Length == 1)
+            return args.Length switch
             {
-                var gamePresets = _prototype.EnumeratePrototypes<GamePresetPrototype>()
-                    .OrderBy(p => p.ID);
-                var options = new List<string>();
-                foreach (var preset in gamePresets)
-                {
-                    options.Add(preset.ID);
-                    options.AddRange(preset.Alias);
-                }
+                1 => CompletionResult.FromHintOptions(CompletionHelper.PrototypeIDs<GamePresetPrototype>(),
+                Loc.GetString("set-game-preset-command-hint-1")),
+                2 => CompletionResult.FromHint(Loc.GetString("set-game-preset-command-hint-2")),
+                3 => CompletionResult.FromHintOptions(CompletionHelper.PrototypeIDs<GamePresetPrototype>(),
+                Loc.GetString("set-game-preset-command-hint-3")),
 
-                return CompletionResult.FromHintOptions(options, "<id>");
-            }
-            return CompletionResult.Empty;
+                _ => CompletionResult.Empty
+            };
         }
     }
 }
index 84a93da955438aeafd9e0bb423ffa3eb653cd18f..40608e45cbce8460672aeac0de4d8c7866bace03 100644 (file)
@@ -18,6 +18,11 @@ public sealed partial class GameTicker
     /// </summary>
     public GamePresetPrototype? Preset { get; private set; }
 
+    /// <summary>
+    /// The selected preset that will be shown at the lobby screen to fool players.
+    /// </summary>
+    public GamePresetPrototype? Decoy { get; private set; }
+
     /// <summary>
     /// The preset that's currently active.
     /// </summary>
@@ -46,10 +51,10 @@ public sealed partial class GameTicker
             DelayStart(TimeSpan.FromSeconds(PresetFailedCooldownIncrease));
         }
 
-            if (_cfg.GetCVar(CCVars.GameLobbyFallbackEnabled))
-            {
-                var fallbackPresets = _cfg.GetCVar(CCVars.GameLobbyFallbackPreset).Split(",");
-                var startFailed = true;
+        if (_cfg.GetCVar(CCVars.GameLobbyFallbackEnabled))
+        {
+            var fallbackPresets = _cfg.GetCVar(CCVars.GameLobbyFallbackPreset).Split(",");
+            var startFailed = true;
 
             foreach (var preset in fallbackPresets)
             {
@@ -89,12 +94,12 @@ public sealed partial class GameTicker
         return true;
     }
 
-        private void InitializeGamePreset()
-        {
-            SetGamePreset(LobbyEnabled ? _cfg.GetCVar(CCVars.GameLobbyDefaultPreset) : "sandbox");
-        }
+    private void InitializeGamePreset()
+    {
+        SetGamePreset(LobbyEnabled ? _cfg.GetCVar(CCVars.GameLobbyDefaultPreset) : "sandbox");
+    }
 
-    public void SetGamePreset(GamePresetPrototype? preset, bool force = false, int? resetDelay = null)
+    public void SetGamePreset(GamePresetPrototype? preset, bool force = false, GamePresetPrototype? decoy = null, int? resetDelay = null)
     {
         // Do nothing if this game ticker is a dummy!
         if (DummyTicker)
@@ -114,6 +119,7 @@ public sealed partial class GameTicker
         }
 
         Preset = preset;
+        Decoy = decoy;
         ValidateMap();
         UpdateInfoText();
 
@@ -126,7 +132,7 @@ public sealed partial class GameTicker
     public void SetGamePreset(string preset, bool force = false)
     {
         var proto = FindGamePreset(preset);
-        if(proto != null)
+        if (proto != null)
             SetGamePreset(proto, force);
     }
 
@@ -214,19 +220,19 @@ public sealed partial class GameTicker
         }
     }
 
-        private void IncrementRoundNumber()
-        {
-            var playerIds = _playerGameStatuses.Keys.Select(player => player.UserId).ToArray();
-            var serverName = _cfg.GetCVar(CCVars.AdminLogsServerName);
-
-    // TODO FIXME AAAAAAAAAAAAAAAAAAAH THIS IS BROKEN
-    // Task.Run as a terrible dirty workaround to avoid synchronization context deadlock from .Result here.
-    // This whole setup logic should be made asynchronous so we can properly wait on the DB AAAAAAAAAAAAAH
-    var task = Task.Run(async () =>
+    private void IncrementRoundNumber()
     {
-        var server = await _dbEntryManager.ServerEntity;
-        return await _db.AddNewRound(server, playerIds);
-    });
+        var playerIds = _playerGameStatuses.Keys.Select(player => player.UserId).ToArray();
+        var serverName = _cfg.GetCVar(CCVars.AdminLogsServerName);
+
+        // TODO FIXME AAAAAAAAAAAAAAAAAAAH THIS IS BROKEN
+        // Task.Run as a terrible dirty workaround to avoid synchronization context deadlock from .Result here.
+        // This whole setup logic should be made asynchronous so we can properly wait on the DB AAAAAAAAAAAAAH
+        var task = Task.Run(async () =>
+        {
+            var server = await _dbEntryManager.ServerEntity;
+            return await _db.AddNewRound(server, playerIds);
+        });
 
         _taskManager.BlockWaitOnTask(task);
         RoundId = task.GetAwaiter().GetResult();
index 9a9eb61b6737036e3ff5ce5bd246305b3799a9c5..66c39ab469cf97264c438a033206999be4d4222f 100644 (file)
@@ -61,7 +61,7 @@ namespace Content.Server.GameTicking
             {
                 foundOne = true;
                 if (stationNames.Length > 0)
-                        stationNames.Append('\n');
+                    stationNames.Append('\n');
 
                 stationNames.Append(meta.EntityName);
             }
@@ -72,8 +72,8 @@ namespace Content.Server.GameTicking
                                     Loc.GetString("game-ticker-no-map-selected"));
             }
 
-            var gmTitle = Loc.GetString(preset.ModeTitle);
-            var desc = Loc.GetString(preset.Description);
+            var gmTitle = (Decoy == null) ? Loc.GetString(preset.ModeTitle) : Loc.GetString(Decoy.ModeTitle);
+            var desc = (Decoy == null) ? Loc.GetString(preset.Description) : Loc.GetString(Decoy.Description);
             return Loc.GetString(
                 RunLevel == GameRunLevel.PreRoundLobby
                     ? "game-ticker-get-info-preround-text"
@@ -107,7 +107,7 @@ namespace Content.Server.GameTicking
 
         private TickerLobbyInfoEvent GetInfoMsg()
         {
-            return new (GetInfoText());
+            return new(GetInfoText());
         }
 
         private void UpdateLateJoinStatus()
index 323d83aebafa1d6c7298aec526ad33a5046e0bef..9659db643f8ea742e4bbe7417be0bd5e8e1345db 100644 (file)
@@ -1,7 +1,13 @@
-set-game-preset-command-description = Sets the game preset for the specified number of upcoming rounds.
-set-game-preset-command-help-text = setgamepreset <id> [number of rounds, defaulting to 1]
-set-game-preset-optional-argument-not-integer = If argument 2 is provided it must be a number.
+set-game-preset-command-description = Sets the game preset for the specified number of upcoming rounds. Can also display another preset's title and description in the lobby to fool players.
+set-game-preset-command-help-text = setgamepreset <id> [number of rounds, defaulting to 1] [decoy preset]
+set-game-preset-command-hint-1 = <id>
+set-game-preset-command-hint-2 = [number of rounds]
+set-game-preset-command-hint-3 = [decoy preset]
 
+set-game-preset-optional-argument-not-integer = If argument 2 is provided it must be a number.
 set-game-preset-preset-error = Unable to find game preset "{$preset}"
+set-game-preset-decoy-error = If argument 3 is provided it must be a valid preset. Unable to find game preset "{$preset}"
+
 #set-game-preset-preset-set = Set game preset to "{$preset}"
 set-game-preset-preset-set-finite = Set game preset to "{$preset}" for the next {$rounds} rounds.
+set-game-preset-preset-set-finite-with-decoy = Set game preset to "{$preset}" for the next {$rounds} rounds, showing {$decoy} in the lobby.
index f5ee905a6a13d1211b3e423d29dabc74ecce7767..ff6450d7cb8a73f5a86540aa5b85fb14f998e8f3 100644 (file)
   rules:
     - Secret
 
-- type: gamePreset
-  id: SecretExtended #For Admin Use: Runs Extended but shows "Secret" in lobby.
-  alias:
-    - secretextended
-  name: secret-title
-  showInVote: false #Admin Use
-  description: secret-description
-  rules:
-    - BasicStationEventScheduler
-    - MeteorSwarmScheduler
-    - SpaceTrafficControlEventScheduler
-    - BasicRoundstartVariation
-
-- type: gamePreset
-  id: SecretGreenshift #For Admin Use: Runs Greenshift but shows "Secret" in lobby.
-  alias:
-  - secretgreenshift
-  name: secret-title
-  showInVote: false #Admin Use
-  description: secret-description
-  rules:
-  - SpaceTrafficControlFriendlyEventScheduler
-  - BasicRoundstartVariation
-
 - type: gamePreset
   id: Sandbox
   alias: