]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Remove ghost role component references (#15262)
authorDrSmugleaf <DrSmugleaf@users.noreply.github.com>
Wed, 12 Apr 2023 13:32:14 +0000 (06:32 -0700)
committerGitHub <noreply@github.com>
Wed, 12 Apr 2023 13:32:14 +0000 (23:32 +1000)
30 files changed:
Content.Server/Botany/Systems/PlantHolderSystem.cs
Content.Server/Chemistry/ReagentEffects/MakeSentient.cs
Content.Server/GameTicking/Rules/NukeopsRuleSystem.cs
Content.Server/Ghost/Roles/Components/GhostRoleComponent.cs
Content.Server/Ghost/Roles/Components/GhostRoleMobSpawnerComponent.cs
Content.Server/Ghost/Roles/Components/GhostTakeoverAvailableComponent.cs
Content.Server/Ghost/Roles/Components/TakeGhostRoleEvent.cs [new file with mode: 0644]
Content.Server/Ghost/Roles/GhostRoleSystem.cs
Content.Server/Ghost/Roles/MakeGhostRoleCommand.cs
Content.Server/PAI/PAISystem.cs
Content.Server/StationEvents/Events/RandomSentience.cs
Content.Server/Zombies/ZombifyOnDeathSystem.cs
Resources/Prototypes/Entities/Markers/Spawners/ghost_roles.yml
Resources/Prototypes/Entities/Mobs/NPCs/animals.yml
Resources/Prototypes/Entities/Mobs/NPCs/bear.yml
Resources/Prototypes/Entities/Mobs/NPCs/carp.yml
Resources/Prototypes/Entities/Mobs/NPCs/pets.yml
Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml
Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml
Resources/Prototypes/Entities/Mobs/NPCs/silicon.yml
Resources/Prototypes/Entities/Mobs/NPCs/spacetick.yml
Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml
Resources/Prototypes/Entities/Mobs/Player/dragon.yml
Resources/Prototypes/Entities/Mobs/Player/familiars.yml
Resources/Prototypes/Entities/Mobs/Player/guardian.yml
Resources/Prototypes/Entities/Mobs/Player/humanoid.yml
Resources/Prototypes/Entities/Mobs/Player/silicon.yml
Resources/Prototypes/Entities/Mobs/Player/skeleton.yml
Resources/Prototypes/Entities/Objects/Devices/Syndicate_Gadgets/reinforcement_teleporter.yml
Resources/Prototypes/XenoArch/Effects/utility_effects.yml

index 29c2d964d3f99a3b8d08068b3f7867b81841b3cd..04ec24e9fca1c4ca515a63f5906209dcd3430cda 100644 (file)
@@ -609,9 +609,10 @@ namespace Content.Server.Botany.Systems
 
             if (component.Seed.Sentient)
             {
-                var comp = EnsureComp<GhostTakeoverAvailableComponent>(uid);
-                comp.RoleName = MetaData(uid).EntityName;
-                comp.RoleDescription = Loc.GetString("station-event-random-sentience-role-description", ("name", comp.RoleName));
+                var ghostRole = EnsureComp<GhostRoleComponent>(uid);
+                EnsureComp<GhostTakeoverAvailableComponent>(uid);
+                ghostRole.RoleName = MetaData(uid).EntityName;
+                ghostRole.RoleDescription = Loc.GetString("station-event-random-sentience-role-description", ("name", ghostRole.RoleName));
             }
 
             if (component.UpdateSpriteAfterUpdate)
index c762ba8a83c47bc0a3395464127f2a62f98ad731..93684df7d74ab897ec790a0fa76b6596c1014486 100644 (file)
@@ -1,8 +1,7 @@
+using Content.Server.Ghost.Roles.Components;
 using Content.Server.Mind.Components;
 using Content.Server.Speech.Components;
-
 using Content.Shared.Chemistry.Reagent;
-using Content.Server.Ghost.Roles.Components;
 
 namespace Content.Server.Chemistry.ReagentEffects;
 
@@ -26,16 +25,18 @@ public sealed class MakeSentient : ReagentEffect
         }
 
         // No idea what anything past this point does
-        if (entityManager.TryGetComponent(uid, out GhostTakeoverAvailableComponent? takeOver))
+        if (entityManager.TryGetComponent(uid, out GhostRoleComponent? ghostRole) ||
+            entityManager.TryGetComponent(uid, out GhostTakeoverAvailableComponent? takeOver))
         {
             return;
         }
 
-        takeOver = entityManager.AddComponent<GhostTakeoverAvailableComponent>(uid);
+        ghostRole = entityManager.AddComponent<GhostRoleComponent>(uid);
+        entityManager.AddComponent<GhostTakeoverAvailableComponent>(uid);
 
         var entityData = entityManager.GetComponent<MetaDataComponent>(uid);
-        takeOver.RoleName = entityData.EntityName;
-        takeOver.RoleDescription = Loc.GetString("ghost-role-information-cognizine-description");
+        ghostRole.RoleName = entityData.EntityName;
+        ghostRole.RoleDescription = Loc.GetString("ghost-role-information-cognizine-description");
     }
 }
 
index bf2c7946db7d69a9b6e918c4afbb5388b9bb0eff..18dd27b859c69c6dca5c08461cd4b4c74be07aba 100644 (file)
@@ -18,6 +18,7 @@ using Content.Server.Station.Components;
 using Content.Server.Station.Systems;
 using Content.Server.Traitor;
 using Content.Shared.Dataset;
+using Content.Shared.Humanoid.Prototypes;
 using Content.Shared.Mobs;
 using Content.Shared.Mobs.Components;
 using Content.Shared.Nuke;
@@ -31,7 +32,6 @@ using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Random;
 using Robust.Shared.Utility;
-using Content.Shared.Humanoid.Prototypes;
 
 namespace Content.Server.GameTicking.Rules;
 
@@ -770,9 +770,10 @@ public sealed class NukeopsRuleSystem : GameRuleSystem
             else if (addSpawnPoints)
             {
                 var spawnPoint = EntityManager.SpawnEntity(_nukeopsRuleConfig.GhostSpawnPointProto, _random.Pick(spawns));
-                var spawner = EnsureComp<GhostRoleMobSpawnerComponent>(spawnPoint);
-                spawner.RoleName = Loc.GetString(nukeOpsAntag.Name);
-                spawner.RoleDescription = Loc.GetString(nukeOpsAntag.Objective);
+                var ghostRole = EnsureComp<GhostRoleComponent>(spawnPoint);
+                EnsureComp<GhostRoleMobSpawnerComponent>(spawnPoint);
+                ghostRole.RoleName = Loc.GetString(nukeOpsAntag.Name);
+                ghostRole.RoleDescription = Loc.GetString(nukeOpsAntag.Objective);
 
                 var nukeOpSpawner = EnsureComp<NukeOperativeSpawnerComponent>(spawnPoint);
                 nukeOpSpawner.OperativeName = spawnDetails.Name;
index 558366e786c4f9fa15bdd838c463aa70e9901a38..0b5a90ff5079d4f90385f23b23c5774694539b23 100644 (file)
@@ -1,10 +1,10 @@
 using Content.Server.Mind.Commands;
-using Robust.Server.Player;
 
 namespace Content.Server.Ghost.Roles.Components
 {
+    [RegisterComponent]
     [Access(typeof(GhostRoleSystem))]
-    public abstract class GhostRoleComponent : Component
+    public sealed class GhostRoleComponent : Component
     {
         [DataField("name")] public string _roleName = "Unknown";
 
@@ -16,7 +16,7 @@ namespace Content.Server.Ghost.Roles.Components
         /// Whether the <see cref="MakeSentientCommand"/> should run on the mob.
         /// </summary>
         [ViewVariables(VVAccess.ReadWrite)] [DataField("makeSentient")]
-        protected bool MakeSentient = true;
+        public bool MakeSentient = true;
 
         /// <summary>
         ///     The probability that this ghost role will be available after init.
@@ -83,7 +83,5 @@ namespace Content.Server.Ghost.Roles.Components
         [ViewVariables(VVAccess.ReadWrite)]
         [DataField("reregister")]
         public bool ReregisterOnGhost { get; set; } = true;
-
-        public abstract bool Take(IPlayerSession session);
     }
 }
index 720bd5387e43b8241ed66c9678add0ccfe8a6d27..39e22d96c12e94fe34d425c605a6606abd700dcb 100644 (file)
@@ -1,67 +1,26 @@
-using Content.Server.Mind.Commands;
-using Content.Server.Mind.Components;
-using JetBrains.Annotations;
-using Robust.Server.Player;
-using Robust.Shared.Prototypes;
+using Robust.Shared.Prototypes;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-using Content.Server.Ghost.Roles.Events;
 
 namespace Content.Server.Ghost.Roles.Components
 {
     /// <summary>
     ///     Allows a ghost to take this role, spawning a new entity.
     /// </summary>
-    [RegisterComponent, ComponentReference(typeof(GhostRoleComponent))]
-    public sealed class GhostRoleMobSpawnerComponent : GhostRoleComponent
+    [RegisterComponent]
+    [Access(typeof(GhostRoleSystem))]
+    public sealed class GhostRoleMobSpawnerComponent : Component
     {
-        [Dependency] private readonly IEntityManager _entMan = default!;
-
         [ViewVariables(VVAccess.ReadWrite)] [DataField("deleteOnSpawn")]
-        private bool _deleteOnSpawn = true;
+        public bool DeleteOnSpawn = true;
 
         [ViewVariables(VVAccess.ReadWrite)] [DataField("availableTakeovers")]
-        private int _availableTakeovers = 1;
+        public int AvailableTakeovers = 1;
 
         [ViewVariables]
-        private int _currentTakeovers = 0;
+        public int CurrentTakeovers = 0;
 
-        [CanBeNull]
         [ViewVariables(VVAccess.ReadWrite)]
         [DataField("prototype", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
         public string? Prototype { get; private set; }
-
-        public override bool Take(IPlayerSession session)
-        {
-            if (Taken)
-                return false;
-
-            if (string.IsNullOrEmpty(Prototype))
-                throw new NullReferenceException("Prototype string cannot be null or empty!");
-
-            var mob = _entMan.SpawnEntity(Prototype, _entMan.GetComponent<TransformComponent>(Owner).Coordinates);
-            var xform = _entMan.GetComponent<TransformComponent>(mob);
-            xform.AttachToGridOrMap();
-
-            var spawnedEvent = new GhostRoleSpawnerUsedEvent(Owner, mob);
-            _entMan.EventBus.RaiseLocalEvent(mob, spawnedEvent, false);
-
-            if (MakeSentient)
-                MakeSentientCommand.MakeSentient(mob, _entMan, AllowMovement, AllowSpeech);
-
-            mob.EnsureComponent<MindComponent>();
-
-            var ghostRoleSystem = EntitySystem.Get<GhostRoleSystem>();
-            ghostRoleSystem.GhostRoleInternalCreateMindAndTransfer(session, Owner, mob, this);
-
-            if (++_currentTakeovers < _availableTakeovers)
-                return true;
-
-            Taken = true;
-
-            if (_deleteOnSpawn)
-                _entMan.QueueDeleteEntity(Owner);
-
-            return true;
-        }
     }
 }
index 0a9a9435f957d2834dfc1966d05d17d004e54713..44c09f3a67320bc29631da13ea0b581a819be120 100644 (file)
@@ -1,36 +1,11 @@
-using Content.Server.Mind.Commands;
-using Content.Server.Mind.Components;
-using Robust.Server.Player;
-
 namespace Content.Server.Ghost.Roles.Components
 {
     /// <summary>
     ///     Allows a ghost to take over the Owner entity.
     /// </summary>
-    [RegisterComponent, ComponentReference(typeof(GhostRoleComponent))]
-    public sealed class GhostTakeoverAvailableComponent : GhostRoleComponent
+    [RegisterComponent]
+    [Access(typeof(GhostRoleSystem))]
+    public sealed class GhostTakeoverAvailableComponent : Component
     {
-        public override bool Take(IPlayerSession session)
-        {
-            if (Taken)
-                return false;
-
-            Taken = true;
-
-            var mind = Owner.EnsureComponent<MindComponent>();
-
-            if (mind.HasMind)
-                return false;
-
-            if (MakeSentient)
-                MakeSentientCommand.MakeSentient(Owner, IoCManager.Resolve<IEntityManager>(), AllowMovement, AllowSpeech);
-
-            var ghostRoleSystem = EntitySystem.Get<GhostRoleSystem>();
-            ghostRoleSystem.GhostRoleInternalCreateMindAndTransfer(session, Owner, Owner, this);
-
-            ghostRoleSystem.UnregisterGhostRole(this);
-
-            return true;
-        }
     }
 }
diff --git a/Content.Server/Ghost/Roles/Components/TakeGhostRoleEvent.cs b/Content.Server/Ghost/Roles/Components/TakeGhostRoleEvent.cs
new file mode 100644 (file)
index 0000000..a76ea95
--- /dev/null
@@ -0,0 +1,9 @@
+using Robust.Server.Player;
+
+namespace Content.Server.Ghost.Roles.Components;
+
+[ByRefEvent]
+public record struct TakeGhostRoleEvent(IPlayerSession Player)
+{
+    public bool TookRole { get; set; }
+}
index 00f53c27fa0ca4b0c0b7e4938d4c2fb9c47e9f5a..6cbb51b0094d7687af8fd06be4a7e7536111b2fb 100644 (file)
@@ -2,7 +2,9 @@ using Content.Server.Administration.Logs;
 using Content.Server.EUI;
 using Content.Server.Ghost.Components;
 using Content.Server.Ghost.Roles.Components;
+using Content.Server.Ghost.Roles.Events;
 using Content.Server.Ghost.Roles.UI;
+using Content.Server.Mind.Commands;
 using Content.Server.Mind.Components;
 using Content.Server.Players;
 using Content.Shared.Administration;
@@ -30,6 +32,7 @@ namespace Content.Server.Ghost.Roles
         [Dependency] private readonly IAdminLogManager _adminLogger = default!;
         [Dependency] private readonly IRobustRandom _random = default!;
         [Dependency] private readonly FollowerSystem _followerSystem = default!;
+        [Dependency] private readonly TransformSystem _transform = default!;
 
         private uint _nextRoleIdentifier;
         private bool _needsUpdateGhostRoleCount = true;
@@ -51,22 +54,27 @@ namespace Content.Server.Ghost.Roles
             SubscribeLocalEvent<GhostTakeoverAvailableComponent, MobStateChangedEvent>(OnMobStateChanged);
             SubscribeLocalEvent<GhostRoleComponent, ComponentInit>(OnInit);
             SubscribeLocalEvent<GhostRoleComponent, ComponentShutdown>(OnShutdown);
+            SubscribeLocalEvent<GhostRoleMobSpawnerComponent, TakeGhostRoleEvent>(OnSpawnerTakeRole);
+            SubscribeLocalEvent<GhostTakeoverAvailableComponent, TakeGhostRoleEvent>(OnTakeoverTakeRole);
             _playerManager.PlayerStatusChanged += PlayerStatusChanged;
         }
 
-        private void OnMobStateChanged(EntityUid uid, GhostRoleComponent component, MobStateChangedEvent args)
+        private void OnMobStateChanged(EntityUid uid, GhostTakeoverAvailableComponent component, MobStateChangedEvent args)
         {
+            if (!TryComp(uid, out GhostRoleComponent? ghostRole))
+                return;
+
             switch (args.NewMobState)
             {
                 case MobState.Alive:
                 {
-                    if (!component.Taken)
-                        RegisterGhostRole(component);
+                    if (!ghostRole.Taken)
+                        RegisterGhostRole(ghostRole);
                     break;
                 }
                 case MobState.Critical:
                 case MobState.Dead:
-                    UnregisterGhostRole(component);
+                    UnregisterGhostRole(ghostRole);
                     break;
             }
         }
@@ -180,7 +188,11 @@ namespace Content.Server.Ghost.Roles
         public void Takeover(IPlayerSession player, uint identifier)
         {
             if (!_ghostRoles.TryGetValue(identifier, out var role)) return;
-            if (!role.Take(player)) return;
+
+            var ev = new TakeGhostRoleEvent(player);
+            RaiseLocalEvent(role.Owner, ref ev);
+
+            if (!ev.TookRole) return;
 
             if (player.AttachedEntity != null)
                 _adminLogger.Add(LogType.GhostRoleTaken, LogImpact.Low, $"{player:player} took the {role.RoleName:roleName} ghost role {ToPrettyString(player.AttachedEntity.Value):entity}");
@@ -239,18 +251,24 @@ namespace Content.Server.Ghost.Roles
 
         private void OnMindAdded(EntityUid uid, GhostTakeoverAvailableComponent component, MindAddedMessage args)
         {
-            component.Taken = true;
-            UnregisterGhostRole(component);
+            if (!TryComp(uid, out GhostRoleComponent? ghostRole))
+                return;
+
+            ghostRole.Taken = true;
+            UnregisterGhostRole(ghostRole);
         }
 
-        private void OnMindRemoved(EntityUid uid, GhostRoleComponent component, MindRemovedMessage args)
+        private void OnMindRemoved(EntityUid uid, GhostTakeoverAvailableComponent component, MindRemovedMessage args)
         {
+            if (!TryComp(uid, out GhostRoleComponent? ghostRole))
+                return;
+
             // Avoid re-registering it for duplicate entries and potential exceptions.
-            if (!component.ReregisterOnGhost || component.LifeStage > ComponentLifeStage.Running)
+            if (!ghostRole.ReregisterOnGhost || component.LifeStage > ComponentLifeStage.Running)
                 return;
 
-            component.Taken = false;
-            RegisterGhostRole(component);
+            ghostRole.Taken = false;
+            RegisterGhostRole(ghostRole);
         }
 
         public void Reset(RoundRestartCleanupEvent ev)
@@ -282,6 +300,73 @@ namespace Content.Server.Ghost.Roles
         {
             UnregisterGhostRole(role);
         }
+
+        private void OnSpawnerTakeRole(EntityUid uid, GhostRoleMobSpawnerComponent component, ref TakeGhostRoleEvent args)
+        {
+            if (!TryComp(uid, out GhostRoleComponent? ghostRole) ||
+                ghostRole.Taken)
+            {
+                args.TookRole = false;
+                return;
+            }
+
+            if (string.IsNullOrEmpty(component.Prototype))
+                throw new NullReferenceException("Prototype string cannot be null or empty!");
+
+            var mob = Spawn(component.Prototype, Transform(uid).Coordinates);
+            _transform.AttachToGridOrMap(mob);
+
+            var spawnedEvent = new GhostRoleSpawnerUsedEvent(uid, mob);
+            RaiseLocalEvent(mob, spawnedEvent);
+
+            if (ghostRole.MakeSentient)
+                MakeSentientCommand.MakeSentient(mob, EntityManager, ghostRole.AllowMovement, ghostRole.AllowSpeech);
+
+            mob.EnsureComponent<MindComponent>();
+
+            GhostRoleInternalCreateMindAndTransfer(args.Player, uid, mob, ghostRole);
+
+            if (++component.CurrentTakeovers < component.AvailableTakeovers)
+            {
+                args.TookRole = true;
+                return;
+            }
+
+            ghostRole.Taken = true;
+
+            if (component.DeleteOnSpawn)
+                QueueDel(uid);
+
+            args.TookRole = true;
+        }
+
+        private void OnTakeoverTakeRole(EntityUid uid, GhostTakeoverAvailableComponent component, ref TakeGhostRoleEvent args)
+        {
+            if (!TryComp(uid, out GhostRoleComponent? ghostRole) ||
+                ghostRole.Taken)
+            {
+                args.TookRole = false;
+                return;
+            }
+
+            ghostRole.Taken = true;
+
+            var mind = EnsureComp<MindComponent>(uid);
+
+            if (mind.HasMind)
+            {
+                args.TookRole = false;
+                return;
+            }
+
+            if (ghostRole.MakeSentient)
+                MakeSentientCommand.MakeSentient(uid, EntityManager, ghostRole.AllowMovement, ghostRole.AllowSpeech);
+
+            GhostRoleInternalCreateMindAndTransfer(args.Player, uid, uid, ghostRole);
+            UnregisterGhostRole(ghostRole);
+
+            args.TookRole = true;
+        }
     }
 
     [AnyCommand]
index 128afc20bcfb87beee58123c5c4d1fb6e1a91097..e6ed1315505057cb320e5fcbd896458051e76116 100644 (file)
@@ -46,16 +46,23 @@ namespace Content.Server.Ghost.Roles
             var description = args[2];
             var rules = args.Length >= 4 ? args[3] : Loc.GetString("ghost-role-component-default-rules");
 
+            if (entityManager.TryGetComponent(uid, out GhostRoleComponent? ghostRole))
+            {
+                shell.WriteLine($"Entity {metaData.EntityName} with id {uid} already has a {nameof(GhostRoleComponent)}");
+                return;
+            }
+
             if (entityManager.TryGetComponent(uid, out GhostTakeoverAvailableComponent? takeOver))
             {
                 shell.WriteLine($"Entity {metaData.EntityName} with id {uid} already has a {nameof(GhostTakeoverAvailableComponent)}");
                 return;
             }
 
-            takeOver = entityManager.AddComponent<GhostTakeoverAvailableComponent>(uid);
-            takeOver.RoleName = name;
-            takeOver.RoleDescription = description;
-            takeOver.RoleRules = rules;
+            ghostRole = entityManager.AddComponent<GhostRoleComponent>(uid);
+            entityManager.AddComponent<GhostTakeoverAvailableComponent>(uid);
+            ghostRole.RoleName = name;
+            ghostRole.RoleDescription = description;
+            ghostRole.RoleRules = rules;
 
             shell.WriteLine($"Made entity {metaData.EntityName} a ghost role.");
         }
index 2939ae58c0a0abef435ac2a2cc672e1d621370c0..5f89c45213f58fa2432576c82d83260ec7f9177b 100644 (file)
@@ -1,14 +1,13 @@
-using Content.Shared.Examine;
-using Content.Shared.PAI;
-using Content.Shared.Verbs;
-using Content.Server.Popups;
-using Content.Server.Instruments;
 using Content.Server.Ghost.Roles.Components;
+using Content.Server.Instruments;
 using Content.Server.Mind.Components;
-using Robust.Server.GameObjects;
-using Robust.Shared.Player;
+using Content.Server.Popups;
+using Content.Shared.Examine;
 using Content.Shared.Interaction.Events;
+using Content.Shared.PAI;
 using Content.Shared.Popups;
+using Content.Shared.Verbs;
+using Robust.Server.GameObjects;
 
 namespace Content.Server.PAI
 {
@@ -79,10 +78,11 @@ namespace Content.Server.PAI
 
             EntityManager.GetComponent<MetaDataComponent>(component.Owner).EntityName = val;
 
-            var ghostFinder = EntityManager.EnsureComponent<GhostTakeoverAvailableComponent>(uid);
+            var ghostRole = AddComp<GhostRoleComponent>(uid);
+            EnsureComp<GhostTakeoverAvailableComponent>(uid);
 
-            ghostFinder.RoleName = Loc.GetString("pai-system-role-name");
-            ghostFinder.RoleDescription = Loc.GetString("pai-system-role-description");
+            ghostRole.RoleName = Loc.GetString("pai-system-role-name");
+            ghostRole.RoleDescription = Loc.GetString("pai-system-role-description");
 
             _popupSystem.PopupEntity(Loc.GetString("pai-system-searching"), uid, args.User);
             UpdatePAIAppearance(uid, PAIStatus.Searching);
index 6f0cbf586b7643bedbf5568b62bf1ba832194080..8181e11c0d7270d45f0b7cd7d4db992a75b90988 100644 (file)
@@ -1,11 +1,8 @@
 using System.Linq;
-using Content.Server.Chat;
 using Content.Server.Chat.Systems;
 using Content.Server.Ghost.Roles.Components;
-using Content.Server.Mind.Commands;
 using Content.Server.Station.Systems;
 using Content.Server.StationEvents.Components;
-using Robust.Shared.Random;
 
 namespace Content.Server.StationEvents.Events;
 
@@ -31,9 +28,10 @@ public sealed class RandomSentience : StationEventSystem
                 break;
 
             EntityManager.RemoveComponent<SentienceTargetComponent>(target.Owner);
-            var comp = EntityManager.AddComponent<GhostTakeoverAvailableComponent>(target.Owner);
-            comp.RoleName = EntityManager.GetComponent<MetaDataComponent>(target.Owner).EntityName;
-            comp.RoleDescription = Loc.GetString("station-event-random-sentience-role-description", ("name", comp.RoleName));
+            var ghostRole = AddComp<GhostRoleComponent>(target.Owner);
+            AddComp<GhostTakeoverAvailableComponent>(target.Owner);
+            ghostRole.RoleName = EntityManager.GetComponent<MetaDataComponent>(target.Owner).EntityName;
+            ghostRole.RoleDescription = Loc.GetString("station-event-random-sentience-role-description", ("name", ghostRole.RoleName));
             groups.Add(Loc.GetString(target.FlavorKind));
         }
 
index 97c6a251b432fe4f88dd529f432bea84285c1e7a..ef7203eba627bb5986074ca7a25695cb1d2f3527 100644 (file)
@@ -210,10 +210,11 @@ namespace Content.Server.Zombies
             if (!HasComp<GhostRoleMobSpawnerComponent>(target) && !mindcomp.HasMind) //this specific component gives build test trouble so pop off, ig
             {
                 //yet more hardcoding. Visit zombie.ftl for more information.
-                EntityManager.EnsureComponent<GhostTakeoverAvailableComponent>(target, out var ghostcomp);
-                ghostcomp.RoleName = Loc.GetString("zombie-generic");
-                ghostcomp.RoleDescription = Loc.GetString("zombie-role-desc");
-                ghostcomp.RoleRules = Loc.GetString("zombie-role-rules");
+                var ghostRole = EnsureComp<GhostRoleComponent>(target);
+                EnsureComp<GhostTakeoverAvailableComponent>(target);
+                ghostRole.RoleName = Loc.GetString("zombie-generic");
+                ghostRole.RoleDescription = Loc.GetString("zombie-role-desc");
+                ghostRole.RoleRules = Loc.GetString("zombie-role-rules");
             }
 
             //Goes through every hand, drops the items in it, then removes the hand
index 6c0f6ffaaab8b9fc1c4f6d3f1736b2a0bec671f0..bfe3d7219f92d472301a067f7086b468b6ce7854 100644 (file)
@@ -4,7 +4,7 @@
   suffix: rat king
   parent: MarkerBase
   components:
-  - type: GhostRoleMobSpawner
+  - type: GhostRole
     prototype: MobRatKing
     name: Rat King
     description: You are the Rat King, scavenge food in order to produce rat minions to do your bidding.
   suffix: Remilia
   parent: MarkerBase
   components:
-  - type: GhostRoleMobSpawner
-    prototype: MobBatRemilia
+  - type: GhostRole
     name: Remilia, the chaplain's familiar
     description: Obey your master. Eat fruit.
     rules: You are an intelligent fruit bat. Follow the chaplain around. Don't cause any trouble unless the chaplain tells you to.
+  - type: GhostRoleMobSpawner
+    prototype: MobBatRemilia
   - type: Sprite
     sprite: Markers/jobs.rsi
     layers:
   suffix: cerberus
   parent: MarkerBase
   components:
-  - type: GhostRoleMobSpawner
-    prototype: MobCorgiCerberus
+  - type: GhostRole
     name: Cerberus, Evil Familiar
     description: Obey your master. Spread chaos.
     rules: You are an intelligent, demonic dog. Try to help the chaplain and any of his flock. As an antagonist, you're otherwise unrestrained.
+  - type: GhostRoleMobSpawner
+    prototype: MobCorgiCerberus
   - type: Sprite
     sprite: Markers/jobs.rsi
     layers:
   suffix: nukeops
   parent: MarkerBase
   components:
+  - type: GhostRole
+    rules: You are a syndicate operative tasked with the destruction of the station. As an antagonist, do whatever is required to complete this task.
   - type: GhostRoleMobSpawner
     prototype: MobHumanNukeOp
-    rules: You are a syndicate operative tasked with the destruction of the station. As an antagonist, do whatever is required to complete this task.
   - type: NukeOperativeSpawner
   - type: Sprite
     sprite: Markers/jobs.rsi
@@ -69,5 +72,3 @@
       - state: green
       - sprite: Structures/Wallmounts/signs.rsi
         state: radiation
-
-
index afbb9f001d0b1cc698f8f8ddd0a8beeb72c5b189..c9c98b949ebe6c7e3579fe43c331df22f8d0845c 100644 (file)
   id: MobMouse
   description: Squeak!
   components:
-  - type: GhostTakeoverAvailable
+  - type: GhostRole
     makeSentient: true
     allowSpeech: true
     allowMovement: true
     name: ghost-role-information-mouse-name
     description: ghost-role-information-mouse-description
+  - type: GhostTakeoverAvailable
   - type: Speech
     speechSounds: Squeak
   - type: Sprite
     - type: MobMover
     - type: HTN
       rootTask: SimpleHostileCompound
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       makeSentient: true
       name: ghost-role-information-giant-spider-name
       description: ghost-role-information-giant-spider-description
+    - type: GhostTakeoverAvailable
 
 - type: entity
   name: possum
   id: MobHamster
   description: A cute, fluffy, robust hamster.
   components:
-  - type: GhostTakeoverAvailable
+  - type: GhostRole
     makeSentient: true
     allowSpeech: true
     allowMovement: true
     name: ghost-role-information-hamster-name
     description: ghost-role-information-hamster-description
+  - type: GhostTakeoverAvailable
   - type: Speech
     speechSounds: Squeak
   - type: Sprite
index 69a2a996706ef1c68787c2ebeb983f5e0dbf12ca..7af585c69a835372273fc259fc6a08de463914bc 100644 (file)
     radius: 1.2
     energy: 2
     color: "#4faffb"
-  - type: GhostTakeoverAvailable
+  - type: GhostRole
     prob: 0.25
     name: space bear
     description: |
       You're a bear! Do bear things.
+  - type: GhostTakeoverAvailable
 
 - type: entity
   id: MobBearSpaceSalvage
   parent: MobBearSpace
   suffix: "Salvage Ruleset"
   components:
-  - type: GhostTakeoverAvailable
+  - type: GhostRole
     prob: 0.25
     name: space bear on salvage wreck
     description: |
       Defend the loot inside the salvage wreck!
+  - type: GhostTakeoverAvailable
   - type: SalvageMobRestrictions
index 66087f4d7b6827a03aee56037fb4ba6c631ef053..768688bec9db7f70a38fa811f6c33c4936c1d37d 100644 (file)
   parent: MobCarp
   suffix: "Salvage Ruleset"
   components:
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       prob: 0.33
       name: space carp on salvage wreck
       allowMovement: true
       allowSpeech: true
       description: |
         Defend the loot inside the salvage wreck!
+    - type: GhostTakeoverAvailable
     - type: SalvageMobRestrictions
 
 - type: entity
   suffix: DragonBrood
   parent: BaseMobCarp
   components:
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       allowMovement: true
       allowSpeech: true
       makeSentient: true
       name: Sentient Carp
       description: Help the dragon flood the station with carps!
+    - type: GhostTakeoverAvailable
     - type: HTN
       rootTask: DragonCarpCompound
index 4e6964808856e627586a0f8061a761db16ba1aa2..4a83c24fd0acec47c1ff28edf9b06025989f348e 100644 (file)
   id: MobHamsterHamlet
   description: A grumpy, cute and fluffy hamster.
   components:
-  - type: GhostTakeoverAvailable
+  - type: GhostRole
     makeSentient: true
     allowSpeech: true
     allowMovement: true
     name: ghost-role-information-hamlet-name
     description: ghost-role-information-hamlet-description
+  - type: GhostTakeoverAvailable
   - type: InteractionPopup
     successChance: 1
   - type: Butcherable
index 6ddc4ed21ccab603f256d27d55309dcc31ba57e7..c1c675f86a8d455ca410ce3dfd6877bce5efa1ae 100644 (file)
         Base: dead
       Dead:
         Base: dead
-  - type: GhostTakeoverAvailable
+  - type: GhostRole
     makeSentient: true
     name: Rat King
     description: You are the Rat King, scavenge food in order to produce rat minions to do your bidding.
     rules: You are an antagonist, scavenge, attack, and grow your hoard!
+  - type: GhostTakeoverAvailable
   - type: Tag
     tags:
       - CannotSuicide
       Female: Mouse
       Unsexed: Mouse
     wilhelmProbability: 0.001
-  - type: GhostTakeoverAvailable
+  - type: GhostRole
     makeSentient: true
     name: Rat Servant
     description: You are a Rat Servant. You must follow your king's orders.
     rules: You are an antagonist, scavenge, attack, and serve your king!
+  - type: GhostTakeoverAvailable
   - type: Tag
     tags:
       - CannotSuicide
index ace5fd81ec352436c35cd98d8891ebc75ac4a759..46b308656617f5ec2430a253aaa8e482867ca549 100644 (file)
   - type: Alerts
   - type: NameIdentifier
     group: GenericNumber
-  - type: GhostTakeoverAvailable
+  - type: GhostRole
     makeSentient: true
     name: Revenant
     description: You are a Revenant. Use your powers to harvest souls and unleash chaos upon the crew. Unlock new abilities with the essence you harvest.
     rules: You are an antagonist, harvest, defile, and drive the crew insane.
+  - type: GhostTakeoverAvailable
   - type: Revenant
   - type: PointLight
     color: MediumPurple
index 5dcc5ec98ab57bff8bdeb007db7d6aa0dfb4dec9..9b698ef25f49352b48165f2b7d3d55a424a6bddc 100644 (file)
   - type: Construction
     graph: HonkBot
     node: bot
-  - type: GhostTakeoverAvailable
+  - type: GhostRole
     makeSentient: true
     name: honkbot
     description: An artificial being of pure evil.
+  - type: GhostTakeoverAvailable
   - type: InteractionPopup
     interactSuccessString: petting-success-honkbot
     interactFailureString: petting-failure-honkbot
index b5029cedb8903532212d52ea9991d450bc2aa322..a4d5ab72575bd3d190c9fb9e4f9d6914ca71db0b 100644 (file)
           Quantity: 5
   - type: MeleeChemicalInjector
     solution: melee
-  - type: GhostTakeoverAvailable
+  - type: GhostRole
     prob: 0.33
     makeSentient: true
     name: space tick
     description: |
       Wreak havoc on the station!
+  - type: GhostTakeoverAvailable
   - type: ReplacementAccent
     accent: genericAggressive
 
   parent: MobTick
   suffix: "Salvage Ruleset"
   components:
-  - type: GhostTakeoverAvailable
+  - type: GhostRole
     name: space tick on salvage wreck
     description: |
       Defend the loot inside the salvage wreck!
+  - type: GhostTakeoverAvailable
   - type: SalvageMobRestrictions
 
index e8a22f7045eb5d387ea520c47a421f89794ab2e9..9203155c6c285b42597994454fbd4cfa2877b2ad 100644 (file)
     spawned:
     - id: FoodMeatXeno
       amount: 5
-  - type: GhostTakeoverAvailable
+  - type: GhostRole
     allowMovement: true
     allowSpeech: true
     makeSentient: true
     name: xeno
     description: You are a xeno, co-operate with your hive to kill all crewmembers!
     rules: You are an antagonist, smack, slash, and wack!
+  - type: GhostTakeoverAvailable
   - type: TypingIndicator
     proto: alien
   - type: Temperature
index 67524ec40f9e3273489d97bbe70818049eb797df..fc96a5f66f5d4bc41112e49ff825c0844300f5f6 100644 (file)
@@ -5,12 +5,13 @@
   suffix: ""
   description: A flying leviathan, loosely related to space carps.
   components:
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       allowMovement: true
       allowSpeech: true
       makeSentient: true
       name: Space dragon
       description: Call in 3 carp rifts and take over this quadrant! You have only 5 minutes in between each rift before you will disappear.
+    - type: GhostTakeoverAvailable
     - type: HTN
       rootTask: XenoCompound
     - type: Faction
index d8477281ed2eea99b96fd2aad4a58d8102931f4f..d4e7c5f47c08819b1e2ab4c45a16da7de572a637 100644 (file)
@@ -4,13 +4,14 @@
   id: MobBatRemilia
   description: The chaplain's familiar. Likes fruit.
   components:
-  - type: GhostTakeoverAvailable
+  - type: GhostRole
     makeSentient: true
     allowMovement: true
     allowSpeech: true
     name: Remilia, the chaplain's familiar
     description: Obey your master. Eat fruit.
     rules: You are an intelligent fruit bat. Follow the chaplain around. Don't cause any trouble unless the chaplain tells you to.
+  - type: GhostTakeoverAvailable
   - type: Grammar
     attributes:
       gender: female
   id: MobCorgiCerberus
   description: This pupper is not wholesome.
   components:
-  - type: GhostTakeoverAvailable
+  - type: GhostRole
     makeSentient: true
     allowMovement: true
     allowSpeech: true
     name: Cerberus, Evil Familiar
     description: Obey your master. Spread chaos.
     rules: You are an intelligent, demonic dog. Try to help the chaplain and any of his flock. As an antagonist, you're otherwise unrestrained.
+  - type: GhostTakeoverAvailable
   - type: MeleeWeapon
     hidden: true
     angle: 0
index 34303467f4686000a26382bf764438759b3bbab9..7d2da5ba66a3b6dd6d6183cf6bffa8544135939c 100644 (file)
@@ -7,12 +7,13 @@
   save: false
   components:
     - type: LagCompensation
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       allowMovement: true
       allowSpeech: true
       makeSentient: true
       name: Guardian
       description: Listen to your owner. Don't tank damage. Punch people hard.
+    - type: GhostTakeoverAvailable
     - type: Input
       context: "human"
     - type: MobMover
   parent: MobGuardianBase
   description: A mesmerising whirl of hard-light patterns weaves a marvelous, yet oddly familiar visage. It stands proud, tuning into its owner's life to sustain itself.
   components:
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       allowMovement: true
       allowSpeech: true
       makeSentient: true
       name: Holoparasite
       description: Listen to your owner. Don't tank damage. Punch people hard.
+    - type: GhostTakeoverAvailable
     - type: NameIdentifier
       group: Holoparasite
     - type: TypingIndicator
   id: MobIfritGuardian
   description: A corrupted jinn, ripped from fitra to serve the wizard's petty needs. It stands wicked, tuning into it's owner's life to sustain itself.
   components:
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       allowMovement: true
       allowSpeech: true
       makeSentient: true
       name: Ifrit
       description: Listen to your owner. Don't tank damage. Punch people hard.
+    - type: GhostTakeoverAvailable
     - type: RandomSprite
       available:
         - enum.DamageStateVisualLayers.BaseUnshaded:
index f985e4dfbc0ba42d77081ee540141044ccebb2bd..a24a69d111a0c0b689df7c50da02a3c3aba120a6 100644 (file)
   id: ERTLeader
   randomizeName: false
   components:
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       name: ERT Leader
       description: Lead a team of specialists to resolve the stations issues.
+    - type: GhostTakeoverAvailable
     - type: Loadout
       prototypes: [ ERTLeaderGear ]
     - type: RandomMetadata
   id: ERTLeaderEVA
   parent: ERTLeader
   components:
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       name: ERT Leader
       description: Lead a team of specialists to resolve the stations issues.
+    - type: GhostTakeoverAvailable
     - type: Loadout
       prototypes: [ ERTLeaderGearEVA ]
 
   id: ERTJanitor
   parent: ERTLeader
   components:
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       name: ERT Janitor
       description: Assist with custodial efforts to resolve the stations issues.
+    - type: GhostTakeoverAvailable
     - type: RandomMetadata
       nameSegments:
       - NamesFirstMilitary
   id: ERTJanitorEVA
   parent: ERTJanitor
   components:
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       name: ERT Janitor
       description: Assist with custodial efforts to resolve the stations issues.
+    - type: GhostTakeoverAvailable
     - type: Loadout
       prototypes: [ ERTJanitorGearEVA ]
 
   id: ERTEngineer
   parent: ERTLeader
   components:
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       name: ERT Engineer
       description: Assist with engineering efforts to resolve the stations issues.
+    - type: GhostTakeoverAvailable
     - type: RandomMetadata
       nameSegments:
       - NamesFirstMilitary
   id: ERTEngineerEVA
   parent: ERTEngineer
   components:
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       name: ERT Engineer
       description: Assist with engineering efforts to resolve the stations issues.
+    - type: GhostTakeoverAvailable
     - type: Loadout
       prototypes: [ ERTEngineerGearEVA ]
 
   id: ERTSecurity
   parent: ERTLeader
   components:
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       name: ERT Security
       description: Assist with security efforts to resolve the stations issues.
+    - type: GhostTakeoverAvailable
     - type: RandomMetadata
       nameSegments:
       - NamesFirstMilitary
   id: ERTSecurityEVA
   parent: ERTSecurity
   components:
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       name: ERT Security
       description: Assist with security efforts to resolve the stations issues.
+    - type: GhostTakeoverAvailable
     - type: Loadout
       prototypes: [ ERTSecurityGearEVA ]
 
   id: ERTMedical
   parent: ERTLeader
   components:
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       name: ERT Medical
       description: Assist with medicaling efforts to resolve the stations issues.
+    - type: GhostTakeoverAvailable
     - type: RandomMetadata
       nameSegments:
       - NamesFirstMilitary
   id: ERTMedicalEVA
   parent: ERTMedical
   components:
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       name: ERT Medical
       description: Assist with medicaling efforts to resolve the stations issues.
+    - type: GhostTakeoverAvailable
     - type: Loadout
       prototypes: [ ERTMedicalGearEVA ]
 
   components:
     - type: Loadout
       prototypes: [CBURNGear]
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       name: CBURN Agent
       description: A highly trained CentCom agent, capable of dealing with various threats.
+    - type: GhostTakeoverAvailable
     - type: RandomMetadata
       nameSegments:
       - NamesFirstMilitary
 - type: randomHumanoidSettings
   id: CentcomOfficial
   components:
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       name: CentCom official
       description: Inspect the station, jot down performance reviews for heads of staff, bug the Captain.
+    - type: GhostTakeoverAvailable
     - type: Loadout
       prototypes: [ CentcomGear ]
 
   id: Cluwne
   randomizeName: false
   components:
-    - type: GhostTakeoverAvailable
+    - type: GhostRole
       name: Cluwne
       description: Become a pitiful cluwne, your only goal in life is to find a sweet release from your suffering (usually by being beaten to death). A cluwne is not an antagonist but may defend itself. Crewmembers may murder cluwnes freely.
+    - type: GhostTakeoverAvailable
     - type: Cluwne
index c5e911ff0ae7351d02846753d0299a1b4bd54ccb..750be45c064e927853aa836e1656d49de8f32a8c 100644 (file)
@@ -84,7 +84,7 @@
     interfaces:
     - key: enum.StrippingUiKey.Key
       type: StrippableBoundUserInterface
-  #- type: GhostTakeoverAvailable
+  #- type: GhostRole
   #  makeSentient: true
   #  name: Maintenance Drone
   #  description: Maintain the station. Ignore other beings except drones.
@@ -93,6 +93,7 @@
   #   1. You may not involve yourself in the matters of another being, even if such matters conflict with Law Two or Law Three, unless the other being is another Drone.
   #   2. You may not harm any being, regardless of intent or circumstance.
   #   3. Your goals are to build, maintain, repair, improve, and power to the best of your abilities, You must never actively work against these goals.
+  #- type: GhostTakeoverAvailable
   - type: MovementSpeedModifier
     baseWalkSpeed : 5
     baseSprintSpeed : 5
     interfaces:
     - key: enum.StrippingUiKey.Key
       type: StrippableBoundUserInterface
-  - type: GhostTakeoverAvailable
+  - type: GhostRole
     makeSentient: true
     name: Onestar Mecha
     description: You are an experimental mecha created by who-knows-what, all you know is that you have weapons and you detect fleshy moving targets nearby...
     rules: Use your weapons to cause havoc. You are an antagonist.
+  - type: GhostTakeoverAvailable
   - type: MovementSpeedModifier
     baseWalkSpeed : 3
     baseSprintSpeed : 2
index 054e50b9219f500420a584f2261c0163a0f4c5ba..a9ef010abffd406c26dc1c49bdadf5b1c27a53ab 100644 (file)
   parent: MobSkeletonPerson
   id: MobSkeletonPirate
   components:
-  - type: GhostTakeoverAvailable
+  - type: GhostRole
     name: Skeleton Pirate
     description: Cause chaos and loot the station for treasure.
+  - type: GhostTakeoverAvailable
   - type: Loadout
     prototypes: [PirateGear]
   - type: RandomHumanoidAppearance
   parent: MobSkeletonPerson
   id: MobSkeletonBiker
   components:
-  - type: GhostTakeoverAvailable
+  - type: GhostRole
     name: Skeleton Biker
     description: Ride around on your sweet ride.
+  - type: GhostTakeoverAvailable
   - type: Loadout
     prototypes: [SkeletonBiker]
   - type: RandomHumanoidAppearance
index d9649cef516273a78407a7504b31f16dcd7cbb19..5d3084d353cb4bee20a1cd3ca9512bb944f2bbeb 100644 (file)
@@ -9,11 +9,12 @@
     layers:
     - state: old-radio
     netsync: false
-  - type: GhostRoleMobSpawner
-    prototype: MobHumanSyndicateAgent
+  - type: GhostRole
     name: Syndicate Agent
     description: Someone needs reinforcements. You, the first person the syndicate could find, will help them.
     rules: Normal syndicate antagonist rules apply. Work with whoever called you in, and don't harm them.
+  - type: GhostRoleMobSpawner
+    prototype: MobHumanSyndicateAgent
   - type: EmitSoundOnUse
     sound: /Audio/Misc/emergency_meeting.ogg
   - type: ItemCooldown
index af2d84a7b9ca48caceb51798df9ad864c0a94356..035c4334a5f567dc768ceb7eab99542c18aea09e 100644 (file)
   targetDepth: 3
   effectHint: artifact-effect-hint-sentience
   permanentComponents:
-  - type: GhostTakeoverAvailable
+  - type: GhostRole
     allowMovement: true
     allowSpeech: true
     makeSentient: true
     description: |
       Enact your eldritch whims.
       Forcibly activate your nodes for good or for evil.
+  - type: GhostTakeoverAvailable
   - type: MovementSpeedModifier
     baseWalkSpeed: 0.25
     baseSprintSpeed: 0.5