From f51d4aef47782e5ac271c754a99f2d81e086f586 Mon Sep 17 00:00:00 2001 From: Babaev <129369024+babaevlsdd@users.noreply.github.com> Date: Sun, 9 Jun 2024 20:01:25 +0600 Subject: [PATCH] =?UTF-8?q?=D0=92=D0=BE=D0=B7=D0=BC=D0=BE=D0=B6=D0=BD?= =?UTF-8?q?=D0=BE=D1=81=D1=82=D1=8C=20=D0=B1=D1=80=D0=B0=D1=82=D1=8C=20?= =?UTF-8?q?=D0=BB=D1=8E=D0=B4=D0=B5=D0=B9=20=D0=BD=D0=B0=20=D1=80=D1=83?= =?UTF-8?q?=D0=BA=D0=B8=20(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * carry * fix * carriable update * Removed carriable from vox, slime --------- Co-authored-by: Vigers Ray <60344369+VigersRay@users.noreply.github.com> --- .../Resist/EscapeInventorySystem.cs | 2 +- .../Carrying/BeingCarriedComponent.cs | 11 + .../_Sunrise/Carrying/CarriableComponent.cs | 17 + .../_Sunrise/Carrying/CarryingComponent.cs | 11 + .../_Sunrise/Carrying/CarryingSystem.cs | 405 ++++++++++++++++++ .../PseudoItem/AllowsSleepInsideComponent.cs | 9 + .../Item/PseudoItem/PseudoItemSystem.cs | 213 +++++++++ .../_Sunrise/Carrying/CarryDoAfterEvent.cs | 9 + .../_Sunrise/Carrying/CarryingDoAfterEvent.cs | 12 + .../Carrying/CarryingSlowdownComponent.cs | 28 ++ .../Carrying/CarryingSlowdownSystem.cs | 47 ++ .../_Sunrise/Cloning/ITransferredByCloning.cs | 9 + .../Item/Components/PseudoItemComponent.cs | 19 + .../Item/PseudoItemInsertDoAfterEvent.cs | 10 + Resources/Changelog/ChangelogSunrise.yml | 10 + .../Locale/ru-RU/_sunrise/carrying/carry.ftl | 2 + .../Prototypes/Entities/Mobs/Species/base.yml | 1 + 17 files changed, 814 insertions(+), 1 deletion(-) create mode 100644 Content.Server/_Sunrise/Carrying/BeingCarriedComponent.cs create mode 100644 Content.Server/_Sunrise/Carrying/CarriableComponent.cs create mode 100644 Content.Server/_Sunrise/Carrying/CarryingComponent.cs create mode 100644 Content.Server/_Sunrise/Carrying/CarryingSystem.cs create mode 100644 Content.Server/_Sunrise/Item/PseudoItem/AllowsSleepInsideComponent.cs create mode 100644 Content.Server/_Sunrise/Item/PseudoItem/PseudoItemSystem.cs create mode 100644 Content.Shared/_Sunrise/Carrying/CarryDoAfterEvent.cs create mode 100644 Content.Shared/_Sunrise/Carrying/CarryingDoAfterEvent.cs create mode 100644 Content.Shared/_Sunrise/Carrying/CarryingSlowdownComponent.cs create mode 100644 Content.Shared/_Sunrise/Carrying/CarryingSlowdownSystem.cs create mode 100644 Content.Shared/_Sunrise/Cloning/ITransferredByCloning.cs create mode 100644 Content.Shared/_Sunrise/Item/Components/PseudoItemComponent.cs create mode 100644 Content.Shared/_Sunrise/Item/PseudoItemInsertDoAfterEvent.cs create mode 100644 Resources/Locale/ru-RU/_sunrise/carrying/carry.ftl diff --git a/Content.Server/Resist/EscapeInventorySystem.cs b/Content.Server/Resist/EscapeInventorySystem.cs index 35c7d0bc65b..679af0d4e7b 100644 --- a/Content.Server/Resist/EscapeInventorySystem.cs +++ b/Content.Server/Resist/EscapeInventorySystem.cs @@ -56,7 +56,7 @@ private void OnRelayMovement(EntityUid uid, CanEscapeInventoryComponent componen AttemptEscape(uid, container.Owner, component); } - private void AttemptEscape(EntityUid user, EntityUid container, CanEscapeInventoryComponent component, float multiplier = 1f) + public void AttemptEscape(EntityUid user, EntityUid container, CanEscapeInventoryComponent component, float multiplier = 1f) { if (component.IsEscaping) return; diff --git a/Content.Server/_Sunrise/Carrying/BeingCarriedComponent.cs b/Content.Server/_Sunrise/Carrying/BeingCarriedComponent.cs new file mode 100644 index 00000000000..afc78978c86 --- /dev/null +++ b/Content.Server/_Sunrise/Carrying/BeingCarriedComponent.cs @@ -0,0 +1,11 @@ +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/_Sunrise/Carrying/CarriableComponent.cs b/Content.Server/_Sunrise/Carrying/CarriableComponent.cs new file mode 100644 index 00000000000..0dabdabbce0 --- /dev/null +++ b/Content.Server/_Sunrise/Carrying/CarriableComponent.cs @@ -0,0 +1,17 @@ +using System.Threading; + +namespace Content.Server.Carrying +{ + [RegisterComponent] + public sealed partial class CarriableComponent : Component + { + /// + /// необходимое количество свободных рук + /// что-бы взять сущность + /// + [DataField("freeHandsRequired")] + public int FreeHandsRequired = 2; + + public CancellationTokenSource? CancelToken; + } +} diff --git a/Content.Server/_Sunrise/Carrying/CarryingComponent.cs b/Content.Server/_Sunrise/Carrying/CarryingComponent.cs new file mode 100644 index 00000000000..e79460595b9 --- /dev/null +++ b/Content.Server/_Sunrise/Carrying/CarryingComponent.cs @@ -0,0 +1,11 @@ +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/_Sunrise/Carrying/CarryingSystem.cs b/Content.Server/_Sunrise/Carrying/CarryingSystem.cs new file mode 100644 index 00000000000..89a33385144 --- /dev/null +++ b/Content.Server/_Sunrise/Carrying/CarryingSystem.cs @@ -0,0 +1,405 @@ +using System.Numerics; +using System.Threading; +using Content.Server.DoAfter; +using Content.Server.Inventory; +using Content.Server.Resist; +using Content.Server.Popups; +using Content.Server.Item.PseudoItem; +using Content.Shared.Climbing; +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; +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.Item.PseudoItem; +using Content.Shared.Mind.Components; +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.Popups; +using Content.Shared.Storage; +using Robust.Shared.Map.Components; + +namespace Content.Server.Carrying +{ + public sealed class CarryingSystem : EntitySystem + { + [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 _pulling = 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 PseudoItemSystem _pseudoItem = default!; + [Dependency] private readonly VirtualItemSystem _virtualItemSystem = 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); + SubscribeLocalEvent>(AddInsertCarriedVerb); + } + + + 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)) + 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 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; + + args.ItemUid = virtItem.BlockingEntity; + + // var multiplier = _contests.MassContest(uid, virtItem.BlockingEntity); + // args.ThrowStrength = 5f * multiplier; + } + + 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.Cancel(); + } + + /// + /// 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.OldMovement == MoveButtons.None || args.OldMovement == MoveButtons.Walk) + return; + + if (_actionBlockerSystem.CanInteract(uid, component.Carrier)) + { + _escapeInventorySystem.AttemptEscape(uid, component.Carrier, escape); + } + } + + 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.Cancel(); + } + + 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 BuckleChangeEvent 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); + args.Handled = true; + } + + private void StartCarryDoAfter(EntityUid carrier, EntityUid carried, CarriableComponent component) + { + var 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); + + _popupSystem.PopupEntity(Loc.GetString("carry-started", ("carrier", carrier)), carried, carried); + } + + private void Carry(EntityUid carrier, EntityUid carried) + { + if (TryComp(carried, out var pullable)) + { + if (pullable.Puller != null) + { + _pulling.TryStopPull(carried, pullable); + } + } + + + if (TryComp(carried, out var carryComp)) + DropCarried(carried, carryComp.Carried); + + Transform(carrier).AttachToGridOrMap(); + Transform(carried).AttachToGridOrMap(); + Transform(carried).Coordinates = Transform(carrier).Coordinates; + Transform(carried).AttachParent(Transform(carrier)); + var carryingComp = EnsureComp(carrier); + ApplyCarrySlowdown(carrier, carried); + var carriedComp = EnsureComp(carried); + EnsureComp(carried); + + _virtualItemSystem.TrySpawnVirtualItemInHand(carried, carrier); + _virtualItemSystem.TrySpawnVirtualItemInHand(carried, carrier); + + carryingComp.Carried = carried; + carriedComp.Carrier = carrier; + + _actionBlockerSystem.UpdateCanMove(carried); + } + + public void DropCarried(EntityUid carrier, EntityUid carried) + { + RemComp(carrier); + RemComp(carrier); + RemComp(carried); + RemComp(carried); + _actionBlockerSystem.UpdateCanMove(carried); + Transform(carried).AttachToGridOrMap(); + _standingState.Stand(carried); + _movementSpeed.RefreshMovementSpeedModifiers(carrier); + _virtualItemSystem.DeleteInHandsMatching(carrier, carried); + } + + private void ApplyCarrySlowdown(EntityUid carrier, EntityUid carried) + { + // Carrying slowdown made static as a part of removing mass contests + // var massRatio = _contests.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 modifier = 0.7f; // 30% slowdown while carrying + 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; + } + + 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; + + if (Transform(carried).ParentUid != carrier) + { + DropCarried(carrier, carried); + continue; + } + + var xform = Transform(carried); + if (!xform.LocalPosition.Equals(Vector2.Zero)) + { + xform.LocalPosition = Vector2.Zero; + } + } + query.Dispose(); + } + + public TimeSpan GetPickupDuration(EntityUid carrier, EntityUid carried) + { + TimeSpan length = TimeSpan.FromSeconds(2.5); + + // var mod = _contests.MassContest(carrier, carried); + // if (mod != 0) + // length /= mod; + + return length; + } + + 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; + + if (HasComp(carrier) || HasComp(carrier)) + return false; + + if (GetPickupDuration(carrier, toCarry) > TimeSpan.FromSeconds(9)) + return false; + + Carry(carrier, toCarry); + + return true; + } + + private void AddInsertCarriedVerb(EntityUid uid, CarryingComponent component, GetVerbsEvent args) + { + var toInsert = args.Using; + if (toInsert is not { Valid: true } || !args.CanAccess || !TryComp(toInsert, out var pseudoItem)) + return; + + if (!HasComp(args.Target)) + return; + + InnateVerb verb = new() + { + Act = () => + { + DropCarried(uid, toInsert.Value); + _pseudoItem.TryInsert(args.Target, toInsert.Value, args.User, pseudoItem); + }, + Text = Loc.GetString("action-name-insert-other", ("target", toInsert)), + Priority = 2 + }; + args.Verbs.Add(verb); + } + } +} diff --git a/Content.Server/_Sunrise/Item/PseudoItem/AllowsSleepInsideComponent.cs b/Content.Server/_Sunrise/Item/PseudoItem/AllowsSleepInsideComponent.cs new file mode 100644 index 00000000000..3f023d6cdce --- /dev/null +++ b/Content.Server/_Sunrise/Item/PseudoItem/AllowsSleepInsideComponent.cs @@ -0,0 +1,9 @@ +namespace Content.Server.Item.PseudoItem; + +/// +/// Signifies that pseudo-item creatures can sleep inside the container to which this component was added. +/// +[RegisterComponent] +public sealed partial class AllowsSleepInsideComponent : Component +{ +} diff --git a/Content.Server/_Sunrise/Item/PseudoItem/PseudoItemSystem.cs b/Content.Server/_Sunrise/Item/PseudoItem/PseudoItemSystem.cs new file mode 100644 index 00000000000..af7af93eab2 --- /dev/null +++ b/Content.Server/_Sunrise/Item/PseudoItem/PseudoItemSystem.cs @@ -0,0 +1,213 @@ +using Content.Server.Actions; +using Content.Server.Bed.Sleep; +using Content.Server.Carrying; +using Content.Server.DoAfter; +using Content.Server.Popups; +using Content.Server.Storage.EntitySystems; +using Content.Shared.Bed.Sleep; +using Content.Shared.DoAfter; +using Content.Shared.Hands; +using Content.Shared.IdentityManagement; +using Content.Shared.Item; +using Content.Shared.Item.PseudoItem; +using Content.Shared.Storage; +using Content.Shared.Tag; +using Content.Shared.Verbs; +using Robust.Shared.Containers; + +namespace Content.Server.Item.PseudoItem; + +public sealed class PseudoItemSystem : EntitySystem +{ + [Dependency] private readonly StorageSystem _storageSystem = default!; + [Dependency] private readonly ItemSystem _itemSystem = default!; + [Dependency] private readonly DoAfterSystem _doAfter = default!; + [Dependency] private readonly TagSystem _tagSystem = default!; + [Dependency] private readonly CarryingSystem _carrying = default!; // Frontier + [Dependency] private readonly ActionsSystem _actions = default!; // Frontier + [Dependency] private readonly PopupSystem _popup = default!; // Frontier + + [ValidatePrototypeId] + private const string PreventTag = "PreventLabel"; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent>(AddInsertVerb); + SubscribeLocalEvent>(AddInsertAltVerb); + SubscribeLocalEvent(OnEntRemoved); + SubscribeLocalEvent(OnGettingPickedUpAttempt); + SubscribeLocalEvent(OnDropAttempt); + SubscribeLocalEvent(OnDoAfter); + SubscribeLocalEvent(OnInsertAttempt); + SubscribeLocalEvent(OnTrySleeping); // Frontier + } + + private void AddInsertVerb(EntityUid uid, PseudoItemComponent component, GetVerbsEvent args) + { + if (!args.CanInteract || !args.CanAccess) + return; + + if (component.Active) + return; + + if (!TryComp(args.Target, out var targetStorage)) + return; + + if (Transform(args.Target).ParentUid == uid) + return; + + InnateVerb verb = new() + { + Act = () => + { + TryInsert(args.Target, uid, args.User, component, targetStorage); + }, + Text = Loc.GetString("action-name-insert-self"), + Priority = 2 + }; + args.Verbs.Add(verb); + } + + private void AddInsertAltVerb(EntityUid uid, PseudoItemComponent component, GetVerbsEvent args) + { + if (!args.CanInteract || !args.CanAccess) + return; + + if (args.User == args.Target) + return; + + if (component.Active) + return; + + if (args.Hands == null) + return; + + if (!TryComp(args.Hands.ActiveHandEntity, out var targetStorage)) + return; + + AlternativeVerb verb = new() + { + Act = () => + { + StartInsertDoAfter(args.User, uid, args.Hands.ActiveHandEntity.Value, component); + }, + Text = Loc.GetString("action-name-insert-other", ("target", Identity.Entity(args.Target, EntityManager))), + Priority = 2 + }; + args.Verbs.Add(verb); + } + + private void OnEntRemoved(EntityUid uid, PseudoItemComponent component, EntGotRemovedFromContainerMessage args) + { + if (!component.Active) + return; + + RemComp(uid); + component.Active = false; + + // Frontier + if (component.SleepAction is { Valid: true }) + _actions.RemoveAction(uid, component.SleepAction); + } + + private void OnGettingPickedUpAttempt(EntityUid uid, PseudoItemComponent component, + GettingPickedUpAttemptEvent args) + { + if (args.User == args.Item) + return; + + // Frontier: prevent people from pushing each other from a bag + if (HasComp(args.User)) + { + args.Cancel(); + return; + } + + // Frontier: try to carry the person when taking them out of a bag. + if (_carrying.TryCarry(args.User, uid)) + { + args.Cancel(); + return; + } + + Transform(uid).AttachToGridOrMap(); + args.Cancel(); + } + + private void OnDropAttempt(EntityUid uid, PseudoItemComponent component, DropAttemptEvent args) + { + if (component.Active) + args.Cancel(); + } + + private void OnDoAfter(EntityUid uid, PseudoItemComponent component, DoAfterEvent args) + { + if (args.Handled || args.Cancelled || args.Args.Used == null) + return; + + args.Handled = TryInsert(args.Args.Used.Value, uid, args.User, component); + } + + public bool TryInsert(EntityUid storageUid, EntityUid toInsert, EntityUid userUid, PseudoItemComponent component, + StorageComponent? storage = null) + { + if (!Resolve(storageUid, ref storage)) + return false; + + var item = EnsureComp(toInsert); + _tagSystem.TryAddTag(toInsert, PreventTag); + _itemSystem.SetSize(toInsert, component.Size, item); + _itemSystem.VisualsChanged(toInsert); + + if (!_storageSystem.CanInsert(storageUid, toInsert, out _) || + !_storageSystem.PlayerInsertEntityInWorld(storageUid, userUid, toInsert)) + { + component.Active = false; + RemComp(toInsert); + return false; + } + _storageSystem.UpdateUI(storageUid); + _storageSystem.UpdateAppearance(storageUid); + + // Frontier + if (HasComp(storageUid)) + _actions.AddAction(toInsert, ref component.SleepAction, SleepingSystem.SleepActionId, toInsert); + + component.Active = true; + return true; + } + + private void StartInsertDoAfter(EntityUid inserter, EntityUid toInsert, EntityUid storageEntity, + PseudoItemComponent? pseudoItem = null) + { + if (!Resolve(toInsert, ref pseudoItem)) + return; + + var ev = new PseudoItemInsertDoAfterEvent(); + var args = new DoAfterArgs(EntityManager, inserter, 5f, ev, toInsert, toInsert, storageEntity) + { + BreakOnMove = true, + NeedHand = true + }; + + _doAfter.TryStartDoAfter(args); + } + + private void OnInsertAttempt(EntityUid uid, PseudoItemComponent component, + ContainerGettingInsertedAttemptEvent args) + { + if (!component.Active) + return; + // This hopefully shouldn't trigger, but this is a failsafe just in case so we dont bluespace them cats + args.Cancel(); + } + + // Frontier - 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(uid) && parent is { Valid: true } && HasComp(parent)) + _popup.PopupEntity(Loc.GetString("popup-sleep-in-bag", ("entity", uid)), uid); + } +} diff --git a/Content.Shared/_Sunrise/Carrying/CarryDoAfterEvent.cs b/Content.Shared/_Sunrise/Carrying/CarryDoAfterEvent.cs new file mode 100644 index 00000000000..940135bfa99 --- /dev/null +++ b/Content.Shared/_Sunrise/Carrying/CarryDoAfterEvent.cs @@ -0,0 +1,9 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared.Carrying; + +[Serializable, NetSerializable] +public sealed partial class CarryDoAfterEvent : SimpleDoAfterEvent +{ +} diff --git a/Content.Shared/_Sunrise/Carrying/CarryingDoAfterEvent.cs b/Content.Shared/_Sunrise/Carrying/CarryingDoAfterEvent.cs new file mode 100644 index 00000000000..abf12ab4de7 --- /dev/null +++ b/Content.Shared/_Sunrise/Carrying/CarryingDoAfterEvent.cs @@ -0,0 +1,12 @@ +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/_Sunrise/Carrying/CarryingSlowdownComponent.cs b/Content.Shared/_Sunrise/Carrying/CarryingSlowdownComponent.cs new file mode 100644 index 00000000000..a4b0cc1781f --- /dev/null +++ b/Content.Shared/_Sunrise/Carrying/CarryingSlowdownComponent.cs @@ -0,0 +1,28 @@ +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 partial class CarryingSlowdownComponentState : ComponentState + { + public float WalkModifier; + public float SprintModifier; + public CarryingSlowdownComponentState(float walkModifier, float sprintModifier) + { + WalkModifier = walkModifier; + SprintModifier = sprintModifier; + } + } +} diff --git a/Content.Shared/_Sunrise/Carrying/CarryingSlowdownSystem.cs b/Content.Shared/_Sunrise/Carrying/CarryingSlowdownSystem.cs new file mode 100644 index 00000000000..9b9c8cec10f --- /dev/null +++ b/Content.Shared/_Sunrise/Carrying/CarryingSlowdownSystem.cs @@ -0,0 +1,47 @@ +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); + } + } +} diff --git a/Content.Shared/_Sunrise/Cloning/ITransferredByCloning.cs b/Content.Shared/_Sunrise/Cloning/ITransferredByCloning.cs new file mode 100644 index 00000000000..a58c50ab347 --- /dev/null +++ b/Content.Shared/_Sunrise/Cloning/ITransferredByCloning.cs @@ -0,0 +1,9 @@ +namespace Content.Shared._Sunrise.Cloning; + +/// +/// Indicates that this Component should be transferred to the new entity when the entity is cloned (for example, using a cloner) +/// +public interface ITransferredByCloning +{ +} + diff --git a/Content.Shared/_Sunrise/Item/Components/PseudoItemComponent.cs b/Content.Shared/_Sunrise/Item/Components/PseudoItemComponent.cs new file mode 100644 index 00000000000..41e2b660e9f --- /dev/null +++ b/Content.Shared/_Sunrise/Item/Components/PseudoItemComponent.cs @@ -0,0 +1,19 @@ +using Content.Shared._Sunrise.Cloning; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Shared.Item.PseudoItem; +/// +/// For entities that behave like an item under certain conditions, +/// but not under most conditions. +/// +[RegisterComponent] +public sealed partial class PseudoItemComponent : Component, ITransferredByCloning +{ + [DataField("size", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string Size = "Huge"; + + public bool Active = false; + + [DataField] + public EntityUid? SleepAction; +} diff --git a/Content.Shared/_Sunrise/Item/PseudoItemInsertDoAfterEvent.cs b/Content.Shared/_Sunrise/Item/PseudoItemInsertDoAfterEvent.cs new file mode 100644 index 00000000000..4b34118f377 --- /dev/null +++ b/Content.Shared/_Sunrise/Item/PseudoItemInsertDoAfterEvent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.Serialization; +using Content.Shared.DoAfter; + +namespace Content.Shared.Item.PseudoItem; + + +[Serializable, NetSerializable] +public sealed partial class PseudoItemInsertDoAfterEvent : SimpleDoAfterEvent +{ +} diff --git a/Resources/Changelog/ChangelogSunrise.yml b/Resources/Changelog/ChangelogSunrise.yml index 5124bb65177..4a9c6e3083f 100644 --- a/Resources/Changelog/ChangelogSunrise.yml +++ b/Resources/Changelog/ChangelogSunrise.yml @@ -420,3 +420,13 @@ Entries: type: Tweak id: 34 time: '2024-06-09T13:38:09.611579+00:00' +- author: Babaev + changes: + - message: "\u0414\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0430 \u0432\u043E\u0437\ + \u043C\u043E\u0436\u043D\u043E\u0441\u0442\u044C \u043F\u0435\u0440\u0435\u0442\ + \u0430\u0441\u043A\u0438\u0432\u0430\u0442\u044C \u0434\u0440\u0443\u0433\u0438\ + \u0445 \u043B\u044E\u0434\u0435\u0439 \u043D\u0430 \u0440\u0443\u043A\u0430\u0445\ + ." + type: Add + id: 35 + time: '2024-06-06T13:07:39.013718+00:00' diff --git a/Resources/Locale/ru-RU/_sunrise/carrying/carry.ftl b/Resources/Locale/ru-RU/_sunrise/carrying/carry.ftl new file mode 100644 index 00000000000..8417df65f09 --- /dev/null +++ b/Resources/Locale/ru-RU/_sunrise/carrying/carry.ftl @@ -0,0 +1,2 @@ +carry-verb = Нести на руках +carry-too-heavy = Ты недостаточно силен. \ No newline at end of file diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index 05cbd0ee306..d3924701f84 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -92,6 +92,7 @@ horizontalRotation: 90 - type: HumanoidAppearance species: Human + - type: Carriable - type: SlowOnDamage speedModifierThresholds: 60: 0.7