using Content.Shared.Body.Components; using Content.Shared.FixedPoint; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Metabolism; /// /// Handles metabolizing various reagents with given effects. /// [RegisterComponent, NetworkedComponent] [AutoGenerateComponentState, AutoGenerateComponentPause, Access(typeof(MetabolizerSystem))] public sealed partial class MetabolizerComponent : Component { /// /// The next time that reagents will be metabolized. /// [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoNetworkedField, AutoPausedField] public TimeSpan NextUpdate; /// /// How often to metabolize reagents. /// /// [DataField] public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1); /// /// Multiplier applied to for adjusting based on metabolic rate multiplier. /// [DataField, AutoNetworkedField] public float UpdateIntervalMultiplier = 1f; /// /// Adjusted update interval based off of the multiplier value. /// [ViewVariables] public TimeSpan AdjustedUpdateInterval => UpdateInterval * UpdateIntervalMultiplier; /// /// From which solution will this metabolizer attempt to metabolize chemicals for a given stage /// This typically does not change and as such isn't networked. /// TODO: Entity relations :( /// [DataField] public Dictionary, MetabolismSolutionEntry> Solutions = new() { ["Respiration"] = new() { SolutionName = "Lung", SolutionOnBody = false, TransferSolutionName = BloodstreamComponent.DefaultBloodSolutionName, MetabolizeAll = true }, ["Digestion"] = new() { SolutionName = "stomach", SolutionOnBody = false, TransferSolutionName = BloodstreamComponent.DefaultBloodSolutionName, TransferEfficacy = 0.5 }, ["Bloodstream"] = new() { SolutionName = BloodstreamComponent.DefaultBloodSolutionName, TransferSolutionName = BloodstreamComponent.DefaultMetabolitesSolutionName, }, ["Metabolites"] = new() { SolutionName = BloodstreamComponent.DefaultMetabolitesSolutionName } }; /// /// Does this component use a solution on it's parent entity (the body) or itself /// /// /// Most things will use the parent entity (bloodstream). /// [DataField] public bool SolutionOnBody = true; /// /// List of metabolizer types that this organ is. ex. Human, Slime, Felinid, w/e. /// [DataField] [Access(typeof(MetabolizerSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends public HashSet>? MetabolizerTypes; /// /// How many reagents 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. /// [DataField("maxReagents")] public int MaxReagentsProcessable = 3; /// /// A list of metabolism stages that this metabolizer will act on, in order of precedence. /// [DataField] public List> Stages = new(); } [DataDefinition] public sealed partial class MetabolismSolutionEntry { /// /// From which solution will this metabolizer attempt to metabolize chemicals /// [DataField(required: true)] public string SolutionName; /// /// Does this metabolizer use a solution on it's parent entity (the body) or itself /// /// /// Most things will use the parent entity (bloodstream). /// [DataField] public bool SolutionOnBody = true; /// /// When true, this solution will be metabolized entirely instead of at a certain rate /// [DataField] public bool MetabolizeAll = false; /// /// Reagents without a metabolism for the current stage will be transferred to this solution /// [DataField] public string? TransferSolutionName; /// /// Reagents transferred by this metabolizer will transfer at this rate if they don't have a metabolism /// [DataField] public FixedPoint2 TransferRate = 0.25; /// /// The percentage of transferred reagents that actually make it to the next step in metabolism if they don't have explicit metabolites /// [DataField] public FixedPoint2 TransferEfficacy = 1; /// /// Does this metabolizer transfer to a solution on the body or on the entity itself /// [DataField] public bool TransferSolutionOnBody = true; }