diff --git a/Content.Server/DeltaV/GlimmerWisp/LifeDrainerSystem.cs b/Content.Server/DeltaV/GlimmerWisp/LifeDrainerSystem.cs index ec800db2a30..079ec77bbf3 100644 --- a/Content.Server/DeltaV/GlimmerWisp/LifeDrainerSystem.cs +++ b/Content.Server/DeltaV/GlimmerWisp/LifeDrainerSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.ActionBlocker; using Content.Shared.Damage; +using Content.Shared.DeltaV.Carrying; using Content.Shared.DoAfter; using Content.Shared.Interaction; using Content.Shared.Mobs.Systems; @@ -11,7 +12,6 @@ using Content.Shared.Verbs; using Content.Shared.Whitelist; using Content.Server.NPC.Components; -using Content.Server.Carrying; using Robust.Shared.Audio.Systems; using Robust.Shared.Player; using Robust.Shared.Utility; diff --git a/Content.Server/Nyanotrasen/Carrying/BeingCarriedComponent.cs b/Content.Server/Nyanotrasen/Carrying/BeingCarriedComponent.cs deleted file mode 100644 index afc78978c86..00000000000 --- a/Content.Server/Nyanotrasen/Carrying/BeingCarriedComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Content.Server.Carrying -{ - /// - /// Stores the carrier of an entity being carried. - /// - [RegisterComponent] - public sealed partial class BeingCarriedComponent : Component - { - public EntityUid Carrier = default!; - } -} diff --git a/Content.Server/Nyanotrasen/Carrying/CarriableComponent.cs b/Content.Server/Nyanotrasen/Carrying/CarriableComponent.cs deleted file mode 100644 index f4fd1fa6d56..00000000000 --- a/Content.Server/Nyanotrasen/Carrying/CarriableComponent.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Threading; - -namespace Content.Server.Carrying -{ - [RegisterComponent] - public sealed partial class CarriableComponent : Component - { - /// - /// Number of free hands required - /// to carry the entity - /// - [DataField("freeHandsRequired")] - public int FreeHandsRequired = 2; - - public CancellationTokenSource? CancelToken; - } -} diff --git a/Content.Server/Nyanotrasen/Carrying/CarryingComponent.cs b/Content.Server/Nyanotrasen/Carrying/CarryingComponent.cs deleted file mode 100644 index e79460595b9..00000000000 --- a/Content.Server/Nyanotrasen/Carrying/CarryingComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Content.Server.Carrying -{ - /// - /// Added to an entity when they are carrying somebody. - /// - [RegisterComponent] - public sealed partial class CarryingComponent : Component - { - public EntityUid Carried = default!; - } -} diff --git a/Content.Server/Nyanotrasen/Carrying/CarryingSystem.cs b/Content.Server/Nyanotrasen/Carrying/CarryingSystem.cs deleted file mode 100644 index 0faff7d8078..00000000000 --- a/Content.Server/Nyanotrasen/Carrying/CarryingSystem.cs +++ /dev/null @@ -1,432 +0,0 @@ -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; -using Content.Shared.Buckle.Components; -using Content.Shared.Hands.Components; -using Content.Shared.Hands; -using Content.Shared.Stunnable; -using Content.Shared.Interaction.Events; -using Content.Shared.Verbs; -using Content.Shared.Climbing.Events; // Added this. -using Content.Shared.Carrying; -using Content.Shared.Movement.Events; -using Content.Shared.Movement.Systems; -using Content.Shared.Pulling; -using Content.Shared.Standing; -using Content.Shared.ActionBlocker; -using Content.Shared.Inventory.VirtualItem; -using Content.Shared.Item; -using Content.Shared.Throwing; -using Content.Shared.Mobs.Systems; -using Content.Shared.Movement.Pulling.Components; -using Content.Shared.Movement.Pulling.Events; -using Content.Shared.Movement.Pulling.Systems; -using Content.Shared.Nyanotrasen.Item.PseudoItem; -using Content.Shared.Storage; -using Robust.Shared.Map.Components; -using Robust.Shared.Physics.Components; - -namespace Content.Server.Carrying -{ - public sealed class CarryingSystem : EntitySystem - { - [Dependency] private readonly VirtualItemSystem _virtualItemSystem = default!; - [Dependency] private readonly CarryingSlowdownSystem _slowdown = default!; - [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly StandingStateSystem _standingState = default!; - [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; - [Dependency] private readonly PullingSystem _pullingSystem = default!; - [Dependency] private readonly MobStateSystem _mobStateSystem = default!; - [Dependency] private readonly EscapeInventorySystem _escapeInventorySystem = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!; - [Dependency] private readonly RespiratorSystem _respirator = default!; - [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly PseudoItemSystem _pseudoItem = default!; // Needed for fitting check - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent>(AddCarryVerb); - SubscribeLocalEvent>(AddInsertCarriedVerb); - SubscribeLocalEvent(OnVirtualItemDeleted); - SubscribeLocalEvent(OnThrow); - SubscribeLocalEvent(OnParentChanged); - SubscribeLocalEvent(OnMobStateChanged); - SubscribeLocalEvent(OnInteractionAttempt); - SubscribeLocalEvent(OnMoveInput); - SubscribeLocalEvent(OnMoveAttempt); - SubscribeLocalEvent(OnStandAttempt); - SubscribeLocalEvent(OnInteractedWith); - SubscribeLocalEvent(OnPullAttempt); - SubscribeLocalEvent(OnStartClimb); - SubscribeLocalEvent(OnBuckleChange); - SubscribeLocalEvent(OnBuckleChange); - SubscribeLocalEvent(OnBuckleChange); - SubscribeLocalEvent(OnBuckleChange); - SubscribeLocalEvent(OnDoAfter); - } - - private void AddCarryVerb(EntityUid uid, CarriableComponent component, GetVerbsEvent args) - { - if (!args.CanInteract || !args.CanAccess) - return; - - if (!CanCarry(args.User, uid, component)) - return; - - if (HasComp(args.User)) // yeah not dealing with that - return; - - if (HasComp(args.User) || HasComp(args.Target)) - return; - - if (!_mobStateSystem.IsAlive(args.User)) - return; - - if (args.User == args.Target) - return; - - AlternativeVerb verb = new() - { - Act = () => - { - StartCarryDoAfter(args.User, uid, component); - }, - Text = Loc.GetString("carry-verb"), - Priority = 2 - }; - args.Verbs.Add(verb); - } - - private void AddInsertCarriedVerb(EntityUid uid, CarryingComponent component, GetVerbsEvent 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(toInsert, out var pseudoItem)) - return; - - if (!TryComp(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); - } - - /// - /// Since the carried entity is stored as 2 virtual items, when deleted we want to drop them. - /// - private void OnVirtualItemDeleted(EntityUid uid, CarryingComponent component, VirtualItemDeletedEvent args) - { - if (!HasComp(args.BlockingEntity)) - return; - - DropCarried(uid, args.BlockingEntity); - } - - /// - /// Basically using virtual item passthrough to throw the carried person. A new age! - /// Maybe other things besides throwing should use virt items like this... - /// - private void OnThrow(EntityUid uid, CarryingComponent component, BeforeThrowEvent args) - { - if (!TryComp(args.ItemUid, out var virtItem) || !HasComp(virtItem.BlockingEntity)) - return; - - args.ItemUid = virtItem.BlockingEntity; - - var multiplier = MassContest(uid, virtItem.BlockingEntity); - args.ThrowSpeed = 5f * multiplier; - } - - private void OnParentChanged(EntityUid uid, CarryingComponent component, ref EntParentChangedMessage args) - { - var xform = Transform(uid); - if (xform.MapUid != 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); - } - - private void OnMobStateChanged(EntityUid uid, CarryingComponent component, MobStateChangedEvent args) - { - DropCarried(uid, component.Carried); - } - - /// - /// Only let the person being carried interact with their carrier and things on their person. - /// - private void OnInteractionAttempt(EntityUid uid, BeingCarriedComponent component, InteractionAttemptEvent args) - { - if (args.Target == null) - return; - - var targetParent = Transform(args.Target.Value).ParentUid; - - if (args.Target.Value != component.Carrier && targetParent != component.Carrier && targetParent != uid) - args.Cancelled = true; - } - - /// - /// Try to escape via the escape inventory system. - /// - private void OnMoveInput(EntityUid uid, BeingCarriedComponent component, ref MoveInputEvent args) - { - if (!TryComp(uid, out var escape)) - return; - - if (!args.HasDirectionalMovement) - return; - - if (_actionBlockerSystem.CanInteract(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)); - } - } - - private void OnMoveAttempt(EntityUid uid, BeingCarriedComponent component, UpdateCanMoveEvent args) - { - args.Cancel(); - } - - private void OnStandAttempt(EntityUid uid, BeingCarriedComponent component, StandAttemptEvent args) - { - args.Cancel(); - } - - private void OnInteractedWith(EntityUid uid, BeingCarriedComponent component, GettingInteractedWithAttemptEvent args) - { - if (args.Uid != component.Carrier) - args.Cancelled = true; - } - - private void OnPullAttempt(EntityUid uid, BeingCarriedComponent component, PullAttemptEvent args) - { - args.Cancelled = true; - } - - private void OnStartClimb(EntityUid uid, BeingCarriedComponent component, ref StartClimbEvent args) - { - DropCarried(component.Carrier, uid); - } - - private void OnBuckleChange(EntityUid uid, BeingCarriedComponent component, TEvent args) // Augh - { - DropCarried(component.Carrier, uid); - } - - private void OnDoAfter(EntityUid uid, CarriableComponent component, CarryDoAfterEvent args) - { - component.CancelToken = null; - if (args.Handled || args.Cancelled) - return; - - if (!CanCarry(args.Args.User, uid, component)) - return; - - Carry(args.Args.User, uid); - args.Handled = true; - } - private void StartCarryDoAfter(EntityUid carrier, EntityUid carried, CarriableComponent component) - { - TimeSpan length = GetPickupDuration(carrier, carried); - - if (length >= TimeSpan.FromSeconds(9)) - { - _popupSystem.PopupEntity(Loc.GetString("carry-too-heavy"), carried, carrier, Shared.Popups.PopupType.SmallCaution); - return; - } - - if (!HasComp(carried)) - length *= 2f; - - component.CancelToken = new CancellationTokenSource(); - - var ev = new CarryDoAfterEvent(); - var args = new DoAfterArgs(EntityManager, carrier, length, ev, carried, target: carried) - { - BreakOnMove = true, - NeedHand = true - }; - - _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) - { - if (TryComp(carried, out var pullable)) - _pullingSystem.TryStopPull(carried, pullable); - - var carrierXform = Transform(carrier); - var xform = Transform(carried); - _transform.AttachToGridOrMap(carrier, carrierXform); - _transform.AttachToGridOrMap(carried, xform); - xform.Coordinates = carrierXform.Coordinates; - _transform.SetParent(carried, xform, carrier, carrierXform); - - _virtualItemSystem.TrySpawnVirtualItemInHand(carried, carrier); - _virtualItemSystem.TrySpawnVirtualItemInHand(carried, carrier); - var carryingComp = EnsureComp(carrier); - ApplyCarrySlowdown(carrier, carried); - var carriedComp = EnsureComp(carried); - EnsureComp(carried); - - carryingComp.Carried = carried; - carriedComp.Carrier = carrier; - - _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(carrier) || HasComp(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(carrier); // get rid of this first so we don't recusrively fire that event - RemComp(carrier); - RemComp(carried); - RemComp(carried); - _actionBlockerSystem.UpdateCanMove(carried); - _virtualItemSystem.DeleteInHandsMatching(carrier, carried); - Transform(carried).AttachToGridOrMap(); - _standingState.Stand(carried); - _movementSpeed.RefreshMovementSpeedModifiers(carrier); - } - - private void ApplyCarrySlowdown(EntityUid carrier, EntityUid carried) - { - var massRatio = MassContest(carrier, carried); - - if (massRatio == 0) - massRatio = 1; - - var massRatioSq = Math.Pow(massRatio, 2); - var modifier = (1 - (0.15 / massRatioSq)); - modifier = Math.Max(0.1, modifier); - var slowdownComp = EnsureComp(carrier); - _slowdown.SetModifier(carrier, (float) modifier, (float) modifier, slowdownComp); - } - - public bool CanCarry(EntityUid carrier, EntityUid carried, CarriableComponent? carriedComp = null) - { - if (!Resolve(carried, ref carriedComp, false)) - return false; - - if (carriedComp.CancelToken != null) - return false; - - if (!HasComp(Transform(carrier).ParentUid)) - return false; - - if (HasComp(carrier) || HasComp(carried)) - return false; - - // if (_respirator.IsReceivingCPR(carried)) - // return false; - - if (!TryComp(carrier, out var hands)) - return false; - - if (hands.CountFreeHands() < carriedComp.FreeHandsRequired) - return false; - - return true; - } - - private float MassContest(EntityUid roller, EntityUid target, PhysicsComponent? rollerPhysics = null, PhysicsComponent? targetPhysics = null) - { - if (!Resolve(roller, ref rollerPhysics, false) || !Resolve(target, ref targetPhysics, false)) - return 1f; - - if (targetPhysics.FixturesMass == 0) - return 1f; - - 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(); - 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(); - } - } -} diff --git a/Content.Server/Nyanotrasen/Item/PseudoItem/PseudoItemSystem.cs b/Content.Server/Nyanotrasen/Item/PseudoItem/PseudoItemSystem.cs index 6df387e6ba8..7437d293da6 100644 --- a/Content.Server/Nyanotrasen/Item/PseudoItem/PseudoItemSystem.cs +++ b/Content.Server/Nyanotrasen/Item/PseudoItem/PseudoItemSystem.cs @@ -1,9 +1,9 @@ -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.DeltaV.Carrying; using Content.Shared.DoAfter; using Content.Shared.IdentityManagement; using Content.Shared.Item; diff --git a/Content.Server/Resist/EscapeInventorySystem.cs b/Content.Server/Resist/EscapeInventorySystem.cs index 93a83465861..eec8ebb5072 100644 --- a/Content.Server/Resist/EscapeInventorySystem.cs +++ b/Content.Server/Resist/EscapeInventorySystem.cs @@ -1,6 +1,5 @@ using Content.Server.Popups; using Content.Shared.Storage; -using Content.Server.Carrying; // Carrying system from Nyanotrasen. using Content.Shared.Inventory; using Content.Shared.Hands.EntitySystems; using Content.Shared.Storage.Components; @@ -25,7 +24,6 @@ public sealed class EscapeInventorySystem : EntitySystem [Dependency] private readonly SharedContainerSystem _containerSystem = default!; [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 /// @@ -109,13 +107,6 @@ private void OnEscape(EntityUid uid, CanEscapeInventoryComponent component, Esca if (args.Handled || args.Cancelled) return; - if (TryComp(uid, out var carried)) // Start of carrying system of nyanotrasen. - { - _carryingSystem.DropCarried(carried.Carrier, uid); - return; - } // End of carrying system of nyanotrasen. - - _containerSystem.AttachParentToContainerOrGrid((uid, Transform(uid))); args.Handled = true; } diff --git a/Content.Shared/DeltaV/Carrying/BeingCarriedComponent.cs b/Content.Shared/DeltaV/Carrying/BeingCarriedComponent.cs new file mode 100644 index 00000000000..7e519e7e04b --- /dev/null +++ b/Content.Shared/DeltaV/Carrying/BeingCarriedComponent.cs @@ -0,0 +1,14 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.DeltaV.Carrying; + +/// +/// Stores the carrier of an entity being carried. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(CarryingSystem))] +[AutoGenerateComponentState] +public sealed partial class BeingCarriedComponent : Component +{ + [DataField, AutoNetworkedField] + public EntityUid Carrier; +} diff --git a/Content.Shared/DeltaV/Carrying/CarriableComponent.cs b/Content.Shared/DeltaV/Carrying/CarriableComponent.cs new file mode 100644 index 00000000000..ad1968aec62 --- /dev/null +++ b/Content.Shared/DeltaV/Carrying/CarriableComponent.cs @@ -0,0 +1,14 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.DeltaV.Carrying; + +[RegisterComponent, NetworkedComponent, Access(typeof(CarryingSystem))] +public sealed partial class CarriableComponent : Component +{ + /// + /// Number of free hands required + /// to carry the entity + /// + [DataField] + public int FreeHandsRequired = 2; +} diff --git a/Content.Shared/DeltaV/Carrying/CarryDoAfterEvent.cs b/Content.Shared/DeltaV/Carrying/CarryDoAfterEvent.cs new file mode 100644 index 00000000000..7ea0375518a --- /dev/null +++ b/Content.Shared/DeltaV/Carrying/CarryDoAfterEvent.cs @@ -0,0 +1,7 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared.DeltaV.Carrying; + +[Serializable, NetSerializable] +public sealed partial class CarryDoAfterEvent : SimpleDoAfterEvent; diff --git a/Content.Shared/DeltaV/Carrying/CarryingComponent.cs b/Content.Shared/DeltaV/Carrying/CarryingComponent.cs new file mode 100644 index 00000000000..e6661da0e04 --- /dev/null +++ b/Content.Shared/DeltaV/Carrying/CarryingComponent.cs @@ -0,0 +1,14 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.DeltaV.Carrying; + +/// +/// Added to an entity when they are carrying somebody. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(CarryingSystem))] +[AutoGenerateComponentState] +public sealed partial class CarryingComponent : Component +{ + [DataField, AutoNetworkedField] + public EntityUid Carried; +} diff --git a/Content.Shared/DeltaV/Carrying/CarryingSlowdownComponent.cs b/Content.Shared/DeltaV/Carrying/CarryingSlowdownComponent.cs new file mode 100644 index 00000000000..9e1be89370c --- /dev/null +++ b/Content.Shared/DeltaV/Carrying/CarryingSlowdownComponent.cs @@ -0,0 +1,14 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.DeltaV.Carrying; + +[RegisterComponent, NetworkedComponent, Access(typeof(CarryingSlowdownSystem))] +[AutoGenerateComponentState] +public sealed partial class CarryingSlowdownComponent : Component +{ + /// + /// Modifier for both walk and sprint speed. + /// + [DataField, AutoNetworkedField] + public float Modifier = 1.0f; +} diff --git a/Content.Shared/DeltaV/Carrying/CarryingSlowdownSystem.cs b/Content.Shared/DeltaV/Carrying/CarryingSlowdownSystem.cs new file mode 100644 index 00000000000..677c53eedc0 --- /dev/null +++ b/Content.Shared/DeltaV/Carrying/CarryingSlowdownSystem.cs @@ -0,0 +1,29 @@ +using Content.Shared.Movement.Systems; + +namespace Content.Shared.DeltaV.Carrying; + +public sealed class CarryingSlowdownSystem : EntitySystem +{ + [Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnRefreshMoveSpeed); + } + + public void SetModifier(Entity ent, float modifier) + { + ent.Comp ??= EnsureComp(ent); + ent.Comp.Modifier = modifier; + Dirty(ent, ent.Comp); + + _movementSpeed.RefreshMovementSpeedModifiers(ent); + } + + private void OnRefreshMoveSpeed(Entity ent, ref RefreshMovementSpeedModifiersEvent args) + { + args.ModifySpeed(ent.Comp.Modifier, ent.Comp.Modifier); + } +} diff --git a/Content.Shared/DeltaV/Carrying/CarryingSystem.cs b/Content.Shared/DeltaV/Carrying/CarryingSystem.cs new file mode 100644 index 00000000000..2b47c49abd1 --- /dev/null +++ b/Content.Shared/DeltaV/Carrying/CarryingSystem.cs @@ -0,0 +1,384 @@ +using Content.Shared.ActionBlocker; +using Content.Shared.Buckle.Components; +using Content.Shared.Climbing.Events; +using Content.Shared.DoAfter; +using Content.Shared.Hands; +using Content.Shared.Hands.Components; +using Content.Shared.Interaction.Events; +using Content.Shared.Inventory.VirtualItem; +using Content.Shared.Item; +using Content.Shared.Mobs; +using Content.Shared.Movement.Events; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Events; +using Content.Shared.Movement.Pulling.Systems; +using Content.Shared.Movement.Systems; +using Content.Shared.Nyanotrasen.Item.PseudoItem; +using Content.Shared.Popups; +using Content.Shared.Pulling; +using Content.Shared.Resist; +using Content.Shared.Standing; +using Content.Shared.Storage; +using Content.Shared.Stunnable; +using Content.Shared.Throwing; +using Content.Shared.Verbs; +using Robust.Shared.Map.Components; +using Robust.Shared.Network; +using Robust.Shared.Physics.Components; +using System.Numerics; + +namespace Content.Shared.DeltaV.Carrying; + +public sealed class CarryingSystem : EntitySystem +{ + [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!; + [Dependency] private readonly CarryingSlowdownSystem _slowdown = default!; + [Dependency] private readonly INetManager _net = default!; + [Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!; + [Dependency] private readonly PullingSystem _pulling = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedPseudoItemSystem _pseudoItem = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly StandingStateSystem _standingState = default!; + [Dependency] private readonly SharedVirtualItemSystem _virtualItem = default!; + + private EntityQuery _physicsQuery; + + public override void Initialize() + { + base.Initialize(); + + _physicsQuery = GetEntityQuery(); + + SubscribeLocalEvent>(AddCarryVerb); + SubscribeLocalEvent>(AddInsertCarriedVerb); + SubscribeLocalEvent(OnVirtualItemDeleted); + SubscribeLocalEvent(OnThrow); + SubscribeLocalEvent(OnParentChanged); + SubscribeLocalEvent(OnMobStateChanged); + SubscribeLocalEvent(OnInteractionAttempt); + SubscribeLocalEvent(OnMoveAttempt); + SubscribeLocalEvent(OnStandAttempt); + SubscribeLocalEvent(OnInteractedWith); + SubscribeLocalEvent(OnPullAttempt); + SubscribeLocalEvent(OnDrop); + SubscribeLocalEvent(OnDrop); + SubscribeLocalEvent(OnDrop); + SubscribeLocalEvent(OnDrop); + SubscribeLocalEvent(OnDrop); + SubscribeLocalEvent(OnDrop); + SubscribeLocalEvent(OnDoAfter); + } + + private void AddCarryVerb(Entity ent, ref GetVerbsEvent args) + { + var user = args.User; + var target = args.Target; + if (!args.CanInteract || !args.CanAccess || user == target) + return; + + if (!CanCarry(user, ent)) + return; + + args.Verbs.Add(new AlternativeVerb() + { + Act = () => StartCarryDoAfter(user, ent), + Text = Loc.GetString("carry-verb"), + Priority = 2 + }); + } + + private void AddInsertCarriedVerb(Entity ent, ref GetVerbsEvent 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 + // AKA put carried felenid into a duffelbag + if (args.Using is not {} carried || !args.CanAccess || !TryComp(carried, out var pseudoItem)) + return; + + var target = args.Target; + if (!TryComp(target, out var storageComp)) + return; + + if (!_pseudoItem.CheckItemFits((carried, pseudoItem), (target, storageComp))) + return; + + args.Verbs.Add(new InnateVerb() + { + Act = () => + { + DropCarried(ent, carried); + _pseudoItem.TryInsert(target, carried, pseudoItem, storageComp); + }, + Text = Loc.GetString("action-name-insert-other", ("target", carried)), + Priority = 2 + }); + } + + /// + /// Since the carried entity is stored as 2 virtual items, when deleted we want to drop them. + /// + private void OnVirtualItemDeleted(Entity ent, ref VirtualItemDeletedEvent args) + { + if (HasComp(args.BlockingEntity)) + DropCarried(ent, args.BlockingEntity); + } + + /// + /// Basically using virtual item passthrough to throw the carried person. A new age! + /// Maybe other things besides throwing should use virt items like this... + /// + private void OnThrow(Entity ent, ref BeforeThrowEvent args) + { + if (!TryComp(args.ItemUid, out var virtItem) || !HasComp(virtItem.BlockingEntity)) + return; + + var carried = virtItem.BlockingEntity; + args.ItemUid = carried; + + args.ThrowSpeed = 5f * MassContest(ent, carried); + } + + private void OnParentChanged(Entity ent, ref EntParentChangedMessage args) + { + var xform = Transform(ent); + if (xform.MapUid != args.OldMapId) + return; + + // Do not drop the carried entity if the new parent is a grid + if (xform.ParentUid == xform.GridUid) + return; + + DropCarried(ent, ent.Comp.Carried); + } + + private void OnMobStateChanged(Entity ent, ref MobStateChangedEvent args) + { + DropCarried(ent, ent.Comp.Carried); + } + + /// + /// Only let the person being carried interact with their carrier and things on their person. + /// + private void OnInteractionAttempt(Entity ent, ref InteractionAttemptEvent args) + { + if (args.Target is not {} target) + return; + + var targetParent = Transform(target).ParentUid; + + var carrier = ent.Comp.Carrier; + if (target != carrier && targetParent != carrier && targetParent != ent.Owner) + args.Cancelled = true; + } + + private void OnMoveAttempt(Entity ent, ref UpdateCanMoveEvent args) + { + args.Cancel(); + } + + private void OnStandAttempt(Entity ent, ref StandAttemptEvent args) + { + args.Cancel(); + } + + private void OnInteractedWith(Entity ent, ref GettingInteractedWithAttemptEvent args) + { + if (args.Uid != ent.Comp.Carrier) + args.Cancelled = true; + } + + private void OnPullAttempt(Entity ent, ref PullAttemptEvent args) + { + args.Cancelled = true; + } + + private void OnDrop(Entity ent, ref TEvent args) // Augh + { + DropCarried(ent.Comp.Carrier, ent); + } + + private void OnDoAfter(Entity ent, ref CarryDoAfterEvent args) + { + if (args.Handled || args.Cancelled) + return; + + if (!CanCarry(args.Args.User, ent)) + return; + + Carry(args.Args.User, ent); + args.Handled = true; + } + + private void StartCarryDoAfter(EntityUid carrier, Entity carried) + { + TimeSpan length = GetPickupDuration(carrier, carried); + + if (length.TotalSeconds >= 9f) + { + _popup.PopupClient(Loc.GetString("carry-too-heavy"), carried, carrier, PopupType.SmallCaution); + return; + } + + if (!HasComp(carried)) + length *= 2f; + + var ev = new CarryDoAfterEvent(); + var args = new DoAfterArgs(EntityManager, carrier, length, ev, carried, target: carried) + { + BreakOnMove = true, + NeedHand = true + }; + + _doAfter.TryStartDoAfter(args); + + // Show a popup to the person getting picked up + _popup.PopupEntity(Loc.GetString("carry-started", ("carrier", carrier)), carried, carried); + } + + private void Carry(EntityUid carrier, EntityUid carried) + { + if (TryComp(carried, out var pullable)) + _pulling.TryStopPull(carried, pullable); + + var carrierXform = Transform(carrier); + var xform = Transform(carried); + _transform.AttachToGridOrMap(carrier, carrierXform); + _transform.AttachToGridOrMap(carried, xform); + _transform.SetParent(carried, xform, carrier, carrierXform); + + var carryingComp = EnsureComp(carrier); + carryingComp.Carried = carried; + Dirty(carrier, carryingComp); + var carriedComp = EnsureComp(carried); + carriedComp.Carrier = carrier; + Dirty(carried, carriedComp); + EnsureComp(carried); + + ApplyCarrySlowdown(carrier, carried); + + _actionBlocker.UpdateCanMove(carried); + + if (_net.IsClient) // no spawning prediction + return; + + _virtualItem.TrySpawnVirtualItemInHand(carried, carrier); + _virtualItem.TrySpawnVirtualItemInHand(carried, carrier); + } + + public bool TryCarry(EntityUid carrier, Entity toCarry) + { + if (!Resolve(toCarry, ref toCarry.Comp, false)) + return false; + + if (!CanCarry(carrier, (toCarry, toCarry.Comp))) + return false; + + // The second one means that carrier is a pseudo-item and is inside a bag. + if (HasComp(carrier) || HasComp(carrier)) + return false; + + if (GetPickupDuration(carrier, toCarry).TotalSeconds > 9f) + return false; + + Carry(carrier, toCarry); + return true; + } + + public void DropCarried(EntityUid carrier, EntityUid carried) + { + Drop(carried); + RemComp(carrier); // get rid of this first so we don't recursively fire that event + RemComp(carrier); + _virtualItem.DeleteInHandsMatching(carrier, carried); + _movementSpeed.RefreshMovementSpeedModifiers(carrier); + } + + private void Drop(EntityUid carried) + { + RemComp(carried); + RemComp(carried); // TODO SHITMED: make sure this doesnt let you make someone with no legs walk + _actionBlocker.UpdateCanMove(carried); + Transform(carried).AttachToGridOrMap(); + _standingState.Stand(carried); + } + + private void ApplyCarrySlowdown(EntityUid carrier, EntityUid carried) + { + var massRatio = MassContest(carrier, carried); + + if (massRatio == 0) + massRatio = 1; + + var massRatioSq = Math.Pow(massRatio, 2); + var modifier = (1 - (0.15 / massRatioSq)); + modifier = Math.Max(0.1, modifier); + _slowdown.SetModifier(carrier, (float) modifier); + } + + public bool CanCarry(EntityUid carrier, Entity carried) + { + return + carrier != carried.Owner && + // can't carry multiple people, even if you have 4 hands it will break invariants when removing carryingcomponent for first carried person + !HasComp(carrier) && + // can't carry someone in a locker, buckled, etc + HasComp(Transform(carrier).ParentUid) && + // no tower of spacemen or stack overflow + !HasComp(carrier) && + !HasComp(carried) && + // finally check that there are enough free hands + TryComp(carrier, out var hands) && + hands.CountFreeHands() >= carried.Comp.FreeHandsRequired; + } + + private float MassContest(EntityUid roller, EntityUid target) + { + if (!_physicsQuery.TryComp(roller, out var rollerPhysics) || !_physicsQuery.TryComp(target, out var targetPhysics)) + return 1f; + + if (targetPhysics.FixturesMass == 0) + return 1f; + + 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) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var carried, out var comp, out var xform)) + { + var carrier = comp.Carrier; + if (TerminatingOrDeleted(carrier)) + { + RemCompDeferred(carried); + 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 (xform.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 + _transform.SetLocalPosition(carried, Vector2.Zero); + } + } +} diff --git a/Content.Shared/Nyanotrasen/Carrying/CarryingDoAfterEvent.cs b/Content.Shared/Nyanotrasen/Carrying/CarryingDoAfterEvent.cs deleted file mode 100644 index 6acd6b775f3..00000000000 --- a/Content.Shared/Nyanotrasen/Carrying/CarryingDoAfterEvent.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Robust.Shared.Serialization; -using Content.Shared.DoAfter; - -namespace Content.Shared.Carrying -{ - [Serializable, NetSerializable] - public sealed partial class CarryDoAfterEvent : SimpleDoAfterEvent - { - } -} diff --git a/Content.Shared/Nyanotrasen/Carrying/CarryingSlowdownComponent.cs b/Content.Shared/Nyanotrasen/Carrying/CarryingSlowdownComponent.cs deleted file mode 100644 index aabde66af0d..00000000000 --- a/Content.Shared/Nyanotrasen/Carrying/CarryingSlowdownComponent.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Robust.Shared.GameStates; -using Robust.Shared.Serialization; - -namespace Content.Shared.Carrying -{ - [RegisterComponent, NetworkedComponent, Access(typeof(CarryingSlowdownSystem))] - - public sealed partial class CarryingSlowdownComponent : Component - { - [DataField("walkModifier", required: true)] [ViewVariables(VVAccess.ReadWrite)] - public float WalkModifier = 1.0f; - - [DataField("sprintModifier", required: true)] [ViewVariables(VVAccess.ReadWrite)] - public float SprintModifier = 1.0f; - } - - [Serializable, NetSerializable] - public sealed class CarryingSlowdownComponentState : ComponentState - { - public float WalkModifier; - public float SprintModifier; - public CarryingSlowdownComponentState(float walkModifier, float sprintModifier) - { - WalkModifier = walkModifier; - SprintModifier = sprintModifier; - } - } -} diff --git a/Content.Shared/Nyanotrasen/Carrying/CarryingSlowdownSystem.cs b/Content.Shared/Nyanotrasen/Carrying/CarryingSlowdownSystem.cs deleted file mode 100644 index 9b9c8cec10f..00000000000 --- a/Content.Shared/Nyanotrasen/Carrying/CarryingSlowdownSystem.cs +++ /dev/null @@ -1,47 +0,0 @@ -using Content.Shared.Movement.Systems; -using Robust.Shared.GameStates; - -namespace Content.Shared.Carrying -{ - public sealed class CarryingSlowdownSystem : EntitySystem - { - [Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnGetState); - SubscribeLocalEvent(OnHandleState); - SubscribeLocalEvent(OnRefreshMoveSpeed); - } - - public void SetModifier(EntityUid uid, float walkSpeedModifier, float sprintSpeedModifier, CarryingSlowdownComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - component.WalkModifier = walkSpeedModifier; - component.SprintModifier = sprintSpeedModifier; - _movementSpeed.RefreshMovementSpeedModifiers(uid); - } - private void OnGetState(EntityUid uid, CarryingSlowdownComponent component, ref ComponentGetState args) - { - args.State = new CarryingSlowdownComponentState(component.WalkModifier, component.SprintModifier); - } - - private void OnHandleState(EntityUid uid, CarryingSlowdownComponent component, ref ComponentHandleState args) - { - if (args.Current is CarryingSlowdownComponentState state) - { - component.WalkModifier = state.WalkModifier; - component.SprintModifier = state.SprintModifier; - - _movementSpeed.RefreshMovementSpeedModifiers(uid); - } - } - private void OnRefreshMoveSpeed(EntityUid uid, CarryingSlowdownComponent component, RefreshMovementSpeedModifiersEvent args) - { - args.ModifySpeed(component.WalkModifier, component.SprintModifier); - } - } -}