CatStation/Content.Shared/Body/Components/BloodstreamComponent.cs
Prole 502af6955d
Predict Forensics V2.0 (Ready for Review) (#40693)
* What sitting on code does to a branch + no mo owner v2

* Replacing ViewVariables with AutoNetworkedField + Missed One V2

* Removing Virtual Methods & Renaming ForensicsSystem to SharedForensicsSystem (Also made it abstract) V2

* IProduceWidgets Review Changes & some of my own V2

* Lil Cleanup V2

* Part 1 of Review Changes (Only thing left is the BUI stuff and the constructor) V2

* It now WORKS!!! (Still needs the BUI and constuctor changes, ill figure it out later)

* Test (Removal of ServerSide VomitSystem.cs)

* Ok...

* Moved System BloodstreamSystem.cs to SharedBloodstreamSystem.cs & deleted client BloodStreamSystem.cs to make SharedBloodstreamSystem.cs sealed. (No Unregistered errors)

* Prediction

* Fix?

* No mo log error?

* Revert "No mo log error?"

This reverts commit 50c15a3deab6af9dffd7baed45820bae5558e41f.

* Constructor going up

* No ForensicScannerBoundUserInterface.cs (It doesnt wanna scan anymore but it doesnt crash)

* For Conflicts

* Guh

* Yea

* Organization & no ErrorHell (maybe)

* Dirtying

* Dirtying take 2

* Revert "Dirtying take 2"

This reverts commit 5735012e21f43808db3ae010fce268eac24d0583.

* the dirties dont do much here

* For Conflict

* Readded

* Another Conflict Commit Fix

* Conflict Fix Thrice

* Empty Space

* Keep it simple

* Add it back in

* Move BloodstreamSystem stuff thats causing the error back to system. This'll prob fix the conflict

* Conflict fix?

* Trust the fix

* Looked at other prediction PR's, they had better code.

* To not mix up the system in client

* Removing Conflict

* For conflict

* For the conflic fix, will be removed later

* Scuffed Fix

* Removal & Adding the Decouple

* Took some HEAVY pointers from SirWarocks work. The Roadblock has been CLEARED, DOAFTERS WORK NOW!!

* Cleanup & Optimizing, mostly comps.

* More Optimization, most of this is just imported Warock Code, his QOL included. Did not want to remove SolutionDNA's tho, mostly due to a Log.Debug in OnPrint.

* Forgot that the Print Refreshes, more of Warocks work.

* Removing Loc.GetString

* All of BloodStream to Shared, hopefully no errors.

* Sketchy Dirty Removed

* Removed Timer.Spawn, added FrameUpdate cause anything else only updated when the window opened, & a bit of cleanup.

* No mo Mispredict

* GenerateDnaEvent Predicted

* Forgot the user

* I knew it was real, bloodstutter is REAL and now GONE.

* Cant dirty NewEntityBloodData due to potential stutter, so its safer to put it all back into server. again...

* Clearer

* Thank you leahcat, no more identity leak.

* Routine Maintenance

* sealing the class

* it checks for this now?!

* While im here, FileScope Namespaces & removing unused namespaces

* Update Attribute Type (SirWarock Import), it works and is much easier to read.

* Forever Fixing Conflicts

* Readonly Genocide & Partial Maxing

* LGTM, BloodstreamSystem moved to SharedBloodstreamSystem!

* Sohn

* If OG Github back push this commit

* One Billionth Commit Fix

* Removed client/server stuff

* Made implicit

* NewLine & comment on why AutoGenCompState is true

* Updatin the rest of the Popups to use Entity

* I think that's what they meant? It looks cleaner at least.

* Mass Attribute for LocalEvents that are safe to change.

* Putting it back as abstract

* maybe?

* Guhh, rolling back empty client/server of BloodStream

* Removed Client/Server BloodstreamSystems, changed access modifier from protected to private, & renamed SharedBloodstreamSystem to BloodstreamSystem

* From Protected to Private

* Removed comment & made it explicit on what changed to true

* Guh, hopefully thats all of it...

* Forgot

* Review

* Test Push

* Line 316 in SharedForensicsSystem was causing the errors, better to play it safe

* Review Changes

* Update Content.Shared/Forensics/Events.cs

Co-authored-by: Whatstone <166147148+whatston3@users.noreply.github.com>

* The only thing SolutionDNAs does is search and clears itself. I see no reason to keep it

* The print.disabled didnt properly disable when cleared. So ill just use the old if check

* Figured out the rest of the SubscribeLocalEvents and networking BloodData cause its fully in Shared

---------

Co-authored-by: Whatstone <166147148+whatston3@users.noreply.github.com>
2026-08-08 22:48:36 +00:00

206 lines
7.3 KiB
C#

using Content.Shared.Alert;
using Content.Shared.Body.Systems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared.Body.Components;
/// <summary>
/// Gives an entity a bloodstream.
/// </summary>
[RegisterComponent, NetworkedComponent,]
[AutoGenerateComponentState(fieldDeltas: true), AutoGenerateComponentPause]
[Access(typeof(BloodstreamSystem))]
public sealed partial class BloodstreamComponent : Component
{
public const string DefaultBloodSolutionName = "bloodstream";
public const string DefaultBloodTemporarySolutionName = "bloodstreamTemporary";
public const string DefaultMetabolitesSolutionName = "metabolites";
/// <summary>
/// The next time that blood level will be updated and bloodloss damage dealt.
/// </summary>
[DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
[AutoNetworkedField, AutoPausedField]
public TimeSpan NextUpdate;
/// <summary>
/// The interval at which this component updates.
/// </summary>
[DataField, AutoNetworkedField]
public TimeSpan UpdateInterval = TimeSpan.FromSeconds(3);
/// <summary>
/// Multiplier applied to <see cref="UpdateInterval"/> for adjusting based on metabolic rate multiplier.
/// </summary>
[DataField, AutoNetworkedField]
public float UpdateIntervalMultiplier = 1f;
/// <summary>
/// Adjusted update interval based off of the multiplier value.
/// </summary>
[ViewVariables]
public TimeSpan AdjustedUpdateInterval => UpdateInterval * UpdateIntervalMultiplier;
/// <summary>
/// How much is this entity currently bleeding?
/// Higher numbers mean more blood lost every tick.
///
/// Goes down slowly over time, and items like bandages
/// or clotting reagents can lower bleeding.
/// </summary>
/// <remarks>
/// This generally corresponds to an amount of damage and can't go above 100.
/// </remarks>
[DataField, AutoNetworkedField]
public float BleedAmount;
/// <summary>
/// How much should bleeding be reduced every update interval?
/// </summary>
[DataField, AutoNetworkedField]
public float BleedReductionAmount = 0.33f;
/// <summary>
/// How high can <see cref="BleedAmount"/> go?
/// </summary>
[DataField, AutoNetworkedField]
public float MaxBleedAmount = 10.0f;
/// <summary>
/// What percentage of current blood is necessary to avoid dealing blood loss damage?
/// </summary>
[DataField, AutoNetworkedField]
public float BloodlossThreshold = 0.9f;
/// <summary>
/// The base bloodloss damage to be incurred if below <see cref="BloodlossThreshold"/>
/// The default values are defined per mob/species in YML.
/// </summary>
[DataField(required: true), AutoNetworkedField]
public DamageSpecifier BloodlossDamage = new();
/// <summary>
/// The base bloodloss damage to be healed if above <see cref="BloodlossThreshold"/>
/// The default values are defined per mob/species in YML.
/// </summary>
[DataField(required: true), AutoNetworkedField]
public DamageSpecifier BloodlossHealDamage = new();
// TODO shouldn't be hardcoded, should just use some organ simulation like bone marrow or smth.
/// <summary>
/// How much reagent of blood should be restored each update interval?
/// </summary>
[DataField, AutoNetworkedField]
public FixedPoint2 BloodRefreshAmount = 1.0f;
/// <summary>
/// How much blood needs to be in the temporary solution in order to create a puddle?
/// </summary>
[DataField, AutoNetworkedField]
public FixedPoint2 BleedPuddleThreshold = 1.0f;
/// <summary>
/// A modifier set prototype ID corresponding to how damage should be modified
/// before taking it into account for bloodloss.
/// </summary>
/// <remarks>
/// For example, piercing damage is increased while poison damage is nullified entirely.
/// </remarks>
[DataField, AutoNetworkedField]
public ProtoId<DamageModifierSetPrototype> DamageBleedModifiers = "BloodlossHuman";
/// <summary>
/// The sound to be played when a weapon instantly deals blood loss damage.
/// </summary>
[DataField, AutoNetworkedField]
public SoundSpecifier InstantBloodSound = new SoundCollectionSpecifier("blood");
/// <summary>
/// The sound to be played when some damage actually heals bleeding rather than starting it.
/// </summary>
[DataField, AutoNetworkedField]
public SoundSpecifier BloodHealedSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg");
/// <summary>
/// The minimum amount damage reduction needed to play the healing sound/popup.
/// This prevents tiny amounts of heat damage from spamming the sound, e.g. spacing.
/// </summary>
[DataField, AutoNetworkedField]
public float BloodHealedSoundThreshold = -0.1f;
// TODO probably damage bleed thresholds.
/// <summary>
/// Modifier applied to <see cref="BloodstreamComponent.BloodReferenceSolution.Volume"/> to determine maximum volume for bloodstream.
/// </summary>
[DataField, AutoNetworkedField]
public float MaxVolumeModifier = 2f;
/// <summary>
/// Defines which reagents are considered as 'blood' and how much of it is normal.
/// </summary>
/// <remarks>
/// Default is human blood at 5 liters (600u) of blood.
/// </remarks>
[DataField, AutoNetworkedField]
public Solution BloodReferenceSolution = new([new("Blood", 600)]);
/// <summary>
/// Caches the blood data of an entity.
/// This is modified by DNA on init so it's not savable.
/// </summary>
[ViewVariables(VVAccess.ReadOnly), AutoNetworkedField]
public List<ReagentData>? BloodData;
/// <summary>
/// Name/Key that <see cref="BloodSolution"/> is indexed by.
/// </summary>
[DataField]
public string BloodSolutionName = DefaultBloodSolutionName;
/// <summary>
/// Name/Key that <see cref="TemporarySolution"/> is indexed by.
/// </summary>
[DataField]
public string BloodTemporarySolutionName = DefaultBloodTemporarySolutionName;
/// <summary>
/// Name/Key that <see cref="MetabolitesSolution"/> is indexed by.
/// </summary>
[DataField]
public string MetabolitesSolutionName = DefaultMetabolitesSolutionName;
/// <summary>
/// Internal solution for blood storage
/// </summary>
[ViewVariables]
public Entity<SolutionComponent>? BloodSolution;
/// <summary>
/// Temporary blood solution.
/// When blood is lost, it goes to this solution, and when this
/// solution hits a certain cap, the blood is actually spilled as a puddle.
/// </summary>
[ViewVariables]
public Entity<SolutionComponent>? TemporarySolution;
/// <summary>
/// Internal solution for metabolite storage
/// </summary>
[ViewVariables]
public Entity<SolutionComponent>? MetabolitesSolution;
/// <summary>
/// Alert to show when bleeding.
/// </summary>
[DataField]
public ProtoId<AlertPrototype> BleedingAlert = "Bleed";
}