Skip to content

Commit

Permalink
Cloning Refactor (#735)
Browse files Browse the repository at this point in the history
# Description

Since Cloning code is effectively abandonware by it's original
codeowners, and I was the last person in this entire game to update it,
I am technically the codeowner of Cloning. And by extension, it's also
my responsibility to maintain the Cloning code. I've been putting this
off for awhile due to how busy I've been with other projects, but since
I'm now waiting on all my other refactors to be reviewed, I decided to
finally sit down and comprehensively refactor Cloning.

In addition to massive substantial code cleanup(Cloning machines no
longer run on Frametime for one!), here's most of the changes.

- Cloning Pods must be powered for the entire 30 second duration of the
cloning process.
- Said "30 second duration" is no longer hardcoded. Although no methods
currently exist to reduce it. I plan on revisiting this after I bring
back Machine Upgrading.
- Cloning can now FAIL partway through. If the cloning pod is Depowered,
Unanchored, or Emagged, it will automatically swap to the "Gore" state.
- When in a Gore state, Cloning Pods will destroy the entity they were
trying to clone, replacing them with a pool of blood and ammonia that
scales with the mass of the entity that was to be cloned!
- Clones come out of the pod with a significant quantity of Cellular
damage, and are almost always in need of resuscitation. Consider using
Cryogenics to "Finish" your clones. Doxarubixadone is literally named
after this process, and is a perfectly suitable cryo chem for
resuscitating clones.

<details><summary><h1>Media</h1></summary>
<p>

New gore sprites for the Metem machine, because it can now have gore
mode.
![Metem gore
spites](https://github.com/user-attachments/assets/7cc06ce2-c8eb-413c-b996-85e555b67db3)


</p>
</details>

# Changelog

:cl:
- add: Cloning & Metempsychosis Machines have been refactored!
- add: Cloning can now fail at any point during the cloning process,
turning the would-be clone into a soup of blood and ammonia.
- add: "Clone Soup" scales directly with the mass of the entity you're
attempting to clone. Fail to clone a Lamia, and you'll be greeted with
an Olympic swimming pool worth of blood when the machine opens.
- add: Cloning will fail if at any point during the procedure, the
machine is depowered, unanchored, or emagged.
- add: Clones come out of the machine with severe Cellular damage.
Consider using Doxarubixadone in a Cryo tube as an affordable means of
"Finishing" clones.
- tweak: Cloning Time is now increased proportionally if an entity being
cloned is larger than a standard human(smaller entities are unchanged)
- tweak: The cost to clone an entity can now be configured on a
per-server basis via CCVar "cloning.biomass_cost_multiplier"
- tweak: The Biomass Reclaimer can now be toggled to round-remove
ensouled bodies or not via CCVar "cloning.reclaim_souled_bodies"
- add: The effects of Metempsychosis now scale with a Psion's relevant
caster stats. More powerful psychics are more likely to get favorable
results from being forcibly reincarnated.

---------

Signed-off-by: VMSolidus <[email protected]>
Co-authored-by: Pspritechologist <[email protected]>
Co-authored-by: Danger Revolution! <[email protected]>
Co-authored-by: DEATHB4DEFEAT <[email protected]>
  • Loading branch information
4 people authored Sep 2, 2024
1 parent 52b2e3f commit 071389e
Show file tree
Hide file tree
Showing 26 changed files with 1,043 additions and 756 deletions.

This file was deleted.

90 changes: 44 additions & 46 deletions Content.Server/Cloning/CloningConsoleSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public sealed class CloningConsoleSystem : EntitySystem
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
[Dependency] private readonly PowerReceiverSystem _powerReceiverSystem = default!;
[Dependency] private readonly SharedMindSystem _mindSystem = default!;

public override void Initialize()
{
base.Initialize();
Expand All @@ -52,14 +52,16 @@ private void OnInit(EntityUid uid, CloningConsoleComponent component, ComponentI
}
private void OnButtonPressed(EntityUid uid, CloningConsoleComponent consoleComponent, UiButtonPressedMessage args)
{
if (!_powerReceiverSystem.IsPowered(uid))
if (!_powerReceiverSystem.IsPowered(uid)
|| consoleComponent.GeneticScanner is null
|| consoleComponent.CloningPod is null
|| !TryComp<CloningPodComponent>(consoleComponent.CloningPod.Value, out var cloningPod))
return;

switch (args.Button)
{
case UiButton.Clone:
if (consoleComponent.GeneticScanner != null && consoleComponent.CloningPod != null)
TryClone(uid, consoleComponent.CloningPod.Value, consoleComponent.GeneticScanner.Value, consoleComponent: consoleComponent);
TryClone(uid, consoleComponent.CloningPod.Value, consoleComponent.GeneticScanner.Value, cloningPod, consoleComponent: consoleComponent);
break;
}
UpdateUserInterface(uid, consoleComponent);
Expand Down Expand Up @@ -93,13 +95,15 @@ private void OnMapInit(EntityUid uid, CloningConsoleComponent component, MapInit

private void OnNewLink(EntityUid uid, CloningConsoleComponent component, NewLinkEvent args)
{
if (TryComp<MedicalScannerComponent>(args.Sink, out var scanner) && args.SourcePort == CloningConsoleComponent.ScannerPort)
if (TryComp<MedicalScannerComponent>(args.Sink, out var scanner)
&& args.SourcePort == CloningConsoleComponent.ScannerPort)
{
component.GeneticScanner = args.Sink;
scanner.ConnectedConsole = uid;
}

if (TryComp<CloningPodComponent>(args.Sink, out var pod) && args.SourcePort == CloningConsoleComponent.PodPort)
if (TryComp<CloningPodComponent>(args.Sink, out var pod)
&& args.SourcePort == CloningConsoleComponent.PodPort)
{
component.CloningPod = args.Sink;
pod.ConnectedConsole = uid;
Expand All @@ -125,11 +129,10 @@ private void OnUIOpen(EntityUid uid, CloningConsoleComponent component, AfterAct

private void OnAnchorChanged(EntityUid uid, CloningConsoleComponent component, ref AnchorStateChangedEvent args)
{
if (args.Anchored)
{
RecheckConnections(uid, component.CloningPod, component.GeneticScanner, component);
if (!args.Anchored
|| !RecheckConnections(uid, component.CloningPod, component.GeneticScanner, component))
return;
}

UpdateUserInterface(uid, component);
}

Expand All @@ -148,49 +151,52 @@ public void UpdateUserInterface(EntityUid consoleUid, CloningConsoleComponent co
_uiSystem.SetUiState(ui, newState);
}

public void TryClone(EntityUid uid, EntityUid cloningPodUid, EntityUid scannerUid, CloningPodComponent? cloningPod = null, MedicalScannerComponent? scannerComp = null, CloningConsoleComponent? consoleComponent = null)
public void TryClone(EntityUid uid, EntityUid cloningPodUid, EntityUid scannerUid, CloningPodComponent cloningPod, MedicalScannerComponent? scannerComp = null, CloningConsoleComponent? consoleComponent = null)
{
if (!Resolve(uid, ref consoleComponent) || !Resolve(cloningPodUid, ref cloningPod) || !Resolve(scannerUid, ref scannerComp))
return;

if (!Transform(cloningPodUid).Anchored || !Transform(scannerUid).Anchored)
return;

if (!consoleComponent.CloningPodInRange || !consoleComponent.GeneticScannerInRange)
if (!Resolve(uid, ref consoleComponent)
|| !Resolve(scannerUid, ref scannerComp)
|| !Transform(cloningPodUid).Anchored
|| !Transform(scannerUid).Anchored
|| !consoleComponent.CloningPodInRange
|| !consoleComponent.GeneticScannerInRange)
return;

var body = scannerComp.BodyContainer.ContainedEntity;

if (body is null)
if (body is null
|| !_mindSystem.TryGetMind(body.Value, out var mindId, out var mind)
|| mind.UserId.HasValue == false
|| mind.Session == null)
return;

if (!_mindSystem.TryGetMind(body.Value, out var mindId, out var mind))
return;

if (mind.UserId.HasValue == false || mind.Session == null)
return;
// Nyano: Adds scannerComp.MetemKarmaBonus
if (_cloningSystem.TryCloning(cloningPodUid, body.Value, (mindId, mind), cloningPod, scannerComp.CloningFailChanceMultiplier, scannerComp.MetemKarmaBonus))
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(uid)} successfully cloned {ToPrettyString(body.Value)}.");
if (_cloningSystem.TryCloning(cloningPodUid, body.Value, (mindId, mind), cloningPod, scannerComp.CloningFailChanceMultiplier))
{
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(uid)} started cloning {ToPrettyString(body.Value)}.");
_cloningSystem.AttemptCloning(cloningPodUid, cloningPod);
}
}

public void RecheckConnections(EntityUid console, EntityUid? cloningPod, EntityUid? scanner, CloningConsoleComponent? consoleComp = null)
public bool RecheckConnections(EntityUid console, EntityUid? cloningPod, EntityUid? scanner, CloningConsoleComponent? consoleComp = null)
{
if (!Resolve(console, ref consoleComp))
return;
return false;

var connected = true;
if (scanner != null)
{
Transform(scanner.Value).Coordinates.TryDistance(EntityManager, Transform((console)).Coordinates, out float scannerDistance);
Transform(scanner.Value).Coordinates.TryDistance(EntityManager, Transform(console).Coordinates, out float scannerDistance);
consoleComp.GeneticScannerInRange = scannerDistance <= consoleComp.MaxDistance;
connected = false;
}
if (cloningPod != null)
{
Transform(cloningPod.Value).Coordinates.TryDistance(EntityManager, Transform((console)).Coordinates, out float podDistance);
Transform(cloningPod.Value).Coordinates.TryDistance(EntityManager, Transform(console).Coordinates, out float podDistance);
consoleComp.CloningPodInRange = podDistance <= consoleComp.MaxDistance;
connected = false;
}

UpdateUserInterface(console, consoleComp);
return connected;
}
private CloningConsoleBoundUserInterfaceState GetUserInterfaceState(CloningConsoleComponent consoleComponent)
{
Expand All @@ -206,25 +212,19 @@ private CloningConsoleBoundUserInterfaceState GetUserInterfaceState(CloningConso
EntityUid? scanBody = scanner.BodyContainer.ContainedEntity;

// GET STATE
if (scanBody == null || !HasComp<MobStateComponent>(scanBody))
if (scanBody == null
|| !HasComp<MobStateComponent>(scanBody))
clonerStatus = ClonerStatus.ScannerEmpty;
else
{
scanBodyInfo = MetaData(scanBody.Value).EntityName;

if (!_mobStateSystem.IsDead(scanBody.Value))
{
clonerStatus = ClonerStatus.ScannerOccupantAlive;
}
else
{
if (!_mindSystem.TryGetMind(scanBody.Value, out _, out var mind) ||
mind.UserId == null ||
!_playerManager.TryGetSessionById(mind.UserId.Value, out _))
{
clonerStatus = ClonerStatus.NoMindDetected;
}
}
else if (!_mindSystem.TryGetMind(scanBody.Value, out _, out var mind)
|| mind.UserId == null
|| !_playerManager.TryGetSessionById(mind.UserId.Value, out _))
clonerStatus = ClonerStatus.NoMindDetected;
}
}

Expand All @@ -240,17 +240,15 @@ private CloningConsoleBoundUserInterfaceState GetUserInterfaceState(CloningConso
EntityUid? cloneBody = clonePod.BodyContainer.ContainedEntity;

clonerMindPresent = clonePod.Status == CloningPodStatus.Cloning;
if (HasComp<ActiveCloningPodComponent>(consoleComponent.CloningPod))
if (clonePod.ActivelyCloning)
{
if (cloneBody != null)
cloneBodyInfo = Identity.Name(cloneBody.Value, EntityManager);
clonerStatus = ClonerStatus.ClonerOccupied;
}
}
else
{
clonerStatus = ClonerStatus.NoClonerDetected;
}

return new CloningConsoleBoundUserInterfaceState(
scanBodyInfo,
Expand Down
Loading

0 comments on commit 071389e

Please sign in to comment.