diff --git a/Content.Server/Polymorph/Components/PolymorphableComponent.cs b/Content.Server/Polymorph/Components/PolymorphableComponent.cs index c05d36a842..2e9c5fee0a 100644 --- a/Content.Server/Polymorph/Components/PolymorphableComponent.cs +++ b/Content.Server/Polymorph/Components/PolymorphableComponent.cs @@ -12,6 +12,12 @@ namespace Content.Server.Polymorph.Components /// public Dictionary? PolymorphActions = null; + /// + /// Timestamp for when the most recent polymorph ended. + /// + [ViewVariables(VVAccess.ReadOnly)] + public TimeSpan? LastPolymorphEnd = null; + /// /// The polymorphs that the entity starts out being able to do. /// diff --git a/Content.Server/Polymorph/Systems/PolymorphSystem.cs b/Content.Server/Polymorph/Systems/PolymorphSystem.cs index 5b8a7b7165..4d3bbcdf78 100644 --- a/Content.Server/Polymorph/Systems/PolymorphSystem.cs +++ b/Content.Server/Polymorph/Systems/PolymorphSystem.cs @@ -7,6 +7,7 @@ using Content.Server.Polymorph.Components; using Content.Shared.Actions; using Content.Shared.Buckle; using Content.Shared.Damage; +using Content.Shared.Destructible; using Content.Shared.Hands.EntitySystems; using Content.Shared.IdentityManagement; using Content.Shared.Mind; @@ -20,6 +21,7 @@ using Robust.Server.Containers; using Robust.Server.GameObjects; using Robust.Shared.Map; using Robust.Shared.Prototypes; +using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Server.Polymorph.Systems @@ -44,6 +46,7 @@ namespace Content.Server.Polymorph.Systems [Dependency] private readonly TransformSystem _transform = default!; [Dependency] private readonly SharedMindSystem _mindSystem = default!; [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly IGameTiming _gameTiming = default!; private ISawmill _sawmill = default!; @@ -59,6 +62,7 @@ namespace Content.Server.Polymorph.Systems SubscribeLocalEvent(OnBeforeFullyEaten); SubscribeLocalEvent(OnBeforeFullySliced); SubscribeLocalEvent(OnRevertPolymorphActionEvent); + SubscribeLocalEvent(OnDestruction); InitializeCollide(); InitializeMap(); @@ -122,6 +126,24 @@ namespace Content.Server.Polymorph.Systems } } + /// + /// It is possible to be polymorphed into an entity that can't "die", but is instead + /// destroyed. This handler ensures that destruction is treated like death. + /// + private void OnDestruction(EntityUid uid, PolymorphedEntityComponent comp, DestructionEventArgs args) + { + if (!_proto.TryIndex(comp.Prototype, out var proto)) + { + _sawmill.Error($"Invalid polymorph prototype {comp.Prototype}"); + return; + } + + if (proto.RevertOnDeath) + { + Revert(uid, comp); + } + } + private void OnBeforeFullySliced(EntityUid uid, PolymorphedEntityComponent comp, BeforeFullySlicedEvent args) { if (!_proto.TryIndex(comp.Prototype, out var proto)) @@ -164,6 +186,13 @@ namespace Content.Server.Polymorph.Systems if (!proto.AllowRepeatedMorphs && HasComp(uid)) return null; + // If this polymorph has a cooldown, check if that amount of time has passed since the + // last polymorph ended. + if (TryComp(uid, out var polymorphableComponent) && + polymorphableComponent.LastPolymorphEnd != null && + _gameTiming.CurTime < polymorphableComponent.LastPolymorphEnd + proto.Cooldown) + return null; + // mostly just for vehicles _buckle.TryUnbuckle(uid, uid, true); @@ -302,6 +331,9 @@ namespace Content.Server.Polymorph.Systems if (_mindSystem.TryGetMind(uid, out var mindId, out var mind)) _mindSystem.TransferTo(mindId, parent, mind: mind); + if (TryComp(parent, out var polymorphableComponent)) + polymorphableComponent.LastPolymorphEnd = _gameTiming.CurTime; + // if an item polymorph was picked up, put it back down after reverting Transform(parent).AttachToGridOrMap(); diff --git a/Content.Shared/Polymorph/PolymorphPrototype.cs b/Content.Shared/Polymorph/PolymorphPrototype.cs index 75c6d58d24..f093489b04 100644 --- a/Content.Shared/Polymorph/PolymorphPrototype.cs +++ b/Content.Shared/Polymorph/PolymorphPrototype.cs @@ -97,6 +97,14 @@ namespace Content.Shared.Polymorph [DataField("allowRepeatedMorphs", serverOnly: true)] public bool AllowRepeatedMorphs = false; + + /// + /// The amount of time that should pass after this polymorph has ended, before a new one + /// can occur. + /// + [DataField("cooldown", serverOnly: true)] + [ViewVariables(VVAccess.ReadWrite)] + public TimeSpan Cooldown = TimeSpan.Zero; } public enum PolymorphInventoryChange : byte diff --git a/Resources/Prototypes/Polymorphs/polymorph.yml b/Resources/Prototypes/Polymorphs/polymorph.yml index 8593c2194d..46590248ae 100644 --- a/Resources/Prototypes/Polymorphs/polymorph.yml +++ b/Resources/Prototypes/Polymorphs/polymorph.yml @@ -101,7 +101,8 @@ forced: true transferName: true revertOnDeath: true - + inventory: Drop + cooldown: 160 # this is the monkey polymorph for artifact. - type: polymorph