diff --git a/Content.Server/Resist/EscapeInventorySystem.cs b/Content.Server/Resist/EscapeInventorySystem.cs index ea95f4c7fb..0f1ef1b518 100644 --- a/Content.Server/Resist/EscapeInventorySystem.cs +++ b/Content.Server/Resist/EscapeInventorySystem.cs @@ -1,5 +1,3 @@ -using Content.Server._Sunrise.Carrying; -using Content.Server.Carrying; using Content.Server.Popups; using Content.Shared.Storage.Components; using Content.Shared.ActionBlocker; @@ -11,6 +9,7 @@ using Content.Shared.Movement.Events; using Content.Shared.Resist; using Content.Shared.Storage; using Robust.Shared.Containers; +using Content.Shared._Sunrise.Carrying; namespace Content.Server.Resist; @@ -21,7 +20,7 @@ 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!; + [Dependency] private readonly SharedCarryingSystem _carryingSystem = default!; public override void Initialize() { diff --git a/Content.Server/_Sunrise/Carrying/CarryingSystem.cs b/Content.Server/_Sunrise/Carrying/CarryingSystem.cs index 217394b638..4034989556 100644 --- a/Content.Server/_Sunrise/Carrying/CarryingSystem.cs +++ b/Content.Server/_Sunrise/Carrying/CarryingSystem.cs @@ -1,169 +1,22 @@ -using System.Threading; -using Content.Server.Carrying; -using Content.Server.DoAfter; -using Content.Server.Popups; using Content.Server.Resist; using Content.Shared._Sunrise.Carrying; -using Content.Shared.IdentityManagement; using Content.Shared.ActionBlocker; -using Content.Shared.Buckle.Components; -using Content.Shared.Carrying; -using Content.Shared.Climbing.Events; -using Content.Shared.DoAfter; -using Content.Shared.Hands; -using Content.Shared.Popups; -using Content.Shared.Hands.Components; -using Content.Shared.Interaction; -using Content.Shared.Interaction.Events; -using Content.Shared.Inventory.VirtualItem; -using Content.Shared.Mobs; -using Content.Shared.Mobs.Systems; 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.Standing; -using Content.Shared.Stunnable; -using Content.Shared.Throwing; -using Content.Shared.Verbs; -using Robust.Shared.Player; -using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; namespace Content.Server._Sunrise.Carrying { public sealed class CarryingSystem : EntitySystem { - private readonly float _maxThrowSpeed = 15f; - - [Dependency] private readonly SharedVirtualItemSystem _virtualItemSystem = default!; - [Dependency] private readonly CarryingSlowdownSystem _slowdown = default!; - [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly SharedStandingStateSystem _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 SharedInteractionSystem _interactionSystem = default!; - [Dependency] private readonly ThrowingSystem _throwingSystem = default!; - [Dependency] private readonly IEntityManager _entityManager = default!; + [Dependency] private readonly SharedCarryingSystem _sharedCarrying = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent>(AddCarryVerb); - 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(OnDoAfter); - } - - 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 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 (!_interactionSystem.InRangeUnobstructed(args.User, uid, 0.75f)) - 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 OnVirtualItemDeleted(EntityUid uid, CarryingComponent component, VirtualItemDeletedEvent args) - { - if (!HasComp(args.BlockingEntity)) - return; - - DropCarried(uid, args.BlockingEntity); - } - - private void OnThrow(EntityUid uid, CarryingComponent component, BeforeThrowEvent args) - { - if (!TryComp(args.ItemUid, out var virtItem) || !HasComp(virtItem.BlockingEntity)) - return; - - var multiplier = MassContest(uid, virtItem.BlockingEntity); - - var throwSpeed = 3f * multiplier > _maxThrowSpeed ? _maxThrowSpeed : 3f * multiplier; - - _throwingSystem.TryThrow(virtItem.BlockingEntity, args.Direction, throwSpeed, uid); - } - - private void OnParentChanged(EntityUid uid, CarryingComponent component, ref EntParentChangedMessage args) - { - var xform = Transform(uid); - - if (xform.ParentUid == args.OldParent) - 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); - } - - 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; } /// @@ -179,174 +32,8 @@ namespace Content.Server._Sunrise.Carrying if (_actionBlockerSystem.CanInteract(uid, component.Carrier)) { - _escapeInventorySystem.AttemptEscape(uid, component.Carrier, escape, MassContest(component.Carrier, uid)); + _escapeInventorySystem.AttemptEscape(uid, component.Carrier, escape, _sharedCarrying.MassContest(component.Carrier, uid) / 2); } } - - 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, ref BuckledEvent args) - { - 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, component); - args.Handled = true; - } - private void StartCarryDoAfter(EntityUid carrier, EntityUid carried, CarriableComponent component) - { - TimeSpan length = TimeSpan.FromSeconds(1); - - var mod = MassContest(carrier, carried); - - if (mod != 0) - length /= mod; - - if (length >= TimeSpan.FromSeconds(5)) - { - _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); - - - ShowCarryPopup("carry-starting", Filter.Entities(carrier), PopupType.Medium, carrier, carried); - ShowCarryPopup("carry-started", Filter.Entities(carried), PopupType.Medium, carrier, carried); - ShowCarryPopup("carry-observed", Filter.PvsExcept(carrier).RemoveWhereAttachedEntity(e => e == carried), PopupType.MediumCaution, carrier, carried); - } - - private void Carry(EntityUid carrier, EntityUid carried, CarriableComponent component) - { - if (TryComp(carried, out var pullable)) - _pullingSystem.TryStopPull(carried, pullable, carrier); - - Transform(carrier).AttachToGridOrMap(); - Transform(carried).Coordinates = Transform(carrier).Coordinates; - Transform(carried).AttachParent(carrier); - for (var i = 0; i < component.FreeHandsRequired; i++) - { - _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 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); - if (_entityManager.TryGetComponent(carried, out var transformComponent)) - { - transformComponent.AttachToGridOrMap(); - } - else - { - Console.WriteLine($"TransformComponent отсутствует у сущности {carried}."); - } - _standingState.Stand(carried); - _movementSpeed.RefreshMovementSpeedModifiers(carrier); - - var ev = new CarryDroppedEvent(); - RaiseLocalEvent(carried, ref ev); - } - - 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 (!TryComp(carrier, out var hands)) - return false; - - if (hands.CountFreeHands() < carriedComp.FreeHandsRequired) - return false; - - return true; - } - - private void ShowCarryPopup(string locString, Filter filter, PopupType type, EntityUid carrier, EntityUid carried) - { - _popupSystem.PopupEntity(Loc.GetString(locString, ("carrier", Identity.Name(carrier, EntityManager)), ("target", Identity.Name(carried, EntityManager))),carrier, filter, true, type); - } } } diff --git a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs index bc505ee989..37b4d35aac 100644 --- a/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs +++ b/Content.Shared/Movement/Pulling/Systems/PullingSystem.cs @@ -1,3 +1,4 @@ +using Content.Shared._Sunrise.Carrying; using Content.Shared.ActionBlocker; using Content.Shared.Administration.Logs; using Content.Shared.Alert; @@ -277,6 +278,13 @@ public sealed class PullingSystem : EntitySystem args.ModifySpeed(walkMod, sprintMod); return; } + // Sunrise-start + if (TryComp(component.Pulling, out var carriable)) + { + args.ModifySpeed(carriable.WalkSpeedModifier, carriable.SprintSpeedModifier); + _popup.PopupPredicted(Loc.GetString("can-carry"), uid, uid, PopupType.SmallCaution); + } + // Sunrise-end args.ModifySpeed(component.WalkSpeedModifier, component.SprintSpeedModifier); } diff --git a/Content.Server/_Sunrise/Carrying/BeingCarriedComponent.cs b/Content.Shared/_Sunrise/Carrying/BeingCarriedComponent.cs similarity index 64% rename from Content.Server/_Sunrise/Carrying/BeingCarriedComponent.cs rename to Content.Shared/_Sunrise/Carrying/BeingCarriedComponent.cs index afc78978c8..a2670b902c 100644 --- a/Content.Server/_Sunrise/Carrying/BeingCarriedComponent.cs +++ b/Content.Shared/_Sunrise/Carrying/BeingCarriedComponent.cs @@ -1,9 +1,11 @@ -namespace Content.Server.Carrying +using Robust.Shared.GameStates; + +namespace Content.Shared._Sunrise.Carrying { /// /// Stores the carrier of an entity being carried. /// - [RegisterComponent] + [RegisterComponent, NetworkedComponent] public sealed partial class BeingCarriedComponent : Component { public EntityUid Carrier = default!; diff --git a/Content.Server/_Sunrise/Carrying/CarriableComponent.cs b/Content.Shared/_Sunrise/Carrying/CarriableComponent.cs similarity index 59% rename from Content.Server/_Sunrise/Carrying/CarriableComponent.cs rename to Content.Shared/_Sunrise/Carrying/CarriableComponent.cs index 0dabdabbce..28aa6095d7 100644 --- a/Content.Server/_Sunrise/Carrying/CarriableComponent.cs +++ b/Content.Shared/_Sunrise/Carrying/CarriableComponent.cs @@ -1,8 +1,9 @@ using System.Threading; +using Robust.Shared.GameStates; -namespace Content.Server.Carrying +namespace Content.Shared._Sunrise.Carrying { - [RegisterComponent] + [RegisterComponent, NetworkedComponent] public sealed partial class CarriableComponent : Component { /// @@ -12,6 +13,10 @@ namespace Content.Server.Carrying [DataField("freeHandsRequired")] public int FreeHandsRequired = 2; - public CancellationTokenSource? CancelToken; + [DataField] + public float WalkSpeedModifier = 0.6f; + + [DataField] + public float SprintSpeedModifier = 0.6f; } } diff --git a/Content.Server/_Sunrise/Carrying/CarryingComponent.cs b/Content.Shared/_Sunrise/Carrying/CarryingComponent.cs similarity index 61% rename from Content.Server/_Sunrise/Carrying/CarryingComponent.cs rename to Content.Shared/_Sunrise/Carrying/CarryingComponent.cs index e79460595b..260316c7d8 100644 --- a/Content.Server/_Sunrise/Carrying/CarryingComponent.cs +++ b/Content.Shared/_Sunrise/Carrying/CarryingComponent.cs @@ -1,11 +1,14 @@ -namespace Content.Server.Carrying +using Robust.Shared.GameStates; + +namespace Content.Shared._Sunrise.Carrying { /// /// Added to an entity when they are carrying somebody. /// - [RegisterComponent] + [RegisterComponent, NetworkedComponent] public sealed partial class CarryingComponent : Component { + [DataField] public EntityUid Carried = default!; } } diff --git a/Content.Shared/_Sunrise/Carrying/SharedCarryingSystem.cs b/Content.Shared/_Sunrise/Carrying/SharedCarryingSystem.cs new file mode 100644 index 0000000000..68712ea3a3 --- /dev/null +++ b/Content.Shared/_Sunrise/Carrying/SharedCarryingSystem.cs @@ -0,0 +1,375 @@ +using Content.Shared.ActionBlocker; +using Content.Shared.DoAfter; +using Content.Shared.Hands.Components; +using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; +using Content.Shared.Inventory.VirtualItem; +using Content.Shared.Mobs.Systems; +using Content.Shared.Movement.Pulling.Components; +using Content.Shared.Movement.Pulling.Systems; +using Content.Shared.Popups; +using Content.Shared.Stunnable; +using Content.Shared.Verbs; +using Robust.Shared.Map.Components; +using Robust.Shared.Player; +using Content.Shared.Carrying; +using Robust.Shared.Physics.Components; +using Content.Shared.Throwing; +using Content.Shared.Mobs.Components; +using Content.Shared.Mobs; +using Content.Shared.Hands; +using Content.Shared.Standing; +using Content.Shared.Movement.Events; +using Content.Shared.Interaction.Events; +using Content.Shared.Movement.Pulling.Events; +using Content.Shared.Climbing.Events; +using Content.Shared.Buckle.Components; +using Content.Shared.Movement.Systems; +using Content.Shared.Coordinates; + +namespace Content.Shared._Sunrise.Carrying; + +public sealed class SharedCarryingSystem : EntitySystem +{ + private readonly float _maxThrowSpeed = 15f; + private const float CarryDistanceThreshold = 0.1f; + private const float BaseCarryTime = 1f; + private const float MaxCarryTime = 5f; + private const float SlowdownCoefficient = 0.15f; + private const float MinimumSpeedModifier = 0.1f; + private const float CarryInteractionRange = 0.75f; + private const float BaseThrowSpeed = 3f; + + [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; + [Dependency] private readonly MobStateSystem _mobStateSystem = default!; + [Dependency] private readonly CarryingSlowdownSystem _slowdown = default!; + [Dependency] private readonly SharedVirtualItemSystem _virtualItemSystem = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; + [Dependency] private readonly PullingSystem _pullingSystem = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly ThrowingSystem _throwingSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly SharedStandingStateSystem _standingState = default!; + [Dependency] private readonly IEntityManager _entityManager = default!; + [Dependency] private readonly MovementSpeedModifierSystem _movementSpeed = default!; + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent>(AddCarryVerb); + SubscribeLocalEvent(OnDoAfter); + + SubscribeLocalEvent(OnBeforeThrow); + SubscribeLocalEvent(OnVirtualItemDeleted); + SubscribeLocalEvent(OnParentChanged); + SubscribeLocalEvent(OnMobStateChanged); + SubscribeLocalEvent(OnDownAttempt); + + SubscribeLocalEvent(OnMoveAttempt); + SubscribeLocalEvent(OnStandAttempt); + SubscribeLocalEvent(OnInteractedWith); + SubscribeLocalEvent(OnPullAttempt); + SubscribeLocalEvent(OnStartClimb); + SubscribeLocalEvent(OnBuckleChange); + SubscribeLocalEvent(OnInteractionAttempt); + } + + public override void Update(float frametime) + { + base.Update(frametime); + + var query = EntityQueryEnumerator(); + + while (query.MoveNext(out var uid, out var carrier, out var carrierXform)) + { + if (!TryComp(uid, out StandingStateComponent? standing)) + continue; + + if (standing.CurrentState == StandingState.Laying) + { + _popupSystem.PopupClient(Loc.GetString("carry-lying-cancel"), carrier.Carried, uid, PopupType.MediumCaution); + DropCarried(uid, carrier.Carried); + continue; + } + + if (!TryComp(carrier.Carried, out TransformComponent? carriedXform)) + continue; + + if (!carrierXform.Coordinates.TryDistance(EntityManager, carriedXform.Coordinates, out var distance)) + continue; + + if (distance > CarryDistanceThreshold) + DropCarried(uid, carrier.Carried); + } + } + 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 (!_interactionSystem.InRangeUnobstructed(args.User, uid, CarryInteractionRange)) + return; + + if (!_mobStateSystem.IsAlive(args.User)) + return; + + if (args.User == args.Target) + return; + + AlternativeVerb verb = new() + { + Act = () => + { + StartCarryDoAfter(args.User, uid); + }, + Text = Loc.GetString("carry-verb"), + Priority = 2 + }; + args.Verbs.Add(verb); + } + + private void StartCarryDoAfter(EntityUid carrier, EntityUid carried) + { + TimeSpan length = TimeSpan.FromSeconds(BaseCarryTime); + + var mod = MassContest(carrier, carried); + + if (mod != 0) + length /= mod; + + if (length >= TimeSpan.FromSeconds(MaxCarryTime)) + { + _popupSystem.PopupPredicted(Loc.GetString("carry-too-heavy"), carried, carrier, PopupType.SmallCaution); + return; + } + + if (!HasComp(carried)) + length *= 2f; + + if (TryComp(carried, out var mobState) && mobState.CurrentState != MobState.Alive) + length /= 2f; + + var ev = new CarryDoAfterEvent(); + var args = new DoAfterArgs(EntityManager, carrier, length, ev, carried, target: carried) + { + BreakOnMove = true, + NeedHand = true, + MovementThreshold = 0.01f + }; + + _doAfterSystem.TryStartDoAfter(args); + + + ShowCarryPopup("carry-starting", Filter.Entities(carrier), PopupType.Medium, carrier, carried); + ShowCarryPopup("carry-started", Filter.Entities(carried), PopupType.Medium, carrier, carried); + ShowCarryPopup("carry-observed", Filter.PvsExcept(carrier).RemoveWhereAttachedEntity(e => e == carried), PopupType.MediumCaution, carrier, carried); + } + private void OnDoAfter(EntityUid uid, CarriableComponent component, CarryDoAfterEvent args) + { + if (args.Handled || args.Cancelled) + return; + + if (!CanCarry(args.Args.User, uid, component)) + return; + + Carry(args.Args.User, uid, component); + args.Handled = true; + } + + private void OnBeforeThrow(EntityUid uid, CarryingComponent component, BeforeThrowEvent args) + { + if (!TryComp(args.ItemUid, out var virtItem) || !HasComp(virtItem.BlockingEntity)) + return; + + var multiplier = MassContest(uid, virtItem.BlockingEntity); + + var throwSpeed = Math.Min(BaseThrowSpeed * multiplier, _maxThrowSpeed); + + _throwingSystem.TryThrow(virtItem.BlockingEntity, args.Direction, throwSpeed, uid); + } + + private void OnVirtualItemDeleted(EntityUid uid, CarryingComponent component, VirtualItemDeletedEvent args) + { + if (!HasComp(args.BlockingEntity)) + return; + + DropCarried(uid, args.BlockingEntity); + } + + private void OnParentChanged(EntityUid uid, CarryingComponent component, ref EntParentChangedMessage args) + { + var xform = Transform(uid); + + if (xform.ParentUid == args.OldParent) + 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); + } + + + private void OnDownAttempt(EntityUid uid, CarryingComponent comp, DownAttemptEvent args) + { + args.Cancel(); + } + + + 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, ref BuckledEvent args) + { + DropCarried(component.Carrier, uid); + } + + + 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; + } + + private void Carry(EntityUid carrier, EntityUid carried, CarriableComponent component) + { + if (TryComp(carried, out var pullable)) + _pullingSystem.TryStopPull(carried, pullable, carrier); + + _transform.AttachToGridOrMap(carrier); + _transform.SetCoordinates(carried, carrier.ToCoordinates()); + _transform.SetParent(carried, carrier); + + for (var i = 0; i < component.FreeHandsRequired; i++) + { + _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); + } + private void ApplyCarrySlowdown(EntityUid carrier, EntityUid carried) + { + var massRatio = MassContest(carrier, carried); + + if (massRatio == 0) + massRatio = 1; + + // Формула замедления: чем меньше соотношение масс, тем больше замедление + // При равных массах (ratio = 1) модификатор = 0.85 + var massRatioSq = Math.Pow(massRatio, 2); + var modifier = 1 - (SlowdownCoefficient / massRatioSq); + modifier = Math.Max(MinimumSpeedModifier, modifier); + var slowdownComp = EnsureComp(carrier); + _slowdown.SetModifier(carrier, (float)modifier, (float)modifier, slowdownComp); + } + + private void ShowCarryPopup(string locString, Filter filter, PopupType type, EntityUid carrier, EntityUid carried) + { + _popupSystem.PopupPredicted(Loc.GetString(locString, ("carrier", Identity.Name(carrier, EntityManager)), ("target", Identity.Name(carried, EntityManager))), carrier, carried, filter, true, type); + } + + public 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; + } + public bool CanCarry(EntityUid carrier, EntityUid carried, CarriableComponent? carriedComp = null) + { + if (!Resolve(carried, ref carriedComp, false)) + return false; + + if (!HasComp(Transform(carrier).ParentUid)) + return false; + + if (HasComp(carrier) || HasComp(carried)) + return false; + + if (!TryComp(carrier, out var hands)) + return false; + + if (hands.CountFreeHands() < carriedComp.FreeHandsRequired) + return false; + + return true; + } + + public void DropCarried(EntityUid carrier, EntityUid carried) + { + RemComp(carried); + RemComp(carrier); // get rid of this first so we don't recusrively fire that event + RemComp(carrier); + RemComp(carried); + + _actionBlockerSystem.UpdateCanMove(carried); + _virtualItemSystem.DeleteInHandsMatching(carrier, carried); + + _transform.AttachToGridOrMap(carried); + + _standingState.Stand(carried); + _movementSpeed.RefreshMovementSpeedModifiers(carrier); + + var ev = new CarryDroppedEvent(); + RaiseLocalEvent(carried, ref ev); + } +} diff --git a/Resources/Locale/en-US/_strings/_sunrise/carrying/carry.ftl b/Resources/Locale/en-US/_strings/_sunrise/carrying/carry.ftl index 390cf21a69..a12f55070d 100644 --- a/Resources/Locale/en-US/_strings/_sunrise/carrying/carry.ftl +++ b/Resources/Locale/en-US/_strings/_sunrise/carrying/carry.ftl @@ -1,4 +1,6 @@ carry-verb = Нести на руках +can-carry = Тяжело! Попробуйте отнести на руках (alt + ЛКМ). +carry-lying-cancel = Попробуйте встать! carry-too-heavy = Вы недостаточно сильны. carry-started = { $carrier } начинает поднимать вас carry-starting = Вы начинаете поднимать { $target }