mirror of
https://github.com/wega-team/ss14-wega.git
synced 2026-06-09 10:06:49 +02:00
580 lines
23 KiB
C#
580 lines
23 KiB
C#
using System.Linq;
|
|
using Content.Server.Body.Systems;
|
|
using Content.Shared.Bed.Sleep;
|
|
using Content.Shared.Body;
|
|
using Content.Shared.Body.Components;
|
|
using Content.Shared.Chemistry.Components;
|
|
using Content.Shared.Damage;
|
|
using Content.Shared.Damage.Components;
|
|
using Content.Shared.Database;
|
|
using Content.Shared.DirtVisuals;
|
|
using Content.Shared.FixedPoint;
|
|
using Content.Shared.IdentityManagement;
|
|
using Content.Shared.Implants;
|
|
using Content.Shared.Implants.Components;
|
|
using Content.Shared.Movement.Systems;
|
|
using Content.Shared.Popups;
|
|
using Content.Shared.Random.Helpers;
|
|
using Content.Shared.Silicons.Borgs.Components;
|
|
using Content.Shared.Surgery;
|
|
using Content.Shared.Surgery.Components;
|
|
using Content.Shared.Traits.Assorted;
|
|
using Robust.Shared.Audio;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Random;
|
|
|
|
namespace Content.Server.Surgery;
|
|
|
|
public sealed partial class SurgerySystem
|
|
{
|
|
[Dependency] private BloodstreamSystem _bloodstream = default!;
|
|
[Dependency] private SharedDirtSystem _dirt = default!;
|
|
[Dependency] private SharedSubdermalImplantSystem _implant = default!;
|
|
[Dependency] private SharedInternalStorageSystem _internal = default!;
|
|
|
|
private void PerformSurgeryEffect(SurgeryActionType action, string? requiredPart, ProtoId<InternalDamagePrototype>? damageType, float successChance,
|
|
List<SurgeryFailedType>? failureEffect, EntityUid patient, EntityUid? item)
|
|
{
|
|
if (!TryComp<OperatedComponent>(patient, out var comp))
|
|
return;
|
|
|
|
switch (action)
|
|
{
|
|
// Organic Start
|
|
case SurgeryActionType.Cut:
|
|
PerformCut((patient, comp), successChance, failureEffect);
|
|
break;
|
|
|
|
case SurgeryActionType.Retract:
|
|
PerformRetract((patient, comp), successChance, failureEffect);
|
|
break;
|
|
|
|
case SurgeryActionType.ClampBleeding:
|
|
PerformClamp((patient, comp), successChance, failureEffect);
|
|
break;
|
|
|
|
case SurgeryActionType.DrillThrough:
|
|
PerformDrill((patient, comp), successChance, failureEffect);
|
|
break;
|
|
|
|
case SurgeryActionType.HealInternalDamage:
|
|
PerformHealInternalDamage((patient, comp), requiredPart, damageType, successChance, failureEffect);
|
|
break;
|
|
|
|
case SurgeryActionType.RemoveOrgan:
|
|
PerformRemoveOrgan((patient, comp), requiredPart, successChance, failureEffect);
|
|
break;
|
|
|
|
case SurgeryActionType.InsertOrgan:
|
|
PerformInsertOrgan((patient, comp), item, requiredPart, successChance, failureEffect);
|
|
break;
|
|
|
|
case SurgeryActionType.Implanting:
|
|
PerformImplant((patient, comp), item, requiredPart, successChance, failureEffect);
|
|
break;
|
|
|
|
case SurgeryActionType.RemoveImplant:
|
|
PerformRemoveImplant((patient, comp), requiredPart, successChance, failureEffect);
|
|
break;
|
|
|
|
case SurgeryActionType.StoreItem:
|
|
PerformStoreItem((patient, comp), item, requiredPart, successChance, failureEffect);
|
|
break;
|
|
|
|
case SurgeryActionType.RetrieveItems:
|
|
PerformRetrieveItems((patient, comp), requiredPart, successChance, failureEffect);
|
|
break;
|
|
// Organic End
|
|
|
|
// Synthetic Start
|
|
case SurgeryActionType.Unscrew:
|
|
PerformUnscrew((patient, comp));
|
|
break;
|
|
|
|
case SurgeryActionType.Screw:
|
|
PerformScrew((patient, comp));
|
|
break;
|
|
|
|
case SurgeryActionType.Pulse:
|
|
PerformPulse((patient, comp));
|
|
break;
|
|
|
|
case SurgeryActionType.Weld:
|
|
PerformWeld((patient, comp));
|
|
break;
|
|
|
|
case SurgeryActionType.CutWire:
|
|
PerformCutWire((patient, comp));
|
|
break;
|
|
|
|
case SurgeryActionType.StripWire:
|
|
PerformStripWire((patient, comp));
|
|
break;
|
|
|
|
case SurgeryActionType.MendWire:
|
|
PerformMendWire((patient, comp));
|
|
break;
|
|
|
|
case SurgeryActionType.Pry:
|
|
PerformPry((patient, comp));
|
|
break;
|
|
|
|
case SurgeryActionType.Anchor:
|
|
PerformAnchor((patient, comp));
|
|
break;
|
|
|
|
case SurgeryActionType.Unanchor:
|
|
PerformUnanchor((patient, comp));
|
|
break;
|
|
// Synthetic End
|
|
|
|
default: return;
|
|
}
|
|
|
|
ApplySterilityConsequences((patient, comp));
|
|
|
|
// Any action without anesthesia will cause pain.
|
|
if (!HasComp<SleepingComponent>(patient) && !HasComp<PainNumbnessStatusEffectComponent>(patient) && !comp.OperatedPart
|
|
&& !_mobState.IsDead(patient) && !HasComp<SyntheticOperatedComponent>(patient))
|
|
_chat.TryEmoteWithoutChat(patient, _proto.Index(Scream), true);
|
|
}
|
|
|
|
#region Organic
|
|
|
|
private void PerformCut(Entity<OperatedComponent> patient, float successChance, List<SurgeryFailedType>? failureEffect)
|
|
{
|
|
if (patient.Comp.Surgeon == null)
|
|
return;
|
|
|
|
if (!RollSuccess(patient, patient.Comp.Surgeon.Value, successChance))
|
|
HandleFailure(patient, failureEffect);
|
|
|
|
if (!TryComp<BloodstreamComponent>(patient, out var bloodstream) || HasComp<SyntheticOperatedComponent>(patient))
|
|
return;
|
|
|
|
string? bloodReagentId = null;
|
|
if (bloodstream.BloodReferenceSolution.Contents.Count > 0)
|
|
{
|
|
var reagentQuantity = bloodstream.BloodReferenceSolution.Contents[0];
|
|
bloodReagentId = reagentQuantity.Reagent.Prototype;
|
|
}
|
|
|
|
ApplyBloodToClothing(patient.Comp.Surgeon.Value, bloodReagentId, 2f * SharedDirtSystem.MaxDirtLevel);
|
|
_bloodstream.TryModifyBleedAmount(patient.Owner, 2f);
|
|
}
|
|
|
|
private void PerformRetract(Entity<OperatedComponent> patient, float successChance, List<SurgeryFailedType>? failureEffect)
|
|
{
|
|
if (patient.Comp.Surgeon == null)
|
|
return;
|
|
|
|
if (!RollSuccess(patient, patient.Comp.Surgeon.Value, successChance))
|
|
HandleFailure(patient, failureEffect);
|
|
}
|
|
|
|
private void PerformClamp(Entity<OperatedComponent> patient, float successChance, List<SurgeryFailedType>? failureEffect)
|
|
{
|
|
if (patient.Comp.Surgeon == null)
|
|
return;
|
|
|
|
if (!RollSuccess(patient, patient.Comp.Surgeon.Value, successChance))
|
|
HandleFailure(patient, failureEffect);
|
|
|
|
if (!HasComp<BloodstreamComponent>(patient) || HasComp<SyntheticOperatedComponent>(patient))
|
|
return;
|
|
|
|
_bloodstream.TryModifyBleedAmount(patient.Owner, -10f);
|
|
}
|
|
|
|
private void PerformDrill(Entity<OperatedComponent> patient, float successChance, List<SurgeryFailedType>? failureEffect)
|
|
{
|
|
if (patient.Comp.Surgeon == null)
|
|
return;
|
|
|
|
if (!RollSuccess(patient, patient.Comp.Surgeon.Value, successChance))
|
|
HandleFailure(patient, failureEffect);
|
|
|
|
_damage.TryChangeDamage(patient.Owner, new DamageSpecifier { DamageDict = { { PiercingDamage, 2.5 } } }, true);
|
|
}
|
|
|
|
private void PerformHealInternalDamage(Entity<OperatedComponent> patient, string? requiredPart, ProtoId<InternalDamagePrototype>? damageType, float successChance, List<SurgeryFailedType>? failureEffect)
|
|
{
|
|
if (patient.Comp.Surgeon == null || string.IsNullOrEmpty(requiredPart) || damageType == null)
|
|
return;
|
|
|
|
if (!RollSuccess(patient, patient.Comp.Surgeon.Value, successChance))
|
|
HandleFailure(patient, failureEffect, requiredPart);
|
|
|
|
TryRemoveInternalDamage(patient, damageType, requiredPart);
|
|
}
|
|
|
|
private void PerformRemoveOrgan(Entity<OperatedComponent> patient, string? requiredOrgan, float successChance, List<SurgeryFailedType>? failureEffect)
|
|
{
|
|
if (patient.Comp.Surgeon == null || string.IsNullOrEmpty(requiredOrgan))
|
|
return;
|
|
|
|
if (!RollSuccess(patient, patient.Comp.Surgeon.Value, successChance))
|
|
HandleFailure(patient, failureEffect);
|
|
|
|
if (!TryComp<BodyComponent>(patient, out var body) || body.Organs == null)
|
|
return;
|
|
|
|
EntityUid? organToRemove = null;
|
|
foreach (var organ in body.Organs.ContainedEntities)
|
|
{
|
|
if (TryComp<OrganComponent>(organ, out var organComp) && organComp.Category == requiredOrgan)
|
|
{
|
|
organToRemove = organ;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (organToRemove == null)
|
|
return;
|
|
|
|
_container.Remove(organToRemove.Value, body.Organs);
|
|
|
|
_popup.PopupEntity(Loc.GetString("surgery-organ-removed"), patient);
|
|
_hands.TryPickupAnyHand(patient.Comp.Surgeon.Value, organToRemove.Value);
|
|
|
|
if (TryComp<BloodstreamComponent>(patient, out var bloodstream) && !HasComp<SyntheticOperatedComponent>(patient))
|
|
{
|
|
string? bloodReagentId = null;
|
|
if (bloodstream.BloodReferenceSolution.Contents.Count > 0)
|
|
{
|
|
var reagentQuantity = bloodstream.BloodReferenceSolution.Contents[0];
|
|
bloodReagentId = reagentQuantity.Reagent.Prototype;
|
|
}
|
|
|
|
ApplyBloodToClothing(patient.Comp.Surgeon.Value, bloodReagentId, 2f * SharedDirtSystem.MaxDirtLevel);
|
|
_bloodstream.TryModifyBleedAmount(patient.Owner, 2f);
|
|
}
|
|
}
|
|
|
|
private void PerformInsertOrgan(Entity<OperatedComponent> patient, EntityUid? item, string? requiredOrgan, float successChance, List<SurgeryFailedType>? failureEffect)
|
|
{
|
|
if (patient.Comp.Surgeon == null || item == null || string.IsNullOrEmpty(requiredOrgan))
|
|
return;
|
|
|
|
if (!RollSuccess(patient, patient.Comp.Surgeon.Value, successChance))
|
|
HandleFailure(patient, failureEffect);
|
|
|
|
if (!TryComp<BodyComponent>(patient, out var body) || body.Organs == null)
|
|
return;
|
|
|
|
_container.Insert(item.Value, body.Organs);
|
|
if (!HasComp<SterileComponent>(item.Value) && _random.Prob(0.4f))
|
|
_disease.TryAddDisease(patient, "SurgicalSepsis");
|
|
|
|
_popup.PopupEntity(Loc.GetString("surgery-organ-inserted"), patient);
|
|
}
|
|
|
|
private void PerformImplant(Entity<OperatedComponent> patient, EntityUid? item, string? requiredPart, float successChance, List<SurgeryFailedType>? failureEffect)
|
|
{
|
|
if (patient.Comp.Surgeon == null || item == null)
|
|
return;
|
|
|
|
if (!RollSuccess(patient, patient.Comp.Surgeon.Value, successChance))
|
|
HandleFailure(patient, failureEffect, requiredPart);
|
|
|
|
if (!HasComp<SubdermalImplantComponent>(item.Value))
|
|
return;
|
|
|
|
_implant.ForceImplant(patient.Owner, item.Value);
|
|
_admin.Add(LogType.Action, LogImpact.Medium,
|
|
$"{ToPrettyString(patient.Comp.Surgeon.Value):user} successfully implanted {ToPrettyString(item.Value):implant} into {ToPrettyString(patient):target}");
|
|
}
|
|
|
|
private void PerformRemoveImplant(Entity<OperatedComponent> patient, string? requiredImplant, float successChance, List<SurgeryFailedType>? failureEffect)
|
|
{
|
|
if (patient.Comp.Surgeon == null || string.IsNullOrEmpty(requiredImplant))
|
|
return;
|
|
|
|
if (!RollSuccess(patient, patient.Comp.Surgeon.Value, successChance))
|
|
HandleFailure(patient, failureEffect, requiredImplant);
|
|
|
|
if (!TryComp<ImplantedComponent>(patient, out var implanted))
|
|
return;
|
|
|
|
foreach (var implant in implanted.ImplantContainer.ContainedEntities.ToArray())
|
|
{
|
|
var proto = MetaData(implant).EntityPrototype;
|
|
if (proto != null && proto.ID == requiredImplant)
|
|
{
|
|
if (TryComp<SubdermalImplantComponent>(implant, out var implantComp) && implantComp.Permanent)
|
|
return;
|
|
|
|
_implant.ForceRemove(patient.Owner, implant);
|
|
_hands.TryPickupAnyHand(patient.Comp.Surgeon.Value, implant);
|
|
var ev = new ImplantRemovedEvent(implant, patient.Owner);
|
|
RaiseLocalEvent(patient.Owner, ref ev);
|
|
|
|
_admin.Add(LogType.Action, LogImpact.Medium,
|
|
$"{ToPrettyString(patient.Comp.Surgeon.Value):user} successfully removed {ToPrettyString(implant):implant} from {ToPrettyString(patient):target}");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PerformStoreItem(Entity<OperatedComponent> patient, EntityUid? item, string? requiredPart, float successChance, List<SurgeryFailedType>? failureEffect)
|
|
{
|
|
if (patient.Comp.Surgeon == null || item == null || string.IsNullOrEmpty(requiredPart) || HasComp<BorgChassisComponent>(patient.Comp.Surgeon))
|
|
return;
|
|
|
|
if (!RollSuccess(patient, patient.Comp.Surgeon.Value, successChance))
|
|
HandleFailure(patient, failureEffect);
|
|
|
|
// You can't perform a mobs, body part, organ, or implant in this way
|
|
if (HasComp<BodyComponent>(item) || HasComp<OrganComponent>(item)
|
|
|| HasComp<SubdermalImplantComponent>(item) || !_internal.TryStoreItem(patient.Owner, item.Value, requiredPart))
|
|
_popup.PopupEntity(Loc.GetString("surgery-store-item-failed"), patient);
|
|
else
|
|
_admin.Add(LogType.Action, LogImpact.Medium,
|
|
$"{ToPrettyString(patient.Comp.Surgeon.Value):user} stored {ToPrettyString(item.Value):item} in {requiredPart} of {ToPrettyString(patient):target}");
|
|
}
|
|
|
|
private void PerformRetrieveItems(Entity<OperatedComponent> patient, string? requiredPart, float successChance, List<SurgeryFailedType>? failureEffect)
|
|
{
|
|
if (patient.Comp.Surgeon == null || string.IsNullOrEmpty(requiredPart))
|
|
return;
|
|
|
|
if (!RollSuccess(patient, patient.Comp.Surgeon.Value, successChance))
|
|
HandleFailure(patient, failureEffect);
|
|
|
|
if (!_internal.TryRemoveItems(patient, requiredPart))
|
|
_popup.PopupEntity(Loc.GetString("surgery-retrieve-items-failed"), patient);
|
|
else
|
|
_admin.Add(LogType.Action, LogImpact.Medium,
|
|
$"{ToPrettyString(patient.Comp.Surgeon.Value):user} retrieved items from {requiredPart} of {ToPrettyString(patient):target}");
|
|
}
|
|
|
|
#endregion Organic
|
|
|
|
#region Synthetic
|
|
|
|
private void PerformUnscrew(Entity<OperatedComponent> patient)
|
|
{
|
|
if (patient.Comp.Surgeon == null || !HasComp<SyntheticOperatedComponent>(patient))
|
|
return;
|
|
|
|
_damage.TryChangeDamage(patient.Owner, new DamageSpecifier { DamageDict = { { BluntDamage, 2f } } }, true);
|
|
if (TryComp<DamageableComponent>(patient, out var damageable))
|
|
{
|
|
var totalDamage = _damage.GetTotalDamage((patient, damageable));
|
|
if (totalDamage >= 50)
|
|
{
|
|
_audio.PlayPvs(new SoundCollectionSpecifier("sparks"), patient.Owner);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void PerformScrew(Entity<OperatedComponent> patient)
|
|
{
|
|
if (patient.Comp.Surgeon == null || !HasComp<SyntheticOperatedComponent>(patient))
|
|
return;
|
|
|
|
_damage.TryChangeDamage(patient.Owner, new DamageSpecifier { DamageDict = { { BluntDamage, -2f } } }, true);
|
|
}
|
|
|
|
private void PerformPulse(Entity<OperatedComponent> patient)
|
|
{
|
|
if (patient.Comp.Surgeon == null || !HasComp<SyntheticOperatedComponent>(patient))
|
|
return;
|
|
|
|
_stun.TryAddStunDuration(patient.Owner, TimeSpan.FromSeconds(5));
|
|
}
|
|
|
|
private void PerformWeld(Entity<OperatedComponent> patient)
|
|
{
|
|
if (patient.Comp.Surgeon == null || !HasComp<SyntheticOperatedComponent>(patient))
|
|
return;
|
|
|
|
var healAmount = new DamageSpecifier { DamageDict = { { BluntDamage, -5f }, { SlashDamage, -5f } } };
|
|
_damage.TryChangeDamage(patient.Owner, healAmount, true);
|
|
|
|
if (HasComp<BloodstreamComponent>(patient))
|
|
_bloodstream.TryModifyBleedAmount(patient.Owner, -10f);
|
|
}
|
|
|
|
private void PerformCutWire(Entity<OperatedComponent> patient)
|
|
{
|
|
if (patient.Comp.Surgeon == null || !HasComp<SyntheticOperatedComponent>(patient))
|
|
return;
|
|
|
|
_audio.PlayPvs(new SoundCollectionSpecifier("sparks"), patient.Owner);
|
|
_stun.TryAddStunDuration(patient.Owner, TimeSpan.FromSeconds(1));
|
|
}
|
|
|
|
private void PerformStripWire(Entity<OperatedComponent> patient)
|
|
{
|
|
if (patient.Comp.Surgeon == null || !HasComp<SyntheticOperatedComponent>(patient))
|
|
return;
|
|
|
|
_damage.TryChangeDamage(patient.Owner, new DamageSpecifier { DamageDict = { { SlashDamage, -2f } } }, true);
|
|
}
|
|
|
|
private void PerformMendWire(Entity<OperatedComponent> patient)
|
|
{
|
|
if (patient.Comp.Surgeon == null || !HasComp<SyntheticOperatedComponent>(patient))
|
|
return;
|
|
|
|
var healAmount = new DamageSpecifier
|
|
{
|
|
DamageDict = { { BluntDamage, -6f }, { SlashDamage, -6f }, { PiercingDamage, -6f }, { HeatDamage, -6f } }
|
|
};
|
|
|
|
_damage.TryChangeDamage(patient.Owner, healAmount, true);
|
|
}
|
|
|
|
private void PerformPry(Entity<OperatedComponent> patient)
|
|
{
|
|
if (patient.Comp.Surgeon == null || !HasComp<SyntheticOperatedComponent>(patient))
|
|
return;
|
|
|
|
_damage.TryChangeDamage(patient.Owner, new DamageSpecifier { DamageDict = { { BluntDamage, 5f } } }, true);
|
|
}
|
|
|
|
private void PerformAnchor(Entity<OperatedComponent> patient)
|
|
{
|
|
if (patient.Comp.Surgeon == null || !HasComp<SyntheticOperatedComponent>(patient))
|
|
return;
|
|
|
|
_damage.TryChangeDamage(patient.Owner, new DamageSpecifier { DamageDict = { { BluntDamage, -8f } } }, true);
|
|
}
|
|
|
|
private void PerformUnanchor(Entity<OperatedComponent> patient)
|
|
{
|
|
if (patient.Comp.Surgeon == null || !HasComp<SyntheticOperatedComponent>(patient))
|
|
return;
|
|
|
|
_damage.TryChangeDamage(patient.Owner, new DamageSpecifier { DamageDict = { { BluntDamage, 4f } } }, true);
|
|
if (_random.Prob(0.3f))
|
|
_audio.PlayPvs(new SoundCollectionSpecifier("sparks"), patient.Owner);
|
|
}
|
|
|
|
#endregion Synthetic
|
|
|
|
#region Other Logic
|
|
|
|
private bool RollSuccess(Entity<OperatedComponent> ent, EntityUid surgeon, float baseChance)
|
|
{
|
|
var item = _hands.GetActiveItemOrSelf(surgeon);
|
|
if (HasComp<SurgicalSkillComponent>(surgeon) && SurgeryTools.Any(tool => _tool.HasQuality(item, tool))
|
|
|| Organs.Any(tag => _tag.HasTag(item, tag)) || Parts.Any(tag => _tag.HasTag(item, tag)))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (TryGetOperatingTable(ent, out var tableModifier))
|
|
baseChance *= tableModifier;
|
|
|
|
return _random.Prob(baseChance);
|
|
}
|
|
|
|
private void ApplySterilityConsequences(Entity<OperatedComponent> patient)
|
|
{
|
|
if (patient.Comp.Surgeon == null || HasComp<SyntheticOperatedComponent>(patient))
|
|
return;
|
|
|
|
var sterility = patient.Comp.Sterility;
|
|
var item = _hands.GetActiveItemOrSelf(patient.Comp.Surgeon.Value);
|
|
if (sterility >= 0.7f && HasComp<SterileComponent>(item))
|
|
return;
|
|
|
|
if (sterility >= 0.5f)
|
|
{
|
|
var painChance = 0.3f + (0.75f - sterility) / 0.25f * 0.4f;
|
|
if (_random.Prob(painChance))
|
|
{
|
|
if (!HasComp<SleepingComponent>(patient) && !HasComp<PainNumbnessStatusEffectComponent>(patient)
|
|
&& !patient.Comp.OperatedPart && !_mobState.IsDead(patient) && !HasComp<SyntheticOperatedComponent>(patient))
|
|
_chat.TryEmoteWithoutChat(patient, _proto.Index(Scream), true);
|
|
|
|
_jittering.DoJitter(patient, TimeSpan.FromSeconds(4), true);
|
|
}
|
|
|
|
var slowChance = 0.15f + (0.75f - sterility) / 0.25f * 0.3f;
|
|
if (_random.Prob(slowChance))
|
|
{
|
|
_movementMod.TryUpdateMovementSpeedModDuration(patient, MovementModStatusSystem.Slowdown,
|
|
TimeSpan.FromSeconds(30), 0.85f, 0.85f);
|
|
}
|
|
return;
|
|
}
|
|
|
|
var painChanceLow = 0.7f + (0.5f - sterility) / 0.3f * 0.2f;
|
|
if (_random.Prob(painChanceLow))
|
|
{
|
|
if (!HasComp<SleepingComponent>(patient) && !HasComp<PainNumbnessStatusEffectComponent>(patient)
|
|
&& !patient.Comp.OperatedPart && !_mobState.IsDead(patient) && !HasComp<SyntheticOperatedComponent>(patient))
|
|
_chat.TryEmoteWithoutChat(patient, _proto.Index(Scream), true);
|
|
|
|
_jittering.DoJitter(patient, TimeSpan.FromSeconds(6), true);
|
|
}
|
|
|
|
var slowChanceLow = 0.45f + (0.5f - sterility) / 0.3f * 0.25f;
|
|
if (_random.Prob(slowChanceLow))
|
|
{
|
|
_movementMod.TryUpdateMovementSpeedModDuration(patient, MovementModStatusSystem.Slowdown,
|
|
TimeSpan.FromSeconds(45), 0.6f, 0.6f);
|
|
}
|
|
|
|
var sepsisChanceLow = 0.05f + (0.5f - sterility) / 0.3f * 0.05f;
|
|
if (_random.Prob(sepsisChanceLow))
|
|
{
|
|
_disease.TryAddDisease(patient, "SurgicalSepsis");
|
|
}
|
|
}
|
|
|
|
public void ApplyBloodToClothing(EntityUid surgeon, string? bloodReagentId, float bloodAmount)
|
|
{
|
|
if (bloodReagentId == null)
|
|
return;
|
|
|
|
var bloodSolution = new Solution();
|
|
bloodSolution.AddReagent(bloodReagentId, FixedPoint2.New(bloodAmount));
|
|
|
|
var clothingSlots = new[] { "outerClothing", "jumpsuit", "gloves" };
|
|
_dirt.ApplyDirtToClothing(surgeon, bloodSolution, _random.Pick(clothingSlots));
|
|
}
|
|
|
|
private void HandleFailure(Entity<OperatedComponent> patient, List<SurgeryFailedType>? failureEffect, string? bodyPart = null)
|
|
{
|
|
if (failureEffect == null || failureEffect.Count == 0)
|
|
return;
|
|
|
|
var effect = _random.Pick(failureEffect);
|
|
switch (effect)
|
|
{
|
|
case SurgeryFailedType.Empty: return;
|
|
case SurgeryFailedType.Cut:
|
|
_damage.TryChangeDamage(patient.Owner, new DamageSpecifier { DamageDict = { { SlashDamage, 5 } } }, true);
|
|
break;
|
|
case SurgeryFailedType.Bleeding:
|
|
TryAddInternalDamage(patient, "ArterialBleeding", bodyPart: bodyPart);
|
|
break;
|
|
case SurgeryFailedType.Burn:
|
|
_damage.TryChangeDamage(patient.Owner, new DamageSpecifier { DamageDict = { { HeatDamage, 5 } } }, true);
|
|
break;
|
|
case SurgeryFailedType.Fracture:
|
|
TryAddInternalDamage(patient, "BoneFracture", bodyPart: bodyPart);
|
|
break;
|
|
case SurgeryFailedType.Pain:
|
|
{
|
|
if (!HasComp<SleepingComponent>(patient) && !HasComp<PainNumbnessStatusEffectComponent>(patient) && !patient.Comp.OperatedPart
|
|
&& !_mobState.IsDead(patient) && !HasComp<SyntheticOperatedComponent>(patient))
|
|
_chat.TryEmoteWithoutChat(patient, _proto.Index(Scream), true);
|
|
|
|
_jittering.DoJitter(patient, TimeSpan.FromSeconds(5), true);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (effect != SurgeryFailedType.Empty && !_mobState.IsDead(patient))
|
|
_popup.PopupPredicted(Loc.GetString($"surgery-handle-failed-{effect.ToString().ToLower()}", ("patient", Identity.Entity(patient, EntityManager))),
|
|
patient, null, PopupType.MediumCaution);
|
|
}
|
|
|
|
#endregion
|
|
}
|