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;