Skip to content

Commit

Permalink
Port All Carrying/PseudoItem/EscapeInventory Tweaks From DeltaV (#484)
Browse files Browse the repository at this point in the history
# Description
This cherry-picks the following two PRs of mine from delta-v:
- DeltaV-Station/Delta-v#1118 (this one is a
port of two other frontier PRs, see the original description for
details)
- DeltaV-Station/Delta-v#1232

Encompassing a total of 8 distinct changes:
1. Fixes dropping the carried person when walking to a different grid
(from station to shuttle or vice versa. Walking into space however will
still make you drop them) and also makes sure that pressing shift while
being carried does not make you escape.
2. Ensures that the carried person is always centered relative to the
parent (under certain conditions, such as walking near a gravitational
anomaly, their position can change, and that leads to really weird
effects)
3. Fixes the mass contest in CarryingSystem that caused stronger
entities to take longer to escape than weaker ones.
4. Adds popups for when you're getting picked up or being stuffed into a
bag as a pseudo-item (e.g. a felinid)
5. Adds an action to stop escaping an inventory. This action gets added
to your action bar when you attempt escaping and gets removed when you
stop or escape. It applies both to carrying and items (hampsters,
felinids, whatever else).
6. Adds a sleep action for pseudo-items stuffed inside a suitable bag.
The bag must have a special component, which is added to the base
backpack item and thus inherited by all soft bags (duffels, satchels,
etc). Contrary to a popular belief, sleeping IS PURELY COSMETICAL and
does not provide healing. (Beds provide healing when you buckle into
them and that healing does not depend on whether or not you're sleeping)
7. Makes it so that when you try to take a pseudo-item out of the bag
(e.g. a felinid), you automatically try to carry them (if you don't have
enough free hands, they will be dropped on the floor like usually), and
enables you to insert the carried person into a bag, but only if they're
a pseudo-item (e.g. felinid).
8. Allows pseudoitems to be inserted into bags even when there are other
items (as long as there's enough space)

---

## For technical details and video showcases, see the original PRs

This PR is split into separate commits so different parts can be
reverted if deemed unneccessary.

---

<!--
This is default collapsed, readers click to expand it and see all your
media
The PR media section can get very large at times, so this is a good way
to keep it clean
The title is written using HTML tags
The title must be within the <summary> tags or you won't see it

<details><summary><h1>Media</h1></summary>
<p>

![Example Media Embed](https://example.com/thisimageisntreal.png)

</p>
</details>

---
-->

# Changelog
:cl:
- fix: Carrying is less likely to behave erratically or suddenly
interrupt now.
- add: You can now see when someone is trying to pick you up, and also
you can interrupt your attempt at escaping from their hands or
inventory.
- add: You can now properly take Felinids out of bags and place them
inside.
- add: Scientists have discovered that Felinids can sleep in bags.
  • Loading branch information
Mnemotechnician authored Jul 5, 2024
1 parent 81dd782 commit 58be850
Show file tree
Hide file tree
Showing 15 changed files with 259 additions and 167 deletions.
116 changes: 107 additions & 9 deletions Content.Server/Nyanotrasen/Carrying/CarryingSystem.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
using System.Numerics;
using System.Threading;
using Content.Server.DoAfter;
using Content.Server.Body.Systems;
using Content.Server.Hands.Systems;
using Content.Server.Resist;
using Content.Server.Popups;
using Content.Server.Inventory;
using Content.Server.Nyanotrasen.Item.PseudoItem;
using Content.Shared.Climbing; // Shared instead of Server
using Content.Shared.Mobs;
using Content.Shared.DoAfter;
Expand All @@ -23,9 +25,12 @@
using Content.Shared.Standing;
using Content.Shared.ActionBlocker;
using Content.Shared.Inventory.VirtualItem;
using Content.Shared.Item;
using Content.Shared.Throwing;
using Content.Shared.Physics.Pull;
using Content.Shared.Mobs.Systems;
using Content.Shared.Nyanotrasen.Item.PseudoItem;
using Content.Shared.Storage;
using Robust.Shared.Map.Components;
using Robust.Shared.Physics.Components;

Expand All @@ -44,11 +49,13 @@ public sealed class CarryingSystem : EntitySystem
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!;
[Dependency] private readonly RespiratorSystem _respirator = default!;
[Dependency] private readonly PseudoItemSystem _pseudoItem = default!;

public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CarriableComponent, GetVerbsEvent<AlternativeVerb>>(AddCarryVerb);
SubscribeLocalEvent<CarryingComponent, GetVerbsEvent<InnateVerb>>(AddInsertCarriedVerb);
SubscribeLocalEvent<CarryingComponent, VirtualItemDeletedEvent>(OnVirtualItemDeleted);
SubscribeLocalEvent<CarryingComponent, BeforeThrowEvent>(OnThrow);
SubscribeLocalEvent<CarryingComponent, EntParentChangedMessage>(OnParentChanged);
Expand All @@ -64,7 +71,6 @@ public override void Initialize()
SubscribeLocalEvent<CarriableComponent, CarryDoAfterEvent>(OnDoAfter);
}


private void AddCarryVerb(EntityUid uid, CarriableComponent component, GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanInteract || !args.CanAccess)
Expand Down Expand Up @@ -97,6 +103,33 @@ private void AddCarryVerb(EntityUid uid, CarriableComponent component, GetVerbsE
args.Verbs.Add(verb);
}

private void AddInsertCarriedVerb(EntityUid uid, CarryingComponent component, GetVerbsEvent<InnateVerb> args)
{
// If the person is carrying someone, and the carried person is a pseudo-item, and the target entity is a storage,
// then add an action to insert the carried entity into the target
var toInsert = args.Using;
if (toInsert is not { Valid: true } || !args.CanAccess || !TryComp<PseudoItemComponent>(toInsert, out var pseudoItem))
return;

if (!TryComp<StorageComponent>(args.Target, out var storageComp))
return;

if (!_pseudoItem.CheckItemFits((toInsert.Value, pseudoItem), (args.Target, storageComp)))
return;

InnateVerb verb = new()
{
Act = () =>
{
DropCarried(uid, toInsert.Value);
_pseudoItem.TryInsert(args.Target, toInsert.Value, pseudoItem, storageComp);
},
Text = Loc.GetString("action-name-insert-other", ("target", toInsert)),
Priority = 2
};
args.Verbs.Add(verb);
}

/// <summary>
/// Since the carried entity is stored as 2 virtual items, when deleted we want to drop them.
/// </summary>
Expand Down Expand Up @@ -125,7 +158,12 @@ private void OnThrow(EntityUid uid, CarryingComponent component, BeforeThrowEven

private void OnParentChanged(EntityUid uid, CarryingComponent component, ref EntParentChangedMessage args)
{
if (Transform(uid).MapID != args.OldMapId)
var xform = Transform(uid);
if (xform.MapID != args.OldMapId)
return;

// Do not drop the carried entity if the new parent is a grid
if (xform.ParentUid == xform.GridUid)
return;

DropCarried(uid, component.Carried);
Expand Down Expand Up @@ -158,9 +196,13 @@ private void OnMoveInput(EntityUid uid, BeingCarriedComponent component, ref Mov
if (!TryComp<CanEscapeInventoryComponent>(uid, out var escape))
return;

if (!args.HasDirectionalMovement)
return;

if (_actionBlockerSystem.CanInteract(uid, component.Carrier))
{
_escapeInventorySystem.AttemptEscape(uid, component.Carrier, escape, MassContest(uid, component.Carrier));
// Note: the mass contest is inverted because weaker entities are supposed to take longer to escape
_escapeInventorySystem.AttemptEscape(uid, component.Carrier, escape, MassContest(component.Carrier, uid));
}
}

Expand Down Expand Up @@ -209,12 +251,7 @@ private void OnDoAfter(EntityUid uid, CarriableComponent component, CarryDoAfter
}
private void StartCarryDoAfter(EntityUid carrier, EntityUid carried, CarriableComponent component)
{
TimeSpan length = TimeSpan.FromSeconds(3);

var mod = MassContest(carrier, carried);

if (mod != 0)
length /= mod;
TimeSpan length = GetPickupDuration(carrier, carried);

if (length >= TimeSpan.FromSeconds(9))
{
Expand All @@ -236,6 +273,9 @@ private void StartCarryDoAfter(EntityUid carrier, EntityUid carried, CarriableCo
};

_doAfterSystem.TryStartDoAfter(args);

// Show a popup to the person getting picked up
_popupSystem.PopupEntity(Loc.GetString("carry-started", ("carrier", carrier)), carried, carried);
}

private void Carry(EntityUid carrier, EntityUid carried)
Expand All @@ -260,6 +300,26 @@ private void Carry(EntityUid carrier, EntityUid carried)
_actionBlockerSystem.UpdateCanMove(carried);
}

public bool TryCarry(EntityUid carrier, EntityUid toCarry, CarriableComponent? carriedComp = null)
{
if (!Resolve(toCarry, ref carriedComp, false))
return false;

if (!CanCarry(carrier, toCarry, carriedComp))
return false;

// The second one means that carrier is a pseudo-item and is inside a bag.
if (HasComp<BeingCarriedComponent>(carrier) || HasComp<ItemComponent>(carrier))
return false;

if (GetPickupDuration(carrier, toCarry) > TimeSpan.FromSeconds(9))
return false;

Carry(carrier, toCarry);

return true;
}

public void DropCarried(EntityUid carrier, EntityUid carried)
{
RemComp<CarryingComponent>(carrier); // get rid of this first so we don't recusrively fire that event
Expand Down Expand Up @@ -323,5 +383,43 @@ private float MassContest(EntityUid roller, EntityUid target, PhysicsComponent?

return rollerPhysics.FixturesMass / targetPhysics.FixturesMass;
}

private TimeSpan GetPickupDuration(EntityUid carrier, EntityUid carried)
{
var length = TimeSpan.FromSeconds(3);

var mod = MassContest(carrier, carried);
if (mod != 0)
length /= mod;

return length;
}

public override void Update(float frameTime)
{
var query = EntityQueryEnumerator<BeingCarriedComponent>();
while (query.MoveNext(out var carried, out var comp))
{
var carrier = comp.Carrier;
if (carrier is not { Valid: true } || carried is not { Valid: true })
continue;

// SOMETIMES - when an entity is inserted into disposals, or a cryosleep chamber - it can get re-parented without a proper reparent event
// when this happens, it needs to be dropped because it leads to weird behavior
if (Transform(carried).ParentUid != carrier)
{
DropCarried(carrier, carried);
continue;
}

// Make sure the carried entity is always centered relative to the carrier, as gravity pulls can offset it otherwise
var xform = Transform(carried);
if (!xform.LocalPosition.Equals(Vector2.Zero))
{
xform.LocalPosition = Vector2.Zero;
}
}
query.Dispose();
}
}
}
30 changes: 28 additions & 2 deletions Content.Server/Nyanotrasen/Item/PseudoItem/PseudoItemSystem.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using Content.Server.DoAfter;
using Content.Server.Carrying;
using Content.Server.DoAfter;
using Content.Server.Item;
using Content.Server.Popups;
using Content.Server.Storage.EntitySystems;
using Content.Shared.Bed.Sleep;
using Content.Shared.DoAfter;
using Content.Shared.IdentityManagement;
using Content.Shared.Item;
Expand All @@ -17,12 +20,14 @@ public sealed class PseudoItemSystem : SharedPseudoItemSystem
[Dependency] private readonly StorageSystem _storage = default!;
[Dependency] private readonly ItemSystem _item = default!;
[Dependency] private readonly DoAfterSystem _doAfter = default!;

[Dependency] private readonly CarryingSystem _carrying = default!;
[Dependency] private readonly PopupSystem _popup = default!;

public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PseudoItemComponent, GetVerbsEvent<AlternativeVerb>>(AddInsertAltVerb);
SubscribeLocalEvent<PseudoItemComponent, TryingToSleepEvent>(OnTrySleeping);
}

private void AddInsertAltVerb(EntityUid uid, PseudoItemComponent component, GetVerbsEvent<AlternativeVerb> args)
Expand Down Expand Up @@ -53,4 +58,25 @@ private void AddInsertAltVerb(EntityUid uid, PseudoItemComponent component, GetV
};
args.Verbs.Add(verb);
}

protected override void OnGettingPickedUpAttempt(EntityUid uid, PseudoItemComponent component, GettingPickedUpAttemptEvent args)
{
// Try to pick the entity up instead first
if (args.User != args.Item && _carrying.TryCarry(args.User, uid))
{
args.Cancel();
return;
}

// If could not pick up, just take it out onto the ground as per default
base.OnGettingPickedUpAttempt(uid, component, args);
}

// Show a popup when a pseudo-item falls asleep inside a bag.
private void OnTrySleeping(EntityUid uid, PseudoItemComponent component, TryingToSleepEvent args)
{
var parent = Transform(uid).ParentUid;
if (!HasComp<SleepingComponent>(uid) && parent is { Valid: true } && HasComp<AllowsSleepInsideComponent>(parent))
_popup.PopupEntity(Loc.GetString("popup-sleep-in-bag", ("entity", uid)), uid);
}
}
6 changes: 6 additions & 0 deletions Content.Server/Resist/CanEscapeInventoryComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,10 @@ public sealed partial class CanEscapeInventoryComponent : Component

[DataField("doAfter")]
public DoAfterId? DoAfter;

/// <summary>
/// Action to cancel inventory escape.
/// </summary>
[DataField]
public EntityUid? EscapeCancelAction;
}
26 changes: 26 additions & 0 deletions Content.Server/Resist/EscapeInventorySystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Content.Shared.Hands.EntitySystems;
using Content.Server.Storage.Components;
using Content.Shared.ActionBlocker;
using Content.Shared.Actions;
using Content.Shared.DoAfter;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction.Events;
Expand All @@ -13,6 +14,7 @@
using Content.Shared.Resist;
using Content.Shared.Storage;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;

namespace Content.Server.Resist;

Expand All @@ -24,11 +26,17 @@ public sealed class EscapeInventorySystem : EntitySystem
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly CarryingSystem _carryingSystem = default!; // Carrying system from Nyanotrasen.
[Dependency] private readonly SharedActionsSystem _actions = default!;

/// <summary>
/// You can't escape the hands of an entity this many times more massive than you.
/// </summary>
public const float MaximumMassDisadvantage = 6f;
/// <summary>
/// Action to cancel inventory escape
/// </summary>
[ValidatePrototypeId<EntityPrototype>]
private readonly string _escapeCancelAction = "ActionCancelEscape";

public override void Initialize()
{
Expand All @@ -37,6 +45,7 @@ public override void Initialize()
SubscribeLocalEvent<CanEscapeInventoryComponent, MoveInputEvent>(OnRelayMovement);
SubscribeLocalEvent<CanEscapeInventoryComponent, EscapeInventoryEvent>(OnEscape);
SubscribeLocalEvent<CanEscapeInventoryComponent, DroppedEvent>(OnDropped);
SubscribeLocalEvent<CanEscapeInventoryComponent, EscapeInventoryCancelActionEvent>(OnCancelEscape);
}

private void OnRelayMovement(EntityUid uid, CanEscapeInventoryComponent component, ref MoveInputEvent args)
Expand Down Expand Up @@ -84,12 +93,20 @@ private void OnRelayMovement(EntityUid uid, CanEscapeInventoryComponent componen

_popupSystem.PopupEntity(Loc.GetString("escape-inventory-component-start-resisting"), user, user);
_popupSystem.PopupEntity(Loc.GetString("escape-inventory-component-start-resisting-target"), container, container);

// Add an escape cancel action
if (component.EscapeCancelAction is not { Valid: true })
_actions.AddAction(user, ref component.EscapeCancelAction, _escapeCancelAction);
}

private void OnEscape(EntityUid uid, CanEscapeInventoryComponent component, EscapeInventoryEvent args)
{
component.DoAfter = null;

// Remove the cancel action regardless of do-after result
_actions.RemoveAction(uid, component.EscapeCancelAction);
component.EscapeCancelAction = null;

if (args.Handled || args.Cancelled)
return;

Expand All @@ -109,4 +126,13 @@ private void OnDropped(EntityUid uid, CanEscapeInventoryComponent component, Dro
if (component.DoAfter != null)
_doAfterSystem.Cancel(component.DoAfter);
}

private void OnCancelEscape(EntityUid uid, CanEscapeInventoryComponent component, EscapeInventoryCancelActionEvent args)
{
if (component.DoAfter != null)
_doAfterSystem.Cancel(component.DoAfter);

_actions.RemoveAction(uid, component.EscapeCancelAction);
component.EscapeCancelAction = null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Content.Shared.Nyanotrasen.Item.PseudoItem;

/// <summary>
/// Signifies that pseudo-item creatures can sleep inside the container to which this component is applied.
/// </summary>
[RegisterComponent]
public sealed partial class AllowsSleepInsideComponent : Component
{
}
14 changes: 10 additions & 4 deletions Content.Shared/Nyanotrasen/Item/PseudoItem/PseudoItemComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@

namespace Content.Shared.Nyanotrasen.Item.PseudoItem;

/// <summary>
/// For entities that behave like an item under certain conditions,
/// but not under most conditions.
/// </summary>
/// <summary>
/// For entities that behave like an item under certain conditions,
/// but not under most conditions.
/// </summary>
[RegisterComponent, AutoGenerateComponentState]
public sealed partial class PseudoItemComponent : Component
{
Expand All @@ -24,4 +24,10 @@ public sealed partial class PseudoItemComponent : Component
public Vector2i StoredOffset;

public bool Active = false;

/// <summary>
/// Action for sleeping while inside a container with <see cref="AllowsSleepInsideComponent"/>.
/// </summary>
[DataField]
public EntityUid? SleepAction;
}
Loading

0 comments on commit 58be850

Please sign in to comment.