Repository: Goob-Station/Goob-Station
Pull request #1962: Storyteller minor update
Author: login: gusxyz
Labels: name: size/M
description: Denotes a PR that changes 100-999 lines.
name: S: Untriaged
description: A professional will be with you shortly.
Created: 2025-03-09T01:49:30Z | Merged: 2025-03-10T10:23:41Z

<!-- Guidelines: https://docs.spacestation14.io/en/getting-started/pr-guideline -->

## About the PR
pirates can spawn
## Why / Balance
<!-- Discuss how this would affect game balance or explain why it was changed. Link any relevant discussions or issues. -->

## Technical details
roundstart antags are tied to weights again.

## Media
<!-- Attach media if the PR makes ingame changes (clothing, items, features, etc). 
Small fixes/refactors are exempt. Media may be used in SS14 progress reports with credit. -->

## Requirements
<!-- Confirm the following by placing an X in the brackets [X]: -->
- [ ] I have read and am following the [Pull Request and Changelog Guidelines](https://docs.spacestation14.com/en/general-development/codebase-info/pull-request-guidelines.html).
- [ ] I have added media to this PR or it does not require an ingame showcase.
<!-- You should understand that not following the above may get your PR closed at maintainer’s discretion -->

## Breaking changes
<!-- List any breaking changes, including namespaces, public class/method/field changes, prototype renames; and provide instructions for fixing them.
This will be posted in #codebase-changes. -->

**Changelog**
<!-- Add a Changelog entry to make players aware of new features or changes that could affect gameplay.
Make sure to read the guidelines and take this Changelog template out of the comment block in order for it to show up.
Changelog must have a :cl: symbol, so the bot recognizes the changes and adds them to the game's changelog. -->
:cl:
- fix: Pirates, Abductors and the Blob can now spawn as midround event in storyteller modes!
- fix: Nukie spam



Diff:
diff --git a/Content.Server/_Goobstation/StationEvents/Components/GameDirectorComponent.cs b/Content.Server/_Goobstation/StationEvents/Components/GameDirectorComponent.cs
index fc2a08aae882..ccd4e2b9be8d 100644
--- a/Content.Server/_Goobstation/StationEvents/Components/GameDirectorComponent.cs
+++ b/Content.Server/_Goobstation/StationEvents/Components/GameDirectorComponent.cs
@@ -1,5 +1,6 @@
 ﻿using Content.Server._Goobstation.StationEvents.Metric;
 using Content.Shared.EntityTable.EntitySelectors;
+using Content.Shared.Random;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
 
@@ -73,6 +74,11 @@ public sealed partial class GameDirectorComponent : Component
     [DataField]
     public List<PossibleEvent> PossibleEvents = new();
     // Could have Chaos multipliers here, or multipliers per player (so stories are harder with more players).
+
+    /// <summary>
+    /// All the possible roundstart antags.
+    /// </summary>
+    public ProtoId<WeightedRandomPrototype> RoundStartAntagsWeightTable = "GameDirector";
 }
 
 /// <summary>
diff --git a/Content.Server/_Goobstation/StationEvents/GameDirectorSystem.cs b/Content.Server/_Goobstation/StationEvents/GameDirectorSystem.cs
index 3f811c76b933..323ab22f777c 100644
--- a/Content.Server/_Goobstation/StationEvents/GameDirectorSystem.cs
+++ b/Content.Server/_Goobstation/StationEvents/GameDirectorSystem.cs
@@ -16,6 +16,8 @@
 using Content.Shared.Ghost;
 using Content.Shared.Humanoid;
 using Content.Shared.Prototypes;
+using Content.Shared.Random;
+using Content.Shared.Random.Helpers;
 using Content.Shared.Tag;
 using JetBrains.Annotations;
 using Robust.Server.Player;
@@ -234,76 +236,26 @@ private void TrySpawnRoundstartAntags(GameDirectorComponent scheduler, int count
             return;
 
         // Spawn antags based on GameDirectorComponent
-            var roundStartAntags = new List<EntityPrototype>();
-            var calmStartAntags = new List<EntityPrototype>();
-            foreach (var proto in GameTicker.GetAllGameRulePrototypes())
-            {
-                if (!proto.TryGetComponent<TagComponent>(out var tag, _factory))
-                    continue;
-
-                if (!proto.TryGetComponent<GameRuleComponent>(out var gameRuleComp, _factory))
-                    continue;
-
-                if (gameRuleComp.MinPlayers > count)
-                    continue;
-
-                if (proto.HasComponent<DynamicRulesetComponent>())
-                    continue;
-
-                if (_tag.HasTag(tag, "MidroundAntag"))
-                    continue;
-
-                if (_tag.HasTag(tag, "RoundstartAntag"))
-                {
-                    roundStartAntags.Add(proto);
-                }
-                else if (_tag.HasTag(tag, "CalmAntag"))
-                {
-                    calmStartAntags.Add(proto);
-                }
-            }
-
-            if (!scheduler.DualAntags)
-            {
-                if (roundStartAntags.Count == 0)
-                {
-                    LogMessage("No valid roundstart antags found!");
-                    return;
-                }
-
-                LogMessage("Choosing roundstart antag");
-                var random = new Random();
-                var randomAntag = roundStartAntags[random.Next(roundStartAntags.Count)];
-                LogMessage($"Roundstart antag chosen: {randomAntag.ID}");
-
-                var ruleEnt = GameTicker.AddGameRule(randomAntag.ID);
-                GameTicker.StartGameRule(ruleEnt);
-            }
-            else
-            {
-                if (calmStartAntags.Count < 2)
-                {
-                    LogMessage("Not enough valid roundstart antags found!");
-                    return;
-                }
-
-                LogMessage("Choosing multiple roundstart antags");
-                var random = new Random();
-
-                var firstIndex = random.Next(calmStartAntags.Count);
-                var randomAntagFirst = calmStartAntags[firstIndex];
-                calmStartAntags.RemoveAt(firstIndex);
+        var weightList = _prototypeManager.Index(scheduler.RoundStartAntagsWeightTable);
 
-                var randomAntagSecond = calmStartAntags[random.Next(calmStartAntags.Count)];
-
-                LogMessage($"Roundstart antags chosen: 1: {randomAntagFirst.ID} 2: {randomAntagSecond.ID}");
-
-                var ruleFirstEnt = GameTicker.AddGameRule(randomAntagFirst.ID);
-                var ruleSecondEnt = GameTicker.AddGameRule(randomAntagSecond.ID);
-
-                GameTicker.StartGameRule(ruleFirstEnt);
-                GameTicker.StartGameRule(ruleSecondEnt);
-            }
+        if (!scheduler.DualAntags)
+        {
+            EntProtoId pick = weightList.Pick(_random);
+            LogMessage("Choosing roundstart antag");
+            LogMessage($"Roundstart antag chosen: {pick}");
+            GameTicker.AddGameRule(pick);
+        }
+        else
+        {
+            var pick = weightList.Pick(_random);
+            var pick2 = weightList.Pick(_random);
+            LogMessage("Choosing roundstart antag");
+            LogMessage($"Roundstart antag chosen: {pick}");
+            LogMessage($"Roundstart antag chosen: {pick2}");
+
+            GameTicker.AddGameRule(pick);
+            GameTicker.AddGameRule(pick2);
+        }
     }
 
     /// <summary>
diff --git a/Resources/Prototypes/_Goobstation/GameRules/gamedirector_weights.yml b/Resources/Prototypes/_Goobstation/GameRules/gamedirector_weights.yml
new file mode 100644
index 000000000000..f2461d93a771
--- /dev/null
+++ b/Resources/Prototypes/_Goobstation/GameRules/gamedirector_weights.yml
@@ -0,0 +1,12 @@
+- type: weightedRandom
+  id: GameDirector
+  weights:
+    Traitor: 0.25
+    Changeling: 0.10
+    Nukeops: 0.09
+    Revolutionary: 0.04
+    Zombie: 0.03
+    Heretic: 0.10
+    BlobGameMode: 0.04
+    Wizard: 0.08
+    HonkOps: 0.01
diff --git a/Resources/Prototypes/_Goobstation/GameRules/roundstart.yml b/Resources/Prototypes/_Goobstation/GameRules/roundstart.yml
index 03f05ce04019..440fbeacacb6 100644
--- a/Resources/Prototypes/_Goobstation/GameRules/roundstart.yml
+++ b/Resources/Prototypes/_Goobstation/GameRules/roundstart.yml
@@ -202,6 +202,10 @@
     earliestStart: 50
     minimumPlayers: 20
     maxOccurrences: 2
+    chaos:
+      Hostile: 200
+      Death: 300
+      Medical: 200
   - type: BlobSpawnRule
     carrierBlobProtos:
     - SpawnPointGhostBlobRat
diff --git a/Resources/Prototypes/_Goobstation/_Pirates/game_ticking.yml b/Resources/Prototypes/_Goobstation/_Pirates/game_ticking.yml
index b1edbad8671f..e553f90e9209 100644
--- a/Resources/Prototypes/_Goobstation/_Pirates/game_ticking.yml
+++ b/Resources/Prototypes/_Goobstation/_Pirates/game_ticking.yml
@@ -8,8 +8,15 @@
     earliestStart: 20
     reoccurrenceDelay: 60
     minimumPlayers: 30
+    chaos:
+      Hostile: 50
+      Medical: 50
+      Death: 25
   - type: PendingPirateRule
     ransomPrototype: BikeHornRansom
+  - type: Tag
+    tags:
+    - MidroundAntag
 
 - type: entity
   parent: BaseGameRule
@@ -35,6 +42,9 @@
       - type: RandomMetadata
         nameSegments:
         - names_death_commando
+  - type: Tag
+    tags:
+    - MidroundAntag
 
 - type: entity
   parent: BikeHorn
diff --git a/Resources/Prototypes/_Shitmed/GameRules/events.yml b/Resources/Prototypes/_Shitmed/GameRules/events.yml
index ebb6f87e0e1c..e050bf52dfcb 100644
--- a/Resources/Prototypes/_Shitmed/GameRules/events.yml
+++ b/Resources/Prototypes/_Shitmed/GameRules/events.yml
@@ -8,6 +8,9 @@
     weight: 7.5
     minimumPlayers: 10
     duration: 1
+    chaos: # Goobstation
+      Hostile: 75
+      Medical: 75
   - type: RuleGrids
   - type: LoadMapRule
     mapPath: /Maps/_Shitmed/Shuttles/ShuttleEvent/abductor_shuttle.yml
@@ -99,6 +102,9 @@
     weight: 7.5
     minimumPlayers: 20
     duration: 1
+    chaos: # Goobstation
+      Hostile: 150
+      Medical: 150
   - type: RuleGrids
   - type: LoadMapRule
     mapPath: /Maps/_Shitmed/Shuttles/ShuttleEvent/duo_abductor_shuttle.yml


Reviews:
user: login: gluesniffler
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-09T08:48:58Z
user: login: gusxyz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-09T21:43:17Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 0**

<details>
<summary>🧹 Nitpick comments (1)</summary><blockquote>

<details>
<summary>Resources/Prototypes/_Goobstation/GameRules/roundstart.yml (1)</summary><blockquote>

`205-208`: **Hark! New Chaos Configuration Unveiled!**  
Thou hast added a most potent "chaos" attribute to the BlobSpawn entity with values set as Hostile: 200, Death: 300, and Medical: 200. Prithee, ensure these numbers are in harmony with the intended mid-round shenanigans and game balance. Consider adding a brief comment elucidating the purpose of each chaos key for future sorcerers (developers) who might peruse these runes.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between aefa7f3bbc98b302160d1f11f1cf190f4940883a and 213f77b7ff1899eb9f749d23ea8ae6472fdc9184.

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `Resources/Prototypes/_Goobstation/GameRules/gamedirector_weights.yml` (1 hunks)
* `Resources/Prototypes/_Goobstation/GameRules/roundstart.yml` (1 hunks)
* `Resources/Prototypes/_Shitmed/GameRules/events.yml` (2 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* Resources/Prototypes/_Goobstation/GameRules/gamedirector_weights.yml

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (5)</summary>

* GitHub Check: build (ubuntu-latest)
* GitHub Check: Test Packaging
* GitHub Check: YAML map schema validator
* GitHub Check: YAML Linter
* GitHub Check: build (ubuntu-latest)

</details>

<details>
<summary>🔇 Additional comments (2)</summary><blockquote>

<details>
<summary>Resources/Prototypes/_Shitmed/GameRules/events.yml (2)</summary>

`11-13`: **Good Bequest of Chaos Attributes for LoneAbductorSpawn!**

Thou hast skilfully inscribed chaotic incantations by adding the `chaos` attribute with values *Hostile: 75* and *Medical: 75*. This noble change lends clarity to how much turmoil these events shall conjure. Pray, verify that these values art in accord with the overall balance desired in this mid-round spectacle.

---

`105-107`: **Thou Hast Increased Chaos for DuoAbductorSpawn!**

By these exalted digits (*Hostile: 150* and *Medical: 150*), the DuoAbductorSpawn is imbued with greater tumult, befitting its doubled might. I entreat thee to ensure that such magnified chaos doth not overwhelm the players, but rather enhances the strategic enchantment of the game.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T06:24:30Z
user: login: gluesniffler
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T10:11:49Z
user: login: SX-7
body: It's gonna be so bad 
![ProgrammerHumor-1j7h065.jpg](https://github.com/user-attachments/assets/66635c54-f6f7-4e85-8c4a-2eaa7789950e)


state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-10T10:23:32Z

Review comments:
diff_hunk: @@ -0,0 +1,11 @@
+- type: weightedRandom
+  id: GameDirector
+  weights:
path: Resources/Prototypes/_Goobstation/GameRules/gamedirector_weights.yml
user: login: gluesniffler
body: Shouldnt you add honkops and abductors here?
created_at: 2025-03-09T08:48:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,11 @@
+- type: weightedRandom
+  id: GameDirector
+  weights:
path: Resources/Prototypes/_Goobstation/GameRules/gamedirector_weights.yml
user: login: gusxyz
body: Ill add abductor but is honkops and actual gamerule as well? Not just a preset?
created_at: 2025-03-09T21:43:17Z
author_association: CONTRIBUTOR

PR comments:
user: login: coderabbitai[bot]
created_at: 2025-03-09T01:51:53Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough
Hark! In this noble quest, the Game Director’s apparatus hath been enhanced. A new public field, **RoundStartAntagsWeightTable**, hath been bestowed upon the GameDirectorComponent to hold a WeightedRandomPrototype identifier, initialized to "GameDirector". In the GameDirectorSystem, the method **TrySpawnRoundstartAntags** now employs a weighted random selection process using this sacred table. Depending on whether dual antagonists are permitted, one or two adversaries are chosen by invoking a Pick method on the weighted list, simplifying the erstwhile tangled logic. Furthermore, a fresh YAML grimoire, **gamedirector_weights.yml**, sets forth the weights for various roles (e.g., Traitor, Changeling, Nukeops, etc.), and additional configuration hath been inscribed in **game_ticking.yml** for pirate entities, introducing new chaos parameters and the tag **MidroundAntag**.

## Sequence Diagram(s)
```mermaid
sequenceDiagram
    participant GDS as GameDirectorSystem
    participant RDC as GameDirectorComponent
    participant WRP as WeightedRandomPrototype
    participant Log as Logger

    GDS->>RDC: Retrieve RoundStartAntagsWeightTable
    GDS->>WRP: Get weighted list using "GameDirector" weights
    alt Dual Antags Enabled
        GDS->>WRP: Pick two antagonists
    else
        GDS->>WRP: Pick one antagonist
    end
    GDS->>Log: Log the chosen antagonist(s)
```

## Possibly related PRs
- **Goob-Station/Goob-Station#709:** The changes in the main PR, which introduce a new field for selecting roundstart antagonists, are related to the modifications in the retrieved PR that also involve adjustments to antagonist roles and selection criteria in the game rules.
- **Goob-Station/Goob-Station#1655:** The changes in the main PR, which introduce a new field for weighted random selection of roundstart antagonists, are related to the modifications in the retrieved PR that adjust the timing of antagonist role assignments, as both involve the selection process for antagonists in the game.
- **Goob-Station/Goob-Station#722:** The changes in the main PR, which introduce a new field for weighted random selection of roundstart antagonists, are related to the modifications in the retrieved PR that adjust the logic for rolling random antagonists, as both involve the selection process for game roles.

## Suggested reviewers
- **Piras314**
- **Aidenkrz**
- **SX-7**

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- tips_start -->

---



<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=Goob-Station/Goob-Station&utm_content=1962):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Generate unit testing code for this file.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai generate unit testing code for this file.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and generate unit testing code.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR
user: login: SX-7
created_at: 2025-03-09T07:38:09Z
body: Does it first announce ransom, and then spawns pirates? Doesn't seem obvious from the code, unless it's handled by pirate code instead.
In which case it's gonna spawn pirates without announcement sometimes.
author_association: CONTRIBUTOR
user: login: Ilya246
created_at: 2025-03-09T18:51:13Z
body: should also maybe fix abductor blob etc
author_association: CONTRIBUTOR
user: login: gluesniffler
created_at: 2025-03-09T18:52:33Z
body: > Does it first announce ransom, and then spawns pirates? Doesn't seem obvious from the code, unless it's handled by pirate code instead. In which case it's gonna spawn pirates without announcement sometimes.

The gamerule for pirates handles it yes, any time it runs it does the announcement, adds the ransom object to cargo and starts a timer for the next gamerule.
author_association: CONTRIBUTOR
user: login: SX-7
created_at: 2025-03-09T19:00:26Z
body: bleh, I'll check the code later, but I don't think it should spawn pirates without announcement even if they have a tag, since that gamerule doesn't have chaos assigned to it, only the ransom one 
author_association: CONTRIBUTOR
user: login: gusxyz
created_at: 2025-03-10T06:15:50Z
body: @gluesniffler 
author_association: CONTRIBUTOR