using System.Linq; using Content.Shared.Kitchen.Components; using Content.Shared.Random.Helpers; namespace Content.Shared.Kitchen.EntitySystems; public abstract partial class MicrowaveSystem { /// /// Adds temperature to every item in the microwave based on the time it took to microwave. /// /// The microwave entity. /// The heating time that has elapsed, in seconds. private void AddTemperature(Entity ent, float time) { var component = ent.Comp; var heatToAdd = time * component.DeltaHeat; var objHeatToAdd = heatToAdd * component.ObjectHeatMultiplier; // TODO: Divide heat amongst contents? foreach (var entity in GetMicrowaveContents(ent.AsNullable())) { _temperature.ChangeHeat(entity, objHeatToAdd); foreach (var (_, soln) in Solution.EnumerateSolutions(entity)) { var solution = soln.Comp.Solution; if (solution.Temperature > component.TemperatureUpperThreshold) continue; Solution.AddThermalEnergy(soln, heatToAdd); } } } /// /// Attempts to roll random "malfunction" events on a malfunctioning microwave. /// protected virtual void RollMalfunction(Entity ent) { if (SharedRandomExtensions.PredictedProb(Timing, ent.Comp.ExplosionChance, GetNetEntity(ent))) Explode(ent.AsNullable()); } public virtual void Explode(Entity ent) { if (!MicrowaveQuery.Resolve(ent.Owner, ref ent.Comp)) return; ent.Comp.Broken = true; DirtyField(ent.AsNullable(), nameof(ent.Comp.Broken)); } /// /// Start up the microwave. This processes each item in the microwave to experience microwave "on-cooking" /// effects, attempts to retrieve a microwave recipe with its valid ingredients, and activates the /// microwave visually. /// public void StartCooking(Entity microwave, EntityUid? user) { if (!HasContents(microwave.AsNullable()) || IsActiveMicrowave(microwave.AsNullable()) || !_power.IsPowered(microwave.Owner)) return; var contents = GetMicrowaveContents(microwave.AsNullable()); var malfunctioning = false; var shouldContinue = TryStartMicrowavingContents(microwave, contents, user, ref malfunctioning, out var ingredientContents); if (!shouldContinue) return; var ingredients = GetTotalIngredients(ingredientContents); var cookTime = microwave.Comp.CurrentCookTimerTime; var recipe = GetRecipe(microwave, ingredients, cookTime); ActivateMicrowave(microwave, recipe, malfunctioning, user); } /// /// Starts up the microwave cooking operation, setting the starting time and recipe of the microwave. /// /// The microwave entity. /// The recipe and portion count associated with this operaton. /// Whether or not this microwave is malfunctioning. /// The entity that is activating the microwave. private void ActivateMicrowave(Entity microwave, PortionedRecipe? recipe, bool malfunctioning, EntityUid? user) { var uid = microwave.Owner; var component = microwave.Comp; var curTime = Timing.CurTime; var cookTime = component.CurrentCookTimerTime * component.CookTimeMultiplier; Audio.PlayPredicted(component.BeginCookingSound, uid, user); var activeComp = new ActiveMicrowaveComponent { PortionedRecipe = recipe, TotalTime = component.CurrentCookTimerTime, CookTimeEnd = curTime + TimeSpan.FromSeconds(cookTime), Malfunctioning = malfunctioning, NextMalfunction = malfunctioning ? curTime + component.MalfunctionInterval : TimeSpan.Zero, }; AddComp(uid, activeComp); UpdateUI(microwave.AsNullable()); } /// /// Iterates over the contents of a microwave and performs some on-microwaved effects. /// Plastic items will melt and turn into burned messes. /// Metal items will cause the microwave to malfunction. /// /// /// This also raises a BeingMicrowavedEvent on each item in the microwave. The result of /// this event may cause us to exit early and proceed with cooking - for example, an /// entity that causes the microwave to explode when microwaved. /// /// If cooking is not cancelled, the items inside will gain `ActiveMicrowaveComponent.`. /// /// The microwave entity. /// The contents of the microwave. /// The entity that is activating the microwave. /// Whether or not the microwave is malfunctioning. /// A list of entities usable as ingredients. /// Wheteher or not we can proceed to use the microwave. private bool TryStartMicrowavingContents(Entity microwave, IReadOnlyList contents, EntityUid? user, ref bool malfunctioning, out List ingredientContents) { ingredientContents = contents.ToList(); foreach (var item in contents) { MicrowaveItem(microwave, item, user, ref malfunctioning, out var shouldExit, out var shouldRemoveFromIngredients); if (shouldExit) return false; if (shouldRemoveFromIngredients) ingredientContents.Remove(item); } foreach (var item in ingredientContents) { BeginActivelyMicrowaving(item, microwave.Owner); } return true; } /// /// Processes a single entity in the microwave. This modifies / results in three /// different parameters related to the operation: if the microwave should malfunction, /// if the microwave should stop, and if the entity is still usable as an ingredient. /// /// The microwave entity. /// The entity being microwaved. /// The entity activating the microwave. /// Whether or not this microwave is malfunctioning. /// Whether or not this microwave should stop. /// Whether or not this entity remains a valid ingredient. private void MicrowaveItem(Entity microwave, EntityUid item, EntityUid? user, ref bool malfunctioning, out bool shouldStopMicrowave, out bool shouldRemoveFromIngredients) { shouldStopMicrowave = false; shouldRemoveFromIngredients = false; // Special item-in-microwave interactions. Certain "being microwaved' interactions // may cancel out any actual cooking, so this may early exit. var beingMicrowaved = new BeingMicrowavedEvent(microwave.Owner, user); RaiseLocalEvent(item, beingMicrowaved); if (beingMicrowaved.Handled) { UpdateUI(microwave.AsNullable()); shouldStopMicrowave = true; return; } if (_whitelist.IsWhitelistPass(microwave.Comp.MalfunctionWhenCookedWhitelist, item)) malfunctioning = true; if (_whitelist.IsWhitelistPass(microwave.Comp.BurnWhenCookedWhitelist, item)) { shouldRemoveFromIngredients = true; CreateBurnedMess(microwave, item); } } /// /// Turns a single entity in the microwave into a failed "burned mess" recipe. /// /// /// This happens to entities that pass /// when microwaved. /// /// The microwave entity. /// The entity to burn. private void CreateBurnedMess(Entity microwave, EntityUid item) { var entProto = microwave.Comp.FailureResult; var coords = Transform(microwave).Coordinates; var junk = PredictedSpawnAtPosition(entProto, coords); Container.Insert(junk, microwave.Comp.Storage); PredictedDel(item); } /// /// Completes a recipe in a microwave, removing its relevant ingredient contents /// from the microwave and producing finished dish entities in their place. /// /// The microwave entity. /// The recipe we are using. private void SpawnFinishedRecipe(Entity microwave, PortionedRecipe portionedRecipe) { SubtractContents(microwave, portionedRecipe); var coords = Transform(microwave).Coordinates; for (var i = 0; i < portionedRecipe.Count; i++) { if (!ProtoMan.Resolve(portionedRecipe.Recipe, out var recipe)) continue; PredictedSpawnAtPosition(recipe.Result, coords); } } /// /// Finishes a cooking operation in the microwave, resulting in a finished food recipe, /// the ejection of all remaining ingredients, and a sound cue. /// /// The micorawve entity. private void CompleteCooking(Entity ent) { var active = ent.Comp1; var microwave = ent.Comp2; var microwaveEnt = (ent.Owner, microwave); // Spawn a finished recipe, if there is one. if (active.PortionedRecipe != null) SpawnFinishedRecipe(microwaveEnt, active.PortionedRecipe.Value); // TODO: Audio prediction sucks. Predict this properly when audio prediction is less broken // https://github.com/space-wizards/RobustToolbox/issues/6436 if (_net.IsServer) Audio.PlayPvs(microwave.FoodDoneSound, ent); // beep... beep... beep // Clean up the microwave. Container.EmptyContainer(microwave.Storage); StopCooking(microwaveEnt); } /// /// Removes components from a microwave and its contents related to active microwave use. /// /// /// When the ActiveMicrowaveComponent is removed, it will trigger on shutdown. /// /// The microwave entity. private void StopCooking(Entity ent) { RemComp(ent); } }