diff --git a/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/MakeGhostRoleEui.cs b/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/MakeGhostRoleEui.cs index 1e24d4c84c..ab2c389f49 100644 --- a/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/MakeGhostRoleEui.cs +++ b/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/MakeGhostRoleEui.cs @@ -1,7 +1,7 @@ using Content.Client.Eui; -using Content.Server.Ghost.Roles.Raffles; using Content.Shared.Eui; using Content.Shared.Ghost.Roles; +using Content.Shared.Ghost.Roles.Raffles; using JetBrains.Annotations; using Robust.Client.Console; using Robust.Client.Player; diff --git a/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/MakeGhostRoleWindow.xaml.cs b/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/MakeGhostRoleWindow.xaml.cs index 6711d76b10..5480db1dcd 100644 --- a/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/MakeGhostRoleWindow.xaml.cs +++ b/Content.Client/UserInterface/Systems/Ghost/Controls/Roles/MakeGhostRoleWindow.xaml.cs @@ -1,6 +1,4 @@ -using System.Linq; -using System.Numerics; -using Content.Server.Ghost.Roles.Raffles; +using System.Numerics; using Content.Shared.Ghost.Roles.Raffles; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface.Controls; diff --git a/Content.Server/Bed/BedSystem.cs b/Content.Server/Bed/BedSystem.cs index 673984c0a7..91b31c782b 100644 --- a/Content.Server/Bed/BedSystem.cs +++ b/Content.Server/Bed/BedSystem.cs @@ -1,11 +1,10 @@ -using Content.Server.Actions; using Content.Server.Bed.Components; -using Content.Server.Body.Systems; using Content.Server.Power.EntitySystems; using Content.Shared.Bed; using Content.Shared.Bed.Components; using Content.Shared.Bed.Sleep; using Content.Shared.Body.Components; +using Content.Shared.Body.Events; using Content.Shared.Buckle.Components; using Content.Shared.Damage; using Content.Shared.Emag.Systems; diff --git a/Content.Server/Body/Systems/BloodstreamSystem.cs b/Content.Server/Body/Systems/BloodstreamSystem.cs index f4f50bf16e..9816c8c3f6 100644 --- a/Content.Server/Body/Systems/BloodstreamSystem.cs +++ b/Content.Server/Body/Systems/BloodstreamSystem.cs @@ -1,8 +1,8 @@ using Content.Server.Body.Components; -using Content.Shared.EntityEffects.Effects; using Content.Server.Fluids.EntitySystems; using Content.Server.Popups; using Content.Shared.Alert; +using Content.Shared.Body.Events; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reaction; @@ -10,6 +10,7 @@ using Content.Shared.Chemistry.Reagent; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.Drunk; +using Content.Shared.EntityEffects.Effects; using Content.Shared.FixedPoint; using Content.Shared.Forensics; using Content.Shared.Forensics.Components; diff --git a/Content.Server/Body/Systems/BodySystem.cs b/Content.Server/Body/Systems/BodySystem.cs index fb6f8d6777..d2fc3d6558 100644 --- a/Content.Server/Body/Systems/BodySystem.cs +++ b/Content.Server/Body/Systems/BodySystem.cs @@ -1,9 +1,12 @@ +using System.Numerics; using Content.Server.Body.Components; using Content.Server.Ghost; using Content.Server.Humanoid; using Content.Shared.Body.Components; +using Content.Shared.Body.Events; using Content.Shared.Body.Part; using Content.Shared.Body.Systems; +using Content.Shared.Damage.Components; using Content.Shared.Humanoid; using Content.Shared.Mind; using Content.Shared.Mobs.Systems; @@ -11,8 +14,6 @@ using Content.Shared.Movement.Events; using Content.Shared.Movement.Systems; using Robust.Shared.Audio; using Robust.Shared.Timing; -using System.Numerics; -using Content.Shared.Damage.Components; namespace Content.Server.Body.Systems; diff --git a/Content.Server/Body/Systems/MetabolizerSystem.cs b/Content.Server/Body/Systems/MetabolizerSystem.cs index 3497d4a6d7..5577b42a06 100644 --- a/Content.Server/Body/Systems/MetabolizerSystem.cs +++ b/Content.Server/Body/Systems/MetabolizerSystem.cs @@ -1,9 +1,10 @@ using Content.Server.Body.Components; -using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Administration.Logs; +using Content.Shared.Body.Events; using Content.Shared.Body.Organ; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; using Content.Shared.Database; using Content.Shared.EntityEffects; @@ -231,29 +232,4 @@ namespace Content.Server.Body.Systems _solutionContainerSystem.UpdateChemicals(soln.Value); } } - - // TODO REFACTOR THIS - // This will cause rates to slowly drift over time due to floating point errors. - // Instead, the system that raised this should trigger an update and subscribe to get-modifier events. - [ByRefEvent] - public readonly record struct ApplyMetabolicMultiplierEvent( - EntityUid Uid, - float Multiplier, - bool Apply) - { - /// - /// The entity whose metabolism is being modified. - /// - public readonly EntityUid Uid = Uid; - - /// - /// What the metabolism's update rate will be multiplied by. - /// - public readonly float Multiplier = Multiplier; - - /// - /// If true, apply the multiplier. If false, revert it. - /// - public readonly bool Apply = Apply; - } } diff --git a/Content.Server/Body/Systems/RespiratorSystem.cs b/Content.Server/Body/Systems/RespiratorSystem.cs index e068aa9c7a..1b4cf4d698 100644 --- a/Content.Server/Body/Systems/RespiratorSystem.cs +++ b/Content.Server/Body/Systems/RespiratorSystem.cs @@ -3,18 +3,19 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Body.Components; using Content.Server.Chat.Systems; using Content.Server.EntityEffects; -using Content.Shared.EntityEffects.EffectConditions; -using Content.Shared.EntityEffects.Effects; -using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Alert; using Content.Shared.Atmos; using Content.Shared.Body.Components; +using Content.Shared.Body.Events; using Content.Shared.Body.Prototypes; using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.EntityEffects; +using Content.Shared.EntityEffects.EffectConditions; +using Content.Shared.EntityEffects.Effects; using Content.Shared.Mobs.Systems; using JetBrains.Annotations; using Robust.Shared.Prototypes; diff --git a/Content.Server/Crayon/CrayonSystem.cs b/Content.Server/Crayon/CrayonSystem.cs index 4257c436c2..f2597cd424 100644 --- a/Content.Server/Crayon/CrayonSystem.cs +++ b/Content.Server/Crayon/CrayonSystem.cs @@ -2,18 +2,17 @@ using System.Linq; using System.Numerics; using Content.Server.Administration.Logs; using Content.Server.Decals; -using Content.Server.Nutrition.EntitySystems; using Content.Server.Popups; using Content.Shared.Crayon; using Content.Shared.Database; using Content.Shared.Decals; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; +using Content.Shared.Nutrition.EntitySystems; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.GameStates; -using Robust.Shared.Player; using Robust.Shared.Prototypes; namespace Content.Server.Crayon; diff --git a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs index ab7961ba18..4b4e16b5db 100644 --- a/Content.Server/Kitchen/EntitySystems/SharpSystem.cs +++ b/Content.Server/Kitchen/EntitySystems/SharpSystem.cs @@ -1,21 +1,20 @@ using Content.Server.Body.Systems; using Content.Server.Kitchen.Components; -using Content.Server.Nutrition.EntitySystems; -using Content.Shared.Body.Components; using Content.Shared.Administration.Logs; +using Content.Shared.Body.Components; using Content.Shared.Database; -using Content.Shared.Interaction; -using Content.Shared.Nutrition.Components; -using Content.Shared.Popups; -using Content.Shared.Storage; -using Content.Shared.Verbs; using Content.Shared.Destructible; using Content.Shared.DoAfter; -using Content.Shared.Hands.Components; using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; using Content.Shared.Kitchen; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; +using Content.Shared.Nutrition.Components; +using Content.Shared.Nutrition.EntitySystems; +using Content.Shared.Popups; +using Content.Shared.Storage; +using Content.Shared.Verbs; using Robust.Server.Containers; using Robust.Server.GameObjects; using Robust.Shared.Random; diff --git a/Content.Server/Medical/VomitSystem.cs b/Content.Server/Medical/VomitSystem.cs index a6982cea4f..9d27247a38 100644 --- a/Content.Server/Medical/VomitSystem.cs +++ b/Content.Server/Medical/VomitSystem.cs @@ -4,8 +4,10 @@ using Content.Server.Fluids.EntitySystems; using Content.Server.Forensics; using Content.Server.Popups; using Content.Server.Stunnable; -using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Body.Components; +using Content.Shared.Body.Systems; using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; using Content.Shared.IdentityManagement; using Content.Shared.Nutrition.Components; diff --git a/Content.Server/Nutrition/Components/BadFoodComponent.cs b/Content.Server/Nutrition/Components/BadFoodComponent.cs index 0924b465a4..16f90533cb 100644 --- a/Content.Server/Nutrition/Components/BadFoodComponent.cs +++ b/Content.Server/Nutrition/Components/BadFoodComponent.cs @@ -1,4 +1,4 @@ -using Content.Server.Nutrition.EntitySystems; +using Content.Shared.Nutrition.EntitySystems; namespace Content.Server.Nutrition.Components; diff --git a/Content.Server/Nutrition/Components/IgnoreBadFoodComponent.cs b/Content.Server/Nutrition/Components/IgnoreBadFoodComponent.cs index cb30b98e19..f5b3c326f3 100644 --- a/Content.Server/Nutrition/Components/IgnoreBadFoodComponent.cs +++ b/Content.Server/Nutrition/Components/IgnoreBadFoodComponent.cs @@ -1,4 +1,4 @@ -using Content.Server.Nutrition.EntitySystems; +using Content.Shared.Nutrition.EntitySystems; namespace Content.Server.Nutrition.Components; diff --git a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs index 7ac5c20a7d..467f7b6334 100644 --- a/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/DrinkSystem.cs @@ -1,12 +1,11 @@ -using Content.Server.Body.Components; using Content.Server.Body.Systems; -using Content.Shared.EntityEffects.Effects; using Content.Server.Fluids.EntitySystems; using Content.Server.Forensics; using Content.Server.Inventory; using Content.Server.Popups; using Content.Shared.Administration.Logs; using Content.Shared.Body.Components; +using Content.Shared.Body.Systems; using Content.Shared.Chemistry; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components.SolutionManager; @@ -14,7 +13,7 @@ using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; using Content.Shared.Database; using Content.Shared.DoAfter; -using Content.Shared.EntityEffects; +using Content.Shared.EntityEffects.Effects; using Content.Shared.FixedPoint; using Content.Shared.Hands.EntitySystems; using Content.Shared.IdentityManagement; @@ -24,7 +23,6 @@ using Content.Shared.Mobs.Systems; using Content.Shared.Nutrition; using Content.Shared.Nutrition.Components; using Content.Shared.Nutrition.EntitySystems; -using Content.Shared.Verbs; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Player; @@ -65,7 +63,6 @@ public sealed class DrinkSystem : SharedDrinkSystem // run after openable so its always open -> drink SubscribeLocalEvent(OnUse, before: [typeof(ServerInventorySystem)], after: [typeof(OpenableSystem)]); SubscribeLocalEvent(AfterInteract); - SubscribeLocalEvent>(AddDrinkVerb); SubscribeLocalEvent(OnDoAfter); } @@ -157,76 +154,6 @@ public sealed class DrinkSystem : SharedDrinkSystem _appearance.SetData(uid, FoodVisuals.Visual, drainAvailable.Float(), appearance); } - /// - /// Tries to feed the drink item to the target entity - /// - private bool TryDrink(EntityUid user, EntityUid target, DrinkComponent drink, EntityUid item) - { - if (!HasComp(target)) - return false; - - if (!_body.TryGetBodyOrganEntityComps(target, out var stomachs)) - return false; - - if (_openable.IsClosed(item, user)) - return true; - - if (!_solutionContainer.TryGetSolution(item, drink.Solution, out _, out var drinkSolution) || drinkSolution.Volume <= 0) - { - if (drink.IgnoreEmpty) - return false; - - _popup.PopupEntity(Loc.GetString("drink-component-try-use-drink-is-empty", ("entity", item)), item, user); - return true; - } - - if (_food.IsMouthBlocked(target, user)) - return true; - - if (!_interaction.InRangeUnobstructed(user, item, popup: true)) - return true; - - var forceDrink = user != target; - - if (forceDrink) - { - var userName = Identity.Entity(user, EntityManager); - - _popup.PopupEntity(Loc.GetString("drink-component-force-feed", ("user", userName)), user, target); - - // logging - _adminLogger.Add(LogType.ForceFeed, LogImpact.High, $"{ToPrettyString(user):user} is forcing {ToPrettyString(target):target} to drink {ToPrettyString(item):drink} {SharedSolutionContainerSystem.ToPrettyString(drinkSolution)}"); - } - else - { - // log voluntary drinking - _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(target):target} is drinking {ToPrettyString(item):drink} {SharedSolutionContainerSystem.ToPrettyString(drinkSolution)}"); - } - - var flavors = _flavorProfile.GetLocalizedFlavorsMessage(user, drinkSolution); - - var doAfterEventArgs = new DoAfterArgs(EntityManager, - user, - forceDrink ? drink.ForceFeedDelay : drink.Delay, - new ConsumeDoAfterEvent(drink.Solution, flavors), - eventTarget: item, - target: target, - used: item) - { - BreakOnHandChange = false, - BreakOnMove = forceDrink, - BreakOnDamage = true, - MovementThreshold = 0.01f, - DistanceThreshold = 1.0f, - // do-after will stop if item is dropped when trying to feed someone else - // or if the item started out in the user's own hands - NeedHand = forceDrink || _hands.IsHolding(user, item), - }; - - _doAfter.TryStartDoAfter(doAfterEventArgs); - return true; - } - /// /// Raised directed at a victim when someone has force fed them a drink. /// @@ -241,7 +168,7 @@ public sealed class DrinkSystem : SharedDrinkSystem if (args.Used is null || !_solutionContainer.TryGetSolution(args.Used.Value, args.Solution, out var soln, out var solution)) return; - if (_openable.IsClosed(args.Used.Value, args.Target.Value)) + if (_openable.IsClosed(args.Used.Value, args.Target.Value, predicted: true)) return; // TODO this should really be checked every tick. @@ -330,36 +257,4 @@ public sealed class DrinkSystem : SharedDrinkSystem if (!forceDrink && solution.Volume > 0) args.Repeat = true; } - - private void AddDrinkVerb(Entity entity, ref GetVerbsEvent ev) - { - if (entity.Owner == ev.User || - !ev.CanInteract || - !ev.CanAccess || - !TryComp(ev.User, out var body) || - !_body.TryGetBodyOrganEntityComps((ev.User, body), out var stomachs)) - return; - - // Make sure the solution exists - if (!_solutionContainer.TryGetSolution(entity.Owner, entity.Comp.Solution, out var solution)) - return; - - // no drinking from living drinks, have to kill them first. - if (_mobState.IsAlive(entity)) - return; - - var user = ev.User; - AlternativeVerb verb = new() - { - Act = () => - { - TryDrink(user, user, entity.Comp, entity); - }, - Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/drink.svg.192dpi.png")), - Text = Loc.GetString("drink-system-verb-drink"), - Priority = 2 - }; - - ev.Verbs.Add(verb); - } } diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs index 48fb946135..72b8396051 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.Vape.cs @@ -1,19 +1,16 @@ -using Content.Server.Atmos; -using Content.Server.Atmos.EntitySystems; using Content.Server.Body.Components; using Content.Server.DoAfter; using Content.Server.Explosion.EntitySystems; using Content.Server.Nutrition.Components; using Content.Server.Popups; +using Content.Shared.Atmos; using Content.Shared.Damage; using Content.Shared.DoAfter; -using Content.Shared.Emag.Components; using Content.Shared.Emag.Systems; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Nutrition; -using System.Threading; -using Content.Shared.Atmos; +using Content.Shared.Nutrition.EntitySystems; /// /// System for vapes diff --git a/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs b/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs deleted file mode 100644 index 766c38d561..0000000000 --- a/Content.Server/Nutrition/EntitySystems/UtensilSystem.cs +++ /dev/null @@ -1,79 +0,0 @@ -using Content.Shared.Containers.ItemSlots; -using Content.Server.Nutrition.Components; -using Content.Server.Popups; -using Content.Shared.Interaction; -using Content.Shared.Nutrition.Components; -using Content.Shared.Nutrition.EntitySystems; -using Content.Shared.Tools.EntitySystems; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Random; - -namespace Content.Server.Nutrition.EntitySystems -{ - /// - /// Handles usage of the utensils on the food items - /// - internal sealed class UtensilSystem : SharedUtensilSystem - { - [Dependency] private readonly IRobustRandom _robustRandom = default!; - [Dependency] private readonly FoodSystem _foodSystem = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnAfterInteract, after: new[] { typeof(ItemSlotsSystem), typeof(ToolOpenableSystem) }); - } - - /// - /// Clicked with utensil - /// - private void OnAfterInteract(Entity entity, ref AfterInteractEvent ev) - { - if (ev.Handled || ev.Target == null || !ev.CanReach) - return; - - var result = TryUseUtensil(ev.User, ev.Target.Value, entity); - ev.Handled = result.Handled; - } - - public (bool Success, bool Handled) TryUseUtensil(EntityUid user, EntityUid target, Entity utensil) - { - if (!EntityManager.TryGetComponent(target, out FoodComponent? food)) - return (false, false); - - //Prevents food usage with a wrong utensil - if ((food.Utensil & utensil.Comp.Types) == 0) - { - _popupSystem.PopupEntity(Loc.GetString("food-system-wrong-utensil", ("food", target), ("utensil", utensil.Owner)), user, user); - return (false, true); - } - - if (!_interactionSystem.InRangeUnobstructed(user, target, popup: true)) - return (false, true); - - return _foodSystem.TryFeed(user, user, target, food); - } - - /// - /// Attempt to break the utensil after interaction. - /// - /// Utensil. - /// User of the utensil. - public void TryBreak(EntityUid uid, EntityUid userUid, UtensilComponent? component = null) - { - if (!Resolve(uid, ref component)) - return; - - if (_robustRandom.Prob(component.BreakChance)) - { - _audio.PlayPvs(component.BreakSound, userUid, AudioParams.Default.WithVolume(-2f)); - EntityManager.DeleteEntity(uid); - } - } - } -} diff --git a/Content.Server/Body/Components/StomachComponent.cs b/Content.Shared/Body/Components/StomachComponent.cs similarity index 89% rename from Content.Server/Body/Components/StomachComponent.cs rename to Content.Shared/Body/Components/StomachComponent.cs index 4821b78f4a..b76dff0902 100644 --- a/Content.Server/Body/Components/StomachComponent.cs +++ b/Content.Shared/Body/Components/StomachComponent.cs @@ -1,13 +1,14 @@ -using Content.Server.Body.Systems; -using Content.Server.Nutrition.EntitySystems; +using Content.Shared.Body.Systems; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; +using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Whitelist; +using Robust.Shared.GameStates; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -namespace Content.Server.Body.Components +namespace Content.Shared.Body.Components { - [RegisterComponent, Access(typeof(StomachSystem), typeof(FoodSystem))] + [RegisterComponent, NetworkedComponent, Access(typeof(StomachSystem), typeof(FoodSystem))] public sealed partial class StomachComponent : Component { /// @@ -32,7 +33,7 @@ namespace Content.Server.Body.Components /// What solution should this stomach push reagents into, on the body? /// [DataField] - public string BodySolutionName = BloodstreamComponent.DefaultChemicalsSolutionName; + public string BodySolutionName = "chemicals"; /// /// Time between reagents being ingested and them being diff --git a/Content.Shared/Body/Events/ApplyMetabolicMultiplierEvent.cs b/Content.Shared/Body/Events/ApplyMetabolicMultiplierEvent.cs new file mode 100644 index 0000000000..dafc1e49de --- /dev/null +++ b/Content.Shared/Body/Events/ApplyMetabolicMultiplierEvent.cs @@ -0,0 +1,26 @@ +namespace Content.Shared.Body.Events; + +// TODO REFACTOR THIS +// This will cause rates to slowly drift over time due to floating point errors. +// Instead, the system that raised this should trigger an update and subscribe to get-modifier events. +[ByRefEvent] +public readonly record struct ApplyMetabolicMultiplierEvent( + EntityUid Uid, + float Multiplier, + bool Apply) +{ + /// + /// The entity whose metabolism is being modified. + /// + public readonly EntityUid Uid = Uid; + + /// + /// What the metabolism's update rate will be multiplied by. + /// + public readonly float Multiplier = Multiplier; + + /// + /// If true, apply the multiplier. If false, revert it. + /// + public readonly bool Apply = Apply; +} diff --git a/Content.Server/Body/Systems/StomachSystem.cs b/Content.Shared/Body/Systems/StomachSystem.cs similarity index 98% rename from Content.Server/Body/Systems/StomachSystem.cs rename to Content.Shared/Body/Systems/StomachSystem.cs index 9fc7ff10e4..935dda6389 100644 --- a/Content.Server/Body/Systems/StomachSystem.cs +++ b/Content.Shared/Body/Systems/StomachSystem.cs @@ -1,12 +1,13 @@ -using Content.Server.Body.Components; -using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Body.Components; +using Content.Shared.Body.Events; using Content.Shared.Body.Organ; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; using Robust.Shared.Timing; using Robust.Shared.Utility; -namespace Content.Server.Body.Systems +namespace Content.Shared.Body.Systems { public sealed class StomachSystem : EntitySystem { diff --git a/Content.Shared/Burial/BurialSystem.cs b/Content.Shared/Burial/BurialSystem.cs index 68b634efc1..5216e39dc8 100644 --- a/Content.Shared/Burial/BurialSystem.cs +++ b/Content.Shared/Burial/BurialSystem.cs @@ -1,5 +1,4 @@ using Content.Shared.ActionBlocker; -using Content.Shared.Burial; using Content.Shared.Burial.Components; using Content.Shared.DoAfter; using Content.Shared.Interaction; @@ -10,7 +9,7 @@ using Content.Shared.Storage.Components; using Content.Shared.Storage.EntitySystems; using Robust.Shared.Audio.Systems; -namespace Content.Server.Burial.Systems; +namespace Content.Shared.Burial; public sealed class BurialSystem : EntitySystem { diff --git a/Content.Shared/CCVar/CCVars.Misc.cs b/Content.Shared/CCVar/CCVars.Misc.cs index 3d597c7427..5fda4dc2fd 100644 --- a/Content.Shared/CCVar/CCVars.Misc.cs +++ b/Content.Shared/CCVar/CCVars.Misc.cs @@ -38,7 +38,7 @@ public sealed partial class CCVars /// some food object won't spam a user with flavors. /// public static readonly CVarDef - FlavorLimit = CVarDef.Create("flavor.limit", 10, CVar.SERVERONLY); + FlavorLimit = CVarDef.Create("flavor.limit", 10, CVar.SERVER | CVar.REPLICATED); public static readonly CVarDef DestinationFile = CVarDef.Create("autogen.destination_file", "", CVar.SERVER | CVar.SERVERONLY); diff --git a/Content.Shared/EntityEffects/EffectConditions/InternalsCondition.cs b/Content.Shared/EntityEffects/EffectConditions/InternalsCondition.cs index 1bc5b26cfb..31294bc21f 100644 --- a/Content.Shared/EntityEffects/EffectConditions/InternalsCondition.cs +++ b/Content.Shared/EntityEffects/EffectConditions/InternalsCondition.cs @@ -1,8 +1,7 @@ using Content.Shared.Body.Components; -using Content.Shared.EntityEffects; using Robust.Shared.Prototypes; -namespace Content.Server.EntityEffects.EffectConditions; +namespace Content.Shared.EntityEffects.EffectConditions; /// /// Condition for if the entity is or isn't wearing internals. diff --git a/Content.Shared/Ghost/Roles/Raffles/GhostRoleRaffleSettings.cs b/Content.Shared/Ghost/Roles/Raffles/GhostRoleRaffleSettings.cs index a7aa757b72..4f31bb7182 100644 --- a/Content.Shared/Ghost/Roles/Raffles/GhostRoleRaffleSettings.cs +++ b/Content.Shared/Ghost/Roles/Raffles/GhostRoleRaffleSettings.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Ghost.Roles.Raffles; +namespace Content.Shared.Ghost.Roles.Raffles; /// /// Defines settings for a ghost role raffle. diff --git a/Content.Shared/Ghost/Roles/Raffles/GhostRoleRaffleSettingsPrototype.cs b/Content.Shared/Ghost/Roles/Raffles/GhostRoleRaffleSettingsPrototype.cs index 2c6e7229d7..18ebff556a 100644 --- a/Content.Shared/Ghost/Roles/Raffles/GhostRoleRaffleSettingsPrototype.cs +++ b/Content.Shared/Ghost/Roles/Raffles/GhostRoleRaffleSettingsPrototype.cs @@ -1,5 +1,4 @@ -using Content.Server.Ghost.Roles.Raffles; -using Robust.Shared.Prototypes; +using Robust.Shared.Prototypes; namespace Content.Shared.Ghost.Roles.Raffles; diff --git a/Content.Shared/Light/Components/LightReplacerComponent.cs b/Content.Shared/Light/Components/LightReplacerComponent.cs index 8d7d233927..3ce9647c88 100644 --- a/Content.Shared/Light/Components/LightReplacerComponent.cs +++ b/Content.Shared/Light/Components/LightReplacerComponent.cs @@ -4,7 +4,7 @@ using Robust.Shared.Audio; using Robust.Shared.Containers; using Robust.Shared.GameStates; -namespace Content.Server.Light.Components; +namespace Content.Shared.Light.Components; /// /// Device that allows user to quikly change bulbs in diff --git a/Content.Shared/Medical/Cryogenics/ActiveCryoPodComponent.cs b/Content.Shared/Medical/Cryogenics/ActiveCryoPodComponent.cs index 21c000a289..e242fd7502 100644 --- a/Content.Shared/Medical/Cryogenics/ActiveCryoPodComponent.cs +++ b/Content.Shared/Medical/Cryogenics/ActiveCryoPodComponent.cs @@ -1,4 +1,4 @@ -namespace Content.Server.Medical.Components; +namespace Content.Shared.Medical.Cryogenics; /// /// Tracking component for an enabled cryo pod (which periodically tries to inject chemicals in the occupant, if one exists) diff --git a/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs b/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs index 891129a2dd..8d7eb05cd9 100644 --- a/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs +++ b/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs @@ -1,4 +1,3 @@ -using Content.Server.Medical.Components; using Content.Shared.Administration.Logs; using Content.Shared.Body.Components; using Content.Shared.Database; diff --git a/Content.Server/Nutrition/Components/FlavorProfileComponent.cs b/Content.Shared/Nutrition/Components/FlavorProfileComponent.cs similarity index 74% rename from Content.Server/Nutrition/Components/FlavorProfileComponent.cs rename to Content.Shared/Nutrition/Components/FlavorProfileComponent.cs index a4d5c1085a..a3ca92ecf2 100644 --- a/Content.Server/Nutrition/Components/FlavorProfileComponent.cs +++ b/Content.Shared/Nutrition/Components/FlavorProfileComponent.cs @@ -1,22 +1,24 @@ -namespace Content.Server.Nutrition.Components; +using Robust.Shared.GameStates; -[RegisterComponent] +namespace Content.Shared.Nutrition.Components; + +[RegisterComponent, NetworkedComponent] public sealed partial class FlavorProfileComponent : Component { /// /// Localized string containing the base flavor of this entity. /// - [DataField("flavors")] + [DataField] public HashSet Flavors { get; private set; } = new(); /// /// Reagent IDs to ignore when processing this flavor profile. Defaults to nutriment. /// - [DataField("ignoreReagents")] + [DataField] public HashSet IgnoreReagents { get; private set; } = new() { "Nutriment", "Vitamin", - "Protein" + "Protein", }; } diff --git a/Content.Server/Nutrition/Components/FoodComponent.cs b/Content.Shared/Nutrition/Components/FoodComponent.cs similarity index 92% rename from Content.Server/Nutrition/Components/FoodComponent.cs rename to Content.Shared/Nutrition/Components/FoodComponent.cs index cfb69f53f0..ce04569fcb 100644 --- a/Content.Server/Nutrition/Components/FoodComponent.cs +++ b/Content.Shared/Nutrition/Components/FoodComponent.cs @@ -1,11 +1,10 @@ -using Content.Server.Body.Components; -using Content.Shared.Nutrition.Components; -using Content.Server.Nutrition.EntitySystems; +using Content.Shared.Body.Components; using Content.Shared.FixedPoint; +using Content.Shared.Nutrition.EntitySystems; using Robust.Shared.Audio; using Robust.Shared.Prototypes; -namespace Content.Server.Nutrition.Components; +namespace Content.Shared.Nutrition.Components; [RegisterComponent, Access(typeof(FoodSystem), typeof(FoodSequenceSystem))] public sealed partial class FoodComponent : Component diff --git a/Content.Server/Nutrition/Components/IngestionBlockerComponent.cs b/Content.Shared/Nutrition/Components/IngestionBlockerComponent.cs similarity index 76% rename from Content.Server/Nutrition/Components/IngestionBlockerComponent.cs rename to Content.Shared/Nutrition/Components/IngestionBlockerComponent.cs index c3188d07a0..803bf1f8b2 100644 --- a/Content.Server/Nutrition/Components/IngestionBlockerComponent.cs +++ b/Content.Shared/Nutrition/Components/IngestionBlockerComponent.cs @@ -1,6 +1,6 @@ -using Content.Server.Nutrition.EntitySystems; +using Content.Shared.Nutrition.EntitySystems; -namespace Content.Server.Nutrition.Components; +namespace Content.Shared.Nutrition.Components; /// /// Component that denotes a piece of clothing that blocks the mouth or otherwise prevents eating & drinking. @@ -9,7 +9,7 @@ namespace Content.Server.Nutrition.Components; /// In the event that more head-wear & mask functionality is added (like identity systems, or raising/lowering of /// masks), then this component might become redundant. /// -[RegisterComponent, Access(typeof(FoodSystem), typeof(DrinkSystem), typeof(IngestionBlockerSystem))] +[RegisterComponent, Access(typeof(FoodSystem), typeof(SharedDrinkSystem), typeof(IngestionBlockerSystem))] public sealed partial class IngestionBlockerComponent : Component { /// diff --git a/Content.Shared/Nutrition/Components/UtensilComponent.cs b/Content.Shared/Nutrition/Components/UtensilComponent.cs index 50158f1f63..e8da588186 100644 --- a/Content.Shared/Nutrition/Components/UtensilComponent.cs +++ b/Content.Shared/Nutrition/Components/UtensilComponent.cs @@ -4,7 +4,7 @@ using Robust.Shared.GameStates; namespace Content.Shared.Nutrition.Components { - [RegisterComponent, NetworkedComponent, Access(typeof(SharedUtensilSystem))] + [RegisterComponent, NetworkedComponent, Access(typeof(UtensilSystem))] public sealed partial class UtensilComponent : Component { [DataField("types")] diff --git a/Content.Server/Nutrition/EntitySystems/FlavorProfileSystem.cs b/Content.Shared/Nutrition/EntitySystems/FlavorProfileSystem.cs similarity index 96% rename from Content.Server/Nutrition/EntitySystems/FlavorProfileSystem.cs rename to Content.Shared/Nutrition/EntitySystems/FlavorProfileSystem.cs index 4a18696cea..31384f3a18 100644 --- a/Content.Server/Nutrition/EntitySystems/FlavorProfileSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/FlavorProfileSystem.cs @@ -1,12 +1,11 @@ -using Content.Server.Nutrition.Components; +using System.Linq; using Content.Shared.CCVar; using Content.Shared.Chemistry.Components; -using Content.Shared.Nutrition; +using Content.Shared.Nutrition.Components; using Robust.Shared.Configuration; using Robust.Shared.Prototypes; -using System.Linq; -namespace Content.Server.Nutrition.EntitySystems; +namespace Content.Shared.Nutrition.EntitySystems; /// /// Deals with flavor profiles when you eat something. diff --git a/Content.Server/Nutrition/EntitySystems/FoodSequenceSystem.cs b/Content.Shared/Nutrition/EntitySystems/FoodSequenceSystem.cs similarity index 96% rename from Content.Server/Nutrition/EntitySystems/FoodSequenceSystem.cs rename to Content.Shared/Nutrition/EntitySystems/FoodSequenceSystem.cs index 8953773053..7b50ae2c8b 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSequenceSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/FoodSequenceSystem.cs @@ -1,21 +1,17 @@ using System.Numerics; using System.Text; -using Content.Server.Nutrition.Components; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Mobs.Systems; -using Content.Shared.Nutrition; using Content.Shared.Nutrition.Components; -using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Nutrition.Prototypes; using Content.Shared.Popups; using Content.Shared.Storage.Components; using Content.Shared.Tag; -using Robust.Server.GameObjects; using Robust.Shared.Prototypes; using Robust.Shared.Random; -namespace Content.Server.Nutrition.EntitySystems; +namespace Content.Shared.Nutrition.EntitySystems; public sealed class FoodSequenceSystem : SharedFoodSequenceSystem { @@ -26,7 +22,7 @@ public sealed class FoodSequenceSystem : SharedFoodSequenceSystem [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; public override void Initialize() { @@ -126,7 +122,7 @@ public sealed class FoodSequenceSystem : SharedFoodSequenceSystem if (start.Comp.FoodLayers.Count >= start.Comp.MaxLayers && !elementIndexed.Final || start.Comp.Finished) { if (user is not null) - _popup.PopupEntity(Loc.GetString("food-sequence-no-space"), start, user.Value); + _popup.PopupClient(Loc.GetString("food-sequence-no-space"), start, user.Value); return false; } diff --git a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs b/Content.Shared/Nutrition/EntitySystems/FoodSystem.cs similarity index 93% rename from Content.Server/Nutrition/EntitySystems/FoodSystem.cs rename to Content.Shared/Nutrition/EntitySystems/FoodSystem.cs index 3bdf3393ae..a4122168e4 100644 --- a/Content.Server/Nutrition/EntitySystems/FoodSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/FoodSystem.cs @@ -1,16 +1,13 @@ -using Content.Server.Body.Components; -using Content.Server.Body.Systems; -using Content.Shared.Chemistry.EntitySystems; -using Content.Server.Inventory; -using Content.Server.Nutrition.Components; -using Content.Shared.Nutrition.Components; -using Content.Server.Popups; -using Content.Server.Stack; +using System.Linq; using Content.Shared.Administration.Logs; using Content.Shared.Body.Components; using Content.Shared.Body.Organ; +using Content.Shared.Body.Systems; using Content.Shared.Chemistry; +using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; +using Content.Shared.Destructible; using Content.Shared.DoAfter; using Content.Shared.FixedPoint; using Content.Shared.Hands.Components; @@ -21,42 +18,38 @@ using Content.Shared.Interaction.Components; using Content.Shared.Interaction.Events; using Content.Shared.Inventory; using Content.Shared.Mobs.Systems; -using Content.Shared.Nutrition; -using Content.Shared.Nutrition.EntitySystems; +using Content.Shared.Nutrition.Components; +using Content.Shared.Popups; using Content.Shared.Stacks; using Content.Shared.Storage; using Content.Shared.Verbs; +using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; using Robust.Shared.Utility; -using System.Linq; -using Content.Shared.Containers.ItemSlots; -using Robust.Server.GameObjects; -using Content.Shared.Whitelist; -using Content.Shared.Destructible; -namespace Content.Server.Nutrition.EntitySystems; +namespace Content.Shared.Nutrition.EntitySystems; /// /// Handles feeding attempts both on yourself and on the target. /// public sealed class FoodSystem : EntitySystem { - [Dependency] private readonly BodySystem _body = default!; + [Dependency] private readonly SharedBodySystem _body = default!; [Dependency] private readonly FlavorProfileSystem _flavorProfile = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; [Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly OpenableSystem _openable = default!; - [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly ReactiveSystem _reaction = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedHandsSystem _hands = default!; [Dependency] private readonly SharedInteractionSystem _interaction = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; - [Dependency] private readonly TransformSystem _transform = default!; - [Dependency] private readonly StackSystem _stack = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedStackSystem _stack = default!; [Dependency] private readonly StomachSystem _stomach = default!; [Dependency] private readonly UtensilSystem _utensil = default!; [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; @@ -69,7 +62,7 @@ public sealed class FoodSystem : EntitySystem // TODO add InteractNoHandEvent for entities like mice. // run after openable for wrapped/peelable foods - SubscribeLocalEvent(OnUseFoodInHand, after: new[] { typeof(OpenableSystem), typeof(ServerInventorySystem) }); + SubscribeLocalEvent(OnUseFoodInHand, after: new[] { typeof(OpenableSystem), typeof(InventorySystem) }); SubscribeLocalEvent(OnFeedFood); SubscribeLocalEvent>(AddEatVerb); SubscribeLocalEvent(OnDoAfter); @@ -116,7 +109,7 @@ public sealed class FoodSystem : EntitySystem if (HasComp(food)) return (false, false); - if (_openable.IsClosed(food, user)) + if (_openable.IsClosed(food, user, predicted: true)) return (false, true); if (!_solutionContainer.TryGetSolution(food, foodComp.Solution, out _, out var foodSolution)) @@ -135,7 +128,7 @@ public sealed class FoodSystem : EntitySystem // Check for used storage on the food item if (TryComp(food, out var storageState) && storageState.Container.ContainedEntities.Any()) { - _popup.PopupEntity(Loc.GetString("food-has-used-storage", ("food", food)), user, user); + _popup.PopupClient(Loc.GetString("food-has-used-storage", ("food", food)), user, user); return (false, true); } @@ -144,7 +137,7 @@ public sealed class FoodSystem : EntitySystem { if (itemSlots.Slots.Any(slot => slot.Value.HasItem)) { - _popup.PopupEntity(Loc.GetString("food-has-used-storage", ("food", food)), user, user); + _popup.PopupClient(Loc.GetString("food-has-used-storage", ("food", food)), user, user); return (false, true); } } @@ -153,7 +146,7 @@ public sealed class FoodSystem : EntitySystem if (GetUsesRemaining(food, foodComp) <= 0) { - _popup.PopupEntity(Loc.GetString("food-system-try-use-food-is-empty", ("entity", food)), user, user); + _popup.PopupClient(Loc.GetString("food-system-try-use-food-is-empty", ("entity", food)), user, user); DeleteAndSpawnTrash(foodComp, food, user); return (false, true); } @@ -171,7 +164,7 @@ public sealed class FoodSystem : EntitySystem if (!_transform.GetMapCoordinates(user).InRange(_transform.GetMapCoordinates(target), MaxFeedDistance)) { var message = Loc.GetString("interaction-system-user-interaction-cannot-reach"); - _popup.PopupEntity(message, user, user); + _popup.PopupClient(message, user, user); return (false, true); } @@ -268,7 +261,7 @@ public sealed class FoodSystem : EntitySystem if (stomachToUse == null) { _solutionContainer.TryAddSolution(soln.Value, split); - _popup.PopupEntity(forceFeed ? Loc.GetString("food-system-you-cannot-eat-any-more-other", ("target", args.Target.Value)) : Loc.GetString("food-system-you-cannot-eat-any-more"), args.Target.Value, args.User); + _popup.PopupClient(forceFeed ? Loc.GetString("food-system-you-cannot-eat-any-more-other", ("target", args.Target.Value)) : Loc.GetString("food-system-you-cannot-eat-any-more"), args.Target.Value, args.User); return; } @@ -283,20 +276,20 @@ public sealed class FoodSystem : EntitySystem var userName = Identity.Entity(args.User, EntityManager); _popup.PopupEntity(Loc.GetString("food-system-force-feed-success", ("user", userName), ("flavors", flavors)), entity.Owner, entity.Owner); - _popup.PopupEntity(Loc.GetString("food-system-force-feed-success-user", ("target", targetName)), args.User, args.User); + _popup.PopupClient(Loc.GetString("food-system-force-feed-success-user", ("target", targetName)), args.User, args.User); // log successful force feed _adminLogger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(entity.Owner):user} forced {ToPrettyString(args.User):target} to eat {ToPrettyString(entity.Owner):food}"); } else { - _popup.PopupEntity(Loc.GetString(entity.Comp.EatMessage, ("food", entity.Owner), ("flavors", flavors)), args.User, args.User); + _popup.PopupClient(Loc.GetString(entity.Comp.EatMessage, ("food", entity.Owner), ("flavors", flavors)), args.User, args.User); // log successful voluntary eating _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(args.User):target} ate {ToPrettyString(entity.Owner):food}"); } - _audio.PlayPvs(entity.Comp.UseSound, args.Target.Value, AudioParams.Default.WithVolume(-1f).WithVariation(0.20f)); + _audio.PlayPredicted(entity.Comp.UseSound, args.Target.Value, args.User, AudioParams.Default.WithVolume(-1f).WithVariation(0.20f)); // Try to break all used utensils foreach (var utensil in utensils) @@ -484,7 +477,7 @@ public sealed class FoodSystem : EntitySystem // If "required" field is set, try to block eating without proper utensils used if (component.UtensilRequired && (usedTypes & component.Utensil) != component.Utensil) { - _popup.PopupEntity(Loc.GetString("food-you-need-to-hold-utensil", ("utensil", component.Utensil ^ usedTypes)), user, user); + _popup.PopupClient(Loc.GetString("food-you-need-to-hold-utensil", ("utensil", component.Utensil ^ usedTypes)), user, user); return false; } @@ -533,7 +526,7 @@ public sealed class FoodSystem : EntitySystem RaiseLocalEvent(uid, attempt, false); if (attempt.Cancelled && attempt.Blocker != null && popupUid != null) { - _popup.PopupEntity(Loc.GetString("food-system-remove-mask", ("entity", attempt.Blocker.Value)), + _popup.PopupClient(Loc.GetString("food-system-remove-mask", ("entity", attempt.Blocker.Value)), uid, popupUid.Value); } diff --git a/Content.Server/Nutrition/EntitySystems/IngestionBlockerSystem.cs b/Content.Shared/Nutrition/EntitySystems/IngestionBlockerSystem.cs similarity index 76% rename from Content.Server/Nutrition/EntitySystems/IngestionBlockerSystem.cs rename to Content.Shared/Nutrition/EntitySystems/IngestionBlockerSystem.cs index 63b39fb524..f9cd233948 100644 --- a/Content.Server/Nutrition/EntitySystems/IngestionBlockerSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/IngestionBlockerSystem.cs @@ -1,7 +1,7 @@ -using Content.Server.Nutrition.Components; -using Content.Shared.Clothing; +using Content.Shared.Clothing; +using Content.Shared.Nutrition.Components; -namespace Content.Server.Nutrition.EntitySystems; +namespace Content.Shared.Nutrition.EntitySystems; public sealed class IngestionBlockerSystem : EntitySystem { diff --git a/Content.Shared/Nutrition/EntitySystems/OpenableSystem.cs b/Content.Shared/Nutrition/EntitySystems/OpenableSystem.cs index 85a94a7ec6..2f276fa93d 100644 --- a/Content.Shared/Nutrition/EntitySystems/OpenableSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/OpenableSystem.cs @@ -1,8 +1,8 @@ using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Examine; -using Content.Shared.Lock; using Content.Shared.Interaction; using Content.Shared.Interaction.Events; +using Content.Shared.Lock; using Content.Shared.Nutrition.Components; using Content.Shared.Popups; using Content.Shared.Verbs; @@ -166,7 +166,7 @@ public sealed partial class OpenableSystem : EntitySystem /// Drinks that don't have OpenableComponent are automatically open, so it returns false. /// If user is not null a popup will be shown to them. /// - public bool IsClosed(EntityUid uid, EntityUid? user = null, OpenableComponent? comp = null) + public bool IsClosed(EntityUid uid, EntityUid? user = null, OpenableComponent? comp = null, bool predicted = false) { if (!Resolve(uid, ref comp, false)) return false; @@ -175,7 +175,12 @@ public sealed partial class OpenableSystem : EntitySystem return false; if (user != null) - _popup.PopupEntity(Loc.GetString(comp.ClosedPopup, ("owner", uid)), user.Value, user.Value); + { + if (predicted) + _popup.PopupClient(Loc.GetString(comp.ClosedPopup, ("owner", uid)), user.Value, user.Value); + else + _popup.PopupEntity(Loc.GetString(comp.ClosedPopup, ("owner", uid)), user.Value, user.Value); + } return true; } diff --git a/Content.Shared/Nutrition/EntitySystems/SharedDrinkSystem.cs b/Content.Shared/Nutrition/EntitySystems/SharedDrinkSystem.cs index bf1e585fab..a4b8e13b2f 100644 --- a/Content.Shared/Nutrition/EntitySystems/SharedDrinkSystem.cs +++ b/Content.Shared/Nutrition/EntitySystems/SharedDrinkSystem.cs @@ -1,15 +1,36 @@ +using Content.Shared.Administration.Logs; +using Content.Shared.Body.Components; +using Content.Shared.Body.Systems; using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Database; +using Content.Shared.DoAfter; using Content.Shared.Examine; using Content.Shared.FixedPoint; +using Content.Shared.Hands.EntitySystems; +using Content.Shared.IdentityManagement; +using Content.Shared.Interaction; +using Content.Shared.Mobs.Systems; using Content.Shared.Nutrition.Components; +using Content.Shared.Popups; +using Content.Shared.Verbs; +using Robust.Shared.Utility; namespace Content.Shared.Nutrition.EntitySystems; public abstract partial class SharedDrinkSystem : EntitySystem { - [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; + [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] private readonly SharedBodySystem _body = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly FlavorProfileSystem _flavorProfile = default!; + [Dependency] private readonly FoodSystem _food = default!; + [Dependency] private readonly SharedHandsSystem _hands = default!; + [Dependency] private readonly SharedInteractionSystem _interaction = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly OpenableSystem _openable = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; public override void Initialize() { @@ -17,6 +38,7 @@ public abstract partial class SharedDrinkSystem : EntitySystem SubscribeLocalEvent(OnAttemptShake); SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent>(AddDrinkVerb); } protected void OnAttemptShake(Entity entity, ref AttemptShakeEvent args) @@ -28,7 +50,7 @@ public abstract partial class SharedDrinkSystem : EntitySystem protected void OnExamined(Entity entity, ref ExaminedEvent args) { TryComp(entity, out var openable); - if (_openable.IsClosed(entity.Owner, null, openable) || !args.IsInDetailsRange || !entity.Comp.Examinable) + if (_openable.IsClosed(entity.Owner, null, openable, true) || !args.IsInDetailsRange || !entity.Comp.Examinable) return; var empty = IsEmpty(entity, entity.Comp); @@ -57,6 +79,38 @@ public abstract partial class SharedDrinkSystem : EntitySystem } } + private void AddDrinkVerb(Entity entity, ref GetVerbsEvent ev) + { + if (entity.Owner == ev.User || + !ev.CanInteract || + !ev.CanAccess || + !TryComp(ev.User, out var body) || + !_body.TryGetBodyOrganEntityComps((ev.User, body), out var stomachs)) + return; + + // Make sure the solution exists + if (!_solutionContainer.TryGetSolution(entity.Owner, entity.Comp.Solution, out var solution)) + return; + + // no drinking from living drinks, have to kill them first. + if (_mobState.IsAlive(entity)) + return; + + var user = ev.User; + AlternativeVerb verb = new() + { + Act = () => + { + TryDrink(user, user, entity.Comp, entity); + }, + Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/drink.svg.192dpi.png")), + Text = Loc.GetString("drink-system-verb-drink"), + Priority = 2 + }; + + ev.Verbs.Add(verb); + } + protected FixedPoint2 DrinkVolume(EntityUid uid, DrinkComponent? component = null) { if (!Resolve(uid, ref component)) @@ -87,4 +141,74 @@ public abstract partial class SharedDrinkSystem : EntitySystem return remainingString; } + + /// + /// Tries to feed the drink item to the target entity + /// + protected bool TryDrink(EntityUid user, EntityUid target, DrinkComponent drink, EntityUid item) + { + if (!HasComp(target)) + return false; + + if (!_body.TryGetBodyOrganEntityComps(target, out var stomachs)) + return false; + + if (_openable.IsClosed(item, user, predicted: true)) + return true; + + if (!_solutionContainer.TryGetSolution(item, drink.Solution, out _, out var drinkSolution) || drinkSolution.Volume <= 0) + { + if (drink.IgnoreEmpty) + return false; + + _popup.PopupClient(Loc.GetString("drink-component-try-use-drink-is-empty", ("entity", item)), item, user); + return true; + } + + if (_food.IsMouthBlocked(target, user)) + return true; + + if (!_interaction.InRangeUnobstructed(user, item, popup: true)) + return true; + + var forceDrink = user != target; + + if (forceDrink) + { + var userName = Identity.Entity(user, EntityManager); + + _popup.PopupEntity(Loc.GetString("drink-component-force-feed", ("user", userName)), user, target); + + // logging + _adminLogger.Add(LogType.ForceFeed, LogImpact.High, $"{ToPrettyString(user):user} is forcing {ToPrettyString(target):target} to drink {ToPrettyString(item):drink} {SharedSolutionContainerSystem.ToPrettyString(drinkSolution)}"); + } + else + { + // log voluntary drinking + _adminLogger.Add(LogType.Ingestion, LogImpact.Low, $"{ToPrettyString(target):target} is drinking {ToPrettyString(item):drink} {SharedSolutionContainerSystem.ToPrettyString(drinkSolution)}"); + } + + var flavors = _flavorProfile.GetLocalizedFlavorsMessage(user, drinkSolution); + + var doAfterEventArgs = new DoAfterArgs(EntityManager, + user, + forceDrink ? drink.ForceFeedDelay : drink.Delay, + new ConsumeDoAfterEvent(drink.Solution, flavors), + eventTarget: item, + target: target, + used: item) + { + BreakOnHandChange = false, + BreakOnMove = forceDrink, + BreakOnDamage = true, + MovementThreshold = 0.01f, + DistanceThreshold = 1.0f, + // do-after will stop if item is dropped when trying to feed someone else + // or if the item started out in the user's own hands + NeedHand = forceDrink || _hands.IsHolding(user, item), + }; + + _doAfter.TryStartDoAfter(doAfterEventArgs); + return true; + } } diff --git a/Content.Shared/Nutrition/EntitySystems/SharedUtensilSystem.cs b/Content.Shared/Nutrition/EntitySystems/SharedUtensilSystem.cs deleted file mode 100644 index 656a36f5d0..0000000000 --- a/Content.Shared/Nutrition/EntitySystems/SharedUtensilSystem.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Content.Shared.Nutrition.EntitySystems; - -public abstract class SharedUtensilSystem : EntitySystem -{ -} diff --git a/Content.Shared/Nutrition/EntitySystems/UtensilSystem.cs b/Content.Shared/Nutrition/EntitySystems/UtensilSystem.cs new file mode 100644 index 0000000000..e100d25589 --- /dev/null +++ b/Content.Shared/Nutrition/EntitySystems/UtensilSystem.cs @@ -0,0 +1,73 @@ +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Interaction; +using Content.Shared.Nutrition.Components; +using Content.Shared.Popups; +using Content.Shared.Tools.EntitySystems; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Random; + +namespace Content.Shared.Nutrition.EntitySystems; + +public sealed class UtensilSystem : EntitySystem +{ + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly FoodSystem _foodSystem = default!; + [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly IRobustRandom _robustRandom = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAfterInteract, after: new[] { typeof(ItemSlotsSystem), typeof(ToolOpenableSystem) }); + } + + /// + /// Clicked with utensil + /// + private void OnAfterInteract(Entity entity, ref AfterInteractEvent ev) + { + if (ev.Handled || ev.Target == null || !ev.CanReach) + return; + + var result = TryUseUtensil(ev.User, ev.Target.Value, entity); + ev.Handled = result.Handled; + } + + public (bool Success, bool Handled) TryUseUtensil(EntityUid user, EntityUid target, Entity utensil) + { + if (!EntityManager.TryGetComponent(target, out FoodComponent? food)) + return (false, false); + + //Prevents food usage with a wrong utensil + if ((food.Utensil & utensil.Comp.Types) == 0) + { + _popupSystem.PopupEntity(Loc.GetString("food-system-wrong-utensil", ("food", target), ("utensil", utensil.Owner)), user, user); + return (false, true); + } + + if (!_interactionSystem.InRangeUnobstructed(user, target, popup: true)) + return (false, true); + + return _foodSystem.TryFeed(user, user, target, food); + } + + /// + /// Attempt to break the utensil after interaction. + /// + /// Utensil. + /// User of the utensil. + public void TryBreak(EntityUid uid, EntityUid userUid, UtensilComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + if (_robustRandom.Prob(component.BreakChance)) + { + _audio.PlayPredicted(component.BreakSound, userUid, userUid, AudioParams.Default.WithVolume(-2f)); + EntityManager.DeleteEntity(uid); + } + } +} diff --git a/Content.Shared/Storage/Components/MagnetPickupComponent.cs b/Content.Shared/Storage/Components/MagnetPickupComponent.cs index 3467439a6d..90b7e83d63 100644 --- a/Content.Shared/Storage/Components/MagnetPickupComponent.cs +++ b/Content.Shared/Storage/Components/MagnetPickupComponent.cs @@ -1,6 +1,6 @@ using Content.Shared.Inventory; -namespace Content.Server.Storage.Components; +namespace Content.Shared.Storage.Components; /// /// Applies an ongoing pickup area around the attached entity. diff --git a/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs b/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs index c1fc856ff3..9a0b48e65b 100644 --- a/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs +++ b/Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs @@ -1,7 +1,6 @@ -using Content.Server.Storage.Components; using Content.Shared.Inventory; +using Content.Shared.Storage.Components; using Content.Shared.Whitelist; -using Robust.Shared.Map; using Robust.Shared.Physics.Components; using Robust.Shared.Timing;