]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Clean up polymorphsystem (#14297)
authorNemanja <98561806+EmoGarbage404@users.noreply.github.com>
Mon, 6 Mar 2023 17:37:18 +0000 (12:37 -0500)
committerGitHub <noreply@github.com>
Mon, 6 Mar 2023 17:37:18 +0000 (09:37 -0800)
12 files changed:
Content.Server/Administration/Commands/AddPolymorphActionCommand.cs
Content.Server/Administration/Commands/PolymorphCommand.cs
Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs
Content.Server/Disease/Effects/DiseasePolymorph.cs
Content.Server/Inventory/ServerInventorySystem.cs
Content.Server/Polymorph/Components/PolymorphableComponent.cs
Content.Server/Polymorph/Components/PolymorphedEntityComponent.cs
Content.Server/Polymorph/Systems/PolymorphSystem.Collide.cs [moved from Content.Server/Polymorph/Systems/PolymorphableSystem.Collide.cs with 81% similarity]
Content.Server/Polymorph/Systems/PolymorphSystem.Map.cs [moved from Content.Server/Polymorph/Systems/PolymorphableSystem.Map.cs with 87% similarity]
Content.Server/Polymorph/Systems/PolymorphSystem.cs [new file with mode: 0644]
Content.Server/Polymorph/Systems/PolymorphableSystem.cs [deleted file]
Content.Server/Polymorph/Systems/PolymorphedEntitySystem.cs [deleted file]

index d40644499d2fc49f6923fd803f1ccf13634914bd..f2cc47b9085c02f6c05a2fb4a6891ee9651a7486 100644 (file)
@@ -29,7 +29,7 @@ public sealed class AddPolymorphActionCommand : IConsoleCommand
         }
 
         var entityManager = IoCManager.Resolve<IEntityManager>();
-        var polySystem = entityManager.EntitySysManager.GetEntitySystem<PolymorphableSystem>();
+        var polySystem = entityManager.EntitySysManager.GetEntitySystem<PolymorphSystem>();
 
         entityManager.EnsureComponent<PolymorphableComponent>(entityUid);
         polySystem.CreatePolymorphAction(args[1], entityUid);
index 3e79e854267ad915186690d817aaf4b29cc60d48..b4d2c9603a428a830f65dfbe282be938c95731f1 100644 (file)
@@ -39,7 +39,7 @@ public sealed class PolymorphCommand : IConsoleCommand
         }
 
         var entityManager = IoCManager.Resolve<IEntityManager>();
-        var polySystem = entityManager.EntitySysManager.GetEntitySystem<PolymorphableSystem>();
+        var polySystem = entityManager.EntitySysManager.GetEntitySystem<PolymorphSystem>();
 
         entityManager.EnsureComponent<PolymorphableComponent>(entityUid);
         polySystem.PolymorphEntity(entityUid, polyproto);
index f2f95109528b6b186045e3c632b3450e0be970c4..c734b2b89d2aac6ae8b2836bc319e6d463079799 100644 (file)
@@ -71,7 +71,7 @@ public sealed partial class AdminVerbSystem
     [Dependency] private readonly GodmodeSystem _godmodeSystem = default!;
     [Dependency] private readonly InventorySystem _inventorySystem = default!;
     [Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifierSystem = default!;
-    [Dependency] private readonly PolymorphableSystem _polymorphableSystem = default!;
+    [Dependency] private readonly PolymorphSystem _polymorphSystem = default!;
     [Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
     [Dependency] private readonly PopupSystem _popupSystem = default!;
     [Dependency] private readonly SharedPhysicsSystem _physics = default!;
@@ -171,7 +171,7 @@ public sealed partial class AdminVerbSystem
             Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Mobs/Animals/monkey.rsi"), "dead"),
             Act = () =>
             {
-                _polymorphableSystem.PolymorphEntity(args.Target, "AdminMonkeySmite");
+                _polymorphSystem.PolymorphEntity(args.Target, "AdminMonkeySmite");
             },
             Impact = LogImpact.Extreme,
             Message = Loc.GetString("admin-smite-monkeyify-description")
@@ -185,7 +185,7 @@ public sealed partial class AdminVerbSystem
             Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Structures/Piping/disposal.rsi"), "disposal"),
             Act = () =>
             {
-                _polymorphableSystem.PolymorphEntity(args.Target, "AdminDisposalsSmite");
+                _polymorphSystem.PolymorphEntity(args.Target, "AdminDisposalsSmite");
             },
             Impact = LogImpact.Extreme,
             Message = Loc.GetString("admin-smite-garbage-can-description")
@@ -487,7 +487,7 @@ public sealed partial class AdminVerbSystem
             Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Consumable/Food/Baked/bread.rsi"), "plain"),
             Act = () =>
             {
-                _polymorphableSystem.PolymorphEntity(args.Target, "AdminBreadSmite");
+                _polymorphSystem.PolymorphEntity(args.Target, "AdminBreadSmite");
             },
             Impact = LogImpact.Extreme,
             Message = Loc.GetString("admin-smite-become-bread-description")
@@ -501,7 +501,7 @@ public sealed partial class AdminVerbSystem
             Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Mobs/Animals/mouse.rsi"), "icon-0"),
             Act = () =>
             {
-                _polymorphableSystem.PolymorphEntity(args.Target, "AdminMouseSmite");
+                _polymorphSystem.PolymorphEntity(args.Target, "AdminMouseSmite");
             },
             Impact = LogImpact.Extreme,
             Message = Loc.GetString("admin-smite-become-mouse-description")
@@ -648,7 +648,7 @@ public sealed partial class AdminVerbSystem
             Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Fun/Instruments/h_synthesizer.rsi"), "icon"),
             Act = () =>
             {
-                _polymorphableSystem.PolymorphEntity(args.Target, "AdminInstrumentSmite");
+                _polymorphSystem.PolymorphEntity(args.Target, "AdminInstrumentSmite");
             },
             Impact = LogImpact.Extreme,
             Message = Loc.GetString("admin-smite-become-instrument-description"),
@@ -679,7 +679,7 @@ public sealed partial class AdminVerbSystem
             Icon = new SpriteSpecifier.Rsi(new ResourcePath("/Textures/Objects/Fun/toys.rsi"), "plushie_lizard"),
             Act = () =>
             {
-                _polymorphableSystem.PolymorphEntity(args.Target, "AdminLizardSmite");
+                _polymorphSystem.PolymorphEntity(args.Target, "AdminLizardSmite");
             },
             Impact = LogImpact.Extreme,
             Message = Loc.GetString("admin-smite-reptilian-species-swap-description"),
index 4eb91f7fc48258e192925f26ad697589da580bb9..f6136cc846de5d460c907ff1daeaf202e4315f33 100644 (file)
@@ -27,7 +27,7 @@ namespace Content.Server.Disease.Effects
 
         public override void Effect(DiseaseEffectArgs args)
         {
-            EntityUid? polyUid = EntitySystem.Get<PolymorphableSystem>().PolymorphEntity(args.DiseasedEntity, PolymorphId);
+            EntityUid? polyUid = EntitySystem.Get<PolymorphSystem>().PolymorphEntity(args.DiseasedEntity, PolymorphId);
 
             if (PolymorphSound != null && polyUid != null)
                 SoundSystem.Play(PolymorphSound.GetSound(), Filter.Pvs(polyUid.Value), polyUid.Value, AudioHelpers.WithVariation(0.2f));
index 50175c72c40e601fff1c569f2c99a6b3fa2270ac..a1cb007d91faf269bc26734c7d315da9e3572867 100644 (file)
@@ -1,12 +1,9 @@
 using Content.Server.Storage.Components;
 using Content.Server.Storage.EntitySystems;
-using Content.Server.Temperature.Systems;
 using Content.Shared.Clothing.Components;
 using Content.Shared.Interaction.Events;
 using Content.Shared.Inventory;
 using Content.Shared.Inventory.Events;
-using Robust.Shared.Containers;
-using InventoryComponent = Content.Shared.Inventory.InventoryComponent;
 
 namespace Content.Server.Inventory
 {
@@ -44,31 +41,31 @@ namespace Content.Server.Inventory
 
         public void TransferEntityInventories(EntityUid uid, EntityUid target)
         {
-            if (TryGetContainerSlotEnumerator(uid, out var enumerator))
+            if (!TryGetContainerSlotEnumerator(uid, out var enumerator))
+                return;
+
+            Dictionary<string, EntityUid> inventoryEntities = new();
+            var slots = GetSlots(uid);
+            while (enumerator.MoveNext(out var containerSlot))
             {
-                Dictionary<string, EntityUid?> inventoryEntities = new();
-                var slots = GetSlots(uid);
-                while (enumerator.MoveNext(out var containerSlot))
+                //records all the entities stored in each of the target's slots
+                foreach (var slot in slots)
                 {
-                    //records all the entities stored in each of the target's slots
-                    foreach (var slot in slots)
+                    if (TryGetSlotContainer(target, slot.Name, out var conslot, out _) &&
+                        conslot.ID == containerSlot.ID &&
+                        containerSlot.ContainedEntity is { } containedEntity)
                     {
-                        if (TryGetSlotContainer(target, slot.Name, out var conslot, out var _) &&
-                            conslot.ID == containerSlot.ID)
-                        {
-                            inventoryEntities.Add(slot.Name, containerSlot.ContainedEntity);
-                        }
+                        inventoryEntities.Add(slot.Name, containedEntity);
                     }
-                    //drops everything in the target's inventory on the ground
-                    containerSlot.EmptyContainer();
-                }
-                // This takes the objects we removed and stored earlier
-                // and actually equips all of it to the new entity
-                foreach (var item in inventoryEntities)
-                {
-                    if (item.Value != null)
-                        TryEquip(target, item.Value.Value, item.Key, true);
                 }
+                //drops everything in the target's inventory on the ground
+                TryUnequip(uid, containerSlot.ID, true, true);
+            }
+            // This takes the objects we removed and stored earlier
+            // and actually equips all of it to the new entity
+            foreach (var (slot, item) in inventoryEntities)
+            {
+                TryEquip(target, item, slot , true, true);
             }
         }
     }
index 882b9d453be983946910e58bb2f91c87bcd5fc63..80211d76801df89186a4e339a50617c5d16eb9d1 100644 (file)
@@ -17,6 +17,6 @@ namespace Content.Server.Polymorph.Components
         /// The polymorphs that the entity starts out being able to do.
         /// </summary>
         [DataField("innatePolymorphs", customTypeSerializer : typeof(PrototypeIdListSerializer<PolymorphPrototype>))]
-        public List<string>? InnatePolymorphs = null;
+        public List<string>? InnatePolymorphs;
     }
 }
index 2a5d489727ef475068daa1c4d8ff56d6908f5d2e..3263832e137740e5c852da85090a75ddb7c72565 100644 (file)
@@ -17,13 +17,13 @@ namespace Content.Server.Polymorph.Components
         /// The original entity that the player will revert back into
         /// </summary>
         [DataField("parent", required: true)]
-        public EntityUid Parent = new();
+        public EntityUid Parent;
 
         /// <summary>
         /// The amount of time that has passed since the entity was created
         /// used for tracking the duration
         /// </summary>
         [DataField("time")]
-        public float Time = 0;
+        public float Time;
     }
 }
similarity index 81%
rename from Content.Server/Polymorph/Systems/PolymorphableSystem.Collide.cs
rename to Content.Server/Polymorph/Systems/PolymorphSystem.Collide.cs
index 82cda8355c065770488065900851a1b3618e8b18..2fb28f39b969ad551ba3619b76b79c14c3beb59c 100644 (file)
@@ -1,22 +1,18 @@
 using Content.Server.Polymorph.Components;
 using Content.Shared.Projectiles;
 using Robust.Shared.Audio;
-using Robust.Shared.Physics.Dynamics;
 using Robust.Shared.Physics.Events;
-using Robust.Shared.Player;
 
 namespace Content.Server.Polymorph.Systems;
 
-public partial class PolymorphableSystem
+public partial class PolymorphSystem
 {
     // Need to do this so we don't get a collection enumeration error in physics by polymorphing
     // an entity we're colliding with
     private Queue<PolymorphQueuedData> _queuedPolymorphUpdates = new();
 
-    public override void Update(float frameTime)
+    public void UpdateCollide()
     {
-        base.Update(frameTime);
-
         while (_queuedPolymorphUpdates.TryDequeue(out var data))
         {
             if (Deleted(data.Ent))
@@ -25,8 +21,7 @@ public partial class PolymorphableSystem
             var ent = PolymorphEntity(data.Ent, data.Polymorph);
             if (ent != null)
             {
-                SoundSystem.Play(data.Sound.GetSound(), Filter.Pvs(ent.Value, entityManager: EntityManager),
-                    ent.Value, data.Sound.Params);
+                _audio.PlayPvs(data.Sound, ent.Value);
             }
         }
     }
@@ -50,7 +45,7 @@ public partial class PolymorphableSystem
     }
 }
 
-struct PolymorphQueuedData
+public struct PolymorphQueuedData
 {
     public EntityUid Ent;
     public SoundSpecifier Sound;
similarity index 87%
rename from Content.Server/Polymorph/Systems/PolymorphableSystem.Map.cs
rename to Content.Server/Polymorph/Systems/PolymorphSystem.Map.cs
index fee16f822e8baee123c135cb278a9cfafe0dd13a..dc71a2213643368762a3577e7febd823fa14238b 100644 (file)
@@ -2,9 +2,9 @@ using Content.Shared.GameTicking;
 
 namespace Content.Server.Polymorph.Systems
 {
-    public sealed partial class PolymorphableSystem : EntitySystem
+    public sealed partial class PolymorphSystem
     {
-        public EntityUid? PausedMap { get; private set; } = null;
+        public EntityUid? PausedMap { get; private set; }
 
         /// <summary>
         /// Used to subscribe to the round restart event
@@ -30,7 +30,7 @@ namespace Content.Server.Polymorph.Systems
         {
             if (PausedMap != null && Exists(PausedMap))
                 return;
-            
+
             var newmap = _mapManager.CreateMap();
             _mapManager.SetMapPaused(newmap, true);
             PausedMap = _mapManager.GetMapEntityId(newmap);
diff --git a/Content.Server/Polymorph/Systems/PolymorphSystem.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.cs
new file mode 100644 (file)
index 0000000..bc91d2e
--- /dev/null
@@ -0,0 +1,378 @@
+using Content.Server.Actions;
+using Content.Server.Buckle.Systems;
+using Content.Server.Humanoid;
+using Content.Server.Inventory;
+using Content.Server.Mind.Commands;
+using Content.Server.Mind.Components;
+using Content.Server.Polymorph.Components;
+using Content.Shared.Actions;
+using Content.Shared.Actions.ActionTypes;
+using Content.Shared.Damage;
+using Content.Shared.Hands.EntitySystems;
+using Content.Shared.IdentityManagement;
+using Content.Shared.Mobs.Components;
+using Content.Shared.Mobs.Systems;
+using Content.Shared.Polymorph;
+using Content.Shared.Popups;
+using JetBrains.Annotations;
+using Robust.Server.Containers;
+using Robust.Server.GameObjects;
+using Robust.Shared.Map;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
+
+namespace Content.Server.Polymorph.Systems
+{
+    public sealed partial class PolymorphSystem : EntitySystem
+    {
+        [Dependency] private readonly IComponentFactory _compFact = default!;
+        [Dependency] private readonly IMapManager _mapManager = default!;
+        [Dependency] private readonly IPrototypeManager _proto = default!;
+        [Dependency] private readonly ActionsSystem _actions = default!;
+        [Dependency] private readonly AudioSystem _audio = default!;
+        [Dependency] private readonly BuckleSystem _buckle = default!;
+        [Dependency] private readonly ContainerSystem _container = default!;
+        [Dependency] private readonly DamageableSystem _damageable = default!;
+        [Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
+        [Dependency] private readonly MobStateSystem _mobState = default!;
+        [Dependency] private readonly MobThresholdSystem _mobThreshold = default!;
+        [Dependency] private readonly ServerInventorySystem _inventory = default!;
+        [Dependency] private readonly SharedHandsSystem _hands = default!;
+        [Dependency] private readonly SharedPopupSystem _popup = default!;
+        [Dependency] private readonly TransformSystem _transform = default!;
+
+        private readonly ISawmill _saw = default!;
+
+        public override void Initialize()
+        {
+            base.Initialize();
+
+            SubscribeLocalEvent<PolymorphableComponent, ComponentStartup>(OnStartup);
+            SubscribeLocalEvent<PolymorphableComponent, PolymorphActionEvent>(OnPolymorphActionEvent);
+            SubscribeLocalEvent<PolymorphedEntityComponent, ComponentStartup>(OnStartup);
+            SubscribeLocalEvent<PolymorphedEntityComponent, RevertPolymorphActionEvent>(OnRevertPolymorphActionEvent);
+
+            InitializeCollide();
+            InitializeMap();
+        }
+
+        private void OnStartup(EntityUid uid, PolymorphableComponent component, ComponentStartup args)
+        {
+            if (component.InnatePolymorphs != null)
+            {
+                foreach (var morph in component.InnatePolymorphs)
+                {
+                    CreatePolymorphAction(morph, uid);
+                }
+            }
+        }
+
+        private void OnPolymorphActionEvent(EntityUid uid, PolymorphableComponent component, PolymorphActionEvent args)
+        {
+            PolymorphEntity(uid, args.Prototype);
+        }
+
+        private void OnRevertPolymorphActionEvent(EntityUid uid, PolymorphedEntityComponent component, RevertPolymorphActionEvent args)
+        {
+            Revert(uid, component);
+        }
+
+        public void OnStartup(EntityUid uid, PolymorphedEntityComponent component, ComponentStartup args)
+        {
+            if (!_proto.TryIndex(component.Prototype, out PolymorphPrototype? proto))
+            {
+                // warning instead of error because of the all-comps one entity test.
+                Logger.Warning($"{nameof(PolymorphSystem)} encountered an improperly set up polymorph component while initializing. Entity {ToPrettyString(uid)}. Prototype: {component.Prototype}");
+                RemCompDeferred(uid, component);
+                return;
+            }
+
+            if (proto.Forced)
+                return;
+
+            var act = new InstantAction
+            {
+                Event = new RevertPolymorphActionEvent(),
+                EntityIcon = component.Parent,
+                DisplayName = Loc.GetString("polymorph-revert-action-name"),
+                Description = Loc.GetString("polymorph-revert-action-description"),
+                UseDelay = TimeSpan.FromSeconds(proto.Delay),
+            };
+
+            _actions.AddAction(uid, act, null);
+        }
+
+        /// <summary>
+        /// Polymorphs the target entity into the specific polymorph prototype
+        /// </summary>
+        /// <param name="target">The entity that will be transformed</param>
+        /// <param name="id">The id of the polymorph prototype</param>
+        public EntityUid? PolymorphEntity(EntityUid target, string id)
+        {
+            if (!_proto.TryIndex<PolymorphPrototype>(id, out var proto))
+            {
+                _saw.Error("Invalid polymorph prototype");
+                return null;
+            }
+
+            return PolymorphEntity(target, proto);
+        }
+
+        /// <summary>
+        /// Polymorphs the target entity into the specific polymorph prototype
+        /// </summary>
+        /// <param name="uid">The entity that will be transformed</param>
+        /// <param name="proto">The polymorph prototype</param>
+        public EntityUid? PolymorphEntity(EntityUid uid, PolymorphPrototype proto)
+        {
+            // if it's already morphed, don't allow it again with this condition active.
+            if (!proto.AllowRepeatedMorphs && HasComp<PolymorphedEntityComponent>(uid))
+                return null;
+
+            // mostly just for vehicles
+            _buckle.TryUnbuckle(uid, uid, true);
+
+            var targetTransformComp = Transform(uid);
+
+            var child = Spawn(proto.Entity, targetTransformComp.Coordinates);
+            MakeSentientCommand.MakeSentient(child, EntityManager);
+
+            var comp = _compFact.GetComponent<PolymorphedEntityComponent>();
+            comp.Owner = child;
+            comp.Parent = uid;
+            comp.Prototype = proto.ID;
+            EntityManager.AddComponent(child, comp);
+
+            var childXform = Transform(child);
+            childXform.LocalRotation = targetTransformComp.LocalRotation;
+
+            if (_container.TryGetContainingContainer(uid, out var cont))
+                cont.Insert(child);
+
+            //Transfers all damage from the original to the new one
+            if (proto.TransferDamage &&
+                TryComp<DamageableComponent>(child, out var damageParent) &&
+                _mobThreshold.GetScaledDamage(uid, child, out var damage) &&
+                damage != null)
+            {
+                _damageable.SetDamage(damageParent, damage);
+            }
+
+            if (proto.Inventory == PolymorphInventoryChange.Transfer)
+            {
+                _inventory.TransferEntityInventories(uid, child);
+                foreach (var hand in _hands.EnumerateHeld(uid))
+                {
+                    _hands.TryDrop(uid, hand, checkActionBlocker: false);
+                    _hands.TryPickupAnyHand(child, hand);
+                }
+            }
+            else if (proto.Inventory == PolymorphInventoryChange.Drop)
+            {
+                if (_inventory.TryGetContainerSlotEnumerator(uid, out var enumerator))
+                {
+                    while (enumerator.MoveNext(out var slot))
+                    {
+                        _inventory.TryUnequip(uid, slot.ID, true, true);
+                    }
+                }
+
+                foreach (var held in _hands.EnumerateHeld(uid))
+                {
+                    _hands.TryDrop(uid, held);
+                }
+            }
+
+            if (proto.TransferName &&
+                TryComp<MetaDataComponent>(uid, out var targetMeta) &&
+                TryComp<MetaDataComponent>(child, out var childMeta))
+            {
+                childMeta.EntityName = targetMeta.EntityName;
+            }
+
+            if (proto.TransferHumanoidAppearance)
+            {
+                _humanoid.CloneAppearance(uid, child);
+            }
+
+            if (TryComp<MindComponent>(uid, out var mind) && mind.Mind != null)
+                    mind.Mind.TransferTo(child);
+
+            //Ensures a map to banish the entity to
+            EnsurePausesdMap();
+            if (PausedMap != null)
+                _transform.SetParent(uid, targetTransformComp, PausedMap.Value);
+
+            return child;
+        }
+
+        /// <summary>
+        /// Reverts a polymorphed entity back into its original form
+        /// </summary>
+        /// <param name="uid">The entityuid of the entity being reverted</param>
+        /// <param name="component"></param>
+        public void Revert(EntityUid uid, PolymorphedEntityComponent? component = null)
+        {
+            if (Deleted(uid))
+                return;
+
+            if (!Resolve(uid, ref component))
+                return;
+
+            var parent = component.Parent;
+            if (Deleted(parent))
+                return;
+
+            if (!_proto.TryIndex(component.Prototype, out PolymorphPrototype? proto))
+            {
+                Logger.Error($"{nameof(PolymorphSystem)} encountered an improperly initialized polymorph component while reverting. Entity {ToPrettyString(uid)}. Prototype: {component.Prototype}");
+                return;
+            }
+
+            var uidXform = Transform(uid);
+            var parentXform = Transform(parent);
+
+            _transform.SetParent(parent, parentXform, uidXform.ParentUid);
+            parentXform.Coordinates = uidXform.Coordinates;
+            parentXform.LocalRotation = uidXform.LocalRotation;
+
+            if (_container.TryGetContainingContainer(uid, out var cont))
+                cont.Insert(component.Parent);
+
+            if (proto.TransferDamage &&
+                TryComp<DamageableComponent>(parent, out var damageParent) &&
+                _mobThreshold.GetScaledDamage(uid, parent, out var damage) &&
+                damage != null)
+            {
+                _damageable.SetDamage(damageParent, damage);
+            }
+
+            if (proto.Inventory == PolymorphInventoryChange.Transfer)
+            {
+                _inventory.TransferEntityInventories(uid, parent);
+                foreach (var held in _hands.EnumerateHeld(uid))
+                {
+                    _hands.TryDrop(uid, held);
+                    _hands.TryPickupAnyHand(parent, held, checkActionBlocker: false);
+                }
+            }
+            else if (proto.Inventory == PolymorphInventoryChange.Drop)
+            {
+                if (_inventory.TryGetContainerSlotEnumerator(uid, out var enumerator))
+                {
+                    while (enumerator.MoveNext(out var slot))
+                    {
+                        _inventory.TryUnequip(uid, slot.ID);
+                    }
+                }
+
+                foreach (var held in _hands.EnumerateHeld(uid))
+                {
+                    _hands.TryDrop(uid, held);
+                }
+            }
+
+            if (TryComp<MindComponent>(uid, out var mind) && mind.Mind != null)
+            {
+                mind.Mind.TransferTo(parent);
+            }
+
+            _popup.PopupEntity(Loc.GetString("polymorph-revert-popup-generic",
+                ("parent", Identity.Entity(uid, EntityManager)),
+                ("child", Identity.Entity(parent, EntityManager))),
+                parent);
+            QueueDel(uid);
+        }
+
+        /// <summary>
+        /// Creates a sidebar action for an entity to be able to polymorph at will
+        /// </summary>
+        /// <param name="id">The string of the id of the polymorph action</param>
+        /// <param name="target">The entity that will be gaining the action</param>
+        public void CreatePolymorphAction(string id, EntityUid target)
+        {
+            if (!_proto.TryIndex<PolymorphPrototype>(id, out var polyproto))
+            {
+                _saw.Error("Invalid polymorph prototype");
+                return;
+            }
+
+            if (!TryComp<PolymorphableComponent>(target, out var polycomp))
+                return;
+
+            var entproto = _proto.Index<EntityPrototype>(polyproto.Entity);
+
+            var act = new InstantAction
+            {
+                Event = new PolymorphActionEvent
+                {
+                    Prototype = polyproto,
+                },
+                DisplayName = Loc.GetString("polymorph-self-action-name", ("target", entproto.Name)),
+                Description = Loc.GetString("polymorph-self-action-description", ("target", entproto.Name)),
+                Icon = new SpriteSpecifier.EntityPrototype(polyproto.Entity),
+                ItemIconStyle = ItemActionIconStyle.NoItem,
+            };
+
+            polycomp.PolymorphActions ??= new();
+
+            polycomp.PolymorphActions.Add(id, act);
+            _actions.AddAction(target, act, target);
+        }
+
+        [PublicAPI]
+        public void RemovePolymorphAction(string id, EntityUid target, PolymorphableComponent? component = null)
+        {
+            if (!Resolve(target, ref component, false))
+                return;
+            if (component.PolymorphActions == null)
+                return;
+            if (component.PolymorphActions.TryGetValue(id, out var val))
+                _actions.RemoveAction(target, val);
+        }
+
+        public override void Update(float frameTime)
+        {
+            base.Update(frameTime);
+
+            foreach (var comp in EntityQuery<PolymorphedEntityComponent>())
+            {
+                comp.Time += frameTime;
+                var ent = comp.Owner;
+
+                if (!_proto.TryIndex(comp.Prototype, out PolymorphPrototype? proto))
+                {
+                    Logger.Error($"{nameof(PolymorphSystem)} encountered an improperly initialized polymorph component while updating. Entity {ToPrettyString(ent)}. Prototype: {comp.Prototype}");
+                    RemCompDeferred(ent, comp);
+                    continue;
+                }
+
+                if(proto.Duration != null && comp.Time >= proto.Duration)
+                    Revert(ent, comp);
+
+                if (!TryComp<MobStateComponent>(ent, out var mob))
+                    continue;
+
+                if (proto.RevertOnDeath && _mobState.IsDead(ent, mob) ||
+                    proto.RevertOnCrit && _mobState.IsIncapacitated(ent, mob))
+                    Revert(ent, comp);
+            }
+
+            UpdateCollide();
+        }
+    }
+
+    public sealed class PolymorphActionEvent : InstantActionEvent
+    {
+        /// <summary>
+        /// The polymorph prototype containing all the information about
+        /// the specific polymorph.
+        /// </summary>
+        public PolymorphPrototype Prototype = default!;
+    }
+
+    public sealed class RevertPolymorphActionEvent : InstantActionEvent
+    {
+
+    }
+}
diff --git a/Content.Server/Polymorph/Systems/PolymorphableSystem.cs b/Content.Server/Polymorph/Systems/PolymorphableSystem.cs
deleted file mode 100644 (file)
index 27300eb..0000000
+++ /dev/null
@@ -1,227 +0,0 @@
-using Content.Server.Actions;
-using Content.Server.Buckle.Systems;
-using Content.Server.Humanoid;
-using Content.Server.Inventory;
-using Content.Server.Mind.Commands;
-using Content.Server.Mind.Components;
-using Content.Server.Polymorph.Components;
-using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
-using Content.Shared.Damage;
-using Content.Shared.Hands.EntitySystems;
-using Content.Shared.Mobs.Systems;
-using Content.Shared.Polymorph;
-using Robust.Server.Containers;
-using Robust.Shared.Containers;
-using Robust.Shared.Map;
-using Robust.Shared.Prototypes;
-using Robust.Shared.Utility;
-
-namespace Content.Server.Polymorph.Systems
-{
-    public sealed partial class PolymorphableSystem : EntitySystem
-    {
-        private readonly ISawmill _saw = default!;
-
-        [Dependency] private readonly ActionsSystem _actions = default!;
-        [Dependency] private readonly BuckleSystem _buckle = default!;
-        [Dependency] private readonly IPrototypeManager _proto = default!;
-        [Dependency] private readonly IComponentFactory _compFact = default!;
-        [Dependency] private readonly ServerInventorySystem _inventory = default!;
-        [Dependency] private readonly SharedHandsSystem _sharedHands = default!;
-        [Dependency] private readonly DamageableSystem _damageable = default!;
-        [Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
-        [Dependency] private readonly IMapManager _mapManager = default!;
-        [Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
-        [Dependency] private readonly ContainerSystem _container = default!;
-
-        public override void Initialize()
-        {
-            base.Initialize();
-
-            SubscribeLocalEvent<PolymorphableComponent, ComponentStartup>(OnStartup);
-            SubscribeLocalEvent<PolymorphableComponent, PolymorphActionEvent>(OnPolymorphActionEvent);
-
-            InitializeCollide();
-            InitializeMap();
-        }
-
-        private void OnStartup(EntityUid uid, PolymorphableComponent component, ComponentStartup args)
-        {
-            if (component.InnatePolymorphs != null)
-            {
-                foreach (var morph in component.InnatePolymorphs)
-                {
-                    CreatePolymorphAction(morph, uid);
-                }
-            }
-        }
-
-        private void OnPolymorphActionEvent(EntityUid uid, PolymorphableComponent component, PolymorphActionEvent args)
-        {
-            PolymorphEntity(uid, args.Prototype);
-        }
-
-        /// <summary>
-        /// Polymorphs the target entity into the specific polymorph prototype
-        /// </summary>
-        /// <param name="target">The entity that will be transformed</param>
-        /// <param name="id">The id of the polymorph prototype</param>
-        public EntityUid? PolymorphEntity(EntityUid target, string id)
-        {
-            if (!_proto.TryIndex<PolymorphPrototype>(id, out var proto))
-            {
-                _saw.Error("Invalid polymorph prototype");
-                return null;
-            }
-
-            return PolymorphEntity(target, proto);
-        }
-
-        /// <summary>
-        /// Polymorphs the target entity into the specific polymorph prototype
-        /// </summary>
-        /// <param name="target">The entity that will be transformed</param>
-        /// <param name="proto">The polymorph prototype</param>
-        public EntityUid? PolymorphEntity(EntityUid target, PolymorphPrototype proto)
-        {
-            // This is the big papa function. This handles the transformation, moving the old entity
-            // logic and conditions specified in the prototype, and everything else that may be needed.
-            // I am clinically insane - emo
-
-            // if it's already morphed, don't allow it again with this condition active.
-            if (!proto.AllowRepeatedMorphs && HasComp<PolymorphedEntityComponent>(target))
-                return null;
-
-            // mostly just for vehicles
-            _buckle.TryUnbuckle(target, target, true);
-
-            var targetTransformComp = Transform(target);
-
-            var child = Spawn(proto.Entity, targetTransformComp.Coordinates);
-            MakeSentientCommand.MakeSentient(child, EntityManager);
-
-            var comp = _compFact.GetComponent<PolymorphedEntityComponent>();
-            comp.Owner = child;
-            comp.Parent = target;
-            comp.Prototype = proto.ID;
-            EntityManager.AddComponent(child, comp);
-
-            var childXform = Transform(child);
-            childXform.LocalRotation = targetTransformComp.LocalRotation;
-
-            if (_container.TryGetContainingContainer(target, out var cont))
-                cont.Insert(child);
-
-            //Transfers all damage from the original to the new one
-            if (proto.TransferDamage &&
-                TryComp<DamageableComponent>(child, out var damageParent) &&
-                _mobThresholdSystem.GetScaledDamage(target, child, out var damage) &&
-                damage != null)
-            {
-                _damageable.SetDamage(damageParent, damage);
-            }
-
-            if (proto.Inventory == PolymorphInventoryChange.Transfer)
-            {
-                _inventory.TransferEntityInventories(target, child);
-                foreach (var hand in _sharedHands.EnumerateHeld(target))
-                {
-                    hand.TryRemoveFromContainer();
-                    _sharedHands.TryPickupAnyHand(child, hand);
-                }
-            }
-            else if (proto.Inventory == PolymorphInventoryChange.Drop)
-            {
-                if(_inventory.TryGetContainerSlotEnumerator(target, out var enumerator))
-                    while (enumerator.MoveNext(out var slot))
-                        slot.EmptyContainer();
-
-                foreach (var hand in _sharedHands.EnumerateHeld(target))
-                    hand.TryRemoveFromContainer();
-            }
-
-            if (proto.TransferName &&
-                TryComp<MetaDataComponent>(target, out var targetMeta) &&
-                TryComp<MetaDataComponent>(child, out var childMeta))
-            {
-                childMeta.EntityName = targetMeta.EntityName;
-            }
-
-            if (proto.TransferHumanoidAppearance)
-            {
-                _humanoid.CloneAppearance(target, child);
-            }
-
-            if (TryComp<MindComponent>(target, out var mind) && mind.Mind != null)
-                    mind.Mind.TransferTo(child);
-
-            //Ensures a map to banish the entity to
-            EnsurePausesdMap();
-            if (PausedMap != null)
-                targetTransformComp.AttachParent(Transform(PausedMap.Value));
-
-            return child;
-        }
-
-        /// <summary>
-        /// Creates a sidebar action for an entity to be able to polymorph at will
-        /// </summary>
-        /// <param name="id">The string of the id of the polymorph action</param>
-        /// <param name="target">The entity that will be gaining the action</param>
-        public void CreatePolymorphAction(string id, EntityUid target)
-        {
-            if (!_proto.TryIndex<PolymorphPrototype>(id, out var polyproto))
-            {
-                _saw.Error("Invalid polymorph prototype");
-                return;
-            }
-
-            if (!TryComp<PolymorphableComponent>(target, out var polycomp))
-                return;
-
-            var entproto = _proto.Index<EntityPrototype>(polyproto.Entity);
-
-            var act = new InstantAction()
-            {
-                Event = new PolymorphActionEvent
-                {
-                    Prototype = polyproto,
-                },
-                DisplayName = Loc.GetString("polymorph-self-action-name", ("target", entproto.Name)),
-                Description = Loc.GetString("polymorph-self-action-description", ("target", entproto.Name)),
-                Icon = new SpriteSpecifier.EntityPrototype(polyproto.Entity),
-                ItemIconStyle = ItemActionIconStyle.NoItem,
-            };
-
-            if (polycomp.PolymorphActions == null)
-                polycomp.PolymorphActions = new();
-
-            polycomp.PolymorphActions.Add(id, act);
-            _actions.AddAction(target, act, target);
-        }
-
-        public void RemovePolymorphAction(string id, EntityUid target)
-        {
-            if (!_proto.TryIndex<PolymorphPrototype>(id, out var polyproto))
-                return;
-            if (!TryComp<PolymorphableComponent>(target, out var comp))
-                return;
-            if (comp.PolymorphActions == null)
-                return;
-
-            comp.PolymorphActions.TryGetValue(id, out var val);
-            if (val != null)
-                _actions.RemoveAction(target, val);
-        }
-    }
-
-    public sealed class PolymorphActionEvent : InstantActionEvent
-    {
-        /// <summary>
-        /// The polymorph prototype containing all the information about
-        /// the specific polymorph.
-        /// </summary>
-        public PolymorphPrototype Prototype = default!;
-    };
-}
diff --git a/Content.Server/Polymorph/Systems/PolymorphedEntitySystem.cs b/Content.Server/Polymorph/Systems/PolymorphedEntitySystem.cs
deleted file mode 100644 (file)
index 965a053..0000000
+++ /dev/null
@@ -1,171 +0,0 @@
-using Content.Server.Actions;
-using Content.Server.Inventory;
-using Content.Server.Mind.Components;
-using Content.Server.Polymorph.Components;
-using Content.Server.Popups;
-using Content.Shared.Actions;
-using Content.Shared.Actions.ActionTypes;
-using Content.Shared.Damage;
-using Content.Shared.Hands.EntitySystems;
-using Content.Shared.IdentityManagement;
-using Content.Shared.Mobs.Components;
-using Content.Shared.Mobs.Systems;
-using Content.Shared.Polymorph;
-using Robust.Server.Containers;
-using Robust.Shared.Containers;
-using Robust.Shared.Player;
-using Robust.Shared.Prototypes;
-
-namespace Content.Server.Polymorph.Systems
-{
-    public sealed class PolymorphedEntitySystem : EntitySystem
-    {
-        [Dependency] private readonly IPrototypeManager _proto = default!;
-        [Dependency] private readonly ActionsSystem _actions = default!;
-        [Dependency] private readonly DamageableSystem _damageable = default!;
-        [Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
-        [Dependency] private readonly PopupSystem _popup = default!;
-        [Dependency] private readonly ServerInventorySystem _inventory = default!;
-        [Dependency] private readonly SharedHandsSystem _sharedHands = default!;
-        [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
-        [Dependency] private readonly ContainerSystem _container = default!;
-
-        public override void Initialize()
-        {
-            base.Initialize();
-
-            SubscribeLocalEvent<PolymorphedEntityComponent, ComponentStartup>(OnInit);
-            SubscribeLocalEvent<PolymorphedEntityComponent, RevertPolymorphActionEvent>(OnRevertPolymorphActionEvent);
-        }
-
-        private void OnRevertPolymorphActionEvent(EntityUid uid, PolymorphedEntityComponent component, RevertPolymorphActionEvent args)
-        {
-            Revert(uid);
-        }
-
-        /// <summary>
-        /// Reverts a polymorphed entity back into its original form
-        /// </summary>
-        /// <param name="uid">The entityuid of the entity being reverted</param>
-        public void Revert(EntityUid uid)
-        {
-            if (Deleted(uid))
-                return;
-
-            if (!TryComp<PolymorphedEntityComponent>(uid, out var component))
-                return;
-
-            if (Deleted(component.Parent))
-                return;
-
-            if (!_proto.TryIndex(component.Prototype, out PolymorphPrototype? proto))
-            {
-                Logger.Error($"{nameof(PolymorphedEntitySystem)} encountered an improperly initialized polymorph component while reverting. Entity {ToPrettyString(uid)}. Prototype: {component.Prototype}");
-                return;
-            }
-
-            var uidXform = Transform(uid);
-            var parentXform = Transform(component.Parent);
-
-            parentXform.AttachParent(uidXform.ParentUid);
-            parentXform.Coordinates = uidXform.Coordinates;
-            parentXform.LocalRotation = uidXform.LocalRotation;
-
-            if (_container.TryGetContainingContainer(uid, out var cont))
-                cont.Insert(component.Parent);
-
-            if (proto.TransferDamage &&
-                TryComp<DamageableComponent>(component.Parent, out var damageParent) &&
-                _mobThresholdSystem.GetScaledDamage(uid, component.Parent, out var damage) &&
-                damage != null)
-            {
-                _damageable.SetDamage(damageParent, damage);
-            }
-
-            if (proto.Inventory == PolymorphInventoryChange.Transfer)
-            {
-                _inventory.TransferEntityInventories(uid, component.Parent);
-                foreach (var hand in _sharedHands.EnumerateHeld(component.Parent))
-                {
-                    hand.TryRemoveFromContainer();
-                    _sharedHands.TryPickupAnyHand(component.Parent, hand);
-                }
-            }
-            else if (proto.Inventory == PolymorphInventoryChange.Drop)
-            {
-                if (_inventory.TryGetContainerSlotEnumerator(uid, out var enumerator))
-                    while (enumerator.MoveNext(out var slot))
-                        slot.EmptyContainer();
-
-                foreach (var hand in _sharedHands.EnumerateHeld(uid))
-                    // This causes errors/bugs. Use hand related functions instead.
-                    hand.TryRemoveFromContainer();
-            }
-
-            if (TryComp<MindComponent>(uid, out var mind) && mind.Mind != null)
-            {
-                mind.Mind.TransferTo(component.Parent);
-            }
-
-            _popup.PopupEntity(Loc.GetString("polymorph-revert-popup-generic",
-                ("parent", Identity.Entity(uid, EntityManager)),
-                ("child", Identity.Entity(component.Parent, EntityManager))),
-                component.Parent);
-            QueueDel(uid);
-        }
-
-        public void OnInit(EntityUid uid, PolymorphedEntityComponent component, ComponentStartup args)
-        {
-            if (!_proto.TryIndex(component.Prototype, out PolymorphPrototype? proto))
-            {
-                // warning instead of error because of the all-comps one entity test.
-                Logger.Warning($"{nameof(PolymorphedEntitySystem)} encountered an improperly set up polymorph component while initializing. Entity {ToPrettyString(uid)}. Prototype: {component.Prototype}");
-                RemCompDeferred(uid, component);
-                return;
-            }
-
-            if (proto.Forced)
-                return;
-
-            var act = new InstantAction()
-            {
-                Event = new RevertPolymorphActionEvent(),
-                EntityIcon = component.Parent,
-                DisplayName = Loc.GetString("polymorph-revert-action-name"),
-                Description = Loc.GetString("polymorph-revert-action-description"),
-                UseDelay = TimeSpan.FromSeconds(proto.Delay),
-           };
-
-            _actions.AddAction(uid, act, null);
-        }
-
-        public override void Update(float frameTime)
-        {
-            base.Update(frameTime);
-
-            foreach (var comp in EntityQuery<PolymorphedEntityComponent>())
-            {
-                comp.Time += frameTime;
-
-                if (!_proto.TryIndex(comp.Prototype, out PolymorphPrototype? proto))
-                {
-                    Logger.Error($"{nameof(PolymorphedEntitySystem)} encountered an improperly initialized polymorph component while updating. Entity {ToPrettyString(comp.Owner)}. Prototype: {comp.Prototype}");
-                    RemCompDeferred(comp.Owner, comp);
-                    continue;
-                }
-
-                if(proto.Duration != null && comp.Time >= proto.Duration)
-                    Revert(comp.Owner);
-
-                if (!TryComp<MobStateComponent>(comp.Owner, out var mob))
-                    continue;
-
-                if ((proto.RevertOnDeath && _mobStateSystem.IsDead(comp.Owner, mob)) ||
-                    (proto.RevertOnCrit && _mobStateSystem.IsCritical(comp.Owner, mob)))
-                    Revert(comp.Owner);
-            }
-        }
-    }
-
-    public sealed class RevertPolymorphActionEvent : InstantActionEvent { };
-}