mirror of
https://github.com/DeltaV-Station/Delta-v.git
synced 2026-09-30 08:07:28 +00:00
* Fix the sensor monitoring console forcing a GC every 3 seconds (#38146) * Optimize sensor monitoring window graph drawing * Add shared static Vector2 pool for all GraphView instances * Address requested changes * remove lock * Update submodule to 264.0.0 (#38629) * Toy/Plushie Inhands and Wearables (#38514) * Fixed dependency injection and some other issues in a few places. * More issue fixes * patchfor broken NetworkConfiguratorLinkMenu (#38632) fix * Fix solutions flickering when transferring contents (#34838) * Use Solution clones when applying SolutionComponent states * Revert "Use Solution clones when applying SolutionComponent states" This reverts commit 013fd111cf92b22562e00f98a7aaa49bc4b4ed62. * Make Solution implement ICloneable and rename Clone method. * Copy CanReact value when cloning a Solution * Convert to IRobustCloneable * Fix typos in guidebook: Buisness → Business (#38636) * Fix typo in `MinorAntagonists.xml` Buisness → Business * Fix typo in `YourFirstCharacter.xml` Buisness → Business * Various Headphones Fixes and Tweaks (#38479) * sprites, wearables * neck * icon-on sprite * Cleanup prototype instantiation in `DamageTest` (#38639) Cleanup prototype instantiation in DamageTest * Cleanup prototype instantiation in `ExplosionSystem` (#38642) Cleanup prototype instantiation in ExplosionSystem * AddBodyPartCommand localization. (#38612) commit * fix ItemSlotsSystem debug assert (#38655) * Allow the Command & Super door remotes to use the access of their user. (Re-creation of PR due to changes to game balance) (#35536) * Added directional beacons (#38284) * Added directional beacons Signed-off-by: Nox38 <nebulousnox38@gmail.com> * Fixed names Signed-off-by: Nox38 <nebulousnox38@gmail.com> --------- Signed-off-by: Nox38 <nebulousnox38@gmail.com> * Power stat and nuke codes commands get some LEC love. (#38585) * commit * requested changes. * Dsay Dirty and Follow commands converted to LEC and localized. (#38666) * commit * whoopwhoopwhoop * Retro laser sprite fix (#38676) * Fixed everything except the icon Signed-off-by: Nox38 <nebulousnox38@gmail.com> * fixed icon Signed-off-by: Nox38 <nebulousnox38@gmail.com> --------- Signed-off-by: Nox38 <nebulousnox38@gmail.com> * fix water coolers (#38681) * Monochromacy typo fix (#38686) * fixes the typo * Fixed cloning looking for the trait, not the component, RE https://github.com/space-wizards/space-station-14/pull/38686#issuecomment-3025093504 * Apply suggestions from code review --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Validate `CloningSettingsPrototype`s (#38688) * Validate CloningSettingsPrototypes * Update Content.IntegrationTests/Tests/Cloning/CloningSettingsPrototypeTest.cs Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Check EventComponents too --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Add test of objective-related console commands (#36400) * Add test of objective add/list/remove commands * Not sure why we're validating test prototypes, but sure * We don't need a map * Improvements and fixups for New Status Effect API (#38660) * Predict healing and bloodstream (#38690) * initial commit * reapply 38126 * fix rootable * someone missed an important minus sign here * try this * fix * fix * reenable crit hits * cleanup * fix status time dirtying * fix * camelCase * Healing and bloodstream prediction fixes. * Resolving Wizard casting recall on nuke disk making it impossible to disarm (#38661) * Resolving Wizard Recall on Nuke disk making it impossible to disarm - Adding a DisarmBomb case to nuke status update loop - Changing a few methods and parameters to properly follow formatting standards - Updating some names to follow camelCase * Updating missed tag * Reverting DataField change Should prevent this preventative bugfix being a breaking change. * Switch HSV to the default colorspace for character customization (#38434) * Made HSV default for character editor * Adds/fixes comments to HSV defaulting * Added dropbox fix, potentially cursed * Revert "Added dropbox fix, potentially cursed" This reverts commit a709883366fbee813e839742125e70844672af29. --------- Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> * Fix: Don't deploy foldables when clicking on items inside containers (#38709) * Fix * Apply suggestions from code review --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Pressure Relief Valve (#36708) * initial system (this math is probably WRONG) * General code cleanup and OnExamined support (holy moly this code sucks) * UICode and related events foundation TODO: - Actually write the XAML UI and the underlying system - Un-shitcode the entire thing - Actually test everything... * Working UI code TODO: Make predicted, as this certainly isn't predicted. Even though I said it was. It isn't. * Remove one TODO for unshitcoding the examine code * Add reminder yea * Make predicted (defenitely isn't) (also defenitely isn't a copypaste from pressure pump code) * It's predicted! TODO: - Give it snazzy predicted visuals! - Have a different field for pressure entry, lest it gets bulldozed every UI update. * Improve gas pressure relief valve UI TODO: Reminder to reduce amount of dirties using deltafields * Implement DirtyField prediction * Entity<T> cleanup A lot of Entity<T> conversions and lukewarm cleanup. Also got caught copy pasting code in 4K UHD but it's not like you couldn't tell. * More cleanup and comments * Remove TODO comment on bulldozing window title * """refactoring""" - Move appearance out of shared and finally fix it. Pointless to predict appearance in this instance. - More Entity<T> conversions because I like them. - Move UI creation handling over entirely to the ActivatableUI system. - Fix a hardcoded locale string (why????). * Add visuals * Revert debugging variable replacememt yea * Revert skissue * Remove unused using directives and remove TODO * Localize, cleanup, document * Fix adminlogging discrepancy * Add ability to construct, add guidebook entry * Clear up comment * Add guidebook tooltip to valve * Convert GasPressureReliefValveBoundUserInterface declaration into primary constructor * Adds more input handling and adds autofill on open * Un-deepfry input validator shitcode Genuinely what was I smoking * improve visuals logic * Refactor again - Update math to the correct implementation - Moved code that could be re-used in the future into a helper method under AtmosphereSystem.Gases.cs * I'm sorry but I hate warnings * Remove unused using directive in AtmosphereSystem.Gases.cs * Review and cleanup * Lukewarm UI glossup * Maintainer for the upstream project btw * Remove redundant state sets and messy logic * Unduplicate valve updater code * Redo UI (im sorry Slarti) * run tests * Test refactored UI messaging * Second round of UI improvements - God please find a way to improve this system. Feels bad. * Update loop implementation * Further predict UI * Clear up SetToCurrentThreshold * cleanup * Update to master + pipe layers and bug fixes want to run tests * fixes * Deploy rename pipebomb * Documentation and requested changes * Rename the method that wiggled away * Undo rounding changes * Fix comment * Rename and cleanup * Apply suggestions from code review --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Fix TextLinkTag (#32203) * Adjust uplink buy button to be under item icon (#38596) * Adjusted uplink buy button to be under item icon * Put the discount subtext under the icon * Indent fixes, added margin --------- Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> * Scurrets - Audio Improvements (#38482) * Scurret audio tuning * Add new sfx * Update sneezing sfx * YAML support * Rename a folder * make telesci wreck easier (#37569) rel * Validate `ShaderPrototype` IDs (#38728) * Convert all shader prototype string literals to protoids in overlays * Convert more shader prototype literal strings to protoids * Convert ValidatePrototypeId to ProtoId * Later * [BUGFIX] Fixed revenant malfunction ability not working properly only MediBots and Stasis bed (#38664) * fixed * clean up * orks fix smart * review fix 1 * more requested changes * less cursed * more descriptive description * better wording * Tiny Tiny Cleanup of the EyeClosingSystem. (#38734) Update EyeClosingSystem.cs * Fix human skin tone distribution (#38701) * fix: Use PredictedQueueDel for gib spell (#38729) * fix: don't default to uncharged sprite state for cells (#38730) * Fix Hristov description - remove inaccurate technical specs (#38746) - Removes inaccurate 'armor piercing 14.5mm shells' reference - Replaces it with a more funny description, matching the style of the other snipers and guns - Keeps ammunition type '.60 anti-materiel ammo' specification - Fixes issue #38590 Co-authored-by: Arthur Fiorese de Andrade <aandrade@cmcxs.gov.br> * feat: allow mopping evaporating puddles (#38743) * Validate remaining `ProtoId` strings (#38747) Validate remaining ProtoId strings * Validate `ProtoId`s in tests (#38745) * Convert string literals to protoids in Content.Tests * Convert string literals to protoids or consts in Content.IntegrationTests * Fix linter failures Tricksy static using misled me * Cleanup warning in StomachSystem (#38748) you did not see this * Vox scars (#38592) * Added vox scars n'stuff, renamed vox_tattoos.ftl to just vox.ftl * Revert "Added vox scars n'stuff, renamed vox_tattoos.ftl to just vox.ftl" This reverts commit c73da55ba3b39ddf93b493aecd85604c54dd8a15. * locale key fix * Changed top surgery scar names to be more generalized * Adjusted face scars * Formatting fixes --------- Co-authored-by: TrixxedHeart <46364955+TrixxedBit@users.noreply.github.com> * UnlockNode command to LEC. (#38751) * commit * Update UnlockNodeCommand.cs * commit * move command locale to its own file. * Update Content.Server/Xenoarchaeology/Artifact/UnlockNodeCommand.cs --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Typofixes for figurine dialogue (#38737) * Typofixes for figurine dialogue * Forgot two * Janitor Tool: Wire Brush (#38667) * Wow! It's -brush- * spacing. * Update Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Update Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * fixed changes --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Minor escape menu UX improvements (#38650) * fix: spellbooks can have infinite charges (#38376) * fix: spellbooks can have infinite charges * refactor: indicate infinite spellbook charges with null Not sure if I like this much better...
798 lines
35 KiB
C#
798 lines
35 KiB
C#
using Content.Server.Administration.Logs;
|
|
using Content.Server.Body.Systems;
|
|
using Content.Server.Construction;
|
|
using Content.Server.Explosion.EntitySystems;
|
|
using Content.Server.DeviceLinking.Systems;
|
|
using Content.Server.Hands.Systems;
|
|
using Content.Server.Kitchen.Components;
|
|
using Content.Server.Power.Components;
|
|
using Content.Server.Power.EntitySystems;
|
|
using Content.Server.Temperature.Components;
|
|
using Content.Server.Temperature.Systems;
|
|
using Content.Shared.Body.Components;
|
|
using Content.Shared.Body.Part;
|
|
using Content.Shared.Chemistry.Components.SolutionManager;
|
|
using Content.Shared.Chemistry.EntitySystems;
|
|
using Content.Shared.Chemistry.Reaction;
|
|
using Content.Shared.Construction.EntitySystems;
|
|
using Content.Shared.Database;
|
|
using Content.Shared.DeviceLinking.Events;
|
|
using Content.Shared.Destructible;
|
|
using Content.Shared.FixedPoint;
|
|
using Content.Shared.Interaction;
|
|
using Content.Shared.Interaction.Events;
|
|
using Robust.Shared.Random;
|
|
using Robust.Shared.Audio;
|
|
using Content.Server.Lightning;
|
|
using Content.Shared.Item;
|
|
using Content.Shared.Kitchen;
|
|
using Content.Shared.Kitchen.Components;
|
|
using Content.Shared.Popups;
|
|
using Content.Shared.Power;
|
|
using Content.Shared.Tag;
|
|
using Robust.Server.GameObjects;
|
|
using Robust.Shared.Audio.Systems;
|
|
using Robust.Shared.Containers;
|
|
using Robust.Shared.Player;
|
|
using System.Linq;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Timing;
|
|
using Content.Shared.Stacks;
|
|
using Content.Server.Construction.Components;
|
|
using Content.Shared.Chat;
|
|
using Content.Shared.Damage;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Content.Server.Kitchen.EntitySystems
|
|
{
|
|
public sealed class MicrowaveSystem : EntitySystem
|
|
{
|
|
[Dependency] private readonly BodySystem _bodySystem = default!;
|
|
[Dependency] private readonly DeviceLinkSystem _deviceLink = default!;
|
|
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
|
[Dependency] private readonly PowerReceiverSystem _power = default!;
|
|
[Dependency] private readonly RecipeManager _recipeManager = default!;
|
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
|
[Dependency] private readonly LightningSystem _lightning = default!;
|
|
[Dependency] private readonly IRobustRandom _random = default!;
|
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
[Dependency] private readonly ExplosionSystem _explosion = default!;
|
|
[Dependency] private readonly SharedContainerSystem _container = default!;
|
|
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
|
|
[Dependency] private readonly TagSystem _tag = default!;
|
|
[Dependency] private readonly TemperatureSystem _temperature = default!;
|
|
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
|
|
[Dependency] private readonly HandsSystem _handsSystem = default!;
|
|
[Dependency] private readonly SharedItemSystem _item = default!;
|
|
[Dependency] private readonly SharedStackSystem _stack = default!;
|
|
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
|
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
|
[Dependency] private readonly SharedSuicideSystem _suicide = default!;
|
|
|
|
private static readonly EntProtoId MalfunctionSpark = "Spark";
|
|
|
|
private static readonly ProtoId<TagPrototype> MetalTag = "Metal";
|
|
private static readonly ProtoId<TagPrototype> PlasticTag = "Plastic";
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
SubscribeLocalEvent<MicrowaveComponent, ComponentInit>(OnInit);
|
|
SubscribeLocalEvent<MicrowaveComponent, MapInitEvent>(OnMapInit);
|
|
SubscribeLocalEvent<MicrowaveComponent, SolutionContainerChangedEvent>(OnSolutionChange);
|
|
SubscribeLocalEvent<MicrowaveComponent, EntInsertedIntoContainerMessage>(OnContentUpdate);
|
|
SubscribeLocalEvent<MicrowaveComponent, EntRemovedFromContainerMessage>(OnContentUpdate);
|
|
SubscribeLocalEvent<MicrowaveComponent, InteractUsingEvent>(OnInteractUsing, after: new[] { typeof(AnchorableSystem) });
|
|
SubscribeLocalEvent<MicrowaveComponent, ContainerIsInsertingAttemptEvent>(OnInsertAttempt);
|
|
SubscribeLocalEvent<MicrowaveComponent, BreakageEventArgs>(OnBreak);
|
|
SubscribeLocalEvent<MicrowaveComponent, PowerChangedEvent>(OnPowerChanged);
|
|
SubscribeLocalEvent<MicrowaveComponent, AnchorStateChangedEvent>(OnAnchorChanged);
|
|
SubscribeLocalEvent<MicrowaveComponent, SuicideByEnvironmentEvent>(OnSuicideByEnvironment);
|
|
|
|
SubscribeLocalEvent<MicrowaveComponent, SignalReceivedEvent>(OnSignalReceived);
|
|
|
|
SubscribeLocalEvent<MicrowaveComponent, MicrowaveStartCookMessage>((u, c, m) => Wzhzhzh(u, c, m.Actor));
|
|
SubscribeLocalEvent<MicrowaveComponent, MicrowaveEjectMessage>(OnEjectMessage);
|
|
SubscribeLocalEvent<MicrowaveComponent, MicrowaveEjectSolidIndexedMessage>(OnEjectIndex);
|
|
SubscribeLocalEvent<MicrowaveComponent, MicrowaveSelectCookTimeMessage>(OnSelectTime);
|
|
|
|
SubscribeLocalEvent<ActiveMicrowaveComponent, ComponentStartup>(OnCookStart);
|
|
SubscribeLocalEvent<ActiveMicrowaveComponent, ComponentShutdown>(OnCookStop);
|
|
SubscribeLocalEvent<ActiveMicrowaveComponent, EntInsertedIntoContainerMessage>(OnActiveMicrowaveInsert);
|
|
SubscribeLocalEvent<ActiveMicrowaveComponent, EntRemovedFromContainerMessage>(OnActiveMicrowaveRemove);
|
|
|
|
SubscribeLocalEvent<ActivelyMicrowavedComponent, OnConstructionTemperatureEvent>(OnConstructionTemp);
|
|
SubscribeLocalEvent<ActivelyMicrowavedComponent, SolutionRelayEvent<ReactionAttemptEvent>>(OnReactionAttempt);
|
|
|
|
SubscribeLocalEvent<FoodRecipeProviderComponent, GetSecretRecipesEvent>(OnGetSecretRecipes);
|
|
}
|
|
|
|
private void OnCookStart(Entity<ActiveMicrowaveComponent> ent, ref ComponentStartup args)
|
|
{
|
|
if (!TryComp<MicrowaveComponent>(ent, out var microwaveComponent))
|
|
return;
|
|
SetAppearance(ent.Owner, MicrowaveVisualState.Cooking, microwaveComponent);
|
|
|
|
microwaveComponent.PlayingStream =
|
|
_audio.PlayPvs(microwaveComponent.LoopingSound, ent, AudioParams.Default.WithLoop(true).WithMaxDistance(5))?.Entity;
|
|
}
|
|
|
|
private void OnCookStop(Entity<ActiveMicrowaveComponent> ent, ref ComponentShutdown args)
|
|
{
|
|
if (!TryComp<MicrowaveComponent>(ent, out var microwaveComponent))
|
|
return;
|
|
|
|
SetAppearance(ent.Owner, MicrowaveVisualState.Idle, microwaveComponent);
|
|
microwaveComponent.PlayingStream = _audio.Stop(microwaveComponent.PlayingStream);
|
|
}
|
|
|
|
private void OnActiveMicrowaveInsert(Entity<ActiveMicrowaveComponent> ent, ref EntInsertedIntoContainerMessage args)
|
|
{
|
|
var microwavedComp = AddComp<ActivelyMicrowavedComponent>(args.Entity);
|
|
microwavedComp.Microwave = ent.Owner;
|
|
}
|
|
|
|
private void OnActiveMicrowaveRemove(Entity<ActiveMicrowaveComponent> ent, ref EntRemovedFromContainerMessage args)
|
|
{
|
|
RemCompDeferred<ActivelyMicrowavedComponent>(args.Entity);
|
|
}
|
|
|
|
// Stop items from transforming through constructiongraphs while being microwaved.
|
|
// They might be reserved for a microwave recipe.
|
|
private void OnConstructionTemp(Entity<ActivelyMicrowavedComponent> ent, ref OnConstructionTemperatureEvent args)
|
|
{
|
|
args.Result = HandleResult.False;
|
|
}
|
|
|
|
// Stop reagents from reacting if they are currently reserved for a microwave recipe.
|
|
// For example Egg would cook into EggCooked, causing it to not being removed once we are done microwaving.
|
|
private void OnReactionAttempt(Entity<ActivelyMicrowavedComponent> ent, ref SolutionRelayEvent<ReactionAttemptEvent> args)
|
|
{
|
|
if (!TryComp<ActiveMicrowaveComponent>(ent.Comp.Microwave, out var activeMicrowaveComp))
|
|
return;
|
|
|
|
if (activeMicrowaveComp.PortionedRecipe.Item1 == null) // no recipe selected
|
|
return;
|
|
|
|
var recipeReagents = activeMicrowaveComp.PortionedRecipe.Item1.IngredientsReagents.Keys;
|
|
|
|
foreach (var reagent in recipeReagents)
|
|
{
|
|
if (args.Event.Reaction.Reactants.ContainsKey(reagent))
|
|
{
|
|
args.Event.Cancelled = true;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds temperature to every item in the microwave,
|
|
/// based on the time it took to microwave.
|
|
/// </summary>
|
|
/// <param name="component">The microwave that is heating up.</param>
|
|
/// <param name="time">The time on the microwave, in seconds.</param>
|
|
private void AddTemperature(MicrowaveComponent component, float time)
|
|
{
|
|
var heatToAdd = time * component.BaseHeatMultiplier;
|
|
foreach (var entity in component.Storage.ContainedEntities)
|
|
{
|
|
if (TryComp<TemperatureComponent>(entity, out var tempComp))
|
|
_temperature.ChangeHeat(entity, heatToAdd * component.ObjectHeatMultiplier, false, tempComp);
|
|
|
|
if (!TryComp<SolutionContainerManagerComponent>(entity, out var solutions))
|
|
continue;
|
|
foreach (var (_, soln) in _solutionContainer.EnumerateSolutions((entity, solutions)))
|
|
{
|
|
var solution = soln.Comp.Solution;
|
|
if (solution.Temperature > component.TemperatureUpperThreshold)
|
|
continue;
|
|
|
|
_solutionContainer.AddThermalEnergy(soln, heatToAdd);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SubtractContents(MicrowaveComponent component, FoodRecipePrototype recipe)
|
|
{
|
|
// TODO Turn recipe.IngredientsReagents into a ReagentQuantity[]
|
|
|
|
var totalReagentsToRemove = new Dictionary<string, FixedPoint2>(recipe.IngredientsReagents);
|
|
|
|
// this is spaghetti ngl
|
|
foreach (var item in component.Storage.ContainedEntities)
|
|
{
|
|
// use the same reagents as when we selected the recipe
|
|
if (!_solutionContainer.TryGetDrainableSolution(item, out var solutionEntity, out var solution))
|
|
continue;
|
|
|
|
foreach (var (reagent, _) in recipe.IngredientsReagents)
|
|
{
|
|
// removed everything
|
|
if (!totalReagentsToRemove.ContainsKey(reagent))
|
|
continue;
|
|
|
|
var quant = solution.GetTotalPrototypeQuantity(reagent);
|
|
|
|
if (quant >= totalReagentsToRemove[reagent])
|
|
{
|
|
quant = totalReagentsToRemove[reagent];
|
|
totalReagentsToRemove.Remove(reagent);
|
|
}
|
|
else
|
|
{
|
|
totalReagentsToRemove[reagent] -= quant;
|
|
}
|
|
|
|
_solutionContainer.RemoveReagent(solutionEntity.Value, reagent, quant);
|
|
}
|
|
}
|
|
|
|
foreach (var recipeSolid in recipe.IngredientsSolids)
|
|
{
|
|
for (var i = 0; i < recipeSolid.Value; i++)
|
|
{
|
|
foreach (var item in component.Storage.ContainedEntities)
|
|
{
|
|
string? itemID = null;
|
|
|
|
// If an entity has a stack component, use the stacktype instead of prototype id
|
|
if (TryComp<StackComponent>(item, out var stackComp))
|
|
{
|
|
itemID = _prototype.Index<StackPrototype>(stackComp.StackTypeId).Spawn;
|
|
}
|
|
else
|
|
{
|
|
var metaData = MetaData(item);
|
|
if (metaData.EntityPrototype == null)
|
|
{
|
|
continue;
|
|
}
|
|
itemID = metaData.EntityPrototype.ID;
|
|
}
|
|
|
|
if (itemID != recipeSolid.Key)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (stackComp is not null)
|
|
{
|
|
if (stackComp.Count == 1)
|
|
{
|
|
_container.Remove(item, component.Storage);
|
|
}
|
|
_stack.Use(item, 1, stackComp);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
_container.Remove(item, component.Storage);
|
|
Del(item);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnInit(Entity<MicrowaveComponent> ent, ref ComponentInit args)
|
|
{
|
|
// this really does have to be in ComponentInit
|
|
ent.Comp.Storage = _container.EnsureContainer<Container>(ent, ent.Comp.ContainerId);
|
|
}
|
|
|
|
private void OnMapInit(Entity<MicrowaveComponent> ent, ref MapInitEvent args)
|
|
{
|
|
_deviceLink.EnsureSinkPorts(ent, ent.Comp.OnPort);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Kills the user by microwaving their head
|
|
/// TODO: Make this not awful, it keeps any items attached to your head still on and you can revive someone and cogni them so you have some dumb headless fuck running around. I've seen it happen.
|
|
/// </summary>
|
|
private void OnSuicideByEnvironment(Entity<MicrowaveComponent> ent, ref SuicideByEnvironmentEvent args)
|
|
{
|
|
if (args.Handled)
|
|
return;
|
|
|
|
// The act of getting your head microwaved doesn't actually kill you
|
|
if (!TryComp<DamageableComponent>(args.Victim, out var damageableComponent))
|
|
return;
|
|
|
|
// The application of lethal damage is what kills you...
|
|
_suicide.ApplyLethalDamage((args.Victim, damageableComponent), "Heat");
|
|
|
|
var victim = args.Victim;
|
|
var headCount = 0;
|
|
|
|
if (TryComp<BodyComponent>(victim, out var body))
|
|
{
|
|
var headSlots = _bodySystem.GetBodyChildrenOfType(victim, BodyPartType.Head, body);
|
|
|
|
foreach (var part in headSlots)
|
|
{
|
|
_container.Insert(part.Id, ent.Comp.Storage);
|
|
headCount++;
|
|
}
|
|
}
|
|
|
|
var othersMessage = headCount > 1
|
|
? Loc.GetString("microwave-component-suicide-multi-head-others-message", ("victim", victim))
|
|
: Loc.GetString("microwave-component-suicide-others-message", ("victim", victim));
|
|
|
|
var selfMessage = headCount > 1
|
|
? Loc.GetString("microwave-component-suicide-multi-head-message")
|
|
: Loc.GetString("microwave-component-suicide-message");
|
|
|
|
_popupSystem.PopupEntity(othersMessage, victim, Filter.PvsExcept(victim), true);
|
|
_popupSystem.PopupEntity(selfMessage, victim, victim);
|
|
|
|
_audio.PlayPvs(ent.Comp.ClickSound, ent.Owner, AudioParams.Default.WithVolume(-2));
|
|
ent.Comp.CurrentCookTimerTime = 10;
|
|
Wzhzhzh(ent.Owner, ent.Comp, args.Victim);
|
|
UpdateUserInterfaceState(ent.Owner, ent.Comp);
|
|
args.Handled = true;
|
|
}
|
|
|
|
private void OnSolutionChange(Entity<MicrowaveComponent> ent, ref SolutionContainerChangedEvent args)
|
|
{
|
|
UpdateUserInterfaceState(ent, ent.Comp);
|
|
}
|
|
|
|
private void OnContentUpdate(EntityUid uid, MicrowaveComponent component, ContainerModifiedMessage args) // For some reason ContainerModifiedMessage just can't be used at all with Entity<T>. TODO: replace with Entity<T> syntax once that's possible
|
|
{
|
|
if (component.Storage != args.Container)
|
|
return;
|
|
|
|
UpdateUserInterfaceState(uid, component);
|
|
}
|
|
|
|
private void OnInsertAttempt(Entity<MicrowaveComponent> ent, ref ContainerIsInsertingAttemptEvent args)
|
|
{
|
|
if (args.Container.ID != ent.Comp.ContainerId)
|
|
return;
|
|
|
|
if (ent.Comp.Broken)
|
|
{
|
|
args.Cancel();
|
|
return;
|
|
}
|
|
|
|
if (TryComp<ItemComponent>(args.EntityUid, out var item))
|
|
{
|
|
if (_item.GetSizePrototype(item.Size) > _item.GetSizePrototype(ent.Comp.MaxItemSize))
|
|
{
|
|
args.Cancel();
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
args.Cancel();
|
|
return;
|
|
}
|
|
|
|
if (ent.Comp.Storage.Count >= ent.Comp.Capacity)
|
|
args.Cancel();
|
|
}
|
|
|
|
private void OnInteractUsing(Entity<MicrowaveComponent> ent, ref InteractUsingEvent args)
|
|
{
|
|
if (args.Handled)
|
|
return;
|
|
if (!(TryComp<ApcPowerReceiverComponent>(ent, out var apc) && apc.Powered))
|
|
{
|
|
_popupSystem.PopupEntity(Loc.GetString("microwave-component-interact-using-no-power"), ent, args.User);
|
|
return;
|
|
}
|
|
|
|
if (ent.Comp.Broken)
|
|
{
|
|
_popupSystem.PopupEntity(Loc.GetString("microwave-component-interact-using-broken"), ent, args.User);
|
|
return;
|
|
}
|
|
|
|
if (TryComp<ItemComponent>(args.Used, out var item))
|
|
{
|
|
// check if size of an item you're trying to put in is too big
|
|
if (_item.GetSizePrototype(item.Size) > _item.GetSizePrototype(ent.Comp.MaxItemSize))
|
|
{
|
|
_popupSystem.PopupEntity(Loc.GetString("microwave-component-interact-item-too-big", ("item", args.Used)), ent, args.User);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// check if thing you're trying to put in isn't an item
|
|
_popupSystem.PopupEntity(Loc.GetString("microwave-component-interact-using-transfer-fail"), ent, args.User);
|
|
return;
|
|
}
|
|
|
|
if (ent.Comp.Storage.Count >= ent.Comp.Capacity)
|
|
{
|
|
_popupSystem.PopupEntity(Loc.GetString("microwave-component-interact-full"), ent, args.User);
|
|
return;
|
|
}
|
|
|
|
args.Handled = true;
|
|
_handsSystem.TryDropIntoContainer(args.User, args.Used, ent.Comp.Storage);
|
|
UpdateUserInterfaceState(ent, ent.Comp);
|
|
}
|
|
|
|
private void OnBreak(Entity<MicrowaveComponent> ent, ref BreakageEventArgs args)
|
|
{
|
|
ent.Comp.Broken = true;
|
|
SetAppearance(ent, MicrowaveVisualState.Broken, ent.Comp);
|
|
StopCooking(ent);
|
|
_container.EmptyContainer(ent.Comp.Storage);
|
|
UpdateUserInterfaceState(ent, ent.Comp);
|
|
}
|
|
|
|
private void OnPowerChanged(Entity<MicrowaveComponent> ent, ref PowerChangedEvent args)
|
|
{
|
|
if (!args.Powered)
|
|
{
|
|
SetAppearance(ent, MicrowaveVisualState.Idle, ent.Comp);
|
|
StopCooking(ent);
|
|
}
|
|
UpdateUserInterfaceState(ent, ent.Comp);
|
|
}
|
|
|
|
private void OnAnchorChanged(EntityUid uid, MicrowaveComponent component, ref AnchorStateChangedEvent args)
|
|
{
|
|
// DeltaV - start of microwave ejection bugfix
|
|
if (!args.Anchored)
|
|
{
|
|
// DeltaV's MicrowaveEventsSystem changes prevent ejection from active microwave, so stop cooking first
|
|
StopCooking((uid, component));
|
|
_container.EmptyContainer(component.Storage);
|
|
UpdateUserInterfaceState(uid, component);
|
|
}
|
|
// DeltaV - end of microwave ejection bugfix
|
|
}
|
|
|
|
private void OnSignalReceived(Entity<MicrowaveComponent> ent, ref SignalReceivedEvent args)
|
|
{
|
|
if (args.Port != ent.Comp.OnPort)
|
|
return;
|
|
|
|
if (ent.Comp.Broken || !_power.IsPowered(ent))
|
|
return;
|
|
|
|
Wzhzhzh(ent.Owner, ent.Comp, null);
|
|
}
|
|
|
|
public void UpdateUserInterfaceState(EntityUid uid, MicrowaveComponent component)
|
|
{
|
|
_userInterface.SetUiState(uid, MicrowaveUiKey.Key, new MicrowaveUpdateUserInterfaceState(
|
|
GetNetEntityArray(component.Storage.ContainedEntities.ToArray()),
|
|
// DeltaV - start of microwave ejection bugfix
|
|
(
|
|
EntityManager.TryGetComponent<ActiveMicrowaveComponent>(uid, out var active)
|
|
&& active.LifeStage < ComponentLifeStage.Stopping
|
|
),
|
|
// DeltaV - end of microwave ejection bugfix
|
|
component.CurrentCookTimeButtonIndex,
|
|
component.CurrentCookTimerTime,
|
|
component.CurrentCookTimeEnd
|
|
));
|
|
}
|
|
|
|
public void SetAppearance(EntityUid uid, MicrowaveVisualState state, MicrowaveComponent? component = null, AppearanceComponent? appearanceComponent = null)
|
|
{
|
|
if (!Resolve(uid, ref component, ref appearanceComponent, false))
|
|
return;
|
|
var display = component.Broken ? MicrowaveVisualState.Broken : state;
|
|
_appearance.SetData(uid, PowerDeviceVisuals.VisualState, display, appearanceComponent);
|
|
}
|
|
|
|
public static bool HasContents(MicrowaveComponent component)
|
|
{
|
|
return component.Storage.ContainedEntities.Any();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Explodes the microwave internally, turning it into a broken state, destroying its board, and spitting out its machine parts
|
|
/// </summary>
|
|
/// <param name="ent"></param>
|
|
public void Explode(Entity<MicrowaveComponent> ent)
|
|
{
|
|
// DeltaV - start of microwave ejection bugfix
|
|
// DeltaV's MicrowaveEventsSystem changes prevent ejection from active microwave, so stop cooking first
|
|
StopCooking(ent);
|
|
// DeltaV - end of microwave ejection bugfix
|
|
ent.Comp.Broken = true; // Make broken so we stop processing stuff
|
|
_explosion.TriggerExplosive(ent);
|
|
if (TryComp<MachineComponent>(ent, out var machine))
|
|
{
|
|
_container.CleanContainer(machine.BoardContainer);
|
|
_container.EmptyContainer(machine.PartContainer);
|
|
}
|
|
|
|
// DeltaV - start of microwave ejection bugfix
|
|
UpdateUserInterfaceState(ent, ent.Comp);
|
|
// DeltaV - end of microwave ejection bugfix
|
|
|
|
_adminLogger.Add(LogType.Action, LogImpact.Medium,
|
|
$"{ToPrettyString(ent)} exploded from unsafe cooking!");
|
|
}
|
|
/// <summary>
|
|
/// Handles the attempted cooking of unsafe objects
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Returns false if the microwave didn't explode, true if it exploded.
|
|
/// </remarks>
|
|
private void RollMalfunction(Entity<ActiveMicrowaveComponent, MicrowaveComponent> ent)
|
|
{
|
|
if (ent.Comp1.MalfunctionTime == TimeSpan.Zero)
|
|
return;
|
|
|
|
if (ent.Comp1.MalfunctionTime > _gameTiming.CurTime)
|
|
return;
|
|
|
|
ent.Comp1.MalfunctionTime = _gameTiming.CurTime + TimeSpan.FromSeconds(ent.Comp2.MalfunctionInterval);
|
|
if (_random.Prob(ent.Comp2.ExplosionChance))
|
|
{
|
|
Explode((ent, ent.Comp2));
|
|
return; // microwave is fucked, stop the cooking.
|
|
}
|
|
|
|
if (_random.Prob(ent.Comp2.LightningChance))
|
|
_lightning.ShootRandomLightnings(ent, 1.0f, 2, MalfunctionSpark, triggerLightningEvents: false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Starts Cooking
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// It does not make a "wzhzhzh" sound, it makes a "mmmmmmmm" sound!
|
|
/// -emo
|
|
/// </remarks>
|
|
public void Wzhzhzh(EntityUid uid, MicrowaveComponent component, EntityUid? user)
|
|
{
|
|
if (!HasContents(component) || HasComp<ActiveMicrowaveComponent>(uid) || !(TryComp<ApcPowerReceiverComponent>(uid, out var apc) && apc.Powered))
|
|
return;
|
|
|
|
var solidsDict = new Dictionary<string, int>();
|
|
var reagentDict = new Dictionary<string, FixedPoint2>();
|
|
var malfunctioning = false;
|
|
// TODO use lists of Reagent quantities instead of reagent prototype ids.
|
|
foreach (var item in component.Storage.ContainedEntities.ToArray())
|
|
{
|
|
// special behavior when being microwaved ;)
|
|
var ev = new BeingMicrowavedEvent(uid, user, component.CurrentCookTimerTime);
|
|
RaiseLocalEvent(item, ev);
|
|
|
|
// TODO MICROWAVE SPARKS & EFFECTS
|
|
// Various microwaveable entities should probably spawn a spark, play a sound, and generate a pop=up.
|
|
// This should probably be handled by the microwave system, with fields in BeingMicrowavedEvent.
|
|
|
|
if (ev.Handled)
|
|
{
|
|
UpdateUserInterfaceState(uid, component);
|
|
return;
|
|
}
|
|
|
|
if (_tag.HasTag(item, MetalTag))
|
|
{
|
|
malfunctioning = true;
|
|
}
|
|
|
|
if (_tag.HasTag(item, PlasticTag))
|
|
{
|
|
var junk = Spawn(component.BadRecipeEntityId, Transform(uid).Coordinates);
|
|
_container.Insert(junk, component.Storage);
|
|
Del(item);
|
|
continue;
|
|
}
|
|
|
|
var microwavedComp = AddComp<ActivelyMicrowavedComponent>(item);
|
|
microwavedComp.Microwave = uid;
|
|
|
|
string? solidID = null;
|
|
int amountToAdd = 1;
|
|
|
|
// If a microwave recipe uses a stacked item, use the default stack prototype id instead of prototype id
|
|
if (TryComp<StackComponent>(item, out var stackComp))
|
|
{
|
|
solidID = _prototype.Index<StackPrototype>(stackComp.StackTypeId).Spawn;
|
|
amountToAdd = stackComp.Count;
|
|
}
|
|
else
|
|
{
|
|
var metaData = MetaData(item); //this simply begs for cooking refactor
|
|
if (metaData.EntityPrototype is not null)
|
|
solidID = metaData.EntityPrototype.ID;
|
|
}
|
|
|
|
if (solidID is null)
|
|
continue;
|
|
|
|
if (!solidsDict.TryAdd(solidID, amountToAdd))
|
|
solidsDict[solidID] += amountToAdd;
|
|
|
|
// only use reagents we have access to
|
|
// you have to break the eggs before we can use them!
|
|
if (!_solutionContainer.TryGetDrainableSolution(item, out var _, out var solution))
|
|
continue;
|
|
|
|
foreach (var (reagent, quantity) in solution.Contents)
|
|
{
|
|
if (!reagentDict.TryAdd(reagent.Prototype, quantity))
|
|
reagentDict[reagent.Prototype] += quantity;
|
|
}
|
|
}
|
|
|
|
// Check recipes
|
|
var getRecipesEv = new GetSecretRecipesEvent();
|
|
RaiseLocalEvent(uid, ref getRecipesEv);
|
|
|
|
List<FoodRecipePrototype> recipes = getRecipesEv.Recipes;
|
|
recipes.AddRange(_recipeManager.Recipes);
|
|
var portionedRecipe = recipes.Select(r =>
|
|
CanSatisfyRecipe(component, r, solidsDict, reagentDict)).FirstOrDefault(r => r.Item2 > 0);
|
|
|
|
_audio.PlayPvs(component.StartCookingSound, uid);
|
|
var activeComp = AddComp<ActiveMicrowaveComponent>(uid); //microwave is now cooking
|
|
activeComp.CookTimeRemaining = component.CurrentCookTimerTime * component.CookTimeMultiplier;
|
|
activeComp.TotalTime = component.CurrentCookTimerTime; //this doesn't scale so that we can have the "actual" time
|
|
activeComp.PortionedRecipe = portionedRecipe;
|
|
//Scale tiems with cook times
|
|
component.CurrentCookTimeEnd = _gameTiming.CurTime + TimeSpan.FromSeconds(component.CurrentCookTimerTime * component.CookTimeMultiplier);
|
|
if (malfunctioning)
|
|
activeComp.MalfunctionTime = _gameTiming.CurTime + TimeSpan.FromSeconds(component.MalfunctionInterval);
|
|
UpdateUserInterfaceState(uid, component);
|
|
}
|
|
|
|
private void StopCooking(Entity<MicrowaveComponent> ent)
|
|
{
|
|
RemCompDeferred<ActiveMicrowaveComponent>(ent);
|
|
foreach (var solid in ent.Comp.Storage.ContainedEntities)
|
|
{
|
|
RemCompDeferred<ActivelyMicrowavedComponent>(solid);
|
|
}
|
|
}
|
|
|
|
public static (FoodRecipePrototype, int) CanSatisfyRecipe(MicrowaveComponent component, FoodRecipePrototype recipe, Dictionary<string, int> solids, Dictionary<string, FixedPoint2> reagents)
|
|
{
|
|
var portions = 0;
|
|
|
|
if (component.CurrentCookTimerTime % recipe.CookTime != 0)
|
|
{
|
|
//can't be a multiple of this recipe
|
|
return (recipe, 0);
|
|
}
|
|
|
|
foreach (var solid in recipe.IngredientsSolids)
|
|
{
|
|
if (!solids.ContainsKey(solid.Key))
|
|
return (recipe, 0);
|
|
|
|
if (solids[solid.Key] < solid.Value)
|
|
return (recipe, 0);
|
|
|
|
portions = portions == 0
|
|
? solids[solid.Key] / solid.Value.Int()
|
|
: Math.Min(portions, solids[solid.Key] / solid.Value.Int());
|
|
}
|
|
|
|
foreach (var reagent in recipe.IngredientsReagents)
|
|
{
|
|
// TODO Turn recipe.IngredientsReagents into a ReagentQuantity[]
|
|
if (!reagents.ContainsKey(reagent.Key))
|
|
return (recipe, 0);
|
|
|
|
if (reagents[reagent.Key] < reagent.Value)
|
|
return (recipe, 0);
|
|
|
|
portions = portions == 0
|
|
? reagents[reagent.Key].Int() / reagent.Value.Int()
|
|
: Math.Min(portions, reagents[reagent.Key].Int() / reagent.Value.Int());
|
|
}
|
|
|
|
//cook only as many of those portions as time allows
|
|
return (recipe, (int) Math.Min(portions, component.CurrentCookTimerTime / recipe.CookTime));
|
|
}
|
|
|
|
public override void Update(float frameTime)
|
|
{
|
|
base.Update(frameTime);
|
|
|
|
var query = EntityQueryEnumerator<ActiveMicrowaveComponent, MicrowaveComponent>();
|
|
while (query.MoveNext(out var uid, out var active, out var microwave))
|
|
{
|
|
|
|
active.CookTimeRemaining -= frameTime;
|
|
|
|
RollMalfunction((uid, active, microwave));
|
|
|
|
//check if there's still cook time left
|
|
if (active.CookTimeRemaining > 0)
|
|
{
|
|
AddTemperature(microwave, frameTime);
|
|
continue;
|
|
}
|
|
|
|
//this means the microwave has finished cooking.
|
|
AddTemperature(microwave, Math.Max(frameTime + active.CookTimeRemaining, 0)); //Though there's still a little bit more heat to pump out
|
|
|
|
if (active.PortionedRecipe.Item1 != null)
|
|
{
|
|
var coords = Transform(uid).Coordinates;
|
|
for (var i = 0; i < active.PortionedRecipe.Item2; i++)
|
|
{
|
|
SubtractContents(microwave, active.PortionedRecipe.Item1);
|
|
Spawn(active.PortionedRecipe.Item1.Result, coords);
|
|
}
|
|
}
|
|
|
|
// DeltaV - start of microwave ejection bugfix
|
|
// StopCooking should be in front of both:
|
|
// - EmptyContainer() call, because DeltaV MicrowaveEventsSystem prevents ejection from active microwave
|
|
// - UpdateUserInterfaceState() call - not very relevant, but UI shouldn't be "busy" after cooking is done
|
|
StopCooking((uid, microwave));
|
|
_container.EmptyContainer(microwave.Storage);
|
|
microwave.CurrentCookTimeEnd = TimeSpan.Zero;
|
|
UpdateUserInterfaceState(uid, microwave);
|
|
_audio.PlayPvs(microwave.FoodDoneSound, uid);
|
|
// DeltaV - end of microwave ejection bugfix
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This event tries to get secret recipes that the microwave might be capable of.
|
|
/// Currently, we only check the microwave itself, but in the future, the user might be able to learn recipes.
|
|
/// </summary>
|
|
private void OnGetSecretRecipes(Entity<FoodRecipeProviderComponent> ent, ref GetSecretRecipesEvent args)
|
|
{
|
|
foreach (ProtoId<FoodRecipePrototype> recipeId in ent.Comp.ProvidedRecipes)
|
|
{
|
|
if (_prototype.TryIndex(recipeId, out var recipeProto))
|
|
{
|
|
args.Recipes.Add(recipeProto);
|
|
}
|
|
}
|
|
}
|
|
|
|
#region ui
|
|
private void OnEjectMessage(Entity<MicrowaveComponent> ent, ref MicrowaveEjectMessage args)
|
|
{
|
|
if (!HasContents(ent.Comp) || HasComp<ActiveMicrowaveComponent>(ent))
|
|
return;
|
|
|
|
_container.EmptyContainer(ent.Comp.Storage);
|
|
_audio.PlayPvs(ent.Comp.ClickSound, ent, AudioParams.Default.WithVolume(-2));
|
|
UpdateUserInterfaceState(ent, ent.Comp);
|
|
}
|
|
|
|
private void OnEjectIndex(Entity<MicrowaveComponent> ent, ref MicrowaveEjectSolidIndexedMessage args)
|
|
{
|
|
if (!HasContents(ent.Comp) || HasComp<ActiveMicrowaveComponent>(ent))
|
|
return;
|
|
|
|
_container.Remove(GetEntity(args.EntityID), ent.Comp.Storage);
|
|
UpdateUserInterfaceState(ent, ent.Comp);
|
|
}
|
|
|
|
private void OnSelectTime(Entity<MicrowaveComponent> ent, ref MicrowaveSelectCookTimeMessage args)
|
|
{
|
|
if (!HasContents(ent.Comp) || HasComp<ActiveMicrowaveComponent>(ent) || !(TryComp<ApcPowerReceiverComponent>(ent, out var apc) && apc.Powered))
|
|
return;
|
|
|
|
// some validation to prevent trollage
|
|
if (args.NewCookTime % 5 != 0 || args.NewCookTime > ent.Comp.MaxCookTime)
|
|
return;
|
|
|
|
ent.Comp.CurrentCookTimeButtonIndex = args.ButtonIndex;
|
|
ent.Comp.CurrentCookTimerTime = args.NewCookTime;
|
|
ent.Comp.CurrentCookTimeEnd = TimeSpan.Zero;
|
|
_audio.PlayPvs(ent.Comp.ClickSound, ent, AudioParams.Default.WithVolume(-2));
|
|
UpdateUserInterfaceState(ent, ent.Comp);
|
|
}
|
|
#endregion
|
|
}
|
|
}
|