From f06fc04768736b8b7e4195fad6c5b122b4fcc8ea Mon Sep 17 00:00:00 2001
From: JJ <47927305+PHCodes@users.noreply.github.com>
Date: Wed, 18 Oct 2023 17:57:10 -0400
Subject: [PATCH] Deep Fryer And Its Powers (#163)
* Deep Fryer And Its Powers
The Deep Fryer has been implemented. It uses Corn Oil, Ghee, and Olive Oil to fry. Other features include:
1. Mixing Oil and Water at a certain temperature causes smoke.
2. When throwing an object at the Deep Fryer, a Chef will *always* land the shot, but anyone else has a chance of missing.
3. When an item is sliced, an event is triggered that other items can see.
* Update meal_recipes.yml
* Reworking the effects so they won't trigger on init.
* Create DeepFryerTest.cs
* Commenting out the UnsafeOilVolumeEffects part of the .yml. Something about the sound script inside of it breaks UnintializedSaveTest and it's not necessary for a smoke reaction to occur anyway.
* Update DeepFryerSystem.cs
---
.../Kitchen/Components/DeepFriedComponent.cs | 10 +
.../Kitchen/Components/DeepFryerComponent.cs | 10 +
.../Kitchen/UI/DeepFryerBoundUserInterface.cs | 64 +
.../Kitchen/UI/DeepFryerWindow.xaml | 67 ++
.../Kitchen/UI/DeepFryerWindow.xaml.cs | 71 ++
.../Visualizers/DeepFriedVisualizer.cs | 73 ++
.../Visualizers/DeepFryerVisualizer.cs | 21 +
.../Tests/Nyanotrasen/DeepFryerTest.cs | 58 +
.../EntitySystems/SliceableFoodSystem.cs | 42 +
.../Kitchen/Components/DeepFriedComponent.cs | 23 +
.../Kitchen/Components/DeepFryerComponent.cs | 240 ++++
.../Components/ProfessionalChefComponent.cs | 5 +
.../Kitchen/EntitySystems/DeepFryerSystem.cs | 1065 +++++++++++++++++
.../Kitchen/ClearSlagDoAfterEvent.cs | 29 +
.../Components/SharedDeepFriedComponent.cs | 22 +
.../Components/SharedDeepFryerComponent.cs | 12 +
.../Kitchen/UI/DeepFryerMessages.cs | 67 ++
Content.Shared/Throwing/ThrowEvents.cs | 13 +-
Content.Shared/Throwing/ThrownItemSystem.cs | 5 +-
.../Nyanotrasen/Machines/attributions.yml | 9 +
.../Machines/deepfryer_basket_add_item.ogg | Bin 0 -> 38471 bytes
.../Machines/deepfryer_basket_remove_item.ogg | Bin 0 -> 9506 bytes
.../kitchen/deep-fryer-component.ftl | 47 +
.../Prototypes/Catalog/Fills/Crates/food.yml | 6 +
.../Entities/Structures/Machines/lathe.yml | 2 +-
.../Markers/Spawners/Random/devices.yml | 2 +-
.../Objects/Consumable/Drinks/drinks_oil.yml | 56 +
.../Devices/CircuitBoards/production.yml | 20 +
.../Structures/Machines/deep_fryer.yml | 166 +++
.../Reagents/Consumable/Food/condiments.yml | 16 +
.../Recipes/Lathes/electronics.yml | 9 +
.../Nyanotrasen/Recipes/Reactions/food.yml | 13 +-
.../Recipes/Reactions/pyrotechnic.yml | 39 +
.../Reagents/Consumable/Food/ingredients.yml | 2 +
.../Prototypes/Research/civilianservices.yml | 1 +
.../Prototypes/Roles/Jobs/Civilian/chef.yml | 6 +-
.../Consumable/Drinks/oil_jar.rsi/butter.png | Bin 0 -> 154 bytes
.../Consumable/Drinks/oil_jar.rsi/corn.png | Bin 0 -> 157 bytes
.../Consumable/Drinks/oil_jar.rsi/icon.png | Bin 0 -> 278 bytes
.../Drinks/oil_jar.rsi/icon_open.png | Bin 0 -> 267 bytes
.../Consumable/Drinks/oil_jar.rsi/meta.json | 26 +
.../Consumable/Drinks/oil_jar.rsi/olives.png | Bin 0 -> 143 bytes
.../Machines/deep_fryer.rsi/meta.json | 255 ++++
.../Machines/deep_fryer.rsi/off-0.png | Bin 0 -> 981 bytes
.../Machines/deep_fryer.rsi/off-1.png | Bin 0 -> 152 bytes
.../Machines/deep_fryer.rsi/off-2.png | Bin 0 -> 208 bytes
.../Machines/deep_fryer.rsi/off-3.png | Bin 0 -> 217 bytes
.../Machines/deep_fryer.rsi/off-4.png | Bin 0 -> 212 bytes
.../Machines/deep_fryer.rsi/off-5.png | Bin 0 -> 236 bytes
.../Machines/deep_fryer.rsi/off-6.png | Bin 0 -> 244 bytes
.../Machines/deep_fryer.rsi/off-7.png | Bin 0 -> 240 bytes
.../Machines/deep_fryer.rsi/off-8.png | Bin 0 -> 259 bytes
.../Machines/deep_fryer.rsi/on-1.png | Bin 0 -> 322 bytes
.../Machines/deep_fryer.rsi/on-2.png | Bin 0 -> 473 bytes
.../Machines/deep_fryer.rsi/on-3.png | Bin 0 -> 539 bytes
.../Machines/deep_fryer.rsi/on-4.png | Bin 0 -> 535 bytes
.../Machines/deep_fryer.rsi/on-5.png | Bin 0 -> 566 bytes
.../Machines/deep_fryer.rsi/on-6.png | Bin 0 -> 720 bytes
.../Machines/deep_fryer.rsi/on-7.png | Bin 0 -> 729 bytes
.../Machines/deep_fryer.rsi/on-8.png | Bin 0 -> 769 bytes
60 files changed, 2564 insertions(+), 8 deletions(-)
create mode 100644 Content.Client/Nyanotrasen/Kitchen/Components/DeepFriedComponent.cs
create mode 100644 Content.Client/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs
create mode 100644 Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerBoundUserInterface.cs
create mode 100644 Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerWindow.xaml
create mode 100644 Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerWindow.xaml.cs
create mode 100644 Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFriedVisualizer.cs
create mode 100644 Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFryerVisualizer.cs
create mode 100644 Content.IntegrationTests/Tests/Nyanotrasen/DeepFryerTest.cs
create mode 100644 Content.Server/Nyanotrasen/Kitchen/Components/DeepFriedComponent.cs
create mode 100644 Content.Server/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs
create mode 100644 Content.Server/Nyanotrasen/Kitchen/Components/ProfessionalChefComponent.cs
create mode 100644 Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs
create mode 100644 Content.Shared/Nyanotrasen/Kitchen/ClearSlagDoAfterEvent.cs
create mode 100644 Content.Shared/Nyanotrasen/Kitchen/Components/SharedDeepFriedComponent.cs
create mode 100644 Content.Shared/Nyanotrasen/Kitchen/Components/SharedDeepFryerComponent.cs
create mode 100644 Content.Shared/Nyanotrasen/Kitchen/UI/DeepFryerMessages.cs
create mode 100644 Resources/Audio/Nyanotrasen/Machines/deepfryer_basket_add_item.ogg
create mode 100644 Resources/Audio/Nyanotrasen/Machines/deepfryer_basket_remove_item.ogg
create mode 100644 Resources/Locale/en-US/nyanotrasen/kitchen/deep-fryer-component.ftl
create mode 100644 Resources/Prototypes/Nyanotrasen/Entities/Objects/Consumable/Drinks/drinks_oil.yml
create mode 100644 Resources/Prototypes/Nyanotrasen/Entities/Structures/Machines/deep_fryer.yml
create mode 100644 Resources/Prototypes/Nyanotrasen/Recipes/Reactions/pyrotechnic.yml
create mode 100644 Resources/Textures/Nyanotrasen/Objects/Consumable/Drinks/oil_jar.rsi/butter.png
create mode 100644 Resources/Textures/Nyanotrasen/Objects/Consumable/Drinks/oil_jar.rsi/corn.png
create mode 100644 Resources/Textures/Nyanotrasen/Objects/Consumable/Drinks/oil_jar.rsi/icon.png
create mode 100644 Resources/Textures/Nyanotrasen/Objects/Consumable/Drinks/oil_jar.rsi/icon_open.png
create mode 100644 Resources/Textures/Nyanotrasen/Objects/Consumable/Drinks/oil_jar.rsi/meta.json
create mode 100644 Resources/Textures/Nyanotrasen/Objects/Consumable/Drinks/oil_jar.rsi/olives.png
create mode 100644 Resources/Textures/Nyanotrasen/Structures/Machines/deep_fryer.rsi/meta.json
create mode 100644 Resources/Textures/Nyanotrasen/Structures/Machines/deep_fryer.rsi/off-0.png
create mode 100644 Resources/Textures/Nyanotrasen/Structures/Machines/deep_fryer.rsi/off-1.png
create mode 100644 Resources/Textures/Nyanotrasen/Structures/Machines/deep_fryer.rsi/off-2.png
create mode 100644 Resources/Textures/Nyanotrasen/Structures/Machines/deep_fryer.rsi/off-3.png
create mode 100644 Resources/Textures/Nyanotrasen/Structures/Machines/deep_fryer.rsi/off-4.png
create mode 100644 Resources/Textures/Nyanotrasen/Structures/Machines/deep_fryer.rsi/off-5.png
create mode 100644 Resources/Textures/Nyanotrasen/Structures/Machines/deep_fryer.rsi/off-6.png
create mode 100644 Resources/Textures/Nyanotrasen/Structures/Machines/deep_fryer.rsi/off-7.png
create mode 100644 Resources/Textures/Nyanotrasen/Structures/Machines/deep_fryer.rsi/off-8.png
create mode 100644 Resources/Textures/Nyanotrasen/Structures/Machines/deep_fryer.rsi/on-1.png
create mode 100644 Resources/Textures/Nyanotrasen/Structures/Machines/deep_fryer.rsi/on-2.png
create mode 100644 Resources/Textures/Nyanotrasen/Structures/Machines/deep_fryer.rsi/on-3.png
create mode 100644 Resources/Textures/Nyanotrasen/Structures/Machines/deep_fryer.rsi/on-4.png
create mode 100644 Resources/Textures/Nyanotrasen/Structures/Machines/deep_fryer.rsi/on-5.png
create mode 100644 Resources/Textures/Nyanotrasen/Structures/Machines/deep_fryer.rsi/on-6.png
create mode 100644 Resources/Textures/Nyanotrasen/Structures/Machines/deep_fryer.rsi/on-7.png
create mode 100644 Resources/Textures/Nyanotrasen/Structures/Machines/deep_fryer.rsi/on-8.png
diff --git a/Content.Client/Nyanotrasen/Kitchen/Components/DeepFriedComponent.cs b/Content.Client/Nyanotrasen/Kitchen/Components/DeepFriedComponent.cs
new file mode 100644
index 00000000000000..a1252cf43edbc0
--- /dev/null
+++ b/Content.Client/Nyanotrasen/Kitchen/Components/DeepFriedComponent.cs
@@ -0,0 +1,10 @@
+using Content.Shared.Kitchen.Components;
+
+namespace Content.Client.Kitchen.Components
+{
+ [RegisterComponent]
+ //Unnecessary item: [ComponentReference(typeof(SharedDeepFriedComponent))]
+ public sealed partial class DeepFriedComponent : SharedDeepFriedComponent
+ {
+ }
+}
diff --git a/Content.Client/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs b/Content.Client/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs
new file mode 100644
index 00000000000000..4123eb341cae94
--- /dev/null
+++ b/Content.Client/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs
@@ -0,0 +1,10 @@
+using Content.Shared.Kitchen.Components;
+
+namespace Content.Client.Kitchen.Components
+{
+ [RegisterComponent]
+ // Unnecessary line: [ComponentReference(typeof(SharedDeepFryerComponent))]
+ public sealed partial class DeepFryerComponent : SharedDeepFryerComponent
+ {
+ }
+}
diff --git a/Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerBoundUserInterface.cs b/Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerBoundUserInterface.cs
new file mode 100644
index 00000000000000..8db884328e45ab
--- /dev/null
+++ b/Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerBoundUserInterface.cs
@@ -0,0 +1,64 @@
+using Robust.Client.GameObjects;
+using Content.Shared.Kitchen.UI;
+
+namespace Content.Client.Nyanotrasen.Kitchen.UI
+{
+ public sealed class DeepFryerBoundUserInterface : BoundUserInterface
+ {
+ private DeepFryerWindow? _window;
+
+ private NetEntity[] _entities = default!;
+
+ public DeepFryerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
+ {
+ base.Open();
+ _window = new DeepFryerWindow();
+ _window.OnClose += Close;
+ _window.ItemList.OnItemSelected += args =>
+ {
+ SendMessage(new DeepFryerRemoveItemMessage(_entities[args.ItemIndex]));
+ };
+ _window.InsertItem.OnPressed += _ =>
+ {
+ SendMessage(new DeepFryerInsertItemMessage());
+ };
+ _window.ScoopVat.OnPressed += _ =>
+ {
+ SendMessage(new DeepFryerScoopVatMessage());
+ };
+ _window.ClearSlag.OnPressed += args =>
+ {
+ SendMessage(new DeepFryerClearSlagMessage());
+ };
+ _window.RemoveAllItems.OnPressed += _ =>
+ {
+ SendMessage(new DeepFryerRemoveAllItemsMessage());
+ };
+ _window.OpenCentered();
+ }
+
+ protected override void UpdateState(BoundUserInterfaceState state)
+ {
+ base.UpdateState(state);
+
+ if (_window == null)
+ return;
+
+ if (state is not DeepFryerBoundUserInterfaceState cast)
+ return;
+
+ _entities = cast.ContainedEntities;
+ _window.UpdateState(cast);
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+
+ if (!disposing)
+ return;
+
+ _window?.Dispose();
+ }
+ }
+}
diff --git a/Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerWindow.xaml b/Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerWindow.xaml
new file mode 100644
index 00000000000000..6ada0b95baf281
--- /dev/null
+++ b/Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerWindow.xaml
@@ -0,0 +1,67 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerWindow.xaml.cs b/Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerWindow.xaml.cs
new file mode 100644
index 00000000000000..7eb8eabd2ca41b
--- /dev/null
+++ b/Content.Client/Nyanotrasen/Kitchen/UI/DeepFryerWindow.xaml.cs
@@ -0,0 +1,71 @@
+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
+{
+ [GenerateTypedNameReferences]
+ [Access(typeof(DeepFryerBoundUserInterface))]
+ public sealed partial class DeepFryerWindow : DefaultWindow
+ {
+ [Dependency] private readonly IEntityManager _entityManager = default!;
+
+ private static readonly Color WarningColor = Color.FromHsv(new Vector4(0.0f, 1.0f, 0.8f, 1.0f));
+
+ public DeepFryerWindow()
+ {
+ RobustXamlLoader.Load(this);
+ IoCManager.InjectDependencies(this);
+ }
+
+ public void UpdateState(DeepFryerBoundUserInterfaceState state)
+ {
+ OilLevel.Value = (float) state.OilLevel;
+ OilPurity.Value = (float) state.OilPurity;
+
+ if (state.OilPurity < state.FryingOilThreshold)
+ {
+ if (OilPurity.ForegroundStyleBoxOverride == null)
+ {
+ OilPurity.ForegroundStyleBoxOverride = new StyleBoxFlat();
+
+ var oilPurityStyle = (StyleBoxFlat) OilPurity.ForegroundStyleBoxOverride;
+ oilPurityStyle.BackgroundColor = WarningColor;
+ }
+ }
+ else
+ {
+ OilPurity.ForegroundStyleBoxOverride = null;
+ }
+
+ ItemList.Clear();
+
+ foreach (var netEntity in state.ContainedEntities)
+ {
+ var entity = _entityManager.GetEntity(netEntity);
+ if (_entityManager.Deleted(entity))
+ continue;
+
+ // Duplicated from MicrowaveBoundUserInterface.cs: keep an eye on that file for when it changes.
+ Texture? texture;
+ if (_entityManager.TryGetComponent(entity, out IconComponent? iconComponent))
+ {
+ texture = _entityManager.System().GetIcon(iconComponent);
+ }
+ else if (_entityManager.TryGetComponent(entity, out SpriteComponent? spriteComponent))
+ {
+ texture = spriteComponent.Icon?.Default;
+ }
+ else
+ {
+ continue;
+ }
+
+ ItemList.AddItem(_entityManager.GetComponent(entity).EntityName, texture);
+ }
+ }
+ }
+}
diff --git a/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFriedVisualizer.cs b/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFriedVisualizer.cs
new file mode 100644
index 00000000000000..58580e3f5ac7a5
--- /dev/null
+++ b/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFriedVisualizer.cs
@@ -0,0 +1,73 @@
+using System.Linq;
+using Robust.Client.GameObjects;
+using static Robust.Client.GameObjects.SpriteComponent;
+using Content.Client.Kitchen.Components;
+using Content.Shared.Clothing;
+using Content.Shared.Hands;
+using Content.Shared.Kitchen.Components;
+
+namespace Content.Client.Kitchen.Visualizers
+{
+ public sealed class DeepFriedVisualizerSystem : VisualizerSystem
+ {
+ private readonly static string ShaderName = "Crispy";
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnHeldVisualsUpdated);
+ SubscribeLocalEvent(OnEquipmentVisualsUpdated);
+ }
+
+ protected override void OnAppearanceChange(EntityUid uid, DeepFriedComponent component, ref AppearanceChangeEvent args)
+ {
+ if (args.Sprite == null)
+ return;
+
+ if (!args.Component.TryGetData(DeepFriedVisuals.Fried, out bool isFried))
+ return;
+
+ for (var i = 0; i < args.Sprite.AllLayers.Count(); ++i)
+ args.Sprite.LayerSetShader(i, ShaderName);
+ }
+
+ private void OnHeldVisualsUpdated(EntityUid uid, DeepFriedComponent component, HeldVisualsUpdatedEvent args)
+ {
+ if (args.RevealedLayers.Count == 0)
+ {
+ return;
+ }
+
+ if (!TryComp(args.User, out SpriteComponent? sprite))
+ return;
+
+ foreach (var key in args.RevealedLayers)
+ {
+ if (!sprite.LayerMapTryGet(key, out var index) || sprite[index] is not Layer layer)
+ continue;
+
+ sprite.LayerSetShader(index, ShaderName);
+ }
+ }
+
+ private void OnEquipmentVisualsUpdated(EntityUid uid, DeepFriedComponent component, EquipmentVisualsUpdatedEvent args)
+ {
+ if (args.RevealedLayers.Count == 0)
+ {
+ return;
+ }
+
+ if (!TryComp(args.Equipee, out SpriteComponent? sprite))
+ return;
+
+ foreach (var key in args.RevealedLayers)
+ {
+ if (!sprite.LayerMapTryGet(key, out var index) || sprite[index] is not Layer layer)
+ continue;
+
+ sprite.LayerSetShader(index, ShaderName);
+ }
+ }
+ }
+}
diff --git a/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFryerVisualizer.cs b/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFryerVisualizer.cs
new file mode 100644
index 00000000000000..69f613fc110fd9
--- /dev/null
+++ b/Content.Client/Nyanotrasen/Kitchen/Visualizers/DeepFryerVisualizer.cs
@@ -0,0 +1,21 @@
+using Robust.Client.GameObjects;
+using Content.Client.Chemistry.Visualizers;
+using Content.Client.Kitchen.Components;
+using Content.Shared.Kitchen.Components;
+
+namespace Content.Client.Kitchen.Visualizers
+{
+ public sealed class DeepFryerVisualizerSystem : VisualizerSystem
+ {
+ protected override void OnAppearanceChange(EntityUid uid, DeepFryerComponent component, ref AppearanceChangeEvent args)
+ {
+ if (!args.Component.TryGetData(DeepFryerVisuals.Bubbling, out bool isBubbling) ||
+ !TryComp(uid, out var scvComponent))
+ {
+ return;
+ }
+
+ scvComponent.FillBaseName = isBubbling ? "on-" : "off-";
+ }
+ }
+}
diff --git a/Content.IntegrationTests/Tests/Nyanotrasen/DeepFryerTest.cs b/Content.IntegrationTests/Tests/Nyanotrasen/DeepFryerTest.cs
new file mode 100644
index 00000000000000..3e1a7ff52c8770
--- /dev/null
+++ b/Content.IntegrationTests/Tests/Nyanotrasen/DeepFryerTest.cs
@@ -0,0 +1,58 @@
+#nullable enable annotations
+using Content.Server.Kitchen.Components;
+using Content.Server.Kitchen.EntitySystems;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Reflection;
+
+namespace Content.IntegrationTests.Tests.DeepFryer
+{
+ [TestFixture]
+ [TestOf(typeof(DeepFriedComponent))]
+ [TestOf(typeof(DeepFryerSystem))]
+ [TestOf(typeof(DeepFryerComponent))]
+ public sealed class DeepFryerTest
+ {
+
+ [TestPrototypes]
+ private const string Prototypes = @"
+- type: entity
+ name: DeepFryerDummy
+ id: DeepFryerDummy
+ components:
+ - type: DeepFryer
+ entryDelay: 0
+ draggedEntryDelay: 0
+ flushTime: 0
+ - type: Anchorable
+ - type: ApcPowerReceiver
+ - type: Physics
+ bodyType: Static
+ - type: Fixtures
+ fixtures:
+ fix1:
+ shape:
+ !type:PhysShapeCircle
+ radius: 0.35
+";
+
+ [Test]
+ public async Task Test()
+ {
+ await using var pair = await PoolManager.GetServerClient();
+ var server = pair.Server;
+
+ var testMap = await pair.CreateTestMap();
+
+ EntityUid unitUid = default;
+
+ var entityManager = server.ResolveDependency();
+ var xformSystem = entityManager.System();
+ var deepFryerSystem = entityManager.System();
+ await server.WaitAssertion(() =>
+ {
+ Assert.That(deepFryerSystem, Is.Not.Null);
+ });
+ await pair.CleanReturnAsync();
+ }
+ }
+}
diff --git a/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs b/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs
index 81bba0eb79dc7b..e2e054f4fa00d2 100644
--- a/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs
+++ b/Content.Server/Nutrition/EntitySystems/SliceableFoodSystem.cs
@@ -90,6 +90,12 @@ private bool TrySliceFood(EntityUid uid, EntityUid user, EntityUid usedItem,
component.Count--;
+ //Nyano - Summary: Begin Nyano Code to tell us we've sliced something for Fryer --
+
+ var sliceEvent = new SliceFoodEvent(user, usedItem, uid, sliceUid);
+ RaiseLocalEvent(uid, sliceEvent);
+ //Nyano - End Nyano Code.
+
// If someone makes food proto with 1 slice...
if (component.Count < 1)
{
@@ -160,4 +166,40 @@ private void OnExamined(EntityUid uid, SliceableFoodComponent component, Examine
args.PushMarkup(Loc.GetString("sliceable-food-component-on-examine-remaining-slices-text", ("remainingCount", component.Count)));
}
}
+ //Nyano - Summary: Begin Nyano Code for the sliced food event.
+ public sealed class SliceFoodEvent : EntityEventArgs
+ {
+ ///
+ /// Who did the slicing?
+ ///
+ public EntityUid User;
+
+ ///
+ /// What did the slicing?
+ ///
+ public EntityUid Tool;
+
+ ///
+ /// What has been sliced?
+ ///
+ ///
+ /// This could soon be deleted if there was not enough food left to
+ /// continue slicing.
+ ///
+ public EntityUid Food;
+
+ ///
+ /// What is the slice?
+ ///
+ public EntityUid Slice;
+
+ public SliceFoodEvent(EntityUid user, EntityUid tool, EntityUid food, EntityUid slice)
+ {
+ User = user;
+ Tool = tool;
+ Food = food;
+ Slice = slice;
+ }
+ }
+ //End Nyano Code.
}
diff --git a/Content.Server/Nyanotrasen/Kitchen/Components/DeepFriedComponent.cs b/Content.Server/Nyanotrasen/Kitchen/Components/DeepFriedComponent.cs
new file mode 100644
index 00000000000000..3bbb96e65d0140
--- /dev/null
+++ b/Content.Server/Nyanotrasen/Kitchen/Components/DeepFriedComponent.cs
@@ -0,0 +1,23 @@
+using Content.Shared.Kitchen.Components;
+
+namespace Content.Server.Kitchen.Components
+{
+ [RegisterComponent]
+ //This line appears to be deprecated. [ComponentReference(typeof(SharedDeepFriedComponent))]
+ public sealed partial class DeepFriedComponent : SharedDeepFriedComponent
+ {
+ ///
+ /// What is the item's base price multiplied by?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("priceCoefficient")]
+ public float PriceCoefficient { get; set; } = 1.0f;
+
+ ///
+ /// What was the entity's original name before any modification?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("originalName")]
+ public string? OriginalName { get; set; }
+ }
+}
diff --git a/Content.Server/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs b/Content.Server/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs
new file mode 100644
index 00000000000000..07cc96e8e78c29
--- /dev/null
+++ b/Content.Server/Nyanotrasen/Kitchen/Components/DeepFryerComponent.cs
@@ -0,0 +1,240 @@
+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
+{
+ [RegisterComponent]
+ [Access(typeof(DeepFryerSystem))]
+ // This line appears to be depracted: [ComponentReference(typeof(SharedDeepFryerComponent))]
+ public sealed partial class DeepFryerComponent : SharedDeepFryerComponent
+ {
+ // There are three levels to how the deep fryer treats entities.
+ //
+ // 1. An entity can be rejected by the blacklist and be untouched by
+ // anything other than heat damage.
+ //
+ // 2. An entity can be deep-fried but not turned into an edible. The
+ // change will be mostly cosmetic. Any entity that does not match
+ // the blacklist will fall into this category.
+ //
+ // 3. An entity can be deep-fried and turned into something edible. The
+ // change will permit the item to be permanently destroyed by eating
+ // it.
+
+ ///
+ /// When will the deep fryer layer on the next stage of crispiness?
+ ///
+ [DataField("nextFryTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
+ public TimeSpan NextFryTime { get; set; }
+
+ ///
+ /// How much waste needs to be added at the next update interval?
+ ///
+ public FixedPoint2 WasteToAdd { get; set; } = FixedPoint2.Zero;
+
+ ///
+ /// How often are items in the deep fryer fried?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("fryInterval")]
+ public TimeSpan FryInterval { get; set; } = TimeSpan.FromSeconds(5);
+
+ ///
+ /// What entities cannot be deep-fried no matter what?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("blacklist")]
+ public EntityWhitelist? Blacklist { get; set; }
+
+ ///
+ /// What entities can be deep-fried into being edible?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("whitelist")]
+ public EntityWhitelist? Whitelist { get; set; }
+
+ ///
+ /// What are over-cooked and burned entities turned into?
+ ///
+ ///
+ /// To prevent unwanted destruction of items, only food can be turned
+ /// into this.
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("charredPrototype", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string? CharredPrototype { get; set; }
+
+ ///
+ /// What reagents are considered valid cooking oils?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("fryingOils", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))]
+ public HashSet FryingOils { get; set; } = new();
+
+ ///
+ /// What reagents are added to tasty deep-fried food?
+ /// JJ Comment: I removed Solution from this. Unsure if I need to replace it with something.
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("goodReagents")]
+ public List GoodReagents { get; set; } = new();
+
+ ///
+ /// What reagents are added to terrible deep-fried food?
+ /// JJ Comment: I removed Solution from this. Unsure if I need to replace it with something.
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("badReagents")]
+ public List BadReagents { get; set; } = new();
+
+ ///
+ /// What reagents replace every 1 unit of oil spent on frying?
+ /// JJ Comment: I removed Solution from this. Unsure if I need to replace it with something.
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("wasteReagents")]
+ public List WasteReagents { get; set; } = new();
+
+ ///
+ /// What flavors go well with deep frying?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("goodFlavors", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))]
+ public HashSet GoodFlavors { get; set; } = new();
+
+ ///
+ /// What flavors don't go well with deep frying?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("badFlavors", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))]
+ public HashSet BadFlavors { get; set; } = new();
+
+ ///
+ /// How much is the price coefficiency of a food changed for each good flavor?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("goodFlavorPriceBonus")]
+ public float GoodFlavorPriceBonus { get; set; } = 0.2f;
+
+ ///
+ /// How much is the price coefficiency of a food changed for each bad flavor?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("badFlavorPriceMalus")]
+ public float BadFlavorPriceMalus { get; set; } = -0.3f;
+
+ ///
+ /// What is the name of the solution container for the fryer's oil?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("solution")]
+ public string SolutionName { get; set; } = "vat_oil";
+
+ public Solution Solution { get; set; } = default!;
+
+ ///
+ /// What is the name of the entity container for items inside the deep fryer?
+ ///
+ [DataField("storage")]
+ public string StorageName { get; set; } = "vat_entities";
+
+ public BaseContainer Storage { get; set; } = default!;
+
+ ///
+ /// How much solution should be imparted based on an item's size?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("solutionSizeCoefficient")]
+ public FixedPoint2 SolutionSizeCoefficient { get; set; } = 0.5f;
+
+ ///
+ /// What's the maximum amount of solution that should ever be imparted?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("solutionSplitMax")]
+ public FixedPoint2 SolutionSplitMax { get; set; } = 10f;
+
+ ///
+ /// What percent of the fryer's solution has to be oil in order for it to fry?
+ ///
+ ///
+ /// The chef will have to clean it out occasionally, and if too much
+ /// non-oil reagents are added, the vat will have to be drained.
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("fryingOilThreshold")]
+ public FixedPoint2 FryingOilThreshold { get; set; } = 0.5f;
+
+ ///
+ /// What is the bare minimum number of oil units to prevent the fryer
+ /// from unsafe operation?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("safeOilVolume")]
+ public FixedPoint2 SafeOilVolume { get; set; } = 10f;
+
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("unsafeOilVolumeEffects")]
+ public List UnsafeOilVolumeEffects = new();
+
+ ///
+ /// What is the temperature of the vat when the deep fryer is powered?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("poweredTemperature")]
+ public float PoweredTemperature = 550.0f;
+
+ ///
+ /// How many entities can this deep fryer hold?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ public int StorageMaxEntities = 4;
+
+ ///
+ /// How many entities can be held, at a minimum?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("baseStorageMaxEntities")]
+ public int BaseStorageMaxEntities = 4;
+
+ ///
+ /// What upgradeable machine part dictates the quality of the storage size?
+ ///
+ [DataField("machinePartStorageMax", customTypeSerializer: typeof(PrototypeIdSerializer))]
+ public string MachinePartStorageMax = "MatterBin";
+
+ ///
+ /// How much extra storage is added per part rating?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("storagePerPartRating")]
+ public int StoragePerPartRating = 4;
+
+ ///
+ /// What sound is played when an item is inserted into hot oil?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("soundInsertItem")]
+ public SoundSpecifier SoundInsertItem = new SoundPathSpecifier("/Audio/Nyanotrasen/Machines/deepfryer_basket_add_item.ogg");
+
+ ///
+ /// What sound is played when an item is removed?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("soundRemoveItem")]
+ public SoundSpecifier SoundRemoveItem = new SoundPathSpecifier("/Audio/Nyanotrasen/Machines/deepfryer_basket_remove_item.ogg");
+ }
+}
diff --git a/Content.Server/Nyanotrasen/Kitchen/Components/ProfessionalChefComponent.cs b/Content.Server/Nyanotrasen/Kitchen/Components/ProfessionalChefComponent.cs
new file mode 100644
index 00000000000000..eb04f0d7b307c5
--- /dev/null
+++ b/Content.Server/Nyanotrasen/Kitchen/Components/ProfessionalChefComponent.cs
@@ -0,0 +1,5 @@
+namespace Content.Server.Kitchen.Components
+{
+ [RegisterComponent]
+ public sealed partial class ProfessionalChefComponent : Component {}
+}
diff --git a/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs
new file mode 100644
index 00000000000000..299df8b9b7cbe4
--- /dev/null
+++ b/Content.Server/Nyanotrasen/Kitchen/EntitySystems/DeepFryerSystem.cs
@@ -0,0 +1,1065 @@
+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.Miasma;
+using Content.Server.Audio;
+using Content.Server.Body.Components;
+using Content.Server.Cargo.Systems;
+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.Components;
+using Content.Server.Nutrition.EntitySystems;
+using Content.Server.Paper;
+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.Miasma;
+using Content.Shared.Buckle.Components;
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.Damage;
+using Content.Shared.Damage.Prototypes;
+using Content.Shared.Database;
+using Content.Shared.DoAfter;
+using Content.Shared.Destructible;
+using Content.Shared.Examine;
+using Content.Shared.FixedPoint;
+using Content.Shared.Hands.Components;
+using Content.Shared.Hands.EntitySystems;
+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.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;
+
+namespace Content.Server.Kitchen.EntitySystems
+{
+ 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!;
+
+ 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(uid, 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);
+
+ component.WasteToAdd = FixedPoint2.Zero;
+
+ _solutionContainerSystem.UpdateChemicals(uid, component.Solution, true);
+ }
+
+ UpdateUserInterface(uid, component);
+ }
+ }
+
+ 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.");
+ }
+
+ ///
+ /// 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;
+ }
+
+ 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;
+ }
+
+ ///
+ /// Make an item look deep-fried.
+ ///
+ private 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);
+ 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;
+ }
+
+ 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 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)
+ {
+ var oilVolume = FixedPoint2.Zero;
+
+ foreach (var reagent in component.Solution)
+ if (component.FryingOils.Contains(reagent.Reagent.ToString()))
+ oilVolume += reagent.Quantity;
+
+ 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)
+ {
+ wasteVolume += component.Solution.GetReagentQuantity(reagent.Reagent);
+ }
+
+ return wasteVolume;
+ }
+
+ ///
+ /// 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;
+ }
+
+ ///
+ /// 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))
+ {
+ // Push the temperature towards what it should be but no higher.
+ var delta = (component.PoweredTemperature - tempComp.CurrentTemperature) * tempComp.HeatCapacity;
+
+ if (delta > 0f)
+ _temperature.ChangeHeat(item, delta, false, tempComp);
+ }
+
+ if (TryComp(item, out var solutions))
+ {
+ foreach (var (_, solution) in solutions.Solutions)
+ {
+ _solutionContainerSystem.SetTemperature(item, solution, component.PoweredTemperature);
+ }
+ }
+
+ // 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.
+ }
+ }
+ }
+
+ ///
+ /// 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);
+ }
+ }
+
+ private void UpdateDeepFriedName(EntityUid uid, DeepFriedComponent component)
+ {
+ if (component.OriginalName == null)
+ return;
+
+ switch (component.Crispiness)
+ {
+ case 0:
+ // Already handled at OnInitDeepFried.
+ break;
+ case 1:
+ MetaData(uid).EntityName = Loc.GetString("deep-fried-fried-item",
+ ("entity", component.OriginalName));
+ break;
+ default:
+ MetaData(uid).EntityName = 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)
+ {
+ 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;
+
+ if (deepFriedComponent.Crispiness > MaximumCrispiness)
+ {
+ BurnItem(uid, component, item);
+ return;
+ }
+
+ UpdateDeepFriedName(item, deepFriedComponent);
+ return;
+ }
+
+ // Allow entity systems to conditionally forbid an attempt at deep-frying.
+ var attemptEvent = new DeepFryAttemptEvent(uid);
+ RaiseLocalEvent(item, attemptEvent);
+
+ if (attemptEvent.Cancelled)
+ return;
+
+ // 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))
+ {
+ _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);
+
+ // Determine how much solution to spend on this item.
+ var solutionQuantity = FixedPoint2.Min(
+ component.Solution.Volume,
+ itemComponent.Size * 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;
+ }
+
+ private void OnInitDeepFryer(EntityUid uid, DeepFryerComponent component, ComponentInit args)
+ {
+ 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)
+ {
+ 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 _);
+ }
+
+ private void OnDestruction(EntityUid uid, DeepFryerComponent component, DestructionEventArgs args)
+ {
+ _containerSystem.EmptyContainer(component.Storage, true);
+ }
+
+ private void OnRefreshParts(EntityUid uid, DeepFryerComponent component, RefreshPartsEvent args)
+ {
+ var ratingStorage = args.PartRatings[component.MachinePartStorageMax];
+
+ component.StorageMaxEntities = component.BaseStorageMaxEntities + (int) (component.StoragePerPartRating * (ratingStorage - 1));
+ }
+
+ ///
+ /// Allow thrown items to land in a basket.
+ ///
+ private void OnThrowHitBy(EntityUid uid, DeepFryerComponent component, ThrowHitByEvent args)
+ {
+ if (args.Handled)
+ return;
+
+ // Chefs never miss this. :)
+ float missChance = HasComp(args.User) ? 0f : ThrowMissChance;
+
+ 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 missed.");
+
+ return;
+ }
+
+ 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 (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);
+
+ args.Handled = true;
+ }
+
+ private void OnSolutionChange(EntityUid uid, DeepFryerComponent component, SolutionChangedEvent args)
+ {
+ UpdateUserInterface(uid, component);
+ UpdateAmbientSound(uid, component);
+ }
+
+ 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);
+ }
+
+ 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 OnBeforeActivatableUIOpen(EntityUid uid, DeepFryerComponent component, BeforeActivatableUIOpenEvent args)
+ {
+ UpdateUserInterface(uid, 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)}.");
+ }
+
+ _audioSystem.PlayPvs(component.SoundRemoveItem, uid, AudioParamsInsertRemove);
+
+ UpdateUserInterface(component.Owner, component);
+ }
+ }
+
+ 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);
+ }
+
+ ///
+ /// 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;
+
+ if (!TryComp(user, out var handsComponent))
+ return false;
+
+ heldItem = handsComponent.ActiveHandEntity;
+
+ 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);
+
+ return false;
+ }
+
+ solution = refillableSolution;
+ transferAmount = solutionTransferComponent.TransferAmount;
+
+ return true;
+ }
+
+ private void OnScoopVat(EntityUid uid, DeepFryerComponent component, DeepFryerScoopVatMessage args)
+ {
+ var user = args.Session.AttachedEntity;
+
+ if (user == null ||
+ !TryGetActiveHandSolutionContainer(uid, user.Value, out var heldItem, out var heldSolution, out var transferAmount))
+ {
+ return;
+ }
+
+ _solutionTransferSystem.Transfer(user.Value,
+ uid,
+ component.Solution,
+ heldItem.Value,
+ heldSolution,
+ transferAmount);
+
+ // UI update is not necessary here, because the solution change event handles it.
+ }
+
+ 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;
+ }
+
+ var wasteVolume = GetWasteVolume(uid, component);
+ if (wasteVolume == FixedPoint2.Zero)
+ {
+ _popupSystem.PopupEntity(
+ Loc.GetString("deep-fryer-oil-no-slag"),
+ uid,
+ user.Value);
+
+ return;
+ }
+
+ var delay = Math.Clamp((float) wasteVolume * 0.1f, 1f, 5f);
+
+ var ev = new ClearSlagDoAfterEvent(heldSolution, transferAmount);
+
+ //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);
+ }
+
+ private void OnRemoveAllItems(EntityUid uid, DeepFryerComponent component, DeepFryerRemoveAllItemsMessage args)
+ {
+ if (component.Storage.ContainedEntities.Count == 0)
+ return;
+
+ _containerSystem.EmptyContainer(component.Storage, false);
+
+ var user = args.Session.AttachedEntity;
+
+ if (user != null)
+ _adminLogManager.Add(LogType.Action, LogImpact.Low,
+ $"{ToPrettyString(user.Value)} removed all items from {ToPrettyString(uid)}.");
+
+ _audioSystem.PlayPvs(component.SoundRemoveItem, uid, AudioParamsInsertRemove);
+
+ UpdateUserInterface(component.Owner, component);
+ }
+
+ private void OnClearSlag(EntityUid uid, DeepFryerComponent component, ClearSlagDoAfterEvent args)
+ {
+ if (args.Handled || args.Cancelled || args.Args.Used == null)
+ return;
+
+ FixedPoint2 reagentCount = component.WasteReagents.Count();
+
+ 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(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;
+ meta.EntityName = 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 OnPriceCalculation(EntityUid uid, DeepFriedComponent component, ref PriceCalculationEvent args)
+ {
+ args.Price *= component.PriceCoefficient;
+ }
+
+ 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);
+
+ sliceDeepFriedComponent.Crispiness = sourceDeepFriedComponent.Crispiness;
+ sliceDeepFriedComponent.PriceCoefficient = sourceDeepFriedComponent.PriceCoefficient;
+
+ 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);
+ }
+ }
+ }
+
+
+ public sealed class DeepFryAttemptEvent : CancellableEntityEventArgs
+ {
+ public EntityUid DeepFryer { get; }
+
+ public DeepFryAttemptEvent(EntityUid deepFryer)
+ {
+ DeepFryer = deepFryer;
+ }
+ }
+
+ 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;
+ }
+ }
+}
diff --git a/Content.Shared/Nyanotrasen/Kitchen/ClearSlagDoAfterEvent.cs b/Content.Shared/Nyanotrasen/Kitchen/ClearSlagDoAfterEvent.cs
new file mode 100644
index 00000000000000..e4ecb88d9a4520
--- /dev/null
+++ b/Content.Shared/Nyanotrasen/Kitchen/ClearSlagDoAfterEvent.cs
@@ -0,0 +1,29 @@
+using Robust.Shared.Serialization;
+using Content.Shared.Chemistry.Components;
+using Content.Shared.DoAfter;
+using Content.Shared.FixedPoint;
+
+namespace Content.Shared.Kitchen
+{
+ [Serializable, NetSerializable]
+ public sealed partial class ClearSlagDoAfterEvent : DoAfterEvent
+ {
+ [DataField("solution", required: true)]
+ public Solution Solution = default!;
+
+ [DataField("amount", required: true)]
+ public FixedPoint2 Amount;
+
+ private ClearSlagDoAfterEvent()
+ {
+ }
+
+ public ClearSlagDoAfterEvent(Solution solution, FixedPoint2 amount)
+ {
+ Solution = solution;
+ Amount = amount;
+ }
+
+ public override DoAfterEvent Clone() => this;
+ }
+}
diff --git a/Content.Shared/Nyanotrasen/Kitchen/Components/SharedDeepFriedComponent.cs b/Content.Shared/Nyanotrasen/Kitchen/Components/SharedDeepFriedComponent.cs
new file mode 100644
index 00000000000000..51eca172981857
--- /dev/null
+++ b/Content.Shared/Nyanotrasen/Kitchen/Components/SharedDeepFriedComponent.cs
@@ -0,0 +1,22 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Kitchen.Components
+{
+ [NetworkedComponent]
+ public abstract partial class SharedDeepFriedComponent : Component
+ {
+ ///
+ /// How deep-fried is this item?
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("crispiness")]
+ public int Crispiness { get; set; }
+ }
+
+ [Serializable, NetSerializable]
+ public enum DeepFriedVisuals : byte
+ {
+ Fried,
+ }
+}
diff --git a/Content.Shared/Nyanotrasen/Kitchen/Components/SharedDeepFryerComponent.cs b/Content.Shared/Nyanotrasen/Kitchen/Components/SharedDeepFryerComponent.cs
new file mode 100644
index 00000000000000..da7319869d9a76
--- /dev/null
+++ b/Content.Shared/Nyanotrasen/Kitchen/Components/SharedDeepFryerComponent.cs
@@ -0,0 +1,12 @@
+using Robust.Shared.Serialization;
+
+namespace Content.Shared.Kitchen.Components
+{
+ public abstract partial class SharedDeepFryerComponent : Component { }
+
+ [Serializable, NetSerializable]
+ public enum DeepFryerVisuals : byte
+ {
+ Bubbling,
+ }
+}
diff --git a/Content.Shared/Nyanotrasen/Kitchen/UI/DeepFryerMessages.cs b/Content.Shared/Nyanotrasen/Kitchen/UI/DeepFryerMessages.cs
new file mode 100644
index 00000000000000..fa1df8a71e8d4c
--- /dev/null
+++ b/Content.Shared/Nyanotrasen/Kitchen/UI/DeepFryerMessages.cs
@@ -0,0 +1,67 @@
+using Robust.Shared.Serialization;
+using Content.Shared.FixedPoint;
+
+namespace Content.Shared.Kitchen.UI
+{
+ [Serializable, NetSerializable]
+ public sealed class DeepFryerBoundUserInterfaceState : BoundUserInterfaceState
+ {
+ public readonly FixedPoint2 OilLevel;
+ public readonly FixedPoint2 OilPurity;
+ public readonly FixedPoint2 FryingOilThreshold;
+ public readonly NetEntity[] ContainedEntities;
+
+ public DeepFryerBoundUserInterfaceState(
+ FixedPoint2 oilLevel,
+ FixedPoint2 oilPurity,
+ FixedPoint2 fryingOilThreshold,
+ NetEntity[] containedEntities)
+ {
+ OilLevel = oilLevel;
+ OilPurity = oilPurity;
+ FryingOilThreshold = fryingOilThreshold;
+ ContainedEntities = containedEntities;
+ }
+ }
+
+ [Serializable, NetSerializable]
+ public sealed class DeepFryerRemoveItemMessage : BoundUserInterfaceMessage
+ {
+ public readonly NetEntity Item;
+
+ public DeepFryerRemoveItemMessage(NetEntity item)
+ {
+ Item = item;
+ }
+ }
+
+ [Serializable, NetSerializable]
+ public sealed class DeepFryerInsertItemMessage : BoundUserInterfaceMessage
+ {
+ public DeepFryerInsertItemMessage() { }
+ }
+
+ [Serializable, NetSerializable]
+ public sealed class DeepFryerScoopVatMessage : BoundUserInterfaceMessage
+ {
+ public DeepFryerScoopVatMessage() { }
+ }
+
+ [Serializable, NetSerializable]
+ public sealed class DeepFryerClearSlagMessage : BoundUserInterfaceMessage
+ {
+ public DeepFryerClearSlagMessage() { }
+ }
+
+ [Serializable, NetSerializable]
+ public sealed class DeepFryerRemoveAllItemsMessage : BoundUserInterfaceMessage
+ {
+ public DeepFryerRemoveAllItemsMessage() { }
+ }
+
+ [NetSerializable, Serializable]
+ public enum DeepFryerUiKey
+ {
+ Key
+ }
+}
diff --git a/Content.Shared/Throwing/ThrowEvents.cs b/Content.Shared/Throwing/ThrowEvents.cs
index fbda80b8ca9d35..5ea78b862eda38 100644
--- a/Content.Shared/Throwing/ThrowEvents.cs
+++ b/Content.Shared/Throwing/ThrowEvents.cs
@@ -5,12 +5,19 @@ namespace Content.Shared.Throwing
///
public abstract class ThrowEvent : HandledEntityEventArgs
{
+ ///Nyano - Summary: Allows us to tell who threw the item. It matters!
+ ///
+ /// The entity that threw .
+ ///
+ public EntityUid? User { get; }
+ // End Nyano code.
public readonly EntityUid Thrown;
public readonly EntityUid Target;
public ThrownItemComponent Component;
- public ThrowEvent(EntityUid thrown, EntityUid target, ThrownItemComponent component)
+ public ThrowEvent(EntityUid? user, EntityUid thrown, EntityUid target, ThrownItemComponent component) //Nyano - Summary: User added.
{
+ User = user; //Nyano - Summary: User added.
Thrown = thrown;
Target = target;
Component = component;
@@ -22,7 +29,7 @@ public ThrowEvent(EntityUid thrown, EntityUid target, ThrownItemComponent compon
///
public sealed class ThrowHitByEvent : ThrowEvent
{
- public ThrowHitByEvent(EntityUid thrown, EntityUid target, ThrownItemComponent component) : base(thrown, target, component)
+ public ThrowHitByEvent(EntityUid? user, EntityUid thrown, EntityUid target, ThrownItemComponent component) : base(user, thrown, target, component) //Nyano - Summary: User added.
{
}
}
@@ -32,7 +39,7 @@ public ThrowHitByEvent(EntityUid thrown, EntityUid target, ThrownItemComponent c
///
public sealed class ThrowDoHitEvent : ThrowEvent
{
- public ThrowDoHitEvent(EntityUid thrown, EntityUid target, ThrownItemComponent component) : base(thrown, target, component)
+ public ThrowDoHitEvent(EntityUid thrown, EntityUid target, ThrownItemComponent component) : base(null, thrown, target, component) //Nyano - Summary: User added.
{
}
}
diff --git a/Content.Shared/Throwing/ThrownItemSystem.cs b/Content.Shared/Throwing/ThrownItemSystem.cs
index d7856543e4ac3d..073252eb927151 100644
--- a/Content.Shared/Throwing/ThrownItemSystem.cs
+++ b/Content.Shared/Throwing/ThrownItemSystem.cs
@@ -140,7 +140,10 @@ public void ThrowCollideInteraction(ThrownItemComponent component, EntityUid thr
_adminLogger.Add(LogType.ThrowHit, LogImpact.Low,
$"{ToPrettyString(thrown):thrown} thrown by {ToPrettyString(component.Thrower.Value):thrower} hit {ToPrettyString(target):target}.");
- RaiseLocalEvent(target, new ThrowHitByEvent(thrown, target, component), true);
+ if (component.Thrower is not null)// Nyano - Summary: Gotta check if there was a thrower.
+ RaiseLocalEvent(target, new ThrowHitByEvent(component.Thrower.Value, thrown, target, component), true); // Nyano - Summary: Gotta update for who threw it.
+ else
+ RaiseLocalEvent(target, new ThrowHitByEvent(null, thrown, target, component), true); // Nyano - Summary: No thrower.
RaiseLocalEvent(thrown, new ThrowDoHitEvent(thrown, target, component), true);
}
diff --git a/Resources/Audio/Nyanotrasen/Machines/attributions.yml b/Resources/Audio/Nyanotrasen/Machines/attributions.yml
index a3cbc35bb7ecee..e0d8a8f7ea2e8c 100644
--- a/Resources/Audio/Nyanotrasen/Machines/attributions.yml
+++ b/Resources/Audio/Nyanotrasen/Machines/attributions.yml
@@ -5,3 +5,12 @@
copyright: "https://freesound.org/people/soundmary/"
source: "https://freesound.org/people/soundmary/sounds/194994/"
+- files: ["deepfryer_basket_remove_item.ogg"]
+ license: "CC0-1.0"
+ copyright: "637625__kyles__kitchen-restaurant-fries-deep-fryer-basket-pickup-put-down-clack-metal.flac by kyles. This is a cleaned-up clip converted to OGG."
+ source: "https://freesound.org/people/kyles/sounds/637625/"
+
+- files: ["deepfryer_basket_add_item.ogg"]
+ license: "CC0-1.0"
+ copyright: "440587__ursenfuns__deep-fryer.wav by ursenfuns. This is a cleaned and shortened clip converted to OGG."
+ source: "https://freesound.org/people/ursenfuns/sounds/440587/"
diff --git a/Resources/Audio/Nyanotrasen/Machines/deepfryer_basket_add_item.ogg b/Resources/Audio/Nyanotrasen/Machines/deepfryer_basket_add_item.ogg
new file mode 100644
index 0000000000000000000000000000000000000000..b936b52df6b432715fa1680064a8e21c1615e916
GIT binary patch
literal 38471
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