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);
}
}