// © SUNRISE, An EULA/CLA with a hosting restriction, full text: https://github.com/space-sunrise/space-station-14/blob/master/CLA.txt
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reaction;
using Content.Shared.Interaction;
using Content.Shared.Popups;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Network;
using Robust.Shared.Timing;
using System.Collections.Frozen;
using Content.Shared.Paper;
using Robust.Shared.Utility;
using static Content.Shared.Paper.SharedPaperComponent;
using Robust.Shared.Prototypes;
using System.Linq;
using Robust.Shared.Serialization;
using Content.Shared.Chemistry.Reagent;
namespace Content.Shared.Chemistry.EntitySystems;
///
/// This handles
///
public abstract class SharedVaccinatorSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedSolutionContainerSystem _solution = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
///
/// A cache of all reactions indexed by at most ONE of their required reactants.
/// I.e., even if a reaction has more than one reagent, it will only ever appear once in this dictionary.
///
private FrozenDictionary> _reactionsSingle = default!;
///
/// A cache of all reactions indexed by one of their required reactants.
///
private FrozenDictionary> _reactions = default!;
///
public override void Initialize()
{
InitializeReactionCache();
SubscribeLocalEvent(OnActivateInWorld);
SubscribeLocalEvent(OnRemoveAttempt);
}
///
/// Handles building the reaction cache.
///
private void InitializeReactionCache()
{
// Construct single-reaction dictionary.
var dict = new Dictionary>();
foreach (var reaction in _prototypeManager.EnumeratePrototypes())
{
// For this dictionary we only need to cache based on the first reagent.
var reagent = reaction.Reactants.Keys.First();
var list = dict.GetOrNew(reagent);
list.Add(reaction);
}
_reactionsSingle = dict.ToFrozenDictionary();
dict.Clear();
foreach (var reaction in _prototypeManager.EnumeratePrototypes())
{
foreach (var reagent in reaction.Reactants.Keys)
{
var list = dict.GetOrNew(reagent);
list.Add(reaction);
}
}
_reactions = dict.ToFrozenDictionary();
}
private void OnActivateInWorld(Entity entity, ref ActivateInWorldEvent args)
{
TryStartMix(entity, args.User);
}
private void OnRemoveAttempt(Entity ent, ref ContainerIsRemovingAttemptEvent args)
{
if (args.Container.ID == ent.Comp.ContainerId && ent.Comp.Mixing)
args.Cancel();
}
protected virtual bool HasPower(Entity entity)
{
return true;
}
public void TryStartMix(Entity entity, EntityUid? user)
{
var (uid, comp) = entity;
if (comp.Mixing)
return;
if (!HasPower(entity))
{
if (user != null)
_popup.PopupClient(Loc.GetString("solution-container-mixer-no-power"), entity, user.Value);
return;
}
if (!_container.TryGetContainer(uid, comp.ContainerId, out var container) || container.Count == 0)
{
if (user != null)
_popup.PopupClient(Loc.GetString("solution-container-mixer-popup-nothing-to-mix"), entity, user.Value);
return;
}
comp.Mixing = true;
try
{
if (_net.IsServer)
comp.MixingSoundEntity = _audio.PlayPvs(comp.MixingSound, entity, comp.MixingSound?.Params.WithLoop(true));
}
catch { }
comp.MixTimeEnd = _timing.CurTime + comp.MixDuration;
_appearance.SetData(entity, SolutionContainerMixerVisuals.Mixing, true);
Dirty(uid, comp);
}
public void StopMix(Entity entity)
{
var (uid, comp) = entity;
if (!comp.Mixing)
return;
_audio.Stop(comp.MixingSoundEntity);
_appearance.SetData(entity, SolutionContainerMixerVisuals.Mixing, false);
comp.Mixing = false;
comp.MixingSoundEntity = null;
Dirty(uid, comp);
}
public void FinishMix(Entity entity)
{
var (uid, comp) = entity;
if (!comp.Mixing)
return;
StopMix(entity);
if (!TryComp(entity, out var reactionMixer)
|| !_container.TryGetContainer(uid, comp.ContainerId, out var container))
return;
bool printed = false;
foreach (var ent in container.ContainedEntities)
{
if (!_solution.TryGetFitsInDispenser(ent, out var soln, out _))
continue;
if (!printed)
{
if (!(_gameTiming.CurTime < comp.PrintReadyAt))
{
RaiseNetworkEvent(new PaperInputTextMessageLigyb(soln.Value.Comp.Solution.Contents, GetNetEntity(uid)));
printed = true;
comp.PrintReadyAt = _gameTiming.CurTime + comp.PrintCooldown;
}
}
_solution.UpdateChemicals(soln.Value, true, reactionMixer);
}
}
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator();
while (query.MoveNext(out var uid, out var comp))
{
if (!comp.Mixing)
continue;
if (_timing.CurTime < comp.MixTimeEnd)
continue;
FinishMix((uid, comp));
}
}
}
[Serializable, NetSerializable]
public sealed class PaperInputTextMessageLigyb : EntityEventArgs
{
public readonly List ReagentQuantity;
public readonly NetEntity Uid;
public PaperInputTextMessageLigyb(List reagentQuantity, NetEntity uid)
{
Uid = uid;
ReagentQuantity = reagentQuantity;
}
}