From eeced1aeba259a42d965b5dcbfe46d64f86c3ea4 Mon Sep 17 00:00:00 2001 From: checkraze Date: Thu, 1 Feb 2024 16:07:19 -0500 Subject: [PATCH] fryer system update --- .../Kitchen/Components/DeepFriedComponent.cs | 1 + .../Kitchen/Components/DeepFryerComponent.cs | 1 + .../Kitchen/UI/DeepFryerBoundUserInterface.cs | 2 +- .../Kitchen/UI/DeepFryerWindow.xaml.cs | 2 +- .../Visualizers/DeepFriedVisualizer.cs | 1 + .../Visualizers/DeepFryerVisualizer.cs | 2 + .../Kitchen/Components/DeepFriedComponent.cs | 1 + .../Kitchen/Components/DeepFryerComponent.cs | 29 +- .../EntitySystems/DeepFryerSystem.Results.cs | 181 +++ .../EntitySystems/DeepFryerSystem.Storage.cs | 93 ++ .../EntitySystems/DeepFryerSystem.Update.cs | 128 ++ .../Kitchen/EntitySystems/DeepFryerSystem.cs | 1440 +++++++---------- .../SalvageSystem.ExpeditionConsole.cs | 2 + .../Salvage/SpawnSalvageMissionJob.cs | 12 +- .../Traits/Assorted/FriedTraitSystem.cs | 1 + .../Kitchen/ClearSlagDoAfterEvent.cs | 4 +- .../Components/SharedDeepFriedComponent.cs | 2 +- .../Components/SharedDeepFryerComponent.cs | 2 +- .../Kitchen/SharedDeepfryerSystem.cs | 5 + .../Kitchen/UI/DeepFryerMessages.cs | 4 +- 20 files changed, 1009 insertions(+), 904 deletions(-) create mode 100644 Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Results.cs create mode 100644 Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Storage.cs create mode 100644 Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Update.cs create mode 100644 Content.Shared/Nyanotrasen/Kitchen/SharedDeepfryerSystem.cs diff --git a/Content.Client/Nyanotrasen/Kitchen/Components/DeepFriedComponent.cs b/Content.Client/Nyanotrasen/Kitchen/Components/DeepFriedComponent.cs index a1252cf43ed..689dcc00fcb 100644 --- a/Content.Client/Nyanotrasen/Kitchen/Components/DeepFriedComponent.cs +++ b/Content.Client/Nyanotrasen/Kitchen/Components/DeepFriedComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.Kitchen.Components; +using Content.Shared.Nyanotrasen.Kitchen.Components; namespace Content.Client.Kitchen.Components { diff --git a/Content.Client/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs b/Content.Client/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs index 4123eb341ca..34d5a864d49 100644 --- a/Content.Client/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs +++ b/Content.Client/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.Kitchen.Components; +using Content.Shared.Nyanotrasen.Kitchen.Components; namespace Content.Client.Kitchen.Components { diff --git a/Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerBoundUserInterface.cs b/Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerBoundUserInterface.cs index 8db884328e4..1d4427c8154 100644 --- a/Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerBoundUserInterface.cs +++ b/Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerBoundUserInterface.cs @@ -1,5 +1,5 @@ +using Content.Shared.Nyanotrasen.Kitchen.UI; using Robust.Client.GameObjects; -using Content.Shared.Kitchen.UI; namespace Content.Client.Nyanotrasen.Kitchen.UI { diff --git a/Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerWindow.xaml.cs b/Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerWindow.xaml.cs index 7eb8eabd2ca..4f9e274eb06 100644 --- a/Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerWindow.xaml.cs +++ b/Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerWindow.xaml.cs @@ -1,9 +1,9 @@ +using Content.Shared.Nyanotrasen.Kitchen.UI; using Robust.Client.AutoGenerated; using Robust.Client.GameObjects; using Robust.Client.Graphics; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; -using Content.Shared.Kitchen.UI; namespace Content.Client.Nyanotrasen.Kitchen.UI { diff --git a/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFriedVisualizer.cs b/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFriedVisualizer.cs index 58580e3f5ac..97fea3d0ca9 100644 --- a/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFriedVisualizer.cs +++ b/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFriedVisualizer.cs @@ -5,6 +5,7 @@ using Content.Shared.Clothing; using Content.Shared.Hands; using Content.Shared.Kitchen.Components; +using Content.Shared.Nyanotrasen.Kitchen.Components; namespace Content.Client.Kitchen.Visualizers { diff --git a/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFryerVisualizer.cs b/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFryerVisualizer.cs index a8c310bcf41..5d208d09598 100644 --- a/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFryerVisualizer.cs +++ b/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFryerVisualizer.cs @@ -1,7 +1,9 @@ using Robust.Client.GameObjects; +using Content.Client.Chemistry.Visualizers; using Content.Client.Kitchen.Components; using Content.Shared.Chemistry.Components; using Content.Shared.Kitchen.Components; +using Content.Shared.Nyanotrasen.Kitchen.Components; namespace Content.Client.Kitchen.Visualizers { diff --git a/Content.Server/Nyanotrasen/Kitchen/Components/DeepFriedComponent.cs b/Content.Server/Nyanotrasen/Kitchen/Components/DeepFriedComponent.cs index 3bbb96e65d0..a0cf888307b 100644 --- a/Content.Server/Nyanotrasen/Kitchen/Components/DeepFriedComponent.cs +++ b/Content.Server/Nyanotrasen/Kitchen/Components/DeepFriedComponent.cs @@ -1,4 +1,5 @@ using Content.Shared.Kitchen.Components; +using Content.Shared.Nyanotrasen.Kitchen.Components; namespace Content.Server.Kitchen.Components { diff --git a/Content.Server/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs b/Content.Server/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs index 07cc96e8e78..72c3297feb6 100644 --- a/Content.Server/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs +++ b/Content.Server/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs @@ -1,23 +1,22 @@ +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Construction.Prototypes; +using Content.Shared.FixedPoint; +using Content.Shared.Nutrition; +using Content.Shared.Nyanotrasen.Kitchen; +using Content.Shared.Nyanotrasen.Kitchen.Components; +using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.Containers; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; -using Content.Server.Kitchen.EntitySystems; -using Content.Server.Nutrition; -using Content.Shared.Chemistry.Components; -using Content.Shared.Chemistry.Reagent; -using Content.Shared.Construction.Prototypes; -using Content.Shared.Kitchen.Components; -using Content.Shared.Nutrition; -using Content.Shared.FixedPoint; -using Content.Shared.Whitelist; -namespace Content.Server.Kitchen.Components +namespace Content.Server.Nyanotrasen.Kitchen.Components { [RegisterComponent] - [Access(typeof(DeepFryerSystem))] + [Access(typeof(SharedDeepfryerSystem))] // This line appears to be depracted: [ComponentReference(typeof(SharedDeepFryerComponent))] public sealed partial class DeepFryerComponent : SharedDeepFryerComponent { @@ -86,7 +85,7 @@ public sealed partial class DeepFryerComponent : SharedDeepFryerComponent /// /// What reagents are added to tasty deep-fried food? - /// JJ Comment: I removed Solution from this. Unsure if I need to replace it with something. + /// JJ Comment: I removed Solution from this. Unsure if I need to replace it with something. /// [ViewVariables(VVAccess.ReadWrite)] [DataField("goodReagents")] @@ -94,7 +93,7 @@ public sealed partial class DeepFryerComponent : SharedDeepFryerComponent /// /// What reagents are added to terrible deep-fried food? - /// JJ Comment: I removed Solution from this. Unsure if I need to replace it with something. + /// JJ Comment: I removed Solution from this. Unsure if I need to replace it with something. /// [ViewVariables(VVAccess.ReadWrite)] [DataField("badReagents")] @@ -102,7 +101,7 @@ public sealed partial class DeepFryerComponent : SharedDeepFryerComponent /// /// What reagents replace every 1 unit of oil spent on frying? - /// JJ Comment: I removed Solution from this. Unsure if I need to replace it with something. + /// JJ Comment: I removed Solution from this. Unsure if I need to replace it with something. /// [ViewVariables(VVAccess.ReadWrite)] [DataField("wasteReagents")] @@ -158,7 +157,7 @@ public sealed partial class DeepFryerComponent : SharedDeepFryerComponent /// [ViewVariables(VVAccess.ReadWrite)] [DataField("solutionSizeCoefficient")] - public FixedPoint2 SolutionSizeCoefficient { get; set; } = 0.5f; + public FixedPoint2 SolutionSizeCoefficient { get; set; } = 1f; /// /// What's the maximum amount of solution that should ever be imparted? diff --git a/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Results.cs b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Results.cs new file mode 100644 index 00000000000..f49ca99f991 --- /dev/null +++ b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Results.cs @@ -0,0 +1,181 @@ +using System.Text; +using Content.Server.Atmos.Components; +using Content.Server.Body.Components; +using Content.Server.Ghost.Roles.Components; +using Content.Server.Kitchen.Components; +using Content.Server.Nutrition.Components; +using Content.Server.Nyanotrasen.Kitchen.Components; +using Content.Server.Paper; +using Content.Shared.Atmos.Rotting; +using Content.Shared.Buckle.Components; +using Content.Shared.Chemistry.Components; +using Content.Shared.FixedPoint; +using Content.Shared.Mobs.Components; +using Content.Shared.NPC; +using Content.Shared.Nutrition.Components; +using Content.Shared.Nyanotrasen.Kitchen.Components; +using Robust.Shared.Random; + +namespace Content.Server.Nyanotrasen.Kitchen.EntitySystems; + +public sealed partial class DeepFryerSystem +{ + /// + /// Make an item look deep-fried. + /// + public void MakeCrispy(EntityUid item) + { + EnsureComp(item); + EnsureComp(item); + + _appearanceSystem.SetData(item, DeepFriedVisuals.Fried, true); + } + + /// + /// Turn a dead mob into food. + /// + /// + /// This is meant to be an irreversible process, similar to gibbing. + /// + public bool TryMakeMobIntoFood(EntityUid mob, MobStateComponent mobStateComponent, bool force = false) + { + // Don't do anything to mobs until they're dead. + if (force || _mobStateSystem.IsDead(mob, mobStateComponent)) + { + RemComp(mob); + RemComp(mob); + RemComp(mob); + RemComp(mob); + RemComp(mob); + RemComp(mob); + RemComp(mob); + RemComp(mob); + RemComp(mob); + + // Ensure it's Food here, so it passes the whitelist. + var mobFoodComponent = EnsureComp(mob); + _solutionContainerSystem.EnsureSolution(mob, mobFoodComponent.Solution, out var alreadyHadFood); + + if (!_solutionContainerSystem.TryGetSolution(mob, mobFoodComponent.Solution, out var mobFoodSolution)) + return false; + + // This line here is mainly for mice, because they have a food + // component that mirrors how much blood they have, which is + // used for the reagent grinder. + if (alreadyHadFood) + _solutionContainerSystem.RemoveAllSolution(mobFoodSolution.Value); + + if (TryComp(mob, out var bloodstreamComponent) && bloodstreamComponent.ChemicalSolution != null) + { + // Fry off any blood into protein. + var bloodSolution = bloodstreamComponent.BloodSolution; + var solPresent = bloodSolution!.Value.Comp.Solution.Volume; + _solutionContainerSystem.RemoveReagent(bloodSolution.Value, "Blood", FixedPoint2.MaxValue); + var bloodRemoved = solPresent - bloodSolution.Value.Comp.Solution.Volume; + + var proteinQuantity = bloodRemoved * BloodToProteinRatio; + mobFoodSolution.Value.Comp.Solution.MaxVolume += proteinQuantity; + _solutionContainerSystem.TryAddReagent(mobFoodSolution.Value, "Protein", proteinQuantity); + + // This is a heuristic. If you had blood, you might just taste meaty. + if (bloodRemoved > FixedPoint2.Zero) + EnsureComp(mob).Flavors.Add(MobFlavorMeat); + + // Bring in whatever chemicals they had in them too. + mobFoodSolution.Value.Comp.Solution.MaxVolume += + bloodstreamComponent.ChemicalSolution.Value.Comp.Solution.Volume; + _solutionContainerSystem.AddSolution(mobFoodSolution.Value, + bloodstreamComponent.ChemicalSolution.Value.Comp.Solution); + } + + return true; + } + + return false; + } + + /// + /// Make an item actually edible. + /// + private void MakeEdible(EntityUid uid, DeepFryerComponent component, EntityUid item, FixedPoint2 solutionQuantity) + { + if (!TryComp(item, out var deepFriedComponent)) + { + _sawmill.Error($"{ToPrettyString(item)} is missing the DeepFriedComponent before being made Edible."); + return; + } + + // Remove any components that wouldn't make sense anymore. + RemComp(item); + + if (TryComp(item, out var paperComponent)) + { + var stringBuilder = new StringBuilder(); + + for (var i = 0; i < paperComponent.Content.Length; ++i) + { + var uchar = paperComponent.Content.Substring(i, 1); + + if (uchar == "\n" || _random.Prob(0.4f)) + stringBuilder.Append(uchar); + else + stringBuilder.Append("x"); + } + + paperComponent.Content = stringBuilder.ToString(); + } + + var foodComponent = EnsureComp(item); + var extraSolution = new Solution(); + if (TryComp(item, out FlavorProfileComponent? flavorProfileComponent)) + { + HashSet goodFlavors = new(flavorProfileComponent.Flavors); + goodFlavors.IntersectWith(component.GoodFlavors); + + HashSet badFlavors = new(flavorProfileComponent.Flavors); + badFlavors.IntersectWith(component.BadFlavors); + + deepFriedComponent.PriceCoefficient = Math.Max(0.01f, + 1.0f + + goodFlavors.Count * component.GoodFlavorPriceBonus + - badFlavors.Count * component.BadFlavorPriceMalus); + + if (goodFlavors.Count > 0) + { + foreach (var reagent in component.GoodReagents) + { + extraSolution.AddReagent(reagent.Reagent.ToString(), reagent.Quantity * goodFlavors.Count); + + // Mask the taste of "medicine." + flavorProfileComponent.IgnoreReagents.Add(reagent.Reagent.ToString()); + } + } + + if (badFlavors.Count > 0) + { + foreach (var reagent in component.BadReagents) + { + extraSolution.AddReagent(reagent.Reagent.ToString(), reagent.Quantity * badFlavors.Count); + } + } + } + else + { + flavorProfileComponent = EnsureComp(item); + // TODO: Default flavor? + } + + // Make sure there's enough room for the fryer solution. + var foodSolution = _solutionContainerSystem.EnsureSolution(item, foodComponent.Solution); + if (!_solutionContainerSystem.TryGetSolution(item, foodSolution.Name, out var foodContainer)) + return; + + // The solution quantity is used to give the fried food an extra + // buffer too, to support injectables or condiments. + foodSolution.MaxVolume = 2 * solutionQuantity + foodSolution.Volume + extraSolution.Volume; + _solutionContainerSystem.AddSolution(foodContainer.Value, + component.Solution.SplitSolution(solutionQuantity)); + _solutionContainerSystem.AddSolution(foodContainer.Value, extraSolution); + _solutionContainerSystem.UpdateChemicals(foodContainer.Value); + } +} diff --git a/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Storage.cs b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Storage.cs new file mode 100644 index 00000000000..8858286dbf6 --- /dev/null +++ b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Storage.cs @@ -0,0 +1,93 @@ +using Content.Server.Kitchen.Components; +using Content.Server.Nyanotrasen.Kitchen.Components; +using Content.Shared.Chemistry.Components; +using Content.Shared.Database; +using Content.Shared.Hands.Components; +using Content.Shared.Interaction; +using Content.Shared.Item; +using Content.Shared.Nyanotrasen.Kitchen.UI; +using Content.Shared.Storage; +using Content.Shared.Tools.Components; + +namespace Content.Server.Nyanotrasen.Kitchen.EntitySystems; + +public sealed partial class DeepFryerSystem +{ + public bool CanInsertItem(EntityUid uid, DeepFryerComponent component, EntityUid item) + { + // Keep this consistent with the checks in TryInsertItem. + return HasComp(item) && + !HasComp(item) && + component.Storage.ContainedEntities.Count < component.StorageMaxEntities; + } + + private bool TryInsertItem(EntityUid uid, DeepFryerComponent component, EntityUid user, EntityUid item) + { + if (!HasComp(item)) + { + _popupSystem.PopupEntity( + Loc.GetString("deep-fryer-interact-using-not-item"), + uid, + user); + return false; + } + + if (HasComp(item)) + { + _popupSystem.PopupEntity( + Loc.GetString("deep-fryer-storage-no-fit", + ("item", item)), + uid, + user); + return false; + } + + if (component.Storage.ContainedEntities.Count >= component.StorageMaxEntities) + { + _popupSystem.PopupEntity( + Loc.GetString("deep-fryer-storage-full"), + uid, + user); + return false; + } + + if (!_handsSystem.TryDropIntoContainer(user, item, component.Storage)) + return false; + + AfterInsert(uid, component, item); + + _adminLogManager.Add(LogType.Action, LogImpact.Low, + $"{ToPrettyString(user)} put {ToPrettyString(item)} inside {ToPrettyString(uid)}."); + + return true; + } + + private void OnInteractUsing(EntityUid uid, DeepFryerComponent component, InteractUsingEvent args) + { + if (args.Handled) + return; + + // By default, allow entities with SolutionTransfer or Tool + // components to perform their usual actions. Inserting them (if + // the chef really wants to) will be supported through the UI. + if (HasComp(args.Used) || + HasComp(args.Used)) + return; + + if (TryInsertItem(uid, component, args.User, args.Used)) + args.Handled = true; + } + + private void OnInsertItem(EntityUid uid, DeepFryerComponent component, DeepFryerInsertItemMessage args) + { + var user = args.Session.AttachedEntity; + + if (user == null || + !TryComp(user, out var handsComponent) || + handsComponent.ActiveHandEntity == null) + return; + + if (handsComponent.ActiveHandEntity != null) + TryInsertItem(uid, component, user.Value, handsComponent.ActiveHandEntity.Value); + } +} diff --git a/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Update.cs b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Update.cs new file mode 100644 index 00000000000..0978cffeb4b --- /dev/null +++ b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.Update.cs @@ -0,0 +1,128 @@ +using System.Linq; +using Content.Server.Kitchen.Components; +using Content.Server.Nyanotrasen.Kitchen.Components; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.FixedPoint; +using Content.Shared.Popups; +using Robust.Shared.Player; + +namespace Content.Server.Nyanotrasen.Kitchen.EntitySystems; + +public sealed partial class DeepFryerSystem +{ + public override void Update(float frameTime) + { + base.Update(frameTime); + + foreach (var component in EntityManager.EntityQuery()) + { + var uid = component.Owner; + + if (_gameTimingSystem.CurTime < component.NextFryTime || + !_powerReceiverSystem.IsPowered(uid)) + { + continue; + } + + UpdateNextFryTime(uid, component); + + if (!_solutionContainerSystem.TryGetSolution(uid, component.Solution.Name, out var solution)) + continue; + + // Heat the vat solution and contained entities. + _solutionContainerSystem.SetTemperature(solution.Value, component.PoweredTemperature); + + foreach (var item in component.Storage.ContainedEntities) + CookItem(uid, component, item); + + // Do something bad if there's enough heat but not enough oil. + var oilVolume = GetOilVolume(uid, component); + + if (oilVolume < component.SafeOilVolume) + { + foreach (var item in component.Storage.ContainedEntities.ToArray()) + BurnItem(uid, component, item); + + if (oilVolume > FixedPoint2.Zero) + { + //JJ Comment - this code block makes the Linter fail, and doesn't seem to be necessary with the changes I made. + foreach (var reagent in component.Solution.Contents.ToArray()) + { + _prototypeManager.TryIndex(reagent.Reagent.ToString(), out var proto); + + foreach (var effect in component.UnsafeOilVolumeEffects) + { + effect.Effect(new ReagentEffectArgs(uid, + null, + component.Solution, + proto!, + reagent.Quantity, + EntityManager, + null, + 1f)); + } + + } + + component.Solution.RemoveAllSolution(); + + _popupSystem.PopupEntity( + Loc.GetString("deep-fryer-oil-volume-low", + ("deepFryer", uid)), + uid, + PopupType.SmallCaution); + + continue; + } + } + + // We only alert the chef that there's a problem with oil purity + // if there's anything to cook beyond this point. + if (!component.Storage.ContainedEntities.Any()) + { + continue; + } + + if (GetOilPurity(uid, component) < component.FryingOilThreshold) + { + _popupSystem.PopupEntity( + Loc.GetString("deep-fryer-oil-purity-low", + ("deepFryer", uid)), + uid, + Filter.Pvs(uid, PvsWarningRange), + true); + continue; + } + + foreach (var item in component.Storage.ContainedEntities.ToArray()) + DeepFry(uid, component, item); + + // After the round of frying, replace the spent oil with a + // waste product. + if (component.WasteToAdd > FixedPoint2.Zero) + { + foreach (var reagent in component.WasteReagents) + component.Solution.AddReagent(reagent.Reagent.ToString(), reagent.Quantity * component.WasteToAdd); + + component.WasteToAdd = FixedPoint2.Zero; + + _solutionContainerSystem.UpdateChemicals(solution.Value, true); + } + + UpdateUserInterface(uid, component); + } + } + + private void UpdateAmbientSound(EntityUid uid, DeepFryerComponent component) + { + _ambientSoundSystem.SetAmbience(uid, HasBubblingOil(uid, component)); + } + + private void UpdateNextFryTime(EntityUid uid, DeepFryerComponent component) + { + component.NextFryTime = _gameTimingSystem.CurTime + component.FryInterval; + } + +} diff --git a/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs index 78ab4dc1e13..cb95251f5ee 100644 --- a/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs +++ b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs @@ -1,48 +1,33 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Text; -using Robust.Server.GameObjects; -using Robust.Shared.Audio; -using Robust.Shared.Containers; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Robust.Shared.Timing; using Content.Server.Administration.Logs; -using Content.Server.Atmos.Components; -using Content.Server.Atmos.Rotting; using Content.Server.Audio; -using Content.Server.Body.Components; using Content.Server.Cargo.Systems; using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Shared.Chemistry.Components.SolutionManager; using Content.Server.Chemistry.EntitySystems; using Content.Server.Construction; -using Content.Server.Construction.Components; using Content.Server.DoAfter; using Content.Server.Fluids.EntitySystems; -using Content.Server.Ghost.Roles.Components; using Content.Server.Kitchen.Components; -using Content.Server.NPC.Components; using Content.Server.Nutrition; using Content.Server.Nutrition.Components; -using Content.Server.Nutrition.EntitySystems; -using Content.Server.Paper; +using Content.Server.Nyanotrasen.Kitchen.Components; using Content.Server.Popups; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.Temperature.Components; using Content.Server.Temperature.Systems; using Content.Server.UserInterface; -using Content.Shared.Atmos.Rotting; -using Content.Shared.Buckle.Components; using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.Reagent; +using Content.Shared.Construction; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; using Content.Shared.Database; -using Content.Shared.DoAfter; using Content.Shared.Destructible; +using Content.Shared.DoAfter; using Content.Shared.Examine; using Content.Shared.FixedPoint; using Content.Shared.Hands.Components; @@ -50,1024 +35,727 @@ using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Item; -using Content.Shared.Kitchen; -using Content.Shared.Kitchen.Components; -using Content.Shared.Kitchen.UI; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Movement.Events; -using Content.Shared.Nutrition.Components; +using Content.Shared.Nyanotrasen.Kitchen; +using Content.Shared.Nyanotrasen.Kitchen.Components; +using Content.Shared.Nyanotrasen.Kitchen.UI; using Content.Shared.Popups; -using Content.Shared.Storage; using Content.Shared.Throwing; -using Content.Shared.Tools.Components; using FastAccessors; -using Content.Shared.NPC; -using Content.Shared.Chemistry.EntitySystems; -using Content.Shared.Construction; +using Robust.Server.GameObjects; +using Robust.Shared.Audio; using Robust.Shared.Audio.Systems; +using Robust.Shared.Containers; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Timing; -namespace Content.Server.Kitchen.EntitySystems +namespace Content.Server.Nyanotrasen.Kitchen.EntitySystems; + +public sealed partial class DeepFryerSystem : SharedDeepfryerSystem { - public sealed class DeepFryerSystem : EntitySystem + [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly IAdminLogManager _adminLogManager = default!; + [Dependency] private readonly IGameTiming _gameTimingSystem = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly PowerReceiverSystem _powerReceiverSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + [Dependency] private readonly SharedContainerSystem _containerSystem = default!; + [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] private readonly MobStateSystem _mobStateSystem = default!; + [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + [Dependency] private readonly SolutionTransferSystem _solutionTransferSystem = default!; + [Dependency] private readonly PuddleSystem _puddleSystem = default!; + [Dependency] private readonly TemperatureSystem _temperature = default!; + [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly AmbientSoundSystem _ambientSoundSystem = default!; + [Dependency] private readonly MetaDataSystem _metaDataSystem = default!; + + private static readonly string CookingDamageType = "Heat"; + private static readonly float CookingDamageAmount = 10.0f; + private static readonly float PvsWarningRange = 0.5f; + private static readonly float ThrowMissChance = 0.25f; + private static readonly int MaximumCrispiness = 2; + private static readonly float BloodToProteinRatio = 0.1f; + private static readonly string MobFlavorMeat = "meaty"; + + private static readonly AudioParams + AudioParamsInsertRemove = new(0.5f, 1f, "Master", 5f, 1.5f, 1f, false, 0f, 0.2f); + + private ISawmill _sawmill = default!; + + public override void Initialize() { - [Dependency] private readonly DamageableSystem _damageableSystem = default!; - [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly IAdminLogManager _adminLogManager = default!; - [Dependency] private readonly IGameTiming _gameTimingSystem = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly PowerReceiverSystem _powerReceiverSystem = default!; - [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; - [Dependency] private readonly SharedContainerSystem _containerSystem = default!; - [Dependency] private readonly SharedHandsSystem _handsSystem = default!; - [Dependency] private readonly MobStateSystem _mobStateSystem = default!; - [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; - [Dependency] private readonly SolutionTransferSystem _solutionTransferSystem = default!; - [Dependency] private readonly PuddleSystem _puddleSystem = default!; - [Dependency] private readonly TemperatureSystem _temperature = default!; - [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; - [Dependency] private readonly AmbientSoundSystem _ambientSoundSystem = default!; - [Dependency] private readonly MetaDataSystem _metaDataSystem = default!; - - private static readonly string CookingDamageType = "Heat"; - private static readonly float CookingDamageAmount = 10.0f; - private static readonly float PvsWarningRange = 0.5f; - private static readonly float ThrowMissChance = 0.25f; - private static readonly int MaximumCrispiness = 2; - private static readonly float BloodToProteinRatio = 0.1f; - private static readonly string MobFlavorMeat = "meaty"; - private static readonly AudioParams AudioParamsInsertRemove = new(0.5f, 1f, "Master", 5f, 1.5f, 1f, false, 0f, 0.2f); - - private ISawmill _sawmill = default!; - - public override void Initialize() - { - base.Initialize(); - - _sawmill = Logger.GetSawmill("deepfryer"); - - SubscribeLocalEvent(OnInitDeepFryer); - SubscribeLocalEvent(OnPowerChange); - SubscribeLocalEvent(OnRefreshParts); - SubscribeLocalEvent(OnDeconstruct); - SubscribeLocalEvent(OnDestruction); - SubscribeLocalEvent(OnThrowHitBy); - SubscribeLocalEvent(OnSolutionChange); - SubscribeLocalEvent(OnRelayMovement); - SubscribeLocalEvent(OnInteractUsing); - - SubscribeLocalEvent(OnBeforeActivatableUIOpen); - SubscribeLocalEvent(OnRemoveItem); - SubscribeLocalEvent(OnInsertItem); - SubscribeLocalEvent(OnScoopVat); - SubscribeLocalEvent(OnClearSlagStart); - SubscribeLocalEvent(OnRemoveAllItems); - SubscribeLocalEvent(OnClearSlag); - - SubscribeLocalEvent(OnInitDeepFried); - SubscribeLocalEvent(OnExamineFried); - SubscribeLocalEvent(OnPriceCalculation); - SubscribeLocalEvent(OnSliceDeepFried); - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - - foreach (var component in EntityManager.EntityQuery()) - { - var uid = component.Owner; - - if (_gameTimingSystem.CurTime < component.NextFryTime || - !_powerReceiverSystem.IsPowered(uid)) - { - continue; - } - - UpdateNextFryTime(uid, component); - - - // Heat the vat solution and contained entities. - _solutionContainerSystem.SetTemperature(component.Solution, component.PoweredTemperature); - - foreach (var item in component.Storage.ContainedEntities) - CookItem(uid, component, item); - - // Do something bad if there's enough heat but not enough oil. - var oilVolume = GetOilVolume(uid, component); - - if (oilVolume < component.SafeOilVolume) - { - foreach (var item in component.Storage.ContainedEntities.ToArray()) - BurnItem(uid, component, item); - - if (oilVolume > FixedPoint2.Zero) - { - //JJ Comment - this code block makes the Linter fail, and doesn't seem to be necessary with the changes I made. - foreach (var reagent in component.Solution.Contents.ToArray()) - { - _prototypeManager.TryIndex(reagent.Reagent.ToString(), out var proto); - - foreach (var effect in component.UnsafeOilVolumeEffects) - { - effect.Effect(new ReagentEffectArgs(uid, - null, - component.Solution, - proto!, - reagent.Quantity, - EntityManager, - null, - 1f)); - } - - } - - component.Solution.RemoveAllSolution(); - - _popupSystem.PopupEntity( - Loc.GetString("deep-fryer-oil-volume-low", - ("deepFryer", uid)), - uid, - PopupType.SmallCaution); - - continue; - } - } - - // We only alert the chef that there's a problem with oil purity - // if there's anything to cook beyond this point. - if (!component.Storage.ContainedEntities.Any()) - { - continue; - } - - if (GetOilPurity(uid, component) < component.FryingOilThreshold) - { - _popupSystem.PopupEntity( - Loc.GetString("deep-fryer-oil-purity-low", - ("deepFryer", uid)), - uid, - Filter.Pvs(uid, PvsWarningRange), - true); - continue; - } - - foreach (var item in component.Storage.ContainedEntities.ToArray()) - DeepFry(uid, component, item); - - // After the round of frying, replace the spent oil with a - // waste product. - if (component.WasteToAdd > FixedPoint2.Zero) - { - foreach (var reagent in component.WasteReagents) - component.Solution.AddReagent(reagent.Reagent.ToString(), reagent.Quantity * component.WasteToAdd); + base.Initialize(); + + _sawmill = Logger.GetSawmill("deepfryer"); + + SubscribeLocalEvent(OnInitDeepFryer); + SubscribeLocalEvent(OnPowerChange); + SubscribeLocalEvent(OnRefreshParts); + SubscribeLocalEvent(OnDeconstruct); + SubscribeLocalEvent(OnDestruction); + SubscribeLocalEvent(OnThrowHitBy); + SubscribeLocalEvent(OnSolutionChange); + SubscribeLocalEvent(OnRelayMovement); + SubscribeLocalEvent(OnInteractUsing); + + SubscribeLocalEvent(OnBeforeActivatableUIOpen); + SubscribeLocalEvent(OnRemoveItem); + SubscribeLocalEvent(OnInsertItem); + SubscribeLocalEvent(OnScoopVat); + SubscribeLocalEvent(OnClearSlagStart); + SubscribeLocalEvent(OnRemoveAllItems); + SubscribeLocalEvent(OnClearSlag); + + SubscribeLocalEvent(OnInitDeepFried); + SubscribeLocalEvent(OnExamineFried); + SubscribeLocalEvent(OnPriceCalculation); + SubscribeLocalEvent(OnSliceDeepFried); + } - component.WasteToAdd = FixedPoint2.Zero; + private void UpdateUserInterface(EntityUid uid, DeepFryerComponent component) + { + var state = new DeepFryerBoundUserInterfaceState( + GetOilLevel(uid, component), + GetOilPurity(uid, component), + component.FryingOilThreshold, + EntityManager.GetNetEntityArray(component.Storage.ContainedEntities.ToArray())); + + if (!_uiSystem.TrySetUiState(uid, DeepFryerUiKey.Key, state)) + _sawmill.Warning($"{ToPrettyString(uid)} was unable to set UI state."); + } - _solutionContainerSystem.UpdateChemicals(uid, component.Solution, true); - } + /// + /// Does the deep fryer have hot oil? + /// + /// + /// This is mainly for audio. + /// + private bool HasBubblingOil(EntityUid uid, DeepFryerComponent component) + { + return _powerReceiverSystem.IsPowered(uid) && GetOilVolume(uid, component) > FixedPoint2.Zero; + } - UpdateUserInterface(uid, component); - } - } + /// + /// Returns how much total oil is in the vat. + /// + public FixedPoint2 GetOilVolume(EntityUid uid, DeepFryerComponent component) + { + var oilVolume = FixedPoint2.Zero; - private void UpdateUserInterface(EntityUid uid, DeepFryerComponent component) + foreach (var reagent in component.Solution) { - var state = new DeepFryerBoundUserInterfaceState( - GetOilLevel(uid, component), - GetOilPurity(uid, component), - component.FryingOilThreshold, - EntityManager.GetNetEntityArray(component.Storage.ContainedEntities.ToArray())); - - if (!_uiSystem.TrySetUiState(uid, DeepFryerUiKey.Key, state)) - _sawmill.Warning($"{ToPrettyString(uid)} was unable to set UI state."); + if (component.FryingOils.Contains(reagent.Reagent.ToString())) + oilVolume += reagent.Quantity; } - /// - /// Does the deep fryer have hot oil? - /// - /// - /// This is mainly for audio. - /// - private bool HasBubblingOil(EntityUid uid, DeepFryerComponent component) - { - return _powerReceiverSystem.IsPowered(uid) && GetOilVolume(uid, component) > FixedPoint2.Zero; - } + return oilVolume; + } - private void UpdateAmbientSound(EntityUid uid, DeepFryerComponent component) - { - _ambientSoundSystem.SetAmbience(uid, HasBubblingOil(uid, component)); - } + /// + /// Returns how much total waste is in the vat. + /// + public FixedPoint2 GetWasteVolume(EntityUid uid, DeepFryerComponent component) + { + var wasteVolume = FixedPoint2.Zero; - private void UpdateNextFryTime(EntityUid uid, DeepFryerComponent component) + foreach (var reagent in component.WasteReagents) { - component.NextFryTime = _gameTimingSystem.CurTime + component.FryInterval; + wasteVolume += component.Solution.GetReagentQuantity(reagent.Reagent); } - /// - /// Make an item look deep-fried. - /// - public void MakeCrispy(EntityUid item) - { - EnsureComp(item); - EnsureComp(item); + return wasteVolume; + } - _appearanceSystem.SetData(item, DeepFriedVisuals.Fried, true); - } + /// + /// Returns a percentage of how much of the total solution is usable oil. + /// + public FixedPoint2 GetOilPurity(EntityUid uid, DeepFryerComponent component) + { + return GetOilVolume(uid, component) / component.Solution.Volume; + } + + /// + /// Returns a percentage of how much of the total volume is usable oil. + /// + public FixedPoint2 GetOilLevel(EntityUid uid, DeepFryerComponent component) + { + return GetOilVolume(uid, component) / component.Solution.MaxVolume; + } - /// - /// Turn a dead mob into food. - /// - /// - /// This is meant to be an irreversible process, similar to gibbing. - /// - public bool TryMakeMobIntoFood(EntityUid mob, MobStateComponent mobStateComponent, bool force = false) + /// + /// This takes care of anything that would happen to an item with or + /// without enough oil. + /// + private void CookItem(EntityUid uid, DeepFryerComponent component, EntityUid item) + { + if (TryComp(item, out var tempComp)) { - // Don't do anything to mobs until they're dead. - if (force || _mobStateSystem.IsDead(mob, mobStateComponent)) - { - RemComp(mob); - RemComp(mob); - RemComp(mob); - RemComp(mob); - RemComp(mob); - RemComp(mob); - RemComp(mob); - RemComp(mob); - RemComp(mob); - - // Ensure it's Food here, so it passes the whitelist. - var mobFoodComponent = EnsureComp(mob); - var mobFoodSolution = _solutionContainerSystem.EnsureSolution(mob, mobFoodComponent.Solution, out bool alreadyHadFood); - - // This line here is mainly for mice, because they have a food - // component that mirrors how much blood they have, which is - // used for the reagent grinder. - if (alreadyHadFood) - _solutionContainerSystem.RemoveAllSolution(mob, mobFoodSolution); - - if (TryComp(mob, out var bloodstreamComponent)) - { - // Fry off any blood into protein. - var bloodSolution = bloodstreamComponent.BloodSolution; - var removedBloodQuantity = bloodSolution.RemoveReagent("Blood", FixedPoint2.MaxValue); - var proteinQuantity = removedBloodQuantity * BloodToProteinRatio; - mobFoodSolution.MaxVolume += proteinQuantity; - mobFoodSolution.AddReagent("Protein", proteinQuantity); - - // This is a heuristic. If you had blood, you might just taste meaty. - if (removedBloodQuantity > FixedPoint2.Zero) - EnsureComp(mob).Flavors.Add(MobFlavorMeat); - - // Bring in whatever chemicals they had in them too. - mobFoodSolution.MaxVolume += bloodstreamComponent.ChemicalSolution.Volume; - mobFoodSolution.AddSolution(bloodstreamComponent.ChemicalSolution, _prototypeManager); - } - - return true; - } + // Push the temperature towards what it should be but no higher. + var delta = (component.PoweredTemperature - tempComp.CurrentTemperature) * tempComp.HeatCapacity; - return false; + if (delta > 0f) + _temperature.ChangeHeat(item, delta, false, tempComp); } - /// - /// Make an item actually edible. - /// - private void MakeEdible(EntityUid uid, DeepFryerComponent component, EntityUid item, FixedPoint2 solutionQuantity) + if (TryComp(item, out var solutions) && solutions.Solutions != null) { - if (!TryComp(item, out var deepFriedComponent)) - { - _sawmill.Error($"{ToPrettyString(item)} is missing the DeepFriedComponent before being made Edible."); - return; - } - - // Remove any components that wouldn't make sense anymore. - RemComp(item); - - if (TryComp(item, out var paperComponent)) - { - var stringBuilder = new StringBuilder(); - - for (var i = 0; i < paperComponent.Content.Length; ++i) - { - var uchar = paperComponent.Content.Substring(i, 1); - - if (uchar == "\n" || _random.Prob(0.4f)) - stringBuilder.Append(uchar); - else - stringBuilder.Append("x"); - } - - paperComponent.Content = stringBuilder.ToString(); - } - - var foodComponent = EnsureComp(item); - var extraSolution = new Solution(); - if (TryComp(item, out FlavorProfileComponent? flavorProfileComponent)) - { - HashSet goodFlavors = new(flavorProfileComponent.Flavors); - goodFlavors.IntersectWith(component.GoodFlavors); - - HashSet badFlavors = new(flavorProfileComponent.Flavors); - badFlavors.IntersectWith(component.BadFlavors); - - deepFriedComponent.PriceCoefficient = Math.Max(0.01f, - 1.0f - + goodFlavors.Count * component.GoodFlavorPriceBonus - - badFlavors.Count * component.BadFlavorPriceMalus); - - if (goodFlavors.Count > 0) - foreach (var reagent in component.GoodReagents) - { - extraSolution.AddReagent(reagent.Reagent.ToString(), reagent.Quantity * goodFlavors.Count); - - // Mask the taste of "medicine." - flavorProfileComponent.IgnoreReagents.Add(reagent.Reagent.ToString()); - } - - if (badFlavors.Count > 0) - foreach (var reagent in component.BadReagents) - extraSolution.AddReagent(reagent.Reagent.ToString(), reagent.Quantity * badFlavors.Count); - } - else + foreach (var (_, solution) in solutions.Solutions) { - flavorProfileComponent = EnsureComp(item); - // TODO: Default flavor? + if(_solutionContainerSystem.TryGetSolution(item, solution.Name, out var solutionRef)) + _solutionContainerSystem.SetTemperature(solutionRef!.Value, component.PoweredTemperature); } - - // Make sure there's enough room for the fryer solution. - var foodContainer = _solutionContainerSystem.EnsureSolution(item, foodComponent.Solution); - - // The solution quantity is used to give the fried food an extra - // buffer too, to support injectables or condiments. - foodContainer.MaxVolume = 2 * solutionQuantity + foodContainer.Volume + extraSolution.Volume; - foodContainer.AddSolution(component.Solution.SplitSolution(solutionQuantity), _prototypeManager); - foodContainer.AddSolution(extraSolution, _prototypeManager); - _solutionContainerSystem.UpdateChemicals(item, foodContainer, true); } - /// - /// Returns how much total oil is in the vat. - /// - public FixedPoint2 GetOilVolume(EntityUid uid, DeepFryerComponent component) + // Damage non-food items and mobs. + if ((!HasComp(item) || HasComp(item)) && + TryComp(item, out var damageableComponent)) { - var oilVolume = FixedPoint2.Zero; - - foreach (var reagent in component.Solution) - if (component.FryingOils.Contains(reagent.Reagent.ToString())) - oilVolume += reagent.Quantity; + var damage = new DamageSpecifier(_prototypeManager.Index(CookingDamageType), + CookingDamageAmount); - return oilVolume; - } - - /// - /// Returns how much total waste is in the vat. - /// - public FixedPoint2 GetWasteVolume(EntityUid uid, DeepFryerComponent component) - { - var wasteVolume = FixedPoint2.Zero; - - foreach (var reagent in component.WasteReagents) + var result = _damageableSystem.TryChangeDamage(item, damage, origin: uid); + if (result?.GetTotal() > FixedPoint2.Zero) { - wasteVolume += component.Solution.GetReagentQuantity(reagent.Reagent); + // TODO: Smoke, waste, sound, or some indication. } - - return wasteVolume; } + } - /// - /// Returns a percentage of how much of the total solution is usable oil. - /// - public FixedPoint2 GetOilPurity(EntityUid uid, DeepFryerComponent component) + /// + /// Destroy a food item and replace it with a charred mess. + /// + private void BurnItem(EntityUid uid, DeepFryerComponent component, EntityUid item) + { + if (HasComp(item) && + !HasComp(item) && + MetaData(item).EntityPrototype?.ID != component.CharredPrototype) { - return GetOilVolume(uid, component) / component.Solution.Volume; + var charred = Spawn(component.CharredPrototype, Transform(uid).Coordinates); + component.Storage.Insert(charred); + Del(item); } + } - /// - /// Returns a percentage of how much of the total volume is usable oil. - /// - public FixedPoint2 GetOilLevel(EntityUid uid, DeepFryerComponent component) - { - return GetOilVolume(uid, component) / component.Solution.MaxVolume; + private void UpdateDeepFriedName(EntityUid uid, DeepFriedComponent component) + { + if (component.OriginalName == null) + return; + + switch (component.Crispiness) + { + case 0: + // Already handled at OnInitDeepFried. + break; + case 1: + _metaDataSystem.SetEntityName(uid, Loc.GetString("deep-fried-crispy-item", + ("entity", component.OriginalName))); + break; + default: + _metaDataSystem.SetEntityName(uid, Loc.GetString("deep-fried-burned-item", + ("entity", component.OriginalName))); + break; } + } - /// - /// This takes care of anything that would happen to an item with or - /// without enough oil. - /// - private void CookItem(EntityUid uid, DeepFryerComponent component, EntityUid item) + /// + /// Try to deep fry a single item, which can + /// - be cancelled by other systems, or + /// - fail due to the blacklist, or + /// - give it a crispy shader, and possibly also + /// - turn it into food. + /// + private void DeepFry(EntityUid uid, DeepFryerComponent component, EntityUid item) + { + if (MetaData(item).EntityPrototype?.ID == component.CharredPrototype) + return; + + // This item has already been deep-fried, and now it's progressing + // into another stage. + if (TryComp(item, out var deepFriedComponent)) { - if (TryComp(item, out var tempComp)) - { - // Push the temperature towards what it should be but no higher. - var delta = (component.PoweredTemperature - tempComp.CurrentTemperature) * tempComp.HeatCapacity; + // TODO: Smoke, waste, sound, or some indication. - if (delta > 0f) - _temperature.ChangeHeat(item, delta, false, tempComp); - } + deepFriedComponent.Crispiness += 1; - if (TryComp(item, out var solutions)) + if (deepFriedComponent.Crispiness > MaximumCrispiness) { - foreach (var (_, solution) in solutions.Solutions) - { - _solutionContainerSystem.SetTemperature(item, solution, component.PoweredTemperature); - } + BurnItem(uid, component, item); + return; } - // Damage non-food items and mobs. - if ((!HasComp(item) || HasComp(item)) && - TryComp(item, out var damageableComponent)) - { - var damage = new DamageSpecifier(_prototypeManager.Index(CookingDamageType), CookingDamageAmount); - - var result = _damageableSystem.TryChangeDamage(item, damage, origin: uid); - if (result?.Total > FixedPoint2.Zero) - { - // TODO: Smoke, waste, sound, or some indication. - } - } + UpdateDeepFriedName(item, deepFriedComponent); + return; } - /// - /// Destroy a food item and replace it with a charred mess. - /// - private void BurnItem(EntityUid uid, DeepFryerComponent component, EntityUid item) - { - if (HasComp(item) && - !HasComp(item) && - MetaData(item).EntityPrototype?.ID != component.CharredPrototype) - { - var charred = Spawn(component.CharredPrototype, Transform(uid).Coordinates); - component.Storage.Insert(charred); - Del(item); - } - } + // Allow entity systems to conditionally forbid an attempt at deep-frying. + var attemptEvent = new DeepFryAttemptEvent(uid); + RaiseLocalEvent(item, attemptEvent); - private void UpdateDeepFriedName(EntityUid uid, DeepFriedComponent component) - { - if (component.OriginalName == null) - return; + if (attemptEvent.Cancelled) + return; - switch (component.Crispiness) - { - case 0: - // Already handled at OnInitDeepFried. - break; - case 1: - _metaDataSystem.SetEntityName(uid, Loc.GetString("deep-fried-crispy-item", - ("entity", component.OriginalName))); - break; - default: - _metaDataSystem.SetEntityName(uid, Loc.GetString("deep-fried-burned-item", - ("entity", component.OriginalName))); - break; - } - } - - /// - /// Try to deep fry a single item, which can - /// - be cancelled by other systems, or - /// - fail due to the blacklist, or - /// - give it a crispy shader, and possibly also - /// - turn it into food. - /// - private void DeepFry(EntityUid uid, DeepFryerComponent component, EntityUid item) + // The attempt event is allowed to go first before the blacklist check, + // just in case the attempt is relevant to any system in the future. + // + // The blacklist overrides all. + if (component.Blacklist != null && component.Blacklist.IsValid(item, EntityManager)) { - if (MetaData(item).EntityPrototype?.ID == component.CharredPrototype) - return; - - // This item has already been deep-fried, and now it's progressing - // into another stage. - if (TryComp(item, out var deepFriedComponent)) - { - // TODO: Smoke, waste, sound, or some indication. - - deepFriedComponent.Crispiness += 1; + _popupSystem.PopupEntity( + Loc.GetString("deep-fryer-blacklist-item-failed", + ("item", item), ("deepFryer", uid)), + uid, + Filter.Pvs(uid, PvsWarningRange), + true); + return; + } - if (deepFriedComponent.Crispiness > MaximumCrispiness) - { - BurnItem(uid, component, item); - return; - } + var beingEvent = new BeingDeepFriedEvent(uid, item); + RaiseLocalEvent(item, beingEvent); - UpdateDeepFriedName(item, deepFriedComponent); + // It's important to check for the MobStateComponent so we know + // it's actually a mob, because functions like + // MobStateSystem.IsAlive will return false if the entity lacks the + // component. + if (TryComp(item, out var mobStateComponent)) + { + if (!TryMakeMobIntoFood(item, mobStateComponent)) return; - } + } - // Allow entity systems to conditionally forbid an attempt at deep-frying. - var attemptEvent = new DeepFryAttemptEvent(uid); - RaiseLocalEvent(item, attemptEvent); + MakeCrispy(item); - if (attemptEvent.Cancelled) - return; + var itemComponent = Comp(item); - // The attempt event is allowed to go first before the blacklist check, - // just in case the attempt is relevant to any system in the future. - // - // The blacklist overrides all. - if (component.Blacklist != null && component.Blacklist.IsValid(item, EntityManager)) + // Determine how much solution to spend on this item. + var solutionQuantity = FixedPoint2.Min( + component.Solution.Volume, + itemComponent.Size.Id switch { - _popupSystem.PopupEntity( - Loc.GetString("deep-fryer-blacklist-item-failed", - ("item", item), ("deepFryer", uid)), - uid, - Filter.Pvs(uid, PvsWarningRange), - true); - return; - } - - var beingEvent = new BeingDeepFriedEvent(uid, item); - RaiseLocalEvent(item, beingEvent); - - // It's important to check for the MobStateComponent so we know - // it's actually a mob, because functions like - // MobStateSystem.IsAlive will return false if the entity lacks the - // component. - if (TryComp(item, out var mobStateComponent)) - if (!TryMakeMobIntoFood(item, mobStateComponent)) - return; - - MakeCrispy(item); - - var itemComponent = Comp(item); + "Tiny" => 1, + "Small" => 5, + "Medium" => 10, + "Large" => 15, + "Huge" => 30, + "Ginormous" => 50, + _ => 10 + } * component.SolutionSizeCoefficient); + + if (component.Whitelist != null && component.Whitelist.IsValid(item, EntityManager) || + beingEvent.TurnIntoFood) + MakeEdible(uid, component, item, solutionQuantity); + else + component.Solution.RemoveSolution(solutionQuantity); + + component.WasteToAdd += solutionQuantity; + } - // Determine how much solution to spend on this item. - var solutionQuantity = FixedPoint2.Min( - component.Solution.Volume, - itemComponent.Size * component.SolutionSizeCoefficient); + private void OnInitDeepFryer(EntityUid uid, DeepFryerComponent component, ComponentInit args) + { + component.Storage = + _containerSystem.EnsureContainer(uid, component.StorageName, out var containerExisted); - if (component.Whitelist != null && component.Whitelist.IsValid(item, EntityManager) || - beingEvent.TurnIntoFood) - { - MakeEdible(uid, component, item, solutionQuantity); - } - else - { - component.Solution.RemoveSolution(solutionQuantity); - } + if (!containerExisted) + _sawmill.Warning( + $"{ToPrettyString(uid)} did not have a {component.StorageName} container. It has been created."); - component.WasteToAdd += solutionQuantity; - } + component.Solution = + _solutionContainerSystem.EnsureSolution(uid, component.SolutionName, out var solutionExisted); - private void OnInitDeepFryer(EntityUid uid, DeepFryerComponent component, ComponentInit args) + if (!solutionExisted) + _sawmill.Warning( + $"{ToPrettyString(uid)} did not have a {component.SolutionName} solution container. It has been created."); + foreach (var reagent in component.Solution.Contents.ToArray()) { - component.Storage = _containerSystem.EnsureContainer(uid, component.StorageName, out bool containerExisted); - - if (!containerExisted) - _sawmill.Warning($"{ToPrettyString(uid)} did not have a {component.StorageName} container. It has been created."); - - component.Solution = _solutionContainerSystem.EnsureSolution(uid, component.SolutionName, out bool solutionExisted); - - if (!solutionExisted) - _sawmill.Warning($"{ToPrettyString(uid)} did not have a {component.SolutionName} solution container. It has been created."); - foreach (var reagent in component.Solution.Contents.ToArray()) + //JJ Comment - not sure this works. Need to check if Reagent.ToString is correct. + _prototypeManager.TryIndex(reagent.Reagent.ToString(), out var proto); + var effectsArgs = new ReagentEffectArgs(uid, + null, + component.Solution, + proto!, + reagent.Quantity, + EntityManager, + null, + 1f); + foreach (var effect in component.UnsafeOilVolumeEffects) { - //JJ Comment - not sure this works. Need to check if Reagent.ToString is correct. - _prototypeManager.TryIndex(reagent.Reagent.ToString(), out var proto); - var effectsArgs = new ReagentEffectArgs(uid, - null, - component.Solution, - proto!, - reagent.Quantity, - EntityManager, - null, - 1f); - foreach (var effect in component.UnsafeOilVolumeEffects) - { - if (!effect.ShouldApply(effectsArgs, _random)) - continue; - effect.Effect(effectsArgs); - } + if (!effect.ShouldApply(effectsArgs, _random)) + continue; + effect.Effect(effectsArgs); } } + } - /// - /// Make sure the UI and interval tracker are updated anytime something - /// is inserted into one of the baskets. - /// - /// - /// This is used instead of EntInsertedIntoContainerMessage so charred - /// items can be inserted into the deep fryer without triggering this - /// event. - /// - private void AfterInsert(EntityUid uid, DeepFryerComponent component, EntityUid item) - { - if (HasBubblingOil(uid, component)) - _audioSystem.PlayPvs(component.SoundInsertItem, uid, AudioParamsInsertRemove); - - UpdateNextFryTime(uid, component); - UpdateUserInterface(uid, component); - } - - private void OnPowerChange(EntityUid uid, DeepFryerComponent component, ref PowerChangedEvent args) - { - _appearanceSystem.SetData(uid, DeepFryerVisuals.Bubbling, args.Powered); - UpdateNextFryTime(uid, component); - UpdateAmbientSound(uid, component); - } - - private void OnDeconstruct(EntityUid uid, DeepFryerComponent component, MachineDeconstructedEvent args) - { - // The EmptyOnMachineDeconstruct component handles the entity container for us. - _puddleSystem.TrySpillAt(uid, component.Solution, out var _); - } + /// + /// Make sure the UI and interval tracker are updated anytime something + /// is inserted into one of the baskets. + /// + /// + /// This is used instead of EntInsertedIntoContainerMessage so charred + /// items can be inserted into the deep fryer without triggering this + /// event. + /// + private void AfterInsert(EntityUid uid, DeepFryerComponent component, EntityUid item) + { + if (HasBubblingOil(uid, component)) + _audioSystem.PlayPvs(component.SoundInsertItem, uid, AudioParamsInsertRemove); - private void OnDestruction(EntityUid uid, DeepFryerComponent component, DestructionEventArgs args) - { - _containerSystem.EmptyContainer(component.Storage, true); - } + UpdateNextFryTime(uid, component); + UpdateUserInterface(uid, component); + } - private void OnRefreshParts(EntityUid uid, DeepFryerComponent component, RefreshPartsEvent args) - { - var ratingStorage = args.PartRatings[component.MachinePartStorageMax]; + private void OnPowerChange(EntityUid uid, DeepFryerComponent component, ref PowerChangedEvent args) + { + _appearanceSystem.SetData(uid, DeepFryerVisuals.Bubbling, args.Powered); + UpdateNextFryTime(uid, component); + UpdateAmbientSound(uid, component); + } - component.StorageMaxEntities = component.BaseStorageMaxEntities + (int) (component.StoragePerPartRating * (ratingStorage - 1)); - } + private void OnDeconstruct(EntityUid uid, DeepFryerComponent component, MachineDeconstructedEvent args) + { + // The EmptyOnMachineDeconstruct component handles the entity container for us. + _puddleSystem.TrySpillAt(uid, component.Solution, out var _); + } - /// - /// Allow thrown items to land in a basket. - /// - private void OnThrowHitBy(EntityUid uid, DeepFryerComponent component, ThrowHitByEvent args) - { - if (args.Handled) - return; + private void OnDestruction(EntityUid uid, DeepFryerComponent component, DestructionEventArgs args) + { + _containerSystem.EmptyContainer(component.Storage, true); + } - // Chefs never miss this. :) - float missChance = HasComp(args.User) ? 0f : ThrowMissChance; + private void OnRefreshParts(EntityUid uid, DeepFryerComponent component, RefreshPartsEvent args) + { + var ratingStorage = args.PartRatings[component.MachinePartStorageMax]; - if (!CanInsertItem(uid, component, args.Thrown) || - _random.Prob(missChance) || - !component.Storage.Insert(args.Thrown)) - { - _popupSystem.PopupEntity( - Loc.GetString("deep-fryer-thrown-missed"), - uid); + component.StorageMaxEntities = component.BaseStorageMaxEntities + + (int) (component.StoragePerPartRating * (ratingStorage - 1)); + } - if (args.User != null) - _adminLogManager.Add(LogType.Action, LogImpact.Low, - $"{ToPrettyString(args.User.Value)} threw {ToPrettyString(args.Thrown)} at {ToPrettyString(uid)}, and it missed."); + /// + /// Allow thrown items to land in a basket. + /// + private void OnThrowHitBy(EntityUid uid, DeepFryerComponent component, ThrowHitByEvent args) + { + if (args.Handled) + return; - return; - } + // Chefs never miss this. :) + var missChance = HasComp(args.User) ? 0f : ThrowMissChance; - if (GetOilVolume(uid, component) < component.SafeOilVolume) - _popupSystem.PopupEntity( - Loc.GetString("deep-fryer-thrown-hit-oil-low"), - uid); - else - _popupSystem.PopupEntity( - Loc.GetString("deep-fryer-thrown-hit-oil"), - uid); + if (!CanInsertItem(uid, component, args.Thrown) || + _random.Prob(missChance) || + !component.Storage.Insert(args.Thrown)) + { + _popupSystem.PopupEntity( + Loc.GetString("deep-fryer-thrown-missed"), + uid); if (args.User != null) + { _adminLogManager.Add(LogType.Action, LogImpact.Low, - $"{ToPrettyString(args.User.Value)} threw {ToPrettyString(args.Thrown)} at {ToPrettyString(uid)}, and it landed inside."); - - AfterInsert(uid, component, args.Thrown); + $"{ToPrettyString(args.User.Value)} threw {ToPrettyString(args.Thrown)} at {ToPrettyString(uid)}, and it missed."); + } - args.Handled = true; + return; } - private void OnSolutionChange(EntityUid uid, DeepFryerComponent component, SolutionChangedEvent args) + if (GetOilVolume(uid, component) < component.SafeOilVolume) { - UpdateUserInterface(uid, component); - UpdateAmbientSound(uid, component); + _popupSystem.PopupEntity( + Loc.GetString("deep-fryer-thrown-hit-oil-low"), + uid); } - - private void OnRelayMovement(EntityUid uid, DeepFryerComponent component, ref ContainerRelayMovementEntityEvent args) + else { - if (!component.Storage.Remove(args.Entity, EntityManager, destination: Transform(uid).Coordinates)) - return; - _popupSystem.PopupEntity( - Loc.GetString("deep-fryer-entity-escape", - ("victim", Identity.Entity(args.Entity, EntityManager)), - ("deepFryer", uid)), - uid, - PopupType.SmallCaution); + Loc.GetString("deep-fryer-thrown-hit-oil"), + uid); } - public bool CanInsertItem(EntityUid uid, DeepFryerComponent component, EntityUid item) + if (args.User != null) { - // Keep this consistent with the checks in TryInsertItem. - return (HasComp(item) && - !HasComp(item) && - component.Storage.ContainedEntities.Count < component.StorageMaxEntities); + _adminLogManager.Add(LogType.Action, LogImpact.Low, + $"{ToPrettyString(args.User.Value)} threw {ToPrettyString(args.Thrown)} at {ToPrettyString(uid)}, and it landed inside."); } - private bool TryInsertItem(EntityUid uid, DeepFryerComponent component, EntityUid user, EntityUid item) - { - if (!HasComp(item)) - { - _popupSystem.PopupEntity( - Loc.GetString("deep-fryer-interact-using-not-item"), - uid, - user); - return false; - } - - if (HasComp(item)) - { - _popupSystem.PopupEntity( - Loc.GetString("deep-fryer-storage-no-fit", - ("item", item)), - uid, - user); - return false; - } - - if (component.Storage.ContainedEntities.Count >= component.StorageMaxEntities) - { - _popupSystem.PopupEntity( - Loc.GetString("deep-fryer-storage-full"), - uid, - user); - return false; - } + AfterInsert(uid, component, args.Thrown); - if (!_handsSystem.TryDropIntoContainer(user, item, component.Storage)) - return false; + args.Handled = true; + } - AfterInsert(uid, component, item); + private void OnSolutionChange(EntityUid uid, DeepFryerComponent component, SolutionChangedEvent args) + { + UpdateUserInterface(uid, component); + UpdateAmbientSound(uid, component); + } - _adminLogManager.Add(LogType.Action, LogImpact.Low, - $"{ToPrettyString(user)} put {ToPrettyString(item)} inside {ToPrettyString(uid)}."); + private void OnRelayMovement(EntityUid uid, DeepFryerComponent component, + ref ContainerRelayMovementEntityEvent args) + { + if (!component.Storage.Remove(args.Entity, EntityManager, destination: Transform(uid).Coordinates)) + return; + + _popupSystem.PopupEntity( + Loc.GetString("deep-fryer-entity-escape", + ("victim", Identity.Entity(args.Entity, EntityManager)), + ("deepFryer", uid)), + uid, + PopupType.SmallCaution); + } - return true; - } + private void OnBeforeActivatableUIOpen(EntityUid uid, DeepFryerComponent component, + BeforeActivatableUIOpenEvent args) + { + UpdateUserInterface(uid, component); + } - private void OnInteractUsing(EntityUid uid, DeepFryerComponent component, InteractUsingEvent args) + private void OnRemoveItem(EntityUid uid, DeepFryerComponent component, DeepFryerRemoveItemMessage args) + { + var removedItem = EntityManager.GetEntity(args.Item); + if (removedItem.Valid) { - if (args.Handled) + //JJ Comment - This line should be unnecessary. Some issue is keeping the UI from updating when converting straight to a Burned Mess while the UI is still open. To replicate, put a Raw Meat in the fryer with no oil in it. Wait until it sputters with no effect. It should transform to Burned Mess, but doesn't. + if (!component.Storage.Remove(removedItem)) return; - // By default, allow entities with SolutionTransfer or Tool - // components to perform their usual actions. Inserting them (if - // the chef really wants to) will be supported through the UI. - if (HasComp(args.Used) || - HasComp(args.Used)) + var user = args.Session.AttachedEntity; + + if (user != null) { - return; + _handsSystem.TryPickupAnyHand(user.Value, removedItem); + + _adminLogManager.Add(LogType.Action, LogImpact.Low, + $"{ToPrettyString(user.Value)} took {ToPrettyString(args.Item)} out of {ToPrettyString(uid)}."); } - if (TryInsertItem(uid, component, args.User, args.Used)) - args.Handled = true; - } + _audioSystem.PlayPvs(component.SoundRemoveItem, uid, AudioParamsInsertRemove); - private void OnBeforeActivatableUIOpen(EntityUid uid, DeepFryerComponent component, BeforeActivatableUIOpenEvent args) - { - UpdateUserInterface(uid, component); + UpdateUserInterface(component.Owner, component); } + } - private void OnRemoveItem(EntityUid uid, DeepFryerComponent component, DeepFryerRemoveItemMessage args) - { - var removedItem = EntityManager.GetEntity(args.Item); - if (removedItem.Valid) - { //JJ Comment - This line should be unnecessary. Some issue is keeping the UI from updating when converting straight to a Burned Mess while the UI is still open. To replicate, put a Raw Meat in the fryer with no oil in it. Wait until it sputters with no effect. It should transform to Burned Mess, but doesn't. - if (!component.Storage.Remove(removedItem)) - return; - - var user = args.Session.AttachedEntity; - - if (user != null) - { - _handsSystem.TryPickupAnyHand(user.Value, removedItem); - - _adminLogManager.Add(LogType.Action, LogImpact.Low, - $"{ToPrettyString(user.Value)} took {ToPrettyString(args.Item)} out of {ToPrettyString(uid)}."); - } + /// + /// This is a helper function for ScoopVat and ClearSlag. + /// + private bool TryGetActiveHandSolutionContainer( + EntityUid fryer, + EntityUid user, + [NotNullWhen(true)] out EntityUid? heldItem, + [NotNullWhen(true)] out Entity? solution, + out FixedPoint2 transferAmount) + { + heldItem = null; + solution = null; + transferAmount = FixedPoint2.Zero; - _audioSystem.PlayPvs(component.SoundRemoveItem, uid, AudioParamsInsertRemove); + if (!TryComp(user, out var handsComponent)) + return false; - UpdateUserInterface(component.Owner, component); - } - } + heldItem = handsComponent.ActiveHandEntity; - private void OnInsertItem(EntityUid uid, DeepFryerComponent component, DeepFryerInsertItemMessage args) + if (heldItem == null || + !TryComp(heldItem, out var solutionTransferComponent) || + !_solutionContainerSystem.TryGetRefillableSolution(heldItem.Value, out var solEnt, out var _) || + !solutionTransferComponent.CanReceive) { - var user = args.Session.AttachedEntity; - - if (user == null || - !TryComp(user, out var handsComponent) || - handsComponent.ActiveHandEntity == null) - { - return; - } + _popupSystem.PopupEntity( + Loc.GetString("deep-fryer-need-liquid-container-in-hand"), + fryer, + user); - if (handsComponent.ActiveHandEntity != null) - TryInsertItem(uid, component, user.Value, handsComponent.ActiveHandEntity.Value); + return false; } - /// - /// This is a helper function for ScoopVat and ClearSlag. - /// - private bool TryGetActiveHandSolutionContainer( - EntityUid fryer, - EntityUid user, - [NotNullWhen(true)] out EntityUid? heldItem, - [NotNullWhen(true)] out Solution? solution, - out FixedPoint2 transferAmount) - { - heldItem = null; - solution = null; - transferAmount = FixedPoint2.Zero; + solution = solEnt; + transferAmount = solutionTransferComponent.TransferAmount; - if (!TryComp(user, out var handsComponent)) - return false; + return true; + } - heldItem = handsComponent.ActiveHandEntity; + private void OnScoopVat(EntityUid uid, DeepFryerComponent component, DeepFryerScoopVatMessage args) + { + var user = args.Session.AttachedEntity; - if (heldItem == null || - !TryComp(heldItem, out var solutionTransferComponent) || - !_solutionContainerSystem.TryGetRefillableSolution(heldItem.Value, out var refillableSolution) || - !solutionTransferComponent.CanReceive) - { - _popupSystem.PopupEntity( - Loc.GetString("deep-fryer-need-liquid-container-in-hand"), - fryer, - user); + if (user == null || + !TryGetActiveHandSolutionContainer(uid, user.Value, out var heldItem, out var heldSolution, + out var transferAmount)) + return; - return false; - } + if (!_solutionContainerSystem.TryGetSolution(component.Owner, component.Solution.Name, out var solution)) + return; - solution = refillableSolution; - transferAmount = solutionTransferComponent.TransferAmount; + _solutionTransferSystem.Transfer(user.Value, + uid, + solution.Value, + heldItem.Value, + heldSolution.Value, + transferAmount); - return true; - } + // UI update is not necessary here, because the solution change event handles it. + } - private void OnScoopVat(EntityUid uid, DeepFryerComponent component, DeepFryerScoopVatMessage args) - { - var user = args.Session.AttachedEntity; + private void OnClearSlagStart(EntityUid uid, DeepFryerComponent component, DeepFryerClearSlagMessage args) + { + var user = args.Session.AttachedEntity; - if (user == null || - !TryGetActiveHandSolutionContainer(uid, user.Value, out var heldItem, out var heldSolution, out var transferAmount)) - { - return; - } + if (user == null || + !TryGetActiveHandSolutionContainer(uid, user.Value, out var heldItem, out var heldSolution, + out var transferAmount)) + return; - _solutionTransferSystem.Transfer(user.Value, + var wasteVolume = GetWasteVolume(uid, component); + if (wasteVolume == FixedPoint2.Zero) + { + _popupSystem.PopupEntity( + Loc.GetString("deep-fryer-oil-no-slag"), uid, - component.Solution, - heldItem.Value, - heldSolution, - transferAmount); + user.Value); - // UI update is not necessary here, because the solution change event handles it. + return; } - private void OnClearSlagStart(EntityUid uid, DeepFryerComponent component, DeepFryerClearSlagMessage args) - { - var user = args.Session.AttachedEntity; + var delay = Math.Clamp((float) wasteVolume * 0.1f, 1f, 5f); - if (user == null || - !TryGetActiveHandSolutionContainer(uid, user.Value, out var heldItem, out var heldSolution, out var transferAmount)) - { - return; - } + var ev = new ClearSlagDoAfterEvent(heldSolution.Value.Comp.Solution, transferAmount); - var wasteVolume = GetWasteVolume(uid, component); - if (wasteVolume == FixedPoint2.Zero) - { - _popupSystem.PopupEntity( - Loc.GetString("deep-fryer-oil-no-slag"), - uid, - user.Value); + //JJ Comment - not sure I have DoAfterArgs configured correctly. + var doAfterArgs = new DoAfterArgs(EntityManager, user.Value, delay, ev, uid, uid, heldItem) + { + BreakOnDamage = true, + BreakOnTargetMove = true, + BreakOnUserMove = true, + MovementThreshold = 0.25f, + NeedHand = true + }; - return; - } + _doAfterSystem.TryStartDoAfter(doAfterArgs); + } - var delay = Math.Clamp((float) wasteVolume * 0.1f, 1f, 5f); + private void OnRemoveAllItems(EntityUid uid, DeepFryerComponent component, DeepFryerRemoveAllItemsMessage args) + { + if (component.Storage.ContainedEntities.Count == 0) + return; - var ev = new ClearSlagDoAfterEvent(heldSolution, transferAmount); + _containerSystem.EmptyContainer(component.Storage); - //JJ Comment - not sure I have DoAfterArgs configured correctly. - var doAfterArgs = new DoAfterArgs(EntityManager, user.Value, delay, ev, uid, uid, used: heldItem) - { - BreakOnDamage = true, - BreakOnTargetMove = true, - BreakOnUserMove = true, - MovementThreshold = 0.25f, - NeedHand = true, - }; - - _doAfterSystem.TryStartDoAfter(doAfterArgs); - } + var user = args.Session.AttachedEntity; - private void OnRemoveAllItems(EntityUid uid, DeepFryerComponent component, DeepFryerRemoveAllItemsMessage args) + if (user != null) { - if (component.Storage.ContainedEntities.Count == 0) - return; + _adminLogManager.Add(LogType.Action, LogImpact.Low, + $"{ToPrettyString(user.Value)} removed all items from {ToPrettyString(uid)}."); + } - _containerSystem.EmptyContainer(component.Storage, false); + _audioSystem.PlayPvs(component.SoundRemoveItem, uid, AudioParamsInsertRemove); - var user = args.Session.AttachedEntity; + UpdateUserInterface(component.Owner, component); + } - if (user != null) - _adminLogManager.Add(LogType.Action, LogImpact.Low, - $"{ToPrettyString(user.Value)} removed all items from {ToPrettyString(uid)}."); + private void OnClearSlag(EntityUid uid, DeepFryerComponent component, ClearSlagDoAfterEvent args) + { + if (args.Handled || args.Cancelled || args.Args.Used == null) + return; - _audioSystem.PlayPvs(component.SoundRemoveItem, uid, AudioParamsInsertRemove); + FixedPoint2 reagentCount = component.WasteReagents.Count(); - UpdateUserInterface(component.Owner, component); + var removingSolution = new Solution(); + foreach (var reagent in component.WasteReagents) + { + var removed = component.Solution.RemoveReagent(reagent.Reagent.ToString(), args.Amount / reagentCount); + removingSolution.AddReagent(reagent.Reagent.ToString(), removed); } - private void OnClearSlag(EntityUid uid, DeepFryerComponent component, ClearSlagDoAfterEvent args) - { - if (args.Handled || args.Cancelled || args.Args.Used == null) - return; + if (!_solutionContainerSystem.TryGetSolution(uid, component.SolutionName, out var solution)) + return; - FixedPoint2 reagentCount = component.WasteReagents.Count(); + if (!_solutionContainerSystem.TryGetSolution(args.Used!.Value, args.Solution.Name, out var targetSolution)) + return; - var removingSolution = new Solution(); - foreach (var reagent in component.WasteReagents) - { - var removed = component.Solution.RemoveReagent(reagent.Reagent.ToString(), args.Amount / reagentCount); - removingSolution.AddReagent(reagent.Reagent.ToString(), removed); - } + _solutionContainerSystem.UpdateChemicals(solution.Value); + _solutionContainerSystem.TryMixAndOverflow(targetSolution.Value, removingSolution, + args.Solution.MaxVolume, out var _); + } - _solutionContainerSystem.UpdateChemicals(uid, component.Solution); - _solutionContainerSystem.TryMixAndOverflow(args.Args.Used.Value, args.Solution, removingSolution, args.Solution.MaxVolume, out var _); - } - private void OnInitDeepFried(EntityUid uid, DeepFriedComponent component, ComponentInit args) - { - var meta = MetaData(uid); - component.OriginalName = meta.EntityName; - _metaDataSystem.SetEntityName(uid, Loc.GetString("deep-fried-crispy-item", ("entity", meta.EntityName))); - } + private void OnInitDeepFried(EntityUid uid, DeepFriedComponent component, ComponentInit args) + { + var meta = MetaData(uid); + component.OriginalName = meta.EntityName; + _metaDataSystem.SetEntityName(uid, Loc.GetString("deep-fried-crispy-item", ("entity", meta.EntityName))); + } - private void OnExamineFried(EntityUid uid, DeepFriedComponent component, ExaminedEvent args) - { - switch (component.Crispiness) - { - case 0: - args.PushMarkup(Loc.GetString("deep-fried-crispy-item-examine")); - break; - case 1: - args.PushMarkup(Loc.GetString("deep-fried-fried-item-examine")); - break; - default: - args.PushMarkup(Loc.GetString("deep-fried-burned-item-examine")); - break; - } + private void OnExamineFried(EntityUid uid, DeepFriedComponent component, ExaminedEvent args) + { + switch (component.Crispiness) + { + case 0: + args.PushMarkup(Loc.GetString("deep-fried-crispy-item-examine")); + break; + case 1: + args.PushMarkup(Loc.GetString("deep-fried-fried-item-examine")); + break; + default: + args.PushMarkup(Loc.GetString("deep-fried-burned-item-examine")); + break; } + } - private void OnPriceCalculation(EntityUid uid, DeepFriedComponent component, ref PriceCalculationEvent args) - { - args.Price *= component.PriceCoefficient; - } + private void OnPriceCalculation(EntityUid uid, DeepFriedComponent component, ref PriceCalculationEvent args) + { + args.Price *= component.PriceCoefficient; + } - private void OnSliceDeepFried(EntityUid uid, DeepFriedComponent component, SliceFoodEvent args) - { - MakeCrispy(args.Slice); + private void OnSliceDeepFried(EntityUid uid, DeepFriedComponent component, SliceFoodEvent args) + { + MakeCrispy(args.Slice); - // Copy relevant values to the slice. - var sourceDeepFriedComponent = Comp(args.Food); - var sliceDeepFriedComponent = Comp(args.Slice); + // Copy relevant values to the slice. + var sourceDeepFriedComponent = Comp(args.Food); + var sliceDeepFriedComponent = Comp(args.Slice); - sliceDeepFriedComponent.Crispiness = sourceDeepFriedComponent.Crispiness; - sliceDeepFriedComponent.PriceCoefficient = sourceDeepFriedComponent.PriceCoefficient; + sliceDeepFriedComponent.Crispiness = sourceDeepFriedComponent.Crispiness; + sliceDeepFriedComponent.PriceCoefficient = sourceDeepFriedComponent.PriceCoefficient; - UpdateDeepFriedName(args.Slice, sliceDeepFriedComponent); + UpdateDeepFriedName(args.Slice, sliceDeepFriedComponent); - // TODO: Flavor profiles aren't copied to the slices. This should - // probably be handled on upstream, but for now let's assume the - // oil of the deep fryer is overpowering enough for this small - // hack. This is likely the only place where it would be useful. - if (TryComp(args.Food, out var sourceFlavorProfileComponent) && - TryComp(args.Slice, out var sliceFlavorProfileComponent)) - { - sliceFlavorProfileComponent.Flavors.UnionWith(sourceFlavorProfileComponent.Flavors); - sliceFlavorProfileComponent.IgnoreReagents.UnionWith(sourceFlavorProfileComponent.IgnoreReagents); - } + // TODO: Flavor profiles aren't copied to the slices. This should + // probably be handled on upstream, but for now let's assume the + // oil of the deep fryer is overpowering enough for this small + // hack. This is likely the only place where it would be useful. + if (TryComp(args.Food, out var sourceFlavorProfileComponent) && + TryComp(args.Slice, out var sliceFlavorProfileComponent)) + { + sliceFlavorProfileComponent.Flavors.UnionWith(sourceFlavorProfileComponent.Flavors); + sliceFlavorProfileComponent.IgnoreReagents.UnionWith(sourceFlavorProfileComponent.IgnoreReagents); } } +} +public sealed class DeepFryAttemptEvent : CancellableEntityEventArgs +{ + public EntityUid DeepFryer { get; } - public sealed class DeepFryAttemptEvent : CancellableEntityEventArgs + public DeepFryAttemptEvent(EntityUid deepFryer) { - public EntityUid DeepFryer { get; } - - public DeepFryAttemptEvent(EntityUid deepFryer) - { - DeepFryer = deepFryer; - } + DeepFryer = deepFryer; } +} - public sealed class BeingDeepFriedEvent : EntityEventArgs - { - public EntityUid DeepFryer { get; } - public EntityUid Item { get; } - public bool TurnIntoFood { get; set; } +public sealed class BeingDeepFriedEvent : EntityEventArgs +{ + public EntityUid DeepFryer { get; } + public EntityUid Item { get; } + public bool TurnIntoFood { get; set; } - public BeingDeepFriedEvent(EntityUid deepFryer, EntityUid item) - { - DeepFryer = deepFryer; - Item = item; - } + public BeingDeepFriedEvent(EntityUid deepFryer, EntityUid item) + { + DeepFryer = deepFryer; + Item = item; } } diff --git a/Content.Server/Salvage/SalvageSystem.ExpeditionConsole.cs b/Content.Server/Salvage/SalvageSystem.ExpeditionConsole.cs index 6439d672509..aa14ef855b9 100644 --- a/Content.Server/Salvage/SalvageSystem.ExpeditionConsole.cs +++ b/Content.Server/Salvage/SalvageSystem.ExpeditionConsole.cs @@ -10,6 +10,8 @@ namespace Content.Server.Salvage; public sealed partial class SalvageSystem { + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + private const float ShuttleFTLMassThreshold = 50f; private const float ShuttleFTLRange = 150f; diff --git a/Content.Server/Salvage/SpawnSalvageMissionJob.cs b/Content.Server/Salvage/SpawnSalvageMissionJob.cs index e4dc0e1c775..dda662f39f6 100644 --- a/Content.Server/Salvage/SpawnSalvageMissionJob.cs +++ b/Content.Server/Salvage/SpawnSalvageMissionJob.cs @@ -242,11 +242,13 @@ private async Task SpawnDungeonLoot(Dungeon? dungeon, SalvageBiomeMod biomeMod, { case BiomeMarkerLoot biomeLoot: { - if (_entManager.TryGetComponent(gridUid, out var biome) && - biomeLoot.Prototype.TryGetValue(biomeMod.ID, out var mod)) - { - _biome.AddMarkerLayer(gridUid, biome, biomeMod.ID); - } + //Frontier + //TODO:fix this proper + //if (_entManager.TryGetComponent(gridUid, out var biome) && + // biomeLoot.Prototype.TryGetValue(biomeMod.ID, out var mod)) + //{ + // _biome.AddMarkerLayer(gridUid, biome, biomeMod.ID); + //} } break; case BiomeTemplateLoot biomeLoot: diff --git a/Content.Server/Traits/Assorted/FriedTraitSystem.cs b/Content.Server/Traits/Assorted/FriedTraitSystem.cs index f3d5c017c0e..b1977bd11c9 100644 --- a/Content.Server/Traits/Assorted/FriedTraitSystem.cs +++ b/Content.Server/Traits/Assorted/FriedTraitSystem.cs @@ -1,4 +1,5 @@ using Content.Server.Kitchen.EntitySystems; +using Content.Server.Nyanotrasen.Kitchen.EntitySystems; namespace Content.Server.Traits.Assorted; diff --git a/Content.Shared/Nyanotrasen/Kitchen/ClearSlagDoAfterEvent.cs b/Content.Shared/Nyanotrasen/Kitchen/ClearSlagDoAfterEvent.cs index e4ecb88d9a4..681dae88c4d 100644 --- a/Content.Shared/Nyanotrasen/Kitchen/ClearSlagDoAfterEvent.cs +++ b/Content.Shared/Nyanotrasen/Kitchen/ClearSlagDoAfterEvent.cs @@ -1,9 +1,9 @@ -using Robust.Shared.Serialization; using Content.Shared.Chemistry.Components; using Content.Shared.DoAfter; using Content.Shared.FixedPoint; +using Robust.Shared.Serialization; -namespace Content.Shared.Kitchen +namespace Content.Shared.Nyanotrasen.Kitchen { [Serializable, NetSerializable] public sealed partial class ClearSlagDoAfterEvent : DoAfterEvent diff --git a/Content.Shared/Nyanotrasen/Kitchen/Components/SharedDeepFriedComponent.cs b/Content.Shared/Nyanotrasen/Kitchen/Components/SharedDeepFriedComponent.cs index 51eca172981..d1f71670f20 100644 --- a/Content.Shared/Nyanotrasen/Kitchen/Components/SharedDeepFriedComponent.cs +++ b/Content.Shared/Nyanotrasen/Kitchen/Components/SharedDeepFriedComponent.cs @@ -1,7 +1,7 @@ using Robust.Shared.GameStates; using Robust.Shared.Serialization; -namespace Content.Shared.Kitchen.Components +namespace Content.Shared.Nyanotrasen.Kitchen.Components { [NetworkedComponent] public abstract partial class SharedDeepFriedComponent : Component diff --git a/Content.Shared/Nyanotrasen/Kitchen/Components/SharedDeepFryerComponent.cs b/Content.Shared/Nyanotrasen/Kitchen/Components/SharedDeepFryerComponent.cs index da7319869d9..7144ed5cdd9 100644 --- a/Content.Shared/Nyanotrasen/Kitchen/Components/SharedDeepFryerComponent.cs +++ b/Content.Shared/Nyanotrasen/Kitchen/Components/SharedDeepFryerComponent.cs @@ -1,6 +1,6 @@ using Robust.Shared.Serialization; -namespace Content.Shared.Kitchen.Components +namespace Content.Shared.Nyanotrasen.Kitchen.Components { public abstract partial class SharedDeepFryerComponent : Component { } diff --git a/Content.Shared/Nyanotrasen/Kitchen/SharedDeepfryerSystem.cs b/Content.Shared/Nyanotrasen/Kitchen/SharedDeepfryerSystem.cs new file mode 100644 index 00000000000..efc177de916 --- /dev/null +++ b/Content.Shared/Nyanotrasen/Kitchen/SharedDeepfryerSystem.cs @@ -0,0 +1,5 @@ +namespace Content.Shared.Nyanotrasen.Kitchen; + +public abstract class SharedDeepfryerSystem : EntitySystem +{ +} diff --git a/Content.Shared/Nyanotrasen/Kitchen/UI/DeepFryerMessages.cs b/Content.Shared/Nyanotrasen/Kitchen/UI/DeepFryerMessages.cs index fa1df8a71e8..31b107310b0 100644 --- a/Content.Shared/Nyanotrasen/Kitchen/UI/DeepFryerMessages.cs +++ b/Content.Shared/Nyanotrasen/Kitchen/UI/DeepFryerMessages.cs @@ -1,7 +1,7 @@ -using Robust.Shared.Serialization; using Content.Shared.FixedPoint; +using Robust.Shared.Serialization; -namespace Content.Shared.Kitchen.UI +namespace Content.Shared.Nyanotrasen.Kitchen.UI { [Serializable, NetSerializable] public sealed class DeepFryerBoundUserInterfaceState : BoundUserInterfaceState