CatStation/Content.Shared/Clothing/EntitySystems/FleetingClothingSystem.cs
Whatstone a88d2c5164
Cleanup: Resolve remaining PopupSystem warnings in Content.Shared (#44685)
* PopupClient to PopupEntity/Coordinates

* PopupPredictedX to PopupEntity/Cursor/Coordinates

* The rest of the damn owl

* nullability fixes

* Buckle cleanup

* cleanup, address PR comments

* ExecutionSystem: one popup method

* correct my filthy lies

* Apply most of ajcm's suggestions

* return user to SharedItemRecallSystem.cs

* Fix lingering Ensnarable broadcast popups

* targeted drain popups

* fix lingering untargeted Guardian popups

I read the comment on GuardianHostRecall.  Oops.

* target stethoscope popups

* fix the PDA vibration popup

* Fix target for encryption key removal popup

* anomaly scanner: args.User, not uid

* disposal unit comment, bcast pda vibration popup
2026-07-16 21:54:33 +00:00

117 lines
5.1 KiB
C#

using Content.Shared.Clothing.Components;
using Content.Shared.Destructible;
using Content.Shared.Examine;
using Content.Shared.Inventory.Events;
using Content.Shared.Popups;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Network;
using Robust.Shared.Timing;
namespace Content.Shared.Clothing.EntitySystems;
public sealed partial class FleetingClothingSystem : EntitySystem
{
[Dependency] private IGameTiming _timing = default!;
[Dependency] private INetManager _net = default!;
[Dependency] private SharedAudioSystem _audio = default!;
[Dependency] private SharedPopupSystem _popup = default!;
[Dependency] private ClothingSystem _clothing = default!;
[Dependency] private SharedDestructibleSystem _destructibleSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<FleetingClothingComponent, ExaminedEvent>(OnExamine);
SubscribeLocalEvent<FleetingClothingComponent, BeforeGettingUnequippedEvent>(OnBeforeGettingUnequipped);
SubscribeLocalEvent<FleetingClothingComponent, GotUnequippedEvent>(OnGotUnequipped);
}
private void OnExamine(Entity<FleetingClothingComponent> ent, ref ExaminedEvent args)
{
if (!args.IsInDetailsRange)
return;
string? examineText = null;
// check if the item is clothing, is currently equipped and if the wearer is the examiner
if (_clothing.IsEquipped(ent.Owner) && Transform(ent.Owner).ParentUid == args.Examiner)
{
// text to show to the wearer only
if (ent.Comp.ExamineWearer != null)
examineText = Loc.GetString(ent.Comp.ExamineWearer);
}
else
{
// text to show to everyone else
if (ent.Comp.ExamineOthers != null)
examineText = Loc.GetString(ent.Comp.ExamineOthers);
}
if (!string.IsNullOrEmpty(examineText))
args.PushMarkup(examineText);
}
// Raised before the item is being unequipped.
// We have to use QueueDel instead of Del because directly deleting the entity while an event is being raised on it will cause errors.
// We can't do this in.GotUnequippedEvent because container events don't include the user.
private void OnBeforeGettingUnequipped(Entity<FleetingClothingComponent> ent, ref BeforeGettingUnequippedEvent args)
{
if (ent.Comp.DestroyOnUnequip)
_destructibleSystem.DestroyEntity(ent.Owner); // Empty containers first.
else
PredictedQueueDel(ent.Owner); // Poof!
// Use coords because the entity will be deleted.
var coords = Transform(ent).Coordinates;
if (ent.Comp.PlaySoundOnSelfUnequip || args.User != args.EquipTarget)
_audio.PlayPredicted(ent.Comp.RemovedSound, coords, args.User);
if (args.User == args.EquipTarget)
{
// If the wearer removes the item themselves show specific messages to them and others.
string? selfMessage = null;
string? othersMessage = null;
if (ent.Comp.SelfUnquipPopupWearer != null)
selfMessage = Loc.GetString(ent.Comp.SelfUnquipPopupWearer, ("item", ent.Owner));
if (ent.Comp.SelfUnquipPopupOthers != null)
othersMessage = Loc.GetString(ent.Comp.SelfUnquipPopupOthers, ("item", ent.Owner));
// Use the wearer for the popup location because the item item itself will get deleted.
_popup.PopupEntity(selfMessage, othersMessage, args.EquipTarget, args.User, PopupType.LargeCaution);
}
else
{
// Show the same popup message for everyone.
if (ent.Comp.RemovedPopup != null)
_popup.PopupEntity(Loc.GetString(ent.Comp.RemovedPopup, ("item", ent.Owner)), args.EquipTarget, PopupType.LargeCaution);
}
}
// In case the item was somehow removed by any other means not using TryUnequip.
private void OnGotUnequipped(Entity<FleetingClothingComponent> ent, ref GotUnequippedEvent args)
{
if (_timing.ApplyingState)
return; // The results of the container change were already networked as part of the same game state.
if (Terminating(ent.Owner) || EntityManager.IsQueuedForDeletion(ent.Owner))
return; // Don't do the popups or sound twice.
if (Terminating(args.EquipTarget))
return; // Don't do anything if the item is removed due to the wearer getting deleted.
if (ent.Comp.DestroyOnUnequip)
_destructibleSystem.DestroyEntity(ent.Owner); // Empty containers first.
else
PredictedQueueDel(ent.Owner); // Poof!
// Can't predict the popup or sound without a user.
// TODO: Make the popup and sound API sane and remove this guard.
if (_net.IsClient)
return;
// Use the wearer for the popup location because the item item itself will get deleted.
_audio.PlayPvs(ent.Comp.RemovedSound, args.EquipTarget);
if (ent.Comp.RemovedPopup != null)
_popup.PopupEntity(Loc.GetString(ent.Comp.RemovedPopup, ("item", ent.Owner)), args.EquipTarget, PopupType.LargeCaution);
}
}