[Feat] CPR (#11)
* CPR ready * Cycled DoAfter * One more check * last fixes * Update damage * some code clean up
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Shared.Damage;
|
||||
using Robust.Shared.Audio; // WD
|
||||
|
||||
namespace Content.Server.Body.Components
|
||||
{
|
||||
@@ -60,6 +61,15 @@ namespace Content.Server.Body.Components
|
||||
public float CycleDelay = 2.0f;
|
||||
|
||||
public float AccumulatedFrametime;
|
||||
|
||||
// WD start
|
||||
[DataField("CPRSound")]
|
||||
public SoundSpecifier CPRSound { get; set; } = new SoundPathSpecifier("/White/Audio/CPR.ogg");
|
||||
|
||||
public IPlayingAudioStream? CPRPlayingStream;
|
||||
|
||||
public EntityUid? CPRPerformedBy = null;
|
||||
// WD end
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,14 +3,28 @@ using Content.Server.Atmos;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Server.Chemistry.Containers.EntitySystems;
|
||||
using Content.Server.DoAfter;
|
||||
using Content.Server.Nutrition.Components; // WD
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.ActionBlocker; // WD
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.DoAfter; // WD
|
||||
using Content.Shared.IdentityManagement; // WD
|
||||
using Content.Shared.Interaction; // WD
|
||||
using Content.Shared.Inventory; // WD
|
||||
using Content.Shared.Mobs; // WD
|
||||
using Content.Shared.Mobs.Components; // WD
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Popups; // WD
|
||||
using Content.Shared.White.CPR.Events; // WD
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.Audio; // WD
|
||||
using Robust.Shared.Audio; // WD
|
||||
// WD removed
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Body.Systems
|
||||
@@ -28,6 +42,11 @@ namespace Content.Server.Body.Systems
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!; // WD
|
||||
[Dependency] private readonly ActionBlockerSystem _blocker = default!; // WD
|
||||
[Dependency] private readonly AudioSystem _audio = default!; // WD
|
||||
[Dependency] private readonly DoAfterSystem _doAfter = default!; // WD
|
||||
[Dependency] private readonly DamageableSystem _damageable = default!; // WD
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -36,6 +55,8 @@ namespace Content.Server.Body.Systems
|
||||
// We want to process lung reagents before we inhale new reagents.
|
||||
UpdatesAfter.Add(typeof(MetabolizerSystem));
|
||||
SubscribeLocalEvent<RespiratorComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
|
||||
SubscribeLocalEvent<RespiratorComponent, InteractHandEvent>(OnHandInteract); // WD
|
||||
SubscribeLocalEvent<RespiratorComponent, CPREndedEvent>(OnCPRDoAfterEnd); // WD
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
@@ -209,6 +230,118 @@ namespace Content.Server.Body.Systems
|
||||
if (component.AccumulatedFrametime >= component.CycleDelay)
|
||||
component.AccumulatedFrametime = component.CycleDelay;
|
||||
}
|
||||
|
||||
// WD start
|
||||
private void OnHandInteract(EntityUid uid, RespiratorComponent component, InteractHandEvent args)
|
||||
{
|
||||
args.Handled = true;
|
||||
|
||||
if (CanCPR(uid, component, args.User))
|
||||
DoCPR(uid, component, args.User);
|
||||
}
|
||||
|
||||
private bool CanCPR(EntityUid target, RespiratorComponent comp, EntityUid user)
|
||||
{
|
||||
if (!_blocker.CanInteract(user, target))
|
||||
return false;
|
||||
|
||||
if (target == user)
|
||||
return false;
|
||||
|
||||
if (comp.CPRPerformedBy != null && comp.CPRPerformedBy != user)
|
||||
return false;
|
||||
|
||||
if (!TryComp<BodyComponent>(target, out var body))
|
||||
return false;
|
||||
|
||||
if (body.Prototype?.Id is not ("Human" or "Diona" or "Slime" or "Dwarf" or "Reptilian" or "Skrell" or "Arachnid"))
|
||||
return false;
|
||||
|
||||
if (!TryComp(target, out MobStateComponent? targetState))
|
||||
return false;
|
||||
|
||||
if (targetState.CurrentState == MobState.Dead)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("cpr-too-late", ("target", Identity.Entity(target, EntityManager))), target, user);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (targetState.CurrentState != MobState.Critical)
|
||||
return false;
|
||||
|
||||
if (_inventorySystem.TryGetSlotEntity(user, "mask", out var maskUidUser) &&
|
||||
EntityManager.TryGetComponent<IngestionBlockerComponent>(maskUidUser, out var blockerUser) &&
|
||||
blockerUser.Enabled)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("cpr-mask-block-user"), user, user);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_inventorySystem.TryGetSlotEntity(target, "mask", out var maskUidTarget) &&
|
||||
EntityManager.TryGetComponent<IngestionBlockerComponent>(maskUidTarget, out var blockerTarget) &&
|
||||
blockerTarget.Enabled)
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("cpr-mask-block-target", ("target", Identity.Entity(target, EntityManager))), target, user);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void DoCPR(EntityUid target, RespiratorComponent comp, EntityUid user)
|
||||
{
|
||||
|
||||
var doAfterEventArgs = new DoAfterArgs(EntityManager, user, comp.CycleDelay * 4, new CPREndedEvent(user, target), target, target: target)
|
||||
{
|
||||
BreakOnTargetMove = true,
|
||||
BreakOnUserMove = true,
|
||||
BreakOnDamage = true,
|
||||
NeedHand = true,
|
||||
BreakOnHandChange = true
|
||||
};
|
||||
|
||||
if (!_doAfter.TryStartDoAfter(doAfterEventArgs))
|
||||
{
|
||||
_popupSystem.PopupEntity(Loc.GetString("cpr-failed"), user, user);
|
||||
return;
|
||||
}
|
||||
|
||||
comp.CPRPerformedBy = user;
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("cpr-started", ("target", Identity.Entity(target, EntityManager)), ("user", Identity.Entity(user, EntityManager))), target, PopupType.Medium);
|
||||
comp.CPRPlayingStream = _audio.PlayPvs(comp.CPRSound, target, audioParams: AudioParams.Default.WithVolume(-3f).WithLoop(true));
|
||||
|
||||
_adminLogger.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(user):entity} начал произовдить СЛР на {ToPrettyString(target):entity}");
|
||||
}
|
||||
|
||||
private void OnCPRDoAfterEnd(EntityUid uid, RespiratorComponent component, CPREndedEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (args.Cancelled || !TryComp<MobStateComponent>(args.Target, out var targetState) || targetState!.CurrentState != MobState.Critical)
|
||||
{
|
||||
component.CPRPlayingStream?.Stop();
|
||||
component.CPRPerformedBy = null;
|
||||
_popupSystem.PopupEntity(Loc.GetString("cpr-failed"), args.User, args.User);
|
||||
_adminLogger.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(args.User):entity} не удалось произвести СЛР на {ToPrettyString(args.Target):entity}");
|
||||
return;
|
||||
}
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
_damageable.TryChangeDamage(uid, -component.Damage * 2, true, false);
|
||||
|
||||
_popupSystem.PopupEntity(Loc.GetString("cpr-cycle-ended", ("target", Identity.Entity(uid, EntityManager)), ("user", Identity.Entity(args.User, EntityManager))), uid);
|
||||
|
||||
_adminLogger.Add(LogType.Action, LogImpact.High, $"{ToPrettyString(args.User):entity} произвёл СЛР на {ToPrettyString(args.Target):entity}");
|
||||
|
||||
if (CanCPR(args.Target, component, args.User))
|
||||
args.Repeat = true;
|
||||
else
|
||||
component.CPRPerformedBy = null;
|
||||
}
|
||||
//WD end
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
26
Content.Shared/White/CPR/Events/CPREndedEvent.cs
Normal file
26
Content.Shared/White/CPR/Events/CPREndedEvent.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Content.Shared.DoAfter;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.White.CPR.Events;
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class CPREndedEvent : DoAfterEvent
|
||||
{
|
||||
[DataField("user", required: true)]
|
||||
public readonly EntityUid User = default!;
|
||||
|
||||
[DataField("target", required: true)]
|
||||
public readonly EntityUid Target = default!;
|
||||
|
||||
private CPREndedEvent()
|
||||
{
|
||||
}
|
||||
|
||||
public CPREndedEvent(EntityUid user, EntityUid target)
|
||||
{
|
||||
User = user;
|
||||
Target = target;
|
||||
}
|
||||
|
||||
public override DoAfterEvent Clone() => this;
|
||||
}
|
||||
6
Resources/Locale/ru-RU/white/cpr/cpr.ftl
Normal file
6
Resources/Locale/ru-RU/white/cpr/cpr.ftl
Normal file
@@ -0,0 +1,6 @@
|
||||
cpr-too-late = {CAPITALIZE($target)} мертво
|
||||
cpr-mask-block-user = Не могу сделать СЛР в маске
|
||||
cpr-mask-block-target = Сначала нужно снять маску с {$target}
|
||||
cpr-failed = Не удалось произвести СЛР
|
||||
cpr-started = {$user} начал производить СЛР на {$target}
|
||||
cpr-cycle-ended = {$user} произвёл СЛР на {$target}
|
||||
BIN
Resources/White/Audio/CPR.ogg
Normal file
BIN
Resources/White/Audio/CPR.ogg
Normal file
Binary file not shown.
Reference in New Issue
Block a user