Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port All Carrying/PseudoItem/EscapeInventory Tweaks From DeltaV #484

Merged
merged 12 commits into from
Jul 5, 2024
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!; // Needed for fitting check

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>
/// DeltaV - action to cancel inventory escape. Added dynamically.
/// </summary>
[DataField]
public EntityUid? EscapeCancelAction;
}
27 changes: 27 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!; // DeltaV

/// <summary>
/// You can't escape the hands of an entity this many times more massive than you.
/// </summary>
public const float MaximumMassDisadvantage = 6f;
/// <summary>
/// DeltaV - 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); // DeltaV
}

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);

// DeltaV - 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;

// DeltaV - remove 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,14 @@ private void OnDropped(EntityUid uid, CanEscapeInventoryComponent component, Dro
if (component.DoAfter != null)
_doAfterSystem.Cancel(component.DoAfter);
}

// DeltaV
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
Loading