From: Nemanja <98561806+EmoGarbage404@users.noreply.github.com> Date: Mon, 6 Mar 2023 17:37:18 +0000 (-0500) Subject: Clean up polymorphsystem (#14297) X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=e412eda97cfbbf3b651ccef3dc3bc06af4be999f;p=space-station-14.git Clean up polymorphsystem (#14297) --- diff --git a/Content.Server/Administration/Commands/AddPolymorphActionCommand.cs b/Content.Server/Administration/Commands/AddPolymorphActionCommand.cs index d40644499d..f2cc47b908 100644 --- a/Content.Server/Administration/Commands/AddPolymorphActionCommand.cs +++ b/Content.Server/Administration/Commands/AddPolymorphActionCommand.cs @@ -29,7 +29,7 @@ public sealed class AddPolymorphActionCommand : IConsoleCommand } var entityManager = IoCManager.Resolve(); - var polySystem = entityManager.EntitySysManager.GetEntitySystem(); + var polySystem = entityManager.EntitySysManager.GetEntitySystem(); entityManager.EnsureComponent(entityUid); polySystem.CreatePolymorphAction(args[1], entityUid); diff --git a/Content.Server/Administration/Commands/PolymorphCommand.cs b/Content.Server/Administration/Commands/PolymorphCommand.cs index 3e79e85426..b4d2c9603a 100644 --- a/Content.Server/Administration/Commands/PolymorphCommand.cs +++ b/Content.Server/Administration/Commands/PolymorphCommand.cs @@ -39,7 +39,7 @@ public sealed class PolymorphCommand : IConsoleCommand } var entityManager = IoCManager.Resolve(); - var polySystem = entityManager.EntitySysManager.GetEntitySystem(); + var polySystem = entityManager.EntitySysManager.GetEntitySystem(); entityManager.EnsureComponent(entityUid); polySystem.PolymorphEntity(entityUid, polyproto); diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs index f2f9510952..c734b2b89d 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs @@ -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"), diff --git a/Content.Server/Disease/Effects/DiseasePolymorph.cs b/Content.Server/Disease/Effects/DiseasePolymorph.cs index 4eb91f7fc4..f6136cc846 100644 --- a/Content.Server/Disease/Effects/DiseasePolymorph.cs +++ b/Content.Server/Disease/Effects/DiseasePolymorph.cs @@ -27,7 +27,7 @@ namespace Content.Server.Disease.Effects public override void Effect(DiseaseEffectArgs args) { - EntityUid? polyUid = EntitySystem.Get().PolymorphEntity(args.DiseasedEntity, PolymorphId); + EntityUid? polyUid = EntitySystem.Get().PolymorphEntity(args.DiseasedEntity, PolymorphId); if (PolymorphSound != null && polyUid != null) SoundSystem.Play(PolymorphSound.GetSound(), Filter.Pvs(polyUid.Value), polyUid.Value, AudioHelpers.WithVariation(0.2f)); diff --git a/Content.Server/Inventory/ServerInventorySystem.cs b/Content.Server/Inventory/ServerInventorySystem.cs index 50175c72c4..a1cb007d91 100644 --- a/Content.Server/Inventory/ServerInventorySystem.cs +++ b/Content.Server/Inventory/ServerInventorySystem.cs @@ -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 inventoryEntities = new(); + var slots = GetSlots(uid); + while (enumerator.MoveNext(out var containerSlot)) { - Dictionary 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); } } } diff --git a/Content.Server/Polymorph/Components/PolymorphableComponent.cs b/Content.Server/Polymorph/Components/PolymorphableComponent.cs index 882b9d453b..80211d7680 100644 --- a/Content.Server/Polymorph/Components/PolymorphableComponent.cs +++ b/Content.Server/Polymorph/Components/PolymorphableComponent.cs @@ -17,6 +17,6 @@ namespace Content.Server.Polymorph.Components /// The polymorphs that the entity starts out being able to do. /// [DataField("innatePolymorphs", customTypeSerializer : typeof(PrototypeIdListSerializer))] - public List? InnatePolymorphs = null; + public List? InnatePolymorphs; } } diff --git a/Content.Server/Polymorph/Components/PolymorphedEntityComponent.cs b/Content.Server/Polymorph/Components/PolymorphedEntityComponent.cs index 2a5d489727..3263832e13 100644 --- a/Content.Server/Polymorph/Components/PolymorphedEntityComponent.cs +++ b/Content.Server/Polymorph/Components/PolymorphedEntityComponent.cs @@ -17,13 +17,13 @@ namespace Content.Server.Polymorph.Components /// The original entity that the player will revert back into /// [DataField("parent", required: true)] - public EntityUid Parent = new(); + public EntityUid Parent; /// /// The amount of time that has passed since the entity was created /// used for tracking the duration /// [DataField("time")] - public float Time = 0; + public float Time; } } diff --git a/Content.Server/Polymorph/Systems/PolymorphableSystem.Collide.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.Collide.cs similarity index 81% rename from Content.Server/Polymorph/Systems/PolymorphableSystem.Collide.cs rename to Content.Server/Polymorph/Systems/PolymorphSystem.Collide.cs index 82cda8355c..2fb28f39b9 100644 --- a/Content.Server/Polymorph/Systems/PolymorphableSystem.Collide.cs +++ b/Content.Server/Polymorph/Systems/PolymorphSystem.Collide.cs @@ -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 _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; diff --git a/Content.Server/Polymorph/Systems/PolymorphableSystem.Map.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.Map.cs similarity index 87% rename from Content.Server/Polymorph/Systems/PolymorphableSystem.Map.cs rename to Content.Server/Polymorph/Systems/PolymorphSystem.Map.cs index fee16f822e..dc71a22136 100644 --- a/Content.Server/Polymorph/Systems/PolymorphableSystem.Map.cs +++ b/Content.Server/Polymorph/Systems/PolymorphSystem.Map.cs @@ -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; } /// /// 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 index 0000000000..bc91d2ed1b --- /dev/null +++ b/Content.Server/Polymorph/Systems/PolymorphSystem.cs @@ -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(OnStartup); + SubscribeLocalEvent(OnPolymorphActionEvent); + SubscribeLocalEvent(OnStartup); + SubscribeLocalEvent(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); + } + + /// + /// Polymorphs the target entity into the specific polymorph prototype + /// + /// The entity that will be transformed + /// The id of the polymorph prototype + public EntityUid? PolymorphEntity(EntityUid target, string id) + { + if (!_proto.TryIndex(id, out var proto)) + { + _saw.Error("Invalid polymorph prototype"); + return null; + } + + return PolymorphEntity(target, proto); + } + + /// + /// Polymorphs the target entity into the specific polymorph prototype + /// + /// The entity that will be transformed + /// The polymorph prototype + 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(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(); + 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(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(uid, out var targetMeta) && + TryComp(child, out var childMeta)) + { + childMeta.EntityName = targetMeta.EntityName; + } + + if (proto.TransferHumanoidAppearance) + { + _humanoid.CloneAppearance(uid, child); + } + + if (TryComp(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; + } + + /// + /// Reverts a polymorphed entity back into its original form + /// + /// The entityuid of the entity being reverted + /// + 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(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(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); + } + + /// + /// Creates a sidebar action for an entity to be able to polymorph at will + /// + /// The string of the id of the polymorph action + /// The entity that will be gaining the action + public void CreatePolymorphAction(string id, EntityUid target) + { + if (!_proto.TryIndex(id, out var polyproto)) + { + _saw.Error("Invalid polymorph prototype"); + return; + } + + if (!TryComp(target, out var polycomp)) + return; + + var entproto = _proto.Index(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()) + { + 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(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 + { + /// + /// The polymorph prototype containing all the information about + /// the specific polymorph. + /// + 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 index 27300eb0ec..0000000000 --- a/Content.Server/Polymorph/Systems/PolymorphableSystem.cs +++ /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(OnStartup); - SubscribeLocalEvent(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); - } - - /// - /// Polymorphs the target entity into the specific polymorph prototype - /// - /// The entity that will be transformed - /// The id of the polymorph prototype - public EntityUid? PolymorphEntity(EntityUid target, string id) - { - if (!_proto.TryIndex(id, out var proto)) - { - _saw.Error("Invalid polymorph prototype"); - return null; - } - - return PolymorphEntity(target, proto); - } - - /// - /// Polymorphs the target entity into the specific polymorph prototype - /// - /// The entity that will be transformed - /// The polymorph prototype - 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(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(); - 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(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(target, out var targetMeta) && - TryComp(child, out var childMeta)) - { - childMeta.EntityName = targetMeta.EntityName; - } - - if (proto.TransferHumanoidAppearance) - { - _humanoid.CloneAppearance(target, child); - } - - if (TryComp(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; - } - - /// - /// Creates a sidebar action for an entity to be able to polymorph at will - /// - /// The string of the id of the polymorph action - /// The entity that will be gaining the action - public void CreatePolymorphAction(string id, EntityUid target) - { - if (!_proto.TryIndex(id, out var polyproto)) - { - _saw.Error("Invalid polymorph prototype"); - return; - } - - if (!TryComp(target, out var polycomp)) - return; - - var entproto = _proto.Index(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(id, out var polyproto)) - return; - if (!TryComp(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 - { - /// - /// The polymorph prototype containing all the information about - /// the specific polymorph. - /// - public PolymorphPrototype Prototype = default!; - }; -} diff --git a/Content.Server/Polymorph/Systems/PolymorphedEntitySystem.cs b/Content.Server/Polymorph/Systems/PolymorphedEntitySystem.cs deleted file mode 100644 index 965a053638..0000000000 --- a/Content.Server/Polymorph/Systems/PolymorphedEntitySystem.cs +++ /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(OnInit); - SubscribeLocalEvent(OnRevertPolymorphActionEvent); - } - - private void OnRevertPolymorphActionEvent(EntityUid uid, PolymorphedEntityComponent component, RevertPolymorphActionEvent args) - { - Revert(uid); - } - - /// - /// Reverts a polymorphed entity back into its original form - /// - /// The entityuid of the entity being reverted - public void Revert(EntityUid uid) - { - if (Deleted(uid)) - return; - - if (!TryComp(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(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(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()) - { - 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(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 { }; -}