From 9268cec0c98ad7c8cb1c4c40c95999bdb696b56d Mon Sep 17 00:00:00 2001 From: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Date: Fri, 5 Dec 2025 21:46:38 +0100 Subject: [PATCH] Predict ReactionMixerSystem (#41218) * predict * documentation * review --- .../EntitySystems/ReactionMixerSystem.cs | 81 ------------- .../Reaction/ReactionMixerComponent.cs | 31 +++-- .../Chemistry/Reaction/ReactionMixerSystem.cs | 109 ++++++++++++++++++ 3 files changed, 122 insertions(+), 99 deletions(-) delete mode 100644 Content.Server/Chemistry/EntitySystems/ReactionMixerSystem.cs create mode 100644 Content.Shared/Chemistry/Reaction/ReactionMixerSystem.cs diff --git a/Content.Server/Chemistry/EntitySystems/ReactionMixerSystem.cs b/Content.Server/Chemistry/EntitySystems/ReactionMixerSystem.cs deleted file mode 100644 index ee99418970..0000000000 --- a/Content.Server/Chemistry/EntitySystems/ReactionMixerSystem.cs +++ /dev/null @@ -1,81 +0,0 @@ -using Content.Shared.Chemistry.Components; -using Content.Shared.Chemistry.Reaction; -using Content.Shared.DoAfter; -using Content.Shared.IdentityManagement; -using Content.Shared.Interaction; -using Content.Shared.Nutrition.EntitySystems; -using Content.Shared.Chemistry.EntitySystems; -using Content.Server.Popups; - -namespace Content.Server.Chemistry.EntitySystems; - -public sealed partial class ReactionMixerSystem : EntitySystem -{ - [Dependency] private readonly PopupSystem _popup = default!; - [Dependency] private readonly SharedSolutionContainerSystem _solutionContainers = default!; - [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnAfterInteract, before: [typeof(IngestionSystem)]); - SubscribeLocalEvent(OnShake); - SubscribeLocalEvent(OnDoAfter); - } - - private void OnAfterInteract(Entity entity, ref AfterInteractEvent args) - { - if (!args.Target.HasValue || !args.CanReach || !entity.Comp.MixOnInteract) - return; - - if (!MixAttempt(entity, args.Target.Value, out _)) - return; - - var doAfterArgs = new DoAfterArgs(EntityManager, args.User, entity.Comp.TimeToMix, new ReactionMixDoAfterEvent(), entity, args.Target.Value, entity); - - _doAfterSystem.TryStartDoAfter(doAfterArgs); - args.Handled = true; - } - - private void OnDoAfter(Entity entity, ref ReactionMixDoAfterEvent args) - { - //Do again to get the solution again - if (!MixAttempt(entity, args.Target!.Value, out var solution)) - return; - - _popup.PopupEntity(Loc.GetString(entity.Comp.MixMessage, ("mixed", Identity.Entity(args.Target!.Value, EntityManager)), ("mixer", Identity.Entity(entity.Owner, EntityManager))), args.User, args.User); - - _solutionContainers.UpdateChemicals(solution!.Value, true, entity.Comp); - - var afterMixingEvent = new AfterMixingEvent(entity, args.Target!.Value); - RaiseLocalEvent(entity, afterMixingEvent); - } - - private void OnShake(Entity entity, ref ShakeEvent args) - { - if (!MixAttempt(entity, entity, out var solution)) - return; - - _solutionContainers.UpdateChemicals(solution!.Value, true, entity.Comp); - - var afterMixingEvent = new AfterMixingEvent(entity, entity); - RaiseLocalEvent(entity, afterMixingEvent); - } - - private bool MixAttempt(EntityUid ent, EntityUid target, out Entity? solution) - { - solution = null; - var mixAttemptEvent = new MixingAttemptEvent(ent); - RaiseLocalEvent(ent, ref mixAttemptEvent); - if (mixAttemptEvent.Cancelled) - { - return false; - } - - if (!_solutionContainers.TryGetMixableSolution(target, out solution, out _)) - return false; - - return true; - } -} diff --git a/Content.Shared/Chemistry/Reaction/ReactionMixerComponent.cs b/Content.Shared/Chemistry/Reaction/ReactionMixerComponent.cs index 8edfa44ce8..25fda25607 100644 --- a/Content.Shared/Chemistry/Reaction/ReactionMixerComponent.cs +++ b/Content.Shared/Chemistry/Reaction/ReactionMixerComponent.cs @@ -1,48 +1,43 @@ -using Content.Shared.Chemistry.Components; using Content.Shared.DoAfter; +using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization; namespace Content.Shared.Chemistry.Reaction; -[RegisterComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class ReactionMixerComponent : Component { /// - /// A list of IDs for categories of reactions that can be mixed (i.e. HOLY for a bible, DRINK for a spoon) + /// A list of IDs for categories of reactions that can be mixed (i.e. HOLY for a bible, DRINK for a spoon). /// - [ViewVariables] - [DataField] - public List> ReactionTypes = default!; + [DataField, AutoNetworkedField] + public List> ReactionTypes = new(); /// - /// A string which identifies the string to be sent when successfully mixing a solution + /// The popup message when successfully mixing a solution. /// - [ViewVariables] - [DataField] + [DataField, AutoNetworkedField] public LocId MixMessage = "default-mixing-success"; /// - /// Defines if interacting is enough to mix with this component + /// Defines if interacting is enough to mix with this component. /// - [ViewVariables] - [DataField] + [DataField, AutoNetworkedField] public bool MixOnInteract = true; /// - /// How long it takes to mix with this + /// How long it takes to mix with this. /// - [ViewVariables] - [DataField] + [DataField, AutoNetworkedField] public TimeSpan TimeToMix = TimeSpan.Zero; } [ByRefEvent] public record struct MixingAttemptEvent(EntityUid Mixed, bool Cancelled = false); +[ByRefEvent] public readonly record struct AfterMixingEvent(EntityUid Mixed, EntityUid Mixer); [Serializable, NetSerializable] -public sealed partial class ReactionMixDoAfterEvent : SimpleDoAfterEvent -{ -} +public sealed partial class ReactionMixDoAfterEvent : SimpleDoAfterEvent; diff --git a/Content.Shared/Chemistry/Reaction/ReactionMixerSystem.cs b/Content.Shared/Chemistry/Reaction/ReactionMixerSystem.cs new file mode 100644 index 0000000000..4bc878a42f --- /dev/null +++ b/Content.Shared/Chemistry/Reaction/ReactionMixerSystem.cs @@ -0,0 +1,109 @@ +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.Popups; + +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!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAfterInteract, before: [typeof(IngestionSystem)]); + SubscribeLocalEvent(OnShake); + SubscribeLocalEvent(OnDoAfter); + } + + private void OnAfterInteract(Entity ent, ref AfterInteractEvent args) + { + if (!args.Target.HasValue || !args.CanReach || !ent.Comp.MixOnInteract) + return; + + if (!CanMix(ent.AsNullable(), args.Target.Value)) + return; + + 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) + { + 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; + + var mixAttemptEvent = new MixingAttemptEvent(ent); + RaiseLocalEvent(ent, ref mixAttemptEvent); + if (mixAttemptEvent.Cancelled) + return false; + + if (!_solutionContainer.TryGetMixableSolution(target, out _, out _)) + 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; + } +} -- 2.52.0