}
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);
}
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);
[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!;
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")
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")
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")
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")
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"),
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"),
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));
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
{
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);
}
}
}
/// 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;
}
}
/// 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;
}
}
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))
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);
}
}
}
}
}
-struct PolymorphQueuedData
+public struct PolymorphQueuedData
{
public EntityUid Ent;
public SoundSpecifier Sound;
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
{
if (PausedMap != null && Exists(PausedMap))
return;
-
+
var newmap = _mapManager.CreateMap();
_mapManager.SetMapPaused(newmap, true);
PausedMap = _mapManager.GetMapEntityId(newmap);
--- /dev/null
+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
+ {
+
+ }
+}
+++ /dev/null
-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!;
- };
-}
+++ /dev/null
-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 { };
-}