From a57d78a3f26e0cd31494c9738ad990b60aae89b7 Mon Sep 17 00:00:00 2001 From: mirrorcult Date: Thu, 17 Feb 2022 15:00:41 -0700 Subject: [PATCH] Basic bleeding mechanics (#6710) --- .../Body/Components/BloodstreamComponent.cs | 128 ++++++++++++- .../Body/Components/MetabolizerComponent.cs | 2 +- .../Body/Components/StomachComponent.cs | 2 +- .../Body/Systems/BloodstreamSystem.cs | 172 ++++++++++++++++-- .../FoamSolutionAreaEffectComponent.cs | 4 +- .../SmokeSolutionAreaEffectComponent.cs | 4 +- .../EntitySystems/ChemistrySystem.Injector.cs | 8 +- .../SolutionInjectOnCollideSystem.cs | 2 +- .../ReagentEffects/ModifyBleedAmount.cs | 25 +++ .../ReagentEffects/ModifyBloodLevel.cs | 25 +++ .../Medical/Components/HealingComponent.cs | 8 + Content.Server/Medical/HealingSystem.cs | 8 + .../Nutrition/EntitySystems/SmokingSystem.cs | 2 +- .../Weapon/Melee/MeleeWeaponSystem.cs | 2 +- .../Audio/Effects/Fluids/ATTRIBUTION.txt | 2 + Resources/Audio/Effects/Fluids/blood1.ogg | Bin 0 -> 21628 bytes Resources/Audio/Effects/Fluids/blood2.ogg | Bin 0 -> 23404 bytes .../Locale/en-US/bloodstream/bloodstream.ftl | 1 + Resources/Prototypes/Damage/modifier_sets.yml | 17 ++ .../Prototypes/Entities/Effects/puddle.yml | 19 ++ .../Entities/Mobs/NPCs/simplemob.yml | 9 +- .../Entities/Mobs/Species/human.yml | 15 +- .../Entities/Mobs/Species/slime.yml | 10 + .../Objects/Specific/Medical/healing.yml | 6 +- Resources/Prototypes/Reagents/biological.yml | 30 +++ Resources/Prototypes/Reagents/elements.yml | 6 + Resources/Prototypes/Reagents/medicine.yml | 33 +++- .../Prototypes/Recipes/Reactions/medicine.yml | 12 ++ Resources/Prototypes/SoundCollections/gib.yml | 6 + 29 files changed, 510 insertions(+), 48 deletions(-) create mode 100644 Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs create mode 100644 Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs create mode 100644 Resources/Audio/Effects/Fluids/ATTRIBUTION.txt create mode 100644 Resources/Audio/Effects/Fluids/blood1.ogg create mode 100644 Resources/Audio/Effects/Fluids/blood2.ogg create mode 100644 Resources/Locale/en-US/bloodstream/bloodstream.ftl create mode 100644 Resources/Prototypes/Reagents/biological.yml diff --git a/Content.Server/Body/Components/BloodstreamComponent.cs b/Content.Server/Body/Components/BloodstreamComponent.cs index 612ef6e153..9ba2ea314f 100644 --- a/Content.Server/Body/Components/BloodstreamComponent.cs +++ b/Content.Server/Body/Components/BloodstreamComponent.cs @@ -1,28 +1,136 @@ -using Content.Server.Atmos; using Content.Server.Body.Systems; -using Content.Shared.Atmos; using Content.Shared.Chemistry.Components; +using Content.Shared.Damage; +using Content.Shared.Damage.Prototypes; using Content.Shared.FixedPoint; -using Robust.Shared.Analyzers; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.ViewVariables; +using Content.Shared.Sound; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Body.Components { [RegisterComponent, Friend(typeof(BloodstreamSystem))] public sealed class BloodstreamComponent : Component { + public static string DefaultChemicalsSolutionName = "chemicals"; + public static string DefaultBloodSolutionName = "bloodstream"; + public static string DefaultBloodTemporarySolutionName = "bloodstreamTemporary"; + + public float AccumulatedFrametime = 0.0f; + /// - /// Max volume of internal solution storage + /// 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. /// - [DataField("maxVolume")] - public FixedPoint2 InitialMaxVolume = FixedPoint2.New(250); + /// + /// This generally corresponds to an amount of damage and can't go above 100. + /// + [ViewVariables(VVAccess.ReadWrite)] + public float BleedAmount; + + /// + /// How much should bleeding should be reduced every update interval? + /// + [DataField("bleedReductionAmount")] + public float BleedReductionAmount = 1.0f; + + /// + /// What percentage of current blood is necessary to avoid dealing blood loss damage? + /// + [DataField("bloodlossThreshold")] + public float BloodlossThreshold = 0.9f; + + /// + /// The base bloodloss damage to be incurred if below + /// + [DataField("bloodlossDamage", required: true)] + public DamageSpecifier BloodlossDamage = default!; + + /// + /// The base bloodloss damage to be healed if above + /// + [DataField("bloodlossHealDamage", required: true)] + public DamageSpecifier BloodlossHealDamage = default!; + + /// + /// How frequently should this bloodstream update, in seconds? + /// + [DataField("updateInterval")] + public float UpdateInterval = 5.0f; + + // TODO shouldn't be hardcoded, should just use some organ simulation like bone marrow or smth. + /// + /// How much reagent of blood should be restored each update interval? + /// + [DataField("bloodRefreshAmount")] + public float BloodRefreshAmount = 0.2f; + + /// + /// How much blood needs to be in the temporary solution in order to create a puddle? + /// + [DataField("bleedPuddleThreshold")] + public FixedPoint2 BleedPuddleThreshold = 10.0f; + + /// + /// A modifier set prototype ID corresponding to how damage should be modified + /// before taking it into account for bloodloss. + /// + /// + /// For example, piercing damage is increased while poison damage is nullified entirely. + /// + [DataField("damageBleedModifiers", customTypeSerializer:typeof(PrototypeIdSerializer))] + public string DamageBleedModifiers = "BloodlossHuman"; + + /// + /// The sound to be played when a weapon instantly deals blood loss damage. + /// + [DataField("instantBloodSound")] + public SoundSpecifier InstantBloodSound = new SoundCollectionSpecifier("blood"); + + // TODO probably damage bleed thresholds. + + /// + /// Max volume of internal chemical solution storage + /// + [DataField("chemicalMaxVolume")] + public FixedPoint2 ChemicalMaxVolume = FixedPoint2.New(250); + + /// + /// Max volume of internal blood storage, + /// and starting level of blood. + /// + [DataField("bloodMaxVolume")] + public FixedPoint2 BloodMaxVolume = FixedPoint2.New(300); + + /// + /// Which reagent is considered this entities 'blood'? + /// + /// + /// Slime-people might use slime as their blood or something like that. + /// + [DataField("bloodReagent")] + public string BloodReagent = "Blood"; /// /// Internal solution for reagent storage /// [ViewVariables(VVAccess.ReadWrite)] - public Solution Solution = default!; + public Solution ChemicalSolution = default!; + + /// + /// Internal solution for blood storage + /// + [ViewVariables(VVAccess.ReadWrite)] + public Solution BloodSolution = default!; + + /// + /// 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. + /// + [ViewVariables(VVAccess.ReadWrite)] + public Solution BloodTemporarySolution = default!; } } diff --git a/Content.Server/Body/Components/MetabolizerComponent.cs b/Content.Server/Body/Components/MetabolizerComponent.cs index fb7fe838db..bd1e6f6ac2 100644 --- a/Content.Server/Body/Components/MetabolizerComponent.cs +++ b/Content.Server/Body/Components/MetabolizerComponent.cs @@ -30,7 +30,7 @@ namespace Content.Server.Body.Components /// From which solution will this metabolizer attempt to metabolize chemicals /// [DataField("solution")] - public string SolutionName { get; set; } = BloodstreamSystem.DefaultSolutionName; + public string SolutionName { get; set; } = BloodstreamComponent.DefaultChemicalsSolutionName; /// /// Does this component use a solution on it's parent entity (the body) or itself diff --git a/Content.Server/Body/Components/StomachComponent.cs b/Content.Server/Body/Components/StomachComponent.cs index ec7227f720..3ec931ab84 100644 --- a/Content.Server/Body/Components/StomachComponent.cs +++ b/Content.Server/Body/Components/StomachComponent.cs @@ -24,7 +24,7 @@ namespace Content.Server.Body.Components /// What solution should this stomach push reagents into, on the body? /// [DataField("bodySolutionName")] - public string BodySolutionName = BloodstreamSystem.DefaultSolutionName; + public string BodySolutionName = BloodstreamComponent.DefaultChemicalsSolutionName; /// /// Initial internal solution storage volume diff --git a/Content.Server/Body/Systems/BloodstreamSystem.cs b/Content.Server/Body/Systems/BloodstreamSystem.cs index 518b309b7d..b0c38dd1ca 100644 --- a/Content.Server/Body/Systems/BloodstreamSystem.cs +++ b/Content.Server/Body/Systems/BloodstreamSystem.cs @@ -1,47 +1,193 @@ -using System; -using Content.Server.Atmos; -using Content.Server.Atmos.EntitySystems; +using System.Linq; using Content.Server.Body.Components; using Content.Server.Chemistry.EntitySystems; +using Content.Server.Fluids.EntitySystems; using Content.Shared.Chemistry.Components; +using Content.Shared.Damage; +using Content.Shared.Damage.Prototypes; +using Content.Shared.Examine; using Content.Shared.FixedPoint; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; +using Content.Shared.MobState.Components; +using Robust.Shared.Audio; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; namespace Content.Server.Body.Systems; public sealed class BloodstreamSystem : EntitySystem { [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; - [Dependency] private readonly AtmosphereSystem _atmosSystem = default!; - [Dependency] private readonly RespiratorSystem _respiratorSystem = default!; + [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly SpillableSystem _spillableSystem = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IRobustRandom _robustRandom = default!; - public static string DefaultSolutionName = "bloodstream"; + // TODO here + // Update over time. Modify bloodloss damage in accordance with (amount of blood / max blood level), and reduce bleeding over time + // Sub to damage changed event and modify bloodloss if incurring large hits of slashing/piercing public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent(OnDamageChanged); + SubscribeLocalEvent(OnExamined); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + foreach (var bloodstream in EntityManager.EntityQuery()) + { + bloodstream.AccumulatedFrametime += frameTime; + + if (bloodstream.AccumulatedFrametime < bloodstream.UpdateInterval) + continue; + + bloodstream.AccumulatedFrametime -= bloodstream.UpdateInterval; + + var uid = bloodstream.Owner; + if (TryComp(uid, out var state) && state.IsDead()) + continue; + + // First, let's refresh their blood if possible. + if (bloodstream.BloodSolution.CurrentVolume < bloodstream.BloodSolution.MaxVolume) + TryModifyBloodLevel(uid, bloodstream.BloodRefreshAmount, bloodstream); + + // Next, let's remove some blood from them according to their bleed level. + // as well as stop their bleeding to a certain extent. + if (bloodstream.BleedAmount > 0) + { + TryModifyBloodLevel(uid, (-bloodstream.BleedAmount) / 10, bloodstream); + TryModifyBleedAmount(uid, -bloodstream.BleedReductionAmount, bloodstream); + } + + // Next, we'll deal some bloodloss damage if their blood level is below a threshold. + var bloodPercentage = GetBloodLevelPercentage(uid, bloodstream); + if (bloodPercentage < bloodstream.BloodlossThreshold) + { + // TODO use a better method for determining this. + var amt = bloodstream.BloodlossDamage / bloodPercentage; + + _damageableSystem.TryChangeDamage(uid, amt, true, false); + } + else + { + // If they're healthy, we'll try and heal some bloodloss instead. + _damageableSystem.TryChangeDamage(uid, bloodstream.BloodlossHealDamage * bloodPercentage, true, false); + } + } } private void OnComponentInit(EntityUid uid, BloodstreamComponent component, ComponentInit args) { - component.Solution = _solutionContainerSystem.EnsureSolution(uid, DefaultSolutionName); - if (component.Solution != null) + component.ChemicalSolution = _solutionContainerSystem.EnsureSolution(uid, BloodstreamComponent.DefaultChemicalsSolutionName); + component.BloodSolution = _solutionContainerSystem.EnsureSolution(uid, BloodstreamComponent.DefaultBloodSolutionName); + component.BloodTemporarySolution = _solutionContainerSystem.EnsureSolution(uid, BloodstreamComponent.DefaultBloodTemporarySolutionName); + + component.ChemicalSolution.MaxVolume = component.ChemicalMaxVolume; + component.BloodSolution.MaxVolume = component.BloodMaxVolume; + component.BloodTemporarySolution.MaxVolume = component.BleedPuddleThreshold * 2; // give some leeway + + // Fill blood solution with BLOOD + _solutionContainerSystem.TryAddReagent(uid, component.BloodSolution, component.BloodReagent, + component.BloodMaxVolume, out _); + } + + private void OnDamageChanged(EntityUid uid, BloodstreamComponent component, DamageChangedEvent args) + { + if (args.DamageDelta is null) + return; + + // TODO probably cache this or something. humans get hurt a lot + if (!_prototypeManager.TryIndex(component.DamageBleedModifiers, out var modifiers)) + return; + + var bloodloss = DamageSpecifier.ApplyModifierSet(args.DamageDelta, modifiers); + + if (bloodloss.Empty) + return; + + var total = bloodloss.Total; + var totalFloat = total.Float(); + TryModifyBleedAmount(uid, totalFloat, component); + + var prob = Math.Clamp(totalFloat / 50, 0, 1); + if (_robustRandom.Prob(prob)) { - component.Solution.MaxVolume = component.InitialMaxVolume; + // This is gonna hurt. + TryModifyBloodLevel(uid, (-total) / 5, component); + SoundSystem.Play(Filter.Pvs(uid), component.InstantBloodSound.GetSound(), uid, AudioParams.Default); + } + } + + private void OnExamined(EntityUid uid, BloodstreamComponent component, ExaminedEvent args) + { + if (GetBloodLevelPercentage(uid, component) < component.BloodlossThreshold) + { + args.PushMarkup(Loc.GetString("bloodstream-component-looks-pale", ("target", uid))); } } /// /// Attempt to transfer provided solution to internal solution. /// - public bool TryAddToBloodstream(EntityUid uid, Solution solution, BloodstreamComponent? component=null) + public bool TryAddToChemicals(EntityUid uid, Solution solution, BloodstreamComponent? component=null) { if (!Resolve(uid, ref component, false)) return false; - return _solutionContainerSystem.TryAddSolution(uid, component.Solution, solution); + return _solutionContainerSystem.TryAddSolution(uid, component.ChemicalSolution, solution); + } + + public float GetBloodLevelPercentage(EntityUid uid, BloodstreamComponent? component = null) + { + if (!Resolve(uid, ref component)) + return 0.0f; + + return (component.BloodSolution.CurrentVolume / component.BloodSolution.MaxVolume).Float(); + } + + /// + /// Attempts to modify the blood level of this entity directly. + /// + public bool TryModifyBloodLevel(EntityUid uid, FixedPoint2 amount, BloodstreamComponent? component = null) + { + if (!Resolve(uid, ref component, false)) + return false; + + if (amount >= 0) + return _solutionContainerSystem.TryAddReagent(uid, component.BloodSolution, component.BloodReagent, amount, out _); + + // Removal is more involved, + // since we also wanna handle moving it to the temporary solution + // and then spilling it if necessary. + var newSol = component.BloodSolution.SplitSolution(-amount); + component.BloodTemporarySolution.AddSolution(newSol); + + if (component.BloodTemporarySolution.MaxVolume > component.BleedPuddleThreshold) + { + _spillableSystem.SpillAt(uid, component.BloodTemporarySolution, "PuddleBlood", false); + component.BloodTemporarySolution.RemoveAllSolution(); + } + + return true; + } + + /// + /// Tries to make an entity bleed more or less + /// + public bool TryModifyBleedAmount(EntityUid uid, float amount, BloodstreamComponent? component = null) + { + if (!Resolve(uid, ref component, false)) + return false; + + component.BleedAmount += amount; + component.BleedAmount = Math.Clamp(component.BleedAmount, 0, 40); + + return true; } } diff --git a/Content.Server/Chemistry/Components/FoamSolutionAreaEffectComponent.cs b/Content.Server/Chemistry/Components/FoamSolutionAreaEffectComponent.cs index 94a7aac416..925c2c92b0 100644 --- a/Content.Server/Chemistry/Components/FoamSolutionAreaEffectComponent.cs +++ b/Content.Server/Chemistry/Components/FoamSolutionAreaEffectComponent.cs @@ -61,10 +61,10 @@ namespace Content.Server.Chemistry.Components var cloneSolution = solution.Clone(); var transferAmount = FixedPoint2.Min(cloneSolution.TotalVolume * solutionFraction * (1 - protection), - bloodstream.Solution.AvailableVolume); + bloodstream.ChemicalSolution.AvailableVolume); var transferSolution = cloneSolution.SplitSolution(transferAmount); - bloodstreamSys.TryAddToBloodstream(entity, transferSolution, bloodstream); + bloodstreamSys.TryAddToChemicals(entity, transferSolution, bloodstream); } protected override void OnKill() diff --git a/Content.Server/Chemistry/Components/SmokeSolutionAreaEffectComponent.cs b/Content.Server/Chemistry/Components/SmokeSolutionAreaEffectComponent.cs index a59ec45b0b..347fdd0143 100644 --- a/Content.Server/Chemistry/Components/SmokeSolutionAreaEffectComponent.cs +++ b/Content.Server/Chemistry/Components/SmokeSolutionAreaEffectComponent.cs @@ -41,7 +41,7 @@ namespace Content.Server.Chemistry.Components var chemistry = EntitySystem.Get(); var cloneSolution = solution.Clone(); - var transferAmount = FixedPoint2.Min(cloneSolution.TotalVolume * solutionFraction, bloodstream.Solution.AvailableVolume); + var transferAmount = FixedPoint2.Min(cloneSolution.TotalVolume * solutionFraction, bloodstream.ChemicalSolution.AvailableVolume); var transferSolution = cloneSolution.SplitSolution(transferAmount); foreach (var reagentQuantity in transferSolution.Contents.ToArray()) @@ -51,7 +51,7 @@ namespace Content.Server.Chemistry.Components } var bloodstreamSys = EntitySystem.Get(); - bloodstreamSys.TryAddToBloodstream(entity, transferSolution, bloodstream); + bloodstreamSys.TryAddToChemicals(entity, transferSolution, bloodstream); } diff --git a/Content.Server/Chemistry/EntitySystems/ChemistrySystem.Injector.cs b/Content.Server/Chemistry/EntitySystems/ChemistrySystem.Injector.cs index 50fc86964a..a94ac2fd96 100644 --- a/Content.Server/Chemistry/EntitySystems/ChemistrySystem.Injector.cs +++ b/Content.Server/Chemistry/EntitySystems/ChemistrySystem.Injector.cs @@ -44,7 +44,7 @@ public sealed partial class ChemistrySystem } private void UseInjector(EntityUid target, EntityUid user, InjectorComponent component) - { + { // Handle injecting/drawing for solutions if (component.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject) { @@ -247,7 +247,7 @@ public sealed partial class ChemistrySystem private void TryInjectIntoBloodstream(InjectorComponent component, BloodstreamComponent targetBloodstream, EntityUid user) { // Get transfer amount. May be smaller than _transferAmount if not enough room - var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetBloodstream.Solution.AvailableVolume); + var realTransferAmount = FixedPoint2.Min(component.TransferAmount, targetBloodstream.ChemicalSolution.AvailableVolume); if (realTransferAmount <= 0) { @@ -257,9 +257,9 @@ public sealed partial class ChemistrySystem } // Move units from attackSolution to targetSolution - var removedSolution = _solutions.SplitSolution(user, targetBloodstream.Solution, realTransferAmount); + var removedSolution = _solutions.SplitSolution(user, targetBloodstream.ChemicalSolution, realTransferAmount); - _blood.TryAddToBloodstream((targetBloodstream).Owner, removedSolution, targetBloodstream); + _blood.TryAddToChemicals((targetBloodstream).Owner, removedSolution, targetBloodstream); removedSolution.DoEntityReaction(targetBloodstream.Owner, ReactionMethod.Injection); diff --git a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs index e26ca86fac..bbebedb4a4 100644 --- a/Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs +++ b/Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs @@ -38,7 +38,7 @@ namespace Content.Server.Chemistry.EntitySystems var solToInject = solRemoved.SplitSolution(solRemovedVol * component.TransferEfficiency); - _bloodstreamSystem.TryAddToBloodstream((args.OtherFixture.Body).Owner, solToInject, bloodstream); + _bloodstreamSystem.TryAddToChemicals((args.OtherFixture.Body).Owner, solToInject, bloodstream); } } } diff --git a/Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs b/Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs new file mode 100644 index 0000000000..ba3dde02be --- /dev/null +++ b/Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs @@ -0,0 +1,25 @@ +using Content.Server.Body.Components; +using Content.Server.Body.Systems; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.FixedPoint; + +namespace Content.Server.Chemistry.ReagentEffects; + +public sealed class ModifyBleedAmount : ReagentEffect +{ + [DataField("scaled")] + public bool Scaled = false; + + [DataField("amount")] + public float Amount = -1.0f; + + public override void Effect(ReagentEffectArgs args) + { + if (args.EntityManager.TryGetComponent(args.SolutionEntity, out var blood)) + { + var sys = EntitySystem.Get(); + var amt = Scaled ? Amount * args.Quantity.Float() : Amount; + sys.TryModifyBleedAmount(args.SolutionEntity, amt, blood); + } + } +} diff --git a/Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs b/Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs new file mode 100644 index 0000000000..5b0ffe5c40 --- /dev/null +++ b/Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs @@ -0,0 +1,25 @@ +using Content.Server.Body.Components; +using Content.Server.Body.Systems; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.FixedPoint; + +namespace Content.Server.Chemistry.ReagentEffects; + +public sealed class ModifyBloodLevel : ReagentEffect +{ + [DataField("scaled")] + public bool Scaled = false; + + [DataField("amount")] + public FixedPoint2 Amount = 1.0f; + + public override void Effect(ReagentEffectArgs args) + { + if (args.EntityManager.TryGetComponent(args.SolutionEntity, out var blood)) + { + var sys = EntitySystem.Get(); + var amt = Scaled ? Amount * args.Quantity : Amount; + sys.TryModifyBloodLevel(args.SolutionEntity, amt, blood); + } + } +} diff --git a/Content.Server/Medical/Components/HealingComponent.cs b/Content.Server/Medical/Components/HealingComponent.cs index 78bceb21a9..0ae08a435b 100644 --- a/Content.Server/Medical/Components/HealingComponent.cs +++ b/Content.Server/Medical/Components/HealingComponent.cs @@ -18,6 +18,14 @@ namespace Content.Server.Medical.Components [ViewVariables(VVAccess.ReadWrite)] public DamageSpecifier Damage = default!; + /// + /// This should generally be negative, + /// since you're, like, trying to heal damage. + /// + [DataField("bloodlossModifier")] + [ViewVariables(VVAccess.ReadWrite)] + public float BloodlossModifier = 0.0f; + /// /// The supported damage types are specified using a s. For a /// HealingComponent this filters what damage container type this component should work on. If null, diff --git a/Content.Server/Medical/HealingSystem.cs b/Content.Server/Medical/HealingSystem.cs index 3c0d7c1c2f..cc7c05b141 100644 --- a/Content.Server/Medical/HealingSystem.cs +++ b/Content.Server/Medical/HealingSystem.cs @@ -1,5 +1,6 @@ using System.Threading; using Content.Server.Administration.Logs; +using Content.Server.Body.Systems; using Content.Server.DoAfter; using Content.Server.Medical.Components; using Content.Server.Stack; @@ -17,6 +18,7 @@ public sealed class HealingSystem : EntitySystem [Dependency] private readonly ActionBlockerSystem _blocker = default!; [Dependency] private readonly AdminLogSystem _logs = default!; [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!; [Dependency] private readonly DoAfterSystem _doAfter = default!; [Dependency] private readonly StackSystem _stacks = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; @@ -37,6 +39,12 @@ public sealed class HealingSystem : EntitySystem if (component.DamageContainerID is not null && !component.DamageContainerID.Equals(component.DamageContainerID)) return; + if (args.Component.BloodlossModifier != 0) + { + // Heal some bloodloss damage. + _bloodstreamSystem.TryModifyBleedAmount(uid, args.Component.BloodlossModifier); + } + var healed = _damageable.TryChangeDamage(uid, args.Component.Damage, true); // Reverify that we can heal the damage. diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs index 356b4792b2..6243e2593f 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs @@ -117,7 +117,7 @@ namespace Content.Server.Nutrition.EntitySystems continue; _reactiveSystem.ReactionEntity(containerManager.Owner, ReactionMethod.Ingestion, inhaledSolution); - _bloodstreamSystem.TryAddToBloodstream(containerManager.Owner, inhaledSolution, bloodstream); + _bloodstreamSystem.TryAddToChemicals(containerManager.Owner, inhaledSolution, bloodstream); } _timer -= UpdateTimer; diff --git a/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs b/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs index 1818e440e3..83a6118ea2 100644 --- a/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs +++ b/Content.Server/Weapon/Melee/MeleeWeaponSystem.cs @@ -295,7 +295,7 @@ namespace Content.Server.Weapon.Melee foreach (var bloodstream in hitBloodstreams) { var individualInjection = solutionToInject.SplitSolution(volPerBloodstream); - _bloodstreamSystem.TryAddToBloodstream((bloodstream).Owner, individualInjection, bloodstream); + _bloodstreamSystem.TryAddToChemicals((bloodstream).Owner, individualInjection, bloodstream); } } diff --git a/Resources/Audio/Effects/Fluids/ATTRIBUTION.txt b/Resources/Audio/Effects/Fluids/ATTRIBUTION.txt new file mode 100644 index 0000000000..78c9a1114a --- /dev/null +++ b/Resources/Audio/Effects/Fluids/ATTRIBUTION.txt @@ -0,0 +1,2 @@ +blood1.ogg under CC-0 from https://freesound.org/people/kyles/sounds/453769/ +blood2.ogg under CC-BY 3.0 from https://freesound.org/people/EminYILDIRIM/sounds/554284/ \ No newline at end of file diff --git a/Resources/Audio/Effects/Fluids/blood1.ogg b/Resources/Audio/Effects/Fluids/blood1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..381d4995173d8591a020f317a9869c3527f6bba7 GIT binary patch literal 21628 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 Ia2|jW0Ei8uk^lez literal 0 HcmV?d00001 diff --git a/Resources/Audio/Effects/Fluids/blood2.ogg b/Resources/Audio/Effects/Fluids/blood2.ogg new file mode 100644 index 0000000000000000000000000000000000000000..cec135b80ad75c09fe790ae480ad7d3f2b715840 GIT binary patch literal 23404 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%0DqKFu!ZzMx2Cqx;2Jjm`O5fJ-ZLqGSF z3Wy0Tcbtd}vZbjdzWOny`c^*|ipwvJmt{})^LwiG94WWG($_cV#q}PKzx7Vn*VA1m z`1wBc&Jp;qWb|q!yyH_#MpA3BLsI|@4X=Zq5QeL}(N+a_qedyTZ%$f@^gLK5y%-dV z9R41z&d%DaO{{7H37uI{7ynz}b^o^5_ngT7`CT=8pRa)62>8d>f&$5U2O6cmQk$Ww-)vVqlaKB6TjGi)F=GukQad@%5A3)7F2tXNw(cel{?h z|Nk0qLsD%zBSI`E+1eOCcifK^WXnX%J|*;y2Lm3I(bED-?zz> z+yF%V%3*dx7D!TL9m_-Z5uipya6j9=x>m^Cr9{)&M}KT=B1^xPZ z?-SF9VXEtNUvIX@zQ6a&sp*dsKfT#g`J&$nNgwQW0CKcr6B)$`2LVhKcg)PtMH3te zohn{wY`a)8-~+7L3iSbhE?Jf*;nV9*TT^ap`p~KKQeHx)*1j%cKm8@TMPrFn%KB;M zomtUOZ~adeOus*guQ*u2kM7H498~3)IsjB5OOi%IJ#pHYRJ@XmjKmcY-1{4q7X)dV zawB)x^z`n#A5L!`P7m{ZfPen;XMtZ3 z5>~}Oaib*zMbZD-8UiF8uXayJLv&i$RTZzz_Efubm<)Y@sEVQ64TKtELO`Ew=y$J_ zTq;GcYn<`>o-`lQKVg>ws#mjs2IuAW+>=9AdSTJ0 zs6PF58>v@pC&T!@a{l=6vCbbVIe(i3dd~iSZf#y|q({Q1Qg>Q&blLt!ibnx7Ew9;t zF)D=ad9znv6ctj6|kIgxx7{d7kO=@ZA+OmCwUp&8P2DKuE30iK zdQK!3ANc`tXVJ8GfXzLyaws)vAb%x=s_1-Hg!-sHo~IsOe7*2=N@zP?_5U?a@D+%0bmKmar;cQ?aMUgqG0ewK1~*7n@Kt`};5 z_yH!mbo4F2cyh;nL)HbU2kv717S$EeE=uL+aqZUExA%bSorgbK8Mc%AbAFfiC&^zs zfn5mrS2}e&Dm!oUg$oNPVndTIWVXb8lTA06$kmbe{qO(l->2R_@mjtsZ};!}?fq3fS39;b zWB3q_YN!R9N~I~61nwd1c1=g#Dm_E}Zgz8TZHIQZY22r)pImzwS$FYb$(^MsTSF(c zQz@2Z?V7Uimc!^Pb@XKTl+Pc_WiIcN&}eOl<&=T8oCmRzYJpfO$!L1fByCWE7N;Ws zKFZ$Q^iZ^m9Heq+0!F23=i?p3*R%F$nw}-jQA4Yz1V+pRQ26>;QH{DrZpyv9-@k{! z3}f3I)#d*F{M?wR_w2tfxhKQtVYVoGE7@mYe&w$0(bfBhGyKp0r+WJBx93d4Q0e&E z4!HhzfBo-21|vS`5sCqT(Cn!+@Bn%bs6sFT zo8`tpeidjZNCDs&7%-9z!Jq*Ch2+Z^C2-LliE2Qaj3Mo53Nk^9lN!+`2Tj+vBj;Qe zqa9v_8y2CVsv1E_0L}}$(bErKZTr>G?5uW!+0h$@<3_2UpBz`;VR&Fr&+vw zw(nm40L!p)`X5w*C41cTYyI>E)zhwrbNe9E=^1( z7!bvp0z)T*aS@mCv{nXCm-Dk4ToBf@gKWBEI+0^91&iwne zKl{)4S{(i>PJN)5qIJBl=R4$NpMmcC{n=-keYmN~Q`0@0AO4*4b@~4L;5+{PbnwOp z{?f(WKc0s10|0gUj=vf64?$A2x#=ubSRoaEQv59Z_AQ-NGj=0L3R^ODscGif8P@>2 z=t2h=3y^Oxk{;I%4~>T#Tq1xbAs>bpq>%}hLn{@M0N#ZZsRkP$H%2u5Lb5T1%z*}) z+8ZyB#7YXE)tW?|0w!YaeBjh79O1d`8~L@eCb@K+j7;&7lQV7E%ln$c zy7l|L%4#B<_5HN7@7?v^U(#;I|NVcw*{1HzSo<>tr;_jaH#1o$6OzgM!^89xZ!7o7 zbB3Q^(>cF~`ct`oKGl1E?`F^0`}gMboL!w9h6MN~2ah{^*%L4hz)$x7A7{3ndgktb z^{)O`|NZRhZSCq^z3bW4yPk3H>ZzN(bz>KH=VNMp*uOV*%=nLK;^1^@LrK&(QOl%n zCW}Eir6ys}hj=qiK&b46*`^Ijf&dNy6#Y%Qa|qsrbjy?^1*FtNtz--tTP?s%&_m7i zR(?p&OQ3G$@jU&Z!1S$|jaF47R8^T7z*KkVe4N8LneS^}<+|GL!=LwQPD<=W-)!8HG8ZgM~9hb&SVwF%XJMf zT_iwv{pal4-|x>9U31l$?3!%;=l*y5`~Ch@p2~SV*_;`OeVd%Z7@OAC~ zU)%qG1^@pF{{I#HKj4?Y@yk`f^?w1s{B6$WY|gI!pWYXS<=hp!Uh=SMrYb#}mrwrSyVoz>>D+h9T6@(-RKyp5wcFd1uRJu`o^ zy$_h)Kf9UUrRl6`NC0}@{r_M)%68O0e!i-gErTo1oAtbp^FLg9_+TpL>i^9wsLMU| zygikB{?*Ktp8~kLg8w#ueEbD`fBoDHyWZu09{7)6wyy(~^w875-AC~6|8f7}V*KVc zQ`bB0KMYeJa{#^-;s_|ve4mnt5deTM)}#Wyh5YYGyu=Q8LrrlD`QMRvi5>8Unj!!I L0000000000Yg+~S literal 0 HcmV?d00001 diff --git a/Resources/Locale/en-US/bloodstream/bloodstream.ftl b/Resources/Locale/en-US/bloodstream/bloodstream.ftl new file mode 100644 index 0000000000..96996b6b91 --- /dev/null +++ b/Resources/Locale/en-US/bloodstream/bloodstream.ftl @@ -0,0 +1 @@ +bloodstream-component-looks-pale = [color=bisque]{CAPITALIZE(SUBJECT($target))} looks pale.[/color] diff --git a/Resources/Prototypes/Damage/modifier_sets.yml b/Resources/Prototypes/Damage/modifier_sets.yml index f3b10b32f2..2a8bc3f7fc 100644 --- a/Resources/Prototypes/Damage/modifier_sets.yml +++ b/Resources/Prototypes/Damage/modifier_sets.yml @@ -72,3 +72,20 @@ Cold: 1.5 Poison: 0.8 +# Represents which damage types should be modified +# in relation to how they cause bloodloss damage. +- type: damageModifierSet + id: BloodlossHuman + coefficients: + Blunt: 0.8 + Slash: 2.5 + Piercing: 2.0 + Shock: 0.0 + Cold: 0.0 + Heat: 0.0 + Poison: 0.0 + Radiation: 0.0 + Asphyxiation: 0.0 + Bloodloss: 0.0 # no double dipping + Cellular: 0.0 + diff --git a/Resources/Prototypes/Entities/Effects/puddle.yml b/Resources/Prototypes/Entities/Effects/puddle.yml index 42df573f26..24b2a86c0a 100644 --- a/Resources/Prototypes/Entities/Effects/puddle.yml +++ b/Resources/Prototypes/Entities/Effects/puddle.yml @@ -88,6 +88,25 @@ visuals: - type: PuddleVisualizer +- type: entity + id: PuddleBlood + name: blood + description: This can't be a good sign. + parent: PuddleBase + components: + - type: Sprite + sprite: Fluids/splatter.rsi # Placeholder + state: splatter-0 + netsync: false + - type: Puddle + slipThreshold: 20 + - type: Evaporation + evaporateTime: 20 # 4 times slower than normal + - type: Appearance + visuals: + - type: PuddleVisualizer + recolor: true + - type: entity name: vomit id: PuddleVomit diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml index b906cfb533..d956ca3a28 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/simplemob.yml @@ -53,7 +53,14 @@ - Opaque - type: SolutionContainerManager - type: Bloodstream - max_volume: 100 + bloodlossDamage: + types: + Bloodloss: + 1 + bloodlossHealDamage: + types: + Bloodloss: + -0.25 - type: Damageable damageContainer: Biological - type: AtmosExposed diff --git a/Resources/Prototypes/Entities/Mobs/Species/human.yml b/Resources/Prototypes/Entities/Mobs/Species/human.yml index 5afb2bfabc..6ac8e820b3 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/human.yml @@ -44,16 +44,19 @@ types: Blunt: 2 # Organs - - type: SolutionContainerManager - solutions: - bloodstream: - maxVol: 250 - type: InjectableSolution - solution: bloodstream + solution: chemicals - type: DrawableSolution solution: bloodstream - type: Bloodstream - max_volume: 100 + bloodlossDamage: + types: + Bloodloss: + 1 + bloodlossHealDamage: + types: + Bloodloss: + -0.25 - type: StatusEffects allowed: - Stun diff --git a/Resources/Prototypes/Entities/Mobs/Species/slime.yml b/Resources/Prototypes/Entities/Mobs/Species/slime.yml index 58666b2515..175de37085 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/slime.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/slime.yml @@ -99,6 +99,16 @@ - type: Damageable damageContainer: Biological damageModifierSet: Slime + - type: Bloodstream + bloodReagent: Slime # TODO Color slime blood based on their slime color or smth + bloodlossDamage: + types: + Bloodloss: + 1 + bloodlossHealDamage: + types: + Bloodloss: + -0.25 - type: Barotrauma damage: types: diff --git a/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml b/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml index 267bcaa9aa..a68581c068 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Medical/healing.yml @@ -39,6 +39,7 @@ damage: groups: Brute: -15 # 5 for each type in the group + bloodlossModifier: -3 # a little bit of bloodloss healing. - type: Stack stackType: Brutepack @@ -57,8 +58,9 @@ damageContainer: Biological damage: types: - Slash: -5 - Piercing: -5 + Slash: -2.5 + Piercing: -2.5 + bloodlossModifier: -10 - type: Stack stackType: Gauze diff --git a/Resources/Prototypes/Reagents/biological.yml b/Resources/Prototypes/Reagents/biological.yml new file mode 100644 index 0000000000..a89831a77b --- /dev/null +++ b/Resources/Prototypes/Reagents/biological.yml @@ -0,0 +1,30 @@ +- type: reagent + id: Blood + name: blood + group: Biological + desc: I hope this is ketchup. + color: "#9e1010" + physicalDesc: ferrous + metabolisms: + Drink: + # Quenching! + effects: + - !type:SatiateThirst + factor: 1.5 + plantMetabolism: + - !type:PlantAdjustWater + amount: 0.5 + +- type: reagent + id: Slime + name: slime + group: Biological + desc: You thought this was gradient blood at first, but you were mistaken. + color: "#2cf274" + physicalDesc: viscous + metabolisms: + Food: + # Delicious! + effects: + - !type:SatiateHunger + factor: 1.5 diff --git a/Resources/Prototypes/Reagents/elements.yml b/Resources/Prototypes/Reagents/elements.yml index 9b0f65b619..c376f320e0 100644 --- a/Resources/Prototypes/Reagents/elements.yml +++ b/Resources/Prototypes/Reagents/elements.yml @@ -105,6 +105,12 @@ color: "#434b4d" boilingPoint: 2862.0 meltingPoint: 1538.0 + metabolisms: + Medicine: + effects: + - !type:ModifyBloodLevel + scaled: true # scales 2x with the amount of iron you consume + amount: 2 - type: reagent id: Lithium diff --git a/Resources/Prototypes/Reagents/medicine.yml b/Resources/Prototypes/Reagents/medicine.yml index c44a4040b9..7014f7d725 100644 --- a/Resources/Prototypes/Reagents/medicine.yml +++ b/Resources/Prototypes/Reagents/medicine.yml @@ -158,7 +158,7 @@ id: Dexalin name: dexalin group: Medicine - desc: Used for treating oxygen deprivation. In most cases where it is likely to be needed, the strength of Dexalin Plus will probably be more useful (Results in 1 unit instead of 2). + desc: Used for treating oxygen deprivation. In most cases where it is likely to be needed, the strength of Dexalin Plus will probably be more useful. physicalDesc: opaque color: "#0041a8" metabolisms: @@ -168,12 +168,13 @@ damage: types: Asphyxiation: -1 + Bloodloss: -0.5 - type: reagent id: DexalinPlus name: dexalin plus group: Medicine - desc: Used in treatment of extreme cases of oxygen deprivation. Even a single unit immediately counters all oxygen loss, which is hugely useful in many circumstances. Any dose beyond this will continue to counter oxygen loss until it is metabolized, essentially removing the need to breathe. + desc: Used in treatment of extreme cases of oxygen deprivation. Effective at healing blood loss damage. physicalDesc: cloudy color: "#4da0bd" metabolisms: @@ -183,6 +184,7 @@ damage: types: Asphyxiation: -3 + Bloodloss: -2 - type: reagent id: Ethylredoxrazine @@ -299,7 +301,7 @@ id: Inaprovaline name: inaprovaline group: Medicine - desc: Inaprovaline is a synaptic stimulant and cardiostimulant. Commonly used to stabilize patients- it stops oxygen loss when the patient is in critical health. It'll also slow down bleeding (internal or external) by half while in the body. Acts as a decent painkiller. + desc: Inaprovaline is a synaptic stimulant and cardiostimulant. Commonly used to stabilize patients- it stops oxygen loss when the patient is in critical health. It'll also slow down bleeding by a good amount. Acts as a decent painkiller. physicalDesc: opaque color: "#731024" metabolisms: @@ -313,6 +315,9 @@ damage: types: Asphyxiation: -5 + - !type:ModifyBleedAmount + scaled: true + amount: -0.25 - type: reagent id: Kelotane @@ -443,6 +448,28 @@ physicalDesc: strong-smelling color: "#2f6ed4" +- type: reagent + id: TranexamicAcid + name: tranexamic acid + group: Medicine + desc: A blood clotting medicine for preventing heavy bleeding. Very dangerous in large quantities. + physicalDesc: viscous + color: "#ba7d7d" + metabolisms: + Medicine: + effects: + # Medium-large quantities can hurt you instead, + # but still technically stop your bleeding. + - !type:ModifyBleedAmount + scaled: true + - !type:HealthChange + conditions: + - !type:ReagentThreshold + min: 15 + damage: + types: + Bloodloss: 3 + - type: reagent id: Tricordrazine name: tricordrazine diff --git a/Resources/Prototypes/Recipes/Reactions/medicine.yml b/Resources/Prototypes/Recipes/Reactions/medicine.yml index 0ab9f3582a..5346c34e90 100644 --- a/Resources/Prototypes/Recipes/Reactions/medicine.yml +++ b/Resources/Prototypes/Recipes/Reactions/medicine.yml @@ -212,6 +212,18 @@ products: Inaprovaline: 3 +- type: reaction + id: TranexamicAcid + reactants: + Inaprovaline: + amount: 1 + SulfuricAcid: + amount: 1 + Glucose: + amount: 1 + products: + TranexamicAcid: 3 + - type: reaction id: Kelotane reactants: diff --git a/Resources/Prototypes/SoundCollections/gib.yml b/Resources/Prototypes/SoundCollections/gib.yml index 5c39aacb3f..e4d00914ab 100644 --- a/Resources/Prototypes/SoundCollections/gib.yml +++ b/Resources/Prototypes/SoundCollections/gib.yml @@ -4,3 +4,9 @@ - /Audio/Effects/gib1.ogg - /Audio/Effects/gib2.ogg - /Audio/Effects/gib3.ogg + +- type: soundCollection + id: blood + files: + - /Audio/Effects/Fluids/blood1.ogg + - /Audio/Effects/Fluids/blood2.ogg