using Content.Shared.Actions;
using Content.Shared.Interaction;
using Content.Shared.Polymorph;
using Content.Shared.Polymorph.Components;
using Content.Shared.Popups;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Prototypes;
using System.Diagnostics.CodeAnalysis;
namespace Content.Shared.Polymorph.Systems;
///
/// Handles whitelist/blacklist checking.
/// Actual polymorphing and deactivation is done serverside.
///
public abstract class SharedChameleonProjectorSystem : EntitySystem
{
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly ISerializationManager _serMan = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent(OnInteract);
}
private void OnInteract(Entity ent, ref AfterInteractEvent args)
{
if (!args.CanReach || args.Target is not {} target)
return;
var user = args.User;
args.Handled = true;
if (IsInvalid(ent.Comp, target))
{
_popup.PopupClient(Loc.GetString(ent.Comp.InvalidPopup), target, user);
return;
}
_popup.PopupClient(Loc.GetString(ent.Comp.SuccessPopup), target, user);
Disguise(ent.Comp, user, target);
}
///
/// Returns true if an entity cannot be used as a disguise.
///
public bool IsInvalid(ChameleonProjectorComponent comp, EntityUid target)
{
return (comp.Whitelist?.IsValid(target, EntityManager) == false)
|| (comp.Blacklist?.IsValid(target, EntityManager) == true);
}
///
/// On server, polymorphs the user into an entity and sets up the disguise.
///
public virtual void Disguise(ChameleonProjectorComponent comp, EntityUid user, EntityUid entity)
{
}
///
/// Copy a component from the source entity/prototype to the disguise entity.
///
///
/// This would probably be a good thing to add to engine in the future.
///
protected bool CopyComp(Entity ent) where T: Component, new()
{
if (!GetSrcComp(ent.Comp, out var src))
return true;
// remove then re-add to prevent a funny
RemComp(ent);
var dest = AddComp(ent);
_serMan.CopyTo(src, ref dest, notNullableOverride: true);
Dirty(ent, dest);
return false;
}
///
/// Try to get a single component from the source entity/prototype.
///
private bool GetSrcComp(ChameleonDisguiseComponent comp, [NotNullWhen(true)] out T? src) where T: Component
{
src = null;
if (TryComp(comp.SourceEntity, out src))
return true;
if (comp.SourceProto is not {} protoId)
return false;
if (!_proto.TryIndex(protoId, out var proto))
return false;
return proto.TryGetComponent(out src);
}
}
///
/// Action event for toggling transform NoRot on a disguise.
///
public sealed partial class DisguiseToggleNoRotEvent : InstantActionEvent
{
}
///
/// Action event for toggling transform Anchored on a disguise.
///
public sealed partial class DisguiseToggleAnchoredEvent : InstantActionEvent
{
}