]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Fix/Addition - Wizard Survivor Antag Status (#35226)
authorkeronshb <54602815+keronshb@users.noreply.github.com>
Sun, 23 Feb 2025 17:24:36 +0000 (12:24 -0500)
committerGitHub <noreply@github.com>
Sun, 23 Feb 2025 17:24:36 +0000 (12:24 -0500)
15 files changed:
Content.Server/GameTicking/Rules/Components/SurvivorRuleComponent.cs [new file with mode: 0644]
Content.Server/GameTicking/Rules/SurvivorRuleSystem.cs [new file with mode: 0644]
Content.Server/Magic/MagicSystem.cs
Content.Server/Roles/SurvivorRoleComponent.cs [new file with mode: 0644]
Content.Server/Zombies/ZombieSystem.Transform.cs
Content.Shared/Magic/Events/RandomGlobalSpawnSpellEvent.cs
Content.Shared/Magic/SharedMagicSystem.cs
Content.Shared/Survivor/Components/SurvivorComponent.cs [new file with mode: 0644]
Resources/Locale/en-US/game-ticking/game-presets/preset-wizard.ftl [new file with mode: 0644]
Resources/Prototypes/GameRules/survivor.yml [new file with mode: 0644]
Resources/Prototypes/Magic/event_spells.yml
Resources/Prototypes/Roles/Antags/wizard.yml [new file with mode: 0644]
Resources/Prototypes/Roles/MindRoles/mind_roles.yml
Resources/Prototypes/Roles/role_types.yml
Resources/Prototypes/tags.yml

diff --git a/Content.Server/GameTicking/Rules/Components/SurvivorRuleComponent.cs b/Content.Server/GameTicking/Rules/Components/SurvivorRuleComponent.cs
new file mode 100644 (file)
index 0000000..5e6cc20
--- /dev/null
@@ -0,0 +1,8 @@
+namespace Content.Server.GameTicking.Rules.Components;
+
+/// <summary>
+/// Component for the SurvivorRuleSystem. Game rule that turns everyone into a survivor and gives them the objective to escape centcom alive.
+/// Started by Wizard Summon Guns/Magic spells.
+/// </summary>
+[RegisterComponent, Access(typeof(SurvivorRuleSystem))]
+public sealed partial class SurvivorRuleComponent : Component;
diff --git a/Content.Server/GameTicking/Rules/SurvivorRuleSystem.cs b/Content.Server/GameTicking/Rules/SurvivorRuleSystem.cs
new file mode 100644 (file)
index 0000000..00f652b
--- /dev/null
@@ -0,0 +1,108 @@
+using Content.Server.Antag;
+using Content.Server.GameTicking.Rules.Components;
+using Content.Server.Mind;
+using Content.Server.Roles;
+using Content.Server.Shuttles.Systems;
+using Content.Shared.GameTicking.Components;
+using Content.Shared.Mind;
+using Content.Shared.Mobs.Systems;
+using Content.Shared.Survivor.Components;
+using Content.Shared.Tag;
+using Robust.Server.GameObjects;
+
+namespace Content.Server.GameTicking.Rules;
+
+public sealed class SurvivorRuleSystem : GameRuleSystem<SurvivorRuleComponent>
+{
+    [Dependency] private readonly RoleSystem _role = default!;
+    [Dependency] private readonly MindSystem _mind = default!;
+    [Dependency] private readonly AntagSelectionSystem _antag = default!;
+    [Dependency] private readonly TransformSystem _xform = default!;
+    [Dependency] private readonly EmergencyShuttleSystem _eShuttle = default!;
+    [Dependency] private readonly TagSystem _tag = default!;
+    [Dependency] private readonly MobStateSystem _mobState = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<SurvivorRoleComponent, GetBriefingEvent>(OnGetBriefing);
+    }
+
+    // TODO: Planned rework post wizard release when RandomGlobalSpawnSpell becomes a gamerule
+    protected override void Started(EntityUid uid, SurvivorRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
+    {
+        base.Started(uid, component, gameRule, args);
+
+        var allAliveHumanMinds = _mind.GetAliveHumans();
+
+        foreach (var humanMind in allAliveHumanMinds)
+        {
+            if (!humanMind.Comp.OwnedEntity.HasValue)
+                continue;
+
+            var mind = humanMind.Owner;
+            var ent = humanMind.Comp.OwnedEntity.Value;
+
+            if (HasComp<SurvivorComponent>(mind) || _tag.HasTag(mind, "InvalidForSurvivorAntag"))
+                continue;
+
+            EnsureComp<SurvivorComponent>(mind);
+            _role.MindAddRole(mind, "MindRoleSurvivor");
+            _antag.SendBriefing(ent, Loc.GetString("survivor-role-greeting"), Color.Olive, null);
+        }
+    }
+
+    private void OnGetBriefing(Entity<SurvivorRoleComponent> ent, ref GetBriefingEvent args)
+    {
+        args.Append(Loc.GetString("survivor-role-greeting"));
+    }
+
+    protected override void AppendRoundEndText(EntityUid uid,
+        SurvivorRuleComponent component,
+        GameRuleComponent gameRule,
+        ref RoundEndTextAppendEvent args)
+    {
+        base.AppendRoundEndText(uid, component, gameRule, ref args);
+
+        // Using this instead of alive antagonists to make checking for shuttle & if the ent is alive easier
+        var existingSurvivors = AllEntityQuery<SurvivorComponent, MindComponent>();
+
+        var deadSurvivors = 0;
+        var aliveMarooned = 0;
+        var aliveOnShuttle = 0;
+        var eShuttle = _eShuttle.GetShuttle();
+
+        while (existingSurvivors.MoveNext(out _, out _, out var mindComp))
+        {
+            // If their brain is gone or they respawned/became a ghost role
+            if (mindComp.CurrentEntity is null)
+            {
+                deadSurvivors++;
+                continue;
+            }
+
+            var survivor = mindComp.CurrentEntity.Value;
+
+            if (!_mobState.IsAlive(survivor))
+            {
+                deadSurvivors++;
+                continue;
+            }
+
+            if (eShuttle != null && eShuttle.Value.IsValid() && (Transform(eShuttle.Value).MapID == _xform.GetMapCoordinates(survivor).MapId))
+            {
+                aliveOnShuttle++;
+                continue;
+            }
+
+            aliveMarooned++;
+        }
+
+        args.AddLine(Loc.GetString("survivor-round-end-dead-count", ("deadCount", deadSurvivors)));
+        args.AddLine(Loc.GetString("survivor-round-end-alive-count", ("aliveCount", aliveMarooned)));
+        args.AddLine(Loc.GetString("survivor-round-end-alive-on-shuttle-count", ("aliveCount", aliveOnShuttle)));
+
+        // Player manifest at EOR shows who's a survivor so no need for extra info here.
+    }
+}
index a7e5e967862acd371580f687f751aa329ccb3146..34c12954c69e9c8feb081f4cbbc0181a4d4c63d2 100644 (file)
@@ -1,12 +1,20 @@
 using Content.Server.Chat.Systems;
+using Content.Server.GameTicking;
+using Content.Server.GameTicking.Rules.Components;
 using Content.Shared.Magic;
 using Content.Shared.Magic.Events;
+using Content.Shared.Mind;
+using Content.Shared.Tag;
+using Robust.Shared.Prototypes;
 
 namespace Content.Server.Magic;
 
 public sealed class MagicSystem : SharedMagicSystem
 {
     [Dependency] private readonly ChatSystem _chat = default!;
+    [Dependency] private readonly GameTicker _gameTicker = default!;
+    [Dependency] private readonly TagSystem _tag = default!;
+    [Dependency] private readonly SharedMindSystem _mind = default!;
 
     public override void Initialize()
     {
@@ -32,4 +40,20 @@ public sealed class MagicSystem : SharedMagicSystem
         Spawn(ev.Effect, perfXForm.Coordinates);
         Spawn(ev.Effect, targetXForm.Coordinates);
     }
+
+    protected override void OnRandomGlobalSpawnSpell(RandomGlobalSpawnSpellEvent ev)
+    {
+        base.OnRandomGlobalSpawnSpell(ev);
+
+        if (!ev.MakeSurvivorAntagonist)
+            return;
+
+        if (_mind.TryGetMind(ev.Performer, out var mind, out _) && !_tag.HasTag(mind, "InvalidForSurvivorAntag"))
+            _tag.AddTag(mind, "InvalidForSurvivorAntag");
+
+        EntProtoId survivorRule = "Survivor";
+
+        if (!_gameTicker.IsGameRuleActive<SurvivorRuleComponent>())
+            _gameTicker.StartGameRule(survivorRule);
+    }
 }
diff --git a/Content.Server/Roles/SurvivorRoleComponent.cs b/Content.Server/Roles/SurvivorRoleComponent.cs
new file mode 100644 (file)
index 0000000..e5e6dd9
--- /dev/null
@@ -0,0 +1,9 @@
+using Content.Shared.Roles;
+
+namespace Content.Server.Roles;
+
+/// <summary>
+///     Adds to a mind role ent to tag they're a Survivor
+/// </summary>
+[RegisterComponent]
+public sealed partial class SurvivorRoleComponent : BaseMindRoleComponent;
index 7acfe9dbbd9632c1754695af4701d25c04c6072d..82c9e2dacce838563cbb2eb4e8743da475807e80 100644 (file)
@@ -35,6 +35,7 @@ using Content.Shared.Prying.Components;
 using Content.Shared.Traits.Assorted;
 using Robust.Shared.Audio.Systems;
 using Content.Shared.Ghost.Roles.Components;
+using Content.Shared.Tag;
 
 namespace Content.Server.Zombies;
 
@@ -58,6 +59,7 @@ public sealed partial class ZombieSystem
     [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
     [Dependency] private readonly NPCSystem _npc = default!;
     [Dependency] private readonly SharedRoleSystem _roles = default!;
+    [Dependency] private readonly TagSystem _tag = default!;
 
     /// <summary>
     /// Handles an entity turning into a zombie when they die or go into crit
@@ -275,5 +277,9 @@ public sealed partial class ZombieSystem
         RaiseLocalEvent(target, ref ev, true);
         //zombies get slowdown once they convert
         _movementSpeedModifier.RefreshMovementSpeedModifiers(target);
+
+        //Need to prevent them from getting an item, they have no hands.
+        // Also prevents them from becoming a Survivor. They're undead.
+        _tag.AddTag(target, "InvalidForGlobalSpawnSpell");
     }
 }
index c77607562a421f6d2403428c7f16dad2500cfaa0..eb39c0cd08904863f459e09123330c7f1796095d 100644 (file)
@@ -20,4 +20,11 @@ public sealed partial class RandomGlobalSpawnSpellEvent : InstantActionEvent, IS
 
     [DataField]
     public string? Speech { get; private set; }
+
+    /// <summary>
+    /// Should this Global spawn spell turn its targets into a Survivor Antagonist?
+    /// Ignores the caster for this.
+    /// </summary>
+    [DataField]
+    public bool MakeSurvivorAntagonist = false;
 }
index 0be5646f4b2535130dea0d6684c8ba8303b652d1..49f0825ddffcfcceebaa79b025e68610c091c2bb 100644 (file)
@@ -469,7 +469,8 @@ public abstract class SharedMagicSystem : EntitySystem
     #endregion
     #region Global Spells
 
-    private void OnRandomGlobalSpawnSpell(RandomGlobalSpawnSpellEvent ev)
+    // TODO: Change this into a "StartRuleAction" when actions with multiple events are supported
+    protected virtual void OnRandomGlobalSpawnSpell(RandomGlobalSpawnSpellEvent ev)
     {
         if (!_net.IsServer || ev.Handled || !PassesSpellPrerequisites(ev.Action, ev.Performer) || ev.Spawns is not { } spawns)
             return;
@@ -486,6 +487,9 @@ public abstract class SharedMagicSystem : EntitySystem
 
             var ent = human.Comp.OwnedEntity.Value;
 
+            if (_tag.HasTag(ent, "InvalidForGlobalSpawnSpell"))
+                continue;
+
             var mapCoords = _transform.GetMapCoordinates(ent);
             foreach (var spawn in EntitySpawnCollection.GetSpawns(spawns, _random))
             {
diff --git a/Content.Shared/Survivor/Components/SurvivorComponent.cs b/Content.Shared/Survivor/Components/SurvivorComponent.cs
new file mode 100644 (file)
index 0000000..140f688
--- /dev/null
@@ -0,0 +1,9 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Survivor.Components;
+
+/// <summary>
+///     Component to keep track of which entities are a Survivor antag.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed partial class SurvivorComponent : Component;
diff --git a/Resources/Locale/en-US/game-ticking/game-presets/preset-wizard.ftl b/Resources/Locale/en-US/game-ticking/game-presets/preset-wizard.ftl
new file mode 100644 (file)
index 0000000..c355f4b
--- /dev/null
@@ -0,0 +1,36 @@
+## Survivor
+
+roles-antag-survivor-name = Survivor
+# It's a Halo reference
+roles-antag-survivor-objective = Current Objective: Survive
+
+survivor-role-greeting =
+    You are a Survivor.
+    Above all you need to make it back to CentComm alive.
+    Collect as much firepower as needed to guarantee your survival.
+    Trust no one.
+
+survivor-round-end-dead-count =
+{
+    $deadCount ->
+        [one] [color=red]{$deadCount}[/color] survivor died.
+        *[other] [color=red]{$deadCount}[/color] survivors died.
+}
+
+survivor-round-end-alive-count =
+{
+    $aliveCount ->
+        [one] [color=yellow]{$aliveCount}[/color] survivor was marooned on the station.
+        *[other] [color=yellow]{$aliveCount}[/color] survivors were marooned on the station.
+}
+
+survivor-round-end-alive-on-shuttle-count =
+{
+    $aliveCount ->
+        [one] [color=green]{$aliveCount}[/color] survivor made it out alive.
+        *[other] [color=green]{$aliveCount}[/color] survivors made it out alive.
+}
+
+## TODO: Wizard
+
+## TODO: Wizard Apprentice (Coming sometime post-wizard release)
diff --git a/Resources/Prototypes/GameRules/survivor.yml b/Resources/Prototypes/GameRules/survivor.yml
new file mode 100644 (file)
index 0000000..e8f73a2
--- /dev/null
@@ -0,0 +1,5 @@
+- type: entity
+  id: Survivor
+  parent: BaseGameRule
+  components:
+  - type: SurvivorRule
index 87b3a571077d4d79fecd43bee586179b7894424e..a72dc8b21795a6c219511c7bd531d55b2cb7a4d8 100644 (file)
@@ -26,6 +26,7 @@
       sprite: Objects/Weapons/Guns/Rifles/ak.rsi
       state: base
     event: !type:RandomGlobalSpawnSpellEvent
+      makeSurvivorAntagonist: true
       spawns:
       - id: WeaponPistolViper
         orGroup: Guns
       - id: RevolverCapGunFake
         orGroup: Guns
       speech: action-speech-spell-summon-guns
-      
+
 - type: entity
   id: ActionSummonMagic
   name: Summon Magic
       sprite: Objects/Magic/magicactions.rsi
       state: magicmissile
     event: !type:RandomGlobalSpawnSpellEvent
+      makeSurvivorAntagonist: true
       spawns:
       - id: SpawnSpellbook
         orGroup: Magics
diff --git a/Resources/Prototypes/Roles/Antags/wizard.yml b/Resources/Prototypes/Roles/Antags/wizard.yml
new file mode 100644 (file)
index 0000000..e8ffe64
--- /dev/null
@@ -0,0 +1,8 @@
+# TODO: Actual wizard coming later, this is just for the survival antags
+
+- type: antag
+  id: Survivor
+  name: roles-antag-survivor-name
+  antagonist: true
+  objective: roles-antag-survivor-objective
+  # guides: [  ]
index e32c173870e72875e74821b79b09867fc09e7fae..ded64895f092618526bbf47012188e8931fe5b8a 100644 (file)
   - type: MindRole
     antagPrototype: Rev
 
+# Survivors (Wizard)
+- type: entity
+  parent: BaseMindRoleAntag
+  id: MindRoleSurvivor
+  name: Survivor Role
+  components:
+  - type: MindRole
+    antagPrototype: Survivor
+    roleType: FreeAgent
+  - type: SurvivorRole
+
 # Thief
 - type: entity
   parent: BaseMindRoleAntag
index 326abe4ee4f463dec9deacc3e0a4b7e878f8cf0c..97e11d80b27f057a183906593e42dd479e51b21c 100644 (file)
@@ -35,3 +35,4 @@
   id: SiliconAntagonist
   name: role-type-silicon-antagonist-name
   color: '#c832e6'
+
index 958caccfe7dc0e367b82ea2e493be5bfb3e93c60..f5eba41c70aaa565e7904403cf131f7434bc006e 100644 (file)
 - type: Tag
   id: IntercomElectronics
 
+- type: Tag
+  id: InvalidForGlobalSpawnSpell
+
+- type: Tag
+  id: InvalidForSurvivorAntag
+
 - type: Tag
   id: JawsOfLife