Clean up polymorphsystem (#14297)
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
378
Content.Server/Polymorph/Systems/PolymorphSystem.cs
Normal file
378
Content.Server/Polymorph/Systems/PolymorphSystem.cs
Normal file
@@ -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
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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!;
|
||||
};
|
||||
}
|
||||
@@ -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 { };
|
||||
}
|
||||
Reference in New Issue
Block a user