Parcel wrap bugfix + multihandedness enhancement (#43956)

* parcel wrap enhancements:
- bugfix: can't wrap stuff that can't be picked up because it's static (ie. usually anchored, maybe other cases, idk)
- enhancement: multi-handedness is inherited by the parcel from its contents
- technical: migrate to `EntityQuery` dependencies for all component lookups.

* Apply suggestion from @Centronias

* Update Content.Shared/ParcelWrap/Systems/ParcelWrappingSystem.cs

* Update ParcelWrappingSystem.cs

---------

Co-authored-by: Pok <113675512+Pok27@users.noreply.github.com>
This commit is contained in:
Centronias 2026-09-19 19:58:53 +00:00 • committed by GitHub
parent 8126120a2e
commit 9b31c2a929
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 38 additions and 12 deletions

View file

@ -61,6 +61,13 @@ public sealed partial class WrappedParcelComponent : Component
[DataField, AutoNetworkedField]
public bool GetsShapeFromContent;
/// <summary>
/// If true, the owner of this entity has <see cref="MultiHandedItemComponent"/> added if any item inserted into
/// <see cref="Container"/> has that component.
/// </summary>
[DataField, AutoNetworkedField]
public bool GetsMultiHandednessFromContent = true;
/// <summary>
/// If a player trapped inside this parcel can escape from it by unwrapping it.
/// This is set by the <see cref="ParcelWrapComponent" /> used to create the parcel.

View file

@ -88,7 +88,7 @@ public sealed partial class ParcelWrappingSystem
{
var duration = wrapper.Comp.WrapDelay;
if (TryComp<ParcelWrapOverrideComponent>(target, out var overrideComp) &&
if (_parcelWrapOverrideQuery.TryComp(target, out var overrideComp) &&
overrideComp.WrapDelay is { } wrapDelayOverride)
duration = wrapDelayOverride;

View file

@ -32,7 +32,10 @@ public sealed partial class ParcelWrappingSystem
entity.Comp.Contents = _container.EnsureContainer<ContainerSlot>(entity, entity.Comp.ContainerId);
}
private void OnEntInsertedIntoContainer(Entity<WrappedParcelComponent> entity, ref EntInsertedIntoContainerMessage args)
private void OnEntInsertedIntoContainer(
Entity<WrappedParcelComponent> entity,
ref EntInsertedIntoContainerMessage args
)
{
// If the entity was inserted because of a server state application, assume that the item's state is applied
// correctly as well and that deriving them from the contents is unneeded.
@ -40,12 +43,10 @@ public sealed partial class ParcelWrappingSystem
return;
if (args.Container != entity.Comp.Contents ||
!TryComp<ItemComponent>(entity, out var parcelItemComp))
!_itemQuery.TryComp(entity, out var parcelItemComp))
return;
// If this wrap maintains the size when wrapping, set the parcel's size to the target's size, or the fallback
// size if the target does not have a size.
var targetItemComp = CompOrNull<ItemComponent>(args.Entity);
var targetItemComp = _itemQuery.CompOrNull(args.Entity);
if (entity.Comp.GetsSizeFromContent)
{
var size = targetItemComp?.Size ?? _fallbackParcelSize;
@ -53,12 +54,16 @@ public sealed partial class ParcelWrappingSystem
_appearance.SetData(entity, WrappedParcelVisuals.Size, size.Id);
}
// If this wrap maintains the shape when wrapping and the item has a shape override, copy the shape override to
// the parcel.
if (entity.Comp.GetsShapeFromContent)
{
_item.SetShape(entity, targetItemComp?.Shape, parcelItemComp);
}
if (entity.Comp.GetsMultiHandednessFromContent &&
_multiHandedItemQuery.TryComp(args.Entity, out var multiHandedItemComp))
{
EnsureComp<MultiHandedItemComponent>(entity).HandsNeeded = multiHandedItemComp.HandsNeeded;
}
}
private void OnUseInHand(Entity<WrappedParcelComponent> entity, ref UseInHandEvent args)
@ -69,8 +74,10 @@ public sealed partial class ParcelWrappingSystem
args.Handled = TryStartUnwrapDoAfter(args.User, entity);
}
private void OnGetVerbsForWrappedParcel(Entity<WrappedParcelComponent> entity,
ref GetVerbsEvent<InteractionVerb> args)
private void OnGetVerbsForWrappedParcel(
Entity<WrappedParcelComponent> entity,
ref GetVerbsEvent<InteractionVerb> args
)
{
if (!args.CanAccess || !args.CanComplexInteract)
return;
@ -93,7 +100,7 @@ public sealed partial class ParcelWrappingSystem
if (args.Handled || args.Cancelled)
return;
if (args.Target is { } target && TryComp<WrappedParcelComponent>(target, out var parcel))
if (args.Target is { } target && _wrappedParcelQuery.TryComp(target, out var parcel))
{
UnwrapInternal(args.User, (target, parcel));
args.Handled = true;

View file

@ -4,8 +4,11 @@ using Content.Shared.Item;
using Content.Shared.ParcelWrap.Components;
using Content.Shared.Popups;
using Content.Shared.Whitelist;
using JetBrains.Annotations;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
namespace Content.Shared.ParcelWrap.Systems;
@ -27,6 +30,12 @@ public sealed partial class ParcelWrappingSystem : EntitySystem
[Dependency] private SharedTransformSystem _transform = default!;
[Dependency] private EntityWhitelistSystem _whitelist = default!;
[Dependency] private EntityQuery<ItemComponent> _itemQuery = default!;
[Dependency] private EntityQuery<MultiHandedItemComponent> _multiHandedItemQuery = default!;
[Dependency] private EntityQuery<ParcelWrapOverrideComponent> _parcelWrapOverrideQuery = default!;
[Dependency] private EntityQuery<PhysicsComponent> _physicsQuery = default!;
[Dependency] private EntityQuery<WrappedParcelComponent> _wrappedParcelQuery = default!;
/// <inheritdoc/>
public override void Initialize()
{
@ -42,6 +51,7 @@ public sealed partial class ParcelWrappingSystem : EntitySystem
/// <param name="wrapper">The entity doing the wrapping.</param>
/// <param name="target">The entity to be wrapped.</param>
/// <returns>True if <paramref name="wrapper"/> can be used to wrap <paramref name="target"/>, false otherwise.</returns>
[PublicAPI]
public bool IsWrappable(Entity<ParcelWrapComponent> wrapper, EntityUid target)
{
return
@ -49,6 +59,8 @@ public sealed partial class ParcelWrappingSystem : EntitySystem
wrapper.Owner != target &&
// Wrapper should never be empty, but may as well make sure.
!_charges.IsEmpty(wrapper.Owner) &&
_whitelist.CheckBoth(target, wrapper.Comp.Blacklist, wrapper.Comp.Whitelist);
_whitelist.CheckBoth(target, wrapper.Comp.Blacklist, wrapper.Comp.Whitelist) &&
// If it can't be moved, it shouldn't be able to go into a parcel and picked up.
_physicsQuery.TryComp(target, out var physics) && physics.BodyType != BodyType.Static;
}
}