using Content.Shared.Actions; using Content.Shared.Clothing.Components; using Content.Shared.Inventory.Events; using Content.Shared.Stealth; using Content.Shared.Stealth.Components; namespace Content.Shared.Clothing.EntitySystems; /// /// Handles the toggle action and disables stealth when clothing is unequipped. /// public sealed class StealthClothingSystem : EntitySystem { [Dependency] private readonly SharedStealthSystem _stealth = default!; [Dependency] private readonly ActionContainerSystem _actionContainer = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnGetItemActions); SubscribeLocalEvent(OnToggleStealth); SubscribeLocalEvent(OnHandleState); SubscribeLocalEvent(OnUnequipped); SubscribeLocalEvent(OnMapInit); } private void OnMapInit(EntityUid uid, StealthClothingComponent component, MapInitEvent args) { _actionContainer.EnsureAction(uid, ref component.ToggleActionEntity, component.ToggleAction); Dirty(uid, component); } /// /// Sets the clothing's stealth effect for the user. /// /// True if it was changed, false otherwise public bool SetEnabled(EntityUid uid, EntityUid user, bool enabled, StealthClothingComponent? comp = null) { if (!Resolve(uid, ref comp) || comp.Enabled == enabled) return false; // TODO remove this when clothing unequip on delete is less sus // prevent debug assert when ending round and its disabled if (MetaData(user).EntityLifeStage >= EntityLifeStage.Terminating) return false; comp.Enabled = enabled; Dirty(uid, comp); var stealth = EnsureComp(user); // slightly visible, but doesn't change when moving so it's ok var visibility = enabled ? stealth.MinVisibility + comp.Visibility : stealth.MaxVisibility; _stealth.SetVisibility(user, visibility, stealth); _stealth.SetEnabled(user, enabled, stealth); return true; } /// /// Raise then add the toggle action if it was not cancelled. /// private void OnGetItemActions(EntityUid uid, StealthClothingComponent comp, GetItemActionsEvent args) { var ev = new AddStealthActionEvent(args.User); RaiseLocalEvent(uid, ev); if (ev.Cancelled) return; args.AddAction(ref comp.ToggleActionEntity, comp.ToggleAction); } /// /// Raises if enabling. /// private void OnToggleStealth(EntityUid uid, StealthClothingComponent comp, ToggleStealthEvent args) { args.Handled = true; var user = args.Performer; if (comp.Enabled) { SetEnabled(uid, user, false, comp); return; } var ev = new AttemptStealthEvent(user); RaiseLocalEvent(uid, ev); if (ev.Cancelled) return; SetEnabled(uid, user, true, comp); } /// /// Calls when server sends new state. /// private void OnHandleState(EntityUid uid, StealthClothingComponent comp, ref AfterAutoHandleStateEvent args) { // SetEnabled checks if it is the same, so change it to before state was received from the server var enabled = comp.Enabled; comp.Enabled = !enabled; var user = Transform(uid).ParentUid; SetEnabled(uid, user, enabled, comp); } /// /// Force unstealths the user, doesnt remove StealthComponent since other things might use it /// private void OnUnequipped(EntityUid uid, StealthClothingComponent comp, GotUnequippedEvent args) { SetEnabled(uid, args.Equipee, false, comp); } } /// /// Raised on the stealth clothing when attempting to add an action. /// public sealed class AddStealthActionEvent : CancellableEntityEventArgs { /// /// User that equipped the stealth clothing. /// public EntityUid User; public AddStealthActionEvent(EntityUid user) { User = user; } } /// /// Raised on the stealth clothing when the user is attemping to enable it. /// public sealed class AttemptStealthEvent : CancellableEntityEventArgs { /// /// User that is attempting to enable the stealth clothing. /// public EntityUid User; public AttemptStealthEvent(EntityUid user) { User = user; } }