Defibrillator (#15922)
Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
@@ -1,4 +1,4 @@
|
|||||||
<DefaultWindow
|
<DefaultWindow
|
||||||
xmlns="https://spacestation14.io"
|
xmlns="https://spacestation14.io"
|
||||||
xmlns:adminTab="clr-namespace:Content.Client.Administration.UI.Tabs.AdminTab"
|
xmlns:adminTab="clr-namespace:Content.Client.Administration.UI.Tabs.AdminTab"
|
||||||
xmlns:adminbusTab="clr-namespace:Content.Client.Administration.UI.Tabs.AdminbusTab"
|
xmlns:adminbusTab="clr-namespace:Content.Client.Administration.UI.Tabs.AdminbusTab"
|
||||||
|
|||||||
@@ -31,3 +31,4 @@ namespace Content.Client.Administration.UI
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
44
Content.Client/Ghost/UI/ReturnToBodyEui.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using Content.Client.Eui;
|
||||||
|
using Content.Shared.Ghost;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
|
||||||
|
namespace Content.Client.Ghost.UI;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public sealed class ReturnToBodyEui : BaseEui
|
||||||
|
{
|
||||||
|
private readonly ReturnToBodyMenu _menu;
|
||||||
|
|
||||||
|
public ReturnToBodyEui()
|
||||||
|
{
|
||||||
|
_menu = new ReturnToBodyMenu();
|
||||||
|
|
||||||
|
_menu.DenyButton.OnPressed += _ =>
|
||||||
|
{
|
||||||
|
SendMessage(new ReturnToBodyMessage(false));
|
||||||
|
_menu.Close();
|
||||||
|
};
|
||||||
|
|
||||||
|
_menu.AcceptButton.OnPressed += _ =>
|
||||||
|
{
|
||||||
|
SendMessage(new ReturnToBodyMessage(true));
|
||||||
|
_menu.Close();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Opened()
|
||||||
|
{
|
||||||
|
IoCManager.Resolve<IClyde>().RequestWindowAttention();
|
||||||
|
_menu.OpenCentered();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Closed()
|
||||||
|
{
|
||||||
|
base.Closed();
|
||||||
|
|
||||||
|
SendMessage(new ReturnToBodyMessage(false));
|
||||||
|
_menu.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
59
Content.Client/Ghost/UI/ReturnToBodyMenu.cs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||||
|
|
||||||
|
namespace Content.Client.Ghost.UI;
|
||||||
|
|
||||||
|
public sealed class ReturnToBodyMenu : DefaultWindow
|
||||||
|
{
|
||||||
|
public readonly Button DenyButton;
|
||||||
|
public readonly Button AcceptButton;
|
||||||
|
|
||||||
|
public ReturnToBodyMenu()
|
||||||
|
{
|
||||||
|
Title = Loc.GetString("ghost-return-to-body-title");
|
||||||
|
|
||||||
|
Contents.AddChild(new BoxContainer
|
||||||
|
{
|
||||||
|
Orientation = LayoutOrientation.Vertical,
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
new BoxContainer
|
||||||
|
{
|
||||||
|
Orientation = LayoutOrientation.Vertical,
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
(new Label()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("ghost-return-to-body-text")
|
||||||
|
}),
|
||||||
|
new BoxContainer
|
||||||
|
{
|
||||||
|
Orientation = LayoutOrientation.Horizontal,
|
||||||
|
Align = AlignMode.Center,
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
(AcceptButton = new Button
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("accept-cloning-window-accept-button"),
|
||||||
|
}),
|
||||||
|
|
||||||
|
(new Control()
|
||||||
|
{
|
||||||
|
MinSize = (20, 0)
|
||||||
|
}),
|
||||||
|
|
||||||
|
(DenyButton = new Button
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("accept-cloning-window-deny-button"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -132,6 +132,7 @@ namespace Content.Server.Atmos.Miasma
|
|||||||
// Core rotting stuff
|
// Core rotting stuff
|
||||||
SubscribeLocalEvent<RottingComponent, ComponentShutdown>(OnShutdown);
|
SubscribeLocalEvent<RottingComponent, ComponentShutdown>(OnShutdown);
|
||||||
SubscribeLocalEvent<RottingComponent, OnTemperatureChangeEvent>(OnTempChange);
|
SubscribeLocalEvent<RottingComponent, OnTemperatureChangeEvent>(OnTempChange);
|
||||||
|
SubscribeLocalEvent<RottingComponent, MobStateChangedEvent>(OnRottingMobStateChanged);
|
||||||
SubscribeLocalEvent<PerishableComponent, MobStateChangedEvent>(OnMobStateChanged);
|
SubscribeLocalEvent<PerishableComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||||
SubscribeLocalEvent<PerishableComponent, BeingGibbedEvent>(OnGibbed);
|
SubscribeLocalEvent<PerishableComponent, BeingGibbedEvent>(OnGibbed);
|
||||||
SubscribeLocalEvent<PerishableComponent, ExaminedEvent>(OnExamined);
|
SubscribeLocalEvent<PerishableComponent, ExaminedEvent>(OnExamined);
|
||||||
@@ -174,10 +175,24 @@ namespace Content.Server.Atmos.Miasma
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnRottingMobStateChanged(EntityUid uid, RottingComponent component, MobStateChangedEvent args)
|
||||||
|
{
|
||||||
|
if (args.NewMobState == MobState.Dead)
|
||||||
|
return;
|
||||||
|
RemCompDeferred(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsRotting(EntityUid uid, PerishableComponent? perishable = null, MetaDataComponent? metadata = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref perishable, ref metadata, false))
|
||||||
|
return true;
|
||||||
|
return IsRotting(perishable, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Has enough time passed for <paramref name="perishable"/> to start rotting?
|
/// Has enough time passed for <paramref name="perishable"/> to start rotting?
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private bool IsRotting(PerishableComponent perishable, MetaDataComponent? metadata = null)
|
public bool IsRotting(PerishableComponent perishable, MetaDataComponent? metadata = null)
|
||||||
{
|
{
|
||||||
if (perishable.TimeOfDeath == TimeSpan.Zero)
|
if (perishable.TimeOfDeath == TimeSpan.Zero)
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
32
Content.Server/Ghost/ReturnToBodyEui.cs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
using Content.Server.EUI;
|
||||||
|
using Content.Server.Players;
|
||||||
|
using Content.Shared.Eui;
|
||||||
|
using Content.Shared.Ghost;
|
||||||
|
|
||||||
|
namespace Content.Server.Ghost;
|
||||||
|
|
||||||
|
public sealed class ReturnToBodyEui : BaseEui
|
||||||
|
{
|
||||||
|
private readonly Mind.Mind _mind;
|
||||||
|
|
||||||
|
public ReturnToBodyEui(Mind.Mind mind)
|
||||||
|
{
|
||||||
|
_mind = mind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void HandleMessage(EuiMessageBase msg)
|
||||||
|
{
|
||||||
|
base.HandleMessage(msg);
|
||||||
|
|
||||||
|
if (msg is not ReturnToBodyMessage choice ||
|
||||||
|
!choice.Accepted)
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_mind.TryGetSession(out var session))
|
||||||
|
session.ContentData()!.Mind?.UnVisit();
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
257
Content.Server/Medical/DefibrillatorSystem.cs
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
using Content.Server.Atmos.Miasma;
|
||||||
|
using Content.Server.Chat.Systems;
|
||||||
|
using Content.Server.DoAfter;
|
||||||
|
using Content.Server.Electrocution;
|
||||||
|
using Content.Server.EUI;
|
||||||
|
using Content.Server.Ghost;
|
||||||
|
using Content.Server.Mind.Components;
|
||||||
|
using Content.Server.Popups;
|
||||||
|
using Content.Server.PowerCell;
|
||||||
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.DoAfter;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Interaction.Components;
|
||||||
|
using Content.Shared.Interaction.Events;
|
||||||
|
using Content.Shared.Medical;
|
||||||
|
using Content.Shared.Mobs;
|
||||||
|
using Content.Shared.Mobs.Components;
|
||||||
|
using Content.Shared.Mobs.Systems;
|
||||||
|
using Content.Shared.Timing;
|
||||||
|
using Content.Shared.Toggleable;
|
||||||
|
using Robust.Server.Player;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Server.Medical;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This handles interactions and logic relating to <see cref="DefibrillatorComponent"/>
|
||||||
|
/// </summary>
|
||||||
|
public sealed class DefibrillatorSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
|
[Dependency] private readonly ChatSystem _chatManager = default!;
|
||||||
|
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||||
|
[Dependency] private readonly DoAfterSystem _doAfter = default!;
|
||||||
|
[Dependency] private readonly ElectrocutionSystem _electrocution = default!;
|
||||||
|
[Dependency] private readonly EuiManager _euiManager = default!;
|
||||||
|
[Dependency] private readonly MiasmaSystem _miasma = default!;
|
||||||
|
[Dependency] private readonly MobStateSystem _mobState = default!;
|
||||||
|
[Dependency] private readonly MobThresholdSystem _mobThreshold = default!;
|
||||||
|
[Dependency] private readonly PopupSystem _popup = default!;
|
||||||
|
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
||||||
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
|
[Dependency] private readonly UseDelaySystem _useDelay = default!;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<DefibrillatorComponent, EntityUnpausedEvent>(OnUnpaused);
|
||||||
|
SubscribeLocalEvent<DefibrillatorComponent, UseInHandEvent>(OnUseInHand);
|
||||||
|
SubscribeLocalEvent<DefibrillatorComponent, PowerCellSlotEmptyEvent>(OnPowerCellSlotEmpty);
|
||||||
|
SubscribeLocalEvent<DefibrillatorComponent, AfterInteractEvent>(OnAfterInteract);
|
||||||
|
SubscribeLocalEvent<DefibrillatorComponent, DefibrillatorZapDoAfterEvent>(OnDoAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUnpaused(EntityUid uid, DefibrillatorComponent component, ref EntityUnpausedEvent args)
|
||||||
|
{
|
||||||
|
component.NextZapTime += args.PausedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUseInHand(EntityUid uid, DefibrillatorComponent component, UseInHandEvent args)
|
||||||
|
{
|
||||||
|
if (args.Handled || _useDelay.ActiveDelay(uid))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!TryToggle(uid, component, args.User))
|
||||||
|
return;
|
||||||
|
args.Handled = true;
|
||||||
|
_useDelay.BeginDelay(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPowerCellSlotEmpty(EntityUid uid, DefibrillatorComponent component, ref PowerCellSlotEmptyEvent args)
|
||||||
|
{
|
||||||
|
TryDisable(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAfterInteract(EntityUid uid, DefibrillatorComponent component, AfterInteractEvent args)
|
||||||
|
{
|
||||||
|
if (args.Handled || args.Target is not { } target)
|
||||||
|
return;
|
||||||
|
args.Handled = TryStartZap(uid, target, args.User, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDoAfter(EntityUid uid, DefibrillatorComponent component, DefibrillatorZapDoAfterEvent args)
|
||||||
|
{
|
||||||
|
if (args.Handled || args.Cancelled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (args.Target is not { } target)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!CanZap(uid, target, args.User, component))
|
||||||
|
return;
|
||||||
|
|
||||||
|
args.Handled = true;
|
||||||
|
Zap(uid, target, args.User, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryToggle(EntityUid uid, DefibrillatorComponent? component = null, EntityUid? user = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return component.Enabled
|
||||||
|
? TryDisable(uid, component)
|
||||||
|
: TryEnable(uid, component, user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryEnable(EntityUid uid, DefibrillatorComponent? component = null, EntityUid? user = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (component.Enabled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (_powerCell.HasActivatableCharge(uid))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
component.Enabled = true;
|
||||||
|
_appearance.SetData(uid, ToggleVisuals.Toggled, true);
|
||||||
|
_audio.PlayPvs(component.PowerOnSound, uid);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryDisable(EntityUid uid, DefibrillatorComponent? component = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!component.Enabled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
component.Enabled = false;
|
||||||
|
_appearance.SetData(uid, ToggleVisuals.Toggled, false);
|
||||||
|
_audio.PlayPvs(component.PowerOffSound, uid);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanZap(EntityUid uid, EntityUid target, EntityUid? user = null, DefibrillatorComponent? component = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!component.Enabled)
|
||||||
|
{
|
||||||
|
if (user != null)
|
||||||
|
_popup.PopupEntity(Loc.GetString("defibrillator-not-on"), uid, user.Value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_timing.CurTime < component.NextZapTime)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!TryComp<MobStateComponent>(target, out var mobState) || _miasma.IsRotting(target))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!_powerCell.HasActivatableCharge(uid, user: user))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (_mobState.IsAlive(target, mobState))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryStartZap(EntityUid uid, EntityUid target, EntityUid user, DefibrillatorComponent? component = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!CanZap(uid, target, user, component))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_audio.PlayPvs(component.ChargeSound, uid);
|
||||||
|
return _doAfter.TryStartDoAfter(new DoAfterArgs(user, component.DoAfterDuration, new DefibrillatorZapDoAfterEvent(),
|
||||||
|
uid, target, uid)
|
||||||
|
{
|
||||||
|
BlockDuplicate = true,
|
||||||
|
BreakOnUserMove = true,
|
||||||
|
BreakOnTargetMove = true,
|
||||||
|
BreakOnHandChange = true,
|
||||||
|
NeedHand = true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Zap(EntityUid uid, EntityUid target, EntityUid user, DefibrillatorComponent? component = null, MobStateComponent? mob = null, MobThresholdsComponent? thresholds = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component) || !Resolve(target, ref mob, ref thresholds, false))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// clowns zap themselves
|
||||||
|
if (HasComp<ClumsyComponent>(user) && user != target)
|
||||||
|
{
|
||||||
|
Zap(uid, user, user, component, mob, thresholds);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_powerCell.TryUseActivatableCharge(uid, user: user))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_mobThreshold.SetAllowRevives(target, true, thresholds);
|
||||||
|
_audio.PlayPvs(component.ZapSound, uid);
|
||||||
|
_electrocution.TryDoElectrocution(target, null, component.ZapDamage, component.WritheDuration, true, ignoreInsulation: true);
|
||||||
|
|
||||||
|
if (_mobState.IsIncapacitated(target, mob))
|
||||||
|
_damageable.TryChangeDamage(target, component.ZapHeal, true, origin: uid);
|
||||||
|
|
||||||
|
component.NextZapTime = _timing.CurTime + component.ZapDelay;
|
||||||
|
_appearance.SetData(uid, DefibrillatorVisuals.Ready, false);
|
||||||
|
_mobState.ChangeMobState(target, MobState.Critical, mob, uid);
|
||||||
|
_mobThreshold.SetAllowRevives(target, false, thresholds);
|
||||||
|
|
||||||
|
IPlayerSession? session = null;
|
||||||
|
if (TryComp<MindComponent>(target, out var mindComp) &&
|
||||||
|
mindComp.Mind?.UserId != null &&
|
||||||
|
_playerManager.TryGetSessionById(mindComp.Mind.UserId.Value, out session))
|
||||||
|
{
|
||||||
|
// notify them they're being revived.
|
||||||
|
if (mindComp.Mind.CurrentEntity != target)
|
||||||
|
{
|
||||||
|
_chatManager.TrySendInGameICMessage(uid, Loc.GetString("defibrillator-ghosted"),
|
||||||
|
InGameICChatType.Speak, true, true);
|
||||||
|
_euiManager.OpenEui(new ReturnToBodyEui(mindComp.Mind), session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_chatManager.TrySendInGameICMessage(uid, Loc.GetString("defibrillator-no-mind"),
|
||||||
|
InGameICChatType.Speak, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
var sound = _mobState.IsAlive(target, mob) && session != null
|
||||||
|
? component.SuccessSound
|
||||||
|
: component.FailureSound;
|
||||||
|
_audio.PlayPvs(sound, uid);
|
||||||
|
|
||||||
|
// if we don't have enough power left for another shot, turn it off
|
||||||
|
if (!_powerCell.HasActivatableCharge(uid))
|
||||||
|
TryDisable(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
var query = EntityQueryEnumerator<DefibrillatorComponent>();
|
||||||
|
while (query.MoveNext(out var uid, out var defib))
|
||||||
|
{
|
||||||
|
if (_timing.CurTime < defib.NextZapTime)
|
||||||
|
continue;
|
||||||
|
_audio.PlayPvs(defib.ReadySound, uid);
|
||||||
|
_appearance.SetData(uid, DefibrillatorVisuals.Ready, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,7 +30,6 @@ public sealed class HealingSystem : EntitySystem
|
|||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly StackSystem _stacks = default!;
|
[Dependency] private readonly StackSystem _stacks = default!;
|
||||||
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
||||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
|
||||||
[Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
|
[Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
|
||||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||||
|
|
||||||
@@ -49,7 +48,7 @@ public sealed class HealingSystem : EntitySystem
|
|||||||
if (!TryComp(args.Used, out HealingComponent? healing))
|
if (!TryComp(args.Used, out HealingComponent? healing))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (args.Handled || args.Cancelled || _mobStateSystem.IsDead(uid))
|
if (args.Handled || args.Cancelled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (component.DamageContainerID is not null && !component.DamageContainerID.Equals(component.DamageContainerID))
|
if (component.DamageContainerID is not null && !component.DamageContainerID.Equals(component.DamageContainerID))
|
||||||
@@ -138,7 +137,7 @@ public sealed class HealingSystem : EntitySystem
|
|||||||
|
|
||||||
private bool TryHeal(EntityUid uid, EntityUid user, EntityUid target, HealingComponent component)
|
private bool TryHeal(EntityUid uid, EntityUid user, EntityUid target, HealingComponent component)
|
||||||
{
|
{
|
||||||
if (_mobStateSystem.IsDead(target) || !TryComp<DamageableComponent>(target, out var targetDamage))
|
if (!TryComp<DamageableComponent>(target, out var targetDamage))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (component.DamageContainerID is not null &&
|
if (component.DamageContainerID is not null &&
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ using System.Linq;
|
|||||||
using Content.Shared.Damage.Prototypes;
|
using Content.Shared.Damage.Prototypes;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
|
using Content.Shared.Mobs.Components;
|
||||||
|
using Content.Shared.Mobs.Systems;
|
||||||
using Content.Shared.Radiation.Events;
|
using Content.Shared.Radiation.Events;
|
||||||
using Content.Shared.Rejuvenate;
|
using Content.Shared.Rejuvenate;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
@@ -16,6 +18,7 @@ namespace Content.Shared.Damage
|
|||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
[Dependency] private readonly INetManager _netMan = default!;
|
[Dependency] private readonly INetManager _netMan = default!;
|
||||||
|
[Dependency] private readonly MobThresholdSystem _mobThreshold = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -264,7 +267,10 @@ namespace Content.Shared.Damage
|
|||||||
|
|
||||||
private void OnRejuvenate(EntityUid uid, DamageableComponent component, RejuvenateEvent args)
|
private void OnRejuvenate(EntityUid uid, DamageableComponent component, RejuvenateEvent args)
|
||||||
{
|
{
|
||||||
|
TryComp<MobThresholdsComponent>(uid, out var thresholds);
|
||||||
|
_mobThreshold.SetAllowRevives(uid, true, thresholds); // do this so that the state changes when we set the damage
|
||||||
SetAllDamage(uid, component, 0);
|
SetAllDamage(uid, component, 0);
|
||||||
|
_mobThreshold.SetAllowRevives(uid, false, thresholds);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DamageableHandleState(EntityUid uid, DamageableComponent component, ref ComponentHandleState args)
|
private void DamageableHandleState(EntityUid uid, DamageableComponent component, ref ComponentHandleState args)
|
||||||
|
|||||||
15
Content.Shared/Ghost/ReturnToBodyEuiMessage.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using Content.Shared.Eui;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Ghost;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class ReturnToBodyMessage : EuiMessageBase
|
||||||
|
{
|
||||||
|
public readonly bool Accepted;
|
||||||
|
|
||||||
|
public ReturnToBodyMessage(bool accepted)
|
||||||
|
{
|
||||||
|
Accepted = accepted;
|
||||||
|
}
|
||||||
|
}
|
||||||
100
Content.Shared/Medical/DefibrillatorComponent.cs
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.DoAfter;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
|
|
||||||
|
namespace Content.Shared.Medical;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is used for defibrillators; a machine that shocks a dead
|
||||||
|
/// person back into the world of the living.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
public sealed class DefibrillatorComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Whether or not it's turned on and able to be used.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("enabled"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public bool Enabled;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The time at which the zap cooldown will be completed
|
||||||
|
/// </summary>
|
||||||
|
[DataField("nextZapTime", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public TimeSpan NextZapTime = TimeSpan.Zero;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The minimum time between zaps
|
||||||
|
/// </summary>
|
||||||
|
[DataField("zapDelay"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public TimeSpan ZapDelay = TimeSpan.FromSeconds(5);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How much damage is healed from getting zapped.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("zapHeal", required: true), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public DamageSpecifier ZapHeal = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The electrical damage from getting zapped.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("zapDamage"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public int ZapDamage = 5;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How long the victim will be electrocuted after getting zapped.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("writheDuration"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public TimeSpan WritheDuration = TimeSpan.FromSeconds(3);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How long the doafter for zapping someone takes
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is synced with the audio; do not change one but not the other.
|
||||||
|
/// </remarks>
|
||||||
|
[DataField("doAfterDuration"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public TimeSpan DoAfterDuration = TimeSpan.FromSeconds(3);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The sound when someone is zapped.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("zapSound")]
|
||||||
|
public SoundSpecifier? ZapSound;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The sound when the defib is powered on.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("powerOnSound")]
|
||||||
|
public SoundSpecifier? PowerOnSound;
|
||||||
|
|
||||||
|
[DataField("powerOffSound")]
|
||||||
|
public SoundSpecifier? PowerOffSound;
|
||||||
|
|
||||||
|
[DataField("chargeSound")]
|
||||||
|
public SoundSpecifier? ChargeSound;
|
||||||
|
|
||||||
|
[DataField("failureSound")]
|
||||||
|
public SoundSpecifier? FailureSound;
|
||||||
|
|
||||||
|
[DataField("successSound")]
|
||||||
|
public SoundSpecifier? SuccessSound;
|
||||||
|
|
||||||
|
[DataField("readySound")]
|
||||||
|
public SoundSpecifier? ReadySound;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum DefibrillatorVisuals : byte
|
||||||
|
{
|
||||||
|
Ready
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class DefibrillatorZapDoAfterEvent : SimpleDoAfterEvent
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Content.Shared.Mobs.Systems;
|
using Content.Shared.Mobs.Systems;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
|
|
||||||
namespace Content.Shared.Mobs.Components;
|
namespace Content.Shared.Mobs.Components;
|
||||||
|
|
||||||
@@ -9,23 +8,18 @@ namespace Content.Shared.Mobs.Components;
|
|||||||
[Access(typeof(MobThresholdSystem))]
|
[Access(typeof(MobThresholdSystem))]
|
||||||
public sealed class MobThresholdsComponent : Component
|
public sealed class MobThresholdsComponent : Component
|
||||||
{
|
{
|
||||||
[DataField("thresholds", required:true)]public SortedDictionary<FixedPoint2, MobState> Thresholds = new();
|
[DataField("thresholds", required:true), AutoNetworkedField(true)]
|
||||||
|
public SortedDictionary<FixedPoint2, MobState> Thresholds = new();
|
||||||
|
|
||||||
[DataField("triggersAlerts")] public bool TriggersAlerts = true;
|
[DataField("triggersAlerts"), AutoNetworkedField]
|
||||||
|
public bool TriggersAlerts = true;
|
||||||
|
|
||||||
|
[DataField("currentThresholdState"), AutoNetworkedField]
|
||||||
public MobState CurrentThresholdState;
|
public MobState CurrentThresholdState;
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class MobThresholdComponentState : ComponentState
|
|
||||||
{
|
|
||||||
public Dictionary<FixedPoint2, MobState> Thresholds;
|
|
||||||
public MobState CurrentThresholdState;
|
|
||||||
public MobThresholdComponentState(MobState currentThresholdState,
|
|
||||||
Dictionary<FixedPoint2, MobState> thresholds)
|
|
||||||
{
|
|
||||||
CurrentThresholdState = currentThresholdState;
|
|
||||||
Thresholds = thresholds;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether or not this entity can be revived out of a dead state.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("allowRevives"), AutoNetworkedField]
|
||||||
|
public bool AllowRevives;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public partial class MobStateSystem
|
|||||||
if (!Resolve(entity, ref component))
|
if (!Resolve(entity, ref component))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var ev = new UpdateMobStateEvent {Target = entity, Component = component, Origin = origin};
|
var ev = new UpdateMobStateEvent {Target = entity, Component = component, Origin = origin, State = mobState};
|
||||||
RaiseLocalEvent(entity, ref ev);
|
RaiseLocalEvent(entity, ref ev);
|
||||||
ChangeState(entity, component, ev.State);
|
ChangeState(entity, component, ev.State);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Content.Shared.Alert;
|
|||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Content.Shared.Mobs.Components;
|
using Content.Shared.Mobs.Components;
|
||||||
using Robust.Shared.GameStates;
|
|
||||||
namespace Content.Shared.Mobs.Systems;
|
namespace Content.Shared.Mobs.Systems;
|
||||||
|
|
||||||
public sealed class MobThresholdSystem : EntitySystem
|
public sealed class MobThresholdSystem : EntitySystem
|
||||||
@@ -17,8 +17,6 @@ public sealed class MobThresholdSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<MobThresholdsComponent, ComponentShutdown>(MobThresholdShutdown);
|
SubscribeLocalEvent<MobThresholdsComponent, ComponentShutdown>(MobThresholdShutdown);
|
||||||
SubscribeLocalEvent<MobThresholdsComponent, ComponentStartup>(MobThresholdStartup);
|
SubscribeLocalEvent<MobThresholdsComponent, ComponentStartup>(MobThresholdStartup);
|
||||||
SubscribeLocalEvent<MobThresholdsComponent, DamageChangedEvent>(OnDamaged);
|
SubscribeLocalEvent<MobThresholdsComponent, DamageChangedEvent>(OnDamaged);
|
||||||
SubscribeLocalEvent<MobThresholdsComponent, ComponentGetState>(OnGetComponentState);
|
|
||||||
SubscribeLocalEvent<MobThresholdsComponent, ComponentHandleState>(OnHandleComponentState);
|
|
||||||
SubscribeLocalEvent<MobThresholdsComponent, UpdateMobStateEvent>(OnUpdateMobState);
|
SubscribeLocalEvent<MobThresholdsComponent, UpdateMobStateEvent>(OnUpdateMobState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,6 +247,14 @@ public sealed class MobThresholdSystem : EntitySystem
|
|||||||
UpdateAlerts(target, mobState.CurrentState, threshold, damageable);
|
UpdateAlerts(target, mobState.CurrentState, threshold, damageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetAllowRevives(EntityUid uid, bool val, MobThresholdsComponent? component = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component, false))
|
||||||
|
return;
|
||||||
|
component.AllowRevives = val;
|
||||||
|
Dirty(component);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Private Implementation
|
#region Private Implementation
|
||||||
@@ -278,7 +284,8 @@ public sealed class MobThresholdSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
thresholds.CurrentThresholdState = newState;
|
if (mobState.CurrentState != MobState.Dead || thresholds.AllowRevives)
|
||||||
|
thresholds.CurrentThresholdState = newState;
|
||||||
_mobStateSystem.UpdateMobState(target, mobState);
|
_mobStateSystem.UpdateMobState(target, mobState);
|
||||||
|
|
||||||
Dirty(target);
|
Dirty(target);
|
||||||
@@ -334,32 +341,6 @@ public sealed class MobThresholdSystem : EntitySystem
|
|||||||
UpdateAlerts(target, mobState.CurrentState, thresholds, args.Damageable);
|
UpdateAlerts(target, mobState.CurrentState, thresholds, args.Damageable);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnHandleComponentState(EntityUid target, MobThresholdsComponent component,
|
|
||||||
ref ComponentHandleState args)
|
|
||||||
{
|
|
||||||
if (args.Current is not MobThresholdComponentState state)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (component.Thresholds.Count != state.Thresholds.Count ||
|
|
||||||
!component.Thresholds.SequenceEqual(state.Thresholds))
|
|
||||||
{
|
|
||||||
component.Thresholds.Clear();
|
|
||||||
|
|
||||||
foreach (var threshold in state.Thresholds)
|
|
||||||
{
|
|
||||||
component.Thresholds.Add(threshold.Key, threshold.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
component.CurrentThresholdState = state.CurrentThresholdState;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGetComponentState(EntityUid target, MobThresholdsComponent component, ref ComponentGetState args)
|
|
||||||
{
|
|
||||||
args.State = new MobThresholdComponentState(component.CurrentThresholdState,
|
|
||||||
new Dictionary<FixedPoint2, MobState>(component.Thresholds));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void MobThresholdStartup(EntityUid target, MobThresholdsComponent thresholds, ComponentStartup args)
|
private void MobThresholdStartup(EntityUid target, MobThresholdsComponent thresholds, ComponentStartup args)
|
||||||
{
|
{
|
||||||
if (!TryComp<MobStateComponent>(target, out var mobState) || !TryComp<DamageableComponent>(target, out var damageable))
|
if (!TryComp<MobStateComponent>(target, out var mobState) || !TryComp<DamageableComponent>(target, out var damageable))
|
||||||
@@ -378,8 +359,14 @@ public sealed class MobThresholdSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnUpdateMobState(EntityUid target, MobThresholdsComponent component, ref UpdateMobStateEvent args)
|
private void OnUpdateMobState(EntityUid target, MobThresholdsComponent component, ref UpdateMobStateEvent args)
|
||||||
{
|
{
|
||||||
if (component.CurrentThresholdState != MobState.Invalid)
|
if (!component.AllowRevives && component.CurrentThresholdState == MobState.Dead)
|
||||||
|
{
|
||||||
|
args.State = MobState.Dead;
|
||||||
|
}
|
||||||
|
else if (component.CurrentThresholdState != MobState.Invalid)
|
||||||
|
{
|
||||||
args.State = component.CurrentThresholdState;
|
args.State = component.CurrentThresholdState;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@@ -394,6 +381,4 @@ public sealed class MobThresholdSystem : EntitySystem
|
|||||||
/// <param name="Damageable">Damageable Component owned by the Target</param>
|
/// <param name="Damageable">Damageable Component owned by the Target</param>
|
||||||
[ByRefEvent]
|
[ByRefEvent]
|
||||||
public readonly record struct MobThresholdChecked(EntityUid Target, MobStateComponent MobState,
|
public readonly record struct MobThresholdChecked(EntityUid Target, MobStateComponent MobState,
|
||||||
MobThresholdsComponent Threshold, DamageableComponent Damageable)
|
MobThresholdsComponent Threshold, DamageableComponent Damageable);
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|||||||
BIN
Resources/Audio/Items/Defib/defib_SaftyOn.ogg
Normal file
BIN
Resources/Audio/Items/Defib/defib_charge.ogg
Normal file
BIN
Resources/Audio/Items/Defib/defib_failed.ogg
Normal file
BIN
Resources/Audio/Items/Defib/defib_ready.ogg
Normal file
BIN
Resources/Audio/Items/Defib/defib_saftyOff.ogg
Normal file
BIN
Resources/Audio/Items/Defib/defib_success.ogg
Normal file
BIN
Resources/Audio/Items/Defib/defib_zap.ogg
Normal file
@@ -20,3 +20,6 @@ ghost-roles-window-request-role-button-timer = Request ({$time}s)
|
|||||||
ghost-roles-window-follow-role-button = Follow
|
ghost-roles-window-follow-role-button = Follow
|
||||||
ghost-roles-window-no-roles-available-label = There are currently no available ghost roles.
|
ghost-roles-window-no-roles-available-label = There are currently no available ghost roles.
|
||||||
ghost-roles-window-rules-footer = The button will enable after {$time} seconds (this delay is to make sure you read the rules).
|
ghost-roles-window-rules-footer = The button will enable after {$time} seconds (this delay is to make sure you read the rules).
|
||||||
|
|
||||||
|
ghost-return-to-body-title = Return to Body
|
||||||
|
ghost-return-to-body-text = You are being revived! Return to your body?
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
defibrillator-not-on = The defibrillator isn't turned on.
|
||||||
|
defibrillator-no-mind = No intelligence pattern can be detected in patient's brain. Further attempts futile
|
||||||
|
defibrillator-ghosted = Resuscitation failed - Mental interface error. Further attempts may be successful.
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
- type: entity
|
||||||
|
id: Defibrillator
|
||||||
|
parent: [ BaseItem, PowerCellSlotMediumItem ]
|
||||||
|
name: defibrillator
|
||||||
|
description: CLEAR! Zzzzat!
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Specific/Medical/defib.rsi
|
||||||
|
layers:
|
||||||
|
- state: icon
|
||||||
|
- state: screen
|
||||||
|
map: [ "enum.ToggleVisuals.Layer" ]
|
||||||
|
visible: false
|
||||||
|
shader: unshaded
|
||||||
|
- state: ready
|
||||||
|
map: ["enum.PowerDeviceVisualLayers.Powered"]
|
||||||
|
shader: unshaded
|
||||||
|
- type: GenericVisualizer
|
||||||
|
visuals:
|
||||||
|
enum.ToggleVisuals.Toggled:
|
||||||
|
enum.ToggleVisuals.Layer:
|
||||||
|
True: { visible: true }
|
||||||
|
False: { visible: false }
|
||||||
|
enum.DefibrillatorVisuals.Ready:
|
||||||
|
enum.PowerDeviceVisualLayers.Powered:
|
||||||
|
True: { visible: true }
|
||||||
|
False: { visible: false }
|
||||||
|
- type: Item
|
||||||
|
size: 50
|
||||||
|
- type: ItemCooldown
|
||||||
|
- type: MultiHandedItem
|
||||||
|
- type: Speech
|
||||||
|
- type: Defibrillator
|
||||||
|
zapHeal:
|
||||||
|
types:
|
||||||
|
Asphyxiation: -40
|
||||||
|
zapSound:
|
||||||
|
path: /Audio/Items/Defib/defib_zap.ogg
|
||||||
|
powerOnSound:
|
||||||
|
path: /Audio/Items/Defib/defib_SaftyOn.ogg
|
||||||
|
powerOffSound:
|
||||||
|
path: /Audio/Items/Defib/defib_saftyOff.ogg
|
||||||
|
chargeSound:
|
||||||
|
path: /Audio/Items/Defib/defib_charge.ogg
|
||||||
|
failureSound:
|
||||||
|
path: /Audio/Items/Defib/defib_failed.ogg
|
||||||
|
successSound:
|
||||||
|
path: /Audio/Items/Defib/defib_success.ogg
|
||||||
|
readySound:
|
||||||
|
path: /Audio/Items/Defib/defib_ready.ogg
|
||||||
|
- type: PowerCellDraw
|
||||||
|
useRate: 100
|
||||||
|
- type: Appearance
|
||||||
|
- type: DoAfter
|
||||||
|
- type: UseDelay
|
||||||
|
- type: StaticPrice
|
||||||
|
price: 100
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: DefibrillatorEmpty
|
||||||
|
parent: Defibrillator
|
||||||
|
suffix: Empty
|
||||||
|
components:
|
||||||
|
- type: ItemSlots
|
||||||
|
slots:
|
||||||
|
cell_slot:
|
||||||
|
name: power-cell-slot-component-slot-name-default
|
||||||
@@ -466,6 +466,7 @@
|
|||||||
idleState: icon
|
idleState: icon
|
||||||
runningState: icon
|
runningState: icon
|
||||||
staticRecipes:
|
staticRecipes:
|
||||||
|
- Defibrillator
|
||||||
- HandheldHealthAnalyzer
|
- HandheldHealthAnalyzer
|
||||||
- ClothingHandsGlovesLatex
|
- ClothingHandsGlovesLatex
|
||||||
- ClothingMaskSterile
|
- ClothingMaskSterile
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
- type: entity
|
||||||
|
id: DefibrillatorCabinet
|
||||||
|
name: defibrillator cabinet
|
||||||
|
description: A small wall mounted cabinet designed to hold a defibrillator.
|
||||||
|
components:
|
||||||
|
- type: WallMount
|
||||||
|
arc: 90
|
||||||
|
- type: Transform
|
||||||
|
anchored: true
|
||||||
|
- type: Clickable
|
||||||
|
- type: InteractionOutline
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Structures/Wallmounts/defib_cabinet.rsi
|
||||||
|
netsync: false
|
||||||
|
noRot: false
|
||||||
|
layers:
|
||||||
|
- state: frame
|
||||||
|
- state: fill
|
||||||
|
map: ["enum.ItemCabinetVisualLayers.ContainsItem"]
|
||||||
|
visible: true
|
||||||
|
- state: closed
|
||||||
|
map: ["enum.ItemCabinetVisualLayers.Door"]
|
||||||
|
- type: ItemCabinet
|
||||||
|
cabinetSlot:
|
||||||
|
ejectOnInteract: true
|
||||||
|
whitelist:
|
||||||
|
components:
|
||||||
|
- Defibrillator
|
||||||
|
doorSound:
|
||||||
|
path: /Audio/Machines/machine_switch.ogg
|
||||||
|
openState: open
|
||||||
|
closedState: closed
|
||||||
|
- type: Appearance
|
||||||
|
- type: ItemSlots
|
||||||
|
- type: ContainerContainer
|
||||||
|
containers:
|
||||||
|
ItemCabinet: !type:ContainerSlot
|
||||||
|
- type: Damageable
|
||||||
|
damageContainer: Inorganic
|
||||||
|
damageModifierSet: Metallic
|
||||||
|
- type: Destructible
|
||||||
|
thresholds:
|
||||||
|
- trigger:
|
||||||
|
!type:DamageTrigger
|
||||||
|
damage: 80
|
||||||
|
behaviors:
|
||||||
|
- !type:DoActsBehavior
|
||||||
|
acts: [ "Destruction" ]
|
||||||
|
- trigger:
|
||||||
|
!type:DamageTrigger
|
||||||
|
damage: 40
|
||||||
|
behaviors:
|
||||||
|
- !type:EmptyAllContainersBehaviour
|
||||||
|
- !type:DoActsBehavior
|
||||||
|
acts: [ "Destruction" ]
|
||||||
|
- !type:PlaySoundBehavior
|
||||||
|
sound:
|
||||||
|
path: /Audio/Effects/metalbreak.ogg
|
||||||
|
placement:
|
||||||
|
mode: SnapgridCenter
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: DefibrillatorCabinetOpen
|
||||||
|
parent: DefibrillatorCabinet
|
||||||
|
suffix: Open
|
||||||
|
components:
|
||||||
|
- type: ItemCabinet
|
||||||
|
opened: true
|
||||||
|
doorSound:
|
||||||
|
path: /Audio/Machines/machine_switch.ogg
|
||||||
|
openState: open
|
||||||
|
closedState: closed
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: DefibrillatorCabinetFilled
|
||||||
|
parent: DefibrillatorCabinet
|
||||||
|
suffix: Filled
|
||||||
|
components:
|
||||||
|
- type: ItemCabinet
|
||||||
|
cabinetSlot:
|
||||||
|
ejectOnInteract: true
|
||||||
|
startingItem: Defibrillator
|
||||||
|
whitelist:
|
||||||
|
components:
|
||||||
|
- Defibrillator
|
||||||
|
doorSound:
|
||||||
|
path: /Audio/Machines/machine_switch.ogg
|
||||||
|
openState: open
|
||||||
|
closedState: closed
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: DefibrillatorCabinetFilledOpen
|
||||||
|
parent: DefibrillatorCabinetFilled
|
||||||
|
suffix: Filled, Open
|
||||||
|
components:
|
||||||
|
- type: ItemCabinet
|
||||||
|
opened: true
|
||||||
|
doorSound:
|
||||||
|
path: /Audio/Machines/machine_switch.ogg
|
||||||
|
openState: open
|
||||||
|
closedState: closed
|
||||||
@@ -103,6 +103,13 @@
|
|||||||
Glass: 500
|
Glass: 500
|
||||||
Steel: 500
|
Steel: 500
|
||||||
|
|
||||||
|
- type: latheRecipe
|
||||||
|
id: Defibrillator
|
||||||
|
result: DefibrillatorEmpty
|
||||||
|
completetime: 2
|
||||||
|
materials:
|
||||||
|
Steel: 300
|
||||||
|
|
||||||
- type: latheRecipe
|
- type: latheRecipe
|
||||||
id: Medkit
|
id: Medkit
|
||||||
result: Medkit
|
result: Medkit
|
||||||
|
|||||||
BIN
Resources/Textures/Objects/Specific/Medical/defib.rsi/icon.png
Normal file
|
After Width: | Height: | Size: 808 B |
|
After Width: | Height: | Size: 364 B |
|
After Width: | Height: | Size: 364 B |
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"license": "CC0-1.0",
|
||||||
|
"copyright": "Created by EmoGarbage404 (github) for Space Staiton 14",
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "icon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "inhand-left",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "inhand-right",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ready"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "screen"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
Resources/Textures/Objects/Specific/Medical/defib.rsi/ready.png
Normal file
|
After Width: | Height: | Size: 139 B |
BIN
Resources/Textures/Objects/Specific/Medical/defib.rsi/screen.png
Normal file
|
After Width: | Height: | Size: 160 B |
|
After Width: | Height: | Size: 357 B |
|
After Width: | Height: | Size: 363 B |
|
After Width: | Height: | Size: 281 B |
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"license": "CC0-1.0",
|
||||||
|
"copyright": "Created by EmoGarbage404 (github) for Space Station 14",
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "closed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fill"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "frame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "open"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 377 B |