using Content.Shared.Actions;
using Content.Shared.Actions.Components;
using Content.Shared.Clothing;
using Content.Shared.Implants;
using Content.Shared.Inventory;
using Content.Shared.Mindshield.Components;
using Content.Shared.Popups;
using Content.Shared.Tag;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Shared.Mindshield;
///
/// This system handles fake mindshields represented by . Works using 's
///
public sealed partial class FakeMindShieldSystem : EntitySystem
{
[Dependency] private SharedActionsSystem _actions = default!;
[Dependency] private TagSystem _tag = default!;
[Dependency] private IGameTiming _timing = default!;
[Dependency] private MindShieldSystem _mindShields = default!;
[Dependency] private SharedPopupSystem _popups = default!;
public override void Initialize()
{
base.Initialize();
// Other events
SubscribeLocalEvent>((ent, ref relay) => OnChameleonControllerOutfitSelectedImplant(ent, ref relay.Args, relay.ImplantedEntity));
// Toggle events
SubscribeLocalEvent>((e, ref sk) => OnToggleMindshield(e, ref sk.Args));
SubscribeLocalEvent>((e, ref sk) => OnToggleMindshield(e, ref sk.Args));
// Mindshield events
SubscribeLocalEvent>((a, ref k) => OnQueryStatus(a, ref k.Args));
SubscribeLocalEvent>((a, ref k) => OnQueryStatus(a, ref k.Args));
}
///
/// Displays a popup to inform the player of activation or deactivation.
///
///
/// An associated tuple. The state in will be used
/// to display the appropriate popup.
///
/// The entity that toggled the fake mindshield.
private void ShowTogglePopup(Entity ent, EntityUid performer)
{
var message = ent.Comp.IsEnabled
? Loc.GetString(ent.Comp.EnabledPopup)
: Loc.GetString(ent.Comp.DisabledPopup);
_popups.PopupEntity(message, performer, performer);
}
[SubscribeLocalEvent]
private void OnFakeMindshieldEquip(Entity ent, ref ClothingGotEquippedEvent args)
{
_mindShields.RefreshMindshieldStatus(args.Wearer);
}
[SubscribeLocalEvent]
private void OnFakeMindshieldUnequip(Entity ent, ref ClothingGotUnequippedEvent args)
{
_mindShields.RefreshMindshieldStatus(args.Wearer);
}
[SubscribeLocalEvent]
private void OnFakeMindshieldRemoved(Entity ent, ref ComponentRemove args)
{
if (!ent.Comp.IsInnate)
return;
_mindShields.RefreshMindshieldStatus(ent.Owner);
}
[SubscribeLocalEvent]
private void OnFakeMindshieldImplantRemoved(Entity ent, ref ImplantRemovedEvent args)
{
// when we reinject the mindshield, the action will be disabled (since its a fresh action)
// which would cause it to be desynced with the state of the mindshield
// so we just disable it here
ent.Comp.IsEnabled = false;
Dirty(ent);
_mindShields.RefreshMindshieldStatus(args.Implanted);
}
[SubscribeLocalEvent]
private void OnFakeMindshieldImplanted(Entity ent, ref ImplantImplantedEvent args)
{
_mindShields.RefreshMindshieldStatus(args.Implanted);
}
[SubscribeLocalEvent]
private void OnMapInit(Entity ent, ref MapInitEvent args)
{
if (!ent.Comp.IsInnate)
return;
_actions.AddAction(ent.Owner, ent.Comp.Action);
_mindShields.RefreshMindshieldStatus(ent.Owner);
}
[SubscribeLocalEvent]
private void OnQueryStatus(Entity ent, ref GetMindShieldStatusEvent args)
{
args.IsVisible |= ent.Comp.IsEnabled;
}
[SubscribeLocalEvent]
private void OnToggleMindshield(Entity ent, ref FakeMindShieldToggleEvent args)
{
if (args.ActionTag != ent.Comp.ActionTag)
return;
ent.Comp.IsEnabled = !ent.Comp.IsEnabled;
args.Toggle = true;
args.Handled = true;
ShowTogglePopup(ent, args.Performer);
Dirty(ent.Owner, ent.Comp);
_mindShields.RefreshMindshieldStatus(args.Performer);
}
[SubscribeLocalEvent]
private void OnChameleonControllerOutfitSelected(Entity ent, ref ChameleonControllerOutfitSelectedEvent args)
{
HandleChameleonOutfit(ent, ent.Comp, ent.Owner, ent.Owner, ref args);
}
private void OnChameleonControllerOutfitSelectedImplant(Entity ent, ref ChameleonControllerOutfitSelectedEvent args, EntityUid implantedEntity)
{
HandleChameleonOutfit(ent, ent.Comp, implantedEntity, implantedEntity, ref args);
}
private void HandleChameleonOutfit(EntityUid fakeMindshieldUid, FakeMindShieldComponent component, EntityUid actionOwner, EntityUid statusOwner, ref ChameleonControllerOutfitSelectedEvent args)
{
if (!component.ChameleonControllable)
return;
if (component.IsEnabled == args.ChameleonOutfit.HasMindShield)
return;
// This assumes there is only one fake mindshield action per entity (This is currently enforced)
if (!TryComp(actionOwner, out var actionsComp))
return;
// In case the fake mindshield ever doesn't have an action.
var actionFound = false;
foreach (var action in actionsComp.Actions)
{
if (!_tag.HasTag(action, component.ActionTag))
continue;
if (!TryComp(action, out var actionComp))
continue;
actionFound = true;
if (_actions.IsCooldownActive(actionComp, _timing.CurTime))
continue;
component.IsEnabled = args.ChameleonOutfit.HasMindShield;
_actions.SetToggled(action, args.ChameleonOutfit.HasMindShield);
Dirty(fakeMindshieldUid, component);
if (actionComp.UseDelay != null)
_actions.SetCooldown(action, actionComp.UseDelay.Value);
_mindShields.RefreshMindshieldStatus(statusOwner);
return;
}
// If they don't have the action for some reason, still set it correctly.
if (!actionFound)
{
component.IsEnabled = args.ChameleonOutfit.HasMindShield;
Dirty(fakeMindshieldUid, component);
_mindShields.RefreshMindshieldStatus(statusOwner);
}
}
}
///
/// Inventory relayed event telling all fake mindshields with the same ActionTag to try to toggle themselves.
///
public sealed partial class FakeMindShieldToggleEvent : InstantActionEvent, IInventoryRelayEvent
{
public SlotFlags TargetSlots => SlotFlags.WITHOUT_POCKET;
///
/// The tag the fake mindshield needs to have to be successfully toggled.
/// We seperate it out to allow an entity to, for example, have an implant one and a store bought one without conflicts.
///
[DataField]
public ProtoId ActionTag = "FakeMindShieldImplant";
}