diff --git a/Content.Client/_White/Items/PseudoItem/PseudoItemSystem.cs b/Content.Client/_White/Items/PseudoItem/PseudoItemSystem.cs new file mode 100644 index 0000000000..d1b1d61d3b --- /dev/null +++ b/Content.Client/_White/Items/PseudoItem/PseudoItemSystem.cs @@ -0,0 +1,7 @@ +using Content.Shared._White.Item.PseudoItem; + +namespace Content.Client._White.Items.PseudoItem; + +public sealed class PseudoItemSystem : SharedPseudoItemSystem +{ +} diff --git a/Content.Server/_White/Carrying/CarriableComponent.cs b/Content.Server/_White/Carrying/CarriableComponent.cs index 3ae2335d47..eb8902f281 100644 --- a/Content.Server/_White/Carrying/CarriableComponent.cs +++ b/Content.Server/_White/Carrying/CarriableComponent.cs @@ -5,6 +5,15 @@ namespace Content.Server._White.Carrying; [RegisterComponent] public sealed partial class CarriableComponent : Component { + [DataField, ViewVariables(VVAccess.ReadWrite)] + public TimeSpan DoAfterLength = TimeSpan.FromSeconds(4); + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float WalkModifier = 0.7f; + + [DataField, ViewVariables(VVAccess.ReadWrite)] + public float SprintModifier = 0.7f; + /// /// Number of free hands required /// to carry the entity @@ -13,4 +22,4 @@ public sealed partial class CarriableComponent : Component public int FreeHandsRequired = 2; public CancellationTokenSource? CancelToken; -} \ No newline at end of file +} diff --git a/Content.Server/_White/Carrying/CarryingSystem.cs b/Content.Server/_White/Carrying/CarryingSystem.cs index 959e58f295..2b0220e90f 100644 --- a/Content.Server/_White/Carrying/CarryingSystem.cs +++ b/Content.Server/_White/Carrying/CarryingSystem.cs @@ -207,9 +207,9 @@ public sealed class CarryingSystem : EntitySystem args.Handled = true; } - private void StartCarryDoAfter(EntityUid carrier, EntityUid carried, CarriableComponent component) + public void StartCarryDoAfter(EntityUid carrier, EntityUid carried, CarriableComponent component) { - var length = TimeSpan.FromSeconds(6); // т.к. удалили систему разницы масс увеличу время с 3 до 6 + var length = component.DoAfterLength; // т.к. удалили систему разницы масс увеличу время с 3 до 6 if (length >= TimeSpan.FromSeconds(9)) { _popupSystem.PopupEntity(Loc.GetString("carry-too-heavy"), carried, carrier, @@ -230,7 +230,11 @@ public sealed class CarryingSystem : EntitySystem NeedHand = true }; - _doAfterSystem.TryStartDoAfter(args); + if(_doAfterSystem.TryStartDoAfter(args)) + { + _popupSystem.PopupEntity(Loc.GetString("carry-start", ("carrier", carrier)), carried, carried, + Shared.Popups.PopupType.SmallCaution); + } } private void Carry(EntityUid carrier, EntityUid carried) @@ -260,17 +264,22 @@ public sealed class CarryingSystem : EntitySystem _actionBlockerSystem.UpdateCanMove(carried); } - public void DropCarried(EntityUid carrier, EntityUid carried) + public void DropCarried(EntityUid carrier, + EntityUid carried, + bool attachToGridOrMap = true, + bool removeCanEscape = true) { RemComp(carrier); // get rid of this first so we don't recusrively fire that event RemComp(carrier); RemComp(carried); RemComp(carried); - RemComp(carried); + if (removeCanEscape) + RemComp(carried); _actionBlockerSystem.UpdateCanMove(carried); _virtualItemSystem.DeleteInHandsMatching(carrier, carried); - _transform.AttachToGridOrMap(carried); + if (attachToGridOrMap) + _transform.AttachToGridOrMap(carried); _movementSpeed.RefreshMovementSpeedModifiers(carrier); } @@ -302,4 +311,4 @@ public sealed class CarryingSystem : EntitySystem return true; } -} \ No newline at end of file +} diff --git a/Content.Server/_White/Items/PseudoItem/PseudoItemSystem.cs b/Content.Server/_White/Items/PseudoItem/PseudoItemSystem.cs new file mode 100644 index 0000000000..b75ca9f652 --- /dev/null +++ b/Content.Server/_White/Items/PseudoItem/PseudoItemSystem.cs @@ -0,0 +1,168 @@ +using Content.Server._White.Carrying; +using Content.Shared.Verbs; +using Content.Shared.Item; +using Content.Shared.Hands; +using Content.Server.Storage.EntitySystems; +using Content.Server.Item; +using Content.Server.Popups; +using Content.Server.Resist; +using Content.Shared._White.Item.PseudoItem; +using Content.Shared.Resist; +using Content.Shared.Storage; +using Robust.Server.GameObjects; +using Robust.Shared.Containers; + +namespace Content.Server._White.Items.PseudoItem; + +public sealed class PseudoItemSystem : SharedPseudoItemSystem +{ + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly StorageSystem _storageSystem = default!; + [Dependency] private readonly ItemSystem _itemSystem = default!; + [Dependency] private readonly CarryingSystem _carrying = default!; + [Dependency] private readonly PopupSystem _popupSystem = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnEntRemoved); + SubscribeLocalEvent(OnDropAttempt); + SubscribeLocalEvent(OnEscape); + SubscribeLocalEvent>(AddAltVerb); + SubscribeLocalEvent>(AddVerb); + SubscribeLocalEvent(OnInteract); + } + + private void OnInteract(Entity ent, ref PseudoItemInteractEvent args) + { + if (!TryComp(args.Used, out PseudoItemComponent? pseudoItem)) + return; + + if (!TryInsert(ent.Owner, args.Used, args.User, pseudoItem, ent.Comp)) + return; + + _carrying.DropCarried(args.User, args.Used, false, false); + } + + private void OnEscape(Entity ent, ref EscapeInventoryEvent args) + { + NoLongerInContainer(ent.Owner, ent.Comp); + } + + private void AddAltVerb(Entity ent, ref GetVerbsEvent args) + { + if (!args.CanInteract || !args.CanAccess) + return; + + var user = args.User; + + var (uid, comp) = ent; + + if (!TryComp(user, out PseudoItemComponent? pseudoItem) || pseudoItem.Active) + return; + + if (Transform(uid).ParentUid == user) + return; + + AlternativeVerb verb = new() + { + Act = () => + { + TryInsert(uid, user, user, pseudoItem, comp); + }, + Text = Loc.GetString("action-name-insert-self"), + }; + args.Verbs.Add(verb); + } + + private void AddVerb(Entity ent, ref GetVerbsEvent args) + { + if (!args.CanInteract || !args.CanAccess) + return; + + var user = args.User; + + var (uid, comp) = ent; + + if (!TryComp(user, out CarryingComponent? carrying)) + return; + + var carried = carrying.Carried; + + if (!TryComp(carried, out PseudoItemComponent? pseudoItem)) + return; + + Verb verb = new() + { + Act = () => + { + if (TryInsert(uid, carried, user, pseudoItem, comp)) + _carrying.DropCarried(user, carried, false, false); + }, + Text = Loc.GetString("action-name-insert-other"), + }; + args.Verbs.Add(verb); + } + + private void OnEntRemoved(EntityUid uid, PseudoItemComponent component, EntGotRemovedFromContainerMessage args) + { + NoLongerInContainer(uid, component); + } + + protected override void OnGettingPickedUp(Entity ent, GettingPickedUpAttemptEvent args) + { + base.OnGettingPickedUp(ent, args); + + if (args.User == args.Item) + return; + + if (!TryComp(ent, out CarriableComponent? carriable)) + _transform.AttachToGridOrMap(ent); + else if (_carrying.CanCarry(args.User, ent)) + _carrying.StartCarryDoAfter(args.User, ent, carriable); + } + + private void OnDropAttempt(EntityUid uid, PseudoItemComponent component, DropAttemptEvent args) + { + if (component.Active) + args.Cancel(); + } + + public bool TryInsert(EntityUid storageUid, + EntityUid toInsert, + EntityUid user, + PseudoItemComponent component, + StorageComponent? storage = null) + { + if (!Resolve(storageUid, ref storage)) + return false; + + var item = EnsureComp(toInsert); + _itemSystem.SetSize(toInsert, component.Size, item); + _itemSystem.SetShape(toInsert, component.Shape, item); + + Dirty(toInsert, component); + if (!_storageSystem.Insert(storageUid, toInsert, out _, user, storage)) + { + _popupSystem.PopupEntity(Loc.GetString("comp-storage-cant-insert"), user, user); + component.Active = false; + RemComp(toInsert); + return false; + } + + component.Active = true; + EnsureComp(toInsert); + return true; + } + + private void NoLongerInContainer(EntityUid uid, PseudoItemComponent component) + { + if (!component.Active) + return; + + RemCompDeferred(uid); + RemCompDeferred(uid); + component.Active = false; + Dirty(uid, component); + } +} diff --git a/Content.Server/_White/Wizard/Magic/WizardSpellsSystem.cs b/Content.Server/_White/Wizard/Magic/WizardSpellsSystem.cs index a08f8af110..b69540d738 100644 --- a/Content.Server/_White/Wizard/Magic/WizardSpellsSystem.cs +++ b/Content.Server/_White/Wizard/Magic/WizardSpellsSystem.cs @@ -24,6 +24,7 @@ using Content.Shared._White.Antag; using Content.Shared._White.BetrayalDagger; using Content.Shared._White.Cult.Components; using Content.Shared._White.Events; +using Content.Shared._White.Item.PseudoItem; using Content.Shared._White.Wizard; using Content.Shared._White.Wizard.Magic; using Content.Shared.Actions; @@ -854,6 +855,9 @@ public sealed class WizardSpellsSystem : EntitySystem public bool CanCast(BaseActionEvent msg) { + if (TryComp(msg.Performer, out PseudoItemComponent? pseudoItem) && pseudoItem.Active) + return false; + return !msg.Handled && CheckRequirements(msg.Action, msg.Performer) && !_statusEffectsSystem.HasStatusEffect(msg.Performer, "Incorporeal"); } diff --git a/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs b/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs index e45530e458..06bbba7d58 100644 --- a/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs +++ b/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs @@ -1,4 +1,5 @@ using System.Diagnostics.CodeAnalysis; +using Content.Shared._White.Item.PseudoItem; using Content.Shared.Hands; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; @@ -67,7 +68,8 @@ public abstract class SharedVirtualItemSystem : EntitySystem private void OnBeforeRangedInteract(Entity ent, ref BeforeRangedInteractEvent args) { // No interactions with a virtual item, please. - args.Handled = true; + if (!HasComp(ent.Comp.BlockingEntity)) + args.Handled = true; } #region Hands diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 9d364dded0..2a9b335c9f 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -1,6 +1,7 @@ using System.Collections.Frozen; using System.Diagnostics.CodeAnalysis; using System.Linq; +using Content.Shared._White.Item.PseudoItem; using Content.Shared.ActionBlocker; using Content.Shared.Containers.ItemSlots; using Content.Shared.Coordinates; @@ -10,6 +11,7 @@ using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Implants.Components; using Content.Shared.Interaction; +using Content.Shared.Inventory.VirtualItem; using Content.Shared.Item; using Content.Shared.Lock; using Content.Shared.Materials; @@ -223,6 +225,12 @@ public abstract class SharedStorageSystem : EntitySystem if (HasComp(uid)) return; + if (TryComp(args.Used, out var virtualItem)) // WD + { + RaiseLocalEvent(uid, new PseudoItemInteractEvent(virtualItem.BlockingEntity, args.User)); + return; + } + PlayerInsertHeldEntity(uid, args.User, storageComp); // Always handle it, even if insertion fails. // We don't want to trigger any AfterInteract logic here. @@ -1072,7 +1080,7 @@ public abstract class SharedStorageSystem : EntitySystem for (int i = 0; i < list.Count; i++) { var saved = list[i]; - + if (saved == location) { list.Remove(location); diff --git a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs index c760b400ae..944aba9dd2 100644 --- a/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs +++ b/Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using System.Numerics; using Content.Shared._White.Containers; using Content.Shared._White.Events; +using Content.Shared._White.Item.PseudoItem; using Content.Shared._White.WeaponModules; using Content.Shared.ActionBlocker; using Content.Shared.Actions; diff --git a/Content.Shared/_White/Item/PseudoItem/PseudoItemComponent.cs b/Content.Shared/_White/Item/PseudoItem/PseudoItemComponent.cs new file mode 100644 index 0000000000..1ecbab1576 --- /dev/null +++ b/Content.Shared/_White/Item/PseudoItem/PseudoItemComponent.cs @@ -0,0 +1,22 @@ +using Content.Shared.Item; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._White.Item.PseudoItem; + +/// +/// For entities that behave like an item under certain conditions, +/// but not under most conditions. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class PseudoItemComponent : Component +{ + [DataField] + public ProtoId Size = "Huge"; + + [DataField] + public List? Shape = new() {new Box2i(0, 0, 4, 4)}; + + [AutoNetworkedField] + public bool Active; +} diff --git a/Content.Shared/_White/Item/PseudoItem/SharedPseudoItemSystem.cs b/Content.Shared/_White/Item/PseudoItem/SharedPseudoItemSystem.cs new file mode 100644 index 0000000000..5c7a1c4779 --- /dev/null +++ b/Content.Shared/_White/Item/PseudoItem/SharedPseudoItemSystem.cs @@ -0,0 +1,36 @@ +using Content.Shared.Interaction.Events; +using Content.Shared.Item; + +namespace Content.Shared._White.Item.PseudoItem; + +public abstract class SharedPseudoItemSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGettingPickedUpAttempt); + SubscribeLocalEvent(OnAttackAttempt); + } + + private void OnAttackAttempt(Entity ent, ref AttackAttemptEvent args) + { + if (ent.Comp.Active) + args.Cancel(); + } + + private void OnGettingPickedUpAttempt(Entity ent, ref GettingPickedUpAttemptEvent args) + { + args.Cancel(); + OnGettingPickedUp(ent, args); + } + + protected virtual void OnGettingPickedUp(Entity ent, GettingPickedUpAttemptEvent args) {} +} + +public sealed class PseudoItemInteractEvent(EntityUid used, EntityUid user) + : EntityEventArgs +{ + public EntityUid Used { get; } = used; + public EntityUid User { get; } = user; +} diff --git a/Content.Shared/_White/Item/PseudoItemInsertDoAfterEvent.cs b/Content.Shared/_White/Item/PseudoItemInsertDoAfterEvent.cs deleted file mode 100644 index c7c76fbd08..0000000000 --- a/Content.Shared/_White/Item/PseudoItemInsertDoAfterEvent.cs +++ /dev/null @@ -1,10 +0,0 @@ -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/Locale/ru-RU/_white/white-shit.ftl b/Resources/Locale/ru-RU/_white/white-shit.ftl index 354000de95..c333a1dc3c 100644 --- a/Resources/Locale/ru-RU/_white/white-shit.ftl +++ b/Resources/Locale/ru-RU/_white/white-shit.ftl @@ -1,4 +1,4 @@ -# Cult +# Cult ent-CultBola = магическая { ent-Bola } .desc = { ent-Bola.desc } @@ -8,3 +8,7 @@ ent-CultBola = магическая { ent-Bola } ent-EnergyBola = энергобола .desc = Соверешенное слияние технологии и справедливости для отлова преступников. + +action-name-insert-self = Залезть внутрь. +action-name-insert-other = Засунуть внутрь. +carry-start = { $carrier } пытается взять вас на руки! diff --git a/Resources/Prototypes/_White/Mobs/Species/felinid.yml b/Resources/Prototypes/_White/Mobs/Species/felinid.yml index 0cd7d91cc7..ba70e372f5 100644 --- a/Resources/Prototypes/_White/Mobs/Species/felinid.yml +++ b/Resources/Prototypes/_White/Mobs/Species/felinid.yml @@ -125,6 +125,9 @@ path: /Audio/Effects/hit_kick.ogg - type: Stamina - type: Perishable + - type: Carriable + doAfterLength: 2 + - type: PseudoItem - type: entity save: false