SharedStrippableSystem.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677
  1. using System.Linq;
  2. using Content.Shared.Administration.Logs;
  3. using Content.Shared.CombatMode;
  4. using Content.Shared.Cuffs;
  5. using Content.Shared.Cuffs.Components;
  6. using Content.Shared.Database;
  7. using Content.Shared.DoAfter;
  8. using Content.Shared.DragDrop;
  9. using Content.Shared.Hands.Components;
  10. using Content.Shared.Hands.EntitySystems;
  11. using Content.Shared.IdentityManagement;
  12. using Content.Shared.Interaction;
  13. using Content.Shared.Interaction.Components;
  14. using Content.Shared.Interaction.Events;
  15. using Content.Shared.Inventory;
  16. using Content.Shared.Inventory.VirtualItem;
  17. using Content.Shared.Popups;
  18. using Content.Shared.Strip.Components;
  19. using Content.Shared.Verbs;
  20. using Robust.Shared.Utility;
  21. namespace Content.Shared.Strip;
  22. public abstract class SharedStrippableSystem : EntitySystem
  23. {
  24. [Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
  25. [Dependency] private readonly InventorySystem _inventorySystem = default!;
  26. [Dependency] private readonly SharedCuffableSystem _cuffableSystem = default!;
  27. [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
  28. [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
  29. [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
  30. [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
  31. public override void Initialize()
  32. {
  33. base.Initialize();
  34. SubscribeLocalEvent<StrippableComponent, GetVerbsEvent<Verb>>(AddStripVerb);
  35. SubscribeLocalEvent<StrippableComponent, GetVerbsEvent<ExamineVerb>>(AddStripExamineVerb);
  36. // BUI
  37. SubscribeLocalEvent<StrippableComponent, StrippingSlotButtonPressed>(OnStripButtonPressed);
  38. // DoAfters
  39. SubscribeLocalEvent<HandsComponent, DoAfterAttemptEvent<StrippableDoAfterEvent>>(OnStrippableDoAfterRunning);
  40. SubscribeLocalEvent<HandsComponent, StrippableDoAfterEvent>(OnStrippableDoAfterFinished);
  41. SubscribeLocalEvent<StrippingComponent, CanDropTargetEvent>(OnCanDropOn);
  42. SubscribeLocalEvent<StrippableComponent, CanDropDraggedEvent>(OnCanDrop);
  43. SubscribeLocalEvent<StrippableComponent, DragDropDraggedEvent>(OnDragDrop);
  44. SubscribeLocalEvent<StrippableComponent, ActivateInWorldEvent>(OnActivateInWorld);
  45. }
  46. private void AddStripVerb(EntityUid uid, StrippableComponent component, GetVerbsEvent<Verb> args)
  47. {
  48. if (args.Hands == null || !args.CanAccess || !args.CanInteract || args.Target == args.User)
  49. return;
  50. Verb verb = new()
  51. {
  52. Text = Loc.GetString("strip-verb-get-data-text"),
  53. Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/outfit.svg.192dpi.png")),
  54. Act = () => TryOpenStrippingUi(args.User, (uid, component), true),
  55. };
  56. args.Verbs.Add(verb);
  57. }
  58. private void AddStripExamineVerb(EntityUid uid, StrippableComponent component, GetVerbsEvent<ExamineVerb> args)
  59. {
  60. if (args.Hands == null || !args.CanAccess || !args.CanInteract || args.Target == args.User)
  61. return;
  62. ExamineVerb verb = new()
  63. {
  64. Text = Loc.GetString("strip-verb-get-data-text"),
  65. Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/outfit.svg.192dpi.png")),
  66. Act = () => TryOpenStrippingUi(args.User, (uid, component), true),
  67. Category = VerbCategory.Examine,
  68. };
  69. args.Verbs.Add(verb);
  70. }
  71. private void OnStripButtonPressed(Entity<StrippableComponent> strippable, ref StrippingSlotButtonPressed args)
  72. {
  73. if (args.Actor is not { Valid: true } user ||
  74. !TryComp<HandsComponent>(user, out var userHands))
  75. return;
  76. if (args.IsHand)
  77. {
  78. StripHand((user, userHands), (strippable.Owner, null), args.Slot, strippable);
  79. return;
  80. }
  81. if (!TryComp<InventoryComponent>(strippable, out var inventory))
  82. return;
  83. var hasEnt = _inventorySystem.TryGetSlotEntity(strippable, args.Slot, out var held, inventory);
  84. if (userHands.ActiveHandEntity != null && !hasEnt)
  85. StartStripInsertInventory((user, userHands), strippable.Owner, userHands.ActiveHandEntity.Value, args.Slot);
  86. else if (hasEnt)
  87. StartStripRemoveInventory(user, strippable.Owner, held!.Value, args.Slot);
  88. }
  89. private void StripHand(
  90. Entity<HandsComponent?> user,
  91. Entity<HandsComponent?> target,
  92. string handId,
  93. StrippableComponent? targetStrippable)
  94. {
  95. if (!Resolve(user, ref user.Comp) ||
  96. !Resolve(target, ref target.Comp) ||
  97. !Resolve(target, ref targetStrippable))
  98. return;
  99. if (!target.Comp.CanBeStripped)
  100. return;
  101. if (!_handsSystem.TryGetHand(target.Owner, handId, out var handSlot))
  102. return;
  103. // Is the target a handcuff?
  104. if (TryComp<VirtualItemComponent>(handSlot.HeldEntity, out var virtualItem) &&
  105. TryComp<CuffableComponent>(target.Owner, out var cuffable) &&
  106. _cuffableSystem.GetAllCuffs(cuffable).Contains(virtualItem.BlockingEntity))
  107. {
  108. _cuffableSystem.TryUncuff(target.Owner, user, virtualItem.BlockingEntity, cuffable);
  109. return;
  110. }
  111. if (user.Comp.ActiveHandEntity != null && handSlot.HeldEntity == null)
  112. StartStripInsertHand(user, target, user.Comp.ActiveHandEntity.Value, handId, targetStrippable);
  113. else if (handSlot.HeldEntity != null)
  114. StartStripRemoveHand(user, target, handSlot.HeldEntity.Value, handId, targetStrippable);
  115. }
  116. /// <summary>
  117. /// Checks whether the item is in a user's active hand and whether it can be inserted into the inventory slot.
  118. /// </summary>
  119. private bool CanStripInsertInventory(
  120. Entity<HandsComponent?> user,
  121. EntityUid target,
  122. EntityUid held,
  123. string slot)
  124. {
  125. if (!Resolve(user, ref user.Comp))
  126. return false;
  127. if (user.Comp.ActiveHand == null)
  128. return false;
  129. if (user.Comp.ActiveHandEntity == null)
  130. return false;
  131. if (user.Comp.ActiveHandEntity != held)
  132. return false;
  133. if (!_handsSystem.CanDropHeld(user, user.Comp.ActiveHand))
  134. {
  135. _popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop"));
  136. return false;
  137. }
  138. var targetIdentity = Identity.Entity(target, EntityManager);
  139. if (_inventorySystem.TryGetSlotEntity(target, slot, out _))
  140. {
  141. _popupSystem.PopupCursor(Loc.GetString("strippable-component-item-slot-occupied", ("owner", targetIdentity)));
  142. return false;
  143. }
  144. if (!_inventorySystem.CanEquip(user, target, held, slot, out _))
  145. {
  146. _popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-equip-message", ("owner", targetIdentity)));
  147. return false;
  148. }
  149. return true;
  150. }
  151. /// <summary>
  152. /// Begins a DoAfter to insert the item in the user's active hand into the inventory slot.
  153. /// </summary>
  154. private void StartStripInsertInventory(
  155. Entity<HandsComponent?> user,
  156. EntityUid target,
  157. EntityUid held,
  158. string slot)
  159. {
  160. if (!Resolve(user, ref user.Comp))
  161. return;
  162. if (!CanStripInsertInventory(user, target, held, slot))
  163. return;
  164. if (!_inventorySystem.TryGetSlot(target, slot, out var slotDef))
  165. {
  166. Log.Error($"{ToPrettyString(user)} attempted to place an item in a non-existent inventory slot ({slot}) on {ToPrettyString(target)}");
  167. return;
  168. }
  169. var (time, stealth) = GetStripTimeModifiers(user, target, held, slotDef.StripTime);
  170. if (!stealth)
  171. _popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-insert", ("user", Identity.Entity(user, EntityManager)), ("item", user.Comp.ActiveHandEntity!.Value)), target, target, PopupType.Large);
  172. var prefix = stealth ? "stealthily " : "";
  173. _adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}place the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s {slot} slot");
  174. var doAfterArgs = new DoAfterArgs(EntityManager, user, time, new StrippableDoAfterEvent(true, true, slot), user, target, held)
  175. {
  176. Hidden = stealth,
  177. AttemptFrequency = AttemptFrequency.EveryTick,
  178. BreakOnDamage = true,
  179. BreakOnMove = true,
  180. NeedHand = true,
  181. DuplicateCondition = DuplicateConditions.SameTool
  182. };
  183. _doAfterSystem.TryStartDoAfter(doAfterArgs);
  184. }
  185. /// <summary>
  186. /// Inserts the item in the user's active hand into the inventory slot.
  187. /// </summary>
  188. private void StripInsertInventory(
  189. Entity<HandsComponent?> user,
  190. EntityUid target,
  191. EntityUid held,
  192. string slot)
  193. {
  194. if (!Resolve(user, ref user.Comp))
  195. return;
  196. if (!CanStripInsertInventory(user, target, held, slot))
  197. return;
  198. if (!_handsSystem.TryDrop(user, handsComp: user.Comp))
  199. return;
  200. _inventorySystem.TryEquip(user, target, held, slot);
  201. _adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):actor} has placed the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s {slot} slot");
  202. }
  203. /// <summary>
  204. /// Checks whether the item can be removed from the target's inventory.
  205. /// </summary>
  206. private bool CanStripRemoveInventory(
  207. EntityUid user,
  208. EntityUid target,
  209. EntityUid item,
  210. string slot)
  211. {
  212. if (!_inventorySystem.TryGetSlotEntity(target, slot, out var slotItem))
  213. {
  214. _popupSystem.PopupCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", Identity.Entity(target, EntityManager))));
  215. return false;
  216. }
  217. if (slotItem != item)
  218. return false;
  219. if (!_inventorySystem.CanUnequip(user, target, slot, out var reason))
  220. {
  221. _popupSystem.PopupCursor(Loc.GetString(reason));
  222. return false;
  223. }
  224. return true;
  225. }
  226. /// <summary>
  227. /// Begins a DoAfter to remove the item from the target's inventory and insert it in the user's active hand.
  228. /// </summary>
  229. private void StartStripRemoveInventory(
  230. EntityUid user,
  231. EntityUid target,
  232. EntityUid item,
  233. string slot)
  234. {
  235. if (!CanStripRemoveInventory(user, target, item, slot))
  236. return;
  237. if (!_inventorySystem.TryGetSlot(target, slot, out var slotDef))
  238. {
  239. Log.Error($"{ToPrettyString(user)} attempted to take an item from a non-existent inventory slot ({slot}) on {ToPrettyString(target)}");
  240. return;
  241. }
  242. var (time, stealth) = GetStripTimeModifiers(user, target, item, slotDef.StripTime);
  243. if (!stealth)
  244. {
  245. if (IsStripHidden(slotDef, user))
  246. _popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-hidden", ("slot", slot)), target, target, PopupType.Large);
  247. else
  248. _popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner", ("user", Identity.Entity(user, EntityManager)), ("item", item)), target, target, PopupType.Large);
  249. }
  250. var prefix = stealth ? "stealthily " : "";
  251. _adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}strip the item {ToPrettyString(item):item} from {ToPrettyString(target):target}'s {slot} slot");
  252. var doAfterArgs = new DoAfterArgs(EntityManager, user, time, new StrippableDoAfterEvent(false, true, slot), user, target, item)
  253. {
  254. Hidden = stealth,
  255. AttemptFrequency = AttemptFrequency.EveryTick,
  256. BreakOnDamage = true,
  257. BreakOnMove = true,
  258. NeedHand = true,
  259. BreakOnHandChange = false, // Allow simultaneously removing multiple items.
  260. DuplicateCondition = DuplicateConditions.SameTool
  261. };
  262. _doAfterSystem.TryStartDoAfter(doAfterArgs);
  263. }
  264. /// <summary>
  265. /// Removes the item from the target's inventory and inserts it in the user's active hand.
  266. /// </summary>
  267. private void StripRemoveInventory(
  268. EntityUid user,
  269. EntityUid target,
  270. EntityUid item,
  271. string slot,
  272. bool stealth)
  273. {
  274. if (!CanStripRemoveInventory(user, target, item, slot))
  275. return;
  276. if (!_inventorySystem.TryUnequip(user, target, slot))
  277. return;
  278. RaiseLocalEvent(item, new DroppedEvent(user), true); // Gas tank internals etc.
  279. _handsSystem.PickupOrDrop(user, item, animateUser: stealth, animate: !stealth);
  280. _adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):actor} has stripped the item {ToPrettyString(item):item} from {ToPrettyString(target):target}'s {slot} slot");
  281. }
  282. /// <summary>
  283. /// Checks whether the item in the user's active hand can be inserted into one of the target's hands.
  284. /// </summary>
  285. private bool CanStripInsertHand(
  286. Entity<HandsComponent?> user,
  287. Entity<HandsComponent?> target,
  288. EntityUid held,
  289. string handName)
  290. {
  291. if (!Resolve(user, ref user.Comp) ||
  292. !Resolve(target, ref target.Comp))
  293. return false;
  294. if (!target.Comp.CanBeStripped)
  295. return false;
  296. if (user.Comp.ActiveHand == null)
  297. return false;
  298. if (user.Comp.ActiveHandEntity == null)
  299. return false;
  300. if (user.Comp.ActiveHandEntity != held)
  301. return false;
  302. if (!_handsSystem.CanDropHeld(user, user.Comp.ActiveHand))
  303. {
  304. _popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop"));
  305. return false;
  306. }
  307. if (!_handsSystem.TryGetHand(target, handName, out var handSlot, target.Comp) ||
  308. !_handsSystem.CanPickupToHand(target, user.Comp.ActiveHandEntity.Value, handSlot, checkActionBlocker: false, target.Comp))
  309. {
  310. _popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-put-message", ("owner", Identity.Entity(target, EntityManager))));
  311. return false;
  312. }
  313. return true;
  314. }
  315. /// <summary>
  316. /// Begins a DoAfter to insert the item in the user's active hand into one of the target's hands.
  317. /// </summary>
  318. private void StartStripInsertHand(
  319. Entity<HandsComponent?> user,
  320. Entity<HandsComponent?> target,
  321. EntityUid held,
  322. string handName,
  323. StrippableComponent? targetStrippable = null)
  324. {
  325. if (!Resolve(user, ref user.Comp) ||
  326. !Resolve(target, ref target.Comp) ||
  327. !Resolve(target, ref targetStrippable))
  328. return;
  329. if (!CanStripInsertHand(user, target, held, handName))
  330. return;
  331. var (time, stealth) = GetStripTimeModifiers(user, target, null, targetStrippable.HandStripDelay);
  332. if (!stealth)
  333. _popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner-insert-hand", ("user", Identity.Entity(user, EntityManager)), ("item", user.Comp.ActiveHandEntity!.Value)), target, target, PopupType.Large);
  334. var prefix = stealth ? "stealthily " : "";
  335. _adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}place the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s hands");
  336. var doAfterArgs = new DoAfterArgs(EntityManager, user, time, new StrippableDoAfterEvent(true, false, handName), user, target, held)
  337. {
  338. Hidden = stealth,
  339. AttemptFrequency = AttemptFrequency.EveryTick,
  340. BreakOnDamage = true,
  341. BreakOnMove = true,
  342. NeedHand = true,
  343. DuplicateCondition = DuplicateConditions.SameTool
  344. };
  345. _doAfterSystem.TryStartDoAfter(doAfterArgs);
  346. }
  347. /// <summary>
  348. /// Places the item in the user's active hand into one of the target's hands.
  349. /// </summary>
  350. private void StripInsertHand(
  351. Entity<HandsComponent?> user,
  352. Entity<HandsComponent?> target,
  353. EntityUid held,
  354. string handName,
  355. bool stealth)
  356. {
  357. if (!Resolve(user, ref user.Comp) ||
  358. !Resolve(target, ref target.Comp))
  359. return;
  360. if (!CanStripInsertHand(user, target, held, handName))
  361. return;
  362. _handsSystem.TryDrop(user, checkActionBlocker: false, handsComp: user.Comp);
  363. _handsSystem.TryPickup(target, held, handName, checkActionBlocker: false, animateUser: stealth, animate: !stealth, handsComp: target.Comp);
  364. _adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):actor} has placed the item {ToPrettyString(held):item} in {ToPrettyString(target):target}'s hands");
  365. // Hand update will trigger strippable update.
  366. }
  367. /// <summary>
  368. /// Checks whether the item is in the target's hand and whether it can be dropped.
  369. /// </summary>
  370. private bool CanStripRemoveHand(
  371. EntityUid user,
  372. Entity<HandsComponent?> target,
  373. EntityUid item,
  374. string handName)
  375. {
  376. if (!Resolve(target, ref target.Comp))
  377. return false;
  378. if (!target.Comp.CanBeStripped)
  379. return false;
  380. if (!_handsSystem.TryGetHand(target, handName, out var handSlot, target.Comp))
  381. {
  382. _popupSystem.PopupCursor(Loc.GetString("strippable-component-item-slot-free-message", ("owner", Identity.Entity(target, EntityManager))));
  383. return false;
  384. }
  385. if (HasComp<VirtualItemComponent>(handSlot.HeldEntity))
  386. return false;
  387. if (handSlot.HeldEntity == null)
  388. return false;
  389. if (handSlot.HeldEntity != item)
  390. return false;
  391. if (!_handsSystem.CanDropHeld(target, handSlot, false))
  392. {
  393. _popupSystem.PopupCursor(Loc.GetString("strippable-component-cannot-drop-message", ("owner", Identity.Entity(target, EntityManager))));
  394. return false;
  395. }
  396. return true;
  397. }
  398. /// <summary>
  399. /// Begins a DoAfter to remove the item from the target's hand and insert it in the user's active hand.
  400. /// </summary>
  401. private void StartStripRemoveHand(
  402. Entity<HandsComponent?> user,
  403. Entity<HandsComponent?> target,
  404. EntityUid item,
  405. string handName,
  406. StrippableComponent? targetStrippable = null)
  407. {
  408. if (!Resolve(user, ref user.Comp) ||
  409. !Resolve(target, ref target.Comp) ||
  410. !Resolve(target, ref targetStrippable))
  411. return;
  412. if (!CanStripRemoveHand(user, target, item, handName))
  413. return;
  414. var (time, stealth) = GetStripTimeModifiers(user, target, null, targetStrippable.HandStripDelay);
  415. if (!stealth)
  416. _popupSystem.PopupEntity(Loc.GetString("strippable-component-alert-owner", ("user", Identity.Entity(user, EntityManager)), ("item", item)), target, target);
  417. var prefix = stealth ? "stealthily " : "";
  418. _adminLogger.Add(LogType.Stripping, LogImpact.Low, $"{ToPrettyString(user):actor} is trying to {prefix}strip the item {ToPrettyString(item):item} from {ToPrettyString(target):target}'s hands");
  419. var doAfterArgs = new DoAfterArgs(EntityManager, user, time, new StrippableDoAfterEvent(false, false, handName), user, target, item)
  420. {
  421. Hidden = stealth,
  422. AttemptFrequency = AttemptFrequency.EveryTick,
  423. BreakOnDamage = true,
  424. BreakOnMove = true,
  425. NeedHand = true,
  426. BreakOnHandChange = false, // Allow simultaneously removing multiple items.
  427. DuplicateCondition = DuplicateConditions.SameTool
  428. };
  429. _doAfterSystem.TryStartDoAfter(doAfterArgs);
  430. }
  431. /// <summary>
  432. /// Takes the item from the target's hand and inserts it in the user's active hand.
  433. /// </summary>
  434. private void StripRemoveHand(
  435. Entity<HandsComponent?> user,
  436. Entity<HandsComponent?> target,
  437. EntityUid item,
  438. string handName,
  439. bool stealth)
  440. {
  441. if (!Resolve(user, ref user.Comp) ||
  442. !Resolve(target, ref target.Comp))
  443. return;
  444. if (!CanStripRemoveHand(user, target, item, handName))
  445. return;
  446. _handsSystem.TryDrop(target, item, checkActionBlocker: false, handsComp: target.Comp);
  447. _handsSystem.PickupOrDrop(user, item, animateUser: stealth, animate: !stealth, handsComp: user.Comp);
  448. _adminLogger.Add(LogType.Stripping, LogImpact.Medium, $"{ToPrettyString(user):actor} has stripped the item {ToPrettyString(item):item} from {ToPrettyString(target):target}'s hands");
  449. // Hand update will trigger strippable update.
  450. }
  451. private void OnStrippableDoAfterRunning(Entity<HandsComponent> entity, ref DoAfterAttemptEvent<StrippableDoAfterEvent> ev)
  452. {
  453. var args = ev.DoAfter.Args;
  454. DebugTools.Assert(entity.Owner == args.User);
  455. DebugTools.Assert(args.Target != null);
  456. DebugTools.Assert(args.Used != null);
  457. DebugTools.Assert(ev.Event.SlotOrHandName != null);
  458. if (ev.Event.InventoryOrHand)
  459. {
  460. if ( ev.Event.InsertOrRemove && !CanStripInsertInventory((entity.Owner, entity.Comp), args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName) ||
  461. !ev.Event.InsertOrRemove && !CanStripRemoveInventory(entity.Owner, args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName))
  462. ev.Cancel();
  463. }
  464. else
  465. {
  466. if ( ev.Event.InsertOrRemove && !CanStripInsertHand((entity.Owner, entity.Comp), args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName) ||
  467. !ev.Event.InsertOrRemove && !CanStripRemoveHand(entity.Owner, args.Target.Value, args.Used.Value, ev.Event.SlotOrHandName))
  468. ev.Cancel();
  469. }
  470. }
  471. private void OnStrippableDoAfterFinished(Entity<HandsComponent> entity, ref StrippableDoAfterEvent ev)
  472. {
  473. if (ev.Cancelled)
  474. return;
  475. DebugTools.Assert(entity.Owner == ev.User);
  476. DebugTools.Assert(ev.Target != null);
  477. DebugTools.Assert(ev.Used != null);
  478. DebugTools.Assert(ev.SlotOrHandName != null);
  479. if (ev.InventoryOrHand)
  480. {
  481. if (ev.InsertOrRemove)
  482. StripInsertInventory((entity.Owner, entity.Comp), ev.Target.Value, ev.Used.Value, ev.SlotOrHandName);
  483. else
  484. StripRemoveInventory(entity.Owner, ev.Target.Value, ev.Used.Value, ev.SlotOrHandName, ev.Args.Hidden);
  485. }
  486. else
  487. {
  488. if (ev.InsertOrRemove)
  489. StripInsertHand((entity.Owner, entity.Comp), ev.Target.Value, ev.Used.Value, ev.SlotOrHandName, ev.Args.Hidden);
  490. else
  491. StripRemoveHand((entity.Owner, entity.Comp), ev.Target.Value, ev.Used.Value, ev.SlotOrHandName, ev.Args.Hidden);
  492. }
  493. }
  494. private void OnActivateInWorld(EntityUid uid, StrippableComponent component, ActivateInWorldEvent args)
  495. {
  496. if (args.Handled || !args.Complex || args.Target == args.User)
  497. return;
  498. if (TryOpenStrippingUi(args.User, (uid, component)))
  499. args.Handled = true;
  500. }
  501. /// <summary>
  502. /// Modify the strip time via events. Raised directed at the item being stripped, the player stripping someone and the player being stripped.
  503. /// </summary>
  504. public (TimeSpan Time, bool Stealth) GetStripTimeModifiers(EntityUid user, EntityUid targetPlayer, EntityUid? targetItem, TimeSpan initialTime)
  505. {
  506. var itemEv = new BeforeItemStrippedEvent(initialTime, false);
  507. if (targetItem != null)
  508. RaiseLocalEvent(targetItem.Value, ref itemEv);
  509. var userEv = new BeforeStripEvent(itemEv.Time, itemEv.Stealth);
  510. RaiseLocalEvent(user, ref userEv);
  511. var targetEv = new BeforeGettingStrippedEvent(userEv.Time, userEv.Stealth);
  512. RaiseLocalEvent(targetPlayer, ref targetEv);
  513. return (targetEv.Time, targetEv.Stealth);
  514. }
  515. private void OnDragDrop(EntityUid uid, StrippableComponent component, ref DragDropDraggedEvent args)
  516. {
  517. // If the user drags a strippable thing onto themselves.
  518. if (args.Handled || args.Target != args.User)
  519. return;
  520. if (TryOpenStrippingUi(args.User, (uid, component)))
  521. args.Handled = true;
  522. }
  523. public bool TryOpenStrippingUi(EntityUid user, Entity<StrippableComponent> target, bool openInCombat = false)
  524. {
  525. if (!openInCombat && TryComp<CombatModeComponent>(user, out var mode) && mode.IsInCombatMode)
  526. return false;
  527. if (!HasComp<StrippingComponent>(user))
  528. return false;
  529. _ui.OpenUi(target.Owner, StrippingUiKey.Key, user);
  530. return true;
  531. }
  532. private void OnCanDropOn(EntityUid uid, StrippingComponent component, ref CanDropTargetEvent args)
  533. {
  534. var val = uid == args.User &&
  535. HasComp<StrippableComponent>(args.Dragged) &&
  536. HasComp<HandsComponent>(args.User) &&
  537. HasComp<StrippingComponent>(args.User);
  538. args.Handled |= val;
  539. args.CanDrop |= val;
  540. }
  541. private void OnCanDrop(EntityUid uid, StrippableComponent component, ref CanDropDraggedEvent args)
  542. {
  543. args.CanDrop |= args.Target == args.User &&
  544. HasComp<StrippingComponent>(args.User) &&
  545. HasComp<HandsComponent>(args.User);
  546. if (args.CanDrop)
  547. args.Handled = true;
  548. }
  549. public bool IsStripHidden(SlotDefinition definition, EntityUid? viewer)
  550. {
  551. if (!definition.StripHidden)
  552. return false;
  553. if (viewer == null)
  554. return true;
  555. return !HasComp<BypassInteractionChecksComponent>(viewer);
  556. }
  557. }