mirror of
https://github.com/space-syndicate/space-station-14.git
synced 2026-10-09 18:31:17 +03:00
* Check equipped status on magboot toggle * ClothingSystem.IsEquipped: explicitly check null * MagbootsSystem: maintain order of event handlers * Address Fildrance's suggestions Co-authored-by: Fildrance <14752842+Fildrance@users.noreply.github.com> --------- Co-authored-by: Fildrance <14752842+Fildrance@users.noreply.github.com>
226 lines
7.7 KiB
C#
226 lines
7.7 KiB
C#
using Content.Shared.Clothing.Components;
|
|
using Content.Shared.Hands.Components;
|
|
using Content.Shared.Hands.EntitySystems;
|
|
using Content.Shared.Interaction.Events;
|
|
using Content.Shared.Inventory;
|
|
using Content.Shared.Inventory.Events;
|
|
using Content.Shared.Item;
|
|
using Content.Shared.Strip.Components;
|
|
|
|
namespace Content.Shared.Clothing.EntitySystems;
|
|
|
|
public abstract partial class ClothingSystem : EntitySystem
|
|
{
|
|
[Dependency] private SharedItemSystem _itemSys = default!;
|
|
[Dependency] private InventorySystem _invSystem = default!;
|
|
[Dependency] private SharedHandsSystem _handsSystem = default!;
|
|
|
|
[Dependency] protected EntityQuery<ClothingComponent> ClothingQuery;
|
|
[Dependency] private EntityQuery<HandsComponent> _handsQuery;
|
|
[Dependency] protected EntityQuery<InventoryComponent> InventoryQuery;
|
|
|
|
#region Event Handlers
|
|
[SubscribeLocalEvent]
|
|
private void OnUseInHand(Entity<ClothingComponent> ent, ref UseInHandEvent args)
|
|
{
|
|
if (args.Handled || !ent.Comp.QuickEquip)
|
|
return;
|
|
|
|
var user = args.User;
|
|
if (!InventoryQuery.TryComp(user, out InventoryComponent? inv) ||
|
|
!_handsQuery.TryComp(user, out HandsComponent? hands))
|
|
return;
|
|
|
|
QuickEquip(ent, (user, inv, hands));
|
|
args.Handled = true;
|
|
args.ApplyDelay = false;
|
|
}
|
|
|
|
private void QuickEquip(
|
|
Entity<ClothingComponent> toEquipEnt,
|
|
Entity<InventoryComponent, HandsComponent> userEnt)
|
|
{
|
|
foreach (var slotDef in userEnt.Comp1.Slots)
|
|
{
|
|
if (!_invSystem.CanEquip(userEnt, toEquipEnt, slotDef.Name, out _, slotDef, userEnt, toEquipEnt, assumeEmpty: true))
|
|
continue;
|
|
|
|
if (_invSystem.TryGetSlotEntity(userEnt, slotDef.Name, out var slotEntity, userEnt))
|
|
{
|
|
// Item in slot has to be quick equipable as well
|
|
if (ClothingQuery.TryComp(slotEntity, out ClothingComponent? item) && !item.QuickEquip)
|
|
continue;
|
|
|
|
if (!_invSystem.TryUnequip(userEnt, slotDef.Name, true, inventory: userEnt, checkDoafter: true))
|
|
continue;
|
|
|
|
if (!_invSystem.TryEquip(userEnt, toEquipEnt, slotDef.Name, inventory: userEnt, clothing: toEquipEnt, checkDoafter: true, triggerHandContact: true))
|
|
continue;
|
|
|
|
_handsSystem.PickupOrDrop(userEnt, slotEntity.Value, handsComp: userEnt);
|
|
}
|
|
else
|
|
{
|
|
if (!_invSystem.TryEquip(userEnt, toEquipEnt, slotDef.Name, inventory: userEnt, clothing: toEquipEnt, checkDoafter: true, triggerHandContact: true))
|
|
continue;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
[SubscribeLocalEvent]
|
|
protected virtual void OnGotEquipped(EntityUid uid, ClothingComponent component, GotEquippedEvent args)
|
|
{
|
|
component.InSlot = args.Slot;
|
|
component.InSlotFlag = args.SlotFlags;
|
|
Dirty(uid, component);
|
|
|
|
if ((component.Slots & args.SlotFlags) == SlotFlags.NONE)
|
|
return;
|
|
|
|
var gotEquippedEvent = new ClothingGotEquippedEvent(args.EquipTarget, component);
|
|
RaiseLocalEvent(uid, ref gotEquippedEvent);
|
|
|
|
var didEquippedEvent = new ClothingDidEquippedEvent((uid, component));
|
|
RaiseLocalEvent(args.EquipTarget, ref didEquippedEvent);
|
|
}
|
|
|
|
[SubscribeLocalEvent]
|
|
protected virtual void OnGotUnequipped(EntityUid uid, ClothingComponent component, GotUnequippedEvent args)
|
|
{
|
|
if ((component.Slots & args.SlotFlags) != SlotFlags.NONE)
|
|
{
|
|
var gotUnequippedEvent = new ClothingGotUnequippedEvent(args.EquipTarget, component);
|
|
RaiseLocalEvent(uid, ref gotUnequippedEvent);
|
|
|
|
var didUnequippedEvent = new ClothingDidUnequippedEvent((uid, component));
|
|
RaiseLocalEvent(args.EquipTarget, ref didUnequippedEvent);
|
|
}
|
|
|
|
component.InSlot = null;
|
|
component.InSlotFlag = null;
|
|
Dirty(uid, component);
|
|
}
|
|
|
|
[SubscribeLocalEvent]
|
|
private void AfterAutoHandleState(Entity<ClothingComponent> ent, ref AfterAutoHandleStateEvent args)
|
|
{
|
|
_itemSys.VisualsChanged(ent.Owner);
|
|
}
|
|
|
|
[SubscribeLocalEvent]
|
|
private void OnEquipDoAfter(Entity<ClothingComponent> ent, ref ClothingEquipDoAfterEvent args)
|
|
{
|
|
if (args.Handled || args.Cancelled || args.Target is not { } target)
|
|
return;
|
|
args.Handled = _invSystem.TryEquip(args.User, target, ent, args.Slot, clothing: ent.Comp, predicted: true, checkDoafter: false);
|
|
}
|
|
|
|
[SubscribeLocalEvent]
|
|
private void OnUnequipDoAfter(Entity<ClothingComponent> ent, ref ClothingUnequipDoAfterEvent args)
|
|
{
|
|
if (args.Handled || args.Cancelled || args.Target is not { } target)
|
|
return;
|
|
args.Handled = _invSystem.TryUnequip(args.User, target, args.Slot, clothing: ent.Comp, predicted: true, checkDoafter: false, triggerHandContact: true);
|
|
if (args.Handled)
|
|
_handsSystem.TryPickup(args.User, ent);
|
|
}
|
|
|
|
[SubscribeLocalEvent]
|
|
private void OnItemStripped(Entity<ClothingComponent> ent, ref BeforeItemStrippedEvent args)
|
|
{
|
|
args.Additive += ent.Comp.StripDelay;
|
|
}
|
|
#endregion Event Handlers
|
|
|
|
#region Public API
|
|
|
|
/// <summary>
|
|
/// Returns true if this clothing item is currently inside an inventory slot AND that slot is considered valid for equipping.
|
|
/// For example putting shoes into your pockets does not count as being equipped.
|
|
/// </summary>
|
|
public bool IsEquipped(Entity<ClothingComponent?> item)
|
|
{
|
|
if (!Resolve(item, ref item.Comp, false))
|
|
return false;
|
|
|
|
// If the entity is in a user's hand, or not on their body at all,
|
|
// the flag is null, and the bitwise and below returns null!
|
|
if (item.Comp.InSlotFlag == null)
|
|
return false;
|
|
|
|
if ((item.Comp.Slots & item.Comp.InSlotFlag) != SlotFlags.NONE)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
public void SetEquippedPrefix(EntityUid uid, string? prefix, ClothingComponent? clothing = null)
|
|
{
|
|
if (!Resolve(uid, ref clothing, false))
|
|
return;
|
|
|
|
if (clothing.EquippedPrefix == prefix)
|
|
return;
|
|
|
|
clothing.EquippedPrefix = prefix;
|
|
_itemSys.VisualsChanged(uid);
|
|
Dirty(uid, clothing);
|
|
}
|
|
|
|
public void SetSlots(EntityUid uid, SlotFlags slots, ClothingComponent? clothing = null)
|
|
{
|
|
if (!Resolve(uid, ref clothing))
|
|
return;
|
|
|
|
clothing.Slots = slots;
|
|
Dirty(uid, clothing);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copy all clothing specific visuals from another item.
|
|
/// </summary>
|
|
public void CopyVisuals(EntityUid uid, ClothingComponent otherClothing, ClothingComponent? clothing = null)
|
|
{
|
|
if (!Resolve(uid, ref clothing))
|
|
return;
|
|
|
|
clothing.ClothingVisuals = otherClothing.ClothingVisuals;
|
|
clothing.EquippedPrefix = otherClothing.EquippedPrefix;
|
|
clothing.RsiPath = otherClothing.RsiPath;
|
|
|
|
_itemSys.VisualsChanged(uid);
|
|
Dirty(uid, clothing);
|
|
}
|
|
|
|
public void SetLayerColor(ClothingComponent clothing, string slot, string mapKey, Color? color)
|
|
{
|
|
foreach (var layer in clothing.ClothingVisuals[slot])
|
|
{
|
|
if (layer.MapKeys == null)
|
|
return;
|
|
|
|
if (!layer.MapKeys.Contains(mapKey))
|
|
continue;
|
|
|
|
layer.Color = color;
|
|
}
|
|
}
|
|
|
|
public void SetLayerState(ClothingComponent clothing, string slot, string mapKey, string state)
|
|
{
|
|
foreach (var layer in clothing.ClothingVisuals[slot])
|
|
{
|
|
if (layer.MapKeys == null)
|
|
return;
|
|
|
|
if (!layer.MapKeys.Contains(mapKey))
|
|
continue;
|
|
|
|
layer.State = state;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|