Changeling Bloodstream Cloning (#44485)

* yippee!!!

* review
This commit is contained in:
ScarKy0 2026-07-22 13:11:01 +00:00 • committed by GitHub
parent d422b4db6f
commit b28ca9f741
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 56 additions and 2 deletions

View file

@ -1,5 +1,7 @@
using Content.Server.Forensics;
using Content.Server.Speech.EntitySystems;
using Content.Shared.Body.Components;
using Content.Shared.Body.Systems;
using Content.Shared.Cloning.Events;
using Content.Shared.Clothing.Components;
using Content.Shared.Clothing.EntitySystems;
@ -38,6 +40,7 @@ public sealed partial class CloningSystem
[Dependency] private MovementSpeedModifierSystem _movementSpeedModifier = default!;
[Dependency] private SharedChameleonClothingSystem _chameleonClothing = default!;
[Dependency] private PullingSystem _pulling = default!;
[Dependency] private SharedBloodstreamSystem _bloodstream = default!;
public override void Initialize()
{
@ -62,6 +65,7 @@ public sealed partial class CloningSystem
SubscribeLocalEvent<InventoryComponent, CloningEvent>(OnCloneInventory);
SubscribeLocalEvent<MovementSpeedModifierComponent, CloningEvent>(OnCloneMovementSpeedModifier);
SubscribeLocalEvent<PullerComponent, CloningEvent>(OnClonePuller);
SubscribeLocalEvent<BloodstreamComponent, CloningEvent>(OnCloneBloodstream);
}
private void OnCloneItemStack(Entity<StackComponent> ent, ref CloningItemEvent args)
@ -149,4 +153,12 @@ public sealed partial class CloningSystem
_pulling.CopyPullerComponent(ent.AsNullable(), args.CloneUid);
}
private void OnCloneBloodstream(Entity<BloodstreamComponent> ent, ref CloningEvent args)
{
if (!args.Settings.EventComponents.Contains(Factory.GetRegistration(ent.Comp.GetType()).Name))
return;
_bloodstream.CopyComponent(ent.AsNullable(), args.CloneUid);
}
}

View file

@ -126,14 +126,14 @@ public sealed partial class BloodstreamComponent : Component
/// <summary>
/// The sound to be played when some damage actually heals bleeding rather than starting it.
/// </summary>
[DataField]
[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]
[DataField, AutoNetworkedField]
public float BloodHealedSoundThreshold = -0.1f;
// TODO probably damage bleed thresholds.

View file

@ -13,6 +13,7 @@ using Content.Shared.Fluids;
using Content.Shared.Forensics.Components;
using Content.Shared.Gibbing;
using Content.Shared.HealthExaminable;
using Content.Shared.Metabolism;
using Content.Shared.Mobs.Systems;
using Content.Shared.Popups;
using Content.Shared.Random.Helpers;
@ -39,6 +40,7 @@ public abstract partial class SharedBloodstreamSystem : EntitySystem
[Dependency] private AlertsSystem _alertsSystem = default!;
[Dependency] private MobStateSystem _mobStateSystem = default!;
[Dependency] private DamageableSystem _damageableSystem = default!;
[Dependency] private MetabolizerSystem _metabolizer = default!;
public override void Initialize()
{
@ -614,4 +616,43 @@ public abstract partial class SharedBloodstreamSystem : EntitySystem
bloodData.Add(dnaData);
return bloodData;
}
/// <summary>
/// Copies the values in <see cref="BloodstreamComponent"/> of one entity to another.
/// If the component isn't on the target entity, it is added.
/// </summary>
/// <param name="source">The entity who's component to use.</param>
/// <param name="target">The target to clone the component onto.</param>
public void CopyComponent(Entity<BloodstreamComponent?> source, EntityUid target)
{
if (!Resolve(source, ref source.Comp))
return;
// We don't create the component before adding it here.
// This is because it causes the initialization to get a bit fucky together with ChangeBloodReagent.
// The reagents won't actually change unless we do it this way.
// The DNA and stuff is updated by themselves anyway.
// BleedAmount is excluded on purpose. There is no point in cloning how much someone is bleeding at the moment.
var cloneComp = EnsureComp<BloodstreamComponent>(target);
cloneComp.UpdateInterval = source.Comp.UpdateInterval;
cloneComp.UpdateIntervalMultiplier = source.Comp.UpdateIntervalMultiplier;
cloneComp.BleedReductionAmount = source.Comp.BleedReductionAmount;
cloneComp.MaxBleedAmount = source.Comp.MaxBleedAmount;
cloneComp.BloodlossThreshold = source.Comp.BloodlossThreshold;
cloneComp.BloodlossDamage = new DamageSpecifier(source.Comp.BloodlossDamage);
cloneComp.BloodlossHealDamage = new DamageSpecifier(source.Comp.BloodlossHealDamage);
cloneComp.BloodRefreshAmount = source.Comp.BloodRefreshAmount;
cloneComp.BleedPuddleThreshold = source.Comp.BleedPuddleThreshold;
cloneComp.DamageBleedModifiers = source.Comp.DamageBleedModifiers;
cloneComp.MaxVolumeModifier = source.Comp.MaxVolumeModifier;
cloneComp.InstantBloodSound = source.Comp.InstantBloodSound;
cloneComp.BloodHealedSound = source.Comp.BloodHealedSound;
cloneComp.BloodHealedSoundThreshold = source.Comp.BloodHealedSoundThreshold;
Dirty(target, cloneComp);
// ChangeBloodReagents dirties the values it changes by itself.
ChangeBloodReagents((target, cloneComp), source.Comp.BloodReferenceSolution);
_metabolizer.UpdateMetabolicMultiplier(target);
}
}

View file

@ -154,6 +154,7 @@
- Sericulture # arachnids
- MovementSpeedModifier # moths when weightless
- JumpAbility # vulp leaping
- Bloodstream # blood reagents
copyInternalStorage: false
copyImplants: false
raiseEntityRenamedEvent: false