using System.Text; using Content.Server.Atmos.Components; using Content.Server.Body.Components; using Content.Server.Ghost.Roles.Components; using Content.Server.Nutrition.Components; using Content.Server.Nyanotrasen.Kitchen.Components; using Content.Shared.Atmos.Rotting; using Content.Shared.Body.Components; 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.Prototypes; using Content.Shared.Paper; using Robust.Shared.Prototypes; 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, ProtoId crispiness) // Frontier: add CrispinessLevelSetPrototype { EnsureComp(item); // Frontier: apply the fryer-appropriate shader var deepFried = EnsureComp(item); var oldCrispinessProto = deepFried.CrispinessLevelSet; SetDeepFriedCrispinessLevelSet(item, deepFried, crispiness); if (_prototypeManager.TryIndex(oldCrispinessProto, out var oldCrispiness)) { _appearanceSystem.SetData(item, oldCrispiness.Visual, false); } if (_prototypeManager.TryIndex(crispiness, out var newCrispiness)) { _appearanceSystem.SetData(item, newCrispiness.Visual, true); } // End Frontier } /// /// 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); if (!_solutionContainerSystem.EnsureSolutionEntity(mob, mobFoodComponent.Solution, out var alreadyHadFood, out var mobFood)) return false; var mobFoodSolution = mobFood.Value.Comp.Solution; // 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) mobFoodSolution.RemoveAllSolution(); 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; mobFoodSolution.RemoveReagent("Blood", FixedPoint2.MaxValue); var bloodRemoved = solPresent - bloodSolution.Value.Comp.Solution.Volume; var proteinQuantity = bloodRemoved * BloodToProteinRatio; mobFoodSolution.MaxVolume += proteinQuantity; mobFoodSolution.AddReagent("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.MaxVolume += bloodstreamComponent.ChemicalSolution.Value.Comp.Solution.Volume; mobFoodSolution.AddSolution(bloodstreamComponent.ChemicalSolution.Value.Comp.Solution, _prototypeManager); } _solutionContainerSystem.UpdateChemicals(mobFood.Value); 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. if (!_solutionContainerSystem.EnsureSolutionEntity(item, foodComponent.Solution, out var foodEnt)) return; var foodSolution = foodEnt.Value.Comp.Solution; // 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; foodSolution.AddSolution(component.Solution.SplitSolution(solutionQuantity), _prototypeManager); foodSolution.AddSolution(extraSolution, _prototypeManager); _solutionContainerSystem.UpdateChemicals(foodEnt.Value); } }