Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cryogenics: restrict cryo pods to cryo meds, restore limited metabolism #1533

Merged
merged 7 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 0 additions & 10 deletions Content.Server/Body/Components/MetabolizerComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,6 @@ public sealed partial class MetabolizerComponent : Component
[DataField("maxReagents")]
public int MaxReagentsProcessable = 3;

/// <summary>
/// Frontier
///
/// How many poisons can this metabolizer process at once?
/// Used to nerf 'stacked poisons' where having 5+ different poisons in a syringe, even at low
/// quantity, would be muuuuch better than just one poison acting.
/// </summary>
[DataField("maxPoisons")]
public int MaxPoisonsProcessable = 3;

/// <summary>
/// A list of metabolism groups that this metabolizer will act on, in order of precedence.
/// </summary>
Expand Down
17 changes: 9 additions & 8 deletions Content.Server/Body/Systems/MetabolizerSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ private void TryMetabolize(Entity<MetabolizerComponent, OrganComponent?, Solutio
var list = solution.Contents.ToArray();
_random.Shuffle(list);

int poisons = 0; // frontier modified
int reagents = 0;
foreach (var (reagent, quantity) in list)
{
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype, out var proto))
Expand All @@ -158,10 +158,11 @@ private void TryMetabolize(Entity<MetabolizerComponent, OrganComponent?, Solutio

continue;
}
// frontier modified
// Already processed all poisons, skip to the next reagent.
if (poisons >= ent.Comp1.MaxPoisonsProcessable && proto.Metabolisms.ContainsKey("Poison"))

// Frontier: all cryogenic reagents in the solution should be processed, others should be limited (buff cryo meds)
if (reagents >= ent.Comp1.MaxReagentsProcessable && !proto.Metabolisms.ContainsKey("Cryogenic"))
continue;
// End Frontier


// loop over all our groups and see which ones apply
Expand Down Expand Up @@ -219,10 +220,10 @@ private void TryMetabolize(Entity<MetabolizerComponent, OrganComponent?, Solutio
if (mostToRemove > FixedPoint2.Zero)
{
solution.RemoveReagent(reagent, mostToRemove);
// frontier modified
// We have processed a poison, so count it towards the cap
if (proto.Metabolisms.ContainsKey("Poison"))
poisons++;
// Frontier: do not count cryogenics chems against the reagent limit (to buff cryo meds)
if (!proto.Metabolisms.ContainsKey("Cryogenic"))
reagents++;
// End Frontier
}
}

Expand Down
13 changes: 8 additions & 5 deletions Content.Server/Medical/CryoPodSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ public sealed partial class CryoPodSystem : SharedCryoPodSystem
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly NodeContainerSystem _nodeContainer = default!;

// Frontier: keep a list of cryogenics reagents. The pod will only filter these out from the provided solution.
private static readonly string[] CryogenicsReagents = ["Cryoxadone", "Aloxadone", "Doxarubixadone", "Opporozidone", "Necrosol", "Traumoxadone", "Stelloxadone"];
Comment on lines +58 to +59
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't like this: frankly, it should be searchable by MetabolismGroup without LINQ.

Whenever the reagents get loaded from YAML, you could look through the effects, associate them with the metabolism groups they have effects for, and then access that set in some lifecycle call here to build this array.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would recommend not using a hard coded list. it will make it makes it harder to maintain given somebody might add, re-name, remove, update, something in the future.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is that (as far as I know) there isn't a good, existing way to look it up. I can tell you what this should be - every reagent that can be metabolized as a Cryogenic drug.

Association's done from the individual reagents, and by the effects they have (can't think of an example of association here that isn't 1:1, but it's possibly 1:N off the top of my head). This isn't being stored at all in the MetabolismGroup, so we can't have a nice look up at runtime.


public override void Initialize()
{
base.Initialize();
Expand Down Expand Up @@ -115,14 +118,14 @@ public override void Update(float frameTime)
continue;
}

// frontier

// Frontier
// Filter out a fixed amount of each reagent from the cryo pod's beaker
var solutionToInject = _solutionContainerSystem.SplitSolutionReagentsEvenly(containerSolution.Value, cryoPod.BeakerTransferAmount);
// for every .25 units used, .5 units per second are added to the body, making cryo-pod more efficient than injections
solutionToInject.ScaleSolution(cryoPod.PotencyMultiplier);
var solutionToInject = _solutionContainerSystem.SplitSolutionPerReagentWithOnly(containerSolution.Value, cryoPod.BeakerTransferAmount, CryogenicsReagents);

// End frontier
// For every .25 units used, .5 units per second are added to the body, making cryo-pod more efficient than injections.
solutionToInject.ScaleSolution(cryoPod.PotencyMultiplier);
// End Frontier

_bloodstreamSystem.TryAddToChemicals(patient.Value, solutionToInject, bloodstream);
_reactiveSystem.DoEntityReaction(patient.Value, solutionToInject, ReactionMethod.Injection);
Expand Down
130 changes: 98 additions & 32 deletions Content.Shared/Chemistry/Components/Solution.cs
Original file line number Diff line number Diff line change
Expand Up @@ -606,11 +606,12 @@ public Solution SplitSolutionWithOnly(FixedPoint2 toTake, params string[] includ

return sol;
}

/// <summary>
/// splits the solution taking the specified amount of reagents proportionally to their quantity.
/// Splits a solution, taking the specified amount of reagents proportionally to their quantity.
/// </summary>
/// <param name="toTake">The total amount of solution to remove and return.</param>
/// <returns>a new solution of equal proportions to the original solution</returns>
/// <returns>A new solution of equal proportions to the original.</returns>
public Solution SplitSolution(FixedPoint2 toTake)
{
if (toTake <= FixedPoint2.Zero)
Expand Down Expand Up @@ -674,58 +675,123 @@ public Solution SplitSolution(FixedPoint2 toTake)
return newSolution;
}

// Frontier: cryogenics per-reagent filter function (#1443, #1533)
/// <summary>
/// Frontier
/// splits the solution taking up to the specified amount of each reagent from the solution.
/// If the solution has less of a reagent than the specified amount, it will take all of that reagent.
/// Splits a solution, taking the specified amount of each reagent from the solution.
/// If any reagent in the solution has less volume than specified, it will all be transferred into the new solution.
/// </summary>
/// <param name="toTakePer">How much of each reagent to take</param>
/// <returns>a new solution containing the reagents taken from the original solution</returns>
public Solution SplitSolutionReagentsEvenly(FixedPoint2 toTakePer)
/// <param name="toTakePer">How much of each reagent to take.</param>
/// <returns>A new solution containing the reagents taken from the original solution.</returns>
public Solution SplitSolutionPerReagent(FixedPoint2 toTakePer)
{
var splitSolution = new Solution();

if (toTakePer <= FixedPoint2.Zero)
return splitSolution;
var reagentsCount = Contents.Count;
var reagentsToRemove = new List<ReagentQuantity>();
for (var i = 0; i < reagentsCount; i++)
return new Solution();

var origVol = Volume;
Solution newSolution = new Solution(Contents.Count) { Temperature = Temperature };

for (var i = Contents.Count - 1; i >= 0; i--) // iterate backwards because of remove swap.
{
var currentReagent = Contents[i];
var (reagent, quantity) = Contents[i];

if (currentReagent.Quantity <= FixedPoint2.Zero)
whatston3 marked this conversation as resolved.
Show resolved Hide resolved
// If the reagent has more than enough volume to remove, no need to remove it from the list.
if (quantity > toTakePer)
{
reagentsToRemove.Add(currentReagent);
continue;
Contents[i] = new ReagentQuantity(reagent, quantity - toTakePer);
newSolution.Contents.Add(new ReagentQuantity(reagent, toTakePer));
Volume -= toTakePer;
}
else
{
Contents.RemoveSwap(i);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as I had mentioned in the private messages, it is bad practice to remove from an iterator while iterating threw it.

the argument of "but everywhere else does it" falls in line with "if they jump off a bridge would you"
quality can not be improved if it is ignored for consistently
that's not directed at you, but anybody and everybody who works on code
as developers it is our responsibility to protect quality where we can

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair, but we are also tied with upstream. If you want to move away from the code they have, alright, that's fine, but it must be done with intent. That is not the case here. I would believe that it is easier to maintain a set of solution splitting functions that look and act similarly than four and an odd duck.

//Only add positive quantities to our new solution.
if (quantity > 0)
{
newSolution.Contents.Add(new ReagentQuantity(reagent, quantity));
Volume -= quantity;
}
}
}

// If old solution is empty, invalidate old solution and transfer all volume to new.
if (Volume <= 0)
{
RemoveAllSolution();
newSolution.Volume = origVol;
}
else
{
newSolution.Volume = origVol - Volume;
_heatCapacityDirty = true;
}
newSolution._heatCapacityDirty = true;

ValidateSolution();
newSolution.ValidateSolution();

return newSolution;
}

/// <summary>
/// Splits a solution, taking the specified amount of each reagent specified in reagents from the solution.
/// If any reagent in the solution has less volume than specified, it will all be transferred into the new solution.
/// </summary>
/// <param name="toTakePer">How much of each reagent to take.</param>
/// <returns>A new solution containing the reagents taken from the original solution.</returns>
public Solution SplitSolutionPerReagentWithOnly(FixedPoint2 toTakePer, params string[] reagents)
{
if (toTakePer <= FixedPoint2.Zero)
return new Solution();

var origVol = Volume;
Solution newSolution = new Solution(Contents.Count) { Temperature = Temperature };

for (var i = Contents.Count - 1; i >= 0; i--) // iterate backwards because of remove swap.
{
var (reagent, quantity) = Contents[i];

// Each reagent to split must be in the set given.
if (!reagents.Contains(reagent.Prototype))
continue;

if (currentReagent.Quantity <= toTakePer)
// If the reagent has more than enough volume to remove, no need to remove it from the list.
if (quantity > toTakePer)
{
splitSolution.AddReagent(currentReagent);
reagentsToRemove.Add(currentReagent);
Contents[i] = new ReagentQuantity(reagent, quantity - toTakePer);
newSolution.Contents.Add(new ReagentQuantity(reagent, toTakePer));
Volume -= toTakePer;
}
else
{
splitSolution.AddReagent(currentReagent.Reagent, toTakePer);
RemoveReagent(currentReagent.Reagent, toTakePer);
Contents.RemoveSwap(i);
//Only add positive quantities to our new solution.
if (quantity > 0)
{
newSolution.Contents.Add(new ReagentQuantity(reagent, quantity));
Volume -= quantity;
}
}
}

foreach (var reagent in reagentsToRemove)
// If old solution is empty, invalidate old solution and transfer all volume to new.
if (Volume <= 0)
{
RemoveReagent(reagent);
}
if (Volume == FixedPoint2.Zero)
whatston3 marked this conversation as resolved.
Show resolved Hide resolved
RemoveAllSolution();

_heatCapacityDirty = true;
splitSolution._heatCapacityDirty = true;
newSolution.Volume = origVol;
}
else
{
newSolution.Volume = origVol - Volume;
_heatCapacityDirty = true;
}
newSolution._heatCapacityDirty = true;

ValidateSolution();
splitSolution.ValidateSolution();
newSolution.ValidateSolution();

return splitSolution;
return newSolution;
}
// End Frontier

/// <summary>
/// Variant of <see cref="SplitSolution(FixedPoint2)"/> that doesn't return a new solution containing the removed reagents.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -310,22 +310,40 @@ public Solution SplitSolution(Entity<SolutionComponent> soln, FixedPoint2 quanti
return splitSol;
}

// Frontier: cryogenics filtering functions (#1443)
/// <summary>
/// Frontier
/// Splits a solution removing a specified amount of each reagent, if available.
/// </summary>
/// <param name="soln">The container to split the solution from.</param>
/// <param name="quantity">The amount of each reagent to split.</param>
/// <returns></returns>
public Solution SplitSolutionReagentsEvenly(Entity<SolutionComponent> soln, FixedPoint2 quantity)
/// <returns>The solution that was removed.</returns>
public Solution SplitSolutionPerReagent(Entity<SolutionComponent> soln, FixedPoint2 quantity)
{
var (uid, comp) = soln;
var solution = comp.Solution;

var splitSol = solution.SplitSolutionPerReagent(quantity);
UpdateChemicals(soln);
return splitSol;
}

/// <summary>
/// Splits a solution removing a specified amount of each reagent, if available.
/// </summary>
/// <param name="soln">The container to split the solution from.</param>
/// <param name="quantity">The amount of each reagent to split.</param>
/// <param name="reagents">The list of reagents to split a fixed amount of, if present.</param>
/// <returns>The solution that was removed.</returns>
public Solution SplitSolutionPerReagentWithOnly(Entity<SolutionComponent> soln, FixedPoint2 quantity, params string[] reagents)
{
var (uid, comp) = soln;
var solution = comp.Solution;

var splitSol = solution.SplitSolutionReagentsEvenly(quantity);
var splitSol = solution.SplitSolutionPerReagentWithOnly(quantity, reagents);
UpdateChemicals(soln);
return splitSol;
}
// End Frontier

public Solution SplitStackSolution(Entity<SolutionComponent> soln, FixedPoint2 quantity, int stackCount)
{
Expand Down
5 changes: 3 additions & 2 deletions Content.Shared/Medical/Cryogenics/CryoPodComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,16 @@ public sealed partial class CryoPodComponent : Component
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("beakerTransferAmount")]
public float BeakerTransferAmount = .25f;// Frontier: 1<0.25
public float BeakerTransferAmount = .25f; // Frontier: 1<0.25 (applied per reagent)

// Frontier: more efficient cryogenics (#1443)
/// <summary>
/// Frontier
/// How potent (multiplier) the reagents are when transferred from the beaker to the mob.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("PotencyAmount")]
public float PotencyMultiplier = 2f;
// End Frontier

/// <summary>
/// Delay applied when inserting a mob in the pod.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
metabolism-group-cryogenic = Cryogenic
8 changes: 4 additions & 4 deletions Resources/Locale/en-US/reagents/meta/medicine.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,11 @@ reagent-name-bicaridine = bicaridine
reagent-desc-bicaridine = An analgesic which is highly effective at treating brute damage. It's useful for stabilizing people who have been severely beaten, as well as treating less life-threatening injuries.

# Frontier: consistent cryogenics descriptors

reagent-name-cryoxadone = cryoxadone
reagent-desc-cryoxadone = Required for the proper function of cryogenics. Useful in treating asphyxiation and bloodloss, but only works in temperatures under 213K. It can treat and rejuvenate plants when applied in small doses. Works regardless of the patient being alive or dead.
reagent-desc-cryoxadone = A cryogenics chemical. Useful in treating asphyxiation and bloodloss, but only works in temperatures under 213K. It can treat and rejuvenate plants when applied in small doses. Works regardless of the patient being alive or dead.

reagent-name-doxarubixadone = doxarubixadone
reagent-desc-doxarubixadone = A cryogenics chemical. Heals certain types of cellular damage done by Slimes and improper use of other chemicals. Works regardless of the patient being alive or dead.

# End Frontier

reagent-name-dermaline = dermaline
Expand Down Expand Up @@ -134,8 +132,10 @@ reagent-desc-insuzine = Rapidly repairs dead tissue caused by electrocution, but
reagent-name-opporozidone = opporozidone
reagent-desc-opporozidone= A difficult to synthesize cryogenic drug used to regenerate rotting tissue and brain matter.

# Frontier: consistent cryogenics descriptors
reagent-name-necrosol = necrosol
reagent-desc-necrosol = A necrotic substance that seems to be able to heal frozen corpses. It can treat and rejuvenate plants when applied in small doses.
reagent-desc-necrosol = A cryogenics chemical. Heals most organic damage, a true panacea. It can treat and rejuvenate plants when applied in small doses. Works regardless of the patient being alive or dead.
# End Frontier

reagent-name-aloxadone = aloxadone
reagent-desc-aloxadone = A cryogenics chemical. Used to treat severe third degree burns via regeneration of the burnt tissue. Works regardless of the patient being alive or dead.
Expand Down
1 change: 1 addition & 0 deletions Resources/Prototypes/Body/Organs/Animal/animal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@
metabolizerTypes: [ Animal ]
groups:
- id: Medicine
- id: Cryogenic # Frontier
- id: Poison
- id: Narcotic

Expand Down
1 change: 1 addition & 0 deletions Resources/Prototypes/Body/Organs/Animal/slimes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
- id: Food
- id: Drink
- id: Medicine
- id: Cryogenic # Frontier
- id: Poison
- id: Narcotic
- id: Alcohol
Expand Down
1 change: 1 addition & 0 deletions Resources/Prototypes/Body/Organs/arachnid.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@
metabolizerTypes: [Arachnid]
groups:
- id: Medicine
- id: Cryogenic # Frontier
- id: Poison
- id: Narcotic

Expand Down
1 change: 1 addition & 0 deletions Resources/Prototypes/Body/Organs/diona.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
- id: Food
- id: Drink
- id: Medicine
- id: Cryogenic # Frontier
- id: Poison
- id: Narcotic
- id: Alcohol
Expand Down
1 change: 1 addition & 0 deletions Resources/Prototypes/Body/Organs/human.yml
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@
metabolizerTypes: [Human]
groups:
- id: Medicine
- id: Cryogenic # Frontier
- id: Poison
- id: Narcotic

Expand Down
1 change: 1 addition & 0 deletions Resources/Prototypes/Body/Organs/moth.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
- id: Food
- id: Drink
- id: Medicine
- id: Cryogenic # Frontier
- id: Poison
- id: Narcotic
- id: Alcohol
Loading
Loading