mirror of
https://github.com/wega-team/ss14-wega.git
synced 2026-06-09 10:06:49 +02:00
surgerypatch
This commit is contained in:
@@ -2,15 +2,12 @@ using Content.Shared.Surgery;
|
||||
using Content.Shared.Surgery.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Client._Wega.Surgery.Ui;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed partial class SurgeryBoundUserInterface : BoundUserInterface
|
||||
{
|
||||
[Dependency] private ISharedPlayerManager _playerManager = default!;
|
||||
|
||||
[ViewVariables]
|
||||
private SurgeryWindow? _window;
|
||||
|
||||
@@ -24,19 +21,22 @@ public sealed partial class SurgeryBoundUserInterface : BoundUserInterface
|
||||
|
||||
_window.OnStepPressed += (targetNode, stepIndex, isParallel) =>
|
||||
{
|
||||
var netEntity = EntMan.GetNetEntity(_playerManager.LocalSession?.AttachedEntity ?? EntityUid.Invalid);
|
||||
SendMessage(new SurgeryStartMessage(netEntity, targetNode, stepIndex, isParallel));
|
||||
SendMessage(new SurgeryStartMessage(targetNode, stepIndex, isParallel));
|
||||
};
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
base.UpdateState(state);
|
||||
|
||||
if (EntMan.TryGetComponent(Owner, out OperatedComponent? comp)
|
||||
&& state is SurgeryProcedureDtoState procedureState)
|
||||
_window?.UpdateState(procedureState, comp);
|
||||
}
|
||||
|
||||
protected override void ReceiveMessage(BoundUserInterfaceMessage message)
|
||||
{
|
||||
if (_window == null || message is not SurgeryProcedureDto msg)
|
||||
return;
|
||||
|
||||
if (EntMan.TryGetComponent(Owner, out OperatedComponent? comp))
|
||||
{
|
||||
_window.UpdateState(msg, comp);
|
||||
}
|
||||
if (message is SurgerySterilityUpdateMessage msg)
|
||||
_window?.UpdateSterilityToolTip(msg.SterilityInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,13 +11,14 @@ using Robust.Shared.Prototypes;
|
||||
using System.Numerics;
|
||||
using Robust.Client.UserInterface;
|
||||
using Content.Shared.Tools;
|
||||
using Content.Shared.Guidebook;
|
||||
|
||||
namespace Content.Client._Wega.Surgery.Ui;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class SurgeryWindow : FancyWindow
|
||||
{
|
||||
private readonly IPrototypeManager _prototypeManager;
|
||||
[Dependency] private IPrototypeManager _prototype = default!;
|
||||
|
||||
private readonly StyleBoxFlat _groupButtonStyle = new()
|
||||
{
|
||||
@@ -49,7 +50,7 @@ public sealed partial class SurgeryWindow : FancyWindow
|
||||
RobustXamlLoader.Load(this);
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
HelpGuidebookIds = new List<ProtoId<GuideEntryPrototype>> { "Surgery" };
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
@@ -64,7 +65,7 @@ public sealed partial class SurgeryWindow : FancyWindow
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateState(SurgeryProcedureDto state, OperatedComponent comp)
|
||||
public void UpdateState(SurgeryProcedureDtoState state, OperatedComponent comp)
|
||||
{
|
||||
GroupListContainer.RemoveAllChildren();
|
||||
ProtoId<SurgeryNodePrototype>? currentTargetNode = comp.CurrentTargetNode;
|
||||
@@ -114,6 +115,30 @@ public sealed partial class SurgeryWindow : FancyWindow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UpdateSterilityToolTip(state.SterilityInfo);
|
||||
}
|
||||
|
||||
public void UpdateSterilityToolTip(SurgerySterilityInfo info)
|
||||
{
|
||||
var percent = (int)(info.Sterility * 100);
|
||||
var tooltipText = Loc.GetString("surgery-sterility-percent", ("percent", percent)) + "\n\n";
|
||||
|
||||
if (info.NegativeFactors.Count > 0)
|
||||
{
|
||||
tooltipText += Loc.GetString("surgery-sterility-negative-header") + "\n";
|
||||
foreach (var factor in info.NegativeFactors)
|
||||
tooltipText += $"• {factor}\n";
|
||||
}
|
||||
|
||||
if (info.PositiveFactors.Count > 0)
|
||||
{
|
||||
tooltipText += Loc.GetString("surgery-sterility-positive-header") + "\n";
|
||||
foreach (var factor in info.PositiveFactors)
|
||||
tooltipText += $"• {factor}\n";
|
||||
}
|
||||
|
||||
HelpButton.ToolTip = tooltipText;
|
||||
}
|
||||
|
||||
private void SelectGroup(Button button)
|
||||
@@ -212,8 +237,8 @@ public sealed partial class SurgeryWindow : FancyWindow
|
||||
Margin = new Thickness(4)
|
||||
};
|
||||
|
||||
if (_prototypeManager.TryIndex<ToolQualityPrototype>(toolQualityId, out var toolQuality) &&
|
||||
_prototypeManager.TryIndex<EntityPrototype>(toolQuality.Spawn, out var toolProto))
|
||||
if (_prototype.TryIndex<ToolQualityPrototype>(toolQualityId, out var toolQuality) &&
|
||||
_prototype.TryIndex<EntityPrototype>(toolQuality.Spawn, out var toolProto))
|
||||
{
|
||||
var icon = new EntityPrototypeView
|
||||
{
|
||||
@@ -242,7 +267,7 @@ public sealed partial class SurgeryWindow : FancyWindow
|
||||
Margin = new Thickness(4)
|
||||
};
|
||||
|
||||
if (_prototypeManager.TryIndex<EntityPrototype>(entityPreview, out var entity))
|
||||
if (_prototype.TryIndex<EntityPrototype>(entityPreview, out var entity))
|
||||
{
|
||||
var icon = new EntityPrototypeView
|
||||
{
|
||||
@@ -266,8 +291,8 @@ public sealed partial class SurgeryWindow : FancyWindow
|
||||
{
|
||||
var tooltip = "";
|
||||
var tool = step.RequiredTool;
|
||||
if (!string.IsNullOrEmpty(tool) && _prototypeManager.TryIndex<ToolQualityPrototype>(tool, out var toolQuality) &&
|
||||
_prototypeManager.TryIndex<EntityPrototype>(toolQuality.Spawn, out var toolProto))
|
||||
if (!string.IsNullOrEmpty(tool) && _prototype.TryIndex<ToolQualityPrototype>(tool, out var toolQuality) &&
|
||||
_prototype.TryIndex<EntityPrototype>(toolQuality.Spawn, out var toolProto))
|
||||
tool = toolProto.Name;
|
||||
|
||||
if (!string.IsNullOrEmpty(tool))
|
||||
@@ -277,4 +302,4 @@ public sealed partial class SurgeryWindow : FancyWindow
|
||||
("condition", Loc.GetString($"surgery-condition-required-{step.RequiredCondition.ToLower()}")));
|
||||
return tooltip.Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,14 +17,14 @@ using Robust.Server.Containers;
|
||||
using Robust.Shared.Map;
|
||||
// Corvax-Wega-Add-start
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Surgery.Components;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Server.Chemistry.Components;
|
||||
using Content.Shared.Vapor;
|
||||
using Content.Server.Surgery; // Corvax-Wega-Surgery
|
||||
|
||||
// Corvax-Wega-Add-end
|
||||
|
||||
namespace Content.Server.Fluids.EntitySystems;
|
||||
@@ -44,6 +44,7 @@ public sealed partial class SpraySystem : SharedSpraySystem
|
||||
[Dependency] private SharedAppearanceSystem _appearance = default!; // Corvax-Wega-Add
|
||||
[Dependency] private IPrototypeManager _proto = default!; // Corvax-Wega-Add
|
||||
[Dependency] private ReactiveSystem _reactive = default!; // Corvax-Wega-Add
|
||||
[Dependency] private SterileSystem _sterile = default!; // Corvax-Wega-Surgery
|
||||
|
||||
private float _gridImpulseMultiplier;
|
||||
|
||||
@@ -88,11 +89,10 @@ public sealed partial class SpraySystem : SharedSpraySystem
|
||||
// Corvax-Wega-Add-end
|
||||
|
||||
// Corvax-Wega-Surgery-start
|
||||
if (args.Target != null && HasComp<ItemComponent>(args.Target)
|
||||
&& _solutionContainer.TryGetSolution(entity.Owner, entity.Comp.Solution, out _, out var solution)
|
||||
&& solution.GetTotalPrototypeQuantity("Ethanol") >= FixedPoint2.New(5))
|
||||
if (args.Target != null && HasComp<ItemComponent>(args.Target) && _solutionContainer.TryGetSolution(entity.Owner, entity.Comp.Solution, out _, out var solution)
|
||||
&& Transform(entity).ParentUid != Transform(args.Target.Value).ParentUid) // Spray can't hand interact
|
||||
{
|
||||
EnsureComp<SterileComponent>(args.Target.Value);
|
||||
_sterile.ApplySterilityFromSolution(args.Target.Value, solution, entity.Comp.TransferAmount.Float());
|
||||
}
|
||||
// Corvax-Wega-Surgery-end
|
||||
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Item;
|
||||
using Content.Shared.Surgery.Components;
|
||||
using Content.Shared.Throwing;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Surgery;
|
||||
|
||||
public sealed partial class SterileSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private IPrototypeManager _proto = default!;
|
||||
|
||||
private static readonly ProtoId<ReagentPrototype> EthanolReagent = "Ethanol";
|
||||
|
||||
private const float EthanolUnitsPerSterilePoint = 0.05f;
|
||||
private const float MaxSterileAmount = 100f;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SterileComponent, ExaminedEvent>(OnExamined);
|
||||
SubscribeLocalEvent<SterileComponent, ThrownEvent>(OnThrow);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
var sterileQuery = EntityQueryEnumerator<SterileComponent>();
|
||||
while (sterileQuery.MoveNext(out var uid, out var sterile))
|
||||
{
|
||||
if (sterile.AlwaysSterile)
|
||||
continue;
|
||||
|
||||
if (sterile.NextUpdateTick <= 0)
|
||||
{
|
||||
sterile.NextUpdateTick = 5f;
|
||||
sterile.Amount -= sterile.DecayRate;
|
||||
if (sterile.Amount <= 0)
|
||||
{
|
||||
RemComp<SterileComponent>(uid);
|
||||
}
|
||||
}
|
||||
sterile.NextUpdateTick -= frameTime;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnExamined(Entity<SterileComponent> entity, ref ExaminedEvent args)
|
||||
{
|
||||
if (args.IsInDetailsRange)
|
||||
args.AddMarkup(Loc.GetString("surgery-sterile-examined") + "\n");
|
||||
}
|
||||
|
||||
private void OnThrow(Entity<SterileComponent> entity, ref ThrownEvent args)
|
||||
=> RemCompDeferred<SterileComponent>(entity);
|
||||
|
||||
public float ApplySterilityFromSolution(EntityUid target, Solution solution, float transferAmount)
|
||||
{
|
||||
if (!HasComp<ItemComponent>(target))
|
||||
return 0f;
|
||||
|
||||
var totalEthanol = GetTotalEthanolEquivalent(solution, transferAmount);
|
||||
if (totalEthanol <= 0)
|
||||
return 0f;
|
||||
|
||||
var sterileAmount = totalEthanol / EthanolUnitsPerSterilePoint;
|
||||
sterileAmount = Math.Min(sterileAmount, MaxSterileAmount);
|
||||
|
||||
var sterileComp = EnsureComp<SterileComponent>(target);
|
||||
sterileComp.Amount = Math.Min(sterileComp.Amount + sterileAmount, MaxSterileAmount);
|
||||
sterileComp.NextUpdateTick = 0f;
|
||||
|
||||
return sterileAmount;
|
||||
}
|
||||
|
||||
private float GetTotalEthanolEquivalent(Solution solution, float transferAmount)
|
||||
{
|
||||
if (transferAmount <= 0)
|
||||
return 0f;
|
||||
|
||||
var totalSolutionVolume = solution.Volume.Float();
|
||||
if (totalSolutionVolume <= 0)
|
||||
return 0f;
|
||||
|
||||
var ratio = transferAmount / totalSolutionVolume;
|
||||
float total = 0f;
|
||||
|
||||
foreach (var reagent in solution.Contents)
|
||||
{
|
||||
var reagentProto = _proto.Index<ReagentPrototype>(reagent.Reagent.Prototype);
|
||||
float ethanolPerUnit = 0f;
|
||||
|
||||
if (reagent.Reagent.Prototype == EthanolReagent)
|
||||
{
|
||||
ethanolPerUnit = 1f;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (reagentProto.Metabolisms?.Metabolisms.TryGetValue("Digestion", out var metabolism) == true
|
||||
&& metabolism.Metabolites != null)
|
||||
{
|
||||
foreach (var metabolite in metabolism.Metabolites)
|
||||
{
|
||||
if (metabolite.Key == EthanolReagent)
|
||||
{
|
||||
ethanolPerUnit = metabolite.Value.Float();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ethanolPerUnit > 0f)
|
||||
{
|
||||
var transferredReagentAmount = reagent.Quantity.Float() * ratio;
|
||||
total += transferredReagentAmount * ethanolPerUnit;
|
||||
}
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ public sealed partial class SurgerySystem
|
||||
/// <param name="args">The SurgeryStartMessage containing details about the surgery start request.</param>
|
||||
private void OnSurgeryStart(EntityUid uid, OperatedComponent comp, SurgeryStartMessage args)
|
||||
{
|
||||
var user = GetEntity(args.User);
|
||||
var user = args.Actor;
|
||||
if (comp.GraphId == null)
|
||||
return;
|
||||
|
||||
@@ -162,7 +162,7 @@ public sealed partial class SurgerySystem
|
||||
}
|
||||
|
||||
CheckTransitionProgress(uid, comp, graph, transition);
|
||||
UpdateUi(uid, comp, graph);
|
||||
UpdateUi(uid, args.User, comp, graph);
|
||||
}
|
||||
|
||||
#region Handle Steps
|
||||
@@ -388,12 +388,11 @@ public sealed partial class SurgerySystem
|
||||
}
|
||||
|
||||
bool hasTool = step.Tool != null && step.Tool.Count != 0;
|
||||
bool toolValid = step.Tool == null || step.Tool.Count == 0 || step.Action == SurgeryActionType.StoreItem
|
||||
|| step.Tool.Any(tool => _tool.HasQuality(item.Value, tool));
|
||||
bool tagValid = step.Tag == null || step.Tag.Count == 0 || step.Action == SurgeryActionType.StoreItem
|
||||
|| step.Tag.Any(tag => _tag.HasTag(item.Value, tag));
|
||||
bool hasTag = step.Tag != null && step.Tag.Count != 0;
|
||||
bool toolValid = !hasTool || step.Action == SurgeryActionType.StoreItem || step.Tool!.Any(tool => _tool.HasQuality(item.Value, tool));
|
||||
bool tagValid = !hasTag || step.Action == SurgeryActionType.StoreItem || step.Tag!.Any(tag => _tag.HasTag(item.Value, tag));
|
||||
|
||||
if (!toolValid || !hasTool && !tagValid)
|
||||
if (hasTool && !toolValid && hasTag && !tagValid)
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("surgery-missing-tool"), user, user);
|
||||
return;
|
||||
@@ -453,7 +452,6 @@ public sealed partial class SurgerySystem
|
||||
comp.ResetOperationState(transition.Target);
|
||||
comp.CurrentNode = transition.Target;
|
||||
Dirty(uid, comp);
|
||||
UpdateUi(uid, comp, graph);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ 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;
|
||||
@@ -127,9 +128,11 @@ public sealed partial class SurgerySystem
|
||||
break;
|
||||
// Synthetic End
|
||||
|
||||
default: break;
|
||||
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))
|
||||
@@ -175,10 +178,7 @@ public sealed partial class SurgerySystem
|
||||
return;
|
||||
|
||||
if (!RollSuccess(patient, patient.Comp.Surgeon.Value, successChance))
|
||||
{
|
||||
HandleFailure(patient, failureEffect);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HasComp<BloodstreamComponent>(patient) || HasComp<SyntheticOperatedComponent>(patient))
|
||||
return;
|
||||
@@ -459,19 +459,71 @@ public sealed partial class SurgerySystem
|
||||
private bool RollSuccess(Entity<OperatedComponent> ent, EntityUid surgeon, float baseChance)
|
||||
{
|
||||
var item = _hands.GetActiveItemOrSelf(surgeon);
|
||||
if (HasComp<SurgicalSkillComponent>(surgeon) && ent.Comp.Sterility == 1f && HasComp<SterileComponent>(item)
|
||||
&& SurgeryTools.Any(tool => _tool.HasQuality(item, tool))
|
||||
|| Organs.Any(tag => _tag.HasTag(item, tag))
|
||||
|| Parts.Any(tag => _tag.HasTag(item, tag)))
|
||||
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;
|
||||
}
|
||||
|
||||
var adjustedChance = baseChance * Math.Clamp(ent.Comp.Sterility, 0f, 1.5f);
|
||||
if (TryGetOperatingTable(ent, out var tableModifier))
|
||||
adjustedChance *= tableModifier;
|
||||
baseChance *= tableModifier;
|
||||
|
||||
return _random.Prob(adjustedChance);
|
||||
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)
|
||||
@@ -494,8 +546,7 @@ public sealed partial class SurgerySystem
|
||||
var effect = _random.Pick(failureEffect);
|
||||
switch (effect)
|
||||
{
|
||||
case SurgeryFailedType.Empty:
|
||||
return;
|
||||
case SurgeryFailedType.Empty: return;
|
||||
case SurgeryFailedType.Cut:
|
||||
_damage.TryChangeDamage(patient.Owner, new DamageSpecifier { DamageDict = { { SlashDamage, 5 } } }, true);
|
||||
break;
|
||||
@@ -509,11 +560,14 @@ public sealed partial class SurgerySystem
|
||||
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);
|
||||
{
|
||||
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;
|
||||
_jittering.DoJitter(patient, TimeSpan.FromSeconds(5), true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (effect != SurgeryFailedType.Empty && !_mobState.IsDead(patient))
|
||||
|
||||
@@ -5,6 +5,7 @@ using Content.Shared.DirtVisuals;
|
||||
using Content.Shared.Ghost;
|
||||
using Content.Shared.Shuttles.Components;
|
||||
using Content.Shared.Silicons.Borgs.Components;
|
||||
using Content.Shared.Surgery;
|
||||
using Content.Shared.Surgery.Components;
|
||||
|
||||
namespace Content.Server.Surgery;
|
||||
@@ -13,6 +14,8 @@ public sealed partial class SurgerySystem
|
||||
{
|
||||
[Dependency] private EntityLookupSystem _entityLookup = default!;
|
||||
|
||||
#region Sterility
|
||||
|
||||
private void UpdateOperationSterility(EntityUid patient, OperatedComponent operated)
|
||||
{
|
||||
if (operated.Surgeon == null || HasComp<SyntheticOperatedComponent>(patient))
|
||||
@@ -20,52 +23,49 @@ public sealed partial class SurgerySystem
|
||||
|
||||
float sterility = 1f;
|
||||
|
||||
// Важные слоты (сильное влияние)
|
||||
CheckClothingSlot(operated.Surgeon.Value, "gloves", ref sterility, 0.6f, true);
|
||||
CheckClothingSlot(operated.Surgeon.Value, "mask", ref sterility, 0.6f, true);
|
||||
// Важные слоты
|
||||
CheckClothingSlot(operated.Surgeon.Value, "gloves", ref sterility, 0.15f, true);
|
||||
CheckClothingSlot(operated.Surgeon.Value, "mask", ref sterility, 0.15f, true);
|
||||
|
||||
// Средние слоты (умеренное влияние)
|
||||
CheckClothingSlot(operated.Surgeon.Value, "head", ref sterility, 0.2f);
|
||||
CheckClothingSlot(operated.Surgeon.Value, "jumpsuit", ref sterility, 0.2f);
|
||||
CheckClothingSlot(operated.Surgeon.Value, "outerClothing", ref sterility, 0.2f, ingnoreSlot: true);
|
||||
// Средние слоты
|
||||
CheckClothingSlot(operated.Surgeon.Value, "head", ref sterility, 0.05f);
|
||||
CheckClothingSlot(operated.Surgeon.Value, "jumpsuit", ref sterility, 0.05f);
|
||||
CheckClothingSlot(operated.Surgeon.Value, "outerClothing", ref sterility, 0.05f, ingnoreSlot: true);
|
||||
|
||||
// Нежелательные слоты (небольшой дебафф)
|
||||
CheckClothingSlot(operated.Surgeon.Value, "back", ref sterility, 0.1f, ingnoreSlot: true);
|
||||
CheckClothingSlot(operated.Surgeon.Value, "belt", ref sterility, 0.1f, ingnoreSlot: true);
|
||||
// Нежелательные слоты
|
||||
CheckClothingSlot(operated.Surgeon.Value, "back", ref sterility, 0.02f, ingnoreSlot: true);
|
||||
CheckClothingSlot(operated.Surgeon.Value, "belt", ref sterility, 0.02f, ingnoreSlot: true);
|
||||
|
||||
var garbageCount = _entityLookup.GetEntitiesInRange<SpaceGarbageComponent>(
|
||||
Transform(patient).Coordinates, 2f).Count;
|
||||
Transform(patient).Coordinates, 1.5f).Count;
|
||||
|
||||
sterility *= Math.Max(0.1f, 1f - garbageCount * 0.1f);
|
||||
sterility *= Math.Max(0.7f, 1f - garbageCount * 0.05f);
|
||||
|
||||
var item = _hands.GetActiveItemOrSelf(operated.Surgeon.Value);
|
||||
if (!HasComp<SterileComponent>(item))
|
||||
sterility *= 0.4f;
|
||||
sterility *= 0.85f;
|
||||
|
||||
var bystanders = _entityLookup.GetEntitiesInRange<BodyComponent>(
|
||||
Transform(patient).Coordinates, 2f)
|
||||
var bystanders = _entityLookup.GetEntitiesInRange<BodyComponent>(Transform(patient).Coordinates, 2f)
|
||||
.Where(e => e.Owner != patient && e.Owner != operated.Surgeon
|
||||
&& !_mobState.IsDead(e.Owner) && !HasComp<GhostComponent>(e.Owner))
|
||||
.Count();
|
||||
&& !_mobState.IsDead(e.Owner) && !HasComp<GhostComponent>(e.Owner));
|
||||
|
||||
float bystanderModifier = bystanders switch
|
||||
float bystanderModifier = bystanders.Count() switch
|
||||
{
|
||||
<= 2 => 1f,
|
||||
<= 4 => 0.9f,
|
||||
<= 6 => 0.8f,
|
||||
_ => 0.7f
|
||||
<= 4 => 0.97f,
|
||||
<= 6 => 0.94f,
|
||||
_ => 0.9f
|
||||
};
|
||||
sterility *= bystanderModifier;
|
||||
|
||||
var corpses = _entityLookup.GetEntitiesInRange<BodyComponent>(
|
||||
Transform(patient).Coordinates, 2f)
|
||||
var corpses = _entityLookup.GetEntitiesInRange<BodyComponent>(Transform(patient).Coordinates, 2f)
|
||||
.Where(e => e.Owner != patient && e.Owner != operated.Surgeon
|
||||
&& _mobState.IsDead(e.Owner) && !HasComp<GhostComponent>(e.Owner))
|
||||
.Count();
|
||||
&& _mobState.IsDead(e.Owner) && !HasComp<GhostComponent>(e.Owner));
|
||||
|
||||
sterility *= 1f - corpses * 0.05f;
|
||||
sterility *= 1f - corpses.Count() * 0.03f;
|
||||
|
||||
operated.Sterility = Math.Clamp(sterility, 0f, 1f);
|
||||
operated.Sterility = Math.Clamp(sterility, 0.2f, 1f);
|
||||
SendSterilityUpdateToUi(patient, operated.Surgeon.Value);
|
||||
}
|
||||
|
||||
private void CheckClothingSlot(EntityUid surgeon, string slot, ref float sterility, float penaltyModifier,
|
||||
@@ -90,20 +90,156 @@ public sealed partial class SurgerySystem
|
||||
|
||||
if (TryComp<ClothingSterilityComponent>(clothing, out var sterilityComp) && !isMaskOff)
|
||||
{
|
||||
sterility *= sterilityComp.Modifier * (isDirty ? 0.8f : 1f);
|
||||
sterility *= sterilityComp.Modifier * (isDirty ? 0.95f : 1f);
|
||||
}
|
||||
else
|
||||
{
|
||||
sterility *= (1f - penaltyModifier) * (isDirty ? 0.9f : 1f);
|
||||
sterility *= (1f - penaltyModifier) * (isDirty ? 0.98f : 1f);
|
||||
}
|
||||
}
|
||||
else if (isCritical)
|
||||
{
|
||||
sterility *= 0.5f;
|
||||
sterility *= 0.85f;
|
||||
}
|
||||
else if (!ingnoreSlot)
|
||||
{
|
||||
sterility *= 1f - penaltyModifier;
|
||||
sterility *= 1f - penaltyModifier * 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region UI Info
|
||||
|
||||
private SurgerySterilityInfo GetSterilityInfo(EntityUid patient, EntityUid surgeon)
|
||||
{
|
||||
if (HasComp<SyntheticOperatedComponent>(patient))
|
||||
return new SurgerySterilityInfo(1f, new List<string>(), new List<string>());
|
||||
|
||||
float sterility = 1f;
|
||||
var negativeFactors = new List<string>();
|
||||
var positiveFactors = new List<string>();
|
||||
|
||||
// Важные слоты
|
||||
CheckClothingSlotWithFactors(surgeon, "gloves", ref sterility, 0.15f, true, negativeFactors, positiveFactors);
|
||||
CheckClothingSlotWithFactors(surgeon, "mask", ref sterility, 0.15f, true, negativeFactors, positiveFactors);
|
||||
|
||||
// Средние слоты
|
||||
CheckClothingSlotWithFactors(surgeon, "head", ref sterility, 0.05f, false, negativeFactors, positiveFactors);
|
||||
CheckClothingSlotWithFactors(surgeon, "jumpsuit", ref sterility, 0.05f, false, negativeFactors, positiveFactors);
|
||||
CheckClothingSlotWithFactors(surgeon, "outerClothing", ref sterility, 0.05f, true, negativeFactors, positiveFactors);
|
||||
|
||||
// Нежелательные слоты
|
||||
CheckClothingSlotWithFactors(surgeon, "back", ref sterility, 0.02f, true, negativeFactors, positiveFactors);
|
||||
CheckClothingSlotWithFactors(surgeon, "belt", ref sterility, 0.02f, true, negativeFactors, positiveFactors);
|
||||
|
||||
var garbageCount = _entityLookup.GetEntitiesInRange<SpaceGarbageComponent>(
|
||||
Transform(patient).Coordinates, 1.5f).Count;
|
||||
|
||||
if (garbageCount > 0)
|
||||
{
|
||||
var garbageModifier = Math.Max(0.7f, 1f - garbageCount * 0.05f);
|
||||
sterility *= garbageModifier;
|
||||
negativeFactors.Add(Loc.GetString("surgery-sterility-garbage", ("count", garbageCount)));
|
||||
}
|
||||
|
||||
var item = _hands.GetActiveItemOrSelf(surgeon);
|
||||
if (!HasComp<SterileComponent>(item))
|
||||
{
|
||||
sterility *= 0.85f;
|
||||
negativeFactors.Add(Loc.GetString("surgery-sterility-non-sterile-tool"));
|
||||
}
|
||||
else
|
||||
{
|
||||
positiveFactors.Add(Loc.GetString("surgery-sterility-sterile-tool"));
|
||||
}
|
||||
|
||||
var bystanders = _entityLookup.GetEntitiesInRange<BodyComponent>(Transform(patient).Coordinates, 2f)
|
||||
.Where(e => e.Owner != patient && e.Owner != surgeon
|
||||
&& !_mobState.IsDead(e.Owner) && !HasComp<GhostComponent>(e.Owner));
|
||||
|
||||
int bystanderCount = bystanders.Count();
|
||||
if (bystanderCount > 2)
|
||||
{
|
||||
float bystanderModifier = bystanderCount switch
|
||||
{
|
||||
<= 4 => 0.97f,
|
||||
<= 6 => 0.94f,
|
||||
_ => 0.9f
|
||||
};
|
||||
sterility *= bystanderModifier;
|
||||
negativeFactors.Add(Loc.GetString("surgery-sterility-bystanders", ("count", bystanderCount)));
|
||||
}
|
||||
|
||||
var corpses = _entityLookup.GetEntitiesInRange<BodyComponent>(Transform(patient).Coordinates, 2f)
|
||||
.Where(e => e.Owner != patient && e.Owner != surgeon
|
||||
&& _mobState.IsDead(e.Owner) && !HasComp<GhostComponent>(e.Owner));
|
||||
|
||||
int corpseCount = corpses.Count();
|
||||
if (corpseCount > 0)
|
||||
{
|
||||
sterility *= 1f - corpseCount * 0.03f;
|
||||
negativeFactors.Add(Loc.GetString("surgery-sterility-corpses", ("count", corpseCount)));
|
||||
}
|
||||
|
||||
if (TryGetOperatingTable(patient, out _))
|
||||
{
|
||||
positiveFactors.Add(Loc.GetString("surgery-sterility-operating-table"));
|
||||
}
|
||||
|
||||
sterility = Math.Clamp(sterility, 0.2f, 1f);
|
||||
return new SurgerySterilityInfo(sterility, negativeFactors, positiveFactors);
|
||||
}
|
||||
|
||||
private void CheckClothingSlotWithFactors(EntityUid surgeon, string slot, ref float sterility, float penaltyModifier,
|
||||
bool ignoreSlot, List<string> negativeFactors, List<string> positiveFactors)
|
||||
{
|
||||
if (HasComp<BorgChassisComponent>(surgeon))
|
||||
return;
|
||||
|
||||
if (_inventory.TryGetSlotEntity(surgeon, slot, out var clothing))
|
||||
{
|
||||
bool isMaskOff = false;
|
||||
if (TryComp(clothing, out MaskComponent? mask))
|
||||
isMaskOff = mask.IsToggled;
|
||||
|
||||
bool isDirty = false;
|
||||
if (TryComp<DirtableComponent>(clothing, out var dirtable))
|
||||
{
|
||||
var dirtLevel = Math.Clamp(dirtable.CurrentDirtLevel.Float() / SharedDirtSystem.MaxDirtLevel * 100f, 0f, 100f);
|
||||
if (dirtable.IsDirty && dirtLevel >= 50f)
|
||||
isDirty = true;
|
||||
}
|
||||
|
||||
if (TryComp<ClothingSterilityComponent>(clothing, out var sterilityComp) && !isMaskOff)
|
||||
{
|
||||
var modifier = sterilityComp.Modifier * (isDirty ? 0.95f : 1f);
|
||||
sterility *= modifier;
|
||||
if (modifier > 1f)
|
||||
positiveFactors.Add(Loc.GetString($"surgery-sterility-sterile-{slot}"));
|
||||
else if (modifier < 1f)
|
||||
negativeFactors.Add(Loc.GetString($"surgery-sterility-non-sterile-{slot}"));
|
||||
}
|
||||
else
|
||||
{
|
||||
var modifier = (1f - penaltyModifier) * (isDirty ? 0.98f : 1f);
|
||||
sterility *= modifier;
|
||||
if (modifier < 1f)
|
||||
negativeFactors.Add(Loc.GetString($"surgery-sterility-no-sterile-{slot}"));
|
||||
}
|
||||
}
|
||||
else if (slot == "gloves" || slot == "mask")
|
||||
{
|
||||
sterility *= 0.85f;
|
||||
negativeFactors.Add(Loc.GetString($"surgery-sterility-no-{slot}"));
|
||||
}
|
||||
else if (!ignoreSlot)
|
||||
{
|
||||
var modifier = 1f - penaltyModifier * 0.5f;
|
||||
sterility *= modifier;
|
||||
negativeFactors.Add(Loc.GetString($"surgery-sterility-no-{slot}"));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ using Content.Shared.Interaction;
|
||||
using Content.Shared.Kitchen.Components;
|
||||
using Content.Shared.Surgery;
|
||||
using Content.Shared.Surgery.Components;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Surgery;
|
||||
|
||||
@@ -47,10 +46,7 @@ public sealed partial class SurgerySystem
|
||||
return;
|
||||
|
||||
var graph = _proto.Index<SurgeryGraphPrototype>(comp.GraphId);
|
||||
Timer.Spawn(250, () =>
|
||||
{
|
||||
UpdateUi(uid, comp, graph);
|
||||
});
|
||||
UpdateUi(uid, args.Actor, comp, graph);
|
||||
}
|
||||
|
||||
private void OnUnbuckled(Entity<OperatedComponent> ent, ref UnbuckledEvent args)
|
||||
@@ -61,7 +57,7 @@ public sealed partial class SurgerySystem
|
||||
_ui.CloseUi(ent.Owner, SurgeryUiKey.Key);
|
||||
}
|
||||
|
||||
private void UpdateUi(EntityUid patient, OperatedComponent comp, SurgeryGraphPrototype graph)
|
||||
private void UpdateUi(EntityUid patient, EntityUid surgeon, OperatedComponent comp, SurgeryGraphPrototype graph)
|
||||
{
|
||||
if (!_ui.HasUi(patient, SurgeryUiKey.Key))
|
||||
return;
|
||||
@@ -83,7 +79,16 @@ public sealed partial class SurgerySystem
|
||||
AddNodeSteps(node, patient, comp, groups);
|
||||
}
|
||||
|
||||
_ui.ServerSendUiMessage(patient, SurgeryUiKey.Key,
|
||||
new SurgeryProcedureDto(groups, GetNetEntity(patient)));
|
||||
var sterilityInfo = GetSterilityInfo(patient, surgeon);
|
||||
_ui.SetUiState(patient, SurgeryUiKey.Key, new SurgeryProcedureDtoState(groups, sterilityInfo));
|
||||
}
|
||||
|
||||
private void SendSterilityUpdateToUi(EntityUid patient, EntityUid surgeon)
|
||||
{
|
||||
if (!_ui.HasUi(patient, SurgeryUiKey.Key))
|
||||
return;
|
||||
|
||||
var sterilityInfo = GetSterilityInfo(patient, surgeon);
|
||||
_ui.ServerSendUiMessage(patient, SurgeryUiKey.Key, new SurgerySterilityUpdateMessage(sterilityInfo), surgeon);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ using Content.Shared.Buckle.Components;
|
||||
using Content.Shared.Chat.Prototypes;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Inventory.Events;
|
||||
@@ -19,7 +18,6 @@ using Content.Shared.Rejuvenate;
|
||||
using Content.Shared.Stunnable;
|
||||
using Content.Shared.Surgery.Components;
|
||||
using Content.Shared.Tag;
|
||||
using Content.Shared.Throwing;
|
||||
using Content.Shared.Tools;
|
||||
using Content.Shared.Tools.Systems;
|
||||
using Robust.Server.GameObjects;
|
||||
@@ -89,9 +87,6 @@ public sealed partial class SurgerySystem : EntitySystem
|
||||
SubscribeLocalEvent<OperatedComponent, RejuvenateEvent>(OnRejuvenate);
|
||||
SubscribeLocalEvent<OperatedComponent, StandUpAttemptEvent>(OnStandUpAttempt);
|
||||
SubscribeLocalEvent<OperatedComponent, IsEquippingAttemptEvent>(OnIsEquipping);
|
||||
|
||||
SubscribeLocalEvent<SterileComponent, ExaminedEvent>(OnSterileExamined);
|
||||
SubscribeLocalEvent<SterileComponent, ThrownEvent>(OnThrow);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
@@ -130,24 +125,6 @@ public sealed partial class SurgerySystem : EntitySystem
|
||||
operated.NextRegenerationTick -= frameTime;
|
||||
}
|
||||
}
|
||||
|
||||
var sterileQuery = EntityQueryEnumerator<SterileComponent>();
|
||||
while (sterileQuery.MoveNext(out var uid, out var sterile))
|
||||
{
|
||||
if (sterile.AlwaysSterile)
|
||||
continue;
|
||||
|
||||
if (sterile.NextUpdateTick <= 0)
|
||||
{
|
||||
sterile.NextUpdateTick = 5f;
|
||||
sterile.Amount -= sterile.DecayRate;
|
||||
if (sterile.Amount <= 0)
|
||||
{
|
||||
RemComp<SterileComponent>(uid);
|
||||
}
|
||||
}
|
||||
sterile.NextUpdateTick -= frameTime;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnRejuvenate(Entity<OperatedComponent> ent, ref RejuvenateEvent args)
|
||||
@@ -341,15 +318,6 @@ public sealed partial class SurgerySystem : EntitySystem
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnSterileExamined(Entity<SterileComponent> entity, ref ExaminedEvent args)
|
||||
{
|
||||
if (args.IsInDetailsRange)
|
||||
args.AddMarkup(Loc.GetString("surgery-sterile-examined") + "\n");
|
||||
}
|
||||
|
||||
private void OnThrow(Entity<SterileComponent> entity, ref ThrownEvent args)
|
||||
=> RemCompDeferred<SterileComponent>(entity);
|
||||
|
||||
private bool TryGetOperatingTable(EntityUid patient, out float tableModifier)
|
||||
{
|
||||
tableModifier = 1f;
|
||||
|
||||
@@ -3,13 +3,13 @@ namespace Content.Shared.Surgery.Components;
|
||||
[RegisterComponent]
|
||||
public sealed partial class SterileComponent : Component
|
||||
{
|
||||
[DataField("amount")]
|
||||
public float Amount = 100f;
|
||||
[DataField]
|
||||
public float Amount = 0f;
|
||||
|
||||
[DataField("decayRate")]
|
||||
[DataField]
|
||||
public float DecayRate = 0.5f;
|
||||
|
||||
[DataField("alwaysSterile")]
|
||||
[DataField]
|
||||
public bool AlwaysSterile = false;
|
||||
|
||||
[ViewVariables]
|
||||
|
||||
@@ -10,15 +10,15 @@ public enum SurgeryUiKey
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class SurgeryProcedureDto : BoundUserInterfaceMessage
|
||||
public sealed partial class SurgeryProcedureDtoState : BoundUserInterfaceState
|
||||
{
|
||||
public List<SurgeryGroupDto> Groups;
|
||||
public NetEntity PatientId;
|
||||
public SurgerySterilityInfo SterilityInfo;
|
||||
|
||||
public SurgeryProcedureDto(List<SurgeryGroupDto> groups, NetEntity patientId)
|
||||
public SurgeryProcedureDtoState(List<SurgeryGroupDto> groups, SurgerySterilityInfo sterilityInfo)
|
||||
{
|
||||
Groups = groups;
|
||||
PatientId = patientId;
|
||||
SterilityInfo = sterilityInfo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,17 +69,41 @@ public sealed partial class SurgeryStepDto
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class SurgerySterilityInfo
|
||||
{
|
||||
public float Sterility;
|
||||
public List<string> NegativeFactors;
|
||||
public List<string> PositiveFactors;
|
||||
|
||||
public SurgerySterilityInfo(float sterility, List<string> negativeFactors, List<string> positiveFactors)
|
||||
{
|
||||
Sterility = sterility;
|
||||
NegativeFactors = negativeFactors;
|
||||
PositiveFactors = positiveFactors;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class SurgerySterilityUpdateMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public SurgerySterilityInfo SterilityInfo;
|
||||
|
||||
public SurgerySterilityUpdateMessage(SurgerySterilityInfo sterilityInfo)
|
||||
{
|
||||
SterilityInfo = sterilityInfo;
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed partial class SurgeryStartMessage : BoundUserInterfaceMessage
|
||||
{
|
||||
public NetEntity User;
|
||||
public ProtoId<SurgeryNodePrototype> TargetNode;
|
||||
public int StepIndex;
|
||||
public bool IsParallel;
|
||||
|
||||
public SurgeryStartMessage(NetEntity user, ProtoId<SurgeryNodePrototype> targetNode, int stepIndex, bool isParallel)
|
||||
public SurgeryStartMessage(ProtoId<SurgeryNodePrototype> targetNode, int stepIndex, bool isParallel)
|
||||
{
|
||||
User = user;
|
||||
TargetNode = targetNode;
|
||||
StepIndex = stepIndex;
|
||||
IsParallel = isParallel;
|
||||
|
||||
@@ -2,5 +2,6 @@ guide-entry-vampires = Вампиры
|
||||
guide-entry-blood-cult = Культ крови
|
||||
guide-entry-blood-brothers = Братья по крови
|
||||
guide-entry-genetic = Генетик
|
||||
guide-entry-surgery = Хирургия
|
||||
guide-entry-mindchat = Чат разума
|
||||
guide-entry-veil-cult = Праведник Ратвара
|
||||
guide-entry-veil-cult = Праведник Ратвара
|
||||
|
||||
@@ -6,6 +6,49 @@ surgery-parallel = (П)
|
||||
surgery-tool-required = Требуется инструмент: {$tool}
|
||||
surgery-condition-required = Требуется часть тела: {$condition}
|
||||
|
||||
surgery-sterility-percent = Стерильность: {$percent}%
|
||||
surgery-sterility-negative-header = Факторы риска:
|
||||
surgery-sterility-positive-header = Положительные факторы:
|
||||
|
||||
# Negative
|
||||
surgery-sterility-no-gloves = Нет перчаток
|
||||
surgery-sterility-no-mask = Нет маски
|
||||
surgery-sterility-no-head = Нет головного убора
|
||||
surgery-sterility-no-jumpsuit = Нет комбинезона
|
||||
surgery-sterility-no-outerClothing = Нет верхней одежды
|
||||
surgery-sterility-no-back = Рюкзак создаёт помехи
|
||||
surgery-sterility-no-belt = Пояс мешает
|
||||
surgery-sterility-non-sterile-gloves = Нестерильные перчатки
|
||||
surgery-sterility-non-sterile-mask = Нестерильная маска
|
||||
surgery-sterility-non-sterile-head = Нестерильная шапочка
|
||||
surgery-sterility-non-sterile-jumpsuit = Нестерильный комбинезон
|
||||
surgery-sterility-non-sterile-outerClothing = Нестерильный халат
|
||||
surgery-sterility-non-sterile-back = Грязный рюкзак
|
||||
surgery-sterility-non-sterile-belt = Грязный пояс
|
||||
surgery-sterility-no-sterile-gloves = Нет стерильных перчаток
|
||||
surgery-sterility-no-sterile-mask = Нет стерильной маски
|
||||
surgery-sterility-no-sterile-head = Нет стерильной шапочки
|
||||
surgery-sterility-no-sterile-jumpsuit = Нет стерильного комбинезона
|
||||
surgery-sterility-no-sterile-outerClothing = Нет стерильного халата
|
||||
surgery-sterility-no-sterile-back = Нет стерильного рюкзака
|
||||
surgery-sterility-no-sterile-belt = Нет стерильного пояса
|
||||
surgery-sterility-non-sterile-tool = Нестерильный инструмент
|
||||
surgery-sterility-mask-off = Маска опущена
|
||||
surgery-sterility-garbage = Мусор рядом ({$count})
|
||||
surgery-sterility-bystanders = Зеваки ({$count})
|
||||
surgery-sterility-corpses = Трупы рядом ({$count})
|
||||
|
||||
# Positive
|
||||
surgery-sterility-sterile-gloves = Стерильные перчатки
|
||||
surgery-sterility-sterile-mask = Стерильная маска
|
||||
surgery-sterility-sterile-head = Стерильная шапочка
|
||||
surgery-sterility-sterile-jumpsuit = Стерильный комбинезон
|
||||
surgery-sterility-sterile-outerClothing = Стерильный халат
|
||||
surgery-sterility-sterile-back = Стерильный рюкзак
|
||||
surgery-sterility-sterile-belt = Стерильный пояс
|
||||
surgery-sterility-sterile-tool = Стерильный инструмент
|
||||
surgery-sterility-operating-table = Операционный стол
|
||||
|
||||
surgery-condition-required-head = голова
|
||||
surgery-condition-required-tooth = зубы
|
||||
surgery-condition-required-torso = тело
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
- Cloning
|
||||
- Cryogenics
|
||||
- Genetic # Corvax-Wega-Genetics
|
||||
- Surgery # Corvax-Wega-Surgery
|
||||
|
||||
- type: guideEntry
|
||||
id: MedicalDoctor
|
||||
|
||||
@@ -2,3 +2,8 @@
|
||||
id: Genetic
|
||||
name: guide-entry-genetic
|
||||
text: "/ServerInfo/_Wega/Guidebook/Medical/Genetic.xml"
|
||||
|
||||
- type: guideEntry
|
||||
id: Surgery
|
||||
name: guide-entry-surgery
|
||||
text: "/ServerInfo/_Wega/Guidebook/Medical/Surgery.xml"
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<Document>
|
||||
|
||||
# Хирургия
|
||||
|
||||
<Box>
|
||||
[color=#999999][italic]"Остались лишь ручки... Да ножки..."[/italic][/color]
|
||||
</Box>
|
||||
|
||||
<Box>
|
||||
<GuideEntityEmbed Entity="computerBodyScanner" Caption="сканер тела"/>
|
||||
<GuideEntityEmbed Entity="OperatingTable" Caption="операционный стол"/>
|
||||
</Box>
|
||||
|
||||
## Начало работы
|
||||
|
||||
На большинстве станций в начале есть доступ к хирургическим наборам и начальному инструментарию. В случае отсутствия необходимого инструменты можно заказать в карго или изучить в РНД.
|
||||
|
||||
<Box>
|
||||
<GuideEntityEmbed Entity="Scalpel" Caption="скальпель"/>
|
||||
<GuideEntityEmbed Entity="Hemostat" Caption="гемостат"/>
|
||||
<GuideEntityEmbed Entity="Retractor" Caption="ретрактор"/>
|
||||
<GuideEntityEmbed Entity="BoneSetter" Caption="костоправ"/>
|
||||
<GuideEntityEmbed Entity="BoneGel" Caption="костный гель"/>
|
||||
</Box>
|
||||
<Box>
|
||||
<GuideEntityEmbed Entity="Cautery" Caption="прижигатель"/>
|
||||
<GuideEntityEmbed Entity="FixOVein" Caption="фиксатор вен"/>
|
||||
<GuideEntityEmbed Entity="Saw" Caption="пила"/>
|
||||
<GuideEntityEmbed Entity="Drill" Caption="дрель"/>
|
||||
</Box>
|
||||
|
||||
## Профилактика инфекций
|
||||
|
||||
Процесс проведения операций сильно связан со стерильностью операционной — её отсутствие может негативно сказываться на пациенте и приводить к осложнениям.
|
||||
|
||||
Чтобы избежать проблем, хирургу необходимо:
|
||||
- Очистить операционную от мусора и трупов.
|
||||
- Стерилизовать инструменты с помощью распылителя с этанолом.
|
||||
- Носить латексные перчатки, стерильную маску и стерильную одежду.
|
||||
- Снять пояса и сумки — они мешают.
|
||||
- Убрать посторонних из операционной (допустимо не более 2 человек помимо хирурга и пациента).
|
||||
|
||||
Несоблюдение этих правил может привести к заражению, боли и замедлению пациента. В случае заражения обратитесь к вирусологу.
|
||||
|
||||
## Стерилизация инструментов
|
||||
|
||||
Используйте распылитель с этанолом или алкогольными напитками для стерилизации инструментов. Чистый этанол даёт максимум единиц стерильности за 5 унций.
|
||||
Алкогольные напитки работают хуже — их эффективность напрямую зависит от содержания этанола.
|
||||
|
||||
Стерильность инструмента со временем уменьшается. [bold]Стерильный инструмент снижает риск осложнений при операции.[/bold]
|
||||
|
||||
## Гетто-хирургия
|
||||
|
||||
Хирургическое вмешательство может быть выполнено с помощью подручных инструментов, если соответствующие инструменты недоступны. Это снижает эффективность и может нанести сопутствующий ущерб.
|
||||
|
||||
- Скальпель → ножи, заточки, осколки стекла, кусачки.
|
||||
- Гемостат → провода, монтировка.
|
||||
- Ретрактор → монтировка.
|
||||
- Костный гель → отвёртка.
|
||||
- Костоправ → гаечный ключ.
|
||||
- Прижигатель → сигареты, зажигалки, сварочный аппарат.
|
||||
- Фиксатор вен → провода.
|
||||
- Пила → топорик, пожарный топор.
|
||||
- Дрель → электродрель, ручка, кирка/бур, отвёртка.
|
||||
|
||||
## Подготовка к операции
|
||||
|
||||
Перед началом операции осмотрите пациента с помощью продвинутого сканера тела. Пациент должен быть максимально здоров — многие операции могут стать для него последними.
|
||||
|
||||
Рекомендуется проводить анестезию (баллон с оксидом азота). Отказ от анестезии увеличивает боль...
|
||||
|
||||
## Операции
|
||||
|
||||
Любая операция представлена в меню хирургических манипуляций. Доступные процедуры включают:
|
||||
- Ампутацию/пришивание конечностей.
|
||||
- Пересадку органов.
|
||||
- Имплантацию предметов и имплантов.
|
||||
- Лечение внутренних повреждений.
|
||||
|
||||
## Имплантация
|
||||
|
||||
Современные технологии позволяют вживлять в тело пациента различные объекты:
|
||||
- [bold]Торс[/bold] — до 3 единиц.
|
||||
- [bold]Голова[/bold] — 1 крошечный предмет.
|
||||
- [bold]Зубные импланты[/bold] — таблетки мгновенно усваиваются при активации.
|
||||
|
||||
## Синтетики
|
||||
|
||||
Для ремонта синтетических существ используется специальный набор инструментов: отвёртка, кусачки, сварочный аппарат, гаечный ключ, монтировка, провода.
|
||||
Гетто-инструменты также работают, но менее эффективно.
|
||||
|
||||
</Document>
|
||||
Reference in New Issue
Block a user