using Content.Shared.Chemistry.Components; using Content.Shared.DoAfter; using Content.Shared.IdentityManagement; using Content.Shared.Interaction; using Content.Shared.Nutrition.EntitySystems; using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Interaction.Events; using Content.Shared.Popups; using Robust.Shared.Audio.Systems; using Robust.Shared.Network; namespace Content.Shared.Chemistry.Reaction; public sealed partial class ReactionMixerSystem : EntitySystem { [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly INetManager _net = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnUseInHand); SubscribeLocalEvent(OnAfterInteract, before: [typeof(IngestionSystem)]); SubscribeLocalEvent(OnShake); SubscribeLocalEvent(OnDoAfter); } private void OnUseInHand(Entity ent, ref UseInHandEvent args) { if (args.Handled) return; if (ent.Comp.MixerType != ReactionMixerType.Handheld) return; args.Handled = true; if (!CanMix(ent.AsNullable(), ent)) return; if (_net.IsServer) // Cannot cancel predicted audio. ent.Comp.AudioStream = _audio.PlayPvs(ent.Comp.MixingSound, ent)?.Entity; var doAfterArgs = new DoAfterArgs(EntityManager, args.User, ent.Comp.TimeToMix, new ReactionMixDoAfterEvent(), ent, ent, ent) { NeedHand = true, BreakOnDamage = true, BreakOnDropItem = true, BreakOnHandChange = true, BreakOnMove = true }; _doAfter.TryStartDoAfter(doAfterArgs); } private void OnAfterInteract(Entity ent, ref AfterInteractEvent args) { if (!args.Target.HasValue || !args.CanReach || ent.Comp.MixerType != ReactionMixerType.Machine) return; if (!CanMix(ent.AsNullable(), args.Target.Value)) return; if (_net.IsServer) // Cannot cancel predicted audio. ent.Comp.AudioStream = _audio.PlayPvs(ent.Comp.MixingSound, ent)?.Entity; var doAfterArgs = new DoAfterArgs(EntityManager, args.User, ent.Comp.TimeToMix, new ReactionMixDoAfterEvent(), ent, args.Target.Value, ent); _doAfter.TryStartDoAfter(doAfterArgs); args.Handled = true; } private void OnDoAfter(Entity ent, ref ReactionMixDoAfterEvent args) { ent.Comp.AudioStream = _audio.Stop(ent.Comp.AudioStream); if (args.Cancelled) return; if (args.Target == null) return; if (!TryMix(ent.AsNullable(), args.Target.Value)) return; _popup.PopupClient( Loc.GetString(ent.Comp.MixMessage, ("mixed", Identity.Entity(args.Target.Value, EntityManager)), ("mixer", Identity.Entity(ent.Owner, EntityManager))), args.User, args.User); } private void OnShake(Entity ent, ref ShakeEvent args) { TryMix(ent.AsNullable(), ent); } /// /// Returns true if given reaction mixer is able to mix the solution inside the target entity, false otherwise. /// /// The reaction mixer used to cause the reaction. /// The target solution container with a . public bool CanMix(Entity ent, EntityUid target) { if (!Resolve(ent, ref ent.Comp, false)) // The used entity needs the component to be able to mix a solution return false; if (!_solutionContainer.TryGetMixableSolution(target, out _, out var mixableSolution)) return false; // Can't mix nothing. if (mixableSolution.Volume <= 0) return false; var mixAttemptEvent = new MixingAttemptEvent(ent); RaiseLocalEvent(ent, ref mixAttemptEvent); if (mixAttemptEvent.Cancelled) return false; return true; } /// /// Attempts to mix the solution inside the target entity using the given reaction mixer. /// /// The reaction mixer used to cause the reaction. /// The target solution container with a . /// If the reaction mixer was able to mix the solution. This does not necessarily mean a reaction took place. public bool TryMix(Entity ent, EntityUid target) { if (!Resolve(ent, ref ent.Comp, false)) return false; var mixAttemptEvent = new MixingAttemptEvent(ent); RaiseLocalEvent(ent, ref mixAttemptEvent); if (mixAttemptEvent.Cancelled) return false; if (!_solutionContainer.TryGetMixableSolution(target, out var solutionEnt, out _)) return false; _solutionContainer.UpdateChemicals(solutionEnt.Value, true, ent.Comp); var afterMixingEvent = new AfterMixingEvent(ent, target); RaiseLocalEvent(ent, ref afterMixingEvent); return true; } }