Basic bleeding mechanics (#6710)
This commit is contained in:
@@ -1,28 +1,136 @@
|
|||||||
using Content.Server.Atmos;
|
|
||||||
using Content.Server.Body.Systems;
|
using Content.Server.Body.Systems;
|
||||||
using Content.Shared.Atmos;
|
|
||||||
using Content.Shared.Chemistry.Components;
|
using Content.Shared.Chemistry.Components;
|
||||||
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Damage.Prototypes;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Robust.Shared.Analyzers;
|
using Content.Shared.Sound;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
using Robust.Shared.ViewVariables;
|
|
||||||
|
|
||||||
namespace Content.Server.Body.Components
|
namespace Content.Server.Body.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent, Friend(typeof(BloodstreamSystem))]
|
[RegisterComponent, Friend(typeof(BloodstreamSystem))]
|
||||||
public sealed class BloodstreamComponent : Component
|
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;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("maxVolume")]
|
/// <remarks>
|
||||||
public FixedPoint2 InitialMaxVolume = FixedPoint2.New(250);
|
/// This generally corresponds to an amount of damage and can't go above 100.
|
||||||
|
/// </remarks>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float BleedAmount;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How much should bleeding should be reduced every update interval?
|
||||||
|
/// </summary>
|
||||||
|
[DataField("bleedReductionAmount")]
|
||||||
|
public float BleedReductionAmount = 1.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What percentage of current blood is necessary to avoid dealing blood loss damage?
|
||||||
|
/// </summary>
|
||||||
|
[DataField("bloodlossThreshold")]
|
||||||
|
public float BloodlossThreshold = 0.9f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The base bloodloss damage to be incurred if below <see cref="BloodlossThreshold"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField("bloodlossDamage", required: true)]
|
||||||
|
public DamageSpecifier BloodlossDamage = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The base bloodloss damage to be healed if above <see cref="BloodlossThreshold"/>
|
||||||
|
/// </summary>
|
||||||
|
[DataField("bloodlossHealDamage", required: true)]
|
||||||
|
public DamageSpecifier BloodlossHealDamage = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How frequently should this bloodstream update, in seconds?
|
||||||
|
/// </summary>
|
||||||
|
[DataField("updateInterval")]
|
||||||
|
public float UpdateInterval = 5.0f;
|
||||||
|
|
||||||
|
// 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("bloodRefreshAmount")]
|
||||||
|
public float BloodRefreshAmount = 0.2f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How much blood needs to be in the temporary solution in order to create a puddle?
|
||||||
|
/// </summary>
|
||||||
|
[DataField("bleedPuddleThreshold")]
|
||||||
|
public FixedPoint2 BleedPuddleThreshold = 10.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("damageBleedModifiers", customTypeSerializer:typeof(PrototypeIdSerializer<DamageModifierSetPrototype>))]
|
||||||
|
public string DamageBleedModifiers = "BloodlossHuman";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The sound to be played when a weapon instantly deals blood loss damage.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("instantBloodSound")]
|
||||||
|
public SoundSpecifier InstantBloodSound = new SoundCollectionSpecifier("blood");
|
||||||
|
|
||||||
|
// TODO probably damage bleed thresholds.
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max volume of internal chemical solution storage
|
||||||
|
/// </summary>
|
||||||
|
[DataField("chemicalMaxVolume")]
|
||||||
|
public FixedPoint2 ChemicalMaxVolume = FixedPoint2.New(250);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max volume of internal blood storage,
|
||||||
|
/// and starting level of blood.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("bloodMaxVolume")]
|
||||||
|
public FixedPoint2 BloodMaxVolume = FixedPoint2.New(300);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Which reagent is considered this entities 'blood'?
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Slime-people might use slime as their blood or something like that.
|
||||||
|
/// </remarks>
|
||||||
|
[DataField("bloodReagent")]
|
||||||
|
public string BloodReagent = "Blood";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Internal solution for reagent storage
|
/// Internal solution for reagent storage
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public Solution Solution = default!;
|
public Solution ChemicalSolution = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Internal solution for blood storage
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public Solution BloodSolution = default!;
|
||||||
|
|
||||||
|
/// <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(VVAccess.ReadWrite)]
|
||||||
|
public Solution BloodTemporarySolution = default!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ namespace Content.Server.Body.Components
|
|||||||
/// From which solution will this metabolizer attempt to metabolize chemicals
|
/// From which solution will this metabolizer attempt to metabolize chemicals
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("solution")]
|
[DataField("solution")]
|
||||||
public string SolutionName { get; set; } = BloodstreamSystem.DefaultSolutionName;
|
public string SolutionName { get; set; } = BloodstreamComponent.DefaultChemicalsSolutionName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Does this component use a solution on it's parent entity (the body) or itself
|
/// Does this component use a solution on it's parent entity (the body) or itself
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace Content.Server.Body.Components
|
|||||||
/// What solution should this stomach push reagents into, on the body?
|
/// What solution should this stomach push reagents into, on the body?
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("bodySolutionName")]
|
[DataField("bodySolutionName")]
|
||||||
public string BodySolutionName = BloodstreamSystem.DefaultSolutionName;
|
public string BodySolutionName = BloodstreamComponent.DefaultChemicalsSolutionName;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initial internal solution storage volume
|
/// Initial internal solution storage volume
|
||||||
|
|||||||
@@ -1,47 +1,193 @@
|
|||||||
using System;
|
using System.Linq;
|
||||||
using Content.Server.Atmos;
|
|
||||||
using Content.Server.Atmos.EntitySystems;
|
|
||||||
using Content.Server.Body.Components;
|
using Content.Server.Body.Components;
|
||||||
using Content.Server.Chemistry.EntitySystems;
|
using Content.Server.Chemistry.EntitySystems;
|
||||||
|
using Content.Server.Fluids.EntitySystems;
|
||||||
using Content.Shared.Chemistry.Components;
|
using Content.Shared.Chemistry.Components;
|
||||||
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Damage.Prototypes;
|
||||||
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using Robust.Shared.GameObjects;
|
using Content.Shared.MobState.Components;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
namespace Content.Server.Body.Systems;
|
namespace Content.Server.Body.Systems;
|
||||||
|
|
||||||
public sealed class BloodstreamSystem : EntitySystem
|
public sealed class BloodstreamSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||||
[Dependency] private readonly AtmosphereSystem _atmosSystem = default!;
|
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
||||||
[Dependency] private readonly RespiratorSystem _respiratorSystem = 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()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<BloodstreamComponent, ComponentInit>(OnComponentInit);
|
SubscribeLocalEvent<BloodstreamComponent, ComponentInit>(OnComponentInit);
|
||||||
|
SubscribeLocalEvent<BloodstreamComponent, DamageChangedEvent>(OnDamageChanged);
|
||||||
|
SubscribeLocalEvent<BloodstreamComponent, ExaminedEvent>(OnExamined);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
foreach (var bloodstream in EntityManager.EntityQuery<BloodstreamComponent>())
|
||||||
|
{
|
||||||
|
bloodstream.AccumulatedFrametime += frameTime;
|
||||||
|
|
||||||
|
if (bloodstream.AccumulatedFrametime < bloodstream.UpdateInterval)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bloodstream.AccumulatedFrametime -= bloodstream.UpdateInterval;
|
||||||
|
|
||||||
|
var uid = bloodstream.Owner;
|
||||||
|
if (TryComp<MobStateComponent>(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)
|
private void OnComponentInit(EntityUid uid, BloodstreamComponent component, ComponentInit args)
|
||||||
{
|
{
|
||||||
component.Solution = _solutionContainerSystem.EnsureSolution(uid, DefaultSolutionName);
|
component.ChemicalSolution = _solutionContainerSystem.EnsureSolution(uid, BloodstreamComponent.DefaultChemicalsSolutionName);
|
||||||
if (component.Solution != null)
|
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<DamageModifierSetPrototype>(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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempt to transfer provided solution to internal solution.
|
/// Attempt to transfer provided solution to internal solution.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
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))
|
if (!Resolve(uid, ref component, false))
|
||||||
return 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to modify the blood level of this entity directly.
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to make an entity bleed more or less
|
||||||
|
/// </summary>
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,10 +61,10 @@ namespace Content.Server.Chemistry.Components
|
|||||||
|
|
||||||
var cloneSolution = solution.Clone();
|
var cloneSolution = solution.Clone();
|
||||||
var transferAmount = FixedPoint2.Min(cloneSolution.TotalVolume * solutionFraction * (1 - protection),
|
var transferAmount = FixedPoint2.Min(cloneSolution.TotalVolume * solutionFraction * (1 - protection),
|
||||||
bloodstream.Solution.AvailableVolume);
|
bloodstream.ChemicalSolution.AvailableVolume);
|
||||||
var transferSolution = cloneSolution.SplitSolution(transferAmount);
|
var transferSolution = cloneSolution.SplitSolution(transferAmount);
|
||||||
|
|
||||||
bloodstreamSys.TryAddToBloodstream(entity, transferSolution, bloodstream);
|
bloodstreamSys.TryAddToChemicals(entity, transferSolution, bloodstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnKill()
|
protected override void OnKill()
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ namespace Content.Server.Chemistry.Components
|
|||||||
|
|
||||||
var chemistry = EntitySystem.Get<ReactiveSystem>();
|
var chemistry = EntitySystem.Get<ReactiveSystem>();
|
||||||
var cloneSolution = solution.Clone();
|
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);
|
var transferSolution = cloneSolution.SplitSolution(transferAmount);
|
||||||
|
|
||||||
foreach (var reagentQuantity in transferSolution.Contents.ToArray())
|
foreach (var reagentQuantity in transferSolution.Contents.ToArray())
|
||||||
@@ -51,7 +51,7 @@ namespace Content.Server.Chemistry.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
var bloodstreamSys = EntitySystem.Get<BloodstreamSystem>();
|
var bloodstreamSys = EntitySystem.Get<BloodstreamSystem>();
|
||||||
bloodstreamSys.TryAddToBloodstream(entity, transferSolution, bloodstream);
|
bloodstreamSys.TryAddToChemicals(entity, transferSolution, bloodstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public sealed partial class ChemistrySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void UseInjector(EntityUid target, EntityUid user, InjectorComponent component)
|
private void UseInjector(EntityUid target, EntityUid user, InjectorComponent component)
|
||||||
{
|
{
|
||||||
// Handle injecting/drawing for solutions
|
// Handle injecting/drawing for solutions
|
||||||
if (component.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject)
|
if (component.ToggleState == SharedInjectorComponent.InjectorToggleMode.Inject)
|
||||||
{
|
{
|
||||||
@@ -247,7 +247,7 @@ public sealed partial class ChemistrySystem
|
|||||||
private void TryInjectIntoBloodstream(InjectorComponent component, BloodstreamComponent targetBloodstream, EntityUid user)
|
private void TryInjectIntoBloodstream(InjectorComponent component, BloodstreamComponent targetBloodstream, EntityUid user)
|
||||||
{
|
{
|
||||||
// Get transfer amount. May be smaller than _transferAmount if not enough room
|
// 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)
|
if (realTransferAmount <= 0)
|
||||||
{
|
{
|
||||||
@@ -257,9 +257,9 @@ public sealed partial class ChemistrySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Move units from attackSolution to targetSolution
|
// 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);
|
removedSolution.DoEntityReaction(targetBloodstream.Owner, ReactionMethod.Injection);
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
|||||||
|
|
||||||
var solToInject = solRemoved.SplitSolution(solRemovedVol * component.TransferEfficiency);
|
var solToInject = solRemoved.SplitSolution(solRemovedVol * component.TransferEfficiency);
|
||||||
|
|
||||||
_bloodstreamSystem.TryAddToBloodstream((args.OtherFixture.Body).Owner, solToInject, bloodstream);
|
_bloodstreamSystem.TryAddToChemicals((args.OtherFixture.Body).Owner, solToInject, bloodstream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
25
Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs
Normal file
25
Content.Server/Chemistry/ReagentEffects/ModifyBleedAmount.cs
Normal file
@@ -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<BloodstreamComponent>(args.SolutionEntity, out var blood))
|
||||||
|
{
|
||||||
|
var sys = EntitySystem.Get<BloodstreamSystem>();
|
||||||
|
var amt = Scaled ? Amount * args.Quantity.Float() : Amount;
|
||||||
|
sys.TryModifyBleedAmount(args.SolutionEntity, amt, blood);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs
Normal file
25
Content.Server/Chemistry/ReagentEffects/ModifyBloodLevel.cs
Normal file
@@ -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<BloodstreamComponent>(args.SolutionEntity, out var blood))
|
||||||
|
{
|
||||||
|
var sys = EntitySystem.Get<BloodstreamSystem>();
|
||||||
|
var amt = Scaled ? Amount * args.Quantity : Amount;
|
||||||
|
sys.TryModifyBloodLevel(args.SolutionEntity, amt, blood);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,14 @@ namespace Content.Server.Medical.Components
|
|||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
public DamageSpecifier Damage = default!;
|
public DamageSpecifier Damage = default!;
|
||||||
|
|
||||||
|
/// <remarks>
|
||||||
|
/// This should generally be negative,
|
||||||
|
/// since you're, like, trying to heal damage.
|
||||||
|
/// </remarks>
|
||||||
|
[DataField("bloodlossModifier")]
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float BloodlossModifier = 0.0f;
|
||||||
|
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// The supported damage types are specified using a <see cref="DamageContainerPrototype"/>s. For a
|
/// The supported damage types are specified using a <see cref="DamageContainerPrototype"/>s. For a
|
||||||
/// HealingComponent this filters what damage container type this component should work on. If null,
|
/// HealingComponent this filters what damage container type this component should work on. If null,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Content.Server.Administration.Logs;
|
using Content.Server.Administration.Logs;
|
||||||
|
using Content.Server.Body.Systems;
|
||||||
using Content.Server.DoAfter;
|
using Content.Server.DoAfter;
|
||||||
using Content.Server.Medical.Components;
|
using Content.Server.Medical.Components;
|
||||||
using Content.Server.Stack;
|
using Content.Server.Stack;
|
||||||
@@ -17,6 +18,7 @@ public sealed class HealingSystem : EntitySystem
|
|||||||
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
||||||
[Dependency] private readonly AdminLogSystem _logs = default!;
|
[Dependency] private readonly AdminLogSystem _logs = default!;
|
||||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||||
|
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
|
||||||
[Dependency] private readonly DoAfterSystem _doAfter = default!;
|
[Dependency] private readonly DoAfterSystem _doAfter = default!;
|
||||||
[Dependency] private readonly StackSystem _stacks = default!;
|
[Dependency] private readonly StackSystem _stacks = default!;
|
||||||
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
||||||
@@ -37,6 +39,12 @@ public sealed class HealingSystem : EntitySystem
|
|||||||
if (component.DamageContainerID is not null &&
|
if (component.DamageContainerID is not null &&
|
||||||
!component.DamageContainerID.Equals(component.DamageContainerID)) return;
|
!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);
|
var healed = _damageable.TryChangeDamage(uid, args.Component.Damage, true);
|
||||||
|
|
||||||
// Reverify that we can heal the damage.
|
// Reverify that we can heal the damage.
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
_reactiveSystem.ReactionEntity(containerManager.Owner, ReactionMethod.Ingestion, inhaledSolution);
|
_reactiveSystem.ReactionEntity(containerManager.Owner, ReactionMethod.Ingestion, inhaledSolution);
|
||||||
_bloodstreamSystem.TryAddToBloodstream(containerManager.Owner, inhaledSolution, bloodstream);
|
_bloodstreamSystem.TryAddToChemicals(containerManager.Owner, inhaledSolution, bloodstream);
|
||||||
}
|
}
|
||||||
|
|
||||||
_timer -= UpdateTimer;
|
_timer -= UpdateTimer;
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ namespace Content.Server.Weapon.Melee
|
|||||||
foreach (var bloodstream in hitBloodstreams)
|
foreach (var bloodstream in hitBloodstreams)
|
||||||
{
|
{
|
||||||
var individualInjection = solutionToInject.SplitSolution(volPerBloodstream);
|
var individualInjection = solutionToInject.SplitSolution(volPerBloodstream);
|
||||||
_bloodstreamSystem.TryAddToBloodstream((bloodstream).Owner, individualInjection, bloodstream);
|
_bloodstreamSystem.TryAddToChemicals((bloodstream).Owner, individualInjection, bloodstream);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
2
Resources/Audio/Effects/Fluids/ATTRIBUTION.txt
Normal file
2
Resources/Audio/Effects/Fluids/ATTRIBUTION.txt
Normal file
@@ -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/
|
||||||
BIN
Resources/Audio/Effects/Fluids/blood1.ogg
Normal file
BIN
Resources/Audio/Effects/Fluids/blood1.ogg
Normal file
Binary file not shown.
BIN
Resources/Audio/Effects/Fluids/blood2.ogg
Normal file
BIN
Resources/Audio/Effects/Fluids/blood2.ogg
Normal file
Binary file not shown.
1
Resources/Locale/en-US/bloodstream/bloodstream.ftl
Normal file
1
Resources/Locale/en-US/bloodstream/bloodstream.ftl
Normal file
@@ -0,0 +1 @@
|
|||||||
|
bloodstream-component-looks-pale = [color=bisque]{CAPITALIZE(SUBJECT($target))} looks pale.[/color]
|
||||||
@@ -72,3 +72,20 @@
|
|||||||
Cold: 1.5
|
Cold: 1.5
|
||||||
Poison: 0.8
|
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
|
||||||
|
|
||||||
|
|||||||
@@ -88,6 +88,25 @@
|
|||||||
visuals:
|
visuals:
|
||||||
- type: PuddleVisualizer
|
- 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
|
- type: entity
|
||||||
name: vomit
|
name: vomit
|
||||||
id: PuddleVomit
|
id: PuddleVomit
|
||||||
|
|||||||
@@ -53,7 +53,14 @@
|
|||||||
- Opaque
|
- Opaque
|
||||||
- type: SolutionContainerManager
|
- type: SolutionContainerManager
|
||||||
- type: Bloodstream
|
- type: Bloodstream
|
||||||
max_volume: 100
|
bloodlossDamage:
|
||||||
|
types:
|
||||||
|
Bloodloss:
|
||||||
|
1
|
||||||
|
bloodlossHealDamage:
|
||||||
|
types:
|
||||||
|
Bloodloss:
|
||||||
|
-0.25
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: Biological
|
damageContainer: Biological
|
||||||
- type: AtmosExposed
|
- type: AtmosExposed
|
||||||
|
|||||||
@@ -44,16 +44,19 @@
|
|||||||
types:
|
types:
|
||||||
Blunt: 2
|
Blunt: 2
|
||||||
# Organs
|
# Organs
|
||||||
- type: SolutionContainerManager
|
|
||||||
solutions:
|
|
||||||
bloodstream:
|
|
||||||
maxVol: 250
|
|
||||||
- type: InjectableSolution
|
- type: InjectableSolution
|
||||||
solution: bloodstream
|
solution: chemicals
|
||||||
- type: DrawableSolution
|
- type: DrawableSolution
|
||||||
solution: bloodstream
|
solution: bloodstream
|
||||||
- type: Bloodstream
|
- type: Bloodstream
|
||||||
max_volume: 100
|
bloodlossDamage:
|
||||||
|
types:
|
||||||
|
Bloodloss:
|
||||||
|
1
|
||||||
|
bloodlossHealDamage:
|
||||||
|
types:
|
||||||
|
Bloodloss:
|
||||||
|
-0.25
|
||||||
- type: StatusEffects
|
- type: StatusEffects
|
||||||
allowed:
|
allowed:
|
||||||
- Stun
|
- Stun
|
||||||
|
|||||||
@@ -99,6 +99,16 @@
|
|||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: Biological
|
damageContainer: Biological
|
||||||
damageModifierSet: Slime
|
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
|
- type: Barotrauma
|
||||||
damage:
|
damage:
|
||||||
types:
|
types:
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
damage:
|
damage:
|
||||||
groups:
|
groups:
|
||||||
Brute: -15 # 5 for each type in the group
|
Brute: -15 # 5 for each type in the group
|
||||||
|
bloodlossModifier: -3 # a little bit of bloodloss healing.
|
||||||
- type: Stack
|
- type: Stack
|
||||||
stackType: Brutepack
|
stackType: Brutepack
|
||||||
|
|
||||||
@@ -57,8 +58,9 @@
|
|||||||
damageContainer: Biological
|
damageContainer: Biological
|
||||||
damage:
|
damage:
|
||||||
types:
|
types:
|
||||||
Slash: -5
|
Slash: -2.5
|
||||||
Piercing: -5
|
Piercing: -2.5
|
||||||
|
bloodlossModifier: -10
|
||||||
- type: Stack
|
- type: Stack
|
||||||
stackType: Gauze
|
stackType: Gauze
|
||||||
|
|
||||||
|
|||||||
30
Resources/Prototypes/Reagents/biological.yml
Normal file
30
Resources/Prototypes/Reagents/biological.yml
Normal file
@@ -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
|
||||||
@@ -105,6 +105,12 @@
|
|||||||
color: "#434b4d"
|
color: "#434b4d"
|
||||||
boilingPoint: 2862.0
|
boilingPoint: 2862.0
|
||||||
meltingPoint: 1538.0
|
meltingPoint: 1538.0
|
||||||
|
metabolisms:
|
||||||
|
Medicine:
|
||||||
|
effects:
|
||||||
|
- !type:ModifyBloodLevel
|
||||||
|
scaled: true # scales 2x with the amount of iron you consume
|
||||||
|
amount: 2
|
||||||
|
|
||||||
- type: reagent
|
- type: reagent
|
||||||
id: Lithium
|
id: Lithium
|
||||||
|
|||||||
@@ -158,7 +158,7 @@
|
|||||||
id: Dexalin
|
id: Dexalin
|
||||||
name: dexalin
|
name: dexalin
|
||||||
group: Medicine
|
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
|
physicalDesc: opaque
|
||||||
color: "#0041a8"
|
color: "#0041a8"
|
||||||
metabolisms:
|
metabolisms:
|
||||||
@@ -168,12 +168,13 @@
|
|||||||
damage:
|
damage:
|
||||||
types:
|
types:
|
||||||
Asphyxiation: -1
|
Asphyxiation: -1
|
||||||
|
Bloodloss: -0.5
|
||||||
|
|
||||||
- type: reagent
|
- type: reagent
|
||||||
id: DexalinPlus
|
id: DexalinPlus
|
||||||
name: dexalin plus
|
name: dexalin plus
|
||||||
group: Medicine
|
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
|
physicalDesc: cloudy
|
||||||
color: "#4da0bd"
|
color: "#4da0bd"
|
||||||
metabolisms:
|
metabolisms:
|
||||||
@@ -183,6 +184,7 @@
|
|||||||
damage:
|
damage:
|
||||||
types:
|
types:
|
||||||
Asphyxiation: -3
|
Asphyxiation: -3
|
||||||
|
Bloodloss: -2
|
||||||
|
|
||||||
- type: reagent
|
- type: reagent
|
||||||
id: Ethylredoxrazine
|
id: Ethylredoxrazine
|
||||||
@@ -299,7 +301,7 @@
|
|||||||
id: Inaprovaline
|
id: Inaprovaline
|
||||||
name: inaprovaline
|
name: inaprovaline
|
||||||
group: Medicine
|
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
|
physicalDesc: opaque
|
||||||
color: "#731024"
|
color: "#731024"
|
||||||
metabolisms:
|
metabolisms:
|
||||||
@@ -313,6 +315,9 @@
|
|||||||
damage:
|
damage:
|
||||||
types:
|
types:
|
||||||
Asphyxiation: -5
|
Asphyxiation: -5
|
||||||
|
- !type:ModifyBleedAmount
|
||||||
|
scaled: true
|
||||||
|
amount: -0.25
|
||||||
|
|
||||||
- type: reagent
|
- type: reagent
|
||||||
id: Kelotane
|
id: Kelotane
|
||||||
@@ -443,6 +448,28 @@
|
|||||||
physicalDesc: strong-smelling
|
physicalDesc: strong-smelling
|
||||||
color: "#2f6ed4"
|
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
|
- type: reagent
|
||||||
id: Tricordrazine
|
id: Tricordrazine
|
||||||
name: tricordrazine
|
name: tricordrazine
|
||||||
|
|||||||
@@ -212,6 +212,18 @@
|
|||||||
products:
|
products:
|
||||||
Inaprovaline: 3
|
Inaprovaline: 3
|
||||||
|
|
||||||
|
- type: reaction
|
||||||
|
id: TranexamicAcid
|
||||||
|
reactants:
|
||||||
|
Inaprovaline:
|
||||||
|
amount: 1
|
||||||
|
SulfuricAcid:
|
||||||
|
amount: 1
|
||||||
|
Glucose:
|
||||||
|
amount: 1
|
||||||
|
products:
|
||||||
|
TranexamicAcid: 3
|
||||||
|
|
||||||
- type: reaction
|
- type: reaction
|
||||||
id: Kelotane
|
id: Kelotane
|
||||||
reactants:
|
reactants:
|
||||||
|
|||||||
@@ -4,3 +4,9 @@
|
|||||||
- /Audio/Effects/gib1.ogg
|
- /Audio/Effects/gib1.ogg
|
||||||
- /Audio/Effects/gib2.ogg
|
- /Audio/Effects/gib2.ogg
|
||||||
- /Audio/Effects/gib3.ogg
|
- /Audio/Effects/gib3.ogg
|
||||||
|
|
||||||
|
- type: soundCollection
|
||||||
|
id: blood
|
||||||
|
files:
|
||||||
|
- /Audio/Effects/Fluids/blood1.ogg
|
||||||
|
- /Audio/Effects/Fluids/blood2.ogg
|
||||||
|
|||||||
Reference in New Issue
Block a user