Обновление хирургии (#1375)

Co-authored-by: Rinary <rinary.super@gmail.com>
This commit is contained in:
iertis 2025-07-08 14:14:23 +05:00 committed by GitHub
parent afdf832c07
commit b3542477e1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
97 changed files with 2490 additions and 428 deletions

View file

@ -5,6 +5,7 @@ using Content.Shared.Chemistry.Reagent;
using Content.Shared.Clothing;
using Content.Shared.Clothing.Components;
using Content.Shared.Hands;
using Content.Shared.Hands.Components;
using Content.Shared.Item;
using Content.Shared.Rounding;
using Robust.Client.GameObjects;
@ -166,7 +167,17 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
var layer = new PrototypeLayerData();
var heldPrefix = item.HeldPrefix == null ? "inhand-" : $"{item.HeldPrefix}-inhand-";
var key = heldPrefix + args.Location.ToString().ToLowerInvariant() + component.InHandsFillBaseName + closestFillSprite;
// Sunrise-start
var locationString = args.Location switch
{
HandLocation.Left => "left",
HandLocation.Right => "right",
_ => "left" // bruh? Maybe add a middle hand for something, so this has some logic
};
var key = $"{heldPrefix}{locationString}{component.InHandsFillBaseName}{closestFillSprite}";
// Sunrise-end
layer.State = key;

View file

@ -0,0 +1,125 @@
using Content.Shared.Interaction.Events;
using Content.Shared.Interaction;
using Content.Shared.Item;
using Content.Shared.Item.ItemToggle.Components;
using Content.Shared.Toggleable;
using Content.Shared.Verbs;
using Robust.Client.GameObjects;
using Content.Shared._Sunrise.Medical.Surgery;
using Content.Shared.Humanoid;
using System;
using System.Numerics;
using Robust.Client.Graphics;
using Content.Shared.DisplacementMap;
using Content.Client.DisplacementMap;
using System.Reflection;
using Robust.Shared.Graphics.RSI;
using Robust.Shared.Utility;
using Content.Client.Clothing;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using System.Linq;
namespace Content.Client._Sunrise.Medical.Surgery;
public sealed class CustomLimbVisualizerSystem : EntitySystem
{
[Dependency] private readonly DisplacementMapSystem _displacement = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CustomLimbVisualizerComponent, AfterAutoHandleStateEvent>(OnChanged);
}
private void OnChanged(Entity<CustomLimbVisualizerComponent> ent, ref AfterAutoHandleStateEvent _) => OnChanged(ent);
private void OnChanged(Entity<CustomLimbVisualizerComponent> ent, bool repeat = true)
{
if (!TryComp<SpriteComponent>(ent.Owner, out var sprite))
return;
var old = ent.Comp.CachedLayers.ToHashSet();
ent.Comp.CachedLayers.Clear();
foreach (var item in ent.Comp.Layers)
{
if (!item.Value.HasValue || !TryComp<SpriteComponent>(GetEntity(item.Value), out var layerSprite))
{
if (repeat) Timer.Spawn(TimeSpan.FromMilliseconds(150), () => OnChanged(ent, false));
return;
}
string? state = null;
if (TryComp<ItemComponent>(GetEntity(item.Value), out var itemComp) && itemComp.HeldPrefix is not null)
state = $"{itemComp.HeldPrefix}-";
var offset = Vector2.Zero;
switch (item.Key)
{
case HumanoidVisualLayers.LArm:
case HumanoidVisualLayers.LHand:
case HumanoidVisualLayers.LLeg:
case HumanoidVisualLayers.LFoot:
state += "inhand-left";
break;
case HumanoidVisualLayers.RArm:
case HumanoidVisualLayers.RHand:
case HumanoidVisualLayers.RLeg:
case HumanoidVisualLayers.RFoot:
state += "inhand-right";
break;
}
if (state is null) continue;
switch (item.Key)
{
case HumanoidVisualLayers.LArm:
offset = new Vector2(0, 0.1875f);
break;
case HumanoidVisualLayers.LHand:
offset = new Vector2(0, 0.09375f);
break;
case HumanoidVisualLayers.LLeg:
offset = new Vector2(0, -0.15625f);
break;
case HumanoidVisualLayers.LFoot:
offset = new Vector2(0, -0.34375f);
break;
case HumanoidVisualLayers.RArm:
offset = new Vector2(0, 0.1875f);
break;
case HumanoidVisualLayers.RHand:
offset = new Vector2(0, 0.09375f);
break;
case HumanoidVisualLayers.RLeg:
offset = new Vector2(0, -0.15625f);
break;
case HumanoidVisualLayers.RFoot:
offset = new Vector2(0, -0.34375f);
break;
}
if (layerSprite?.BaseRSI?.TryGetState(state, out var rsiState) ?? false)
{
var index = sprite.LayerMapReserveBlank($"custom-{item.Key}");
sprite.LayerSetState(index, rsiState.StateId, layerSprite.BaseRSI);
sprite.LayerSetOffset(index, offset);
sprite.LayerSetVisible(index, true);
ent.Comp.CachedLayers.Add(item.Key);
}
//if (ent.Comp.Displacements.TryGetValue(item.Key, out var displacementData) && !ent.Comp.CachedLayers.Contains($"{item.Key}-displacement"))
//{
// sprite.LayerMapSet(item.Key.ToString(), (int)item.Key);
// _displacement.TryAddDisplacement(displacementData, sprite, (int)item.Key, item.Key.ToString(), ent.Comp.CachedLayers);
//}
}
foreach (var layer in old)
if (!ent.Comp.CachedLayers.Contains(layer))
{
var index = sprite.LayerMapReserveBlank($"custom-{layer}");
sprite.LayerSetVisible(layer, false);
}
}
}

View file

@ -1,14 +1,17 @@
using Content.Client._Sunrise.Choice;
using Content.Client.Administration.UI.CustomControls;
using Content.Client.Administration.UI.CustomControls;
using Content.Client.Hands.Systems;
using Content.Client._Sunrise.Choice;
using Content.Shared._Sunrise.Medical.Surgery;
using Content.Shared.Body.Part;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using static Robust.Client.UserInterface.Control;
using Content.Shared.Timing;
using Robust.Shared.Timing;
namespace Content.Client._Sunrise.Medical.Surgery;
// Based on the RMC14 build.
@ -19,7 +22,7 @@ public sealed class SurgeryBui : BoundUserInterface
{
[Dependency] private readonly IEntityManager _entities = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IGameTiming _game = default!;
private readonly SurgerySystem _system;
private readonly HandsSystem _hands;
@ -38,16 +41,17 @@ public sealed class SurgeryBui : BoundUserInterface
_system.OnRefresh += UpdateDisabledPanel;
_hands.OnPlayerItemAdded += OnPlayerItemAdded;
}
private DateTime _lastRefresh = DateTime.UtcNow;
private (string k1, EntityUid k2) _throttling = ("", new EntityUid());
private void OnPlayerItemAdded(string k1, EntityUid k2)
{
if (_throttling.k1.Equals(k1) && _throttling.k2.Equals(k2) && DateTime.UtcNow - _lastRefresh < TimeSpan.FromSeconds(1)) return;
_throttling = (k1, k2);
_lastRefresh = DateTime.UtcNow;
if (!_game.IsFirstTimePredicted) return;
RefreshUI();
}
protected override void Open() => UpdateState(State);
protected override void Open()
{
base.Open();
UpdateState(State);
}
protected override void UpdateState(BoundUserInterfaceState? state)
{
if (state is SurgeryBuiState s)
@ -215,24 +219,30 @@ public sealed class SurgeryBui : BoundUserInterface
_window.Steps.DisposeAllChildren();
if (surgery.Comp.Requirement is { } requirementId && _system.GetSingleton(requirementId) is { } requirement)
if (surgery.Comp.Requirement is { } requirementIds)
{
var label = new ChoiceControl();
label.Button.OnPressed += _ =>
foreach (var requirementId in requirementIds)
{
_previousSurgeries.Add(surgeryId);
if (_system.GetSingleton(requirementId) is { } requirement && _entities.TryGetComponent(_part, out BodyPartComponent? partComp) && partComp.Body is { } Body && _part is { } Part && _system.IsSurgeryValid(Body, Part, requirementId, surgeryId, out _, out _, out _))
{
var label = new ChoiceControl();
label.Button.OnPressed += _ =>
{
_previousSurgeries.Add(surgeryId);
if (_entities.TryGetComponent(requirement, out SurgeryComponent? requirementComp))
OnSurgeryPressed((requirement, requirementComp), netPart, requirementId);
};
if (_entities.TryGetComponent(requirement, out SurgeryComponent? requirementComp))
OnSurgeryPressed((requirement, requirementComp), netPart, requirementId);
};
var msg = new FormattedMessage();
var surgeryName = _entities.GetComponent<MetaDataComponent>(requirement).EntityName;
msg.AddMarkupOrThrow(Loc.GetString("surgery-window-reguires", ("surgeryname", surgeryName)));
label.Set(msg, null);
var msg = new FormattedMessage();
var surgeryName = _entities.GetComponent<MetaDataComponent>(requirement).EntityName;
msg.AddMarkupOrThrow(Loc.GetString("surgery-window-reguires", ("surgeryname", surgeryName)));
label.Set(msg, null);
_window.Steps.AddChild(label);
_window.Steps.AddChild(new HSeparator(Color.FromHex("#4972A1")) { Margin = new Thickness(0, 0, 0, 1) });
_window.Steps.AddChild(label);
_window.Steps.AddChild(new HSeparator(Color.FromHex("#4972A1")) { Margin = new Thickness(0, 0, 0, 1) });
}
}
}
foreach (var stepId in surgery.Comp.Steps)
@ -354,6 +364,12 @@ public sealed class SurgeryBui : BoundUserInterface
case StepInvalidReason.MissingTool:
stepName.AddMarkupOrThrow(Loc.GetString("surgery-window-reguires-tool"));
break;
case StepInvalidReason.DisabledTool:
stepName.AddMarkupOrThrow(Loc.GetString("surgery-window-reguires-enable"));
break;
case StepInvalidReason.TooHigh:
stepName.AddMarkupOrThrow(Loc.GetString("surgery-window-too-high"));
break;
}
}
}
@ -369,22 +385,21 @@ public sealed class SurgeryBui : BoundUserInterface
if (_window == null)
return;
if (_system.IsLyingDown(Owner))
{
_window.DisabledPanel.Visible = false;
_window.DisabledPanel.MouseFilter = MouseFilterMode.Ignore;
return;
}
_window.DisabledPanel.Visible = false;
_window.DisabledPanel.MouseFilter = MouseFilterMode.Ignore;
return;
_window.DisabledPanel.Visible = true;
if (_window.DisabledLabel.GetMessage() is null)
if (!_system.IsLyingDown(Owner))
{
var text = new FormattedMessage();
text.AddMarkupOrThrow(Loc.GetString("surgery-window-reguires-laydown"));
_window.DisabledLabel.SetMessage(text);
_window.DisabledPanel.Visible = true;
if (_window.DisabledLabel.GetMessage() is null)
{
var text = new FormattedMessage();
text.AddMarkupOrThrow(Loc.GetString("surgery-window-reguires-laydown"));
_window.DisabledLabel.SetMessage(text);
}
_window.DisabledPanel.MouseFilter = MouseFilterMode.Stop;
}
_window.DisabledPanel.MouseFilter = MouseFilterMode.Stop;
}
private void View(ViewType type)
@ -437,7 +452,6 @@ public sealed class SurgeryBui : BoundUserInterface
if (disposing)
_window?.Dispose();
_system.OnRefresh -= UpdateDisabledPanel;
_hands.OnPlayerItemAdded -= OnPlayerItemAdded;
}
}

View file

@ -1,6 +1,6 @@
using Content.Client._Sunrise.Choice;
using Robust.Client.AutoGenerated;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
using Content.Client._Sunrise.Choice;
namespace Content.Client._Sunrise.Medical.Surgery;
// Based on the RMC14.

View file

@ -0,0 +1,156 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Content.Server.Body.Systems;
using Content.Server.Hands.Systems;
using Content.Server._Sunrise.Medical.Surgery;
using Content.Shared._Sunrise.Medical.Damage;
using Content.Shared.Body.Components;
using Content.Shared.Body.Part;
using Content.Shared.Damage;
using Content.Shared.Hands.Components;
using Content.Shared.Humanoid;
using Content.Shared.Interaction;
using Content.Shared.Interaction.Components;
using Content.Shared.NukeOps;
using Content.Shared.Random.Helpers;
using Content.Shared._Sunrise.Medical.Surgery;
using Content.Shared._Sunrise.Medical.Surgery.Effects.Step;
using Robust.Server.Containers;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Server._Sunrise.Medical;
public sealed class LimbDamageSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _rand = default!;
[Dependency] private readonly BodySystem _body = default!;
[Dependency] private readonly ContainerSystem _containers = default!;
[Dependency] private readonly HandsSystem _hands = default!;
public override void Initialize()
{
SubscribeLocalEvent<BodyComponent, DamageBeforeApplyEvent>(OnDamage);
}
//duct tape solution
private void OnDamage(Entity<BodyComponent> ent, ref DamageBeforeApplyEvent args)
{
if (HasComp<NukeOperativeComponent>(ent)) return; // Nuke Ops are immune to limb damage. Temporary solution. mb Deathsquad?
if (!TryComp<HumanoidAppearanceComponent>(ent, out var appr) || appr.Species == "SlimePerson") return; // SlimePeople are immune to limb damage.
var chance = 0f;
foreach (var damage in args.Damage.DamageDict.Where(x => x.Value > 0))
switch (damage.Key)
{
case "Blunt":
chance += 0.00005f * damage.Value.Float();
break;
case "Slash":
chance += 0.0005f * damage.Value.Float();
break;
case "Piercing":
chance += 0.0001f * damage.Value.Float();
break;
case "Heat":
chance += 0.0002f * damage.Value.Float();
break;
case "Cold":
chance += 0.0004f * damage.Value.Float();
break;
case "Caustic":
chance += 0.001f * damage.Value.Float();
break;
default:
break;
}
chance = Math.Clamp(chance, 0, 1);
if (_rand.Prob(chance))
{
if (!TryRemoveLimb(ent, out var part)) return;
Dirty(ent);
QueueDel(part.Value.Owner);
args.Cancelled = true;
}
}
private bool TryRemoveLimb(Entity<BodyComponent> ent, [NotNullWhen(true)] out Entity<BodyPartComponent>? part)
{
part = null;
var root = _body.GetRootPartOrNull(ent.Owner);
if (root is null) return false;
var parts = _body.GetAllBodyPart(root.Value.Entity, root.Value.BodyPart)
.Where(p => p.Comp.PartType != BodyPartType.Head)
.ToList();
if (parts.Count == 0) return false;
part = _rand.Pick(parts);
var parentPartAndSlot = _body.GetParentPartAndSlotOrNull(part.Value.Owner);
if (parentPartAndSlot is null) return false;
var (_, slotId) = parentPartAndSlot.Value;
if (!_containers.TryGetContainingContainer((part.Value.Owner, null, null), out var container)) return false;
if (!_containers.Remove(part.Value.Owner, container)) return false;
if (TryComp<CustomLimbComponent>(part.Value.Owner, out var virtualLimb)
&& virtualLimb.Item.HasValue)
{
RemoveItemHand(ent.Owner, virtualLimb.Item.Value, BodySystem.GetPartSlotContainerId(slotId));
var vizualizer = EnsureComp<CustomLimbVisualizerComponent>(ent.Owner);
var layer = SurgerySystem.GetLayer(slotId);
if (layer is not null)
{
vizualizer.Layers.Remove(layer.Value);
Dirty(ent.Owner, vizualizer);
}
}
else
{
switch (part.Value.Comp.PartType)
{
case BodyPartType.Arm: //todo move to systems
foreach (var limbSlotId in part.Value.Comp.Children.Keys)
{
if (limbSlotId is null) continue;
var child = _containers.GetContainer(part.Value.Owner, BodySystem.GetPartSlotContainerId(limbSlotId));
foreach (var containedEnt in child.ContainedEntities)
{
if (TryComp(containedEnt, out BodyPartComponent? innerPart)
&& innerPart.PartType == BodyPartType.Hand)
_hands.RemoveHand(ent.Owner, BodySystem.GetPartSlotContainerId(limbSlotId));
}
}
break;
case BodyPartType.Hand:
var parentSlot = _body.GetParentPartAndSlotOrNull(part.Value.Owner);
if (parentSlot is not null)
_hands.RemoveHand(ent.Owner, BodySystem.GetPartSlotContainerId(parentSlot.Value.Slot));
break;
case BodyPartType.Leg:
case BodyPartType.Foot:
break;
}
}
return true;
}
private void RemoveItemHand(EntityUid bodyId, EntityUid itemId, string handId)
{
if (!TryComp<HandsComponent>(bodyId, out var hands)
|| !_hands.TryGetHand(bodyId, handId, out var hand, hands))
return;
if (!itemId.IsValid())
{
Log.Debug("no valid item");
return;
}
RemComp<UnremoveableComponent>(itemId);
_hands.DoDrop(itemId, hand);
_hands.RemoveHand(bodyId, handId, hands);
}
}

View file

@ -0,0 +1,147 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Content.Server.Humanoid;
using Content.Shared.Damage;
using Content.Shared.Eye.Blinding.Components;
using Content.Shared.Eye.Blinding.Systems;
using Content.Shared.Interaction;
using Content.Shared.Speech.Muting;
using Content.Shared._Sunrise.Antags.Abductor;
using Content.Shared._Sunrise.Medical.Surgery.Effects.Step;
using Content.Shared._Sunrise.Medical.Surgery.Events;
using Content.Shared._Sunrise.Medical.Surgery.Steps.Parts;
using Content.Shared._Sunrise.VentCraw;
using Robust.Shared.Prototypes;
namespace Content.Server._Sunrise.Medical.Surgery;
public sealed partial class OrganSystem : EntitySystem
{
[Dependency] private readonly BlindableSystem _blindable = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly IComponentFactory _compFactory = default!;
[Dependency] private readonly HumanoidAppearanceSystem _humanoidAppearanceSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<FunctionalOrganComponent, SurgeryOrganImplantationCompleted>(OnFunctionalOrganImplanted);
SubscribeLocalEvent<FunctionalOrganComponent, SurgeryOrganExtracted>(OnFunctionalOrganExtracted);
SubscribeLocalEvent<OrganEyesComponent, SurgeryOrganImplantationCompleted>(OnEyeImplanted);
SubscribeLocalEvent<OrganEyesComponent, SurgeryOrganExtracted>(OnEyeExtracted);
SubscribeLocalEvent<OrganTongueComponent, SurgeryOrganImplantationCompleted>(OnTongueImplanted);
SubscribeLocalEvent<OrganTongueComponent, SurgeryOrganExtracted>(OnTongueExtracted);
SubscribeLocalEvent<AbductorOrganComponent, SurgeryOrganImplantationCompleted>(OnAbductorOrganImplanted);
SubscribeLocalEvent<AbductorOrganComponent, SurgeryOrganExtracted>(OnAbductorOrganExtracted);
SubscribeLocalEvent<DamageableComponent, SurgeryOrganImplantationCompleted>(OnOrganImplanted);
SubscribeLocalEvent<DamageableComponent, SurgeryOrganExtracted>(OnOrganExtracted);
SubscribeLocalEvent<OrganVisualizationComponent, SurgeryOrganImplantationCompleted>(OnVisualizationImplanted);
SubscribeLocalEvent<OrganVisualizationComponent, SurgeryOrganExtracted>(OnVisualizationExtracted);
}
//
private void OnFunctionalOrganImplanted(Entity<FunctionalOrganComponent> ent, ref SurgeryOrganImplantationCompleted args)
{
foreach (var comp in (ent.Comp.Components ?? []).Values)
if (!EntityManager.HasComponent(args.Body, comp.Component.GetType()))
EntityManager.AddComponent(args.Body, _compFactory.GetComponent(comp.Component.GetType()));
}
private void OnFunctionalOrganExtracted(Entity<FunctionalOrganComponent> ent, ref SurgeryOrganExtracted args)
{
foreach (var comp in (ent.Comp.Components ?? []).Values)
if (EntityManager.HasComponent(args.Body, comp.Component.GetType()))
EntityManager.RemoveComponent(args.Body, _compFactory.GetComponent(comp.Component.GetType()));
}
//
private void OnOrganImplanted(Entity<DamageableComponent> ent, ref SurgeryOrganImplantationCompleted args)
{
if (!TryComp<DamageableComponent>(args.Body, out var bodyDamageable)) return;
var change = _damageableSystem.TryChangeDamage(args.Body, ent.Comp.Damage, true, false, bodyDamageable);
if (change is not null)
_damageableSystem.TryChangeDamage(ent.Owner, change.Invert(), true, false, ent.Comp);
}
private void OnOrganExtracted(Entity<DamageableComponent> ent, ref SurgeryOrganExtracted args)
{
if (!TryComp<OrganDamageComponent>(ent.Owner, out var damageRule)
|| damageRule.Damage is null
|| !TryComp<DamageableComponent>(args.Body, out var bodyDamageable)) return;
var change = _damageableSystem.TryChangeDamage(args.Body, damageRule.Damage.Invert(), true, false, bodyDamageable);
if (change is not null)
_damageableSystem.TryChangeDamage(ent.Owner, change.Invert(), true, false, ent.Comp);
}
//
private void OnAbductorOrganImplanted(Entity<AbductorOrganComponent> ent, ref SurgeryOrganImplantationCompleted args)
{
if (TryComp<AbductorVictimComponent>(args.Body, out var victim))
victim.Organ = ent.Comp.Organ;
if (ent.Comp.Organ == AbductorOrganType.Vent)
AddComp<VentCrawlerComponent>(args.Body);
}
private void OnAbductorOrganExtracted(Entity<AbductorOrganComponent> ent, ref SurgeryOrganExtracted args)
{
if (TryComp<AbductorVictimComponent>(args.Body, out var victim))
if (victim.Organ == ent.Comp.Organ)
victim.Organ = AbductorOrganType.None;
if (ent.Comp.Organ == AbductorOrganType.Vent)
RemComp<VentCrawlerComponent>(args.Body);
}
//
private void OnTongueImplanted(Entity<OrganTongueComponent> ent, ref SurgeryOrganImplantationCompleted args)
{
if (HasComp<AbductorComponent>(args.Body) || !ent.Comp.IsMuted) return;
RemComp<MutedComponent>(args.Body);
}
private void OnTongueExtracted(Entity<OrganTongueComponent> ent, ref SurgeryOrganExtracted args)
{
ent.Comp.IsMuted = HasComp<MutedComponent>(args.Body);
AddComp<MutedComponent>(args.Body);
}
//
private void OnEyeExtracted(Entity<OrganEyesComponent> ent, ref SurgeryOrganExtracted args)
{
if (!TryComp<BlindableComponent>(args.Body, out var blindable)) return;
ent.Comp.EyeDamage = blindable.EyeDamage;
ent.Comp.MinDamage = blindable.MinDamage;
_blindable.UpdateIsBlind((args.Body, blindable));
}
private void OnEyeImplanted(Entity<OrganEyesComponent> ent, ref SurgeryOrganImplantationCompleted args)
{
if (!TryComp<BlindableComponent>(args.Body, out var blindable)) return;
_blindable.SetMinDamage((args.Body, blindable), ent.Comp.MinDamage ?? 0);
_blindable.AdjustEyeDamage((args.Body, blindable), (ent.Comp.EyeDamage ?? 0) - blindable.MaxDamage);
}
//
private void OnVisualizationExtracted(Entity<OrganVisualizationComponent> ent, ref SurgeryOrganExtracted args)
=> _humanoidAppearanceSystem.SetLayersVisibility(args.Body, [ent.Comp.Layer], false);
private void OnVisualizationImplanted(Entity<OrganVisualizationComponent> ent, ref SurgeryOrganImplantationCompleted args)
{
_humanoidAppearanceSystem.SetLayersVisibility(args.Body, [ent.Comp.Layer], true);
_humanoidAppearanceSystem.SetBaseLayerId(args.Body, ent.Comp.Layer, ent.Comp.Prototype);
}
}

View file

@ -7,8 +7,8 @@ using Content.Shared._Sunrise.Medical.Surgery.Steps.Parts;
using Content.Shared.Body.Components;
using Content.Shared.Body.Organ;
using Content.Shared.Body.Part;
using Content.Shared.Body.Systems;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.Eye.Blinding.Components;
using Content.Shared.Hands.Components;
using Content.Shared.Speech.Muting;
@ -16,12 +16,21 @@ using Robust.Shared.Prototypes;
using Content.Shared.Humanoid;
using Content.Shared._Sunrise;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Interaction.Components;
using Microsoft.CodeAnalysis;
namespace Content.Server._Sunrise.Medical.Surgery;
// Based on the RMC14.
// https://github.com/RMC-14/RMC-14
//
//This file is already overloaded with responsibilities,
//its time to break its functionality into different systems.
//However, I dont want to touch the official systems, so I need to come up with extensions for them.
public sealed partial class SurgerySystem : SharedSurgerySystem
{
[Dependency] private readonly IComponentFactory _compFactory = default!;
private readonly EntProtoId _virtual = "PartVirtual";
public void InitializeSteps()
{
SubscribeLocalEvent<SurgeryStepBleedEffectComponent, SurgeryStepEvent>(OnStepBleedComplete);
@ -32,16 +41,29 @@ public sealed partial class SurgerySystem : SharedSurgerySystem
SubscribeLocalEvent<SurgeryStepOrganExtractComponent, SurgeryStepEvent>(OnStepOrganExtractComplete);
SubscribeLocalEvent<SurgeryStepOrganInsertComponent, SurgeryStepEvent>(OnStepOrganInsertComplete);
SubscribeLocalEvent<SurgeryStepAttachLimbEffectComponent, SurgeryStepEvent>(OnStepAttachLimbComplete);
SubscribeLocalEvent<SurgeryStepAttachLimbEffectComponent, SurgeryStepEvent>(OnStepAttachComplete);
SubscribeLocalEvent<SurgeryStepAmputationEffectComponent, SurgeryStepEvent>(OnStepAmputationComplete);
SubscribeLocalEvent<CustomLimbMarkerComponent, ComponentRemove>(CustomLimbRemoved);
SubscribeLocalEvent<SurgeryRemoveAccentComponent, SurgeryStepEvent>(OnRemoveAccent);
}
private void OnStepAttachComplete(Entity<SurgeryStepAttachLimbEffectComponent> ent, ref SurgeryStepEvent args)
{
if (GetSingleton(args.SurgeryProto) is not { } surgery
|| !TryComp<SurgeryLimbSlotConditionComponent>(surgery, out var slotComp))
return;
OnStepAttachLimbComplete(ent, slotComp.Slot, ref args);
if (slotComp.Slot != "head")
OnStepAttachItemComplete(ent, slotComp.Slot, ref args);
}
private void OnStepBleedComplete(Entity<SurgeryStepBleedEffectComponent> ent, ref SurgeryStepEvent args)
{
if(ent.Comp.Damage is not null && TryComp<DamageableComponent>(args.Body, out var comp))
if (ent.Comp.Damage is not null && TryComp<DamageableComponent>(args.Body, out var comp))
_damageableSystem.TryChangeDamage(args.Body, ent.Comp.Damage);
//todo add wound
}
@ -54,8 +76,18 @@ public sealed partial class SurgerySystem : SharedSurgerySystem
{
if (args.Tools.Count == 0
|| !(args.Tools.FirstOrDefault() is var organId)
|| !TryComp<BodyPartComponent>(args.Part, out var bodyPart)
|| !TryComp<OrganComponent>(organId, out var organComp))
|| !TryComp<BodyPartComponent>(args.Part, out var bodyPart))
return;
var containerId = SharedBodySystem.GetOrganContainerId(ent.Comp.Slot);
if (ent.Comp.Slot == "cavity" && _containers.TryGetContainer(args.Part, containerId, out var container))
{
_containers.Insert(organId, container);
return;
}
if (!TryComp<OrganComponent>(organId, out var organComp))
return;
var part = args.Part;
@ -63,59 +95,36 @@ public sealed partial class SurgerySystem : SharedSurgerySystem
_delayAccumulator = 0;
_delayQueue.Enqueue(() =>
{
if (_body.InsertOrgan(part, organId, ent.Comp.Slot, bodyPart, organComp)
&& TryComp<DamageableComponent>(organId, out var organDamageable)
&& TryComp<DamageableComponent>(body, out var bodyDamageable))
{
if (TryComp<OrganEyesComponent>(organId, out var organEyes)
&& TryComp<BlindableComponent>(body, out var blindable))
{
_blindable.SetMinDamage((body, blindable), organEyes.MinDamage ?? 0);
_blindable.AdjustEyeDamage((body, blindable), (organEyes.EyeDamage ?? 0) - blindable.MaxDamage);
}
if (TryComp<OrganTongueComponent>(organId, out var organTongue)
&& !organTongue.IsMuted)
RemComp<MutedComponent>(body);
if (!_body.InsertOrgan(part, organId, ent.Comp.Slot, bodyPart, organComp)) return;
var change = _damageableSystem.TryChangeDamage(body, organDamageable.Damage, true, false, bodyDamageable);
if (change is not null)
_damageableSystem.TryChangeDamage(organId, change.Invert(), true, false, organDamageable);
}
var ev = new SurgeryOrganImplantationCompleted(body, part, organId);
RaiseLocalEvent(organId, ref ev);
});
}
private void OnStepOrganExtractComplete(Entity<SurgeryStepOrganExtractComponent> ent, ref SurgeryStepEvent args)
{
if (ent.Comp.Organ?.Count != 1) return;
var organs = _body.GetPartOrgans(args.Part, Comp<BodyPartComponent>(args.Part));
var type = ent.Comp.Organ.Values.First().Component.GetType();
if (ent.Comp.Slot != null && _containers.TryGetContainer(args.Part, SharedBodySystem.GetOrganContainerId(ent.Comp.Slot), out var container))
{
foreach (var containedEnt in container.ContainedEntities)
if (HasComp(containedEnt, type))
_containers.Remove(containedEnt, container);
return;
}
var organs = _body.GetPartOrgans(args.Part, Comp<BodyPartComponent>(args.Part));
foreach (var organ in organs)
{
if (HasComp(organ.Id, type))
{
if (_body.RemoveOrgan(organ.Id, organ.Component)
&& TryComp<OrganDamageComponent>(organ.Id, out var damageRule)
&& damageRule.Damage is not null
&& TryComp<DamageableComponent>(organ.Id, out var organDamageable)
&& TryComp<DamageableComponent>(args.Body, out var bodyDamageable))
{
if (TryComp<OrganEyesComponent>(organ.Id, out var organEyes)
&& TryComp<BlindableComponent>(args.Body, out var blindable))
{
organEyes.EyeDamage = blindable.EyeDamage;
organEyes.MinDamage = blindable.MinDamage;
_blindable.UpdateIsBlind((args.Body, blindable));
}
if (TryComp<OrganTongueComponent>(organ.Id, out var organTongue))
{
organTongue.IsMuted = HasComp<MutedComponent>(args.Body);
AddComp<MutedComponent>(args.Body);
}
var change = _damageableSystem.TryChangeDamage(args.Body, damageRule.Damage.Invert(), true, false, bodyDamageable);
if (change is not null)
_damageableSystem.TryChangeDamage(organ.Id, change.Invert(), true, false, organDamageable);
}
return;
}
if (!HasComp(organ.Id, type) || !_body.RemoveOrgan(organ.Id, organ.Component)) continue;
var ev = new SurgeryOrganExtracted(args.Body, args.Part, organ.Id);
RaiseLocalEvent(organ.Id, ref ev);
return;
}
}
@ -133,7 +142,7 @@ public sealed partial class SurgerySystem : SharedSurgerySystem
if (TryComp(args.Body, out TransformComponent? xform))
SpawnAtPosition(ent.Comp.Entity, xform.Coordinates);
}
private void OnStepAttachLimbComplete(Entity<SurgeryStepAttachLimbEffectComponent> ent, ref SurgeryStepEvent args)
private void OnStepAttachLimbComplete(Entity<SurgeryStepAttachLimbEffectComponent> _, string slot, ref SurgeryStepEvent args)
{
if (args.Tools.Count == 0
|| !(args.Tools.FirstOrDefault() is var limbId)
@ -144,61 +153,130 @@ public sealed partial class SurgerySystem : SharedSurgerySystem
var part = args.Part;
var body = args.Body;
_delayAccumulator = 0;
_delayQueue.Enqueue(() =>
if (!_body.AttachPart(part, slot, limbId, bodyPart, limb))
{
var slot = "";
foreach (var slotTemp in _body.TryGetFreePartSlots(part, bodyPart))
{
slot = slotTemp;
if (_body.AttachPart(part, slot, limbId, bodyPart, limb))
break;
}
args.IsCancelled = true;
return;
}
if (TryComp<HumanoidAppearanceComponent>(body, out var humanoid)) //todo move to system
if (TryComp<HumanoidAppearanceComponent>(body, out var humanoid)) //todo move to system
{
var limbs = _body.GetBodyPartAdjacentParts(limbId, limb).Except([part]).Concat([limbId]);
foreach (var partLimbId in limbs)
{
var limbs = _body.GetBodyPartAdjacentParts(limbId, limb).Except([part]).Concat([limbId]);
foreach (var partLimbId in limbs)
if (TryComp<BaseLayerIdComponent>(partLimbId, out var baseLayerStorage)
&& TryComp(partLimbId, out BodyPartComponent? partLimb))
{
if (TryComp<BaseLayerIdComponent>(partLimbId, out var baseLayerStorage)
&& TryComp(partLimbId, out BodyPartComponent? partLimb))
{
var layer = partLimb.ToHumanoidLayers();
if (layer is null) continue;
_humanoidAppearanceSystem.SetBaseLayerId(body, layer.Value, baseLayerStorage.Layer, true, humanoid);
}
var layer = partLimb.ToHumanoidLayers();
if (layer is null) continue;
_humanoidAppearanceSystem.SetBaseLayerId(body, layer.Value, baseLayerStorage.Layer, true, humanoid);
}
}
switch (limb.PartType)
{
case BodyPartType.Arm: //todo move to systems
if (limb.Children.Keys.Count == 0)
{
_body.TryCreatePartSlot(limbId, limb.Symmetry == BodyPartSymmetry.Left ? "left hand" : "right hand", BodyPartType.Hand, out var slotId);
}
foreach (var slotId in limb.Children.Keys)
{
if (slotId is null) continue;
var slotFullId = BodySystem.GetPartSlotContainerId(slotId);
var child = _containers.GetContainer(limbId, slotFullId);
}
foreach (var containedEnt in child.ContainedEntities)
{
if (TryComp(containedEnt, out BodyPartComponent? innerPart)
&& innerPart.PartType == BodyPartType.Hand)
_hands.AddHand(body, slotFullId, limb.Symmetry == BodyPartSymmetry.Left ? HandLocation.Left : HandLocation.Right);
}
switch (limb.PartType)
{
case BodyPartType.Arm: //todo move to systems
if (limb.Children.Keys.Count == 0)
_body.TryCreatePartSlot(limbId, limb.Symmetry == BodyPartSymmetry.Left ? "left hand" : "right hand", BodyPartType.Hand, out var slotId);
foreach (var slotId in limb.Children.Keys)
{
if (slotId is null) continue;
var slotFullId = BodySystem.GetPartSlotContainerId(slotId);
var child = _containers.GetContainer(limbId, slotFullId);
foreach (var containedEnt in child.ContainedEntities)
{
if (TryComp(containedEnt, out BodyPartComponent? innerPart)
&& innerPart.PartType == BodyPartType.Hand)
_hands.AddHand(body, slotFullId, limb.Symmetry == BodyPartSymmetry.Left ? HandLocation.Left : HandLocation.Right);
}
break;
case BodyPartType.Hand:
_hands.AddHand(body, BodySystem.GetPartSlotContainerId(slot), limb.Symmetry == BodyPartSymmetry.Left ? HandLocation.Left : HandLocation.Right);
break;
case BodyPartType.Leg:
case BodyPartType.Foot:
break;
}
});
}
break;
case BodyPartType.Hand:
_hands.AddHand(body, BodySystem.GetPartSlotContainerId(slot), limb.Symmetry == BodyPartSymmetry.Left ? HandLocation.Left : HandLocation.Right);
break;
case BodyPartType.Leg:
if (limb.Children.Keys.Count == 0)
_body.TryCreatePartSlot(limbId, limb.Symmetry == BodyPartSymmetry.Left ? "left foot" : "right foot", BodyPartType.Foot, out var slotId);
break;
case BodyPartType.Foot:
break;
}
}
private void OnStepAttachItemComplete(Entity<SurgeryStepAttachLimbEffectComponent> ent, string slot, ref SurgeryStepEvent args)
{
if (args.Tools.Count == 0
|| !(args.Tools.FirstOrDefault() is var itemId)
|| !TryComp<BodyPartComponent>(args.Part, out var bodyPart)
|| !TryComp(itemId, out MetaDataComponent? metada)
|| TryComp<BodyPartComponent>(itemId, out var _)
|| Prototype(itemId) is not EntityPrototype prototype)
return;
var marker = EnsureComp<CustomLimbMarkerComponent>(itemId);
var virtualIteam = Spawn(_virtual);
var virtualBodyPart = EnsureComp<BodyPartComponent>(virtualIteam);
var virtualMetadata = EnsureComp<MetaDataComponent>(virtualIteam);
var virtualCustomLimb = EnsureComp<CustomLimbComponent>(virtualIteam);
_metadata.SetEntityName(virtualIteam, metada.EntityName, virtualMetadata);
marker.VirtualPart = virtualIteam;
virtualCustomLimb.Item = itemId;
virtualBodyPart.PartType = slot switch
{
"left arm" => BodyPartType.Arm,
"right arm" => BodyPartType.Arm,
"left hand" => BodyPartType.Hand,
"right hand" => BodyPartType.Hand,
"left leg" => BodyPartType.Leg,
"right leg" => BodyPartType.Leg,
"left foot" => BodyPartType.Foot,
"right foot" => BodyPartType.Foot,
"tail" => BodyPartType.Tail,
_ => BodyPartType.Other,
};
if (!_body.AttachPart(args.Part, slot, virtualIteam, bodyPart, virtualBodyPart))
{
args.IsCancelled = true;
QueueDel(virtualIteam);
return;
}
if (TryComp<HumanoidAppearanceComponent>(args.Body, out var humanoid)) //todo move to system
{
var layer = GetLayer(slot);
if (layer is null)
return;
var vizualizer = EnsureComp<CustomLimbVisualizerComponent>(args.Body);
vizualizer.Layers[layer.Value] = GetNetEntity(itemId);
Dirty(args.Body, vizualizer);
}
AddItemHand(args.Body, itemId, BodySystem.GetPartSlotContainerId(slot));
}
private void AddItemHand(EntityUid bodyId, EntityUid itemId, string handId)
{
if (!TryComp<HandsComponent>(bodyId, out var hands))
return;
if (!itemId.IsValid())
{
Log.Debug("no valid item");
return;
}
_hands.AddHand(bodyId, handId, HandLocation.Middle, hands);
_hands.DoPickup(bodyId, hands.Hands[handId], itemId, hands);
EnsureComp<UnremoveableComponent>(itemId);
}
private void OnStepAmputationComplete(Entity<SurgeryStepAmputationEffectComponent> ent, ref SurgeryStepEvent args)
{
if (TryComp(args.Body, out TransformComponent? xform)
@ -207,55 +285,114 @@ public sealed partial class SurgerySystem : SharedSurgerySystem
{
if (!_containers.TryGetContainingContainer((args.Part, null, null), out var container)) return;
var parentPartAndSlot = _body.GetParentPartAndSlotOrNull(args.Part);
if (parentPartAndSlot is null) return;
var (_, slotId) = parentPartAndSlot.Value;
if (_containers.Remove(args.Part, container, destination: xform.Coordinates))
{
if (TryComp<HumanoidAppearanceComponent>(args.Body, out var humanoid)) //todo move to system
if (TryComp<CustomLimbComponent>(args.Part, out var virtualLimb)
&& virtualLimb.Item.HasValue)
{
var limbs = _body.GetBodyPartAdjacentParts(args.Part, limb).Concat([args.Part]); ;
foreach (var partLimbId in limbs)
RemoveItemHand(args.Body, virtualLimb.Item.Value, BodySystem.GetPartSlotContainerId(slotId));
var vizualizer = EnsureComp<CustomLimbVisualizerComponent>(args.Body);
var layer = GetLayer(slotId);
if (layer is not null)
{
if (TryComp<BaseLayerIdComponent>(partLimbId, out var baseLayerStorage)
&& TryComp(partLimbId, out BodyPartComponent? partLimb))
vizualizer.Layers.Remove(layer.Value);
Dirty(args.Body, vizualizer);
}
QueueDel(args.Part);
}
else
{
if (TryComp<HumanoidAppearanceComponent>(args.Body, out var humanoid)) //todo move to system
{
var limbs = _body.GetBodyPartAdjacentParts(args.Part, limb).Concat([args.Part]); ;
foreach (var partLimbId in limbs)
{
var layer = partLimb.ToHumanoidLayers();
if (layer is null) continue;
if (humanoid.CustomBaseLayers.TryGetValue(layer.Value, out var customBaseLayer))
baseLayerStorage.Layer = customBaseLayer.Id;
else
if (TryComp<BaseLayerIdComponent>(partLimbId, out var baseLayerStorage)
&& TryComp(partLimbId, out BodyPartComponent? partLimb))
{
var bodyType = _prototypes.Index(humanoid.BodyType);
if (bodyType.Sprites.TryGetValue(layer.Value, out var baseLayer))
baseLayerStorage.Layer = baseLayer;
var layer = partLimb.ToHumanoidLayers();
if (layer is null) continue;
if (humanoid.CustomBaseLayers.TryGetValue(layer.Value, out var customBaseLayer))
baseLayerStorage.Layer = customBaseLayer.Id;
else
{
var bodyType = _prototypes.Index(humanoid.BodyType);
if (bodyType.Sprites.TryGetValue(layer.Value, out var baseLayer))
baseLayerStorage.Layer = baseLayer;
}
}
}
}
}
switch (limb.PartType)
{
case BodyPartType.Arm: //todo move to systems
foreach (var slotId in limb.Children.Keys)
{
if (slotId is null) continue;
var child = _containers.GetContainer(args.Part, BodySystem.GetPartSlotContainerId(slotId));
foreach (var containedEnt in child.ContainedEntities)
switch (limb.PartType)
{
case BodyPartType.Arm: //todo move to systems
foreach (var limbSlotId in limb.Children.Keys)
{
if (TryComp(containedEnt, out BodyPartComponent? innerPart)
&& innerPart.PartType == BodyPartType.Hand)
_hands.RemoveHand(args.Body, BodySystem.GetPartSlotContainerId(slotId));
if (limbSlotId is null) continue;
var child = _containers.GetContainer(args.Part, BodySystem.GetPartSlotContainerId(limbSlotId));
foreach (var containedEnt in child.ContainedEntities)
{
if (TryComp(containedEnt, out BodyPartComponent? innerPart)
&& innerPart.PartType == BodyPartType.Hand)
_hands.RemoveHand(args.Body, BodySystem.GetPartSlotContainerId(limbSlotId));
}
}
}
break;
case BodyPartType.Hand:
var parentSlot = _body.GetParentPartAndSlotOrNull(args.Part);
if (parentSlot is not null)
_hands.RemoveHand(args.Body, BodySystem.GetPartSlotContainerId(parentSlot.Value.Slot));
break;
case BodyPartType.Leg:
case BodyPartType.Foot:
break;
break;
case BodyPartType.Hand:
var parentSlot = _body.GetParentPartAndSlotOrNull(args.Part);
if (parentSlot is not null)
_hands.RemoveHand(args.Body, BodySystem.GetPartSlotContainerId(parentSlot.Value.Slot));
break;
case BodyPartType.Leg:
case BodyPartType.Foot:
break;
}
}
}
}
}
private void RemoveItemHand(EntityUid bodyId, EntityUid itemId, string handId)
{
if (!TryComp<HandsComponent>(bodyId, out var hands)
|| !_hands.TryGetHand(bodyId, handId, out var hand, hands))
return;
if (!itemId.IsValid())
{
Log.Debug("no valid item");
return;
}
RemComp<UnremoveableComponent>(itemId);
_hands.DoDrop(itemId, hand);
_hands.RemoveHand(bodyId, handId, hands);
}
private void CustomLimbRemoved(Entity<CustomLimbMarkerComponent> ent, ref ComponentRemove args)
{
if (ent.Comp.VirtualPart is null) return;
QueueDel(ent.Comp.VirtualPart.Value);
}
public static HumanoidVisualLayers? GetLayer(string slotId) => slotId switch
{
"left arm" => HumanoidVisualLayers.LArm,
"right arm" => HumanoidVisualLayers.RArm,
"left hand" => HumanoidVisualLayers.LHand,
"right hand" => HumanoidVisualLayers.RHand,
"left leg" => HumanoidVisualLayers.LLeg,
"right leg" => HumanoidVisualLayers.RLeg,
"left foot" => HumanoidVisualLayers.LFoot,
"right foot" => HumanoidVisualLayers.RFoot,
"tail" => HumanoidVisualLayers.Tail,
_ => null,
};
}

View file

@ -1,8 +1,10 @@
using Content.Server.Body.Systems;
using System.Linq;
using Content.Server.Body.Systems;
using Content.Server.Chat.Systems;
using Content.Server.Hands.Systems;
using Content.Server.Humanoid;
using Content.Server.Popups;
using Content.Shared.Body.Part;
using Content.Shared._Sunrise.Medical.Surgery;
using Content.Shared._Sunrise.Medical.Surgery.Effects.Step;
using Content.Shared._Sunrise.Medical.Surgery.Events;
@ -11,6 +13,7 @@ using Content.Shared.Eye.Blinding.Systems;
using Content.Shared.HealthExaminable;
using Content.Shared.Interaction;
using Content.Shared.Prototypes;
using Content.Shared.Tag;
using Robust.Server.Containers;
using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
@ -31,6 +34,8 @@ public sealed partial class SurgerySystem : SharedSurgerySystem
[Dependency] private readonly UserInterfaceSystem _ui = default!;
[Dependency] private readonly ContainerSystem _containers = default!;
[Dependency] private readonly BlindableSystem _blindable = default!;
[Dependency] private readonly TagSystem _tag = default!;
[Dependency] private readonly MetaDataSystem _metadata = default!;
private readonly List<EntProtoId> _surgeries = [];
public override void Initialize()
@ -61,40 +66,52 @@ public sealed partial class SurgerySystem : SharedSurgerySystem
return;
var surgeries = new Dictionary<NetEntity, List<(EntProtoId, string suffix, bool isCompleted)>>();
foreach (var part in _body.GetBodyChildren(body))
if (HasComp<BodyPartComponent>(body))
{
if (!TryComp<SurgeryProgressComponent>(part.Id, out var progress))
AddSurgeries(body, body, surgeries);
}
else
{
foreach (var part in _body.GetBodyChildren(body))
{
progress = new SurgeryProgressComponent();
AddComp(part.Id, progress);
}
foreach (var surgery in _surgeries)
{
if (GetSingleton(surgery) is not { } surgeryEnt
|| !TryComp(surgeryEnt, out SurgeryComponent? surgeryComp)
|| (surgeryComp.Requirement is not null && !progress.CompletedSurgeries.Contains(surgeryComp.Requirement.Value)))
continue;
var ev = new SurgeryValidEvent(body, part.Id);
var isCompleted = progress.CompletedSurgeries.Contains(surgery);
if (!progress.StartedSurgeries.Contains(surgery)
&& !isCompleted)
{
RaiseLocalEvent(surgeryEnt, ref ev);
if (ev.Cancelled)
continue;
}
surgeries.GetOrNew(GetNetEntity(part.Id)).Add((surgery, ev.Suffix, isCompleted));
AddSurgeries(part.Id, body, surgeries);
}
}
_ui.SetUiState(body, SurgeryUIKey.Key, new SurgeryBuiState() { Choices = surgeries });
}
private void AddSurgeries(EntityUid part, EntityUid body, Dictionary<NetEntity, List<(EntProtoId, string suffix, bool isCompleted)>> surgeries)
{
if (!TryComp<SurgeryProgressComponent>(part, out var progress))
{
progress = new SurgeryProgressComponent();
AddComp(part, progress);
}
foreach (var surgery in _surgeries)
{
if (GetSingleton(surgery) is not { } surgeryEnt
|| !TryComp(surgeryEnt, out SurgeryComponent? surgeryComp)
|| (surgeryComp.Requirement.Count() > 0 && !progress.CompletedSurgeries.Any(x => surgeryComp.Requirement.Contains(x))))
continue;
var ev = new SurgeryValidEvent(body, part);
var isCompleted = progress.CompletedSurgeries.Contains(surgery);
if (!progress.StartedSurgeries.Contains(surgery)
&& !isCompleted)
{
RaiseLocalEvent(surgeryEnt, ref ev);
if (ev.Cancelled)
continue;
}
surgeries.GetOrNew(GetNetEntity(part)).Add((surgery, ev.Suffix, isCompleted));
}
}
private void OnToolAfterInteract(Entity<SurgeryToolComponent> ent, ref AfterInteractEvent args)
{
var user = args.User;
@ -106,7 +123,7 @@ public sealed partial class SurgerySystem : SharedSurgerySystem
if (user == args.Target)
{
_popup.PopupEntity("You can't perform surgery on yourself!", user, user);
_popup.PopupEntity(Loc.GetString("cant-perform-operation-on-yourself"), user, user);
return;
}

View file

@ -3,11 +3,12 @@ using Content.Shared.Body.Systems;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
using Content.Shared._Sunrise.Medical.Surgery;
namespace Content.Shared.Body.Part;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
[Access(typeof(SharedBodySystem))]
[Access(typeof(SharedBodySystem), typeof(SharedSurgerySystem))] // Sunrise-edit
public sealed partial class BodyPartComponent : Component
{
// Need to set this on container changes as it may be several transform parents up the hierarchy.

View file

@ -40,11 +40,23 @@ public sealed class BodyPrototypeSerializer : ITypeReader<BodyPrototype, Mapping
{
foreach (var (key, value) in organsNode)
{
// STARLIGHT-SURGERY start
// if (key is not ValueDataNode)
// {
// nodes.Add(new ErrorNode(key, $"Key is not a value data node"));
// continue;
// }
// STARLIGHT-SURGERY end
if (value is not ValueDataNode organ)
{
nodes.Add(new ErrorNode(value, $"Value is not a value data node"));
continue;
}
// STARLIGHT-SURGERY start
if (organ.Value == "null" || organ.Value == null)
continue;
// STARLIGHT-SURGERY end
if (!prototypes.TryIndex(organ.Value, out EntityPrototype? organPrototype))
{

View file

@ -205,6 +205,12 @@ public partial class SharedBodySystem
foreach (var (organSlotId, organProto) in organs)
{
var slot = CreateOrganSlot((ent, ent), organSlotId);
// Starlight-surgery start
if (organProto == "null")
return;
// Starlight-surgery end
SpawnInContainerOrDrop(organProto, ent, GetOrganContainerId(organSlotId));
if (slot is null)

View file

@ -602,6 +602,36 @@ public partial class SharedBodySystem
}
}
}
// Sunrise-Start
public IEnumerable<Entity<BodyPartComponent>> GetAllBodyPart(
EntityUid partId,
BodyPartComponent? part = null)
{
if (!Resolve(partId, ref part, logMissing: false))
yield break;
foreach (var (slotId, slot) in part.Children)
{
var containerSlotId = GetPartSlotContainerId(slotId);
if (Containers.TryGetContainer(partId, containerSlotId, out var container))
{
foreach (var containedEnt in container.ContainedEntities)
{
if (!TryComp(containedEnt, out BodyPartComponent? childPart))
continue;
yield return (containedEnt, childPart);
foreach (var subPart in GetAllBodyPart(containedEnt, childPart))
{
yield return subPart;
}
}
}
}
}
// Sunrise-End
/// <summary>
/// Returns true if the bodyId has any parts of this type.

View file

@ -18,6 +18,7 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using Robust.Shared.Configuration;
using Robust.Shared.Random;
using Content.Shared._Sunrise.Medical.Damage;
namespace Content.Shared.Damage
{
@ -239,6 +240,17 @@ namespace Content.Shared.Damage
damage = ApplyUniversalAllModifiers(damage);
// Sunrise-start
var finalEv = new DamageBeforeApplyEvent
{
Damage = damage,
Origin = origin
};
RaiseLocalEvent(uid.Value, finalEv);
if (finalEv.Cancelled)
return damage;
// Sunrise-end
// TODO DAMAGE PERFORMANCE
// Consider using a local private field instead of creating a new dictionary here.
// Would need to check that nothing ever tries to cache the delta.

View file

@ -550,9 +550,12 @@ public abstract class SharedHumanoidAppearanceSystem : EntitySystem
{
return;
}
var markingObject = new Marking(marking, colors);
markingObject.Forced = forced;
// Sunrise-start
var markingObject = new Marking(marking, colors)
{
Forced = forced
};
// Sunrise-end
humanoid.MarkingSet.AddBack(prototype.MarkingCategory, markingObject);
if (sync)

View file

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Content.Shared.Damage;
using Content.Shared.Inventory;
namespace Content.Shared._Sunrise.Medical.Damage;
public sealed class DamageBeforeApplyEvent : EntityEventArgs
{
public required DamageSpecifier Damage;
public EntityUid? Origin;
public bool Cancelled { get; set; }
}

View file

@ -0,0 +1,32 @@
using Content.Shared.DisplacementMap;
using Content.Shared.Humanoid;
using Content.Shared.Item;
using Robust.Shared.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared._Sunrise.Medical.Surgery;
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)]
public sealed partial class CustomLimbVisualizerComponent : Component
{
[DataField, AutoNetworkedField]
public Dictionary<HumanoidVisualLayers, NetEntity?> Layers = [];
[DataField]
public HashSet<HumanoidVisualLayers> CachedLayers = [];
[DataField]
public Dictionary<HumanoidVisualLayers, DisplacementData> Displacements = [];
}
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class CustomLimbComponent : Component
{
[DataField, AutoNetworkedField]
public EntityUid? Item;
}
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class CustomLimbMarkerComponent : Component
{
[DataField, AutoNetworkedField]
public EntityUid? VirtualPart;
}

View file

@ -13,7 +13,7 @@ public sealed partial class SurgeryComponent : Component
public int Priority;
[DataField, AutoNetworkedField]
public EntProtoId? Requirement;
public List<EntProtoId> Requirement = [];
[DataField(required: true), AutoNetworkedField]
public List<EntProtoId> Steps = new();

View file

@ -15,4 +15,4 @@ public sealed partial class SurgeryProgressComponent : Component
[DataField, AutoNetworkedField]
public HashSet<EntProtoId> StartedSurgeries = [];
}
}

View file

@ -1,4 +1,6 @@
using Content.Shared.Body.Part;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Item;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
// Based on the RMC14.
@ -9,6 +11,20 @@ namespace Content.Shared._Sunrise.Medical.Surgery.Effects.Step;
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))] public sealed partial class SurgeryAnyLimbSlotConditionComponent : Component;
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))] public sealed partial class SurgeryOperatingTableConditionComponent : Component;
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))]
public sealed partial class SurgeryLimbSlotConditionComponent : Component
{
[DataField]
public string Slot;
}
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))]
public sealed partial class SurgeryItemSizeConditionComponent : Component
{
[DataField]
public ProtoId<ItemSizePrototype> Size = "Small";
}
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))]
public sealed partial class SurgeryPartConditionComponent : Component
{
@ -16,14 +32,35 @@ public sealed partial class SurgeryPartConditionComponent : Component
public List<BodyPartType> Parts = [];
}
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))]
public sealed partial class SurgerySpeciesConditionComponent : Component
{
[DataField]
public HashSet<ProtoId<SpeciesPrototype>> SpeciesBlacklist = [];
[DataField]
public HashSet<ProtoId<SpeciesPrototype>> SpeciesWhitelist = [];
}
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))]
public sealed partial class SurgeryOrganExistConditionComponent : Component
{
[DataField]
public ComponentRegistry? Organ;
[DataField]
public string? Container;
}
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))]
public sealed partial class SurgeryHasCompConditionComponent : Component
{
[DataField]
public ComponentRegistry? Component;
}
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))]
public sealed partial class SurgeryOrganDontExistConditionComponent : Component
{
[DataField]
public ComponentRegistry? Organ;
}
[DataField]
public string? Container;
}

View file

@ -1,8 +1,11 @@
using Content.Shared.Damage;
using Content.Shared.Damage;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Prototypes;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared._Sunrise.Medical.Surgery.Steps.Parts;
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))] public sealed partial class EyeImplantComponent : Component;
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))] public sealed partial class OrganBrainComponent : Component;
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))] public sealed partial class OrganAppendixComponent : Component;
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))] public sealed partial class OrganEarsComponent : Component;
@ -11,14 +14,14 @@ namespace Content.Shared._Sunrise.Medical.Surgery.Steps.Parts;
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))] public sealed partial class OrganStomachComponent : Component;
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))] public sealed partial class OrganLiverComponent : Component;
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))] public sealed partial class OrganKidneysComponent : Component;
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))]
[RegisterComponent, NetworkedComponent]
public sealed partial class OrganTongueComponent : Component
{
[DataField]
public bool IsMuted;
}
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))]
[RegisterComponent, NetworkedComponent]
public sealed partial class OrganEyesComponent : Component
{
[DataField]
@ -26,10 +29,25 @@ public sealed partial class OrganEyesComponent : Component
[DataField]
public int? MinDamage;
}
[RegisterComponent, NetworkedComponent]
public sealed partial class OrganVisualizationComponent : Component
{
[DataField]
public HumanoidVisualLayers Layer;
[DataField]
public ProtoId<HumanoidSpeciesSpriteLayer> Prototype;
}
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))]
public sealed partial class FunctionalOrganComponent : Component
{
[DataField("comps")]
public ComponentRegistry? Components;
}
[RegisterComponent, NetworkedComponent]
public sealed partial class OrganDamageComponent : Component
{
[DataField]
public DamageSpecifier? Damage;
}
}

View file

@ -12,10 +12,13 @@ public sealed partial class SurgeryStepOrganExtractComponent : Component
{
[DataField]
public ComponentRegistry? Organ;
[DataField]
public string? Slot;
}
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))]
public sealed partial class SurgeryStepOrganInsertComponent : Component
public sealed partial class SurgeryStepOrganInsertComponent : Component
{
[DataField(required: true)]
public string Slot;

View file

@ -12,7 +12,7 @@ namespace Content.Shared._Sunrise.Medical.Surgery.Effects.Step;
{
[DataField]
public DamageSpecifier? Damage;
};
}
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))] public sealed partial class SurgeryStepAmputationEffectComponent : Component;
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))] public sealed partial class SurgeryRemoveAccentComponent : Component;
[RegisterComponent, NetworkedComponent, Access(typeof(SharedSurgerySystem))] public sealed partial class SurgeryClearProgressComponent : Component;

View file

@ -12,6 +12,9 @@ public sealed partial class SurgeryToolComponent : Component
[DataField, AutoNetworkedField]
public float Speed = 1;
[DataField, AutoNetworkedField]
public float SuccessRate = 1f;
[DataField, AutoNetworkedField]
public SoundSpecifier? StartSound;

View file

@ -10,10 +10,12 @@ public sealed partial class SurgeryDoAfterEvent : SimpleDoAfterEvent
{
public readonly EntProtoId Surgery;
public readonly EntProtoId Step;
public readonly float SuccessRate;
public SurgeryDoAfterEvent(EntProtoId surgery, EntProtoId step)
public SurgeryDoAfterEvent(EntProtoId surgery, EntProtoId step, float successRate)
{
Surgery = surgery;
Step = step;
SuccessRate = successRate;
}
}

View file

@ -0,0 +1,9 @@
using Robust.Shared.Prototypes;
namespace Content.Shared._Sunrise.Medical.Surgery.Events;
// Based on the RMC14.
// https://github.com/RMC-14/RMC-14
[ByRefEvent]
public record struct SurgeryOrganImplantationCompleted(EntityUid Body, EntityUid Part, EntityUid Organ);
[ByRefEvent]
public record struct SurgeryOrganExtracted(EntityUid Body, EntityUid Part, EntityUid Organ);

View file

@ -8,6 +8,13 @@ namespace Content.Shared._Sunrise.Medical.Surgery.Events;
/// </summary>
[ByRefEvent]
public record struct SurgeryStepEvent(EntityUid User, EntityUid Body, EntityUid Part, List<EntityUid> Tools)
{
public required EntProtoId StepProto { get; init; }
public required EntProtoId SurgeryProto { get; init; }
public bool IsCancelled { get; set; }
}
[ByRefEvent]
public record struct SurgeryStepCompleteEvent(EntityUid User, EntityUid Body, EntityUid Part, List<EntityUid> Tools)
{
public required EntProtoId StepProto { get; init; }
public required EntProtoId SurgeryProto { get; init; }

View file

@ -1,12 +1,15 @@
using Content.Shared._Sunrise.Medical.Surgery.Steps;
using Content.Shared.Body.Part;
using Content.Shared.Body.Part;
using Content.Shared.Buckle.Components;
using Content.Shared.DoAfter;
using Content.Shared.Inventory;
using Content.Shared.Item;
using Content.Shared.Item.ItemToggle.Components;
using Content.Shared.Popups;
using Robust.Shared.Prototypes;
using Content.Shared._Sunrise.Medical.Surgery.Events;
using Content.Shared._Sunrise.Medical.Surgery.Effects.Step;
using Content.Shared._Sunrise.Medical.Surgery.Events;
using Content.Shared._Sunrise.Medical.Surgery.Steps;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using System.Linq;
namespace Content.Shared._Sunrise.Medical.Surgery;
@ -14,13 +17,15 @@ namespace Content.Shared._Sunrise.Medical.Surgery;
// https://github.com/RMC-14/RMC-14
public abstract partial class SharedSurgerySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
protected float _delayAccumulator = 0f;
protected readonly Queue<Action> _delayQueue = new();
private void InitializeSteps()
{
SubscribeLocalEvent<SurgeryStepComponent, SurgeryStepCompleteEvent>(OnStepComplete);
SubscribeLocalEvent<SurgeryClearProgressComponent, SurgeryStepCompleteEvent>(OnClearProgressStep);
SubscribeLocalEvent<SurgeryStepComponent, SurgeryStepEvent>(OnStep);
SubscribeLocalEvent<SurgeryClearProgressComponent, SurgeryStepEvent>(OnClearProgressStep);
SubscribeLocalEvent<SurgeryTargetComponent, SurgeryDoAfterEvent>(OnTargetDoAfter);
SubscribeLocalEvent<SurgeryStepComponent, SurgeryCanPerformStepEvent>(OnCanPerformStep);
@ -43,47 +48,63 @@ public abstract partial class SharedSurgerySystem
return;
}
if (!_random.Prob(args.SuccessRate))
{
if (_net.IsClient) return;
_popup.PopupClient(Loc.GetString("surgery-careless-tool"), args.User, PopupType.SmallCaution);
return;
}
var ev = new SurgeryStepEvent(args.User, ent, part, GetTools(args.User))
{
StepProto = args.Step,
SurgeryProto = args.Surgery,
};
RaiseLocalEvent(step, ref ev);
if (ev.IsCancelled) return;
var evComplete = new SurgeryStepCompleteEvent(args.User, ent, part, GetTools(args.User))
{
StepProto = args.Step,
SurgeryProto = args.Surgery,
IsFinal = surgery.Comp.Steps[^1] == args.Step,
};
RaiseLocalEvent(step, ref ev);
RaiseLocalEvent(step, ref evComplete);
if (_net.IsClient) return;
_delayAccumulator = 0f;
_delayQueue.Enqueue(() => RefreshUI(ent));
}
private void OnClearProgressStep(Entity<SurgeryClearProgressComponent> ent, ref SurgeryStepEvent args)
private void OnClearProgressStep(Entity<SurgeryClearProgressComponent> ent, ref SurgeryStepCompleteEvent args)
{
var progress = Comp<SurgeryProgressComponent>(args.Part);
progress.CompletedSteps.Clear();
progress.CompletedSurgeries.Clear();
}
private void OnStepComplete(Entity<SurgeryStepComponent> ent, ref SurgeryStepCompleteEvent args)
{
if (TryComp<SurgeryClearProgressComponent>(ent, out _)) return;
if (TryComp<SurgeryProgressComponent>(args.Part, out var progress))
{
progress.CompletedSteps.Add($"{args.SurgeryProto}:{args.StepProto}");
if (!progress.StartedSurgeries.Contains(args.SurgeryProto) && !args.IsFinal)
progress.StartedSurgeries.Add(args.SurgeryProto);
if (progress.StartedSurgeries.Contains(args.SurgeryProto) && args.IsFinal)
progress.StartedSurgeries.Remove(args.SurgeryProto);
}
else
{
progress = new SurgeryProgressComponent { CompletedSteps = [$"{args.SurgeryProto}:{args.StepProto}"]};
if(!args.IsFinal)
progress.StartedSurgeries.Add(args.SurgeryProto);
AddComp(args.Part, progress);
}
if (args.IsFinal)
progress.CompletedSurgeries.Add(args.SurgeryProto);
}
private void OnStep(Entity<SurgeryStepComponent> ent, ref SurgeryStepEvent args)
{
if (!TryComp<SurgeryClearProgressComponent>(ent, out _))
{
if (TryComp<SurgeryProgressComponent>(args.Part, out var progress))
{
progress.CompletedSteps.Add($"{args.SurgeryProto}:{args.StepProto}");
if(!progress.StartedSurgeries.Contains(args.SurgeryProto) && !args.IsFinal)
progress.StartedSurgeries.Add(args.SurgeryProto);
if (progress.StartedSurgeries.Contains(args.SurgeryProto) && args.IsFinal)
progress.StartedSurgeries.Remove(args.SurgeryProto);
}
else
{
progress = new SurgeryProgressComponent { CompletedSteps = [$"{args.SurgeryProto}:{args.StepProto}"] };
AddComp(args.Part, progress);
}
if (args.IsFinal)
progress.CompletedSurgeries.Add(args.SurgeryProto);
}
foreach (var reg in (ent.Comp.Tools ?? []).Values)
{
var tool = args.Tools.FirstOrDefault(x => HasComp(x, reg.Component.GetType()));
@ -130,6 +151,28 @@ public abstract partial class SharedSurgerySystem
RaiseLocalEvent(args.Body, ref args);
if (args.Invalid != StepInvalidReason.None)
return;
if (_inventory.TryGetContainerSlotEnumerator(args.Body, out var enumerator, args.TargetSlots))
{
var items = 0f;
var total = 0f;
while (enumerator.MoveNext(out var con))
{
total++;
if (con.ContainedEntity != null)
items++;
}
if (items > 0)
{
args.Invalid = StepInvalidReason.Armor;
args.Popup = Loc.GetString("surgery-need-remove-armor");
return;
}
}
if (args.Invalid != StepInvalidReason.None || ent.Comp.Tools == null)
return;
@ -141,10 +184,24 @@ public abstract partial class SharedSurgerySystem
args.Invalid = StepInvalidReason.MissingTool;
if (reg.Component is ISurgeryToolComponent toolComp)
args.Popup = $"You need {toolComp.ToolName} to perform this step!";
args.Popup = Loc.GetString("surgery-need-tool");
return;
}
else if (TryComp<ItemToggleComponent>(tool, out var togglable) && !togglable.Activated)
{
args.Invalid = StepInvalidReason.DisabledTool;
if (reg.Component is ISurgeryToolComponent toolComp)
args.Popup = Loc.GetString("surgery-need-enable");
return;
}
else if (TryComp<SurgeryItemSizeConditionComponent>(ent, out var itemSizeComp) && TryComp<ItemComponent>(tool, out var item) && _item.GetSizePrototype(item.Size) > _item.GetSizePrototype(itemSizeComp.Size))
{
args.Invalid = StepInvalidReason.TooHigh;
return;
}
args.ValidTools.Add(tool);
}
@ -162,7 +219,7 @@ public abstract partial class SharedSurgerySystem
{
return;
}
if(!PreviousStepsComplete(body, part, surgery, args.Step) || IsStepComplete(part, args.Surgery, args.Step))
if (!PreviousStepsComplete(body, part, surgery, args.Step) || IsStepComplete(part, args.Surgery, args.Step))
{
var progress = Comp<SurgeryProgressComponent>(part);
Dirty(part, progress);
@ -173,17 +230,22 @@ public abstract partial class SharedSurgerySystem
var duration = stepComp.Duration;
float SmallestSuccessRate = 1f;
foreach (var tool in validTools)
if (TryComp(tool, out SurgeryToolComponent? toolComp))
{
duration *= toolComp.Speed;
if (toolComp.StartSound != null) _audio.PlayPvs(toolComp.StartSound, tool);
if(toolComp.SuccessRate < SmallestSuccessRate)
SmallestSuccessRate = toolComp.SuccessRate;
}
if (TryComp(body, out TransformComponent? xform))
_rotateToFace.TryFaceCoordinates(user, _transform.GetMapCoordinates(body, xform).Position);
var ev = new SurgeryDoAfterEvent(args.Surgery, args.Step);
var ev = new SurgeryDoAfterEvent(args.Surgery, args.Step, SmallestSuccessRate);
var doAfter = new DoAfterArgs(EntityManager, user, duration, ev, body, part)
{
BreakOnMove = true,
@ -204,10 +266,16 @@ public abstract partial class SharedSurgerySystem
requirements.Add(surgery);
if (surgery.Comp.Requirement is { } requirementId &&
GetSingleton(requirementId) is { } requirement &&
GetNextStep(body, part, requirement, requirements) is { } requiredNext)
return requiredNext;
if (surgery.Comp.Requirement is { } requirementsIds)
{
foreach (var requirementId in requirementsIds)
{
if (GetSingleton(requirementId) is { } requirement
&& GetNextStep(body, part, requirement, requirements) is { } requiredNext
&& IsSurgeryValid(body, part, requirementId, requiredNext.Surgery.Comp.Steps[requiredNext.Step], out _, out _, out _))
return requiredNext;
}
}
if (!TryComp<SurgeryProgressComponent>(part, out var progress))
{
@ -224,13 +292,15 @@ public abstract partial class SharedSurgerySystem
public bool PreviousStepsComplete(EntityUid body, EntityUid part, Entity<SurgeryComponent> surgery, EntProtoId step)
{
if (surgery.Comp.Requirement is { } requirement)
if (surgery.Comp.Requirement is { } requirements)
{
if (GetSingleton(requirement) is not { } requiredEnt ||
!TryComp(requiredEnt, out SurgeryComponent? requiredComp) ||
!PreviousStepsComplete(body, part, (requiredEnt, requiredComp), step))
foreach (var requirement in requirements)
{
return false;
if (GetSingleton(requirement) is not { } requiredEnt
|| !TryComp(requiredEnt, out SurgeryComponent? requiredComp)
|| !PreviousStepsComplete(body, part, (requiredEnt, requiredComp), step)
&& IsSurgeryValid(body, part, requirement, step, out _, out _, out _))
return false;
}
}
@ -251,7 +321,7 @@ public abstract partial class SharedSurgerySystem
{
var slot = part switch
{
BodyPartType.Head => SlotFlags.HEAD,
BodyPartType.Head => SlotFlags.HEAD | SlotFlags.MASK | SlotFlags.EYES,
BodyPartType.Torso => SlotFlags.OUTERCLOTHING | SlotFlags.INNERCLOTHING,
BodyPartType.Arm => SlotFlags.OUTERCLOTHING | SlotFlags.INNERCLOTHING,
BodyPartType.Hand => SlotFlags.GLOVES,

View file

@ -1,8 +1,11 @@
using Content.Shared.Body.Part;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Humanoid;
using System.Linq;
using Content.Shared._Sunrise.Medical.Surgery.Steps.Parts;
using Content.Shared._Sunrise.Medical.Surgery.Events;
using Content.Shared._Sunrise.Medical.Surgery.Effects.Step;
using Content.Shared.Body.Systems;
namespace Content.Shared._Sunrise.Medical.Surgery;
// Based on the RMC14.
@ -17,33 +20,86 @@ public abstract partial class SharedSurgerySystem
.ToList();
SubscribeLocalEvent<SurgeryPartConditionComponent, SurgeryValidEvent>(OnPartConditionValid);
SubscribeLocalEvent<SurgerySpeciesConditionComponent, SurgeryValidEvent>(OnSpeciesConditionValid);
SubscribeLocalEvent<SurgeryOrganExistConditionComponent, SurgeryValidEvent>(OnOrganExistConditionValid);
SubscribeLocalEvent<SurgeryOrganDontExistConditionComponent, SurgeryValidEvent>(OnOrganDontExistConditionValid);
SubscribeLocalEvent<SurgeryAnyAccentConditionComponent, SurgeryValidEvent>(OnAnyAccentConditionValid);
SubscribeLocalEvent<SurgeryAnyLimbSlotConditionComponent, SurgeryValidEvent>(OnAnyLimbSlotConditionValid);
SubscribeLocalEvent<SurgeryLimbSlotConditionComponent, SurgeryValidEvent>(OnLimbSlotConditionValid);
}
private void OnOrganDontExistConditionValid(Entity<SurgeryOrganDontExistConditionComponent> ent, ref SurgeryValidEvent args)
{
if (ent.Comp.Organ?.Count != 1) return;
var type = ent.Comp.Organ.Values.First().Component.GetType();
var organs = _body.GetPartOrgans(args.Part, Comp<BodyPartComponent>(args.Part));
foreach (var organ in organs)
if (HasComp(organ.Id, type))
if (ent.Comp.Container != null)
{
foreach (var slotId in Comp<BodyPartComponent>(args.Part).Organs.Keys)
{
args.Cancelled = true;
return;
if (ent.Comp.Container == slotId)
{
if (!_containers.TryGetContainer(args.Part, ent.Comp.Container, out var container))
continue;
foreach (var containedEnt in container.ContainedEntities)
{
if (HasComp(containedEnt, type))
{
args.Cancelled = true;
return;
}
}
}
}
}
else
{
var organs = _body.GetPartOrgans(args.Part, Comp<BodyPartComponent>(args.Part));
foreach (var organ in organs)
if (HasComp(organ.Id, type))
{
args.Cancelled = true;
return;
}
}
}
private void OnOrganExistConditionValid(Entity<SurgeryOrganExistConditionComponent> ent, ref SurgeryValidEvent args)
{
if (ent.Comp.Organ?.Count != 1) return;
var organs = _body.GetPartOrgans(args.Part, Comp<BodyPartComponent>(args.Part));
var type = ent.Comp.Organ.Values.First().Component.GetType();
foreach (var organ in organs)
if (HasComp(organ.Id, type))
return;
args.Cancelled = true;
EntityUid mainPart = args.Part;
if (TryComp<BodyPartComponent>(args.Body, out var itemPart))
mainPart = args.Body;
if (ent.Comp.Container != null)
{
foreach (var slotId in Comp<BodyPartComponent>(mainPart).Organs.Keys)
{
if (ent.Comp.Container == slotId)
{
if (!_containers.TryGetContainer(mainPart, SharedBodySystem.GetOrganContainerId(ent.Comp.Container), out var container))
continue;
foreach (var containedEnt in container.ContainedEntities)
if (HasComp(containedEnt, type))
return;
args.Cancelled = true;
}
}
}
else
{
var organs = _body.GetPartOrgans(mainPart, Comp<BodyPartComponent>(mainPart));
foreach (var organ in organs)
if (HasComp(organ.Id, type))
return;
args.Cancelled = true;
}
}
private void OnPartConditionValid(Entity<SurgeryPartConditionComponent> ent, ref SurgeryValidEvent args)
@ -51,9 +107,35 @@ public abstract partial class SharedSurgerySystem
if (ent.Comp.Parts.Count == 0)
return;
if (TryComp<BodyPartComponent>(args.Body, out var itemPart) && itemPart.PartType is BodyPartType item && !ent.Comp.Parts.Contains(item))
{
Logger.Warning("don't have part at part");
args.Cancelled = true;
}
if (CompOrNull<BodyPartComponent>(args.Part)?.PartType is BodyPartType part && !ent.Comp.Parts.Contains(part))
args.Cancelled = true;
}
private void OnSpeciesConditionValid(Entity<SurgerySpeciesConditionComponent> ent, ref SurgeryValidEvent args)
{
if (!EntityManager.TryGetComponent<HumanoidAppearanceComponent>(args.Body, out var humanoidAppearanceComponent))
{
args.Cancelled = true;
return;
}
if (ent.Comp.SpeciesBlacklist.Contains(humanoidAppearanceComponent.Species))
{
args.Cancelled = true;
return;
}
if (ent.Comp.SpeciesWhitelist.Count > 0 && !ent.Comp.SpeciesWhitelist.Contains(humanoidAppearanceComponent.Species))
{
args.Cancelled = true;
return;
}
}
private void OnAnyAccentConditionValid(Entity<SurgeryAnyAccentConditionComponent> ent, ref SurgeryValidEvent args)
{
foreach (var accent in _accents)
@ -71,4 +153,7 @@ public abstract partial class SharedSurgerySystem
else
args.Cancelled = true;
}
private void OnLimbSlotConditionValid(Entity<SurgeryLimbSlotConditionComponent> ent, ref SurgeryValidEvent args)
=> args.Cancelled = !(_containers.TryGetContainer(args.Part, SharedBodySystem.GetPartSlotContainerId(ent.Comp.Slot), out var container)
&& container.ContainedEntities.Count == 0);
}

View file

@ -12,6 +12,8 @@ using Content.Shared.DoAfter;
using Content.Shared.GameTicking;
using Content.Shared.Hands.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Inventory;
using Content.Shared.Item;
using Content.Shared.Popups;
using Content.Shared.Standing;
using Robust.Shared.Audio.Systems;
@ -43,6 +45,8 @@ public abstract partial class SharedSurgerySystem : EntitySystem
[Dependency] private readonly ISerializationManager _serialization = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly SharedContainerSystem _containers = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly SharedItemSystem _item = default!;
private readonly Dictionary<EntProtoId, EntityUid> _surgeries = new();
@ -61,7 +65,7 @@ public abstract partial class SharedSurgerySystem : EntitySystem
_surgeries.Clear();
}
protected bool IsSurgeryValid(EntityUid body, EntityUid targetPart, EntProtoId surgery, EntProtoId stepId, out Entity<SurgeryComponent> surgeryEnt, out Entity<BodyPartComponent> part, out EntityUid step)
public bool IsSurgeryValid(EntityUid body, EntityUid targetPart, EntProtoId surgery, EntProtoId stepId, out Entity<SurgeryComponent> surgeryEnt, out Entity<BodyPartComponent> part, out EntityUid step)
{
surgeryEnt = default;
part = default;
@ -125,6 +129,9 @@ public abstract partial class SharedSurgerySystem : EntitySystem
if (_standing.IsDown(entity))
return true;
if (HasComp<ItemComponent>(entity))
return true;
if (TryComp(entity, out BuckleComponent? buckle) &&
TryComp(buckle.BuckledTo, out StrapComponent? strap))
{

View file

@ -7,4 +7,6 @@ public enum StepInvalidReason
NeedsOperatingTable,
Armor,
MissingTool,
DisabledTool,
TooHigh,
}

View file

@ -0,0 +1,4 @@
surgery-careless-tool = Из-за небрежного обращения с инструментом ваша рука дрогнула. Вам придется начать этот шаг заново!
surgery-need-remove-armor = Чтобы выполнить этот шаг, необходимо снять броню с пациента!
surgery-need-tool = Вам необходим {toolComp.ToolName} чтобы выполнить этот шаг!
surgery-need-enable = Вам необходимо включить {toolComp.ToolName} чтобы выполнить шаг!

View file

@ -0,0 +1 @@
cant-perform-operation-on-yourself = Вы не можете провести операцию на самом себе!

View file

@ -1,9 +1,11 @@
surgery-window-name = Хирургия
surgery-window-partsbutton-name = Части тела
surgery-window-surgeriesbutton-name = Операции
surgery-window-stepsbutton-name = Этапы
surgery-window-partsbutton-name = < Части тела
surgery-window-surgeriesbutton-name = < Операции
surgery-window-stepsbutton-name = < Этапы
surgery-window-reguires = [bold]Требует: { $surgeryname }[/bold]
surgery-window-reguires-table = [color=red](Требует операционный стол)[/color]
surgery-window-reguires-undress = [color=red](Снимите с него броню!)[/color]
surgery-window-reguires-tool = [color=red](Отсутствует инструмент)[/color]
surgery-window-reguires-laydown = [color=red][font size=16]Он должен лежать![/font][/color]
surgery-window-reguires-enable = [color=red](Предмет выключен)[/color]
surgery-window-too-high = [color=red](Слишком высокий)[/color]

View file

@ -128,7 +128,7 @@
- type: entity
id: OrganAnimalHeart
parent: BaseAnimalOrgan
parent: [ BaseAnimalOrgan, BaseOrganHeart]
name: heart
categories: [ HideSpawnMenu ]
components:

View file

@ -160,6 +160,9 @@
- type: Item
size: Small
heldPrefix: eyeballs
- type: OrganVisualization
layer: Eyes
prototype: MobArachnidEyes
- type: entity
id: OrganArachnidTongue

View file

@ -19,7 +19,10 @@
Caustic: 5
- type: Damageable
damageContainer: Biological
- type: OrganVisualization
layer: Eyes
prototype: MobHumanoidEyes
- type: entity
id: BaseOrganTongue
abstract: true

View file

@ -62,6 +62,9 @@
layers:
- state: eyeball-l
- state: eyeball-r
- type: OrganVisualization
layer: Eyes
prototype: MobDionaEyes
- type: entity
id: OrganDionaStomach

View file

@ -88,6 +88,9 @@
- type: Item
size: Small
heldPrefix: eyeballs
- type: OrganVisualization
layer: Eyes
prototype: MobHumanoidEyes
- type: entity
id: OrganHumanTongue

View file

@ -15,9 +15,11 @@
groups:
- id: Food
- id: Drink
- id: Medicine
- id: Poison
- id: Narcotic
# Sunrise-start
#- id: Medicine
#- id: Poison
#- id: Narcotic
# Sunrise-end
- id: Alcohol
rateModifier: 0.25
- type: SolutionContainerManager
@ -37,7 +39,6 @@
size: Small
heldPrefix: brain
- type: entity
id: OrganSlimeLungs
parent: [BaseHumanOrgan, BaseOrganLungs]

View file

@ -20,6 +20,13 @@
- type: Tag
tags:
- Trash
# Sunrise-start
- type: SurgeryTarget
- type: UserInterface
interfaces:
enum.SurgeryUIKey.Key:
type: SurgeryBui
# Sunrise-end
- type: entity
id: BaseTorso

View file

@ -10,20 +10,31 @@
organs:
brain: OrganHumanBrain
eyes: OrganArachnidEyes
# Sunrise-start
tongue: OrganHumanTongue
eye_implant: null
# Sunrise-end
torso:
part: TorsoArachnid
# Sunrise-start
connections:
- right arm
- left arm
- right leg
- left leg
# Sunrise-end
organs:
heart: OrganArachnidHeart
lungs: OrganAnimalLungs
stomach: OrganArachnidStomach
liver: OrganArachnidLiver
kidneys: OrganArachnidKidneys
connections:
- right arm
- left arm
- right leg
- left leg
cavity: null # Sunrise-edit
# connections:
# - right arm
# - left arm
# - right leg
# - left leg
right arm:
part: RightArmArachnid
connections:

View file

@ -21,6 +21,8 @@
organs:
stomach: OrganDionaStomachNymph
lungs: OrganDionaLungsNymph
heart: null # Sunrise-edit
cavity: null # Sunrise-edit
right arm:
part: RightArmDiona
connections:

View file

@ -10,20 +10,31 @@
organs:
brain: OrganHumanBrain
eyes: OrganHumanEyes
# Sunrise-start
tongue: OrganHumanTongue
eye_implant: null
# Sunrise-end
torso:
part: TorsoHuman
# Sunrise-start
connections:
- right arm
- left arm
- right leg
- left leg
# Sunrise-end
organs:
heart: OrganDwarfHeart
lungs: OrganHumanLungs
stomach: OrganDwarfStomach
liver: OrganDwarfLiver
kidneys: OrganHumanKidneys
cavity: null # Sunrise-edit
# connections:
# - right arm
# - left arm
# - right leg
# - left leg
right arm:
part: RightArmHuman
connections:

View file

@ -24,6 +24,7 @@
stomach: OrganHumanStomach
liver: OrganHumanLiver
kidneys: OrganHumanKidneys
cavity: null # Sunrise-edit
right arm:
part: RightArmGingerbread
connections:

View file

@ -10,41 +10,43 @@
organs:
brain: OrganHumanBrain
eyes: OrganHumanEyes
tongue: OrganHumanTongue
tongue: OrganHumanTongue # Sunrise-edit
torso:
part: TorsoHuman
connections:
- right_arm
- left_arm
- right_leg
- left_leg
- right arm # STARLIGHT-SURGERY EDITED, be careful to not delete this
- left arm
- right leg
- left leg
organs:
heart: OrganHumanHeart
lungs: OrganHumanLungs
stomach: OrganHumanStomach
liver: OrganHumanLiver
kidneys: OrganHumanKidneys
right_arm:
cavity: null # Sunrise-edit
right arm:
part: RightArmHuman
connections:
- right_hand
left_arm:
- right hand
left arm:
part: LeftArmHuman
connections:
- left_hand
right_hand:
- left hand
right hand:
part: RightHandHuman
left_hand:
left hand:
part: LeftHandHuman
right_leg:
right leg:
part: RightLegHuman
connections:
- right_foot
left_leg:
- right foot
left leg:
part: LeftLegHuman
connections:
- left_foot
right_foot:
- left foot
right foot:
part: RightFootHuman
left_foot:
left foot:
part: LeftFootHuman

View file

@ -10,20 +10,31 @@
organs:
brain: OrganHumanBrain
eyes: OrganHumanEyes
# Sunrise-start
tongue: OrganHumanTongue
eye_implant: null
# Sunrise-end
torso:
part: TorsoMoth
# Sunrise-start
connections:
- right arm
- left arm
- right leg
- left leg
# Sunrise-end
organs:
heart: OrganAnimalHeart
lungs: OrganHumanLungs
stomach: OrganMothStomach
liver: OrganAnimalLiver
kidneys: OrganHumanKidneys
connections:
- right arm
- left arm
- right leg
- left leg
cavity: null # Sunrise-edit
# connections:
# - right arm
# - left arm
# - right leg
# - left leg
right arm:
part: RightArmMoth
connections:

View file

@ -14,6 +14,7 @@
liver: OrganAnimalLiver
heart: OrganPrimateHeart # Sunrise Edit
kidneys: OrganAnimalKidneys
cavity: null # Sunrise-edit
hands:
part: HandsAnimal
legs:

View file

@ -13,6 +13,7 @@
liver: OrganAnimalLiver
heart: OrganAnimalHeart
kidneys: OrganAnimalKidneys
cavity: null # Sunrise-edit
legs:
part: LegsAnimal
connections:

View file

@ -10,20 +10,31 @@
organs:
brain: OrganHumanBrain
eyes: OrganHumanEyes
# Sunrise-start
tongue: OrganHumanTongue
eye_implant: null
# Sunrise-end
torso:
part: TorsoReptilian
# Sunrise-start
connections:
- right arm
- left arm
- right leg
- left leg
# Sunrise-end
organs:
heart: OrganAnimalHeart
lungs: OrganHumanLungs
stomach: OrganReptilianStomach
liver: OrganAnimalLiver
kidneys: OrganHumanKidneys
connections:
- right arm
- left arm
- right leg
- left leg
cavity: null # Sunrise-edit
# connections:
# - right arm
# - left arm
# - right leg
# - left leg
right arm:
part: RightArmReptilian
connections:

View file

@ -9,7 +9,10 @@
- torso
organs:
eyes: OrganHumanEyes
# Sunrise-start
tongue: OrganHumanTongue
eye_implant: null
# Sunrise-end
torso:
part: TorsoSlime
connections:
@ -18,8 +21,10 @@
- right leg
- left leg
organs:
heart: OrganSlimeHeart # Sunrise-edit
core: SentientSlimeCore
lungs: OrganSlimeLungs
cavity: null # Sunrise-edit
right arm:
part: RightArmSlime
connections:

View file

@ -10,6 +10,7 @@
- torso
organs:
brain: MobTerminatorEndoskeleton
cavity: null # Sunrise-edit
torso:
part: TorsoHuman
connections:

View file

@ -10,7 +10,10 @@
organs:
brain: OrganHumanBrain
eyes: OrganHumanEyes
# Sunrise-start
tongue: OrganHumanTongue
eye_implant: null
# Sunrise-end
torso:
part: TorsoVox
connections:
@ -24,6 +27,7 @@
stomach: OrganVoxStomach
liver: OrganVoxLiver
kidneys: OrganHumanKidneys
cavity: null # Sunrise-edit
right arm:
part: RightArmVox
connections:

View file

@ -4,7 +4,6 @@
id: BaseMobSlimePerson
abstract: true
components:
- type: Absorbable
- type: Hunger
- type: Thirst
baseDecayRate: 0.3
@ -40,7 +39,13 @@
type: StrippableBoundUserInterface
enum.StoreUiKey.Key:
type: StoreBoundUserInterface
# to prevent bag open/honk spam
# Sunrise-start
enum.SurgeryUIKey.Key:
type: SurgeryBui
enum.VampireMutationUiKey.Key:
type: VampireMutationBoundUserInterface
- type: Absorbable
# Sunrise-end
- type: UseDelay
delay: 0.5
- type: HumanoidAppearance

View file

@ -48,7 +48,50 @@
- type: TTS
- type: DamageOverlay
- type: EmoteAnimation
# Sunrise-End
- type: CustomLimbVisualizer
displacements:
LHand:
sizeMaps:
32:
sprite: _Sunrise/CustomLimb/displacement.rsi
state: l_hand
RHand:
sizeMaps:
32:
sprite: _Sunrise/CustomLimb/displacement.rsi
state: r_hand
LLeg:
sizeMaps:
32:
sprite: _Sunrise/CustomLimb/displacement.rsi
state: l_leg
RLeg:
sizeMaps:
32:
sprite: _Sunrise/CustomLimb/displacement.rsi
state: r_leg
LFoot:
sizeMaps:
32:
sprite: _Sunrise/CustomLimb/displacement.rsi
state: l_foot
RFoot:
sizeMaps:
32:
sprite: _Sunrise/CustomLimb/displacement.rsi
state: r_foot
LArm:
sizeMaps:
32:
sprite: _Sunrise/CustomLimb/displacement.rsi
state: l_arm
RArm:
sizeMaps:
32:
sprite: _Sunrise/CustomLimb/displacement.rsi
state: r_arm
# Sunrise-end
- type: entity
save: false

View file

@ -79,6 +79,15 @@
- type: DeleteOnTrigger
- type: StaticPrice
price: 0
# Sunrise-start
- type: Scalpel
- type: SurgeryTool
successRate: 0.6
startSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel1.ogg
endSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel2.ogg
# Sunrise-end
- type: entity
parent: ShardBase

View file

@ -24,6 +24,15 @@
Piercing: 6
soundHit:
path: "/Audio/Weapons/bladeslice.ogg"
# Sunrise-start
- type: Scalpel
- type: SurgeryTool
successRate: 0.12
startSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel1.ogg
endSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel2.ogg
# Sunrise-end
- type: PhysicalComposition
materialComposition:
Steel: 200

View file

@ -4,6 +4,15 @@
id: Lighter
description: "A simple plastic cigarette lighter."
components:
# Sunrise-start
- type: SurgeryTool
successRate: 0.1
startSound:
path: /Audio/_Sunrise/Medical/Surgery/cautery1.ogg
endSound:
path: /Audio/_Sunrise/Medical/Surgery/cautery2.ogg
- type: Cautery
# Sunrise-end
- type: IgnitionSource
ignited: false
- type: ItemToggle

View file

@ -55,6 +55,15 @@
Steel: 100
- type: StaticPrice
price: 30
# Sunrise-start
- type: Scalpel
- type: SurgeryTool
successRate: 0.35
startSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel1.ogg
endSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel2.ogg
# Sunrise-end
- type: entity
name: screwdriver

View file

@ -47,3 +47,12 @@
maxVol: 300
- type: UseDelay
delay: 1
# Sunrise-start
- type: Scalpel
- type: SurgeryTool
successRate: 0.01
startSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel1.ogg
endSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel2.ogg
# Sunrise-end

View file

@ -98,6 +98,15 @@
sprite: Objects/Weapons/Melee/e_sword-inhands.rsi
- type: StaticPrice
price: 2500
# Sunrise-start
- type: Scalpel
- type: SurgeryTool
successRate: 0.6
startSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel1.ogg
endSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel2.ogg
# Sunrise-end
- type: entity
name: energy dagger
@ -162,6 +171,15 @@
energy: 1.5
color: white
netsync: false
# Sunrise-start
- type: Scalpel
- type: SurgeryTool
successRate: 0.6
startSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel1.ogg
endSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel2.ogg
# Sunrise-end
- type: entity
name: pen
@ -232,6 +250,15 @@
tags:
- Write
- NoPaint # Sunrise-edit
# Sunrise-start
- type: Scalpel
- type: SurgeryTool
successRate: 0.6
startSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel1.ogg
endSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel2.ogg
# Sunrise-end
- type: entity
parent: [BaseItem, BaseSyndicateContraband]
@ -341,6 +368,15 @@
spread: 75
- type: FlipOnAttack # Sunrise-Edit
probability: 0.5 # Sunrise-Edit
# Sunrise-start
- type: Scalpel
- type: SurgeryTool
successRate: 0.6
startSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel1.ogg
endSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel2.ogg
# Sunrise-end
# Item will look weird in handslot. Will need to adjust handstorage visuals in a future PR
- type: entity
@ -413,3 +449,12 @@
- type: EnergySword
colorOptions:
- "#2288ff" # can only be blue
# Sunrise-start
- type: Scalpel
- type: SurgeryTool
successRate: 0.6
startSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel1.ogg
endSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel2.ogg
# Sunrise-end

View file

@ -27,6 +27,15 @@
- Slicing
useSound:
path: /Audio/Items/Culinary/chop.ogg
# Sunrise-start
- type: Scalpel
- type: SurgeryTool
successRate: 0.9
startSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel1.ogg
endSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel2.ogg
# Sunrise-end
- type: entity
name: kitchen knife
@ -210,6 +219,15 @@
sprite: Objects/Weapons/Melee/shiv.rsi
- type: DisarmMalus
malus: 0.225
# Sunrise-start
- type: Scalpel
- type: SurgeryTool
successRate: 0.7
startSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel1.ogg
endSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel2.ogg
# Sunrise-end
- type: entity
name: reinforced shiv

View file

@ -209,8 +209,17 @@
- Back
- SuitStorage #Bigger than the other swords, easier to strap to your suit.
- type: DisarmMalus
# Sunrise-start
- type: Scalpel
- type: SurgeryTool
successRate: 0.6
startSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel1.ogg
endSound:
path: /Audio/_Sunrise/Medical/Surgery/scalpel2.ogg
# Sunrise-end
#Other/Weird
- type: entity
name: throngler
parent: [ BaseSword, BaseMajorContraband ]

View file

@ -0,0 +1,20 @@
- type: entity
id: OrganSlimeHeart
parent: [BaseHumanOrgan, BaseOrganHeart]
name: slime circulator
description: "A little circulator what makes the fluid move through the slime body."
components:
- type: Sprite
sprite: _Sunrise/Mobs/Species/Slime/organs.rsi
state: heart-on
- type: Item
size: Small
heldPrefix: heart
- type: Metabolizer
maxReagents: 6
metabolizerTypes: [ Slime ]
removeEmpty: true
groups:
- id: Medicine
- id: Poison
- id: Narcotic

View file

@ -0,0 +1,5 @@
- type: entity
id: PartVirtual
parent: BaseItem
components:
- type: BodyPart

View file

@ -13,6 +13,7 @@
liver: OrganAnimalLiver
heart: OrganPigHeart
kidneys: OrganAnimalKidneys
cavity: null
legs:
part: LegsAnimal
connections:
@ -90,4 +91,4 @@
connections:
- feet
feet:
part: FeetAnimal
part: FeetAnimal

View file

@ -14,6 +14,7 @@
liver: OrganBloodsuckerLiver
heart: OrganBloodsuckerHeart
kidneys: OrganAnimalKidneys
cavity: null
right arm:
part: RightArmHuman
connections:
@ -25,4 +26,4 @@
right hand:
part: RightHandHuman
left hand:
part: LeftHandHuman
part: LeftHandHuman

View file

@ -18,6 +18,7 @@
stomach: OrganDemonStomach
liver: OrganAnimalLiver
kidneys: OrganHumanKidneys
cavity: null
connections:
- right arm
- left arm
@ -46,4 +47,4 @@
right foot:
part: RightFootDemon
left foot:
part: LeftFootDemon
part: LeftFootDemon

View file

@ -14,7 +14,7 @@
part: TorsoHuman
connections:
- right arm
- left arm
- left arm
- left leg
- right leg
organs:
@ -23,6 +23,7 @@
stomach: OrganReptilianStomach
liver: OrganAnimalLiver
kidneys: OrganHumanKidneys
cavity: null
right arm:
part: RightArmHuman
connections:

View file

@ -19,6 +19,7 @@
stomach: OrganHumanoidXenoStomach
liver: OrganHumanoidXenoLiver
kidneys: OrganHumanoidXenoKidneys
cavity: null
connections:
- right arm
- left arm

View file

@ -19,6 +19,7 @@
stomach: OrganPredatorStomach
liver: OrganPredatorLiver
kidneys: OrganPredatorKidneys
cavity: null
connections:
- right arm
- left arm

View file

@ -18,6 +18,7 @@
stomach: OrganSwineStomach
liver: OrganAnimalLiver
kidneys: OrganHumanKidneys
cavity: null
connections:
- right arm
- left arm

View file

@ -18,6 +18,7 @@
stomach: OrganTajaranStomach
liver: OrganAnimalLiver
kidneys: OrganHumanKidneys
cavity: null
connections:
- right arm
- left arm

View file

@ -18,6 +18,7 @@
stomach: OrganVulpkaninStomach
liver: OrganAnimalLiver
kidneys: OrganHumanKidneys
cavity: null
connections:
- right_arm
- left_arm

View file

@ -16,3 +16,70 @@
types:
Slash: 24
- type: SurgeryStepAmputationEffect
- type: entity
parent: SurgeryStepBase
id: SurgeryStepExposeSkull
name: Expose Skull
components:
- type: SurgeryStep
duration: 2
tools:
- type: BoneSaw
- type: Sprite
sprite: Objects/Specific/Medical/Surgery/saw.rsi
state: saw
- type: SurgeryStepBleedEffect
damage:
types:
Slash: 5
- type: SurgeryStepEmoteEffect
- type: entity
parent: SurgeryStepBase
id: SurgeryStepLigateVessels
name: Ligate Vessels
components:
- type: SurgeryStep
duration: 3
tools:
- type: Hemostat
- type: Sprite
sprite: Objects/Specific/Medical/Surgery/scissors.rsi
state: hemostat
- type: entity
parent: SurgeryStepBase
id: SurgeryStepSawSkull
name: Saw Skull
components:
- type: SurgeryStep
duration: 20
tools:
- type: BoneSaw
- type: Sprite
sprite: Objects/Specific/Medical/Surgery/saw.rsi
state: saw
- type: SurgeryStepBleedEffect
damage:
types:
Slash: 15
- type: SurgeryStepAmputationEffect
- type: entity
parent: SurgeryStepBase
id: SurgeryStepInciseCartilage
name: Incise Cartilage
components:
- type: SurgeryStep
tools:
- type: Scalpel
- type: Sprite
sprite: Objects/Specific/Medical/Surgery/scalpel.rsi
state: scalpel
- type: entity
parent: SurgeryStepSawSkull
id: SurgeryStepHeadFinalizeSeparation
name: Finalize Separation

View file

@ -0,0 +1,78 @@
- type: entity
parent: SurgeryBase
id: SurgeryAmputation
name: Amputation
description: Surgical removal of a limb.
components:
- type: Surgery
requirement:
- SurgeryOpenIncision
steps:
- SurgeryStepInciseCartilage
- SurgeryStepExposeNerves
- SurgeryStepExposeBloodVessels
- SurgeryAmputationStep
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Arm
- Hand
- Leg
- Foot
- Tail
- type: Sprite
sprite: Objects/Specific/Medical/Surgery/saw.rsi
state: saw
- type: entity
parent: SurgeryAmputation
id: SurgeryAmputationHead
name: Amputation
description: Surgical removal of a head.
components:
- type: Surgery
requirement:
- SurgeryOpenIncision
steps:
- SurgeryStepDeepIncision
- SurgeryStepExposeSkull
- SurgeryStepExposeNerves
- SurgeryStepExposeBloodVessels
- SurgeryStepSawSkull
- type: SurgeryPartCondition
parts:
- Head
- type: entity
parent: SurgeryAmputation
id: SurgeryAmputationSlime
components:
- type: Surgery
requirement:
- SurgeryOpenIncisionSlime
steps:
- SurgeryStepDeepIncision
- SurgeryStepInciseCartilage
- SurgeryStepSawSkull
- type: SurgerySpeciesCondition
speciesBlacklist: []
speciesWhitelist:
- SlimePerson
- type: entity
parent: SurgeryAmputationHead
id: SurgeryAmputationHeadSlime
components:
- type: Surgery
requirement:
- SurgeryOpenIncisionSlime
steps:
- SurgeryStepDeepIncision
- SurgeryStepInciseCartilage
- SurgeryStepHeadFinalizeSeparation
- type: SurgerySpeciesCondition
speciesBlacklist: []
speciesWhitelist:
- SlimePerson

View file

@ -1,32 +1,4 @@
- type: entity
parent: SurgeryStepBase
id: SurgeryStepExposeNerves
name: Expose Nerves
components:
- type: SurgeryStep
duration: 4
tools:
- type: Scalpel
- type: Sprite
sprite: Objects/Specific/Medical/Surgery/scalpel.rsi
state: scalpel
- type: SurgeryStepEmoteEffect
- type: entity
parent: SurgeryStepBase
id: SurgeryStepExposeBloodVessels
name: Expose Blood Vessels
components:
- type: SurgeryStep
duration: 4
tools:
- type: Scalpel
- type: Sprite
sprite: Objects/Specific/Medical/Surgery/scalpel.rsi
state: scalpel
- type: SurgeryStepEmoteEffect
- type: entity
parent: SurgeryStepBase
id: SurgeryLimbAttachmentStep
name: attach a limb
@ -34,7 +6,7 @@
- type: SurgeryStep
duration: 4
tools:
- type: BodyPart
- type: MetaData
- type: Sprite
sprite: Objects/Consumable/Food/Baked/pizza.rsi
state: meat-slice

View file

@ -0,0 +1,223 @@
- type: entity
parent: SurgeryBase
id: SurgeryLimbAttachmentHead
name: Limb Attachment Head
description: Surgical attachment of a limb.
components:
- type: Surgery
requirement:
- SurgeryOpenIncision
steps:
- SurgeryStepExposeNerves
- SurgeryStepExposeBloodVessels
- SurgeryLimbAttachmentStep
- SurgeryStepRejoinNerves
- SurgeryStepRejoinBloodVessels
- SurgeryStepRestoreCartilage
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: SurgeryLimbSlotCondition
slot: head
- type: Sprite
sprite: Objects/Specific/Medical/medical.rsi
state: medicated-suture
- type: entity
parent: SurgeryLimbAttachmentHead
id: SurgeryLimbAttachmentLeftArm
name: Limb Attachment Left Arm
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: left arm
- type: entity
parent: SurgeryLimbAttachmentHead
id: SurgeryLimbAttachmentLeftHand
name: Limb Attachment Left Hand
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: left hand
- type: entity
parent: SurgeryLimbAttachmentHead
id: SurgeryLimbAttachmentRightArm
name: Limb Attachment Right Arm
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: right arm
- type: entity
parent: SurgeryLimbAttachmentHead
id: SurgeryLimbAttachmentRightHand
name: Limb Attachment Right Hand
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: right hand
- type: entity
parent: SurgeryLimbAttachmentHead
id: SurgeryLimbAttachmentLeftLeg
name: Limb Attachment Left Leg
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: left leg
- type: entity
parent: SurgeryLimbAttachmentHead
id: SurgeryLimbAttachmentLeftFoot
name: Limb Attachment Left Foot
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: left foot
- type: entity
parent: SurgeryLimbAttachmentHead
id: SurgeryLimbAttachmentRightLeg
name: Limb Attachment Right Leg
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: right leg
- type: entity
parent: SurgeryLimbAttachmentHead
id: SurgeryLimbAttachmentRightFoot
name: Limb Attachment Right Foot
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: right foot
- type: entity
parent: SurgeryLimbAttachmentHead
id: SurgeryLimbAttachmentTail
name: Limb Attachment Tail
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: tail
- type: entity
parent: SurgeryLimbAttachmentHead
id: SurgeryLimbAttachmentOther
name: Limb Attachment Other
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: other
#SlimePerson
- type: entity
parent: SurgeryLimbAttachmentHead
id: SurgeryLimbAttachmentSlimeHead
components:
- type: Surgery
requirement:
- SurgeryOpenIncisionSlime
steps:
- SurgeryStepDeepIncision
- SurgeryLimbAttachmentStep
- SurgeryStepRestoreCartilage
- type: SurgerySpeciesCondition
speciesBlacklist: []
speciesWhitelist:
- SlimePerson
- type: entity
parent: SurgeryLimbAttachmentSlimeHead
id: SurgeryLimbAttachmentSlimeLeftArm
name: Limb Attachment Left Arm
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: left arm
- type: entity
parent: SurgeryLimbAttachmentSlimeHead
id: SurgeryLimbAttachmentSlimeLeftHand
name: Limb Attachment Left Hand
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: left hand
- type: entity
parent: SurgeryLimbAttachmentSlimeHead
id: SurgeryLimbAttachmentSlimeRightArm
name: Limb Attachment Right Arm
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: right arm
- type: entity
parent: SurgeryLimbAttachmentSlimeHead
id: SurgeryLimbAttachmentSlimeRightHand
name: Limb Attachment Right Hand
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: right hand
- type: entity
parent: SurgeryLimbAttachmentSlimeHead
id: SurgeryLimbAttachmentSlimeLeftLeg
name: Limb Attachment Left Leg
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: left leg
- type: entity
parent: SurgeryLimbAttachmentSlimeHead
id: SurgeryLimbAttachmentSlimeLeftFoot
name: Limb Attachment Left Foot
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: left foot
- type: entity
parent: SurgeryLimbAttachmentSlimeHead
id: SurgeryLimbAttachmentSlimeRightLeg
name: Limb Attachment Right Leg
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: right leg
- type: entity
parent: SurgeryLimbAttachmentSlimeHead
id: SurgeryLimbAttachmentSlimeRightFoot
name: Limb Attachment Right Foot
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: right foot
- type: entity
parent: SurgeryLimbAttachmentSlimeHead
id: SurgeryLimbAttachmentSlimeTail
name: Limb Attachment Tail
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: tail
- type: entity
parent: SurgeryLimbAttachmentSlimeHead
id: SurgeryLimbAttachmentSlimeOther
name: Limb Attachment Other
description: Surgical attachment of a limb.
components:
- type: SurgeryLimbSlotCondition
slot: other

View file

@ -0,0 +1,72 @@
- type: entity
parent: SurgeryStepBase
id: SurgeryStepExposeNerves
name: Expose Nerves
components:
- type: SurgeryStep
duration: 4
tools:
- type: Scalpel
- type: Sprite
sprite: Objects/Specific/Medical/Surgery/scalpel.rsi
state: scalpel
- type: SurgeryStepEmoteEffect
- type: entity
parent: SurgeryStepBase
id: SurgeryStepExposeBloodVessels
name: Expose Blood Vessels
components:
- type: SurgeryStep
duration: 4
tools:
- type: Scalpel
- type: Sprite
sprite: Objects/Specific/Medical/Surgery/scalpel.rsi
state: scalpel
- type: SurgeryStepEmoteEffect
- type: entity
parent: SurgeryStepBase
id: SurgeryStepDeepIncision
name: Deep Incision
components:
- type: SurgeryStep
duration: 5
tools:
- type: Scalpel
- type: Sprite
sprite: Objects/Specific/Medical/Surgery/scalpel.rsi
state: scalpel
- type: SurgeryStepEmoteEffect
- type: entity
parent: SurgeryStepBase
id: SurgeryStepRetractSkin
name: Retract the skin
components:
- type: SurgeryStep
tools:
- type: Retractor
add:
- type: SkinRetracted
- type: Sprite
sprite: Objects/Specific/Medical/Surgery/scissors.rsi
state: retractor
- type: entity
parent: SurgeryStepRetractSkin
id: SurgeryStepRetractMembrane
name: Retract the membrane
- type: entity
parent: SurgeryStepBase
id: SurgeryStepCoagulateJellyFlow
name: Coagulate Jelly Flow
components:
- type: SurgeryStep
tools:
- type: Hemostat
- type: Sprite
sprite: Objects/Specific/Medical/Surgery/scissors.rsi
state: hemostat

View file

@ -574,7 +574,7 @@
state: hemostat
# kidneys
- type: entity
parent: SurgeryStepBase
id: SurgeryStepPrepareImplantSiteKidneys
@ -619,7 +619,7 @@
state: hemostat
# stomach
- type: entity
parent: SurgeryStepBase
id: SurgeryStepPrepareImplantSiteStomach
@ -662,7 +662,7 @@
state: hemostat
# lungs
- type: entity
parent: SurgeryStepBase
id: SurgeryStepPrepareImplantSiteLungs
@ -705,7 +705,7 @@
state: hemostat
# heart
- type: entity
parent: SurgeryStepBase
id: SurgeryStepPrepareImplantSiteHeart
@ -984,4 +984,62 @@
state: cautery
- type: SurgeryClearProgress
# Cavity
- type: entity
parent: SurgeryStepBase
id: SurgeryStepLocateItemCavity
name: Locate Item
components:
- type: SurgeryStep
duration: 3
- type: Sprite
sprite: Mobs/Species/Human/parts.rsi
state: torso_m
- type: entity
parent: SurgeryStepBase
id: SurgeryStepRemoveItemCavity
name: Remove Item
components:
- type: SurgeryStep
duration: 6
tools:
- type: Scalpel
- type: Sprite
sprite: Mobs/Species/Human/parts.rsi
state: torso_m
- type: SurgeryStepEmoteEffect
- type: SurgeryStepOrganExtract
organ:
- type: Item
slot: cavity
- type: entity
parent: SurgeryStepBase
id: SurgeryStepPrepareImplantSiteCavity
name: Prepare Implant Site
components:
- type: SurgeryStep
duration: 4
tools:
- type: Scalpel
- type: Sprite
sprite: Objects/Specific/Medical/Surgery/scalpel.rsi
state: scalpel
- type: entity
parent: SurgeryStepBase
id: SurgeryStepInsertItemCavity
name: Insert Item
components:
- type: SurgeryStep
duration: 3
tools:
- type: Item
- type: Sprite
sprite: Mobs/Species/Human/parts.rsi
state: torso_m
- type: SurgeryItemSizeCondition
- type: SurgeryStepOrganInsert
slot: cavity

View file

@ -14,6 +14,24 @@
- SurgeryStepOpenIncisionScalpel
- SurgeryStepClampBleeders
- SurgeryStepRetractSkin
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: entity
parent: SurgeryOpenIncision
id: SurgeryOpenIncisionSlime
components:
- type: Surgery
priority: -100
steps:
- SurgeryStepOpenIncisionScalpel
- SurgeryStepCoagulateJellyFlow
- SurgeryStepRetractMembrane
- type: SurgerySpeciesCondition
speciesBlacklist: []
speciesWhitelist:
- SlimePerson
- type: entity
parent: SurgeryBase
@ -21,12 +39,32 @@
name: Close Incision
components:
- type: Surgery
requirement: SurgeryOpenIncision
requirement:
- SurgeryOpenIncision
priority: 100
steps:
- SurgeryStepCloseBones
- SurgeryStepMendRibcage
- SurgeryStepCloseIncision
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: entity
parent: SurgeryCloseIncision
id: SurgeryCloseIncisionSlime
components:
- type: Surgery
requirement:
- SurgeryOpenIncisionSlime
priority: 100
steps:
- SurgeryStepMendRibcage
- SurgeryStepCloseIncision
- type: SurgerySpeciesCondition
speciesBlacklist: []
speciesWhitelist:
- SlimePerson
- type: entity
parent: SurgeryBase
@ -34,25 +72,31 @@
name: Open Ribcage
components:
- type: Surgery
requirement: SurgeryOpenIncision
requirement:
- SurgeryOpenIncision
priority: -50
steps:
- SurgeryStepSawBones
- SurgeryStepPriseOpenBones
- type: SurgeryPartCondition
parts:
parts:
- Torso
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: Sprite
sprite: Mobs/Species/Human/parts.rsi
sprite: Mobs/Species/Skeleton/parts.rsi
state: torso_m
- type: entity
parent: SurgeryBase
id: SurgeryOpenAbdomen
name: Open Abdomen
components:
- type: Surgery
requirement: SurgeryOpenIncision
requirement:
- SurgeryOpenIncision
- SurgeryOpenIncisionSlime
priority: -50
steps:
- SurgeryStepCutAbdominalMuscles
@ -61,71 +105,31 @@
parts:
- Torso
- type: Sprite
sprite: Mobs/Species/Skeleton/parts.rsi
sprite: Mobs/Species/Human/parts.rsi
state: torso_m
- type: entity
parent: SurgeryBase
id: SurgeryEliminateVocalCordDefects
name: Eliminate vocal cord defects.
name: Eliminate vocal cord defects
description: Remove speech defects, accents.
components:
- type: Surgery
requirement: SurgeryOpenIncision
requirement:
- SurgeryOpenIncision
- SurgeryOpenIncisionSlime
steps:
- SurgeryStepExposeVocalCords
- SurgeryStepAdjustVocalCords
- SurgeryStepSutureIncision
- type: SurgeryAnyAccentCondition
- type: SurgeryPartCondition
parts:
parts:
- Head
- type: Sprite
sprite: Objects/Fun/Instruments/microphone.rsi
state: icon
- type: entity
parent: SurgeryBase
id: SurgeryAmputation
name: Amputation
description: Surgical removal of a limb.
components:
- type: Surgery
requirement: SurgeryOpenIncision
steps:
- SurgeryStepExposeNerves
- SurgeryStepExposeBloodVessels
- SurgeryAmputationStep
- type: SurgeryPartCondition
parts:
- Arm
- Hand
- Leg
- Foot
- Tail
- type: Sprite
sprite: Objects/Specific/Medical/Surgery/saw.rsi
state: saw
- type: entity
parent: SurgeryBase
id: SurgeryLimbAttachment
name: Limb Attachment
description: Surgical attachment of a limb.
components:
- type: Surgery
requirement: SurgeryOpenIncision
steps:
- SurgeryStepExposeNerves
- SurgeryStepExposeBloodVessels
- SurgeryLimbAttachmentStep
- SurgeryStepRejoinNerves
- SurgeryStepRejoinBloodVessels
- SurgeryStepRestoreCartilage
- type: SurgeryAnyLimbSlotCondition
- type: Sprite
sprite: Objects/Specific/Medical/medical.rsi
state: medicated-suture
#organs
- type: entity
@ -134,11 +138,15 @@
name: Extract Liver
components:
- type: Surgery
requirement: SurgeryOpenAbdomen
requirement:
- SurgeryOpenAbdomen
steps:
- SurgeryStepLocateLiver
- SurgeryStepClampLiverVessels
- SurgeryStepRemoveLiver
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
@ -148,20 +156,24 @@
- type: Sprite
sprite: Mobs/Species/Human/organs.rsi
state: liver
- type: entity
parent: SurgeryBase
id: SurgeryImplantLiver
name: Implant Liver
components:
- type: Surgery
requirement: SurgeryOpenAbdomen
requirement:
- SurgeryOpenAbdomen
steps:
- SurgeryStepPrepareImplantSiteLiver
- SurgeryStepInsertLiver
- SurgeryStepConnectLiverVessels
- SurgeryStepRestoreAbdominalWalls
- SurgeryStepSutureMuscles
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
@ -178,11 +190,15 @@
name: Extract Appendix
components:
- type: Surgery
requirement: SurgeryOpenAbdomen
requirement:
- SurgeryOpenAbdomen
steps:
- SurgeryStepLocateAppendix
- SurgeryStepClampAppendix
- SurgeryStepRemoveAppendix
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
@ -199,11 +215,15 @@
name: Extract Kidneys
components:
- type: Surgery
requirement: SurgeryOpenAbdomen
requirement:
- SurgeryOpenAbdomen
steps:
- SurgeryStepLocateKidneys
- SurgeryStepClampKidneysVessels
- SurgeryStepRemoveKidneys
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
@ -222,13 +242,17 @@
name: Implant Kidneys
components:
- type: Surgery
requirement: SurgeryOpenAbdomen
requirement:
- SurgeryOpenAbdomen
steps:
- SurgeryStepPrepareImplantSiteKidneys
- SurgeryStepInsertKidneys
- SurgeryStepConnectKidneysVessels
- SurgeryStepRestoreAbdominalWalls
- SurgeryStepSutureMuscles
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
@ -247,11 +271,15 @@
name: Extract Stomach
components:
- type: Surgery
requirement: SurgeryOpenAbdomen
requirement:
- SurgeryOpenAbdomen
steps:
- SurgeryStepLocateStomach
- SurgeryStepClampStomachVessels
- SurgeryStepRemoveStomach
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
@ -268,13 +296,17 @@
name: Implant Stomach
components:
- type: Surgery
requirement: SurgeryOpenAbdomen
requirement:
- SurgeryOpenAbdomen
steps:
- SurgeryStepPrepareImplantSiteStomach
- SurgeryStepInsertStomach
- SurgeryStepConnectStomachVessels
- SurgeryStepRestoreAbdominalWalls
- SurgeryStepSutureMuscles
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
@ -291,11 +323,15 @@
name: Extract Lungs
components:
- type: Surgery
requirement: SurgeryOpenRibcage
requirement:
- SurgeryOpenRibcage
steps:
- SurgeryStepLocateLungs
- SurgeryStepClampLungVessels
- SurgeryStepRemoveLungs
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
@ -314,11 +350,71 @@
name: Implant Lungs
components:
- type: Surgery
requirement: SurgeryOpenRibcage
requirement:
- SurgeryOpenRibcage
steps:
- SurgeryStepPrepareImplantSiteLungs
- SurgeryStepInsertLungs
- SurgeryStepConnectLungVessels
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
- type: SurgeryOrganDontExistCondition
organ:
- type: OrganLungs
- type: Sprite
sprite: Mobs/Species/Human/organs.rsi
layers:
- state: lung-l
- state: lung-r
- type: entity
parent: SurgeryBase
id: SurgeryExtractLungsSlime
name: Extract Lungs
components:
- type: Surgery
requirement:
- SurgeryOpenAbdomen
steps:
- SurgeryStepLocateLungs
- SurgeryStepClampLungVessels
- SurgeryStepRemoveLungs
- type: SurgerySpeciesCondition
speciesBlacklist: []
speciesWhitelist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
- type: SurgeryOrganExistCondition
organ:
- type: OrganLungs
- type: Sprite
sprite: Mobs/Species/Human/organs.rsi
layers:
- state: lung-l
- state: lung-r
- type: entity
parent: SurgeryBase
id: SurgeryImplantLungsSlime
name: Implant Lungs
components:
- type: Surgery
requirement:
- SurgeryOpenAbdomen
steps:
- SurgeryStepPrepareImplantSiteLungs
- SurgeryStepInsertLungs
- SurgeryStepConnectLungVessels
- type: SurgerySpeciesCondition
speciesBlacklist: []
speciesWhitelist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
@ -337,11 +433,15 @@
name: Extract Heart
components:
- type: Surgery
requirement: SurgeryOpenRibcage
requirement:
- SurgeryOpenRibcage
steps:
- SurgeryStepLocateHeart
- SurgeryStepClampHeartVessels
- SurgeryStepRemoveHeart
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
@ -358,11 +458,15 @@
name: Implant Heart
components:
- type: Surgery
requirement: SurgeryOpenRibcage
requirement:
- SurgeryOpenRibcage
steps:
- SurgeryStepPrepareImplantSiteHeart
- SurgeryStepInsertHeart
- SurgeryStepConnectHeartVessels
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
@ -373,13 +477,65 @@
sprite: Mobs/Species/Human/organs.rsi
state: heart-on
#
- type: entity
parent: SurgeryExtractHeart
id: SurgeryExtractHeartSlime
components:
- type: Surgery
requirement:
- SurgeryOpenAbdomen
steps:
- SurgeryStepLocateHeart
- SurgeryStepClampHeartVessels
- SurgeryStepRemoveHeart
- type: SurgerySpeciesCondition
speciesBlacklist: []
speciesWhitelist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
- type: SurgeryOrganExistCondition
organ:
- type: OrganHeart
- type: Sprite
sprite: _Sunrise/Mobs/Species/Slime/organs.rsi
state: heart-on
- type: entity
parent: SurgeryImplantHeart
id: SurgeryImplantHeartSlime
components:
- type: Surgery
requirement:
- SurgeryOpenAbdomen
steps:
- SurgeryStepPrepareImplantSiteHeart
- SurgeryStepInsertHeart
- SurgeryStepConnectHeartVessels
- type: SurgerySpeciesCondition
speciesBlacklist: []
speciesWhitelist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
- type: SurgeryOrganDontExistCondition
organ:
- type: OrganHeart
- type: Sprite
sprite: _Sunrise/Mobs/Species/Slime/organs.rsi
state: heart-on
#
- type: entity
parent: SurgeryBase
id: SurgeryExtractEyes
name: Extract Eyes
components:
- type: Surgery
requirement: SurgeryOpenIncision
requirement:
- SurgeryOpenIncision
- SurgeryOpenIncisionSlime
steps:
- SurgeryStepLocateEyes
- SurgeryStepClampOpticNerve
@ -402,7 +558,9 @@
name: Implant Eyes
components:
- type: Surgery
requirement: SurgeryOpenIncision
requirement:
- SurgeryOpenIncision
- SurgeryOpenIncisionSlime
steps:
- SurgeryStepPrepareImplantSiteEyes
- SurgeryStepInsertEyes
@ -464,7 +622,8 @@
name: Extract Brain
components:
- type: Surgery
requirement: SurgeryOpenIncision
requirement:
- SurgeryOpenIncision
steps:
- SurgeryStepPreparePatient
- SurgeryStepShaveHead
@ -479,6 +638,9 @@
- SurgeryStepRetractDuraMater
- SurgeryStepSeverCranialNerves
- SurgeryStepExtractBrain
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Head
@ -495,7 +657,8 @@
name: Implant Brain
components:
- type: Surgery
requirement: SurgeryOpenIncision
requirement:
- SurgeryOpenIncision
steps:
- SurgeryStepCleanImplantSite
- SurgeryStepPrepareScalp
@ -515,6 +678,9 @@
- SurgeryStepSecureBoneFlap
- SurgeryStepReplaceScalp
- SurgeryStepSutureScalp
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Head
@ -524,3 +690,112 @@
- type: Sprite
sprite: Mobs/Species/Human/organs.rsi
state: brain
#
- type: entity
parent: SurgeryBase
id: SurgeryExtractCore
name: Extract Core
components:
- type: Surgery
requirement:
- SurgeryOpenAbdomen
steps:
- SurgeryStepPreparePatient
- SurgeryStepDisinfectScalp
- SurgeryStepMakeIncisionScalp
- SurgeryStepRetractScalp
- SurgeryStepExtractBrain
- type: SurgerySpeciesCondition
speciesBlacklist: []
speciesWhitelist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
- type: SurgeryOrganExistCondition
organ:
- type: OrganBrain
- type: Sprite
sprite: Mobs/Species/Slime/organs.rsi
state: brain-slime
- type: entity
parent: SurgeryBase
id: SurgeryImplantCore
name: Implant Core
components:
- type: Surgery
requirement:
- SurgeryOpenAbdomen
steps:
- SurgeryStepCleanImplantSite
- SurgeryStepPrepareScalp
- SurgeryStepMakeIncisionScalp
- SurgeryStepRetractScalp
- SurgeryStepInsertBrain
- SurgeryStepReplaceScalp
- SurgeryStepSutureScalp
- type: SurgerySpeciesCondition
speciesBlacklist: []
speciesWhitelist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
- type: SurgeryOrganDontExistCondition
organ:
- type: OrganBrain
- type: Sprite
sprite: Mobs/Species/Slime/organs.rsi
state: brain-slime
#
- type: entity
parent: SurgeryBase
id: SurgeryExtractCavityItem
name: Extract Item
components:
- type: Surgery
requirement:
- SurgeryOpenAbdomen
steps:
- SurgeryStepLocateItemCavity
- SurgeryStepRemoveItemCavity
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
- type: SurgeryOrganExistCondition
organ:
- type: Item
container: cavity
- type: Sprite
sprite: Mobs/Species/Human/parts.rsi
state: torso_m
- type: entity
parent: SurgeryBase
id: SurgeryImplantCavityItem
name: Implant Item
components:
- type: Surgery
requirement:
- SurgeryOpenAbdomen
steps:
- SurgeryStepPrepareImplantSiteCavity
- SurgeryStepInsertItemCavity
- type: SurgerySpeciesCondition
speciesBlacklist:
- SlimePerson
- type: SurgeryPartCondition
parts:
- Torso
- type: SurgeryOrganDontExistCondition
organ:
- type: Item
container: cavity
- type: Sprite
sprite: Mobs/Species/Human/parts.rsi
state: torso_m

View file

@ -39,20 +39,6 @@
state: hemostat
- type: SurgeryClampBleedEffect
- type: entity
parent: SurgeryStepBase
id: SurgeryStepRetractSkin
name: Retract the skin
components:
- type: SurgeryStep
tools:
- type: Retractor
add:
- type: SkinRetracted
- type: Sprite
sprite: Objects/Specific/Medical/Surgery/scissors.rsi
state: retractor
- type: entity
parent: SurgeryStepBase
id: SurgeryStepSawBones

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

View file

@ -0,0 +1,46 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "starlight",
"size": {
"x": 32,
"y": 32
},
"load": {
"srgb": false
},
"states": [
{
"name": "l_arm",
"directions": 4
},
{
"name": "r_arm",
"directions": 4
},
{
"name": "l_foot",
"directions": 4
},
{
"name": "r_foot",
"directions": 4
},
{
"name": "l_leg",
"directions": 4
},
{
"name": "r_leg",
"directions": 4
},
{
"name": "l_hand",
"directions": 4
},
{
"name": "r_hand",
"directions": 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

View file

@ -112,6 +112,10 @@
"name": "r_foot",
"directions": 4
},
{
"name": "eyes",
"directions": 4
},
{
"name": "r_hand",
"directions": 4,

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 891 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -0,0 +1,32 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Sprited by Nimfar11 (Github) for Space Station 14, inhands by mubururu_ (github)",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "heart-off"
},
{
"name": "heart-on",
"delays": [
[
0.6,
0.1,
0.1
]
]
},
{
"name": "heart-inhand-left",
"directions": 4
},
{
"name": "heart-inhand-right",
"directions": 4
}
]
}