+++ /dev/null
-using Content.Shared.Chemistry.Components;
-using Content.Shared.FixedPoint;
-
-namespace Content.Client.Chemistry.Components
-{
- [RegisterComponent]
- public sealed partial class HyposprayComponent : SharedHyposprayComponent
- {
- [ViewVariables]
- public FixedPoint2 CurrentVolume;
- [ViewVariables]
- public FixedPoint2 TotalVolume;
- [ViewVariables(VVAccess.ReadWrite)]
- public bool UiUpdateNeeded;
- }
-}
--- /dev/null
+using Content.Client.Chemistry.UI;
+using Content.Client.Items;
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Chemistry.EntitySystems;
+
+namespace Content.Client.Chemistry.EntitySystems;
+
+public sealed class HypospraySystem : SharedHypospraySystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+ Subs.ItemStatus<HyposprayComponent>(ent => new HyposprayStatusControl(ent, _solutionContainers));
+ }
+}
-using Content.Client.Chemistry.Components;
using Content.Client.Chemistry.UI;
using Content.Client.Items;
using Content.Shared.Chemistry.Components;
{
base.Initialize();
Subs.ItemStatus<InjectorComponent>(ent => new InjectorStatusControl(ent, SolutionContainers));
- SubscribeLocalEvent<HyposprayComponent, ComponentHandleState>(OnHandleHyposprayState);
- Subs.ItemStatus<HyposprayComponent>(ent => new HyposprayStatusControl(ent));
- }
-
- private void OnHandleHyposprayState(EntityUid uid, HyposprayComponent component, ref ComponentHandleState args)
- {
- if (args.Current is not HyposprayComponentState cState)
- return;
-
- component.CurrentVolume = cState.CurVolume;
- component.TotalVolume = cState.MaxVolume;
- component.UiUpdateNeeded = true;
}
}
-using Content.Client.Chemistry.Components;
using Content.Client.Message;
using Content.Client.Stylesheets;
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.FixedPoint;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Timing;
public sealed class HyposprayStatusControl : Control
{
- private readonly HyposprayComponent _parent;
+ private readonly Entity<HyposprayComponent> _parent;
private readonly RichTextLabel _label;
+ private readonly SharedSolutionContainerSystem _solutionContainers;
- public HyposprayStatusControl(HyposprayComponent parent)
+ private FixedPoint2 PrevVolume;
+ private FixedPoint2 PrevMaxVolume;
+ private bool PrevOnlyAffectsMobs;
+
+ public HyposprayStatusControl(Entity<HyposprayComponent> parent, SharedSolutionContainerSystem solutionContainers)
{
_parent = parent;
- _label = new RichTextLabel {StyleClasses = {StyleNano.StyleClassItemStatus}};
+ _solutionContainers = solutionContainers;
+ _label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
AddChild(_label);
-
- Update();
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
- if (!_parent.UiUpdateNeeded)
+
+ if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.SolutionName, out _, out var solution))
return;
- Update();
- }
- public void Update()
- {
+ // only updates the UI if any of the details are different than they previously were
+ if (PrevVolume == solution.Volume
+ && PrevMaxVolume == solution.MaxVolume
+ && PrevOnlyAffectsMobs == _parent.Comp.OnlyAffectsMobs)
+ return;
+
+ PrevVolume = solution.Volume;
+ PrevMaxVolume = solution.MaxVolume;
+ PrevOnlyAffectsMobs = _parent.Comp.OnlyAffectsMobs;
- _parent.UiUpdateNeeded = false;
+ var modeStringLocalized = Loc.GetString(_parent.Comp.OnlyAffectsMobs switch
+ {
+ false => "hypospray-all-mode-text",
+ true => "hypospray-mobs-only-mode-text",
+ });
- _label.SetMarkup(Loc.GetString(
- "hypospray-volume-text",
- ("currentVolume", _parent.CurrentVolume),
- ("totalVolume", _parent.TotalVolume)));
+ _label.SetMarkup(Loc.GetString("hypospray-volume-label",
+ ("currentVolume", solution.Volume),
+ ("totalVolume", solution.MaxVolume),
+ ("modeString", modeStringLocalized)));
}
}
namespace Content.Server.Body.Components
{
- [RegisterComponent, Access(typeof(BloodstreamSystem), (typeof(ChemistrySystem)))]
+ [RegisterComponent, Access(typeof(BloodstreamSystem), typeof(ReactionMixerSystem))]
public sealed partial class BloodstreamComponent : Component
{
public static string DefaultChemicalsSolutionName = "chemicals";
+++ /dev/null
-using Content.Shared.Chemistry.Components;
-using Content.Shared.FixedPoint;
-using Robust.Shared.Audio;
-
-namespace Content.Server.Chemistry.Components
-{
- [RegisterComponent]
- public sealed partial class HyposprayComponent : SharedHyposprayComponent
- {
- // TODO: This should be on clumsycomponent.
- [DataField("clumsyFailChance")]
- [ViewVariables(VVAccess.ReadWrite)]
- public float ClumsyFailChance = 0.5f;
-
- [DataField("transferAmount")]
- [ViewVariables(VVAccess.ReadWrite)]
- public FixedPoint2 TransferAmount = FixedPoint2.New(5);
-
- [DataField("injectSound")]
- public SoundSpecifier InjectSound = new SoundPathSpecifier("/Audio/Items/hypospray.ogg");
-
- /// <summary>
- /// Whether or not the hypo is able to inject only into mobs. On false you can inject into beakers/jugs
- /// </summary>
- [DataField("onlyMobs")]
- public bool OnlyMobs = true;
- }
-}
+++ /dev/null
-using Content.Server.Administration.Logs;
-using Content.Server.Chemistry.Containers.EntitySystems;
-using Content.Server.Interaction;
-using Content.Server.Popups;
-using Content.Shared.Chemistry;
-using Robust.Shared.Audio.Systems;
-
-namespace Content.Server.Chemistry.EntitySystems;
-
-public sealed partial class ChemistrySystem : EntitySystem
-{
- [Dependency] private readonly IAdminLogManager _adminLogger = default!;
- [Dependency] private readonly IEntityManager _entMan = default!;
- [Dependency] private readonly InteractionSystem _interaction = default!;
- [Dependency] private readonly PopupSystem _popup = default!;
- [Dependency] private readonly ReactiveSystem _reactiveSystem = default!;
- [Dependency] private readonly SharedAudioSystem _audio = default!;
- [Dependency] private readonly SolutionContainerSystem _solutionContainers = default!;
-
- public override void Initialize()
- {
- // Why ChemMaster duplicates reagentdispenser nobody knows.
- InitializeHypospray();
- InitializeMixing();
- }
-}
+++ /dev/null
-using Content.Server.Chemistry.Components;
-using Content.Server.Chemistry.Containers.EntitySystems;
-using Content.Shared.Chemistry.Components;
-using Content.Shared.Chemistry.Components.SolutionManager;
-using Content.Shared.Chemistry.EntitySystems;
-using Content.Shared.Chemistry.Reagent;
-using Content.Shared.Database;
-using Content.Shared.FixedPoint;
-using Content.Shared.Forensics;
-using Content.Shared.IdentityManagement;
-using Content.Shared.Interaction;
-using Content.Shared.Interaction.Events;
-using Content.Shared.Mobs.Components;
-using Content.Shared.Timing;
-using Content.Shared.Weapons.Melee.Events;
-using Robust.Shared.GameStates;
-using System.Diagnostics.CodeAnalysis;
-using System.Linq;
-
-namespace Content.Server.Chemistry.EntitySystems
-{
- public sealed partial class ChemistrySystem
- {
- [Dependency] private readonly UseDelaySystem _useDelay = default!;
-
- private void InitializeHypospray()
- {
- SubscribeLocalEvent<HyposprayComponent, AfterInteractEvent>(OnAfterInteract);
- SubscribeLocalEvent<HyposprayComponent, MeleeHitEvent>(OnAttack);
- SubscribeLocalEvent<HyposprayComponent, SolutionContainerChangedEvent>(OnSolutionChange);
- SubscribeLocalEvent<HyposprayComponent, UseInHandEvent>(OnUseInHand);
- SubscribeLocalEvent<HyposprayComponent, ComponentGetState>(OnHypoGetState);
- }
-
- private void OnHypoGetState(Entity<HyposprayComponent> entity, ref ComponentGetState args)
- {
- args.State = _solutionContainers.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out _, out var solution)
- ? new HyposprayComponentState(solution.Volume, solution.MaxVolume)
- : new HyposprayComponentState(FixedPoint2.Zero, FixedPoint2.Zero);
- }
-
- private void OnUseInHand(Entity<HyposprayComponent> entity, ref UseInHandEvent args)
- {
- if (args.Handled)
- return;
-
- TryDoInject(entity, args.User, args.User);
- args.Handled = true;
- }
-
- private void OnSolutionChange(Entity<HyposprayComponent> entity, ref SolutionContainerChangedEvent args)
- {
- Dirty(entity);
- }
-
- public void OnAfterInteract(Entity<HyposprayComponent> entity, ref AfterInteractEvent args)
- {
- if (!args.CanReach)
- return;
-
- var target = args.Target;
- var user = args.User;
-
- TryDoInject(entity, target, user);
- }
-
- public void OnAttack(Entity<HyposprayComponent> entity, ref MeleeHitEvent args)
- {
- if (!args.HitEntities.Any())
- return;
-
- TryDoInject(entity, args.HitEntities.First(), args.User);
- }
-
- public bool TryDoInject(Entity<HyposprayComponent> hypo, EntityUid? target, EntityUid user)
- {
- var (uid, component) = hypo;
-
- if (!EligibleEntity(target, _entMan, component))
- return false;
-
- if (TryComp(uid, out UseDelayComponent? delayComp))
- {
- if (_useDelay.IsDelayed((uid, delayComp)))
- return false;
- }
-
-
- string? msgFormat = null;
-
- if (target == user)
- msgFormat = "hypospray-component-inject-self-message";
- else if (EligibleEntity(user, _entMan, component) && _interaction.TryRollClumsy(user, component.ClumsyFailChance))
- {
- msgFormat = "hypospray-component-inject-self-clumsy-message";
- target = user;
- }
-
- if (!_solutionContainers.TryGetSolution(uid, component.SolutionName, out var hypoSpraySoln, out var hypoSpraySolution) || hypoSpraySolution.Volume == 0)
- {
- _popup.PopupCursor(Loc.GetString("hypospray-component-empty-message"), user);
- return true;
- }
-
- if (!_solutionContainers.TryGetInjectableSolution(target.Value, out var targetSoln, out var targetSolution))
- {
- _popup.PopupCursor(Loc.GetString("hypospray-cant-inject", ("target", Identity.Entity(target.Value, _entMan))), user);
- return false;
- }
-
- _popup.PopupCursor(Loc.GetString(msgFormat ?? "hypospray-component-inject-other-message", ("other", target)), user);
-
- if (target != user)
- {
- _popup.PopupEntity(Loc.GetString("hypospray-component-feel-prick-message"), target.Value, target.Value);
- // TODO: This should just be using melee attacks...
- // meleeSys.SendLunge(angle, user);
- }
-
- _audio.PlayPvs(component.InjectSound, user);
-
- // Medipens and such use this system and don't have a delay, requiring extra checks
- // BeginDelay function returns if item is already on delay
- if (delayComp != null)
- _useDelay.TryResetDelay((uid, delayComp));
-
- // Get transfer amount. May be smaller than component.TransferAmount if not enough room
- var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetSolution.AvailableVolume);
-
- if (realTransferAmount <= 0)
- {
- _popup.PopupCursor(Loc.GetString("hypospray-component-transfer-already-full-message", ("owner", target)), user);
- return true;
- }
-
- // Move units from attackSolution to targetSolution
- var removedSolution = _solutionContainers.SplitSolution(hypoSpraySoln.Value, realTransferAmount);
-
- if (!targetSolution.CanAddSolution(removedSolution))
- return true;
- _reactiveSystem.DoEntityReaction(target.Value, removedSolution, ReactionMethod.Injection);
- _solutionContainers.TryAddSolution(targetSoln.Value, removedSolution);
-
- var ev = new TransferDnaEvent { Donor = target.Value, Recipient = uid };
- RaiseLocalEvent(target.Value, ref ev);
-
- // same LogType as syringes...
- _adminLogger.Add(LogType.ForceFeed, $"{_entMan.ToPrettyString(user):user} injected {_entMan.ToPrettyString(target.Value):target} with a solution {SolutionContainerSystem.ToPrettyString(removedSolution):removedSolution} using a {_entMan.ToPrettyString(uid):using}");
-
- return true;
- }
-
- static bool EligibleEntity([NotNullWhen(true)] EntityUid? entity, IEntityManager entMan, HyposprayComponent component)
- {
- // TODO: Does checking for BodyComponent make sense as a "can be hypospray'd" tag?
- // In SS13 the hypospray ONLY works on mobs, NOT beakers or anything else.
- // But this is 14, we dont do what SS13 does just because SS13 does it.
- return component.OnlyMobs
- ? entMan.HasComponent<SolutionContainerManagerComponent>(entity) &&
- entMan.HasComponent<MobStateComponent>(entity)
- : entMan.HasComponent<SolutionContainerManagerComponent>(entity);
- }
- }
-}
--- /dev/null
+using Content.Shared.Chemistry.EntitySystems;
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Chemistry.Components.SolutionManager;
+using Content.Shared.Chemistry.Reagent;
+using Content.Shared.Database;
+using Content.Shared.FixedPoint;
+using Content.Shared.Forensics;
+using Content.Shared.IdentityManagement;
+using Content.Shared.Interaction;
+using Content.Shared.Interaction.Events;
+using Content.Shared.Mobs.Components;
+using Content.Shared.Timing;
+using Content.Shared.Weapons.Melee.Events;
+using Content.Server.Interaction;
+using Content.Server.Body.Components;
+using Content.Server.Chemistry.Containers.EntitySystems;
+using Robust.Shared.GameStates;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using Robust.Server.Audio;
+
+namespace Content.Server.Chemistry.EntitySystems;
+
+public sealed class HypospraySystem : SharedHypospraySystem
+{
+ [Dependency] private readonly AudioSystem _audio = default!;
+ [Dependency] private readonly InteractionSystem _interaction = default!;
+ [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<HyposprayComponent, AfterInteractEvent>(OnAfterInteract);
+ SubscribeLocalEvent<HyposprayComponent, MeleeHitEvent>(OnAttack);
+ SubscribeLocalEvent<HyposprayComponent, UseInHandEvent>(OnUseInHand);
+ }
+
+ private void UseHypospray(Entity<HyposprayComponent> entity, EntityUid target, EntityUid user)
+ {
+ // if target is ineligible but is a container, try to draw from the container
+ if (!EligibleEntity(target, EntityManager, entity)
+ && _solutionContainers.TryGetDrawableSolution(target, out var drawableSolution, out _))
+ {
+ TryDraw(entity, target, drawableSolution.Value, user);
+ }
+
+ TryDoInject(entity, target, user);
+ }
+
+ private void OnUseInHand(Entity<HyposprayComponent> entity, ref UseInHandEvent args)
+ {
+ if (args.Handled)
+ return;
+
+ TryDoInject(entity, args.User, args.User);
+ args.Handled = true;
+ }
+
+ public void OnAfterInteract(Entity<HyposprayComponent> entity, ref AfterInteractEvent args)
+ {
+ if (args.Handled || !args.CanReach || args.Target == null)
+ return;
+
+ UseHypospray(entity, args.Target.Value, args.User);
+ args.Handled = true;
+ }
+
+ public void OnAttack(Entity<HyposprayComponent> entity, ref MeleeHitEvent args)
+ {
+ if (!args.HitEntities.Any())
+ return;
+
+ TryDoInject(entity, args.HitEntities.First(), args.User);
+ }
+
+ public bool TryDoInject(Entity<HyposprayComponent> entity, EntityUid target, EntityUid user)
+ {
+ var (uid, component) = entity;
+
+ if (!EligibleEntity(target, EntityManager, component))
+ return false;
+
+ if (TryComp(uid, out UseDelayComponent? delayComp))
+ {
+ if (_useDelay.IsDelayed((uid, delayComp)))
+ return false;
+ }
+
+ string? msgFormat = null;
+
+ if (target == user)
+ msgFormat = "hypospray-component-inject-self-message";
+ else if (EligibleEntity(user, EntityManager, component) && _interaction.TryRollClumsy(user, component.ClumsyFailChance))
+ {
+ msgFormat = "hypospray-component-inject-self-clumsy-message";
+ target = user;
+ }
+
+ if (!_solutionContainers.TryGetSolution(uid, component.SolutionName, out var hypoSpraySoln, out var hypoSpraySolution) || hypoSpraySolution.Volume == 0)
+ {
+ _popup.PopupEntity(Loc.GetString("hypospray-component-empty-message"), target, user);
+ return true;
+ }
+
+ if (!_solutionContainers.TryGetInjectableSolution(target, out var targetSoln, out var targetSolution))
+ {
+ _popup.PopupEntity(Loc.GetString("hypospray-cant-inject", ("target", Identity.Entity(target, EntityManager))), target, user);
+ return false;
+ }
+
+ _popup.PopupEntity(Loc.GetString(msgFormat ?? "hypospray-component-inject-other-message", ("other", target)), target, user);
+
+ if (target != user)
+ {
+ _popup.PopupEntity(Loc.GetString("hypospray-component-feel-prick-message"), target, target);
+ // TODO: This should just be using melee attacks...
+ // meleeSys.SendLunge(angle, user);
+ }
+
+ _audio.PlayPvs(component.InjectSound, user);
+
+ // Medipens and such use this system and don't have a delay, requiring extra checks
+ // BeginDelay function returns if item is already on delay
+ if (delayComp != null)
+ _useDelay.TryResetDelay((uid, delayComp));
+
+ // Get transfer amount. May be smaller than component.TransferAmount if not enough room
+ var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetSolution.AvailableVolume);
+
+ if (realTransferAmount <= 0)
+ {
+ _popup.PopupEntity(Loc.GetString("hypospray-component-transfer-already-full-message", ("owner", target)), target, user);
+ return true;
+ }
+
+ // Move units from attackSolution to targetSolution
+ var removedSolution = _solutionContainers.SplitSolution(hypoSpraySoln.Value, realTransferAmount);
+
+ if (!targetSolution.CanAddSolution(removedSolution))
+ return true;
+ _reactiveSystem.DoEntityReaction(target, removedSolution, ReactionMethod.Injection);
+ _solutionContainers.TryAddSolution(targetSoln.Value, removedSolution);
+
+ var ev = new TransferDnaEvent { Donor = target, Recipient = uid };
+ RaiseLocalEvent(target, ref ev);
+
+ // same LogType as syringes...
+ _adminLogger.Add(LogType.ForceFeed, $"{EntityManager.ToPrettyString(user):user} injected {EntityManager.ToPrettyString(target):target} with a solution {SolutionContainerSystem.ToPrettyString(removedSolution):removedSolution} using a {EntityManager.ToPrettyString(uid):using}");
+
+ return true;
+ }
+
+ private void TryDraw(Entity<HyposprayComponent> entity, Entity<BloodstreamComponent?> target, Entity<SolutionComponent> targetSolution, EntityUid user)
+ {
+ if (!_solutionContainers.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln,
+ out var solution) || solution.AvailableVolume == 0)
+ {
+ return;
+ }
+
+ // Get transfer amount. May be smaller than _transferAmount if not enough room, also make sure there's room in the injector
+ var realTransferAmount = FixedPoint2.Min(entity.Comp.TransferAmount, targetSolution.Comp.Solution.Volume,
+ solution.AvailableVolume);
+
+ if (realTransferAmount <= 0)
+ {
+ _popup.PopupEntity(
+ Loc.GetString("injector-component-target-is-empty-message",
+ ("target", Identity.Entity(target, EntityManager))),
+ entity.Owner, user);
+ return;
+ }
+
+ var removedSolution = _solutionContainers.Draw(target.Owner, targetSolution, realTransferAmount);
+
+ if (!_solutionContainers.TryAddSolution(soln.Value, removedSolution))
+ {
+ return;
+ }
+
+ _popup.PopupEntity(Loc.GetString("injector-component-draw-success-message",
+ ("amount", removedSolution.Volume),
+ ("target", Identity.Entity(target, EntityManager))), entity.Owner, user);
+ }
+
+ private bool EligibleEntity(EntityUid entity, IEntityManager entMan, HyposprayComponent component)
+ {
+ // TODO: Does checking for BodyComponent make sense as a "can be hypospray'd" tag?
+ // In SS13 the hypospray ONLY works on mobs, NOT beakers or anything else.
+ // But this is 14, we dont do what SS13 does just because SS13 does it.
+ return component.OnlyAffectsMobs
+ ? entMan.HasComponent<SolutionContainerManagerComponent>(entity) &&
+ entMan.HasComponent<MobStateComponent>(entity)
+ : entMan.HasComponent<SolutionContainerManagerComponent>(entity);
+ }
+}
using Content.Shared.Chemistry.Reaction;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction;
+using Content.Server.Chemistry.Containers.EntitySystems;
+using Content.Server.Popups;
namespace Content.Server.Chemistry.EntitySystems;
-public sealed partial class ChemistrySystem
+public sealed partial class ReactionMixerSystem : EntitySystem
{
- public void InitializeMixing()
+ [Dependency] private readonly PopupSystem _popup = default!;
+ [Dependency] private readonly SolutionContainerSystem _solutionContainers = default!;
+
+ public override void Initialize()
{
+ base.Initialize();
+
SubscribeLocalEvent<ReactionMixerComponent, AfterInteractEvent>(OnAfterInteract);
}
--- /dev/null
+using Content.Shared.FixedPoint;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+using Robust.Shared.Audio;
+
+namespace Content.Shared.Chemistry.Components;
+
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class HyposprayComponent : Component
+{
+ [DataField]
+ public string SolutionName = "hypospray";
+
+ // TODO: This should be on clumsycomponent.
+ [DataField]
+ [ViewVariables(VVAccess.ReadWrite)]
+ public float ClumsyFailChance = 0.5f;
+
+ [DataField]
+ [ViewVariables(VVAccess.ReadWrite)]
+ public FixedPoint2 TransferAmount = FixedPoint2.New(5);
+
+ [DataField]
+ public SoundSpecifier InjectSound = new SoundPathSpecifier("/Audio/Items/hypospray.ogg");
+
+ /// <summary>
+ /// Decides whether you can inject everything or just mobs.
+ /// When you can only affect mobs, you're capable of drawing from beakers.
+ /// </summary>
+ [AutoNetworkedField]
+ [DataField(required: true)]
+ public bool OnlyAffectsMobs = false;
+}
+++ /dev/null
-using Content.Shared.FixedPoint;
-using Robust.Shared.GameStates;
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.Chemistry.Components;
-
-[NetworkedComponent()]
-public abstract partial class SharedHyposprayComponent : Component
-{
- [DataField("solutionName")]
- public string SolutionName = "hypospray";
-}
-
-[Serializable, NetSerializable]
-public sealed class HyposprayComponentState : ComponentState
-{
- public FixedPoint2 CurVolume { get; }
- public FixedPoint2 MaxVolume { get; }
-
- public HyposprayComponentState(FixedPoint2 curVolume, FixedPoint2 maxVolume)
- {
- CurVolume = curVolume;
- MaxVolume = maxVolume;
- }
-}
--- /dev/null
+using Content.Shared.Chemistry.Components;
+using Content.Shared.Timing;
+using Content.Shared.Verbs;
+using Content.Shared.Popups;
+using Robust.Shared.Player;
+using Content.Shared.Administration.Logs;
+
+namespace Content.Shared.Chemistry.EntitySystems;
+
+public abstract class SharedHypospraySystem : EntitySystem
+{
+ [Dependency] protected readonly UseDelaySystem _useDelay = default!;
+ [Dependency] protected readonly SharedPopupSystem _popup = default!;
+ [Dependency] protected readonly SharedSolutionContainerSystem _solutionContainers = default!;
+ [Dependency] protected readonly ISharedAdminLogManager _adminLogger = default!;
+ [Dependency] protected readonly ReactiveSystem _reactiveSystem = default!;
+
+ public override void Initialize()
+ {
+ SubscribeLocalEvent<HyposprayComponent, GetVerbsEvent<AlternativeVerb>>(AddToggleModeVerb);
+ }
+
+ // <summary>
+ // Uses the OnlyMobs field as a check to implement the ability
+ // to draw from jugs and containers with the hypospray
+ // Toggleable to allow people to inject containers if they prefer it over drawing
+ // </summary>
+ private void AddToggleModeVerb(Entity<HyposprayComponent> entity, ref GetVerbsEvent<AlternativeVerb> args)
+ {
+ if (!args.CanAccess || !args.CanInteract || args.Hands == null)
+ return;
+
+ var (_, component) = entity;
+ var user = args.User;
+ var verb = new AlternativeVerb
+ {
+ Text = Loc.GetString("hypospray-verb-mode-label"),
+ Act = () =>
+ {
+ ToggleMode(entity, user);
+ }
+ };
+ args.Verbs.Add(verb);
+ }
+
+ private void ToggleMode(Entity<HyposprayComponent> entity, EntityUid user)
+ {
+ SetMode(entity, !entity.Comp.OnlyAffectsMobs);
+ string msg = entity.Comp.OnlyAffectsMobs ? "hypospray-verb-mode-inject-mobs-only" : "hypospray-verb-mode-inject-all";
+ _popup.PopupClient(Loc.GetString(msg), entity, user);
+ }
+
+ public void SetMode(Entity<HyposprayComponent> entity, bool onlyAffectsMobs)
+ {
+ if (entity.Comp.OnlyAffectsMobs == onlyAffectsMobs)
+ return;
+
+ entity.Comp.OnlyAffectsMobs = onlyAffectsMobs;
+ Dirty(entity);
+ }
+}
if (!args.CanAccess || !args.CanInteract || args.Hands == null)
return;
- if (!HasComp<ActorComponent>(args.User))
- return;
var user = args.User;
-
var (_, component) = entity;
var min = component.MinimumTransferAmount;
## UI
-hypospray-volume-text = Volume: [color=white]{$currentVolume}/{$totalVolume}[/color]
+hypospray-all-mode-text = Only Injects
+hypospray-mobs-only-mode-text = Draws and Injects
+hypospray-invalid-text = Invalid
+hypospray-volume-label = Volume: [color=white]{$currentVolume}/{$totalVolume}u[/color]
+ Mode: [color=white]{$modeString}[/color]
## Entity
hypospray-component-inject-other-message = You inject {$other}.
hypospray-component-inject-self-message = You inject yourself.
hypospray-component-inject-self-clumsy-message = Oops! You injected yourself.
-hypospray-component-empty-message = It's empty!
+hypospray-component-empty-message = Nothing to inject.
hypospray-component-feel-prick-message = You feel a tiny prick!
hypospray-component-transfer-already-full-message = {$owner} is already full!
hypospray-cant-inject = Can't inject into {$target}!
+
+hypospray-verb-mode-label = Toggle Container Draw
+hypospray-verb-mode-inject-all = You cannot draw from containers anymore.
+hypospray-verb-mode-inject-mobs-only = You can now draw from containers.
- type: ExaminableSolution
solution: hypospray
- type: Hypospray
- onlyMobs: false
+ onlyAffectsMobs: false
- type: UseDelay
delay: 0.5
- type: StaticPrice
- type: ExaminableSolution
solution: hypospray
- type: Hypospray
- onlyMobs: false
+ onlyAffectsMobs: false
- type: UseDelay
delay: 0.5
- type: ExaminableSolution
solution: hypospray
- type: Hypospray
+ onlyAffectsMobs: false
- type: UseDelay
delay: 0.5
- type: Hypospray
solutionName: pen
transferAmount: 15
+ onlyAffectsMobs: false
- type: Appearance
- type: SolutionContainerVisuals
maxFillLevels: 1
- type: Hypospray
solutionName: pen
transferAmount: 20
+ onlyAffectsMobs: false
- type: SolutionContainerManager
solutions:
pen:
- type: Hypospray
solutionName: pen
transferAmount: 20
+ onlyAffectsMobs: false
- type: SolutionContainerManager
solutions:
pen:
- type: Hypospray
solutionName: pen
transferAmount: 20
+ onlyAffectsMobs: false
- type: SolutionContainerManager
solutions:
pen:
- type: Hypospray
solutionName: pen
transferAmount: 30
+ onlyAffectsMobs: false
- type: SolutionContainerManager
solutions:
pen:
- type: Hypospray
solutionName: pen
transferAmount: 30
+ onlyAffectsMobs: false
- type: StaticPrice
price: 500
- type: Tag
- type: Hypospray
solutionName: pen
transferAmount: 30
+ onlyAffectsMobs: false
- type: StaticPrice
price: 500
- type: Tag
- type: ExaminableSolution
solution: hypospray
- type: Hypospray
- onlyMobs: false
+ onlyAffectsMobs: false
- type: UseDelay
delay: 0.5
- type: StaticPrice # A new shitcurity meta