Files
tbd-station-14/Content.Shared/_Offbrand/Wounds/TendingSystem.cs
Janet Blackquill 300a78a791 Fix floating damages
2025-09-29 22:20:31 -04:00

167 lines
5.8 KiB
C#

using Content.Shared.Damage;
using Content.Shared.DoAfter;
using Content.Shared.IdentityManagement;
using Content.Shared.Interaction.Events;
using Content.Shared.Interaction;
using Content.Shared.Medical.Healing;
using Content.Shared.Popups;
using Content.Shared.Stacks;
using Content.Shared.StatusEffectNew;
using Content.Shared.Whitelist;
using Robust.Shared.Audio.Systems;
namespace Content.Shared._Offbrand.Wounds;
public sealed class TendingSystem : EntitySystem
{
[Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
[Dependency] private readonly SharedStackSystem _stack = default!;
[Dependency] private readonly StatusEffectsSystem _statusEffects = default!;
[Dependency] private readonly WoundableSystem _woundable = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<TendingComponent, UseInHandEvent>(OnUseInHand, before: new[] { typeof(HealingSystem) });
SubscribeLocalEvent<TendingComponent, AfterInteractEvent>(OnAfterInteract, before: new[] { typeof(HealingSystem) });
SubscribeLocalEvent<TendableWoundComponent, TendingDoAfterEvent>(OnTendingDoAfter);
}
private void OnUseInHand(Entity<TendingComponent> ent, ref UseInHandEvent args)
{
if (args.Handled)
return;
if (TryTend(ent, args.User, args.User))
args.Handled = true;
}
private void OnAfterInteract(Entity<TendingComponent> ent, ref AfterInteractEvent args)
{
if (args.Handled || !args.CanReach || args.Target == null)
return;
if (TryTend(ent, args.Target.Value, args.User))
args.Handled = true;
}
private Entity<TendableWoundComponent, WoundComponent>? GetWoundToTend(Entity<TendingComponent> ent, Entity<WoundableComponent?> target)
{
if (!_statusEffects.TryEffectsWithComp<TendableWoundComponent>(target, out var effects))
{
return null;
}
foreach (var wound in effects)
{
if (wound.Comp1.Tended)
continue;
if (!_entityWhitelist.CheckBoth(wound, ent.Comp.WoundBlacklist, ent.Comp.WoundWhitelist))
continue;
return (wound.Owner, wound.Comp1, Comp<WoundComponent>(wound));
}
return null;
}
private bool TryTend(Entity<TendingComponent> ent, Entity<WoundableComponent?> target, EntityUid user, bool isRepeat = false)
{
if (!Resolve(target, ref target.Comp, false))
return false;
var woundToTend = GetWoundToTend(ent, target);
if (woundToTend is not { } foundWound)
{
if (isRepeat)
_popup.PopupClient(Loc.GetString(ent.Comp.NothingToTendRepeat, ("target", Identity.Entity(target, EntityManager)), ("tending", ent)), user);
else
_popup.PopupClient(Loc.GetString(ent.Comp.NothingToTend, ("target", Identity.Entity(target, EntityManager)), ("tending", ent)), user);
return true;
}
if (user != target.Owner && !_interaction.InRangeUnobstructed(user, target.Owner, popup: true))
return false;
if (TryComp<StackComponent>(ent, out var stack) && stack.Count < 1)
return false;
_audio.PlayPredicted(ent.Comp.TendingBeginSound, ent, user);
var differentTarget = user != target.Owner;
var delay = ent.Comp.Delay;
if (!differentTarget)
delay *= ent.Comp.SelfTendPenaltyModifier;
if (differentTarget)
{
_popup.PopupPredicted(
Loc.GetString(ent.Comp.UserPopup, ("target", Identity.Entity(target, EntityManager)), ("tending", ent), ("wound", foundWound)),
Loc.GetString(ent.Comp.OtherPopup, ("user", Identity.Entity(user, EntityManager)), ("target", Identity.Entity(target, EntityManager)), ("tending", ent), ("wound", foundWound)),
target,
user
);
}
else
{
_popup.PopupClient(Loc.GetString(ent.Comp.SelfPopup, ("tending", ent), ("wound", foundWound)), user);
}
var args =
new DoAfterArgs(EntityManager, user, delay, new TendingDoAfterEvent(), foundWound, target: target, used: ent)
{
NeedHand = true,
BreakOnMove = true,
BreakOnWeightlessMove = false,
};
_doAfter.TryStartDoAfter(args);
return true;
}
private void OnTendingDoAfter(Entity<TendableWoundComponent> ent, ref TendingDoAfterEvent args)
{
if (args.Handled || args.Cancelled || args.Args.Target is not { } target)
return;
if (!TryComp<TendingComponent>(args.Used, out var tending))
return;
_woundable.TendWound(target, ent, tending.Damage);
var hasMoreItems = true;
if (TryComp<StackComponent>(args.Used.Value, out var stackComp))
{
_stack.Use(args.Used.Value, 1, stackComp);
if (_stack.GetCount(args.Used.Value, stackComp) <= 0)
hasMoreItems = false;
}
else
{
hasMoreItems = false;
PredictedQueueDel(args.Used.Value);
}
_audio.PlayPredicted(tending.TendingEndSound, target, args.User);
if (hasMoreItems)
{
TryTend((args.Used.Value, tending), target, args.Args.User, true);
}
else
{
_popup.PopupClient(Loc.GetString(tending.UsedUp, ("tending", args.Used.Value)), args.Args.User);
}
}
}