diff --git a/Content.Packaging/Properties/launchSettings.json b/Content.Packaging/Properties/launchSettings.json deleted file mode 100644 index 33504c948ad..00000000000 --- a/Content.Packaging/Properties/launchSettings.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "profiles": { - "WSL": { - "commandName": "WSL2", - "distributionName": "" - } - } -} \ No newline at end of file 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/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs b/Content.Server/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs index d77bec6b1ae..faf27484ce9 100644 --- a/Content.Server/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs +++ b/Content.Server/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs @@ -11,230 +11,184 @@ using Robust.Shared.Containers; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; -namespace Content.Server.Nyanotrasen.Kitchen.Components +namespace Content.Server.Nyanotrasen.Kitchen.Components; + +// TODO: move to shared and get rid of SharedDeepFryerComponent +[RegisterComponent, Access(typeof(SharedDeepfryerSystem))] +public sealed partial class DeepFryerComponent : SharedDeepFryerComponent { - [RegisterComponent] - [Access(typeof(SharedDeepfryerSystem))] - // This line appears to be depracted: [ComponentReference(typeof(SharedDeepFryerComponent))] - public sealed partial class DeepFryerComponent : SharedDeepFryerComponent - { - // There are three levels to how the deep fryer treats entities. - // - // 1. An entity can be rejected by the blacklist and be untouched by - // anything other than heat damage. - // - // 2. An entity can be deep-fried but not turned into an edible. The - // change will be mostly cosmetic. Any entity that does not match - // the blacklist will fall into this category. - // - // 3. An entity can be deep-fried and turned into something edible. The - // change will permit the item to be permanently destroyed by eating - // it. - - /// - /// When will the deep fryer layer on the next stage of crispiness? - /// - [DataField("nextFryTime", customTypeSerializer: typeof(TimeOffsetSerializer))] - public TimeSpan NextFryTime { get; set; } - - /// - /// How much waste needs to be added at the next update interval? - /// - public FixedPoint2 WasteToAdd { get; set; } = FixedPoint2.Zero; - - /// - /// How often are items in the deep fryer fried? - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("fryInterval")] - public TimeSpan FryInterval { get; set; } = TimeSpan.FromSeconds(5); - - /// - /// What entities cannot be deep-fried no matter what? - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("blacklist")] - public EntityWhitelist? Blacklist { get; set; } - - /// - /// What entities can be deep-fried into being edible? - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("whitelist")] - public EntityWhitelist? Whitelist { get; set; } - - /// - /// What are over-cooked and burned entities turned into? - /// - /// - /// To prevent unwanted destruction of items, only food can be turned - /// into this. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("charredPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? CharredPrototype { get; set; } - - /// - /// What reagents are considered valid cooking oils? - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("fryingOils", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet FryingOils { get; set; } = new(); - - /// - /// What reagents are added to tasty deep-fried food? - /// JJ Comment: I removed Solution from this. Unsure if I need to replace it with something. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("goodReagents")] - public List GoodReagents { get; set; } = new(); - - /// - /// What reagents are added to terrible deep-fried food? - /// JJ Comment: I removed Solution from this. Unsure if I need to replace it with something. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("badReagents")] - public List BadReagents { get; set; } = new(); - - /// - /// What reagents replace every 1 unit of oil spent on frying? - /// JJ Comment: I removed Solution from this. Unsure if I need to replace it with something. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("wasteReagents")] - public List WasteReagents { get; set; } = new(); - - /// - /// What flavors go well with deep frying? - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("goodFlavors", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet GoodFlavors { get; set; } = new(); - - /// - /// What flavors don't go well with deep frying? - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("badFlavors", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] - public HashSet BadFlavors { get; set; } = new(); - - /// - /// How much is the price coefficiency of a food changed for each good flavor? - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("goodFlavorPriceBonus")] - public float GoodFlavorPriceBonus { get; set; } = 0.2f; - - /// - /// How much is the price coefficiency of a food changed for each bad flavor? - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("badFlavorPriceMalus")] - public float BadFlavorPriceMalus { get; set; } = -0.3f; - - /// - /// What is the name of the solution container for the fryer's oil? - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("solution")] - public string SolutionName { get; set; } = "vat_oil"; - - public Solution Solution { get; set; } = default!; - - /// - /// What is the name of the entity container for items inside the deep fryer? - /// - [DataField("storage")] - public string StorageName { get; set; } = "vat_entities"; - - public BaseContainer Storage { get; set; } = default!; - - /// - /// How much solution should be imparted based on an item's size? - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("solutionSizeCoefficient")] - public FixedPoint2 SolutionSizeCoefficient { get; set; } = 1f; - - /// - /// What's the maximum amount of solution that should ever be imparted? - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("solutionSplitMax")] - public FixedPoint2 SolutionSplitMax { get; set; } = 10f; - - /// - /// What percent of the fryer's solution has to be oil in order for it to fry? - /// - /// - /// The chef will have to clean it out occasionally, and if too much - /// non-oil reagents are added, the vat will have to be drained. - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("fryingOilThreshold")] - public FixedPoint2 FryingOilThreshold { get; set; } = 0.5f; - - /// - /// What is the bare minimum number of oil units to prevent the fryer - /// from unsafe operation? - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("safeOilVolume")] - public FixedPoint2 SafeOilVolume { get; set; } = 10f; - - [ViewVariables(VVAccess.ReadWrite)] - [DataField("unsafeOilVolumeEffects")] - public List UnsafeOilVolumeEffects = new(); - - /// - /// What is the temperature of the vat when the deep fryer is powered? - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("poweredTemperature")] - public float PoweredTemperature = 550.0f; - - /// - /// How many entities can this deep fryer hold? - /// - [ViewVariables(VVAccess.ReadWrite)] - public int StorageMaxEntities = 4; - - /// - /// How many entities can be held, at a minimum? - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("baseStorageMaxEntities")] - public int BaseStorageMaxEntities = 4; - - // /// - // /// What upgradeable machine part dictates the quality of the storage size? - // /// - // [DataField("machinePartStorageMax", customTypeSerializer: typeof(PrototypeIdSerializer))] - // public string MachinePartStorageMax = "MatterBin"; - - /// - /// How much extra storage is added per part rating? - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("storagePerPartRating")] - public int StoragePerPartRating = 4; - - /// - /// What sound is played when an item is inserted into hot oil? - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("soundInsertItem")] - public SoundSpecifier SoundInsertItem = new SoundPathSpecifier("/Audio/Nyanotrasen/Machines/deepfryer_basket_add_item.ogg"); - - /// - /// What sound is played when an item is removed? - /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("soundRemoveItem")] - public SoundSpecifier SoundRemoveItem = new SoundPathSpecifier("/Audio/Nyanotrasen/Machines/deepfryer_basket_remove_item.ogg"); - } + // There are three levels to how the deep fryer treats entities. + // + // 1. An entity can be rejected by the blacklist and be untouched by + // anything other than heat damage. + // + // 2. An entity can be deep-fried but not turned into an edible. The + // change will be mostly cosmetic. Any entity that does not match + // the blacklist will fall into this category. + // + // 3. An entity can be deep-fried and turned into something edible. The + // change will permit the item to be permanently destroyed by eating + // it. + + /// + /// When will the deep fryer layer on the next stage of crispiness? + /// + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + public TimeSpan NextFryTime; + + /// + /// How much waste needs to be added at the next update interval? + /// + [DataField] + public FixedPoint2 WasteToAdd = FixedPoint2.Zero; + + /// + /// How often are items in the deep fryer fried? + /// + [DataField] + public TimeSpan FryInterval = TimeSpan.FromSeconds(5); + + /// + /// What entities cannot be deep-fried no matter what? + /// + [DataField] + public EntityWhitelist? Blacklist; + + /// + /// What entities can be deep-fried into being edible? + /// + [DataField] + public EntityWhitelist? Whitelist; + + /// + /// What are over-cooked and burned entities turned into? + /// + /// + /// To prevent unwanted destruction of items, only food can be turned + /// into this. + /// + [DataField] + public EntProtoId? CharredPrototype; + + /// + /// What reagents are considered valid cooking oils? + /// + [DataField] + public HashSet> FryingOils = new(); + + /// + /// What reagents are added to tasty deep-fried food? + /// + [DataField] + public List GoodReagents = new(); + + /// + /// What reagents are added to terrible deep-fried food? + /// + [DataField] + public List BadReagents = new(); + + /// + /// What reagents replace every 1 unit of oil spent on frying? + /// + [DataField] + public List WasteReagents = new(); + + /// + /// What flavors go well with deep frying? + /// + [DataField] + public HashSet> GoodFlavors = new(); + + /// + /// What flavors don't go well with deep frying? + /// + [DataField] + public HashSet> BadFlavors = new(); + + /// + /// How much is the price coefficiency of a food changed for each good flavor? + /// + [DataField] + public float GoodFlavorPriceBonus = 0.2f; + + /// + /// How much is the price coefficiency of a food changed for each bad flavor? + /// + [DataField] + public float BadFlavorPriceMalus = -0.3f; + + /// + /// What is the name of the solution container for the fryer's oil? + /// + [DataField] + public string SolutionName = "vat_oil"; + + // TODO: Entity + public Solution Solution = default!; + + /// + /// What is the name of the entity container for items inside the deep fryer? + /// + [DataField("storage")] + public string StorageName = "vat_entities"; + + public BaseContainer Storage = default!; + + /// + /// How much solution should be imparted based on an item's size? + /// + [DataField] + public FixedPoint2 SolutionSizeCoefficient = 1f; + + /// + /// What's the maximum amount of solution that should ever be imparted? + /// + [DataField] + public FixedPoint2 SolutionSplitMax = 10f; + + /// + /// What percent of the fryer's solution has to be oil in order for it to fry? + /// + /// + /// The chef will have to clean it out occasionally, and if too much + /// non-oil reagents are added, the vat will have to be drained. + /// + [DataField] + public FixedPoint2 FryingOilThreshold = 0.5f; + + /// + /// What is the bare minimum number of oil units to prevent the fryer + /// from unsafe operation? + /// + [DataField] + public FixedPoint2 SafeOilVolume = 10f; + + [DataField] + public List UnsafeOilVolumeEffects = new(); + + /// + /// What is the temperature of the vat when the deep fryer is powered? + /// + [DataField] + public float PoweredTemperature = 550.0f; + + /// + /// How many entities can this deep fryer hold? + /// + [DataField] + public int StorageMaxEntities = 4; + + /// + /// What sound is played when an item is inserted into hot oil? + /// + [DataField] + public SoundSpecifier SoundInsertItem = new SoundPathSpecifier("/Audio/Nyanotrasen/Machines/deepfryer_basket_add_item.ogg"); + + /// + /// What sound is played when an item is removed? + /// + [DataField] + public SoundSpecifier SoundRemoveItem = new SoundPathSpecifier("/Audio/Nyanotrasen/Machines/deepfryer_basket_remove_item.ogg"); } diff --git a/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Results.cs b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Results.cs index 3f93787934c..fa2807509a6 100644 --- a/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Results.cs +++ b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Results.cs @@ -11,15 +11,20 @@ using Content.Shared.FixedPoint; using Content.Shared.Mobs.Components; using Content.Shared.NPC; +using Content.Shared.Nutrition; using Content.Shared.Nutrition.Components; using Content.Shared.Nyanotrasen.Kitchen.Components; using Content.Shared.Paper; +using Robust.Shared.Prototypes; using Robust.Shared.Random; namespace Content.Server.Nyanotrasen.Kitchen.EntitySystems; public sealed partial class DeepFryerSystem { + private HashSet> _badFlavors = new(); + private HashSet> _goodFlavors = new(); + /// /// Make an item look deep-fried. /// @@ -129,33 +134,33 @@ private void MakeEdible(EntityUid uid, DeepFryerComponent component, EntityUid i var extraSolution = new Solution(); if (TryComp(item, out FlavorProfileComponent? flavorProfileComponent)) { - HashSet goodFlavors = new(flavorProfileComponent.Flavors); - goodFlavors.IntersectWith(component.GoodFlavors); + _goodFlavors.Clear(); + _goodFlavors.IntersectWith(component.GoodFlavors); - HashSet badFlavors = new(flavorProfileComponent.Flavors); - badFlavors.IntersectWith(component.BadFlavors); + _badFlavors.Clear(); + _badFlavors.IntersectWith(component.BadFlavors); deepFriedComponent.PriceCoefficient = Math.Max(0.01f, 1.0f - + goodFlavors.Count * component.GoodFlavorPriceBonus - - badFlavors.Count * component.BadFlavorPriceMalus); + + _goodFlavors.Count * component.GoodFlavorPriceBonus + - _badFlavors.Count * component.BadFlavorPriceMalus); - if (goodFlavors.Count > 0) + if (_goodFlavors.Count > 0) { foreach (var reagent in component.GoodReagents) { - extraSolution.AddReagent(reagent.Reagent.ToString(), reagent.Quantity * goodFlavors.Count); + extraSolution.AddReagent(reagent.Reagent.ToString(), reagent.Quantity * _goodFlavors.Count); // Mask the taste of "medicine." flavorProfileComponent.IgnoreReagents.Add(reagent.Reagent.ToString()); } } - if (badFlavors.Count > 0) + if (_badFlavors.Count > 0) { foreach (var reagent in component.BadReagents) { - extraSolution.AddReagent(reagent.Reagent.ToString(), reagent.Quantity * badFlavors.Count); + extraSolution.AddReagent(reagent.Reagent.ToString(), reagent.Quantity * _badFlavors.Count); } } } diff --git a/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs index 40a658f0c44..4ac8684a7a3 100644 --- a/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs +++ b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs @@ -105,7 +105,6 @@ public override void Initialize() SubscribeLocalEvent(OnInitDeepFryer); SubscribeLocalEvent(OnPowerChange); - // SubscribeLocalEvent(OnRefreshParts); SubscribeLocalEvent(OnDeconstruct); SubscribeLocalEvent(OnDestruction); SubscribeLocalEvent(OnThrowHitBy); @@ -437,14 +436,6 @@ private void OnDestruction(EntityUid uid, DeepFryerComponent component, Destruct _containerSystem.EmptyContainer(component.Storage, true); } - // private void OnRefreshParts(EntityUid uid, DeepFryerComponent component, RefreshPartsEvent args) - // { - // var ratingStorage = args.PartRatings[component.MachinePartStorageMax]; - // - // component.StorageMaxEntities = component.BaseStorageMaxEntities + - // (int) (component.StoragePerPartRating * (ratingStorage - 1)); - // } - /// /// Allow thrown items to land in a basket. /// 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); - } - } -} diff --git a/Resources/Locale/en-US/deltav/fugitive/sets.ftl b/Resources/Locale/en-US/deltav/fugitive/sets.ftl index cb4bb4e735f..e353fd38de7 100644 --- a/Resources/Locale/en-US/deltav/fugitive/sets.ftl +++ b/Resources/Locale/en-US/deltav/fugitive/sets.ftl @@ -27,4 +27,3 @@ fugitive-set-disruptor-name = disruptor's kit fugitive-set-disruptor-description = Hack the stations various systems and use them to your advantage. Comes with a cryptographic sequencer and camera bug. - diff --git a/Resources/Prototypes/DeltaV/Catalog/Cargo/cargo_food.yml b/Resources/Prototypes/DeltaV/Catalog/Cargo/cargo_food.yml index 7c4e08cfcf0..325eb6e078a 100644 --- a/Resources/Prototypes/DeltaV/Catalog/Cargo/cargo_food.yml +++ b/Resources/Prototypes/DeltaV/Catalog/Cargo/cargo_food.yml @@ -87,4 +87,3 @@ cost: 500 category: Food group: market - \ No newline at end of file diff --git a/Resources/Prototypes/DeltaV/Catalog/fugitive_sets.yml b/Resources/Prototypes/DeltaV/Catalog/fugitive_sets.yml index aad6f781bd0..b2e2328693a 100644 --- a/Resources/Prototypes/DeltaV/Catalog/fugitive_sets.yml +++ b/Resources/Prototypes/DeltaV/Catalog/fugitive_sets.yml @@ -70,4 +70,3 @@ content: - Emag - CameraBug - diff --git a/Resources/Prototypes/DeltaV/Entities/Objects/Specific/fugitive.yml b/Resources/Prototypes/DeltaV/Entities/Objects/Specific/fugitive.yml index 7558a322c00..b23f4635421 100644 --- a/Resources/Prototypes/DeltaV/Entities/Objects/Specific/fugitive.yml +++ b/Resources/Prototypes/DeltaV/Entities/Objects/Specific/fugitive.yml @@ -14,4 +14,3 @@ - FugitiveLeverage - FugitiveInfiltrator - FugitiveDisruptor - diff --git a/Resources/Textures/DeltaV/Objects/Storage/barrel.rsi/meta.json b/Resources/Textures/DeltaV/Objects/Storage/barrel.rsi/meta.json index 2ed4c3638b9..30f4889fd7e 100644 --- a/Resources/Textures/DeltaV/Objects/Storage/barrel.rsi/meta.json +++ b/Resources/Textures/DeltaV/Objects/Storage/barrel.rsi/meta.json @@ -1,20 +1,20 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Taken from tgstation PR https://github.com/tgstation/tgstation/blob/master/icons/obj/objects.dmi, modified by rosieposieeee", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "base" + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from tgstation PR https://github.com/tgstation/tgstation/blob/master/icons/obj/objects.dmi, modified by rosieposieeee", + "size": { + "x": 32, + "y": 32 }, - { - "name": "open" - }, - { - "name": "closed" - } - ] + "states": [ + { + "name": "base" + }, + { + "name": "open" + }, + { + "name": "closed" + } + ] } diff --git a/Resources/Textures/DeltaV/Objects/Storage/keg.rsi/meta.json b/Resources/Textures/DeltaV/Objects/Storage/keg.rsi/meta.json index cbbc0d6e89f..ef539daf601 100644 --- a/Resources/Textures/DeltaV/Objects/Storage/keg.rsi/meta.json +++ b/Resources/Textures/DeltaV/Objects/Storage/keg.rsi/meta.json @@ -1,15 +1,15 @@ { - "version": 1, - "license": "CC-BY-SA-3.0", - "copyright": "Made by rosieposieeee", - "size": { - "x": 32, - "y": 32 - }, - "states": [ - { - "name": "base", - "directions": 4 - } - ] + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Made by rosieposieeee", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "base", + "directions": 4 + } + ] }