[Bugfix/Optimization] Metabolize Foreign Blood (#41892)

* Metabolize foreign blood

* fix

* misc

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>
This commit is contained in:
Princess Cheeseballs 2025-12-18 00:48:30 -08:00 committed by BarryNorfolk
parent d50706dd5e
commit fdda156dec
6 changed files with 39 additions and 17 deletions

View File

@ -30,12 +30,12 @@ public sealed class BloodstreamSystem : SharedBloodstreamSystem
bloodSolution.MaxVolume = entity.Comp.BloodReferenceSolution.Volume * entity.Comp.MaxVolumeModifier; bloodSolution.MaxVolume = entity.Comp.BloodReferenceSolution.Volume * entity.Comp.MaxVolumeModifier;
tempSolution.MaxVolume = entity.Comp.BleedPuddleThreshold * 4; // give some leeway, for chemstream as well tempSolution.MaxVolume = entity.Comp.BleedPuddleThreshold * 4; // give some leeway, for chemstream as well
entity.Comp.BloodReferenceSolution.SetReagentData(GetEntityBloodData((entity, entity.Comp)));
// Fill blood solution with BLOOD // Fill blood solution with BLOOD
// The DNA string might not be initialized yet, but the reagent data gets updated in the GenerateDnaEvent subscription // The DNA string might not be initialized yet, but the reagent data gets updated in the GenerateDnaEvent subscription
var solution = entity.Comp.BloodReferenceSolution.Clone(); var solution = entity.Comp.BloodReferenceSolution.Clone();
solution.ScaleTo(entity.Comp.BloodReferenceSolution.Volume - bloodSolution.Volume); solution.ScaleTo(entity.Comp.BloodReferenceSolution.Volume - bloodSolution.Volume);
solution.SetReagentData(GetEntityBloodData(entity.Owner));
bloodSolution.AddSolution(solution, PrototypeManager); bloodSolution.AddSolution(solution, PrototypeManager);
} }
@ -44,11 +44,14 @@ public sealed class BloodstreamSystem : SharedBloodstreamSystem
{ {
if (SolutionContainer.ResolveSolution(entity.Owner, entity.Comp.BloodSolutionName, ref entity.Comp.BloodSolution, out var bloodSolution)) if (SolutionContainer.ResolveSolution(entity.Owner, entity.Comp.BloodSolutionName, ref entity.Comp.BloodSolution, out var bloodSolution))
{ {
var data = NewEntityBloodData(entity);
entity.Comp.BloodReferenceSolution.SetReagentData(data);
foreach (var reagent in bloodSolution.Contents) foreach (var reagent in bloodSolution.Contents)
{ {
List<ReagentData> reagentData = reagent.Reagent.EnsureReagentData(); List<ReagentData> reagentData = reagent.Reagent.EnsureReagentData();
reagentData.RemoveAll(x => x is DnaData); reagentData.RemoveAll(x => x is DnaData);
reagentData.AddRange(GetEntityBloodData(entity.Owner)); reagentData.AddRange(data);
} }
} }
else else

View File

@ -16,7 +16,6 @@ using Content.Shared.EntityEffects.Effects.Body;
using Content.Shared.EntityEffects.Effects.Solution; using Content.Shared.EntityEffects.Effects.Solution;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Content.Shared.Random.Helpers;
using Robust.Shared.Collections; using Robust.Shared.Collections;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
@ -138,8 +137,7 @@ public sealed class MetabolizerSystem : SharedMetabolizerSystem
var list = solution.Contents.ToList(); var list = solution.Contents.ToList();
// Collecting blood reagent for filtering // Collecting blood reagent for filtering
var bloodList = new List<string>(); var ev = new MetabolismExclusionEvent();
var ev = new MetabolismExclusionEvent(bloodList);
RaiseLocalEvent(solutionEntityUid.Value, ref ev); RaiseLocalEvent(solutionEntityUid.Value, ref ev);
// randomize the reagent list so we don't have any weird quirks // randomize the reagent list so we don't have any weird quirks
@ -155,7 +153,7 @@ public sealed class MetabolizerSystem : SharedMetabolizerSystem
continue; continue;
// Skip blood reagents // Skip blood reagents
if (bloodList.Contains(reagent.Prototype)) if (ev.Reagents.Contains(reagent))
continue; continue;
var mostToRemove = FixedPoint2.Zero; var mostToRemove = FixedPoint2.Zero;

View File

@ -1,6 +1,7 @@
using Content.Shared.Alert; using Content.Shared.Alert;
using Content.Shared.Body.Systems; using Content.Shared.Body.Systems;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes; using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
@ -137,9 +138,9 @@ public sealed partial class BloodstreamComponent : Component
// TODO probably damage bleed thresholds. // TODO probably damage bleed thresholds.
/// <summary> /// <summary>
/// Modifier applied to <see cref="BloodReferenceSolution.Volume"/> to determine maximum volume for bloodstream. /// Modifier applied to <see cref="BloodstreamComponent.BloodReferenceSolution.Volume"/> to determine maximum volume for bloodstream.
/// </summary> /// </summary>
[DataField] [DataField, AutoNetworkedField]
public float MaxVolumeModifier = 2f; public float MaxVolumeModifier = 2f;
/// <summary> /// <summary>
@ -151,6 +152,13 @@ public sealed partial class BloodstreamComponent : Component
[DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public Solution BloodReferenceSolution = new([new("Blood", 300)]); public Solution BloodReferenceSolution = new([new("Blood", 300)]);
/// <summary>
/// Caches the blood data of an entity.
/// This is modified by DNA on init so it's not savable.
/// </summary>
[ViewVariables(VVAccess.ReadOnly)]
public List<ReagentData>? BloodData;
/// <summary> /// <summary>
/// Name/Key that <see cref="BloodSolution"/> is indexed by. /// Name/Key that <see cref="BloodSolution"/> is indexed by.
/// </summary> /// </summary>

View File

@ -1,3 +1,5 @@
using Content.Shared.Chemistry.Reagent;
namespace Content.Shared.Body.Events; namespace Content.Shared.Body.Events;
/// <summary> /// <summary>
@ -5,4 +7,7 @@ namespace Content.Shared.Body.Events;
/// blood like reagents for metabolism to skip. /// blood like reagents for metabolism to skip.
/// </summary> /// </summary>
[ByRefEvent] [ByRefEvent]
public readonly record struct MetabolismExclusionEvent(List<string> ReagentList); public readonly record struct MetabolismExclusionEvent()
{
public readonly List<ReagentId> Reagents = [];
}

View File

@ -296,9 +296,9 @@ public abstract class SharedBloodstreamSystem : EntitySystem
private void OnMetabolismExclusion(Entity<BloodstreamComponent> ent, ref MetabolismExclusionEvent args) private void OnMetabolismExclusion(Entity<BloodstreamComponent> ent, ref MetabolismExclusionEvent args)
{ {
// Adding all blood reagents for filtering blood in metabolizer // Adding all blood reagents for filtering blood in metabolizer
foreach (var (reagentId, _) in ent.Comp.BloodReferenceSolution) foreach (var (reagent, _) in ent.Comp.BloodReferenceSolution)
{ {
args.ReagentList.Add(reagentId.Prototype); args.Reagents.Add(reagent);
} }
} }
@ -424,8 +424,7 @@ public abstract class SharedBloodstreamSystem : EntitySystem
if (error > 0) if (error > 0)
{ {
error = FixedPoint2.Min(error, adjustedAmount); error = FixedPoint2.Min(error, adjustedAmount);
var reagentToAdd = new ReagentId(referenceReagent.Prototype, GetEntityBloodData(ent)); bloodSolution.AddReagent(referenceReagent, error);
bloodSolution.AddReagent(reagentToAdd, error);
} }
else if (error < 0) else if (error < 0)
{ {
@ -561,14 +560,24 @@ public abstract class SharedBloodstreamSystem : EntitySystem
var solution = ent.Comp.BloodReferenceSolution.Clone(); var solution = ent.Comp.BloodReferenceSolution.Clone();
solution.ScaleSolution(currentVolume / solution.Volume); solution.ScaleSolution(currentVolume / solution.Volume);
solution.SetReagentData(GetEntityBloodData(ent));
SolutionContainer.AddSolution(ent.Comp.BloodSolution.Value, solution); SolutionContainer.AddSolution(ent.Comp.BloodSolution.Value, solution);
} }
/// <summary> /// <summary>
/// Get the reagent data for blood that a specific entity should have. /// Get the reagent data for blood that a specific entity should have.
/// </summary> /// </summary>
public List<ReagentData> GetEntityBloodData(EntityUid uid) public List<ReagentData> GetEntityBloodData(Entity<BloodstreamComponent?> entity)
{
if (!Resolve(entity, ref entity.Comp))
return NewEntityBloodData(entity);
return entity.Comp.BloodData ?? NewEntityBloodData(entity);
}
/// <summary>
/// Gets new blood data for this entity and caches it in <see cref="BloodstreamComponent.BloodData"/>
/// </summary>
protected List<ReagentData> NewEntityBloodData(EntityUid uid)
{ {
var bloodData = new List<ReagentData>(); var bloodData = new List<ReagentData>();
var dnaData = new DnaData(); var dnaData = new DnaData();
@ -579,7 +588,6 @@ public abstract class SharedBloodstreamSystem : EntitySystem
dnaData.DNA = Loc.GetString("forensics-dna-unknown"); dnaData.DNA = Loc.GetString("forensics-dna-unknown");
bloodData.Add(dnaData); bloodData.Add(dnaData);
return bloodData; return bloodData;
} }
} }

View File

@ -121,7 +121,7 @@ public sealed class VomitSystem : EntitySystem
} }
// Makes a vomit solution the size of 90% of the chemicals removed from the chemstream // Makes a vomit solution the size of 90% of the chemicals removed from the chemstream
solution.AddReagent(new ReagentId(VomitPrototype, _bloodstream.GetEntityBloodData(uid)), vomitAmount); solution.AddReagent(new ReagentId(VomitPrototype, _bloodstream.GetEntityBloodData((uid, bloodStream))), vomitAmount);
} }
if (_puddle.TrySpillAt(uid, solution, out var puddle, false)) if (_puddle.TrySpillAt(uid, solution, out var puddle, false))