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"; }