-using Content.Shared.Chemistry.EntitySystems;
-using Content.Shared.Chemistry.Components;
+using Content.Shared.Administration.Logs;
using Content.Shared.Chemistry.Components.SolutionManager;
+using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Hypospray.Events;
-using Content.Shared.Chemistry;
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.Interaction;
using Content.Shared.Mobs.Components;
+using Content.Shared.Popups;
using Content.Shared.Timing;
+using Content.Shared.Verbs;
using Content.Shared.Weapons.Melee.Events;
-using Content.Server.Body.Components;
-using System.Linq;
-using Robust.Server.Audio;
+using Robust.Shared.Audio.Systems;
-namespace Content.Server.Chemistry.EntitySystems;
+namespace Content.Shared.Chemistry.EntitySystems;
-public sealed class HypospraySystem : SharedHypospraySystem
+public sealed class HypospraySystem : EntitySystem
{
- [Dependency] private readonly AudioSystem _audio = default!;
+ [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
+ [Dependency] private readonly ReactiveSystem _reactiveSystem = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly SharedPopupSystem _popup = default!;
+ [Dependency] private readonly SharedSolutionContainerSystem _solutionContainers = default!;
+ [Dependency] private readonly UseDelaySystem _useDelay = default!;
public override void Initialize()
{
SubscribeLocalEvent<HyposprayComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<HyposprayComponent, MeleeHitEvent>(OnAttack);
SubscribeLocalEvent<HyposprayComponent, UseInHandEvent>(OnUseInHand);
+ SubscribeLocalEvent<HyposprayComponent, GetVerbsEvent<AlternativeVerb>>(AddToggleModeVerb);
}
- private bool TryUseHypospray(Entity<HyposprayComponent> entity, EntityUid target, EntityUid user)
- {
- // if target is ineligible but is a container, try to draw from the container if allowed
- if (entity.Comp.CanContainerDraw
- && !EligibleEntity(target, EntityManager, entity)
- && _solutionContainers.TryGetDrawableSolution(target, out var drawableSolution, out _))
- {
- return TryDraw(entity, target, drawableSolution.Value, user);
- }
-
- return TryDoInject(entity, target, user);
- }
-
+ #region Ref events
private void OnUseInHand(Entity<HyposprayComponent> entity, ref UseInHandEvent args)
{
if (args.Handled)
args.Handled = TryDoInject(entity, args.User, args.User);
}
- public void OnAfterInteract(Entity<HyposprayComponent> entity, ref AfterInteractEvent args)
+ private void OnAfterInteract(Entity<HyposprayComponent> entity, ref AfterInteractEvent args)
{
if (args.Handled || !args.CanReach || args.Target == null)
return;
args.Handled = TryUseHypospray(entity, args.Target.Value, args.User);
}
- public void OnAttack(Entity<HyposprayComponent> entity, ref MeleeHitEvent args)
+ private void OnAttack(Entity<HyposprayComponent> entity, ref MeleeHitEvent args)
{
- if (!args.HitEntities.Any())
+ if (args.HitEntities is [])
return;
- TryDoInject(entity, args.HitEntities.First(), args.User);
+ TryDoInject(entity, args.HitEntities[0], args.User);
+ }
+
+ #endregion
+
+ #region Draw/Inject
+ private bool TryUseHypospray(Entity<HyposprayComponent> entity, EntityUid target, EntityUid user)
+ {
+ // if target is ineligible but is a container, try to draw from the container if allowed
+ if (entity.Comp.CanContainerDraw
+ && !EligibleEntity(target, entity)
+ && _solutionContainers.TryGetDrawableSolution(target, out var drawableSolution, out _))
+ {
+ return TryDraw(entity, target, drawableSolution.Value, user);
+ }
+
+ return TryDoInject(entity, target, user);
}
public bool TryDoInject(Entity<HyposprayComponent> entity, EntityUid target, EntityUid user)
{
var (uid, component) = entity;
- if (!EligibleEntity(target, EntityManager, component))
+ if (!EligibleEntity(target, component))
return false;
if (TryComp(uid, out UseDelayComponent? delayComp))
if (selfEvent.Cancelled)
{
- _popup.PopupEntity(Loc.GetString(selfEvent.InjectMessageOverride ?? "hypospray-cant-inject", ("owner", Identity.Entity(target, EntityManager))), target, user);
+ _popup.PopupClient(Loc.GetString(selfEvent.InjectMessageOverride ?? "hypospray-cant-inject", ("owner", Identity.Entity(target, EntityManager))), target, user);
return false;
}
target = selfEvent.TargetGettingInjected;
- if (!EligibleEntity(target, EntityManager, component))
+ if (!EligibleEntity(target, component))
return false;
// Target event
if (targetEvent.Cancelled)
{
- _popup.PopupEntity(Loc.GetString(targetEvent.InjectMessageOverride ?? "hypospray-cant-inject", ("owner", Identity.Entity(target, EntityManager))), target, user);
+ _popup.PopupClient(Loc.GetString(targetEvent.InjectMessageOverride ?? "hypospray-cant-inject", ("owner", Identity.Entity(target, EntityManager))), target, user);
return false;
}
target = targetEvent.TargetGettingInjected;
- if (!EligibleEntity(target, EntityManager, component))
+ if (!EligibleEntity(target, component))
return false;
// The target event gets priority for the overriden message.
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);
+ _popup.PopupClient(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);
+ _popup.PopupClient(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);
+ _popup.PopupClient(Loc.GetString(msgFormat ?? "hypospray-component-inject-other-message", ("other", target)), target, user);
if (target != user)
{
// meleeSys.SendLunge(angle, user);
}
- _audio.PlayPvs(component.InjectSound, user);
+ _audio.PlayPredicted(component.InjectSound, target, 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 (realTransferAmount <= 0)
{
- _popup.PopupEntity(Loc.GetString("hypospray-component-transfer-already-full-message", ("owner", target)), target, user);
+ _popup.PopupClient(Loc.GetString("hypospray-component-transfer-already-full-message", ("owner", target)), target, user);
return true;
}
return true;
}
- private bool TryDraw(Entity<HyposprayComponent> entity, Entity<BloodstreamComponent?> target, Entity<SolutionComponent> targetSolution, EntityUid user)
+ private bool TryDraw(Entity<HyposprayComponent> entity, EntityUid target, Entity<SolutionComponent> targetSolution, EntityUid user)
{
if (!_solutionContainers.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln,
out var solution) || solution.AvailableVolume == 0)
if (realTransferAmount <= 0)
{
- _popup.PopupEntity(
+ _popup.PopupClient(
Loc.GetString("injector-component-target-is-empty-message",
("target", Identity.Entity(target, EntityManager))),
entity.Owner, user);
return false;
}
- var removedSolution = _solutionContainers.Draw(target.Owner, targetSolution, realTransferAmount);
+ var removedSolution = _solutionContainers.Draw(target, targetSolution, realTransferAmount);
if (!_solutionContainers.TryAddSolution(soln.Value, removedSolution))
{
return false;
}
- _popup.PopupEntity(Loc.GetString("injector-component-draw-success-message",
+ _popup.PopupClient(Loc.GetString("injector-component-draw-success-message",
("amount", removedSolution.Volume),
("target", Identity.Entity(target, EntityManager))), entity.Owner, user);
return true;
}
- private bool EligibleEntity(EntityUid entity, IEntityManager entMan, HyposprayComponent component)
+ private bool EligibleEntity(EntityUid entity, 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);
+ ? HasComp<SolutionContainerManagerComponent>(entity) &&
+ HasComp<MobStateComponent>(entity)
+ : HasComp<SolutionContainerManagerComponent>(entity);
}
+
+ #endregion
+
+ #region Verbs
+
+ // <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 || entity.Comp.InjectOnly)
+ return;
+
+ 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);
+ var msg = (entity.Comp.OnlyAffectsMobs && entity.Comp.CanContainerDraw) ? "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);
+ }
+
+ #endregion
}