mirror of
https://github.com/DeltaV-Station/Delta-v.git
synced 2026-09-29 23:57:29 +00:00
* Reapply "Nubody Merge" (#5807) This reverts commitd62f7cef10. * Revert "Nubody Merge (#5670)" This reverts commitb465cfb3fd. * Revert "IPC fibers (#4968)" This reverts commitc2b42211f0. * Revert "IPC Polymorph fix (#4545)" This reverts commitfa03d8c655. * Revert "fix IPC not having humanoid emotes (#4445)" This reverts commit216fdf999f. * Revert "ipc heat/cold (#4306)" This reverts commitfafd5cf816. * Revert "ipc cables (#4283)" This reverts commit54958039a1. * Revert "feat: ipcs now have dynamic power draw based off movement speed (#3917)" This reverts commit71e8b7e64b. * Revert "fix ipcs not being able to disarm (#3374)" This reverts commit764c4dcf54. * Revert "make steel heal caustic for ipcs (#3206)" This reverts commit450ac82550. * Revert "add missing ipc survival boxes (#3065)" This reverts commit888c9cdb00. * Revert "make borgs use ipc repairing (#3064)" This reverts commitf77a49dd0b. * Revert "Update hud.yml to add ipcs to diagnostic hud (#3019)" This reverts commit7220643531. * Revert "Tweak IPCs (#2979)" This reverts commit98b47476de. * Revert "fix nukies ipc test fail (#2957)" This reverts commit4a1acbf863. * Revert "fix ipc nukies not getting comms (#2884)" This reverts commit8a2254770e. * Revert "fix ipc encryption key mispredict (#2879)" This reverts commitf08726efc1. * Revert "Merge pull request #2800 from Spielern/port-ipc" This reverts commit822fa4312c, reversing changes made tofe4c23c702. * Remove and Fix rest of IPC Hangons causing crashes * Integration test Errors * Expand migration file with removed IPC stuff * IPC Leg was found and migrated to naught * Revert "Merge pull request #2542 from deltanedas/shitmed-ops" This reverts commit83a8002922, reversing changes made tobaa89b0052. * Revert "Set up surgical augments infrastructure & add tool panel augments (#3059)" This reverts commita40f0be6fd. * Revert "Psionic Prosthetics (#4689)" This reverts commit2679bb238a. * Revert "Add surgical cleanliness and sanitation (#2847)" This reverts commit64565ba4f7. * Remove unremoved mentions * Revert "Add autoclave to easily sanitize large amounts of items and faster (#2933)" This reverts commitd0da90fa2e. * Shit Med Removed * Remove _ShitMed Folders and fix Errors in Console * Add OrganAnimalBrain into the mirgraiton file * Heads no Input Mover * Monke * Full migration file I guess * oops * shiv state fix * Restore accidental deleted recipe * Forgot one flt mention * Fix more issues * Comment out Damage Visual for motorkind for now * Motorkind... * Commenting stuff out for now * thought there'd be more of this to gut --------- Co-authored-by: Janet Blackquill <uhhadd@gmail.com>
567 lines
22 KiB
C#
567 lines
22 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using Content.Shared.Armor;
|
|
using Content.Shared.Clothing.Components;
|
|
using Content.Shared.DoAfter;
|
|
using Content.Shared.Hands;
|
|
using Content.Shared.Hands.Components;
|
|
using Content.Shared.Hands.EntitySystems;
|
|
using Content.Shared.Interaction;
|
|
using Content.Shared.Inventory.Events;
|
|
using Content.Shared.Item;
|
|
using Content.Shared.Movement.Systems;
|
|
using Content.Shared.Popups;
|
|
using Content.Shared.Strip;
|
|
using Content.Shared.Strip.Components;
|
|
using Content.Shared.Whitelist;
|
|
using Robust.Shared.Audio.Systems;
|
|
using Robust.Shared.Containers;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Content.Shared.Inventory;
|
|
|
|
public abstract partial class InventorySystem
|
|
{
|
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
|
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!;
|
|
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
|
[Dependency] private readonly SharedItemSystem _item = default!;
|
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
|
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
|
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
|
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
|
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
|
[Dependency] private readonly SharedStrippableSystem _strippable = default!;
|
|
|
|
private static readonly ProtoId<ItemSizePrototype> PocketableItemSize = "Small";
|
|
|
|
private void InitializeEquip()
|
|
{
|
|
//these events ensure that the client also gets its proper events raised when getting its containerstate updated
|
|
SubscribeLocalEvent<InventoryComponent, EntInsertedIntoContainerMessage>(OnEntInserted);
|
|
SubscribeLocalEvent<InventoryComponent, EntRemovedFromContainerMessage>(OnEntRemoved);
|
|
|
|
SubscribeAllEvent<UseSlotNetworkMessage>(OnUseSlot);
|
|
}
|
|
|
|
private void OnEntRemoved(EntityUid uid, InventoryComponent component, EntRemovedFromContainerMessage args)
|
|
{
|
|
if (!TryGetSlot(uid, args.Container.ID, out var slotDef, inventory: component))
|
|
return;
|
|
|
|
var unequippedEvent = new DidUnequipEvent(uid, args.Entity, slotDef);
|
|
RaiseLocalEvent(uid, unequippedEvent, true);
|
|
|
|
var gotUnequippedEvent = new GotUnequippedEvent(uid, args.Entity, slotDef);
|
|
RaiseLocalEvent(args.Entity, gotUnequippedEvent, true);
|
|
}
|
|
|
|
private void OnEntInserted(EntityUid uid, InventoryComponent component, EntInsertedIntoContainerMessage args)
|
|
{
|
|
if (!TryGetSlot(uid, args.Container.ID, out var slotDef, inventory: component))
|
|
return;
|
|
|
|
var equippedEvent = new DidEquipEvent(uid, args.Entity, slotDef);
|
|
RaiseLocalEvent(uid, equippedEvent, true);
|
|
|
|
var gotEquippedEvent = new GotEquippedEvent(uid, args.Entity, slotDef);
|
|
RaiseLocalEvent(args.Entity, gotEquippedEvent, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Will attempt to equip or unequip an item to/from the clicked slot. If the user clicked on an occupied slot
|
|
/// with some entity, will instead attempt to interact with this entity.
|
|
/// </summary>
|
|
private void OnUseSlot(UseSlotNetworkMessage ev, EntitySessionEventArgs eventArgs)
|
|
{
|
|
if (eventArgs.SenderSession.AttachedEntity is not { Valid: true } actor)
|
|
return;
|
|
|
|
if (!TryComp(actor, out InventoryComponent? inventory) || !TryComp<HandsComponent>(actor, out var hands))
|
|
return;
|
|
|
|
var held = _handsSystem.GetActiveItem((actor, hands));
|
|
TryGetSlotEntity(actor, ev.Slot, out var itemUid, inventory);
|
|
|
|
// attempt to perform some interaction
|
|
if (held != null && itemUid != null)
|
|
{
|
|
_interactionSystem.InteractUsing(actor, held.Value, itemUid.Value,
|
|
Transform(itemUid.Value).Coordinates);
|
|
return;
|
|
}
|
|
|
|
// unequip the item.
|
|
if (itemUid != null)
|
|
{
|
|
if (!TryUnequip(actor, ev.Slot, out var item, predicted: true, inventory: inventory, checkDoafter: true, triggerHandContact: true))
|
|
return;
|
|
|
|
_handsSystem.PickupOrDrop(actor, item.Value);
|
|
return;
|
|
}
|
|
|
|
// finally, just try to equip the held item.
|
|
if (held == null)
|
|
return;
|
|
|
|
// before we drop the item, check that it can be equipped in the first place.
|
|
if (!CanEquip(actor, held.Value, ev.Slot, out var reason))
|
|
{
|
|
_popup.PopupCursor(Loc.GetString(reason));
|
|
return;
|
|
}
|
|
|
|
if (!_handsSystem.CanDropHeld(actor, hands.ActiveHandId!, checkActionBlocker: false))
|
|
return;
|
|
|
|
RaiseLocalEvent(held.Value, new HandDeselectedEvent(actor));
|
|
|
|
TryEquip(actor, actor, held.Value, ev.Slot, predicted: true, inventory: inventory, force: true, checkDoafter: true, triggerHandContact: true);
|
|
}
|
|
|
|
public bool TryEquip(EntityUid uid, EntityUid itemUid, string slot, bool silent = false, bool force = false, bool predicted = false,
|
|
InventoryComponent? inventory = null, ClothingComponent? clothing = null, bool checkDoafter = false, bool triggerHandContact = false) =>
|
|
TryEquip(uid, uid, itemUid, slot, silent, force, predicted, inventory, clothing, checkDoafter, triggerHandContact);
|
|
|
|
public bool TryEquip(EntityUid actor, EntityUid target, EntityUid itemUid, string slot, bool silent = false, bool force = false, bool predicted = false,
|
|
InventoryComponent? inventory = null, ClothingComponent? clothing = null, bool checkDoafter = false, bool triggerHandContact = false)
|
|
{
|
|
if (!Resolve(target, ref inventory, false))
|
|
{
|
|
if(!silent)
|
|
_popup.PopupCursor(Loc.GetString("inventory-component-can-equip-cannot"));
|
|
return false;
|
|
}
|
|
|
|
// Not required to have, since pockets can take any item.
|
|
// CanEquip will still check, so we don't have to worry about it.
|
|
Resolve(itemUid, ref clothing, false);
|
|
|
|
if (!TryGetSlotContainer(target, slot, out var slotContainer, out var slotDefinition, inventory))
|
|
{
|
|
if(!silent)
|
|
_popup.PopupCursor(Loc.GetString("inventory-component-can-equip-cannot"));
|
|
return false;
|
|
}
|
|
|
|
if (!force && !CanEquip(actor, target, itemUid, slot, out var reason, slotDefinition, inventory, clothing))
|
|
{
|
|
if(!silent)
|
|
_popup.PopupCursor(Loc.GetString(reason));
|
|
return false;
|
|
}
|
|
|
|
if (checkDoafter &&
|
|
clothing != null &&
|
|
clothing.EquipDelay > TimeSpan.Zero &&
|
|
(clothing.Slots & slotDefinition.SlotFlags) != 0 &&
|
|
_containerSystem.CanInsert(itemUid, slotContainer))
|
|
{
|
|
var args = new DoAfterArgs(
|
|
EntityManager,
|
|
actor,
|
|
clothing.EquipDelay,
|
|
new ClothingEquipDoAfterEvent(slot),
|
|
itemUid,
|
|
target,
|
|
itemUid)
|
|
{
|
|
BreakOnMove = !clothing.EquipWhileMoving,
|
|
NeedHand = true,
|
|
};
|
|
|
|
_doAfter.TryStartDoAfter(args);
|
|
return false;
|
|
}
|
|
|
|
if (!_containerSystem.Insert(itemUid, slotContainer))
|
|
{
|
|
if(!silent)
|
|
_popup.PopupCursor(Loc.GetString("inventory-component-can-unequip-cannot"));
|
|
return false;
|
|
}
|
|
|
|
if (!silent && clothing != null)
|
|
{
|
|
_audio.PlayPredicted(clothing.EquipSound, target, actor);
|
|
}
|
|
|
|
// If new gloves are equipped, trigger OnContactInteraction for held items
|
|
if (triggerHandContact && !((slotDefinition.SlotFlags & SlotFlags.GLOVES) == 0))
|
|
TriggerHandContactInteraction(target);
|
|
|
|
_movementSpeed.RefreshMovementSpeedModifiers(target);
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool CanAccess(EntityUid actor, EntityUid target, EntityUid itemUid)
|
|
{
|
|
// if the item is something like a hardsuit helmet, it may be contained within the hardsuit.
|
|
// in that case, we check accesibility for the owner-entity instead.
|
|
if (TryComp(itemUid, out AttachedClothingComponent? attachedComp))
|
|
itemUid = attachedComp.AttachedUid;
|
|
|
|
// Can the actor reach the target?
|
|
if (actor != target && !(_interactionSystem.InRangeUnobstructed(actor, target) && _containerSystem.IsInSameOrParentContainer(actor, target)))
|
|
return false;
|
|
|
|
// Can the actor reach the item?
|
|
if (_interactionSystem.InRangeAndAccessible(actor, itemUid))
|
|
return true;
|
|
|
|
// Is the actor currently stripping the target? Here we could check if the actor has the stripping UI open, but
|
|
// that requires server/client specific code.
|
|
// Uhhh TODO, fix this. This doesn't even fucking check if the target item is IN the targets inventory.
|
|
return actor != target &&
|
|
HasComp<StrippableComponent>(target) &&
|
|
HasComp<StrippingComponent>(actor) &&
|
|
HasComp<HandsComponent>(actor);
|
|
}
|
|
|
|
public bool CanEquip(EntityUid uid, EntityUid itemUid, string slot, [NotNullWhen(false)] out string? reason,
|
|
SlotDefinition? slotDefinition = null, InventoryComponent? inventory = null,
|
|
ClothingComponent? clothing = null, ItemComponent? item = null) =>
|
|
CanEquip(uid, uid, itemUid, slot, out reason, slotDefinition, inventory, clothing, item);
|
|
|
|
public bool CanEquip(EntityUid actor, EntityUid target, EntityUid itemUid, string slot, [NotNullWhen(false)] out string? reason, SlotDefinition? slotDefinition = null,
|
|
InventoryComponent? inventory = null, ClothingComponent? clothing = null, ItemComponent? item = null)
|
|
{
|
|
reason = "inventory-component-can-equip-cannot";
|
|
if (!Resolve(target, ref inventory, false))
|
|
return false;
|
|
|
|
Resolve(itemUid, ref clothing, ref item, false);
|
|
|
|
if (slotDefinition == null && !TryGetSlot(target, slot, out slotDefinition, inventory: inventory))
|
|
return false;
|
|
|
|
DebugTools.Assert(slotDefinition.Name == slot);
|
|
if (slotDefinition.DependsOn != null)
|
|
{
|
|
if (!TryGetSlotEntity(target, slotDefinition.DependsOn, out EntityUid? slotEntity, inventory))
|
|
return false;
|
|
|
|
if (slotDefinition.DependsOnComponents is { } componentRegistry)
|
|
{
|
|
foreach (var (_, entry) in componentRegistry)
|
|
{
|
|
if (!HasComp(slotEntity, entry.Component.GetType()))
|
|
return false;
|
|
|
|
continue; // DeltaV - ignore suit storage whitelist
|
|
if (TryComp<AllowSuitStorageComponent>(slotEntity, out var comp) &&
|
|
_whitelistSystem.IsWhitelistFailOrNull(comp.Whitelist, itemUid))
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
var fittingInPocket = slotDefinition.SlotFlags.HasFlag(SlotFlags.POCKET) &&
|
|
item != null &&
|
|
_item.GetSizePrototype(item.Size) <= _item.GetSizePrototype(PocketableItemSize);
|
|
if (clothing == null && !fittingInPocket
|
|
|| clothing != null && !clothing.Slots.HasFlag(slotDefinition.SlotFlags) && !fittingInPocket)
|
|
{
|
|
reason = "inventory-component-can-equip-does-not-fit";
|
|
return false;
|
|
}
|
|
|
|
if (!CanAccess(actor, target, itemUid))
|
|
{
|
|
reason = "interaction-system-user-interaction-cannot-reach";
|
|
return false;
|
|
}
|
|
|
|
if (_whitelistSystem.IsWhitelistFail(slotDefinition.Whitelist, itemUid) ||
|
|
_whitelistSystem.IsWhitelistPass(slotDefinition.Blacklist, itemUid))
|
|
{
|
|
reason = "inventory-component-can-equip-does-not-fit";
|
|
return false;
|
|
}
|
|
|
|
var attemptEvent = new IsEquippingAttemptEvent(actor, target, itemUid, slotDefinition);
|
|
RaiseLocalEvent(actor, attemptEvent, true);
|
|
|
|
if (attemptEvent.Cancelled)
|
|
{
|
|
reason = attemptEvent.Reason ?? reason;
|
|
return false;
|
|
}
|
|
|
|
var targetAttemptEvent = new IsEquippingTargetAttemptEvent(actor, target, itemUid, slotDefinition);
|
|
RaiseLocalEvent(target, targetAttemptEvent, true);
|
|
|
|
if (targetAttemptEvent.Cancelled)
|
|
{
|
|
reason = targetAttemptEvent.Reason ?? reason;
|
|
return false;
|
|
}
|
|
|
|
var itemAttemptEvent = new BeingEquippedAttemptEvent(actor, target, itemUid, slotDefinition);
|
|
RaiseLocalEvent(itemUid, itemAttemptEvent, true);
|
|
if (itemAttemptEvent.Cancelled)
|
|
{
|
|
reason = itemAttemptEvent.Reason ?? reason;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public bool TryUnequip(
|
|
EntityUid uid,
|
|
string slot,
|
|
bool silent = false,
|
|
bool force = false,
|
|
bool predicted = false,
|
|
InventoryComponent? inventory = null,
|
|
ClothingComponent? clothing = null,
|
|
bool reparent = true,
|
|
bool checkDoafter = false,
|
|
bool triggerHandContact = false)
|
|
{
|
|
return TryUnequip(uid, uid, slot, silent, force, predicted, inventory, clothing, reparent, checkDoafter, triggerHandContact);
|
|
}
|
|
|
|
public bool TryUnequip(
|
|
EntityUid actor,
|
|
EntityUid target,
|
|
string slot,
|
|
bool silent = false,
|
|
bool force = false,
|
|
bool predicted = false,
|
|
InventoryComponent? inventory = null,
|
|
ClothingComponent? clothing = null,
|
|
bool reparent = true,
|
|
bool checkDoafter = false,
|
|
bool triggerHandContact = false)
|
|
{
|
|
return TryUnequip(actor, target, slot, out _, silent, force, predicted, inventory, clothing, reparent, checkDoafter, triggerHandContact);
|
|
}
|
|
|
|
public bool TryUnequip(
|
|
EntityUid uid,
|
|
string slot,
|
|
[NotNullWhen(true)] out EntityUid? removedItem,
|
|
bool silent = false,
|
|
bool force = false,
|
|
bool predicted = false,
|
|
InventoryComponent? inventory = null,
|
|
ClothingComponent? clothing = null,
|
|
bool reparent = true,
|
|
bool checkDoafter = false,
|
|
bool triggerHandContact = false)
|
|
{
|
|
return TryUnequip(uid, uid, slot, out removedItem, silent, force, predicted, inventory, clothing, reparent, checkDoafter, triggerHandContact);
|
|
}
|
|
|
|
public bool TryUnequip(
|
|
EntityUid actor,
|
|
EntityUid target,
|
|
string slot,
|
|
[NotNullWhen(true)] out EntityUid? removedItem,
|
|
bool silent = false,
|
|
bool force = false,
|
|
bool predicted = false,
|
|
InventoryComponent? inventory = null,
|
|
ClothingComponent? clothing = null,
|
|
bool reparent = true,
|
|
bool checkDoafter = false,
|
|
bool triggerHandContact = false)
|
|
{
|
|
var itemsDropped = 0;
|
|
return TryUnequip(actor, target, slot, out removedItem, ref itemsDropped,
|
|
silent, force, predicted, inventory, clothing, reparent, checkDoafter);
|
|
}
|
|
|
|
private bool TryUnequip(
|
|
EntityUid actor,
|
|
EntityUid target,
|
|
string slot,
|
|
[NotNullWhen(true)] out EntityUid? removedItem,
|
|
ref int itemsDropped,
|
|
bool silent = false,
|
|
bool force = false,
|
|
bool predicted = false,
|
|
InventoryComponent? inventory = null,
|
|
ClothingComponent? clothing = null,
|
|
bool reparent = true,
|
|
bool checkDoafter = false,
|
|
bool triggerHandContact = false)
|
|
{
|
|
removedItem = null;
|
|
|
|
if (TerminatingOrDeleted(target))
|
|
return false;
|
|
|
|
if (!Resolve(target, ref inventory, false))
|
|
{
|
|
if(!silent)
|
|
_popup.PopupCursor(Loc.GetString("inventory-component-can-unequip-cannot"));
|
|
return false;
|
|
}
|
|
|
|
if (!TryGetSlotContainer(target, slot, out var slotContainer, out var slotDefinition, inventory))
|
|
{
|
|
if(!silent)
|
|
_popup.PopupCursor(Loc.GetString("inventory-component-can-unequip-cannot"));
|
|
return false;
|
|
}
|
|
|
|
removedItem = slotContainer.ContainedEntity;
|
|
|
|
if (!removedItem.HasValue || TerminatingOrDeleted(removedItem.Value))
|
|
return false;
|
|
|
|
if (!force && !CanUnequip(actor, target, slot, out var reason, slotContainer, slotDefinition, inventory))
|
|
{
|
|
if(!silent)
|
|
_popup.PopupCursor(Loc.GetString(reason));
|
|
return false;
|
|
}
|
|
|
|
//we need to do this to make sure we are 100% removing this entity, since we are now dropping dependant slots
|
|
if (!force && !_containerSystem.CanRemove(removedItem.Value, slotContainer))
|
|
return false;
|
|
|
|
if (checkDoafter &&
|
|
Resolve(removedItem.Value, ref clothing, false) &&
|
|
(clothing.Slots & slotDefinition.SlotFlags) != 0 &&
|
|
clothing.UnequipDelay > TimeSpan.Zero)
|
|
{
|
|
var args = new DoAfterArgs(
|
|
EntityManager,
|
|
actor,
|
|
clothing.UnequipDelay,
|
|
new ClothingUnequipDoAfterEvent(slot),
|
|
removedItem.Value,
|
|
target,
|
|
removedItem.Value)
|
|
{
|
|
BreakOnMove = !clothing.EquipWhileMoving,
|
|
NeedHand = true,
|
|
};
|
|
|
|
_doAfter.TryStartDoAfter(args);
|
|
return false;
|
|
}
|
|
|
|
if (!_containerSystem.Remove(removedItem.Value, slotContainer, force: force, reparent: reparent))
|
|
return false;
|
|
|
|
// this is in order to keep track of whether this is the first instance of a recursion call
|
|
var firstRun = itemsDropped == 0;
|
|
++itemsDropped;
|
|
|
|
foreach (var slotDef in inventory.Slots)
|
|
{
|
|
if (slotDef != slotDefinition && slotDef.DependsOn == slotDefinition.Name)
|
|
{
|
|
//this recursive call might be risky
|
|
TryUnequip(actor, target, slotDef.Name, out _, ref itemsDropped, true, true, predicted, inventory, reparent: reparent);
|
|
}
|
|
}
|
|
|
|
// we check if any items were dropped, and make a popup if they were.
|
|
// the reason we check for > 1 is because the first item is always the one we are trying to unequip,
|
|
// whereas we only want to notify for extra dropped items.
|
|
if (!silent && firstRun && itemsDropped > 1)
|
|
_popup.PopupClient(Loc.GetString("inventory-component-dropped-from-unequip", ("items", itemsDropped - 1)), target, target);
|
|
|
|
// TODO: Inventory needs a hot cleanup hoo boy
|
|
// Check if something else (AKA toggleable) dumped it into a container.
|
|
if (!_containerSystem.IsEntityInContainer(removedItem.Value))
|
|
_transform.DropNextTo(removedItem.Value, target);
|
|
|
|
if (!silent && Resolve(removedItem.Value, ref clothing, false) && clothing.UnequipSound != null)
|
|
{
|
|
_audio.PlayPredicted(clothing.UnequipSound, target, actor);
|
|
}
|
|
|
|
// If gloves are unequipped, OnContactInteraction should trigger for held items
|
|
if (triggerHandContact && !((slotDefinition.SlotFlags & SlotFlags.GLOVES) == 0))
|
|
TriggerHandContactInteraction(target);
|
|
|
|
_movementSpeed.RefreshMovementSpeedModifiers(target);
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool CanUnequip(EntityUid uid, string slot, [NotNullWhen(false)] out string? reason,
|
|
ContainerSlot? containerSlot = null, SlotDefinition? slotDefinition = null,
|
|
InventoryComponent? inventory = null) =>
|
|
CanUnequip(uid, uid, slot, out reason, containerSlot, slotDefinition, inventory);
|
|
|
|
public bool CanUnequip(EntityUid actor, EntityUid target, string slot, [NotNullWhen(false)] out string? reason, ContainerSlot? containerSlot = null, SlotDefinition? slotDefinition = null, InventoryComponent? inventory = null)
|
|
{
|
|
reason = "inventory-component-can-unequip-cannot";
|
|
if (!Resolve(target, ref inventory, false))
|
|
return false;
|
|
|
|
if ((containerSlot == null || slotDefinition == null) && !TryGetSlotContainer(target, slot, out containerSlot, out slotDefinition, inventory))
|
|
return false;
|
|
|
|
if (containerSlot.ContainedEntity is not { } itemUid)
|
|
return false;
|
|
|
|
if (!_containerSystem.CanRemove(itemUid, containerSlot))
|
|
return false;
|
|
|
|
// make sure the user can actually reach the target
|
|
if (!CanAccess(actor, target, itemUid))
|
|
{
|
|
reason = "interaction-system-user-interaction-cannot-reach";
|
|
return false;
|
|
}
|
|
|
|
var attemptEvent = new IsUnequippingAttemptEvent(actor, target, itemUid, slotDefinition);
|
|
RaiseLocalEvent(actor, attemptEvent, true);
|
|
|
|
if (attemptEvent.Cancelled)
|
|
{
|
|
reason = attemptEvent.Reason ?? reason;
|
|
return false;
|
|
}
|
|
|
|
var targetAttemptEvent = new IsUnequippingTargetAttemptEvent(actor, target, itemUid, slotDefinition);
|
|
RaiseLocalEvent(target, targetAttemptEvent, true);
|
|
|
|
if (targetAttemptEvent.Cancelled)
|
|
{
|
|
reason = targetAttemptEvent.Reason ?? reason;
|
|
return false;
|
|
}
|
|
|
|
var itemAttemptEvent = new BeingUnequippedAttemptEvent(actor, target, itemUid, slotDefinition);
|
|
RaiseLocalEvent(itemUid, itemAttemptEvent, true);
|
|
if (itemAttemptEvent.Cancelled)
|
|
{
|
|
reason = itemAttemptEvent.Reason ?? reason;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool TryGetSlotEntity(EntityUid uid, string slot, [NotNullWhen(true)] out EntityUid? entityUid, InventoryComponent? inventoryComponent = null, ContainerManagerComponent? containerManagerComponent = null)
|
|
{
|
|
entityUid = null;
|
|
if (!Resolve(uid, ref inventoryComponent, ref containerManagerComponent, false)
|
|
|| !TryGetSlotContainer(uid, slot, out var container, out _, inventoryComponent, containerManagerComponent))
|
|
return false;
|
|
|
|
entityUid = container.ContainedEntity;
|
|
return entityUid != null;
|
|
}
|
|
|
|
public void TriggerHandContactInteraction(EntityUid uid)
|
|
{
|
|
foreach (var item in _handsSystem.EnumerateHeld(uid))
|
|
{
|
|
_interactionSystem.DoContactInteraction(uid, item);
|
|
}
|
|
}
|
|
}
|