using System.Linq; using Content.Server.Body.Systems; using Content.Shared._Sunrise.Medical.Surgery; 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.Body.Components; using Content.Shared.Body.Organ; using Content.Shared.Body.Part; 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; using Robust.Shared.Prototypes; using Content.Shared.Humanoid; using Content.Shared._Sunrise; using Content.Shared.Humanoid.Prototypes; namespace Content.Server._Sunrise.Medical.Surgery; // Based on the RMC14. // https://github.com/RMC-14/RMC-14 public sealed partial class SurgerySystem : SharedSurgerySystem { public void InitializeSteps() { SubscribeLocalEvent(OnStepBleedComplete); SubscribeLocalEvent(OnStepClampBleedComplete); SubscribeLocalEvent(OnStepEmoteEffectComplete); SubscribeLocalEvent(OnStepSpawnComplete); SubscribeLocalEvent(OnStepOrganExtractComplete); SubscribeLocalEvent(OnStepOrganInsertComplete); SubscribeLocalEvent(OnStepAttachLimbComplete); SubscribeLocalEvent(OnStepAmputationComplete); SubscribeLocalEvent(OnRemoveAccent); } private void OnStepBleedComplete(Entity ent, ref SurgeryStepEvent args) { if(ent.Comp.Damage is not null && TryComp(args.Body, out var comp)) _damageableSystem.TryChangeDamage(args.Body, ent.Comp.Damage); //todo add wound } private void OnStepClampBleedComplete(Entity ent, ref SurgeryStepEvent args) { //todo remove wound } private void OnStepOrganInsertComplete(Entity ent, ref SurgeryStepEvent args) { if (args.Tools.Count == 0 || !(args.Tools.FirstOrDefault() is var organId) || !TryComp(args.Part, out var bodyPart) || !TryComp(organId, out var organComp)) return; var part = args.Part; var body = args.Body; _delayAccumulator = 0; _delayQueue.Enqueue(() => { if (_body.InsertOrgan(part, organId, ent.Comp.Slot, bodyPart, organComp) && TryComp(organId, out var organDamageable) && TryComp(body, out var bodyDamageable)) { if (TryComp(organId, out var organEyes) && TryComp(body, out var blindable)) { _blindable.SetMinDamage((body, blindable), organEyes.MinDamage ?? 0); _blindable.AdjustEyeDamage((body, blindable), (organEyes.EyeDamage ?? 0) - blindable.MaxDamage); } if (TryComp(organId, out var organTongue) && !organTongue.IsMuted) RemComp(body); var change = _damageableSystem.TryChangeDamage(body, organDamageable.Damage, true, false, bodyDamageable); if (change is not null) _damageableSystem.TryChangeDamage(organId, change.Invert(), true, false, organDamageable); } }); } private void OnStepOrganExtractComplete(Entity ent, ref SurgeryStepEvent args) { if (ent.Comp.Organ?.Count != 1) return; var organs = _body.GetPartOrgans(args.Part, Comp(args.Part)); var type = ent.Comp.Organ.Values.First().Component.GetType(); foreach (var organ in organs) { if (HasComp(organ.Id, type)) { if (_body.RemoveOrgan(organ.Id, organ.Component) && TryComp(organ.Id, out var damageRule) && damageRule.Damage is not null && TryComp(organ.Id, out var organDamageable) && TryComp(args.Body, out var bodyDamageable)) { if (TryComp(organ.Id, out var organEyes) && TryComp(args.Body, out var blindable)) { organEyes.EyeDamage = blindable.EyeDamage; organEyes.MinDamage = blindable.MinDamage; _blindable.UpdateIsBlind((args.Body, blindable)); } if (TryComp(organ.Id, out var organTongue)) { organTongue.IsMuted = HasComp(args.Body); AddComp(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; } } } private void OnRemoveAccent(Entity ent, ref SurgeryStepEvent args) { foreach (var accent in _accents) if (HasComp(args.Body, accent)) RemCompDeferred(args.Body, accent); } private void OnStepEmoteEffectComplete(Entity ent, ref SurgeryStepEvent args) => _chat.TryEmoteWithChat(args.Body, ent.Comp.Emote); private void OnStepSpawnComplete(Entity ent, ref SurgeryStepEvent args) { if (TryComp(args.Body, out TransformComponent? xform)) SpawnAtPosition(ent.Comp.Entity, xform.Coordinates); } private void OnStepAttachLimbComplete(Entity ent, ref SurgeryStepEvent args) { if (args.Tools.Count == 0 || !(args.Tools.FirstOrDefault() is var limbId) || !TryComp(args.Part, out var bodyPart) || !TryComp(limbId, out var limb)) return; var part = args.Part; var body = args.Body; _delayAccumulator = 0; _delayQueue.Enqueue(() => { var slot = ""; foreach (var slotTemp in _body.TryGetFreePartSlots(part, bodyPart)) { slot = slotTemp; if (_body.AttachPart(part, slot, limbId, bodyPart, limb)) break; } if (TryComp(body, out var humanoid)) //todo move to system { var limbs = _body.GetBodyPartAdjacentParts(limbId, limb).Except([part]).Concat([limbId]); foreach (var partLimbId in limbs) { if (TryComp(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); } } } 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; } }); } private void OnStepAmputationComplete(Entity ent, ref SurgeryStepEvent args) { if (TryComp(args.Body, out TransformComponent? xform) && TryComp(args.Body, out BodyComponent? body) && TryComp(args.Part, out BodyPartComponent? limb)) { if (!_containers.TryGetContainingContainer((args.Part, null, null), out var container)) return; if (_containers.Remove(args.Part, container, destination: xform.Coordinates)) { if (TryComp(args.Body, out var humanoid)) //todo move to system { var limbs = _body.GetBodyPartAdjacentParts(args.Part, limb).Concat([args.Part]); ; foreach (var partLimbId in limbs) { if (TryComp(partLimbId, out var baseLayerStorage) && TryComp(partLimbId, out BodyPartComponent? partLimb)) { var layer = partLimb.ToHumanoidLayers(); if (layer is null) continue; if (humanoid.CustomBaseLayers.TryGetValue(layer.Value, out var customBaseLayer)) baseLayerStorage.Layer = customBaseLayer.Id; else { var speciesProto = _prototypes.Index(humanoid.Species); var baseSprites = _prototypes.Index(speciesProto.SpriteSet); if (baseSprites.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) { if (TryComp(containedEnt, out BodyPartComponent? innerPart) && innerPart.PartType == BodyPartType.Hand) _hands.RemoveHand(args.Body, BodySystem.GetPartSlotContainerId(slotId)); } } 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; } } } } }