Metabolizing bloodstream (#35071)

* merged chemical into bloodstream

* changed injectable to bloodstream

* separated bleeding and direct blood removal

* removed blood gain from protein

* reduced blood gain from saline

* rejuvenating fills to reference volume

* fixed blood regulation

* red mead requires stirring to make

* reverted accidental line deletion

* cleared the skeletons from the closet

* additional routing

* field rename for xeno

* removed mention of chemstream and field rename for asteroid mobs

* minor optimizations

* Revert "reduced blood gain from saline"

This reverts commit de26fd1c0d.

* Revert "removed blood gain from protein"

This reverts commit 7a1648caf3.

* removed unused component fetch

* dead check mini refactor

* eventized blood exclusion

* quick fix

* Pain

* Commit of doom

* COMMIT

* renamed bloodMaxFactor to MaxVolumeFactor

* addressed floating point error

* returned vomiting chemicals

* blood reagent always skips the flush

* no need to mention blood reagent

* fixed passing blood flush

* adadsafasfasfassfasf

* whoops

* merge fixed injectors

* Revert "adadsafasfasfassfasf"

This reverts commit 0a5313a68d.

* simplify reagent removal

* enabled foreign blood transfusion

* Revert "COMMIT"

This reverts commit 19abd679cd.

* simplified reagent removal when modifying blood level

* removed misleading coment since the changes

* documented MetabolismExclusionEvent

* fixed negative negative modification of blood level

* fixed hypervolemia not normalizing

* constrainted blood modification

* returned bloodpack stop on fully healed

* forgot to stage this

* band aid for diona blood

* swapping GetReagent with GetPrototype

* optimize blood filtering

* multiplicative multi reagent blood level calculation

* removed unused stuff

* optimized blood calculation a tiny bit

* added per reagent blood regulation

* optimized (referenceVolume + bloodReagents) into referenceSolution

* polished coded to proper function

* forgot to stage rootable system change

* clean up, unnecessary GetBloodLevel call

* rename method name to TryAddToBloodstream instead of Chemicals

* placed overfill safety

* cleanup and final touches

* final touch

---------

Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
This commit is contained in:
Nikovnik
2025-12-17 20:21:16 +01:00
committed by GitHub
parent 4484f0f351
commit 0e76d4e5ed
42 changed files with 362 additions and 267 deletions

View File

@@ -42,7 +42,7 @@ namespace Content.Server.Body.Components
/// From which solution will this metabolizer attempt to metabolize chemicals
/// </summary>
[DataField("solution")]
public string SolutionName = BloodstreamComponent.DefaultChemicalsSolutionName;
public string SolutionName = BloodstreamComponent.DefaultBloodSolutionName;
/// <summary>
/// Does this component use a solution on it's parent entity (the body) or itself

View File

@@ -21,9 +21,6 @@ public sealed class BloodstreamSystem : SharedBloodstreamSystem
private void OnComponentInit(Entity<BloodstreamComponent> entity, ref ComponentInit args)
{
if (!SolutionContainer.EnsureSolution(entity.Owner,
entity.Comp.ChemicalSolutionName,
out var chemicalSolution) ||
!SolutionContainer.EnsureSolution(entity.Owner,
entity.Comp.BloodSolutionName,
out var bloodSolution) ||
!SolutionContainer.EnsureSolution(entity.Owner,
@@ -31,14 +28,13 @@ public sealed class BloodstreamSystem : SharedBloodstreamSystem
out var tempSolution))
return;
chemicalSolution.MaxVolume = entity.Comp.ChemicalMaxVolume;
bloodSolution.MaxVolume = entity.Comp.BloodMaxVolume;
bloodSolution.MaxVolume = entity.Comp.BloodReferenceSolution.Volume * entity.Comp.MaxVolumeModifier;
tempSolution.MaxVolume = entity.Comp.BleedPuddleThreshold * 4; // give some leeway, for chemstream as well
// Fill blood solution with BLOOD
// The DNA string might not be initialized yet, but the reagent data gets updated in the GenerateDnaEvent subscription
var solution = entity.Comp.BloodReagents.Clone();
solution.ScaleTo(entity.Comp.BloodMaxVolume - bloodSolution.Volume);
var solution = entity.Comp.BloodReferenceSolution.Clone();
solution.ScaleTo(entity.Comp.BloodReferenceSolution.Volume - bloodSolution.Volume);
solution.SetReagentData(GetEntityBloodData(entity.Owner));
bloodSolution.AddSolution(solution, PrototypeManager);
}

View File

@@ -1,3 +1,4 @@
using System.Linq;
using Content.Server.Body.Components;
using Content.Shared.Body.Events;
using Content.Shared.Body.Organ;
@@ -14,7 +15,6 @@ using Content.Shared.EntityEffects;
using Content.Shared.EntityEffects.Effects.Body;
using Content.Shared.EntityEffects.Effects.Solution;
using Content.Shared.FixedPoint;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Random.Helpers;
using Robust.Shared.Collections;
@@ -134,17 +134,30 @@ public sealed class MetabolizerSystem : SharedMetabolizerSystem
return;
}
// Copy the solution do not edit the original solution list
var list = solution.Contents.ToList();
// Collecting blood reagent for filtering
var bloodList = new List<string>();
var ev = new MetabolismExclusionEvent(bloodList);
RaiseLocalEvent(solutionEntityUid.Value, ref ev);
// randomize the reagent list so we don't have any weird quirks
// like alphabetical order or insertion order mattering for processing
var list = solution.Contents.ToArray();
_random.Shuffle(list);
bool isDead = _mobStateSystem.IsDead(solutionEntityUid.Value);
int reagents = 0;
foreach (var (reagent, quantity) in list)
{
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype, out var proto))
continue;
// Skip blood reagents
if (bloodList.Contains(reagent.Prototype))
continue;
var mostToRemove = FixedPoint2.Zero;
if (proto.Metabolisms is null)
{
@@ -186,11 +199,8 @@ public sealed class MetabolizerSystem : SharedMetabolizerSystem
// if it's possible for them to be dead, and they are,
// then we shouldn't process any effects, but should probably
// still remove reagents
if (TryComp<MobStateComponent>(solutionEntityUid.Value, out var state))
{
if (!proto.WorksOnTheDead && _mobStateSystem.IsDead(solutionEntityUid.Value, state))
continue;
}
if (isDead && !proto.WorksOnTheDead)
continue;
var actualEntity = ent.Comp2?.Body ?? solutionEntityUid.Value;

View File

@@ -148,7 +148,7 @@ public sealed class SolutionInjectOnCollideSystem : EntitySystem
// Take our portion of the adjusted solution for this target
var individualInjection = solutionToInject.SplitSolution(volumePerBloodstream);
// Inject our portion into the target's bloodstream
if (_bloodstream.TryAddToChemicals(targetBloodstream.AsNullable(), individualInjection))
if (_bloodstream.TryAddToBloodstream(targetBloodstream.AsNullable(), individualInjection))
anySuccess = true;
}

View File

@@ -264,14 +264,14 @@ public sealed class SmokeSystem : EntitySystem
if (!TryComp<BloodstreamComponent>(entity, out var bloodstream))
return;
if (!_solutionContainerSystem.ResolveSolution(entity, bloodstream.ChemicalSolutionName, ref bloodstream.ChemicalSolution, out var chemSolution) || chemSolution.AvailableVolume <= 0)
if (!_solutionContainerSystem.ResolveSolution(entity, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution) || bloodSolution.AvailableVolume <= 0)
return;
var blockIngestion = _internals.AreInternalsWorking(entity);
var cloneSolution = solution.Clone();
var availableTransfer = FixedPoint2.Min(cloneSolution.Volume, component.TransferRate);
var transferAmount = FixedPoint2.Min(availableTransfer, chemSolution.AvailableVolume);
var transferAmount = FixedPoint2.Min(availableTransfer, bloodSolution.AvailableVolume);
var transferSolution = cloneSolution.SplitSolution(transferAmount);
foreach (var reagentQuantity in transferSolution.Contents.ToArray())
@@ -287,7 +287,7 @@ public sealed class SmokeSystem : EntitySystem
if (blockIngestion)
return;
if (_blood.TryAddToChemicals((entity, bloodstream), transferSolution))
if (_blood.TryAddToBloodstream((entity, bloodstream), transferSolution))
{
// Log solution addition by smoke
_logger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(entity):target} ingested smoke {SharedSolutionContainerSystem.ToPrettyString(transferSolution)}");

View File

@@ -18,6 +18,7 @@ using Robust.Server.GameObjects;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
using Robust.Shared.Timing;
using Content.Server.Body.Systems;
namespace Content.Server.Medical;
@@ -32,6 +33,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly TransformSystem _transformSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
public override void Initialize()
{
@@ -204,7 +206,7 @@ public sealed class HealthAnalyzerSystem : EntitySystem
_solutionContainerSystem.ResolveSolution(target, bloodstream.BloodSolutionName,
ref bloodstream.BloodSolution, out var bloodSolution))
{
bloodAmount = bloodSolution.FillFraction;
bloodAmount = _bloodstreamSystem.GetBloodLevel(target);
bleeding = bloodstream.BleedAmount > 0;
}

View File

@@ -157,7 +157,7 @@ namespace Content.Server.Nutrition.EntitySystems
}
_reactiveSystem.DoEntityReaction(containerManager.Owner, inhaledSolution, ReactionMethod.Ingestion);
_bloodstreamSystem.TryAddToChemicals((containerManager.Owner, bloodstream), inhaledSolution);
_bloodstreamSystem.TryAddToBloodstream((containerManager.Owner, bloodstream), inhaledSolution);
}
_timer -= UpdateTimer;

View File

@@ -193,7 +193,7 @@ public sealed partial class ZombieSystem
zombiecomp.BeforeZombifiedSkinColor = huApComp.SkinColor;
zombiecomp.BeforeZombifiedEyeColor = huApComp.EyeColor;
zombiecomp.BeforeZombifiedCustomBaseLayers = new(huApComp.CustomBaseLayers);
if (TryComp<BloodstreamComponent>(target, out var stream) && stream.BloodReagents is { } reagents)
if (TryComp<BloodstreamComponent>(target, out var stream) && stream.BloodReferenceSolution is { } reagents)
zombiecomp.BeforeZombifiedBloodReagents = reagents.Clone();
_humanoidAppearance.SetSkinColor(target, zombiecomp.SkinColor, verify: false, humanoid: huApComp);

View File

@@ -19,7 +19,6 @@ namespace Content.Shared.Body.Components;
[Access(typeof(SharedBloodstreamSystem))]
public sealed partial class BloodstreamComponent : Component
{
public const string DefaultChemicalsSolutionName = "chemicals";
public const string DefaultBloodSolutionName = "bloodstream";
public const string DefaultBloodTemporarySolutionName = "bloodstreamTemporary";
@@ -138,26 +137,19 @@ public sealed partial class BloodstreamComponent : Component
// TODO probably damage bleed thresholds.
/// <summary>
/// Max volume of internal chemical solution storage
/// Modifier applied to <see cref="BloodReferenceSolution.Volume"/> to determine maximum volume for bloodstream.
/// </summary>
[DataField]
public FixedPoint2 ChemicalMaxVolume = FixedPoint2.New(250);
public float MaxVolumeModifier = 2f;
/// <summary>
/// Max volume of internal blood storage,
/// and starting level of blood.
/// </summary>
[DataField]
public FixedPoint2 BloodMaxVolume = FixedPoint2.New(300);
/// <summary>
/// Which reagents are considered this entities 'blood'?
/// Defines which reagents are considered as 'blood' and how much of it is normal.
/// </summary>
/// <remarks>
/// Slime-people might use slime as their blood or something like that.
/// </remarks>
[DataField, AutoNetworkedField]
public Solution BloodReagents = new([new("Blood", 1)]);
public Solution BloodReferenceSolution = new([new("Blood", 300)]);
/// <summary>
/// Name/Key that <see cref="BloodSolution"/> is indexed by.
@@ -165,12 +157,6 @@ public sealed partial class BloodstreamComponent : Component
[DataField]
public string BloodSolutionName = DefaultBloodSolutionName;
/// <summary>
/// Name/Key that <see cref="ChemicalSolution"/> is indexed by.
/// </summary>
[DataField]
public string ChemicalSolutionName = DefaultChemicalsSolutionName;
/// <summary>
/// Name/Key that <see cref="TemporarySolution"/> is indexed by.
/// </summary>
@@ -183,12 +169,6 @@ public sealed partial class BloodstreamComponent : Component
[ViewVariables]
public Entity<SolutionComponent>? BloodSolution;
/// <summary>
/// Internal solution for reagent storage
/// </summary>
[ViewVariables]
public Entity<SolutionComponent>? ChemicalSolution;
/// <summary>
/// Temporary blood solution.
/// When blood is lost, it goes to this solution, and when this

View File

@@ -45,7 +45,7 @@ namespace Content.Shared.Body.Components
/// What solution should this stomach push reagents into, on the body?
/// </summary>
[DataField]
public string BodySolutionName = "chemicals";
public string BodySolutionName = BloodstreamComponent.DefaultBloodSolutionName;
/// <summary>
/// Time between reagents being ingested and them being

View File

@@ -0,0 +1,8 @@
namespace Content.Shared.Body.Events;
/// <summary>
/// Event called by <see cref="Content.Server.Body.Systems.MetabolizerSystem"/> to get a list of
/// blood like reagents for metabolism to skip.
/// </summary>
[ByRefEvent]
public readonly record struct MetabolismExclusionEvent(List<string> ReagentList);

View File

@@ -1,3 +1,4 @@
using System.Linq;
using Content.Shared.Alert;
using Content.Shared.Body.Components;
using Content.Shared.Body.Events;
@@ -53,6 +54,7 @@ public abstract class SharedBloodstreamSystem : EntitySystem
SubscribeLocalEvent<BloodstreamComponent, BeingGibbedEvent>(OnBeingGibbed);
SubscribeLocalEvent<BloodstreamComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
SubscribeLocalEvent<BloodstreamComponent, RejuvenateEvent>(OnRejuvenate);
SubscribeLocalEvent<BloodstreamComponent, MetabolismExclusionEvent>(OnMetabolismExclusion);
}
public override void Update(float frameTime)
@@ -72,11 +74,8 @@ public abstract class SharedBloodstreamSystem : EntitySystem
if (!SolutionContainer.ResolveSolution(uid, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution))
continue;
// Adds blood to their blood level if it is below the maximum; Blood regeneration. Must be alive.
if (bloodSolution.Volume < bloodSolution.MaxVolume && !_mobStateSystem.IsDead(uid))
{
TryModifyBloodLevel((uid, bloodstream), bloodstream.BloodRefreshAmount);
}
// Blood level regulation. Must be alive.
TryRegulateBloodLevel(uid, bloodstream.BloodRefreshAmount);
// Removes blood from the bloodstream based on bleed amount (bleed rate)
// as well as stop their bleeding to a certain extent.
@@ -86,14 +85,14 @@ public abstract class SharedBloodstreamSystem : EntitySystem
RaiseLocalEvent(uid, ref ev);
// Blood is removed from the bloodstream at a 1-1 rate with the bleed amount
TryModifyBloodLevel((uid, bloodstream), -ev.BleedAmount);
TryBleedOut((uid, bloodstream), ev.BleedAmount);
// Bleed rate is reduced by the bleed reduction amount in the bloodstream component.
TryModifyBleedAmount((uid, bloodstream), -ev.BleedReductionAmount);
}
// deal bloodloss damage if their blood level is below a threshold.
var bloodPercentage = GetBloodLevelPercentage((uid, bloodstream));
var bloodPercentage = GetBloodLevel(uid);
if (bloodPercentage < bloodstream.BloodlossThreshold && !_mobStateSystem.IsDead(uid))
{
// bloodloss damage is based on the base value, and modified by how low your blood level is.
@@ -133,9 +132,6 @@ public abstract class SharedBloodstreamSystem : EntitySystem
if (args.Entity == entity.Comp.BloodSolution?.Owner)
entity.Comp.BloodSolution = null;
if (args.Entity == entity.Comp.ChemicalSolution?.Owner)
entity.Comp.ChemicalSolution = null;
if (args.Entity == entity.Comp.TemporarySolution?.Owner)
entity.Comp.TemporarySolution = null;
}
@@ -170,7 +166,6 @@ public abstract class SharedBloodstreamSystem : EntitySystem
private void OnReactionAttempt(Entity<BloodstreamComponent> ent, ref SolutionRelayEvent<ReactionAttemptEvent> args)
{
if (args.Name != ent.Comp.BloodSolutionName
&& args.Name != ent.Comp.ChemicalSolutionName
&& args.Name != ent.Comp.BloodTemporarySolutionName)
{
return;
@@ -221,7 +216,7 @@ public abstract class SharedBloodstreamSystem : EntitySystem
var prob = Math.Clamp(totalFloat / 25, 0, 1);
if (totalFloat > 0 && rand.Prob(prob))
{
TryModifyBloodLevel(ent.AsNullable(), -total / 5);
TryBleedOut(ent.AsNullable(), total / 5);
_audio.PlayPredicted(ent.Comp.InstantBloodSound, ent, args.Origin);
}
@@ -269,7 +264,7 @@ public abstract class SharedBloodstreamSystem : EntitySystem
}
// If the mob's blood level is below the damage threshhold, the pale message is added.
if (GetBloodLevelPercentage(ent.AsNullable()) < ent.Comp.BloodlossThreshold)
if (GetBloodLevel(ent.AsNullable()) < ent.Comp.BloodlossThreshold)
{
args.Message.PushNewline();
args.Message.AddMarkupOrThrow(Loc.GetString("bloodstream-component-looks-pale", ("target", ent.Owner)));
@@ -291,25 +286,46 @@ public abstract class SharedBloodstreamSystem : EntitySystem
{
TryModifyBleedAmount(ent.AsNullable(), -ent.Comp.BleedAmount);
if (SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodSolutionName, ref ent.Comp.BloodSolution, out var bloodSolution))
TryModifyBloodLevel(ent.AsNullable(), bloodSolution.AvailableVolume);
if (SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodSolutionName, ref ent.Comp.BloodSolution))
{
SolutionContainer.RemoveAllSolution(ent.Comp.BloodSolution.Value);
TryModifyBloodLevel(ent.AsNullable(), ent.Comp.BloodReferenceSolution.Volume);
}
}
if (SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.ChemicalSolutionName, ref ent.Comp.ChemicalSolution))
SolutionContainer.RemoveAllSolution(ent.Comp.ChemicalSolution.Value);
private void OnMetabolismExclusion(Entity<BloodstreamComponent> ent, ref MetabolismExclusionEvent args)
{
// Adding all blood reagents for filtering blood in metabolizer
foreach (var (reagentId, _) in ent.Comp.BloodReferenceSolution)
{
args.ReagentList.Add(reagentId.Prototype);
}
}
/// <summary>
/// Returns the current blood level as a percentage (between 0 and 1).
/// This returns the minimum amount of *usable* blood.
/// For multi reagent bloodstreams, if you have 100 of Reagent Y need 100, and 50 of Reagent X and need 100,
/// this will return 0.5f
/// </summary>
public float GetBloodLevelPercentage(Entity<BloodstreamComponent?> ent)
/// <returns>Returns the current blood level as a value from 0 to <see cref="BloodstreamComponent.MaxVolumeModifier"/></returns>
public float GetBloodLevel(Entity<BloodstreamComponent?> entity)
{
if (!Resolve(ent, ref ent.Comp)
|| !SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodSolutionName, ref ent.Comp.BloodSolution, out var bloodSolution))
if (!Resolve(entity, ref entity.Comp)
|| !SolutionContainer.ResolveSolution(entity.Owner, entity.Comp.BloodSolutionName, ref entity.Comp.BloodSolution, out var bloodSolution)
|| entity.Comp.BloodReferenceSolution.Volume == 0)
{
return 0.0f;
}
return bloodSolution.FillFraction;
var totalBloodLevel = FixedPoint2.New(entity.Comp.MaxVolumeModifier); // Can't go above max volume factor...
foreach (var (reagentId, quantity) in entity.Comp.BloodReferenceSolution.Contents)
{
// Ideally we use a different calculation for blood pressure, this just defines how much *usable* blood you have!
totalBloodLevel = FixedPoint2.Min(totalBloodLevel, bloodSolution.GetTotalPrototypeQuantity(reagentId.Prototype) / quantity);
}
return (float)totalBloodLevel;
}
/// <summary>
@@ -327,33 +343,95 @@ public abstract class SharedBloodstreamSystem : EntitySystem
/// <summary>
/// Attempt to transfer a provided solution to internal solution.
/// </summary>
public bool TryAddToChemicals(Entity<BloodstreamComponent?> ent, Solution solution)
public bool TryAddToBloodstream(Entity<BloodstreamComponent?> ent, Solution solution)
{
if (!Resolve(ent, ref ent.Comp, logMissing: false)
|| !SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.ChemicalSolutionName, ref ent.Comp.ChemicalSolution))
|| !SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodSolutionName, ref ent.Comp.BloodSolution))
return false;
if (SolutionContainer.TryAddSolution(ent.Comp.ChemicalSolution.Value, solution))
if (SolutionContainer.TryAddSolution(ent.Comp.BloodSolution.Value, solution))
return true;
return false;
}
/// <summary>
/// Removes a certain amount of all reagents except of a single excluded one from the bloodstream.
/// Removes a certain amount of all reagents except of a single excluded one from the bloodstream and blood itself.
/// </summary>
public bool FlushChemicals(Entity<BloodstreamComponent?> ent, ProtoId<ReagentPrototype>? excludedReagentID, FixedPoint2 quantity)
/// <returns>
/// Solution of removed chemicals or null if none were removed.
/// </returns>
public Solution? FlushChemicals(Entity<BloodstreamComponent?> ent, FixedPoint2 quantity, ProtoId<ReagentPrototype>? excludedReagent = null )
{
if (!Resolve(ent, ref ent.Comp, logMissing: false)
|| !SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.ChemicalSolutionName, ref ent.Comp.ChemicalSolution, out var chemSolution))
|| !SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodSolutionName, ref ent.Comp.BloodSolution, out var bloodSolution))
return null;
var flushedSolution = new Solution();
for (var i = bloodSolution.Contents.Count - 1; i >= 0; i--)
{
var (reagentId, _) = bloodSolution.Contents[i];
if (ent.Comp.BloodReferenceSolution.ContainsPrototype(reagentId.Prototype) || reagentId.Prototype == excludedReagent)
continue;
var reagentFlushAmount = SolutionContainer.RemoveReagent(ent.Comp.BloodSolution.Value, reagentId, quantity);
flushedSolution.AddReagent(reagentId, reagentFlushAmount);
}
return flushedSolution.Volume == 0 ? null : flushedSolution;
}
/// <summary>
/// A simple helper that tries to move blood volume up or down by a specified amount.
/// Blood will not go over normal volume for this entity's bloodstream.
/// </summary>
public bool TryModifyBloodLevel(Entity<BloodstreamComponent?> ent, FixedPoint2 amount)
{
var reference = 1f;
if (amount < 0)
{
reference = 0f;
amount *= -1;
}
return TryRegulateBloodLevel(ent, amount, reference);
}
/// <summary>
/// Attempts to bring an entity's blood level to a modified equilibrium volume.
/// </summary>
/// <param name="ent">Entity whose bloodstream we're modifying.</param>
/// <param name="amount">The absolute maximum amount of blood we can add or remove.</param>
/// <param name="referenceFactor">The modifier for an entity's blood equilibrium, try to hit an entity's default blood volume multiplied by this value.</param>
/// <remarks>This CANNOT go above maximum blood volume!</remarks>
/// <returns>False if we were unable to regulate blood level. This may return true even if blood level doesn't change!</returns>
public bool TryRegulateBloodLevel(Entity<BloodstreamComponent?> ent, FixedPoint2 amount, float referenceFactor = 1f)
{
if (!Resolve(ent, ref ent.Comp, logMissing: false)
|| !SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodSolutionName, ref ent.Comp.BloodSolution, out var bloodSolution)
|| amount == 0)
return false;
for (var i = chemSolution.Contents.Count - 1; i >= 0; i--)
referenceFactor = Math.Clamp(referenceFactor, 0f, ent.Comp.MaxVolumeModifier);
foreach (var (referenceReagent, referenceQuantity) in ent.Comp.BloodReferenceSolution)
{
var (reagentId, _) = chemSolution.Contents[i];
if (reagentId.Prototype != excludedReagentID)
var error = referenceQuantity * referenceFactor - bloodSolution.GetTotalPrototypeQuantity(referenceReagent.Prototype);
var adjustedAmount = amount * referenceQuantity / ent.Comp.BloodReferenceSolution.Volume;
if (error > 0)
{
SolutionContainer.RemoveReagent(ent.Comp.ChemicalSolution.Value, reagentId, quantity);
error = FixedPoint2.Min(error, adjustedAmount);
var reagentToAdd = new ReagentId(referenceReagent.Prototype, GetEntityBloodData(ent));
bloodSolution.AddReagent(reagentToAdd, error);
}
else if (error < 0)
{
// invert the error since we're removing reagents...
error = FixedPoint2.Min( -error, adjustedAmount);
bloodSolution.RemoveReagent(referenceReagent, error);
}
}
@@ -361,43 +439,26 @@ public abstract class SharedBloodstreamSystem : EntitySystem
}
/// <summary>
/// Attempts to modify the blood level of this entity directly.
/// Removes blood by spilling out the bloodstream.
/// </summary>
public bool TryModifyBloodLevel(Entity<BloodstreamComponent?> ent, FixedPoint2 amount)
public bool TryBleedOut(Entity<BloodstreamComponent?> ent, FixedPoint2 amount)
{
if (!Resolve(ent, ref ent.Comp, logMissing: false)
|| !SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodSolutionName, ref ent.Comp.BloodSolution, out var bloodSolution))
return false;
if (amount >= 0)
|| !SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodSolutionName, ref ent.Comp.BloodSolution)
|| amount <= 0)
{
var min = FixedPoint2.Min(bloodSolution.AvailableVolume, amount);
var solution = ent.Comp.BloodReagents.Clone();
solution.ScaleTo(min);
solution.SetReagentData(GetEntityBloodData(ent));
SolutionContainer.AddSolution(ent.Comp.BloodSolution.Value, solution);
return min == amount;
return false;
}
// Removal is more involved,
// since we also wanna handle moving it to the temporary solution
// and then spilling it if necessary.
var newSol = SolutionContainer.SplitSolution(ent.Comp.BloodSolution.Value, -amount);
var leakedBlood = SolutionContainer.SplitSolution(ent.Comp.BloodSolution.Value, amount);
if (!SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodTemporarySolutionName, ref ent.Comp.TemporarySolution, out var tempSolution))
return true;
tempSolution.AddSolution(newSol, PrototypeManager);
tempSolution.AddSolution(leakedBlood, PrototypeManager);
if (tempSolution.Volume > ent.Comp.BleedPuddleThreshold)
{
// Pass some of the chemstream into the spilled blood.
if (SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.ChemicalSolutionName, ref ent.Comp.ChemicalSolution))
{
var temp = SolutionContainer.SplitSolution(ent.Comp.ChemicalSolution.Value, tempSolution.Volume / 10);
tempSolution.AddSolution(temp, PrototypeManager);
}
_puddle.TrySpillAt(ent.Owner, tempSolution, out _, sound: false);
tempSolution.RemoveAllSolution();
@@ -450,13 +511,6 @@ public abstract class SharedBloodstreamSystem : EntitySystem
SolutionContainer.RemoveAllSolution(ent.Comp.BloodSolution.Value);
}
if (SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.ChemicalSolutionName, ref ent.Comp.ChemicalSolution, out var chemSolution))
{
tempSol.MaxVolume += chemSolution.MaxVolume;
tempSol.AddSolution(chemSolution, PrototypeManager);
SolutionContainer.RemoveAllSolution(ent.Comp.ChemicalSolution.Value);
}
if (SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodTemporarySolutionName, ref ent.Comp.TemporarySolution, out var tempSolution))
{
tempSol.MaxVolume += tempSolution.MaxVolume;
@@ -488,24 +542,24 @@ public abstract class SharedBloodstreamSystem : EntitySystem
if (!SolutionContainer.ResolveSolution(ent.Owner, ent.Comp.BloodSolutionName, ref ent.Comp.BloodSolution, out var bloodSolution))
{
ent.Comp.BloodReagents = reagents.Clone();
DirtyField(ent, ent.Comp, nameof(BloodstreamComponent.BloodReagents));
ent.Comp.BloodReferenceSolution = reagents.Clone();
DirtyField(ent, ent.Comp, nameof(BloodstreamComponent.BloodReferenceSolution));
return;
}
var currentVolume = FixedPoint2.Zero;
foreach (var reagent in ent.Comp.BloodReagents)
foreach (var reagent in ent.Comp.BloodReferenceSolution)
{
currentVolume += bloodSolution.RemoveReagent(reagent.Reagent, quantity: bloodSolution.Volume, ignoreReagentData: true);
}
ent.Comp.BloodReagents = reagents.Clone();
DirtyField(ent, ent.Comp, nameof(BloodstreamComponent.BloodReagents));
ent.Comp.BloodReferenceSolution = reagents.Clone();
DirtyField(ent, ent.Comp, nameof(BloodstreamComponent.BloodReferenceSolution));
if (currentVolume == FixedPoint2.Zero)
return;
var solution = ent.Comp.BloodReagents.Clone();
var solution = ent.Comp.BloodReferenceSolution.Clone();
solution.ScaleSolution(currentVolume / solution.Volume);
solution.SetReagentData(GetEntityBloodData(ent));
SolutionContainer.AddSolution(ent.Comp.BloodSolution.Value, solution);

View File

@@ -335,8 +335,8 @@ public abstract class SharedInjectorSystem : EntitySystem
EntityUid user)
{
// Get transfer amount. May be smaller than _transferAmount if not enough room
if (!SolutionContainer.ResolveSolution(target.Owner, target.Comp.ChemicalSolutionName,
ref target.Comp.ChemicalSolution, out var chemSolution))
if (!SolutionContainer.ResolveSolution(target.Owner, target.Comp.BloodSolutionName,
ref target.Comp.BloodSolution, out var bloodSolution))
{
LocId msg = target.Owner == user ? "injector-component-cannot-inject-message-self" : "injector-component-cannot-inject-message";
_popup.PopupClient(
@@ -346,7 +346,7 @@ public abstract class SharedInjectorSystem : EntitySystem
return false;
}
var realTransferAmount = FixedPoint2.Min(injector.Comp.CurrentTransferAmount, chemSolution.AvailableVolume);
var realTransferAmount = FixedPoint2.Min(injector.Comp.CurrentTransferAmount, bloodSolution.AvailableVolume);
if (realTransferAmount <= 0)
{
LocId msg = target.Owner == user ? "injector-component-cannot-inject-message-self" : "injector-component-cannot-inject-message";
@@ -358,9 +358,9 @@ public abstract class SharedInjectorSystem : EntitySystem
}
// Move units from attackSolution to targetSolution
var removedSolution = SolutionContainer.SplitSolution(target.Comp.ChemicalSolution.Value, realTransferAmount);
var removedSolution = SolutionContainer.SplitSolution(target.Comp.BloodSolution.Value, realTransferAmount);
_blood.TryAddToChemicals(target.AsNullable(), removedSolution);
_blood.TryAddToBloodstream(target.AsNullable(), removedSolution);
_reactiveSystem.DoEntityReaction(target, removedSolution, ReactionMethod.Injection);
@@ -438,20 +438,10 @@ public abstract class SharedInjectorSystem : EntitySystem
private void DrawFromBlood(Entity<InjectorComponent> injector, Entity<BloodstreamComponent> target,
Entity<SolutionComponent> injectorSolution, FixedPoint2 transferAmount, EntityUid user)
{
var drawAmount = (float)transferAmount;
if (SolutionContainer.ResolveSolution(target.Owner, target.Comp.ChemicalSolutionName,
ref target.Comp.ChemicalSolution))
{
var chemTemp = SolutionContainer.SplitSolution(target.Comp.ChemicalSolution.Value, drawAmount * 0.15f);
SolutionContainer.TryAddSolution(injectorSolution, chemTemp);
drawAmount -= (float)chemTemp.Volume;
}
if (SolutionContainer.ResolveSolution(target.Owner, target.Comp.BloodSolutionName,
ref target.Comp.BloodSolution))
{
var bloodTemp = SolutionContainer.SplitSolution(target.Comp.BloodSolution.Value, drawAmount);
var bloodTemp = SolutionContainer.SplitSolution(target.Comp.BloodSolution.Value, transferAmount);
SolutionContainer.TryAddSolution(injectorSolution, bloodTemp);
}

View File

@@ -108,7 +108,7 @@ public sealed class DevourSystem : EntitySystem
// Grant ichor if the devoured thing meets the dragon's food preference
if (args.Args.Target != null && _whitelistSystem.IsWhitelistPassOrNull(ent.Comp.FoodPreferenceWhitelist, (EntityUid)args.Args.Target))
{
_bloodstreamSystem.TryAddToChemicals(ent.Owner, ichorInjection);
_bloodstreamSystem.TryAddToBloodstream(ent.Owner, ichorInjection);
}
// If the devoured thing meets the stomach whitelist criteria, add it to the stomach

View File

@@ -19,7 +19,7 @@ public sealed partial class CleanBloodstreamEntityEffectSystem : EntityEffectSys
{
var scale = args.Scale * args.Effect.CleanseRate;
_bloodstream.FlushChemicals((entity, entity), args.Effect.Excluded, scale);
_bloodstream.FlushChemicals((entity, entity), scale, args.Effect.Excluded);
}
}

View File

@@ -108,7 +108,7 @@ public abstract partial class SharedCryoPodSystem : EntitySystem
&& _bloodstreamQuery.TryComp(patient, out var bloodstream))
{
var solutionToInject = _solutionContainer.SplitSolution(containerSolution.Value, cryoPod.BeakerTransferAmount);
_bloodstream.TryAddToChemicals((patient.Value, bloodstream), solutionToInject);
_bloodstream.TryAddToBloodstream((patient.Value, bloodstream), solutionToInject);
_reactive.DoEntityReaction(patient.Value, solutionToInject, ReactionMethod.Injection);
}
}

View File

@@ -141,7 +141,7 @@ public sealed class HealingSystem : EntitySystem
// Is ent missing blood that we can restore?
if (healing.Comp.ModifyBloodLevel > 0
&& _solutionContainerSystem.ResolveSolution(target.Owner, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution)
&& bloodSolution.Volume < bloodSolution.MaxVolume)
&& _bloodstreamSystem.GetBloodLevel((target, bloodstream)) < 1)
{
return true;
}

View File

@@ -107,14 +107,17 @@ public sealed class VomitSystem : EntitySystem
{
var vomitAmount = solutionSize;
// Takes 10% of the chemicals removed from the chem stream
if (_solutionContainer.ResolveSolution(uid, bloodStream.ChemicalSolutionName, ref bloodStream.ChemicalSolution))
// Flushes small portion of the chemicals removed from the bloodstream stream
if (_solutionContainer.ResolveSolution(uid, bloodStream.BloodSolutionName, ref bloodStream.BloodSolution))
{
var vomitChemstreamAmount = _solutionContainer.SplitSolution(bloodStream.ChemicalSolution.Value, vomitAmount);
vomitChemstreamAmount.ScaleSolution(ChemMultiplier);
solution.AddSolution(vomitChemstreamAmount, _proto);
var vomitChemstreamAmount = _bloodstream.FlushChemicals((uid, bloodStream), vomitAmount);
vomitAmount -= (float)vomitChemstreamAmount.Volume;
if (vomitChemstreamAmount != null)
{
vomitChemstreamAmount.ScaleSolution(ChemMultiplier);
solution.AddSolution(vomitChemstreamAmount, _proto);
vomitAmount -= (float)vomitChemstreamAmount.Volume;
}
}
// Makes a vomit solution the size of 90% of the chemicals removed from the chemstream

View File

@@ -102,17 +102,17 @@ public sealed class RootableSystem : EntitySystem
/// </summary>
private void ReactWithEntity(Entity<RootableComponent, BloodstreamComponent> ent, Entity<PuddleComponent> puddleEntity, Solution solution)
{
if (!_solutionContainer.ResolveSolution(ent.Owner, ent.Comp2.ChemicalSolutionName, ref ent.Comp2.ChemicalSolution, out var chemSolution) || chemSolution.AvailableVolume <= 0)
if (!_solutionContainer.ResolveSolution(ent.Owner, ent.Comp2.BloodSolutionName, ref ent.Comp2.BloodSolution, out var bloodSolution) || bloodSolution.AvailableVolume <= 0)
return;
var availableTransfer = FixedPoint2.Min(solution.Volume, ent.Comp1.TransferRate);
var transferAmount = FixedPoint2.Min(availableTransfer, chemSolution.AvailableVolume);
var transferAmount = FixedPoint2.Min(availableTransfer, bloodSolution.AvailableVolume);
var transferSolution = _solutionContainer.SplitSolution(puddleEntity.Comp.Solution!.Value, transferAmount);
_reactive.DoEntityReaction(ent, transferSolution, ReactionMethod.Ingestion);
// Log solution addition by puddle.
if (_blood.TryAddToChemicals((ent, ent.Comp2), transferSolution))
if (_blood.TryAddToBloodstream((ent, ent.Comp2), transferSolution))
_logger.Add(LogType.ForceFeed, LogImpact.Medium, $"{ToPrettyString(ent):target} absorbed puddle {SharedSolutionContainerSystem.ToPrettyString(transferSolution)}");
}

View File

@@ -50,7 +50,10 @@
- type: SentienceTarget
flavorKind: station-event-random-sentience-flavor-organic
- type: Bloodstream
bloodMaxVolume: 50
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 50
- type: ReplacementAccent
accent: mouse
- type: MeleeWeapon
@@ -140,11 +143,10 @@
- Bee
- Trash
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: InsectBlood
Quantity: 1
bloodMaxVolume: 0.1
Quantity: 0.1
- type: MobPrice
price: 50
- type: NPCRetaliation
@@ -190,7 +192,10 @@
factions:
- SimpleHostile
- type: Bloodstream
bloodMaxVolume: 0.1
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 0.1
- type: ZombieImmune
@@ -264,7 +269,10 @@
interactSuccessSound:
path: /Audio/Animals/chicken_cluck_happy.ogg
- type: Bloodstream
bloodMaxVolume: 100
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 100
- type: EggLayer
eggSpawn:
- id: FoodEgg
@@ -391,11 +399,10 @@
Dead:
Base: cockroach_dead
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: InsectBlood
Quantity: 1
bloodMaxVolume: 20
Quantity: 20
- type: Edible
- type: FlavorProfile
flavors:
@@ -558,10 +565,10 @@
damageContainer: Biological
damageModifierSet: Moth
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: InsectBlood
Quantity: 1
Quantity: 20
- type: Respirator
damage:
types:
@@ -713,7 +720,10 @@
interactSuccessSound:
path: /Audio/Animals/duck_quack_happy.ogg
- type: Bloodstream
bloodMaxVolume: 100
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 100
- type: EggLayer
eggSpawn:
- id: FoodEgg
@@ -841,11 +851,10 @@
tags:
- Trash
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: InsectBlood
Quantity: 1
bloodMaxVolume: 0.1
Quantity: 0.1
- type: MobPrice
price: 50
- type: Destructible
@@ -1001,11 +1010,10 @@
- type: ReplacementAccent
accent: crab
- type: Bloodstream
bloodMaxVolume: 50
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: CopperBlood
Quantity: 1
Quantity: 50
- type: Tag
tags:
- VimPilot
@@ -1162,7 +1170,10 @@
interactSuccessSound:
path: /Audio/Animals/goose_honk.ogg
- type: Bloodstream
bloodMaxVolume: 100
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 100
- type: NpcFactionMember
factions:
- Passive
@@ -1204,7 +1215,10 @@
- id: FoodMeat
amount: 4
- type: Bloodstream
bloodMaxVolume: 300
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 300
# if you fuck with the gorilla he will harambe you
- type: MeleeWeapon
soundHit:
@@ -1875,7 +1889,10 @@
types:
Piercing: 0
- type: Bloodstream
bloodMaxVolume: 50
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 50
- type: CanEscapeInventory
- type: MobPrice
price: 50
@@ -2011,10 +2028,10 @@
- type: RadiationSource
intensity: 0.3
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: UnstableMutagen
Quantity: 1
Quantity: 50
- type: SolutionContainerManager
solutions:
food:
@@ -2080,7 +2097,10 @@
interactSuccessSound:
path: /Audio/Animals/lizard_happy.ogg
- type: Bloodstream
bloodMaxVolume: 150
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 150
- type: Damageable
damageContainer: Biological
damageModifierSet: Scale
@@ -2134,7 +2154,10 @@
interactFailureString: petting-failure-generic
interactSuccessSpawn: EffectHearts
- type: Bloodstream
bloodMaxVolume: 50
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 50
- type: entity
name: frog
@@ -2190,7 +2213,10 @@
interactSuccessSound:
path: /Audio/Animals/frog_ribbit.ogg
- type: Bloodstream
bloodMaxVolume: 50
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 50
- type: Tag
tags:
- VimPilot
@@ -2281,7 +2307,10 @@
interactSuccessSound:
path: /Audio/Animals/parrot_raught.ogg
- type: Bloodstream
bloodMaxVolume: 50
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 50
- type: entity
parent: MobParrotBase
@@ -2469,7 +2498,10 @@
interactFailureString: petting-failure-generic
interactSuccessSpawn: EffectHearts
- type: Bloodstream
bloodMaxVolume: 50
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 50
- type: Damageable
damageContainer: Biological
damageModifierSet: Scale
@@ -2532,11 +2564,10 @@
- type: Spider
- type: IgnoreSpiderWeb
- type: Bloodstream
bloodMaxVolume: 150
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: CopperBlood
Quantity: 1
Quantity: 150
- type: Speech
speechVerb: Arachnid
speechSounds: Arachnid
@@ -2681,11 +2712,10 @@
- type: Speech
speechVerb: Cluwne
- type: Bloodstream
bloodMaxVolume: 150
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Laughter
Quantity: 1
Quantity: 150
- type: entity
name: wizard spider
parent: MobGiantSpider
@@ -2881,7 +2911,10 @@
attributes:
gender: epicene
- type: Bloodstream
bloodMaxVolume: 100
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 100
- type: MeleeWeapon
angle: 0
animation: WeaponArcBite
@@ -3014,10 +3047,10 @@
attributes:
gender: epicene
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: DemonsBlood
Quantity: 1
Quantity: 150
- type: Damageable
damageContainer: BiologicalMetaphysical
damageModifierSet: Infernal
@@ -3539,7 +3572,10 @@
interactSuccessSound:
path: /Audio/Animals/fox_squeak.ogg
- type: Bloodstream
bloodMaxVolume: 60
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 60
- type: CanEscapeInventory
baseResistTime: 3
- type: MobPrice
@@ -3657,11 +3693,10 @@
speciesId: cat
templateId: pet
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Sap
Quantity: 1
bloodMaxVolume: 60
Quantity: 60
- type: DamageStateVisuals
states:
Alive:
@@ -3765,7 +3800,10 @@
- id: FoodMeat
amount: 4
- type: Bloodstream
bloodMaxVolume: 300
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 300
# Horns though
- type: MeleeWeapon
damage:

View File

@@ -18,11 +18,10 @@
- type: ReplacementAccent
accent: xeno
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: FerrochromicAcid
Quantity: 1
bloodMaxVolume: 75 #we don't want the map to become pools of blood
Quantity: 75 #we don't want the map to become pools of blood
bloodlossDamage:
types:
Bloodloss:

View File

@@ -49,8 +49,10 @@
baseWalkSpeed : 2.00
baseSprintSpeed : 2.00
- type: Bloodstream
bloodMaxVolume: 350
chemicalMaxVolume: 0
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 350
- type: MobThresholds
thresholds:
0: Alive
@@ -404,8 +406,10 @@
capacity: 1
count: 1
- type: Bloodstream
bloodMaxVolume: 200
chemicalMaxVolume: 0
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 200
- type: NpcFactionMember
factions:
- SimpleHostile

View File

@@ -114,11 +114,10 @@
- type: Input
context: "human"
- type: Bloodstream
bloodMaxVolume: 300
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Laughter
Quantity: 1
Quantity: 300
- type: entity
name: behonker
parent: BaseMobBehonker

View File

@@ -349,11 +349,10 @@
speedModifierThresholds:
50: 0.4
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Water
Quantity: 1
chemicalMaxVolume: 100
Quantity: 100
- type: StatusEffects
allowed:
- Electrocution
@@ -421,10 +420,10 @@
suffix: Beer
components:
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Beer
Quantity: 1
Quantity: 100
- type: PointLight
color: "#cfa85f"
- type: Sprite
@@ -441,10 +440,10 @@
suffix: Pax
components:
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Pax
Quantity: 1
Quantity: 100
- type: PointLight
color: "#AAAAAA"
- type: Sprite
@@ -464,10 +463,10 @@
suffix: Nocturine
components:
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Nocturine
Quantity: 1
Quantity: 100
- type: PointLight
color: "#128e80"
- type: Sprite
@@ -487,10 +486,10 @@
suffix: THC
components:
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: THC
Quantity: 1
Quantity: 100
- type: PointLight
color: "#808080"
- type: Sprite
@@ -507,10 +506,10 @@
suffix: Bicaridine
components:
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Bicaridine
Quantity: 1
Quantity: 100
- type: PointLight
color: "#ffaa00"
- type: Sprite
@@ -527,10 +526,10 @@
suffix: Toxin
components:
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Toxin
Quantity: 1
Quantity: 100
- type: PointLight
color: "#cf3600"
- type: Sprite
@@ -547,10 +546,10 @@
suffix: Napalm
components:
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Napalm
Quantity: 1
Quantity: 100
- type: PointLight
color: "#FA00AF"
- type: Sprite
@@ -567,10 +566,10 @@
suffix: Omnizine
components:
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Omnizine
Quantity: 1
Quantity: 100
- type: PointLight
color: "#fcf7f9"
- type: Sprite
@@ -587,10 +586,10 @@
suffix: Mute Toxin
components:
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: MuteToxin
Quantity: 1
Quantity: 100
- type: PointLight
color: "#0f0f0f"
- type: Sprite
@@ -607,10 +606,10 @@
suffix: Norepinephric Acid
components:
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: NorepinephricAcid
Quantity: 1
Quantity: 100
- type: PointLight
color: "#96a8b5"
- type: Sprite
@@ -627,10 +626,10 @@
suffix: Ephedrine
components:
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Ephedrine
Quantity: 1
Quantity: 100
- type: PointLight
color: "#D2FFFA"
- type: Sprite
@@ -647,10 +646,10 @@
suffix: Robust Harvest
components:
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: RobustHarvest
Quantity: 1
Quantity: 100
- type: PointLight
color: "#3e901c"
- type: Sprite

View File

@@ -48,7 +48,10 @@
- id: FoodMeat
amount: 1
- type: Bloodstream
bloodMaxVolume: 100
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 100
- type: CombatMode
- type: MeleeWeapon
soundHit:
@@ -247,7 +250,10 @@
- id: FoodMeat
amount: 1
- type: Bloodstream
bloodMaxVolume: 100
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 100
- type: CombatMode
- type: MeleeWeapon
soundHit:

View File

@@ -153,12 +153,10 @@
- map: [ "enum.DamageStateVisualLayers.Base" ]
state: alive
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: JuiceTomato
Quantity: 1
bloodMaxVolume: 50
chemicalMaxVolume: 30
Quantity: 50
- type: DamageStateVisuals
states:
Alive:

View File

@@ -101,7 +101,10 @@
- RadiationProtection
- Adrenaline
- type: Bloodstream
bloodMaxVolume: 150
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 150
- type: MobPrice
price: 150
- type: FloatingVisuals

View File

@@ -53,10 +53,10 @@
damageContainer: Biological
damageModifierSet: Slime
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Slime
Quantity: 1
Quantity: 150
bloodlossDamage:
types:
Bloodloss:

View File

@@ -39,11 +39,10 @@
critThreshold: 150
- type: MovementAlwaysTouching
- type: Bloodstream
bloodMaxVolume: 300
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Cryoxadone
Quantity: 1
Quantity: 300
- type: CombatMode
- type: Temperature
heatDamageThreshold: 500
@@ -219,11 +218,10 @@
amount: 1
prob: 0.5
- type: Bloodstream
bloodMaxVolume: 250
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Cryoxadone
Quantity: 1
Quantity: 250
- type: Fixtures
fixtures:
fix1:
@@ -325,11 +323,10 @@
amount: 1
prob: 0.3
- type: Bloodstream
bloodMaxVolume: 200
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Cryoxadone
Quantity: 1
Quantity: 200
- type: Fixtures
fixtures:
fix1:
@@ -502,11 +499,10 @@
- type: CombatMode
combatToggleAction: ActionCombatModeToggleOff
- type: Bloodstream
bloodMaxVolume: 30
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Cryoxadone
Quantity: 1
Quantity: 30
- type: CanEscapeInventory
- type: MobPrice
price: 50

View File

@@ -66,7 +66,10 @@
- id: FoodMeatXeno
amount: 1
- type: Bloodstream
bloodMaxVolume: 50
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 50
- type: CombatMode
- type: MeleeWeapon
soundHit:

View File

@@ -67,11 +67,10 @@
- type: Stamina
critThreshold: 200
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: FluorosulfuricAcid
Quantity: 1
bloodMaxVolume: 650
Quantity: 650
- type: MeleeWeapon
altDisarm: false
angle: 0
@@ -495,11 +494,10 @@
- type: Stamina
critThreshold: 200
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: FluorosulfuricAcid
Quantity: 1
bloodMaxVolume: 300
Quantity: 300
- type: MeleeWeapon
altDisarm: false
angle: 0

View File

@@ -7,7 +7,10 @@
abstract: true
components:
- type: Bloodstream
bloodMaxVolume: 650
bloodReferenceSolution:
reagents:
- ReagentId: Blood
Quantity: 650
- type: GhostRole
allowMovement: true
allowSpeech: true

View File

@@ -42,10 +42,10 @@
- !type:WashCreamPie
# Damage (Self)
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: CopperBlood
Quantity: 1
Quantity: 300
# Damage (Others)
- type: MeleeWeapon
animation: WeaponArcBite

View File

@@ -33,10 +33,10 @@
- id: FoodMeatPlant
amount: 5
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Sap
Quantity: 1
Quantity: 300
- type: Reactive
groups:
Flammable: [ Touch ]

View File

@@ -29,12 +29,12 @@
- id: FoodBakedCookie #should be replaced with gingerbread sheets or something... provided you're willing to make a full spriteset of those.
amount: 5
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: Sugar
Quantity: 1
Quantity: 100
- ReagentId: Butter
Quantity: 2
Quantity: 200
- type: Fixtures
fixtures:
fix1:

View File

@@ -33,10 +33,10 @@
- id: FoodMeat
amount: 5
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: InsectBlood
Quantity: 1
Quantity: 300
- type: DamageVisuals
damageOverlayGroups:
Brute:

View File

@@ -68,10 +68,10 @@
Burn:
sprite: Mobs/Effects/burn_damage.rsi
- type: Bloodstream
bloodReagents: # TODO Color slime blood based on their slime color or smth
bloodReferenceSolution: # TODO Color slime blood based on their slime color or smth
reagents:
- ReagentId: Slime
Quantity: 1
Quantity: 300
- type: Barotrauma
damage:
types:

View File

@@ -89,10 +89,10 @@
Burn:
sprite: Mobs/Effects/burn_damage.rsi
- type: Bloodstream
bloodReagents:
bloodReferenceSolution:
reagents:
- ReagentId: AmmoniaBlood
Quantity: 1
Quantity: 300
- type: MeleeWeapon
soundHit:
collection: AlienClaw

View File

@@ -258,7 +258,7 @@
components:
- type: SolutionContainerManager
- type: InjectableSolution
solution: chemicals
solution: bloodstream
- type: Bloodstream
bloodlossDamage:
types:

View File

@@ -982,6 +982,8 @@
- type: reaction
id: RedMead
requiredMixerCategories:
- Stir
reactants:
Mead:
amount: 1

View File

@@ -43,6 +43,8 @@
- type: reaction
id: SpaceGlue
requiredMixerCategories:
- Stir # prevents turning slimes to glue, TODO: make it react only X units per second; make bloodstream an open thermal system
minTemp: 370
reactants:
SpaceLube:

View File

@@ -22,6 +22,8 @@
- type: reaction
id: SapBoiling
requiredMixerCategories:
- Stir # prevents cooking diona from inside out, TODO: make it react only X units per second; make bloodstream an open thermal system
impact: Low
minTemp: 377
reactants: