Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add CPR and remove roundstart cloning #18

Merged
merged 2 commits into from
Jan 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions Content.Client/_Emberfall/Medical/CPR/CPRSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using Content.Shared._Emberfall.Medical.CPR.Systems;

namespace Content.Client._Emberfall.Medical.CPR;

// ReSharper disable InconsistentNaming
public sealed class CPRSystem : SharedCPRSystem;
6 changes: 6 additions & 0 deletions Content.Server/_Emberfall/Medical/CPR/CPRSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
using Content.Shared._Emberfall.Medical.CPR.Systems;

namespace Content.Server._Emberfall.Medical.CPR;

// ReSharper disable InconsistentNaming
public sealed class CPRSystem : SharedCPRSystem;
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Content.Shared._Emberfall.Medical.CPR.Systems;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;

namespace Content.Shared._Emberfall.Medical.CPR.Components;

// ReSharper disable InconsistentNaming
[RegisterComponent, NetworkedComponent, Access(typeof(SharedCPRSystem))]
[AutoGenerateComponentState, AutoGenerateComponentPause]
public sealed partial class CPRReceivedComponent : Component
{
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
[AutoNetworkedField, AutoPausedField]
public TimeSpan Last;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Content.Shared._Emberfall.Medical.CPR.Systems;
using Robust.Shared.GameStates;

namespace Content.Shared._Emberfall.Medical.CPR.Components;

[RegisterComponent, NetworkedComponent]
[Access(typeof(SharedCPRSystem))]
public sealed partial class ReceivingCPRComponent : Component;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;

namespace Content.Shared._Emberfall.Medical.CPR.Events;

// ReSharper disable InconsistentNaming
[Serializable, NetSerializable]
public sealed partial class CPRDoAfterEvent : SimpleDoAfterEvent;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace Content.Shared._Emberfall.Medical.CPR.Events;

// ReSharper disable InconsistentNaming
[ByRefEvent]
public record struct PerformCPRAttemptEvent(EntityUid Target, bool Cancelled = false);
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using Content.Shared.Inventory;

namespace Content.Shared._Emberfall.Medical.CPR.Events;

// ReSharper disable InconsistentNaming
[ByRefEvent]
public record struct ReceiveCPRAttemptEvent(
EntityUid Performer,
EntityUid Target,
bool Start,
SlotFlags TargetSlots = SlotFlags.MASK,
bool Cancelled = false) : IInventoryRelayEvent;
202 changes: 202 additions & 0 deletions Content.Shared/_Emberfall/Medical/CPR/Systems/SharedCPRSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
using Content.Shared._Emberfall.Medical.CPR.Components;
using Content.Shared._Emberfall.Medical.CPR.Events;
using Content.Shared.Atmos.Rotting;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.DoAfter;
using Content.Shared.FixedPoint;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Humanoid;
using Content.Shared.Interaction;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Popups;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;

namespace Content.Shared._Emberfall.Medical.CPR.Systems;

// ReSharper disable InconsistentNaming
public abstract class SharedCPRSystem : EntitySystem
{
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly SharedPopupSystem _popups = default!;
[Dependency] private readonly SharedRottingSystem _rotting = default!;

// TODO: move this to a component
private readonly ProtoId<DamageTypePrototype> _healType = "Asphyxiation";

private static readonly FixedPoint2 HealAmount = FixedPoint2.New(10);

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<HumanoidAppearanceComponent, InteractHandEvent>(OnInteractHand,
[typeof(InteractionPopupSystem)]);
SubscribeLocalEvent<HumanoidAppearanceComponent, CPRDoAfterEvent>(OnDoAfter);

SubscribeLocalEvent<ReceivingCPRComponent, ReceiveCPRAttemptEvent>(OnReceivingCPRAttempt);
SubscribeLocalEvent<CPRReceivedComponent, ReceiveCPRAttemptEvent>(OnReceivedCPRAttempt);
SubscribeLocalEvent<MobStateComponent, ReceiveCPRAttemptEvent>(OnMobStateCPRAttempt);
}

private void OnInteractHand(Entity<HumanoidAppearanceComponent> ent, ref InteractHandEvent args)
{
if (args.Handled)
return;

args.Handled = StartCPR(args.User, args.Target);
}

private void OnDoAfter(Entity<HumanoidAppearanceComponent> ent, ref CPRDoAfterEvent args)
{
var performer = args.User;

if (args.Target != null)
RemComp<ReceivingCPRComponent>(args.Target.Value);

if (args.Cancelled ||
args.Handled ||
args.Target is not { } target ||
!CanCPRPopup(performer, target, false, out var damage))
return;

args.Handled = true;

if (_net.IsServer)
_rotting.ReduceAccumulator(target, TimeSpan.FromSeconds(7));

if (!TryComp(target, out DamageableComponent? damageable) ||
!damageable.Damage.DamageDict.TryGetValue(_healType, out damage))
return;

var heal = -FixedPoint2.Min(damage, HealAmount);
var healSpecifier = new DamageSpecifier();
healSpecifier.DamageDict.Add(_healType, heal);
_damageable.TryChangeDamage(target, healSpecifier, true);
EnsureComp<CPRReceivedComponent>(target).Last = _timing.CurTime;

if (_net.IsClient)
return;

// TODO RMC14 move this value to a component
var selfPopup = Loc.GetString("cpr-self-perform", ("target", target), ("seconds", 7));
_popups.PopupEntity(selfPopup, target, performer);

var othersPopup = Loc.GetString("cpr-other-perform", ("performer", performer), ("target", target));
var othersFilter = Filter.Pvs(performer).RemoveWhereAttachedEntity(e => e == performer);
_popups.PopupEntity(othersPopup, performer, othersFilter, true, PopupType.Medium);
}

private void OnReceivingCPRAttempt(Entity<ReceivingCPRComponent> ent, ref ReceiveCPRAttemptEvent args)
{
args.Cancelled = true;

if (_net.IsClient)
return;

var popup = Loc.GetString("cpr-already-being-performed", ("target", ent.Owner));
_popups.PopupEntity(popup, ent, args.Performer, PopupType.Medium);
}

private void OnReceivedCPRAttempt(Entity<CPRReceivedComponent> ent, ref ReceiveCPRAttemptEvent args)
{
if (args.Start)
return;

var target = ent.Owner;
var performer = args.Performer;

// TODO move this value to a component
if (ent.Comp.Last > _timing.CurTime - TimeSpan.FromSeconds(7))
{
args.Cancelled = true;

if (_net.IsClient)
return;

var selfPopup = Loc.GetString("cpr-self-perform-fail-received-too-recently", ("target", target));
_popups.PopupEntity(selfPopup, target, performer, PopupType.SmallCaution);

var othersPopup = Loc.GetString("cpr-other-perform-fail", ("performer", performer), ("target", target));
var othersFilter = Filter.Pvs(performer).RemoveWhereAttachedEntity(e => e == performer);
_popups.PopupEntity(othersPopup, performer, othersFilter, true, PopupType.SmallCaution);
}
}

private void OnMobStateCPRAttempt(Entity<MobStateComponent> ent, ref ReceiveCPRAttemptEvent args)
{
if (args.Cancelled)
return;

if (_mobState.IsAlive(ent) || _rotting.IsRotten(ent))
args.Cancelled = true;
}

private bool CanCPRPopup(EntityUid performer, EntityUid target, bool start, out FixedPoint2 damage)
{
damage = default;

if (!HasComp<HumanoidAppearanceComponent>(target) || !HasComp<HumanoidAppearanceComponent>(performer))
return false;

var performAttempt = new PerformCPRAttemptEvent(target);
RaiseLocalEvent(performer, ref performAttempt);

if (performAttempt.Cancelled)
return false;

var receiveAttempt = new ReceiveCPRAttemptEvent(performer, target, start);
RaiseLocalEvent(target, ref receiveAttempt);

if (receiveAttempt.Cancelled)
return false;

if (!_hands.TryGetEmptyHand(performer, out _))
return false;

return true;
}

private bool StartCPR(EntityUid performer, EntityUid target)
{
if (!CanCPRPopup(performer, target, true, out _))
return false;

EnsureComp<ReceivingCPRComponent>(target);

var doAfter = new DoAfterArgs(EntityManager,
performer,
TimeSpan.FromSeconds(4),
new CPRDoAfterEvent(),
performer,
target)
{
BreakOnMove = true,
NeedHand = true,
BlockDuplicate = true,
DuplicateCondition = DuplicateConditions.SameEvent,
};
_doAfter.TryStartDoAfter(doAfter);

if (_net.IsClient)
return true;

var selfPopup = Loc.GetString("cpr-self-start-perform", ("target", target));
_popups.PopupEntity(selfPopup, target, performer);

var othersPopup = Loc.GetString("cpr-other-start-perform", ("performer", performer), ("target", target));
var othersFilter = Filter.Pvs(performer).RemoveWhereAttachedEntity(e => e == performer);
_popups.PopupEntity(othersPopup, performer, othersFilter, true);

return true;
}
}
7 changes: 7 additions & 0 deletions Resources/Locale/en-US/_emberfall/medical/cpr.ftl
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
cpr-already-being-performed = CPR is already being performed on {$target}!
cpr-self-start-perform = You start performing CPR on {$target}!
cpr-other-start-perform = {$performer} starts performing CPR on {$target}!
cpr-self-perform = You perform CPR on {$target}. Repeat at least every {$seconds} seconds.
cpr-other-perform = {$performer} performs CPR on {$target}.
cpr-self-perform-fail-received-too-recently = You fail to perform CPR on {$target}. Incorrect rhythm. Do it slower.
cpr-other-perform-fail = {$performer} fails to perform CPR on {$target}!
3 changes: 3 additions & 0 deletions Resources/Migrations/emberfallMigration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@ WeaponRifleLecter: WeaponRifleLecterEmberfall
WeaponRifleM90GrenadeLauncher: WeaponRifleM90
WeaponSubMachineGunVector: WeaponSubMachineGunVectorEmberfall
GunSafeLecter: GunSafeLightRifleLecter

# 01-06-2025
ComputerCloningConsole: null
6 changes: 3 additions & 3 deletions Resources/Prototypes/Entities/Structures/Machines/lathe.yml
Original file line number Diff line number Diff line change
Expand Up @@ -481,8 +481,8 @@
- ThermomachineFreezerMachineCircuitBoard
- HellfireFreezerMachineCircuitBoard
- PortableScrubberMachineCircuitBoard
- CloningPodMachineCircuitboard
- MedicalScannerMachineCircuitboard
# - CloningPodMachineCircuitboard
# - MedicalScannerMachineCircuitboard
- CryoPodMachineCircuitboard
- VaccinatorMachineCircuitboard
- DiagnoserMachineCircuitboard
Expand All @@ -509,7 +509,7 @@
- RadarConsoleCircuitboard
- TechDiskComputerCircuitboard
- DawInstrumentMachineCircuitboard
- CloningConsoleComputerCircuitboard
# - CloningConsoleComputerCircuitboard
- StasisBedMachineCircuitboard
- OreProcessorIndustrialMachineCircuitboard
- CargoTelepadMachineCircuitboard
Expand Down
6 changes: 3 additions & 3 deletions Resources/Prototypes/Procedural/salvage_rewards.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@
CratePartsT3T4: 0.5
TechnologyDiskRare: 0.5
# cloning boards
CloningPodMachineCircuitboard: 0.5
MedicalScannerMachineCircuitboard: 0.5
CloningConsoleComputerCircuitboard: 0.5
# CloningPodMachineCircuitboard: 0.5
# MedicalScannerMachineCircuitboard: 0.5
# CloningConsoleComputerCircuitboard: 0.5
BiomassReclaimerMachineCircuitboard: 0.5
# basic weapons
Machete: 0.25
Expand Down
16 changes: 8 additions & 8 deletions Resources/Prototypes/Recipes/Lathes/electronics.yml
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,10 @@
id: SignalTimerElectronics
result: SignalTimerElectronics

- type: latheRecipe
parent: BaseGoldCircuitboardRecipe
id: CloningPodMachineCircuitboard
result: CloningPodMachineCircuitboard
#- type: latheRecipe # Emberfall
# parent: BaseGoldCircuitboardRecipe
# id: CloningPodMachineCircuitboard
# result: CloningPodMachineCircuitboard

- type: latheRecipe
parent: BaseGoldCircuitboardRecipe
Expand Down Expand Up @@ -429,10 +429,10 @@
id: SolarTrackerElectronics
result: SolarTrackerElectronics

- type: latheRecipe
parent: BaseCircuitboardRecipe
id: CloningConsoleComputerCircuitboard
result: CloningConsoleComputerCircuitboard
#- type: latheRecipe # Emberfall
# parent: BaseCircuitboardRecipe
# id: CloningConsoleComputerCircuitboard
# result: CloningConsoleComputerCircuitboard

- type: latheRecipe
parent: BaseCircuitboardRecipe
Expand Down
Loading