Skip to content

Commit

Permalink
Fix Lobotomy (#1306)
Browse files Browse the repository at this point in the history
# Description

Fixes several bugs in the lobotomy procedure.

The lobotomy effect is now stored in the brain instead of the body, so
transferring a lobotomized brain will still give the lobotomy effects,
and a brain transplant to a body where a lobotomy occurred no longer
applies the lobotomy effects. The "Mend brain tissue" procedure to
reverse a lobotomy has been unlocked after a bug prevented it from
showing in the surgery UI.

Lobotomies now add the `ClumsyComponent`, which makes the lobotomized
target as clumsy as clowns.

## Technical Details

This deletes
[SurgeryComponentConditionComponent.cs](https://github.com/Simple-Station/Einstein-Engines/compare/master...angelofallars:Einstein-Engines:fix-lobotomy?expand=1#diff-3786e2be1879fd877a8b501352bbd92baa3a17aecfa4a62827ad41497deb0fd7)
which was only used for the lobotomy procedures (incorrectly, it was
checking for `OhioAccentComponent` in the body part) in favor of
[SurgeryPartComponentConditionComponent.cs](https://github.com/Simple-Station/Einstein-Engines/compare/master...angelofallars:Einstein-Engines:fix-lobotomy?expand=1#diff-7e180742b3a6f00b9f867d3ee4e8891dd00587dc4a2da8ad5e199180a387d18d)
and
[SurgeryBodyComponentConditionComponent.cs](https://github.com/Simple-Station/Einstein-Engines/compare/master...angelofallars:Einstein-Engines:fix-lobotomy?expand=1#diff-249e5a937ba929ffc76f85e8a43f17918afc9ba866e81f4ea4eba2c90fd0c408).

These two components are currently unused as the lobotomy procedures use
a new condition component checking for the brain's
`OrganComponent.OnAdd` field, but they provide a way to check for
components on the body part and on the body, respectively.

## Media

**Lobotomy**

![image](https://github.com/user-attachments/assets/4deb80a8-30d1-4a01-9caa-bc288a88ba95)

**Mend brain tissue**

![image](https://github.com/user-attachments/assets/44403092-cac1-4d12-bd25-ebb7f3f1bc53)

**Remove organ step picture**


![image](https://github.com/user-attachments/assets/85d6960a-1f54-4525-ad53-84b039c91fda)


## Changelog

<!--
You can add an author after the `:cl:` to change the name that appears
in the changelog (ex: `:cl: Death`)
Leaving it blank will default to your GitHub display name
This includes all available types for the changelog
-->

:cl: Skubman
- add: The lobotomy procedure makes the target clumsy like the clown.
This makes them bonk when climbing tables and makes guns they're
shooting blow up on their face.
- tweak: The lobotomy step now requires a scalpel instead of a drill.
- fix: Enabled the "Mend brain tissue" surgical procedure on a
lobotomized target.
- fix: The lobotomized effect is now stored in the brain instead of the
body. The same brain stays lobotomized throughout brain transplants, and
transferring a normal brain to a body where a lobotomy occurred no
longer applies the lobotomized effect.
- fix: The lobotomy procedure now shows the proper popup during the
lobotomization step.
- fix: Removed the ability to perform lobotomies on bodies without a
brain.
- fix: The "Remove organ" surgery step on the UI now properly shows the
retractor sprite instead of the hemostat.

---------

Co-authored-by: sleepyyapril <[email protected]>
  • Loading branch information
angelofallars and sleepyyapril authored Dec 3, 2024
1 parent 114ecde commit 1895f3a
Show file tree
Hide file tree
Showing 11 changed files with 280 additions and 44 deletions.
5 changes: 3 additions & 2 deletions Content.Shared/Body/Organ/OrganComponent.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
using Content.Shared.Body.Systems;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Content.Shared.Medical.Surgery;
using Content.Shared.Medical.Surgery.Tools;
using Robust.Shared.Prototypes;

namespace Content.Shared.Body.Organ;

[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
[Access(typeof(SharedBodySystem))]
[Access(typeof(SharedBodySystem), typeof(SharedSurgerySystem))]
public sealed partial class OrganComponent : Component, ISurgeryToolComponent
{
/// <summary>
Expand Down Expand Up @@ -50,7 +51,7 @@ public sealed partial class OrganComponent : Component, ISurgeryToolComponent
public ComponentRegistry? OnAdd;

/// <summary>
/// When removed, the organ will ensure these components on the entity, and add them on removal.
/// When removed, the organ will ensure these components on the entity, and delete them on insertion.
/// </summary>
[DataField]
public ComponentRegistry? OnRemove;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Content.Shared.Body.Part;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;

namespace Content.Shared.Medical.Surgery.Conditions;

// <summary>
// What components are necessary in the body for the surgery to be valid.
// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class SurgeryBodyComponentConditionComponent : Component
{
// <summary>
// The components to check for.
// </summary>
[DataField(required: true)]
public ComponentRegistry Components;

// <summary>
// If true, the lack of these components will instead make the surgery valid.
// </summary>
[DataField]
public bool Inverse = false;
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Content.Shared.Body.Part;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;

namespace Content.Shared.Medical.Surgery.Conditions;

// <summary>
// What components are necessary in the part's organs' OnAdd fields for the surgery to be valid.
//
// Not all components need to be present (or missing for Inverse = true). At least one component matching (or missing) can make the surgery valid.
// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class SurgeryOrganOnAddConditionComponent : Component
{
// <summary>
// The components to check for on each organ, with the key being the organ's SlotId.
// </summary>
[DataField(required: true)]
public Dictionary<string, ComponentRegistry> Components;

// <summary>
// If true, the lack of these components will instead make the surgery valid.
// </summary>
[DataField]
public bool Inverse = false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Content.Shared.Body.Part;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;

namespace Content.Shared.Medical.Surgery.Conditions;

// <summary>
// What components are necessary in the targeted body part for the surgery to be valid.
// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class SurgeryPartComponentConditionComponent : Component
{
// <summary>
// The components to check for.
// </summary>
[DataField(required: true)]
public ComponentRegistry Components;

// <summary>
// If true, the lack of these components will instead make the surgery valid.
// </summary>
[DataField]
public bool Inverse = false;
}
72 changes: 72 additions & 0 deletions Content.Shared/Medical/Surgery/SharedSurgerySystem.Steps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Content.Shared.Body.Part;
using Content.Shared.Body.Organ;
using Content.Shared.Body.Events;
using Content.Shared.BodyEffects;
using Content.Shared.Buckle.Components;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.Damage;
Expand Down Expand Up @@ -120,6 +121,45 @@ private void OnToolStep(Entity<SurgeryStepComponent> ent, ref SurgeryStepEvent a
}
}

if (ent.Comp.AddOrganOnAdd != null)
{
var organSlotIdToOrgan = _body.GetPartOrgans(args.Part).ToDictionary(o => o.Item2.SlotId, o => o);

foreach (var (organSlotId, compsToAdd) in ent.Comp.AddOrganOnAdd)
{
if (!organSlotIdToOrgan.TryGetValue(organSlotId, out var organValue))
continue;
var (organId, organ) = organValue;

organ.OnAdd ??= new();

foreach (var (key, compToAdd) in compsToAdd)
organ.OnAdd[key] = compToAdd;

EnsureComp<OrganEffectComponent>(organId);
RaiseLocalEvent(organId, new OrganComponentsModifyEvent(args.Body, true));
}
}

if (ent.Comp.RemoveOrganOnAdd != null)
{
var organSlotIdToOrgan = _body.GetPartOrgans(args.Part).ToDictionary(o => o.Item2.SlotId, o => o);

foreach (var (organSlotId, compsToRemove) in ent.Comp.RemoveOrganOnAdd)
{
if (!organSlotIdToOrgan.TryGetValue(organSlotId, out var organValue) ||
organValue.Item2.OnAdd == null)
continue;
var (organId, organ) = organValue;

// Need to raise this event first before removing the component entries so
// OrganEffectSystem still knows which components on the body to remove
RaiseLocalEvent(organId, new OrganComponentsModifyEvent(args.Body, false));
foreach (var key in compsToRemove.Keys)
organ.OnAdd.Remove(key);
}
}

if (!HasComp<ForcedSleepingComponent>(args.Body))
RaiseLocalEvent(args.Body, new MoodEffectEvent("SurgeryPain"));

Expand Down Expand Up @@ -182,6 +222,38 @@ private void OnToolCheck(Entity<SurgeryStepComponent> ent, ref SurgeryStepComple
}
}
}

if (ent.Comp.AddOrganOnAdd != null)
{
var organSlotIdToOrgan = _body.GetPartOrgans(args.Part).ToDictionary(o => o.Item2.SlotId, o => o.Item2);
foreach (var (organSlotId, compsToAdd) in ent.Comp.AddOrganOnAdd)
{
if (!organSlotIdToOrgan.TryGetValue(organSlotId, out var organ))
continue;

if (organ.OnAdd == null || compsToAdd.Keys.Any(key => !organ.OnAdd.ContainsKey(key)))
{
args.Cancelled = true;
return;
}
}
}

if (ent.Comp.RemoveOrganOnAdd != null)
{
var organSlotIdToOrgan = _body.GetPartOrgans(args.Part).ToDictionary(o => o.Item2.SlotId, o => o.Item2);
foreach (var (organSlotId, compsToRemove) in ent.Comp.RemoveOrganOnAdd)
{
if (!organSlotIdToOrgan.TryGetValue(organSlotId, out var organ) || organ.OnAdd == null)
continue;

if (compsToRemove.Keys.Any(key => organ.OnAdd.ContainsKey(key)))
{
args.Cancelled = true;
return;
}
}
}
}

private void OnToolCanPerform(Entity<SurgeryStepComponent> ent, ref SurgeryCanPerformStepEvent args)
Expand Down
61 changes: 58 additions & 3 deletions Content.Shared/Medical/Surgery/SharedSurgerySystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@ public override void Initialize()
SubscribeLocalEvent<SurgeryTargetComponent, SurgeryDoAfterEvent>(OnTargetDoAfter);
SubscribeLocalEvent<SurgeryCloseIncisionConditionComponent, SurgeryValidEvent>(OnCloseIncisionValid);
//SubscribeLocalEvent<SurgeryLarvaConditionComponent, SurgeryValidEvent>(OnLarvaValid);
SubscribeLocalEvent<SurgeryComponentConditionComponent, SurgeryValidEvent>(OnComponentConditionValid);
SubscribeLocalEvent<SurgeryBodyComponentConditionComponent, SurgeryValidEvent>(OnBodyComponentConditionValid);
SubscribeLocalEvent<SurgeryPartComponentConditionComponent, SurgeryValidEvent>(OnPartComponentConditionValid);
SubscribeLocalEvent<SurgeryPartConditionComponent, SurgeryValidEvent>(OnPartConditionValid);
SubscribeLocalEvent<SurgeryOrganConditionComponent, SurgeryValidEvent>(OnOrganConditionValid);
SubscribeLocalEvent<SurgeryOrganOnAddConditionComponent, SurgeryValidEvent>(OnOrganOnAddConditionValid);
SubscribeLocalEvent<SurgeryWoundedConditionComponent, SurgeryValidEvent>(OnWoundedValid);
SubscribeLocalEvent<SurgeryPartRemovedConditionComponent, SurgeryValidEvent>(OnPartRemovedConditionValid);
SubscribeLocalEvent<SurgeryPartPresentConditionComponent, SurgeryValidEvent>(OnPartPresentConditionValid);
Expand Down Expand Up @@ -129,10 +131,24 @@ private void OnWoundedValid(Entity<SurgeryWoundedConditionComponent> ent, ref Su
args.Cancelled = true;
}*/

private void OnComponentConditionValid(Entity<SurgeryComponentConditionComponent> ent, ref SurgeryValidEvent args)
private void OnBodyComponentConditionValid(Entity<SurgeryBodyComponentConditionComponent> ent, ref SurgeryValidEvent args)
{
var present = true;
foreach (var reg in ent.Comp.Component.Values)
foreach (var reg in ent.Comp.Components.Values)
{
var compType = reg.Component.GetType();
if (!HasComp(args.Body, compType))
present = false;
}

if (ent.Comp.Inverse ? present : !present)
args.Cancelled = true;
}

private void OnPartComponentConditionValid(Entity<SurgeryPartComponentConditionComponent> ent, ref SurgeryValidEvent args)
{
var present = true;
foreach (var reg in ent.Comp.Components.Values)
{
var compType = reg.Component.GetType();
if (!HasComp(args.Part, compType))
Expand Down Expand Up @@ -185,6 +201,45 @@ private void OnOrganConditionValid(Entity<SurgeryOrganConditionComponent> ent, r
}
}

// This is literally a duplicate of the checks in OnToolCheck for SurgeryStepComponent.AddOrganOnAdd
private void OnOrganOnAddConditionValid(Entity<SurgeryOrganOnAddConditionComponent> ent, ref SurgeryValidEvent args)
{
if (!TryComp<BodyPartComponent>(args.Part, out var part)
|| part.Body != args.Body)
{
args.Cancelled = true;
return;
}

var organSlotIdToOrgan = _body.GetPartOrgans(args.Part, part).ToDictionary(o => o.Item2.SlotId, o => o.Item2);

var allOnAddFound = true;
var zeroOnAddFound = true;

foreach (var (organSlotId, components) in ent.Comp.Components)
{
if (!organSlotIdToOrgan.TryGetValue(organSlotId, out var organ))
continue;

if (organ.OnAdd == null)
{
allOnAddFound = false;
continue;
}

foreach (var key in components.Keys)
{
if (!organ.OnAdd.ContainsKey(key))
allOnAddFound = false;
else
zeroOnAddFound = false;
}
}

if (ent.Comp.Inverse ? allOnAddFound : zeroOnAddFound)
args.Cancelled = true;
}

private void OnPartRemovedConditionValid(Entity<SurgeryPartRemovedConditionComponent> ent, ref SurgeryValidEvent args)
{
if (!_body.CanAttachToSlot(args.Part, ent.Comp.Connection))
Expand Down
18 changes: 18 additions & 0 deletions Content.Shared/Medical/Surgery/Steps/SurgeryStepComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,24 @@ public sealed partial class SurgeryStepComponent : Component
[DataField]
public ComponentRegistry? BodyRemove;

/// <summary>
/// These components will be added to the body part's organs' OnAdd field.
/// Each key is the SlotId of the organ to look for.
///
/// Used to make organs add components to whatever body it's residing in.
/// </summary>
[DataField]
public Dictionary<string, ComponentRegistry>? AddOrganOnAdd;

/// <summary>
/// These components will be removed from the body part's organs' OnAdd field.
/// Each key is the SlotId of the organ to look for.
///
/// Used to stop organs from adding components to whatever body it's residing in.
/// </summary>
[DataField]
public Dictionary<string, ComponentRegistry>? RemoveOrganOnAdd;

[DataField]
public float Duration = 2f;
}
3 changes: 3 additions & 0 deletions Resources/Locale/en-US/surgery/surgery-popup.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,6 @@ surgery-popup-step-SurgeryStepInsertHeart = {$user} is inserting a heart into {$
surgery-popup-step-SurgeryStepInsertStomach = {$user} is inserting a stomach into {$target}'s {$part}!
surgery-popup-step-SurgeryStepSealOrganWound = {$user} is sealing the wounds on {$target}'s {$part}.
surgery-popup-step-SurgeryStepLobotomize = {$user} is lobotomizing {$target}!
surgery-popup-step-SurgeryStepMendBrainTissue = {$user} is mending the brain tissue on {$target}'s {$part}.
32 changes: 25 additions & 7 deletions Resources/Prototypes/Entities/Surgery/surgeries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,21 @@
steps:
- SurgeryStepLobotomize
- SurgeryStepCloseIncision
- type: SurgeryComponentCondition
component:
- type: OhioAccent
inverse: true
- type: SurgeryPartCondition
part: Head
- type: SurgeryOrganCondition
organ:
- type: Brain
- type: SurgeryOrganOnAddCondition
components:
brain:
- type: OhioAccent
- type: RatvarianLanguage
- type: Clumsy
clumsyDamage: # Placeholder values to be able to initialize the component
types:
Blunt: 0
inverse: true

- type: entity
parent: SurgeryBase
Expand All @@ -72,11 +81,20 @@
steps:
- SurgeryStepMendBrainTissue
- SurgeryStepCloseIncision
- type: SurgeryComponentCondition
component:
- type: OhioAccent
- type: SurgeryPartCondition
part: Head
- type: SurgeryOrganCondition
organ:
- type: Brain
- type: SurgeryOrganOnAddCondition
components:
brain:
- type: OhioAccent
- type: RatvarianLanguage
- type: Clumsy
clumsyDamage:
types:
Blunt: 0

- type: entity
parent: SurgeryBase
Expand Down
Loading

0 comments on commit 1895f3a

Please sign in to comment.