using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
-using Robust.Shared.Maths;
using System.Linq;
using System.Numerics;
var mapManager = server.ResolveDependency<IMapManager>();
var entityManager = server.ResolveDependency<IEntityManager>();
var mapLoader = entityManager.System<MapLoaderSystem>();
- RespiratorSystem respSys = default;
- MetabolizerSystem metaSys = default;
MapId mapId;
EntityUid? grid = null;
BodyComponent body = default;
+ RespiratorComponent resp = default;
EntityUid human = default;
GridAtmosphereComponent relevantAtmos = default;
var startingMoles = 0.0f;
await server.WaitAssertion(() =>
{
- var coords = new Vector2(0.5f, -1f);
- var coordinates = new EntityCoordinates(grid.Value, coords);
+ var center = new Vector2(0.5f, 0.5f);
+ var coordinates = new EntityCoordinates(grid.Value, center);
human = entityManager.SpawnEntity("HumanLungDummy", coordinates);
- respSys = entityManager.System<RespiratorSystem>();
- metaSys = entityManager.System<MetabolizerSystem>();
relevantAtmos = entityManager.GetComponent<GridAtmosphereComponent>(grid.Value);
- startingMoles = GetMapMoles();
+ startingMoles = 100f; // Hardcoded because GetMapMoles returns 900 here for some reason.
#pragma warning disable NUnit2045
Assert.That(entityManager.TryGetComponent(human, out body), Is.True);
- Assert.That(entityManager.HasComponent<RespiratorComponent>(human), Is.True);
+ Assert.That(entityManager.TryGetComponent(human, out resp), Is.True);
#pragma warning restore NUnit2045
});
var inhaleCycles = 100;
for (var i = 0; i < inhaleCycles; i++)
{
- await server.WaitAssertion(() =>
- {
- // inhale
- respSys.Update(2.0f);
- Assert.That(GetMapMoles(), Is.LessThan(startingMoles));
-
- // metabolize + exhale
- metaSys.Update(1.0f);
- metaSys.Update(1.0f);
- respSys.Update(2.0f);
- Assert.That(GetMapMoles(), Is.EqualTo(startingMoles).Within(0.0002));
- });
+ // Breathe in
+ await PoolManager.WaitUntil(server, () => resp.Status == RespiratorStatus.Exhaling);
+ Assert.That(
+ GetMapMoles(), Is.LessThan(startingMoles),
+ "Did not inhale in any gas"
+ );
+
+ // Breathe out
+ await PoolManager.WaitUntil(server, () => resp.Status == RespiratorStatus.Inhaling);
+ Assert.That(
+ GetMapMoles(), Is.EqualTo(startingMoles).Within(0.0002),
+ "Did not exhale as much gas as was inhaled"
+ );
}
await pair.CleanReturnAsync();
if (!this.IsPowered(uid, EntityManager))
return;
- var metabolicEvent = new ApplyMetabolicMultiplierEvent
- {Uid = args.BuckledEntity, Multiplier = component.Multiplier, Apply = args.Buckling};
- RaiseLocalEvent(args.BuckledEntity, metabolicEvent);
+ var metabolicEvent = new ApplyMetabolicMultiplierEvent(args.BuckledEntity, component.Multiplier, args.Buckling);
+ RaiseLocalEvent(args.BuckledEntity, ref metabolicEvent);
}
private void OnPowerChanged(EntityUid uid, StasisBedComponent component, ref PowerChangedEvent args)
foreach (var buckledEntity in strap.BuckledEntities)
{
- var metabolicEvent = new ApplyMetabolicMultiplierEvent
- {Uid = buckledEntity, Multiplier = component.Multiplier, Apply = shouldApply};
- RaiseLocalEvent(buckledEntity, metabolicEvent);
+ var metabolicEvent = new ApplyMetabolicMultiplierEvent(buckledEntity, component.Multiplier, shouldApply);
+ RaiseLocalEvent(buckledEntity, ref metabolicEvent);
}
}
}
switch (args.Length)
{
case 0:
- {
if (player == null)
{
shell.WriteLine("Only a player can run this command without arguments.");
entity = player.AttachedEntity.Value;
hand = _entManager.SpawnEntity(DefaultHandPrototype, _entManager.GetComponent<TransformComponent>(entity).Coordinates);
break;
- }
case 1:
- {
- if (NetEntity.TryParse(args[0], out var uidNet) && _entManager.TryGetEntity(uidNet, out var uid))
{
- if (!_entManager.EntityExists(uid))
+ if (NetEntity.TryParse(args[0], out var uidNet) && _entManager.TryGetEntity(uidNet, out var uid))
{
- shell.WriteLine($"No entity found with uid {uid}");
- return;
+ if (!_entManager.EntityExists(uid))
+ {
+ shell.WriteLine($"No entity found with uid {uid}");
+ return;
+ }
+
+ entity = uid.Value;
+ hand = _entManager.SpawnEntity(DefaultHandPrototype, _entManager.GetComponent<TransformComponent>(entity).Coordinates);
+ }
+ else
+ {
+ if (player == null)
+ {
+ shell.WriteLine("You must specify an entity to add a hand to when using this command from the server terminal.");
+ return;
+ }
+
+ if (player.AttachedEntity == null)
+ {
+ shell.WriteLine("You don't have an entity to add a hand to.");
+ return;
+ }
+
+ entity = player.AttachedEntity.Value;
+ hand = _entManager.SpawnEntity(args[0], _entManager.GetComponent<TransformComponent>(entity).Coordinates);
}
- entity = uid.Value;
- hand = _entManager.SpawnEntity(DefaultHandPrototype, _entManager.GetComponent<TransformComponent>(entity).Coordinates);
+ break;
}
- else
+ case 2:
{
- if (player == null)
+ if (!NetEntity.TryParse(args[0], out var netEnt) || !_entManager.TryGetEntity(netEnt, out var uid))
{
- shell.WriteLine("You must specify an entity to add a hand to when using this command from the server terminal.");
+ shell.WriteLine($"{args[0]} is not a valid entity uid.");
return;
}
- if (player.AttachedEntity == null)
+ if (!_entManager.EntityExists(uid))
{
- shell.WriteLine("You don't have an entity to add a hand to.");
+ shell.WriteLine($"No entity exists with uid {uid}.");
return;
}
- entity = player.AttachedEntity.Value;
- hand = _entManager.SpawnEntity(args[0], _entManager.GetComponent<TransformComponent>(entity).Coordinates);
- }
-
- break;
- }
- case 2:
- {
- if (!NetEntity.TryParse(args[0], out var netEnt) || !_entManager.TryGetEntity(netEnt, out var uid))
- {
- shell.WriteLine($"{args[0]} is not a valid entity uid.");
- return;
- }
+ entity = uid.Value;
- if (!_entManager.EntityExists(uid))
- {
- shell.WriteLine($"No entity exists with uid {uid}.");
- return;
- }
+ if (!_protoManager.HasIndex<EntityPrototype>(args[1]))
+ {
+ shell.WriteLine($"No hand entity exists with id {args[1]}.");
+ return;
+ }
- entity = uid.Value;
+ hand = _entManager.SpawnEntity(args[1], _entManager.GetComponent<TransformComponent>(entity).Coordinates);
- if (!_protoManager.HasIndex<EntityPrototype>(args[1]))
- {
- shell.WriteLine($"No hand entity exists with id {args[1]}.");
- return;
+ break;
}
-
- hand = _entManager.SpawnEntity(args[1], _entManager.GetComponent<TransformComponent>(entity).Coordinates);
-
- break;
- }
default:
- {
shell.WriteLine(Help);
return;
- }
}
if (!_entManager.TryGetComponent(entity, out BodyComponent? body) || body.RootContainer.ContainedEntity == null)
var slotId = part.GetHashCode().ToString();
- if (!bodySystem.TryCreatePartSlotAndAttach(attachAt.Id, slotId, hand, BodyPartType.Hand,attachAt.Component, part))
+ if (!bodySystem.TryCreatePartSlotAndAttach(attachAt.Id, slotId, hand, BodyPartType.Hand, attachAt.Component, part))
{
shell.WriteError($"Couldn't create a slot with id {slotId} on entity {_entManager.ToPrettyString(entity)}");
return;
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
if (body.RootContainer.ContainedEntity != null)
{
- bodySystem.AttachPartToRoot(bodyId,partUid.Value, body ,part);
+ bodySystem.AttachPartToRoot(bodyId, partUid.Value, body, part);
}
else
{
- var (rootPartId,rootPart) = bodySystem.GetRootPartOrNull(bodyId, body)!.Value;
+ var (rootPartId, rootPart) = bodySystem.GetRootPartOrNull(bodyId, body)!.Value;
if (!bodySystem.TryCreatePartSlotAndAttach(rootPartId, slotId, partUid.Value, part.PartType, rootPart, part))
{
shell.WriteError($"Could not create slot {slotId} on entity {_entManager.ToPrettyString(bodyId)}");
namespace Content.Server.Body.Components;
-public sealed class BeingGibbedEvent : EntityEventArgs
-{
- public readonly HashSet<EntityUid> GibbedParts;
-
- public BeingGibbedEvent(HashSet<EntityUid> gibbedParts)
- {
- GibbedParts = gibbedParts;
- }
-}
+/// <summary>
+/// Raised when a body gets gibbed, before it is deleted.
+/// </summary>
+[ByRefEvent]
+public readonly record struct BeingGibbedEvent(HashSet<EntityUid> GibbedParts);
using Content.Server.Body.Systems;
using Content.Server.Chemistry.EntitySystems;
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.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Server.Body.Components
{
public static string DefaultBloodSolutionName = "bloodstream";
public static string DefaultBloodTemporarySolutionName = "bloodstreamTemporary";
- public float AccumulatedFrametime = 0.0f;
+ /// <summary>
+ /// The next time that blood level will be updated and bloodloss damage dealt.
+ /// </summary>
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
+ public TimeSpan NextUpdate;
+
+ /// <summary>
+ /// The interval at which this component updates.
+ /// </summary>
+ [DataField]
+ public TimeSpan UpdateInterval = TimeSpan.FromSeconds(3);
/// <summary>
/// How much is this entity currently bleeding?
public float BleedAmount;
/// <summary>
- /// How much should bleeding should be reduced every update interval?
+ /// How much should bleeding be reduced every update interval?
/// </summary>
[DataField]
public float BleedReductionAmount = 0.33f;
[DataField(required: true)]
public DamageSpecifier BloodlossHealDamage = new();
- /// <summary>
- /// How frequently should this bloodstream update, in seconds?
- /// </summary>
- [DataField]
- public float UpdateInterval = 3.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]
- public float BloodRefreshAmount = 1.0f;
+ public FixedPoint2 BloodRefreshAmount = 1.0f;
/// <summary>
/// How much blood needs to be in the temporary solution in order to create a puddle?
/// <remarks>
/// For example, piercing damage is increased while poison damage is nullified entirely.
/// </remarks>
- [DataField(customTypeSerializer:typeof(PrototypeIdSerializer<DamageModifierSetPrototype>))]
- public string DamageBleedModifiers = "BloodlossHuman";
+ [DataField]
+ public ProtoId<DamageModifierSetPrototype> DamageBleedModifiers = "BloodlossHuman";
/// <summary>
/// The sound to be played when a weapon instantly deals blood loss damage.
/// Slime-people might use slime as their blood or something like that.
/// </remarks>
[DataField]
- public string BloodReagent = "Blood";
+ public ProtoId<ReagentPrototype> BloodReagent = "Blood";
/// <summary>Name/Key that <see cref="BloodSolution"/> is indexed by.</summary>
[DataField]
/// Variable that stores the amount of status time added by having a low blood level.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
- public float StatusTime;
+ public TimeSpan StatusTime;
}
}
-using System.Threading;
namespace Content.Server.Body.Components
{
/// <summary>
[RegisterComponent]
public sealed partial class InternalsComponent : Component
{
- [ViewVariables] public EntityUid? GasTankEntity { get; set; }
- [ViewVariables] public EntityUid? BreathToolEntity { get; set; }
+ [ViewVariables]
+ public EntityUid? GasTankEntity;
+
+ [ViewVariables]
+ public EntityUid? BreathToolEntity;
/// <summary>
- /// Toggle Internals delay (seconds) when the target is not you.
+ /// Toggle Internals delay when the target is not you.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
- [DataField("delay")]
- public float Delay = 3;
+ [DataField]
+ public TimeSpan Delay = TimeSpan.FromSeconds(3);
}
}
-using Content.Server.Atmos;
+using Content.Server.Atmos;
using Content.Server.Body.Systems;
using Content.Shared.Alert;
using Content.Shared.Atmos;
{
[DataField]
[Access(typeof(LungSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
- public GasMixture Air { get; set; } = new()
+ public GasMixture Air = new()
{
Volume = 6,
Temperature = Atmospherics.NormalBodyTemperature
-using Content.Server.Body.Systems;
+using Content.Server.Body.Systems;
using Content.Shared.Body.Prototypes;
using Content.Shared.FixedPoint;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Server.Body.Components
{
[RegisterComponent, Access(typeof(MetabolizerSystem))]
public sealed partial class MetabolizerComponent : Component
{
- public float AccumulatedFrametime = 0.0f;
+ /// <summary>
+ /// The next time that reagents will be metabolized.
+ /// </summary>
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
+ public TimeSpan NextUpdate;
/// <summary>
- /// How often to metabolize reagents, in seconds.
+ /// How often to metabolize reagents.
/// </summary>
/// <returns></returns>
[DataField]
- public float UpdateFrequency = 1.0f;
+ public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1);
/// <summary>
/// From which solution will this metabolizer attempt to metabolize chemicals
/// </summary>
[DataField("solution")]
- public string SolutionName { get; set; } = BloodstreamComponent.DefaultChemicalsSolutionName;
+ public string SolutionName = BloodstreamComponent.DefaultChemicalsSolutionName;
/// <summary>
/// Does this component use a solution on it's parent entity (the body) or itself
/// <summary>
/// List of metabolizer types that this organ is. ex. Human, Slime, Felinid, w/e.
/// </summary>
- [DataField(customTypeSerializer:typeof(PrototypeIdHashSetSerializer<MetabolizerTypePrototype>))]
+ [DataField]
[Access(typeof(MetabolizerSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
- public HashSet<string>? MetabolizerTypes = null;
+ public HashSet<ProtoId<MetabolizerTypePrototype>>? MetabolizerTypes = null;
/// <summary>
/// Should this metabolizer remove chemicals that have no metabolisms defined?
[DataDefinition]
public sealed partial class MetabolismGroupEntry
{
- [DataField(required: true, customTypeSerializer:typeof(PrototypeIdSerializer<MetabolismGroupPrototype>))]
- public string Id = default!;
+ [DataField(required: true)]
+ public ProtoId<MetabolismGroupPrototype> Id = default!;
[DataField("rateModifier")]
public FixedPoint2 MetabolismRateModifier = 1.0;
using Content.Server.Body.Systems;
using Content.Shared.Damage;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Server.Body.Components
{
public sealed partial class RespiratorComponent : Component
{
/// <summary>
- /// Saturation level. Reduced by CycleDelay each tick.
+ /// The next time that this body will inhale or exhale.
+ /// </summary>
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
+ public TimeSpan NextUpdate;
+
+ /// <summary>
+ /// The interval between updates. Each update is either inhale or exhale,
+ /// so a full cycle takes twice as long.
+ /// </summary>
+ [DataField]
+ public TimeSpan UpdateInterval = TimeSpan.FromSeconds(2);
+
+ /// <summary>
+ /// Saturation level. Reduced by UpdateInterval each tick.
/// Can be thought of as 'how many seconds you have until you start suffocating' in this configuration.
/// </summary>
- [DataField("saturation")]
+ [DataField]
public float Saturation = 5.0f;
/// <summary>
/// At what level of saturation will you begin to suffocate?
/// </summary>
- [DataField("suffocationThreshold")]
+ [DataField]
public float SuffocationThreshold;
- [DataField("maxSaturation")]
+ [DataField]
public float MaxSaturation = 5.0f;
- [DataField("minSaturation")]
+ [DataField]
public float MinSaturation = -2.0f;
// TODO HYPEROXIA?
- [DataField("damage", required: true)]
+ [DataField(required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageSpecifier Damage = default!;
- [DataField("damageRecovery", required: true)]
+ [DataField(required: true)]
[ViewVariables(VVAccess.ReadWrite)]
public DamageSpecifier DamageRecovery = default!;
- [DataField("gaspPopupCooldown")]
- public TimeSpan GaspPopupCooldown { get; private set; } = TimeSpan.FromSeconds(8);
+ [DataField]
+ public TimeSpan GaspPopupCooldown = TimeSpan.FromSeconds(8);
[ViewVariables]
public TimeSpan LastGaspPopupTime;
[ViewVariables]
public RespiratorStatus Status = RespiratorStatus.Inhaling;
-
- [DataField("cycleDelay")]
- public float CycleDelay = 2.0f;
-
- public float AccumulatedFrametime;
}
}
-using Content.Server.Body.Systems;
+using Content.Server.Body.Systems;
using Content.Server.Nutrition.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Whitelist;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Server.Body.Components
{
[RegisterComponent, Access(typeof(StomachSystem), typeof(FoodSystem))]
public sealed partial class StomachComponent : Component
{
- public float AccumulatedFrameTime;
+ /// <summary>
+ /// The next time that the stomach will try to digest its contents.
+ /// </summary>
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
+ public TimeSpan NextUpdate;
/// <summary>
- /// How fast should this component update, in seconds?
+ /// The interval at which this stomach digests its contents.
/// </summary>
[DataField]
- public float UpdateInterval = 1.0f;
+ public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1);
/// <summary>
/// The solution inside of this stomach this transfers reagents to the body.
public string BodySolutionName = BloodstreamComponent.DefaultChemicalsSolutionName;
/// <summary>
- /// Time in seconds between reagents being ingested and them being
+ /// Time between reagents being ingested and them being
/// transferred to <see cref="BloodstreamComponent"/>
/// </summary>
[DataField]
- public float DigestionDelay = 20;
+ public TimeSpan DigestionDelay = TimeSpan.FromSeconds(20);
/// <summary>
/// A whitelist for what special-digestible-required foods this stomach is capable of eating.
public sealed class ReagentDelta
{
public readonly ReagentQuantity ReagentQuantity;
- public float Lifetime { get; private set; }
+ public TimeSpan Lifetime { get; private set; }
public ReagentDelta(ReagentQuantity reagentQuantity)
{
ReagentQuantity = reagentQuantity;
- Lifetime = 0.0f;
+ Lifetime = TimeSpan.Zero;
}
- public void Increment(float delta) => Lifetime += delta;
+ public void Increment(TimeSpan delta) => Lifetime += delta;
}
}
}
using Content.Server.Body.Systems;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Server.Body.Components;
[Access(typeof(ThermalRegulatorSystem))]
public sealed partial class ThermalRegulatorComponent : Component
{
+ /// <summary>
+ /// The next time that the body will regulate its heat.
+ /// </summary>
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
+ public TimeSpan NextUpdate;
+
+ /// <summary>
+ /// The interval at which thermal regulation is processed.
+ /// </summary>
+ [DataField]
+ public TimeSpan UpdateInterval = TimeSpan.FromSeconds(1);
+
/// <summary>
/// Heat generated due to metabolism. It's generated via metabolism
/// </summary>
- [DataField("metabolismHeat")]
- public float MetabolismHeat { get; private set; }
+ [DataField]
+ public float MetabolismHeat;
/// <summary>
/// Heat output via radiation.
/// </summary>
- [DataField("radiatedHeat")]
- public float RadiatedHeat { get; private set; }
+ [DataField]
+ public float RadiatedHeat;
/// <summary>
/// Maximum heat regulated via sweat
/// </summary>
- [DataField("sweatHeatRegulation")]
- public float SweatHeatRegulation { get; private set; }
+ [DataField]
+ public float SweatHeatRegulation;
/// <summary>
/// Maximum heat regulated via shivering
/// </summary>
- [DataField("shiveringHeatRegulation")]
- public float ShiveringHeatRegulation { get; private set; }
+ [DataField]
+ public float ShiveringHeatRegulation;
/// <summary>
/// Amount of heat regulation that represents thermal regulation processes not
/// explicitly coded.
/// </summary>
- [DataField("implicitHeatRegulation")]
- public float ImplicitHeatRegulation { get; private set; }
+ [DataField]
+ public float ImplicitHeatRegulation;
/// <summary>
/// Normal body temperature
/// </summary>
- [DataField("normalBodyTemperature")]
- public float NormalBodyTemperature { get; private set; }
+ [DataField]
+ public float NormalBodyTemperature;
/// <summary>
/// Deviation from normal temperature for body to start thermal regulation
/// </summary>
- [DataField("thermalRegulationTemperatureThreshold")]
- public float ThermalRegulationTemperatureThreshold { get; private set; }
-
- public float AccumulatedFrametime;
+ [DataField]
+ public float ThermalRegulationTemperatureThreshold;
}
using Content.Shared.Damage.Prototypes;
using Content.Shared.Drunk;
using Content.Shared.FixedPoint;
-using Content.Shared.IdentityManagement;
using Content.Shared.Mobs.Systems;
using Content.Shared.Popups;
using Content.Shared.Rejuvenate;
using Robust.Server.Audio;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
+using Robust.Shared.Timing;
namespace Content.Server.Body.Systems;
public sealed class BloodstreamSystem : EntitySystem
{
+ [Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _robustRandom = default!;
[Dependency] private readonly AudioSystem _audio = default!;
base.Initialize();
SubscribeLocalEvent<BloodstreamComponent, ComponentInit>(OnComponentInit);
+ SubscribeLocalEvent<BloodstreamComponent, MapInitEvent>(OnMapInit);
+ SubscribeLocalEvent<BloodstreamComponent, EntityUnpausedEvent>(OnUnpaused);
SubscribeLocalEvent<BloodstreamComponent, DamageChangedEvent>(OnDamageChanged);
SubscribeLocalEvent<BloodstreamComponent, HealthBeingExaminedEvent>(OnHealthBeingExamined);
SubscribeLocalEvent<BloodstreamComponent, BeingGibbedEvent>(OnBeingGibbed);
SubscribeLocalEvent<BloodstreamComponent, RejuvenateEvent>(OnRejuvenate);
}
+ private void OnMapInit(Entity<BloodstreamComponent> ent, ref MapInitEvent args)
+ {
+ ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval;
+ }
+
+ private void OnUnpaused(Entity<BloodstreamComponent> ent, ref EntityUnpausedEvent args)
+ {
+ ent.Comp.NextUpdate += args.PausedTime;
+ }
+
private void OnReactionAttempt(Entity<BloodstreamComponent> entity, ref ReactionAttemptEvent args)
{
if (args.Cancelled)
if (args.Name != entity.Comp.BloodSolutionName
&& args.Name != entity.Comp.ChemicalSolutionName
&& args.Name != entity.Comp.BloodTemporarySolutionName)
+ {
return;
+ }
OnReactionAttempt(entity, ref args.Event);
}
var query = EntityQueryEnumerator<BloodstreamComponent>();
while (query.MoveNext(out var uid, out var bloodstream))
{
- bloodstream.AccumulatedFrametime += frameTime;
-
- if (bloodstream.AccumulatedFrametime < bloodstream.UpdateInterval)
+ if (_gameTiming.CurTime < bloodstream.NextUpdate)
continue;
- bloodstream.AccumulatedFrametime -= bloodstream.UpdateInterval;
+ bloodstream.NextUpdate += bloodstream.UpdateInterval;
if (!_solutionContainerSystem.ResolveSolution(uid, bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution))
continue;
// bloodloss damage is based on the base value, and modified by how low your blood level is.
var amt = bloodstream.BloodlossDamage / (0.1f + bloodPercentage);
- _damageableSystem.TryChangeDamage(uid, amt, false, false);
+ _damageableSystem.TryChangeDamage(uid, amt,
+ ignoreResistances: false, interruptsDoAfters: false);
// Apply dizziness as a symptom of bloodloss.
// The effect is applied in a way that it will never be cleared without being healthy.
// Multiplying by 2 is arbitrary but works for this case, it just prevents the time from running out
- _drunkSystem.TryApplyDrunkenness(uid, bloodstream.UpdateInterval*2, false);
- _stutteringSystem.DoStutter(uid, TimeSpan.FromSeconds(bloodstream.UpdateInterval*2), false);
+ _drunkSystem.TryApplyDrunkenness(
+ uid,
+ (float) bloodstream.UpdateInterval.TotalSeconds * 2,
+ applySlur: false);
+ _stutteringSystem.DoStutter(uid, bloodstream.UpdateInterval * 2, refresh: false);
// storing the drunk and stutter time so we can remove it independently from other effects additions
bloodstream.StatusTime += bloodstream.UpdateInterval * 2;
else if (!_mobStateSystem.IsDead(uid))
{
// If they're healthy, we'll try and heal some bloodloss instead.
- _damageableSystem.TryChangeDamage(uid, bloodstream.BloodlossHealDamage * bloodPercentage, true, false);
+ _damageableSystem.TryChangeDamage(
+ uid,
+ bloodstream.BloodlossHealDamage * bloodPercentage,
+ ignoreResistances: true, interruptsDoAfters: false);
// Remove the drunk effect when healthy. Should only remove the amount of drunk and stutter added by low blood level
- _drunkSystem.TryRemoveDrunkenessTime(uid, bloodstream.StatusTime);
- _stutteringSystem.DoRemoveStutterTime(uid, bloodstream.StatusTime);
+ _drunkSystem.TryRemoveDrunkenessTime(uid, bloodstream.StatusTime.TotalSeconds);
+ _stutteringSystem.DoRemoveStutterTime(uid, bloodstream.StatusTime.TotalSeconds);
// Reset the drunk and stutter time to zero
- bloodstream.StatusTime = 0;
+ bloodstream.StatusTime = TimeSpan.Zero;
}
}
}
bloodSolution.AddReagent(entity.Comp.BloodReagent, entity.Comp.BloodMaxVolume - bloodSolution.Volume);
}
- private void OnDamageChanged(EntityUid uid, BloodstreamComponent component, DamageChangedEvent args)
+ private void OnDamageChanged(Entity<BloodstreamComponent> ent, ref DamageChangedEvent args)
{
- if (args.DamageDelta is null)
- return;
-
- // definitely don't make them bleed if they got healed
- if (!args.DamageIncreased)
+ if (args.DamageDelta is null || !args.DamageIncreased)
+ {
return;
+ }
// TODO probably cache this or something. humans get hurt a lot
- if (!_prototypeManager.TryIndex<DamageModifierSetPrototype>(component.DamageBleedModifiers, out var modifiers))
+ if (!_prototypeManager.TryIndex<DamageModifierSetPrototype>(ent.Comp.DamageBleedModifiers, out var modifiers))
return;
var bloodloss = DamageSpecifier.ApplyModifierSet(args.DamageDelta, modifiers);
return;
// Does the calculation of how much bleed rate should be added/removed, then applies it
- var oldBleedAmount = component.BleedAmount;
+ var oldBleedAmount = ent.Comp.BleedAmount;
var total = bloodloss.GetTotal();
var totalFloat = total.Float();
- TryModifyBleedAmount(uid, totalFloat, component);
+ TryModifyBleedAmount(ent, totalFloat, ent);
/// <summary>
/// Critical hit. Causes target to lose blood, using the bleed rate modifier of the weapon, currently divided by 5
var prob = Math.Clamp(totalFloat / 25, 0, 1);
if (totalFloat > 0 && _robustRandom.Prob(prob))
{
- TryModifyBloodLevel(uid, (-total) / 5, component);
- _audio.PlayPvs(component.InstantBloodSound, uid);
+ TryModifyBloodLevel(ent, (-total) / 5, ent);
+ _audio.PlayPvs(ent.Comp.InstantBloodSound, ent);
}
// Heat damage will cauterize, causing the bleed rate to be reduced.
// because it's burn damage that cauterized their wounds.
// We'll play a special sound and popup for feedback.
- _audio.PlayPvs(component.BloodHealedSound, uid);
- _popupSystem.PopupEntity(Loc.GetString("bloodstream-component-wounds-cauterized"), uid,
- uid, PopupType.Medium);
+ _audio.PlayPvs(ent.Comp.BloodHealedSound, ent);
+ _popupSystem.PopupEntity(Loc.GetString("bloodstream-component-wounds-cauterized"), ent,
+ ent, PopupType.Medium);
}
}
/// <summary>
/// Shows text on health examine, based on bleed rate and blood level.
/// </summary>
- private void OnHealthBeingExamined(EntityUid uid, BloodstreamComponent component, HealthBeingExaminedEvent args)
+ private void OnHealthBeingExamined(Entity<BloodstreamComponent> ent, ref HealthBeingExaminedEvent args)
{
// Shows profusely bleeding at half the max bleed rate.
- if (component.BleedAmount > component.MaxBleedAmount / 2)
+ if (ent.Comp.BleedAmount > ent.Comp.MaxBleedAmount / 2)
{
args.Message.PushNewline();
- args.Message.AddMarkup(Loc.GetString("bloodstream-component-profusely-bleeding", ("target", Identity.Entity(uid, EntityManager))));
+ args.Message.AddMarkup(Loc.GetString("bloodstream-component-profusely-bleeding", ("target", ent.Owner)));
}
// Shows bleeding message when bleeding, but less than profusely.
- else if (component.BleedAmount > 0)
+ else if (ent.Comp.BleedAmount > 0)
{
args.Message.PushNewline();
- args.Message.AddMarkup(Loc.GetString("bloodstream-component-bleeding", ("target", Identity.Entity(uid, EntityManager))));
+ args.Message.AddMarkup(Loc.GetString("bloodstream-component-bleeding", ("target", ent.Owner)));
}
// If the mob's blood level is below the damage threshhold, the pale message is added.
- if (GetBloodLevelPercentage(uid, component) < component.BloodlossThreshold)
+ if (GetBloodLevelPercentage(ent, ent) < ent.Comp.BloodlossThreshold)
{
args.Message.PushNewline();
- args.Message.AddMarkup(Loc.GetString("bloodstream-component-looks-pale", ("target", Identity.Entity(uid, EntityManager))));
+ args.Message.AddMarkup(Loc.GetString("bloodstream-component-looks-pale", ("target", ent.Owner)));
}
}
- private void OnBeingGibbed(EntityUid uid, BloodstreamComponent component, BeingGibbedEvent args)
+ private void OnBeingGibbed(Entity<BloodstreamComponent> ent, ref BeingGibbedEvent args)
{
- SpillAllSolutions(uid, component);
+ SpillAllSolutions(ent, ent);
}
- private void OnApplyMetabolicMultiplier(EntityUid uid, BloodstreamComponent component, ApplyMetabolicMultiplierEvent args)
+ private void OnApplyMetabolicMultiplier(
+ Entity<BloodstreamComponent> ent,
+ ref ApplyMetabolicMultiplierEvent args)
{
if (args.Apply)
{
- component.UpdateInterval *= args.Multiplier;
+ ent.Comp.UpdateInterval *= args.Multiplier;
return;
}
- component.UpdateInterval /= args.Multiplier;
- // Reset the accumulator properly
- if (component.AccumulatedFrametime >= component.UpdateInterval)
- component.AccumulatedFrametime = component.UpdateInterval;
+ ent.Comp.UpdateInterval /= args.Multiplier;
}
private void OnRejuvenate(Entity<BloodstreamComponent> entity, ref RejuvenateEvent args)
/// </summary>
public bool TryAddToChemicals(EntityUid uid, Solution solution, BloodstreamComponent? component = null)
{
- if (!Resolve(uid, ref component, false))
- return false;
-
- if (!_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution))
- return false;
-
- return _solutionContainerSystem.TryAddSolution(component.ChemicalSolution.Value, solution);
+ return Resolve(uid, ref component, logMissing: false)
+ && _solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution)
+ && _solutionContainerSystem.TryAddSolution(component.ChemicalSolution.Value, solution);
}
public bool FlushChemicals(EntityUid uid, string excludedReagentID, FixedPoint2 quantity, BloodstreamComponent? component = null)
{
- if (!Resolve(uid, ref component, false))
- return false;
-
- if (!_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution, out var chemSolution))
+ if (!Resolve(uid, ref component, logMissing: false)
+ || !_solutionContainerSystem.ResolveSolution(uid, component.ChemicalSolutionName, ref component.ChemicalSolution, out var chemSolution))
return false;
for (var i = chemSolution.Contents.Count - 1; i >= 0; i--)
public float GetBloodLevelPercentage(EntityUid uid, BloodstreamComponent? component = null)
{
- if (!Resolve(uid, ref component))
- return 0.0f;
-
- if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution))
+ if (!Resolve(uid, ref component)
+ || !_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution))
+ {
return 0.0f;
+ }
return bloodSolution.FillFraction;
}
/// </summary>
public bool TryModifyBloodLevel(EntityUid uid, FixedPoint2 amount, BloodstreamComponent? component = null)
{
- if (!Resolve(uid, ref component, false))
- return false;
-
- if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution))
+ if (!Resolve(uid, ref component, logMissing: false)
+ || !_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution))
+ {
return false;
+ }
if (amount >= 0)
return _solutionContainerSystem.TryAddReagent(component.BloodSolution.Value, component.BloodReagent, amount, out _);
tempSolution.AddSolution(temp, _prototypeManager);
}
- if (_puddleSystem.TrySpillAt(uid, tempSolution, out var puddleUid, false))
+ if (_puddleSystem.TrySpillAt(uid, tempSolution, out var puddleUid, sound: false))
{
- _forensicsSystem.TransferDna(puddleUid, uid, false);
+ _forensicsSystem.TransferDna(puddleUid, uid, canDnaBeCleaned: false);
}
tempSolution.RemoveAllSolution();
/// </summary>
public bool TryModifyBleedAmount(EntityUid uid, float amount, BloodstreamComponent? component = null)
{
- if (!Resolve(uid, ref component, false))
+ if (!Resolve(uid, ref component, logMissing: false))
return false;
component.BleedAmount += amount;
if (_puddleSystem.TrySpillAt(uid, tempSol, out var puddleUid))
{
- _forensicsSystem.TransferDna(puddleUid, uid, false);
+ _forensicsSystem.TransferDna(puddleUid, uid, canDnaBeCleaned: false);
}
}
/// </summary>
public void ChangeBloodReagent(EntityUid uid, string reagent, BloodstreamComponent? component = null)
{
- if (!Resolve(uid, ref component, false))
- return;
-
- if (reagent == component.BloodReagent)
+ if (!Resolve(uid, ref component, logMissing: false)
+ || reagent == component.BloodReagent)
+ {
return;
+ }
if (!_solutionContainerSystem.ResolveSolution(uid, component.BloodSolutionName, ref component.BloodSolution, out var bloodSolution))
{
using Content.Server.Body.Components;
using Content.Server.GameTicking;
using Content.Server.Humanoid;
-using Content.Server.Kitchen.Components;
using Content.Shared.Body.Components;
using Content.Shared.Body.Part;
using Content.Shared.Body.Systems;
using Content.Shared.Humanoid;
-using Content.Shared.Kitchen.Components;
using Content.Shared.Mind;
using Content.Shared.Mobs.Systems;
using Content.Shared.Movement.Events;
+using Content.Shared.Movement.Systems;
using Robust.Shared.Audio;
-using Robust.Shared.Player;
+using Robust.Shared.Audio.Systems;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using System.Numerics;
-using Content.Shared.Gibbing.Components;
-using Content.Shared.Movement.Systems;
-using Robust.Shared.Audio.Systems;
namespace Content.Server.Body.Systems;
[Dependency] private readonly HumanoidAppearanceSystem _humanoidSystem = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
- [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedMindSystem _mindSystem = default!;
[Dependency] private readonly IRobustRandom _random = default!;
SubscribeLocalEvent<BodyComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
}
- private void OnRelayMoveInput(EntityUid uid, BodyComponent component, ref MoveInputEvent args)
+ private void OnRelayMoveInput(Entity<BodyComponent> ent, ref MoveInputEvent args)
{
// If they haven't actually moved then ignore it.
if ((args.Component.HeldMoveButtons &
return;
}
- if (_mobState.IsDead(uid) && _mindSystem.TryGetMind(uid, out var mindId, out var mind))
+ if (_mobState.IsDead(ent) && _mindSystem.TryGetMind(ent, out var mindId, out var mind))
{
mind.TimeOfDeath ??= _gameTiming.RealTime;
- _ticker.OnGhostAttempt(mindId, true, mind: mind);
+ _ticker.OnGhostAttempt(mindId, canReturnGlobal: true, mind: mind);
}
}
- private void OnApplyMetabolicMultiplier(EntityUid uid, BodyComponent component,
- ApplyMetabolicMultiplierEvent args)
+ private void OnApplyMetabolicMultiplier(
+ Entity<BodyComponent> ent,
+ ref ApplyMetabolicMultiplierEvent args)
{
- foreach (var organ in GetBodyOrgans(uid, component))
+ foreach (var organ in GetBodyOrgans(ent, ent))
{
- RaiseLocalEvent(organ.Id, args);
+ RaiseLocalEvent(organ.Id, ref args);
}
}
protected override void AddPart(
- EntityUid bodyUid,
- EntityUid partUid,
- string slotId,
- BodyPartComponent component,
- BodyComponent? bodyComp = null)
+ Entity<BodyComponent?> bodyEnt,
+ Entity<BodyPartComponent> partEnt,
+ string slotId)
{
// TODO: Predict this probably.
- base.AddPart(bodyUid, partUid, slotId, component, bodyComp);
+ base.AddPart(bodyEnt, partEnt, slotId);
- if (TryComp<HumanoidAppearanceComponent>(bodyUid, out var humanoid))
+ if (TryComp<HumanoidAppearanceComponent>(bodyEnt, out var humanoid))
{
- var layer = component.ToHumanoidLayers();
+ var layer = partEnt.Comp.ToHumanoidLayers();
if (layer != null)
{
var layers = HumanoidVisualLayersExtension.Sublayers(layer.Value);
- _humanoidSystem.SetLayersVisibility(bodyUid, layers, true, true, humanoid);
+ _humanoidSystem.SetLayersVisibility(
+ bodyEnt, layers, visible: true, permanent: true, humanoid);
}
}
}
protected override void RemovePart(
- EntityUid bodyUid,
- EntityUid partUid,
- string slotId,
- BodyPartComponent component,
- BodyComponent? bodyComp = null)
+ Entity<BodyComponent?> bodyEnt,
+ Entity<BodyPartComponent> partEnt,
+ string slotId)
{
- base.RemovePart(bodyUid, partUid, slotId, component, bodyComp);
+ base.RemovePart(bodyEnt, partEnt, slotId);
- if (!TryComp<HumanoidAppearanceComponent>(bodyUid, out var humanoid))
+ if (!TryComp<HumanoidAppearanceComponent>(bodyEnt, out var humanoid))
return;
- var layer = component.ToHumanoidLayers();
+ var layer = partEnt.Comp.ToHumanoidLayers();
- if (layer == null)
+ if (layer is null)
return;
var layers = HumanoidVisualLayersExtension.Sublayers(layer.Value);
- _humanoidSystem.SetLayersVisibility(bodyUid, layers, false, true, humanoid);
+ _humanoidSystem.SetLayersVisibility(
+ bodyEnt, layers, visible: false, permanent: true, humanoid);
}
public override HashSet<EntityUid> GibBody(
EntityUid bodyId,
bool gibOrgans = false,
- BodyComponent? body = null ,
+ BodyComponent? body = null,
bool launchGibs = true,
Vector2? splatDirection = null,
float splatModifier = 1,
SoundSpecifier? gibSoundOverride = null
)
{
- if (!Resolve(bodyId, ref body, false))
- return new HashSet<EntityUid>();
-
- if (TerminatingOrDeleted(bodyId) || EntityManager.IsQueuedForDeletion(bodyId))
+ if (!Resolve(bodyId, ref body, logMissing: false)
+ || TerminatingOrDeleted(bodyId)
+ || EntityManager.IsQueuedForDeletion(bodyId))
+ {
return new HashSet<EntityUid>();
+ }
var xform = Transform(bodyId);
- if (xform.MapUid == null)
+ if (xform.MapUid is null)
return new HashSet<EntityUid>();
var gibs = base.GibBody(bodyId, gibOrgans, body, launchGibs: launchGibs,
splatDirection: splatDirection, splatModifier: splatModifier, splatCone:splatCone);
- RaiseLocalEvent(bodyId, new BeingGibbedEvent(gibs));
+
+ var ev = new BeingGibbedEvent(gibs);
+ RaiseLocalEvent(bodyId, ref ev);
+
QueueDel(bodyId);
return gibs;
{
base.Initialize();
- SubscribeLocalEvent<BrainComponent, AddedToPartInBodyEvent>((uid, _, args) => HandleMind(args.Body, uid));
- SubscribeLocalEvent<BrainComponent, RemovedFromPartInBodyEvent>((uid, _, args) => HandleMind(uid, args.OldBody));
+ SubscribeLocalEvent<BrainComponent, OrganAddedToBodyEvent>((uid, _, args) => HandleMind(args.Body, uid));
+ SubscribeLocalEvent<BrainComponent, OrganRemovedFromBodyEvent>((uid, _, args) => HandleMind(uid, args.OldBody));
SubscribeLocalEvent<BrainComponent, PointAttemptEvent>(OnPointAttempt);
}
_mindSystem.TransferTo(mindId, newEntity, mind: mind);
}
- private void OnPointAttempt(EntityUid uid, BrainComponent component, PointAttemptEvent args)
+ private void OnPointAttempt(Entity<BrainComponent> ent, ref PointAttemptEvent args)
{
args.Cancel();
}
public sealed class InternalsSystem : EntitySystem
{
- [Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly AlertsSystem _alerts = default!;
[Dependency] private readonly AtmosphereSystem _atmos = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly GasTankSystem _gasTank = default!;
- [Dependency] private readonly HandsSystem _hands = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
SubscribeLocalEvent<InternalsComponent, InternalsDoAfterEvent>(OnDoAfter);
}
- private void OnGetInteractionVerbs(EntityUid uid, InternalsComponent component, GetVerbsEvent<InteractionVerb> args)
+ private void OnGetInteractionVerbs(
+ Entity<InternalsComponent> ent,
+ ref GetVerbsEvent<InteractionVerb> args)
{
- if (!args.CanAccess || !args.CanInteract || args.Hands == null)
+ if (!args.CanAccess || !args.CanInteract || args.Hands is null)
return;
+ var user = args.User;
+
InteractionVerb verb = new()
{
Act = () =>
{
- ToggleInternals(uid, args.User, false, component);
+ ToggleInternals(ent, user, force: false, ent);
},
Message = Loc.GetString("action-description-internals-toggle"),
- Icon = new SpriteSpecifier.Texture(new ("/Textures/Interface/VerbIcons/dot.svg.192dpi.png")),
+ Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/dot.svg.192dpi.png")),
Text = Loc.GetString("action-name-internals-toggle"),
};
args.Verbs.Add(verb);
}
- public void ToggleInternals(EntityUid uid, EntityUid user, bool force, InternalsComponent? internals = null)
+ public void ToggleInternals(
+ EntityUid uid,
+ EntityUid user,
+ bool force,
+ InternalsComponent? internals = null)
{
- if (!Resolve(uid, ref internals, false))
+ if (!Resolve(uid, ref internals, logMissing: false))
return;
// Toggle off if they're on
return;
}
- StartToggleInternalsDoAfter(user, uid, internals);
+ StartToggleInternalsDoAfter(user, (uid, internals));
return;
}
// If they're not on then check if we have a mask to use
- if (internals.BreathToolEntity == null)
+ if (internals.BreathToolEntity is null)
{
_popupSystem.PopupEntity(Loc.GetString("internals-no-breath-tool"), uid, user);
return;
var tank = FindBestGasTank(uid);
- if (tank == null)
+ if (tank is null)
{
_popupSystem.PopupEntity(Loc.GetString("internals-no-tank"), uid, user);
return;
if (!force)
{
- StartToggleInternalsDoAfter(user, uid, internals);
+ StartToggleInternalsDoAfter(user, (uid, internals));
return;
}
_gasTank.ConnectToInternals(tank.Value);
}
- private void StartToggleInternalsDoAfter(EntityUid user, EntityUid target, InternalsComponent internals)
+ private void StartToggleInternalsDoAfter(EntityUid user, Entity<InternalsComponent> targetEnt)
{
// Is the target not you? If yes, use a do-after to give them time to respond.
- var isUser = user == target;
- var delay = !isUser ? internals.Delay : 0f;
+ var isUser = user == targetEnt.Owner;
+ var delay = !isUser ? targetEnt.Comp.Delay : TimeSpan.Zero;
- _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, delay, new InternalsDoAfterEvent(), target, target: target)
+ _doAfter.TryStartDoAfter(new DoAfterArgs(EntityManager, user, delay, new InternalsDoAfterEvent(), targetEnt, target: targetEnt)
{
BreakOnDamage = true,
BreakOnMove = true,
});
}
- private void OnDoAfter(EntityUid uid, InternalsComponent component, InternalsDoAfterEvent args)
+ private void OnDoAfter(Entity<InternalsComponent> ent, ref InternalsDoAfterEvent args)
{
if (args.Cancelled || args.Handled)
return;
- ToggleInternals(uid, args.User, true, component);
+ ToggleInternals(ent, args.User, force: true, ent);
args.Handled = true;
}
- private void OnInternalsStartup(EntityUid uid, InternalsComponent component, ComponentStartup args)
+ private void OnInternalsStartup(Entity<InternalsComponent> ent, ref ComponentStartup args)
{
- _alerts.ShowAlert(uid, AlertType.Internals, GetSeverity(component));
+ _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent));
}
- private void OnInternalsShutdown(EntityUid uid, InternalsComponent component, ComponentShutdown args)
+ private void OnInternalsShutdown(Entity<InternalsComponent> ent, ref ComponentShutdown args)
{
- _alerts.ClearAlert(uid, AlertType.Internals);
+ _alerts.ClearAlert(ent, AlertType.Internals);
}
- private void OnInhaleLocation(EntityUid uid, InternalsComponent component, InhaleLocationEvent args)
+ private void OnInhaleLocation(Entity<InternalsComponent> ent, ref InhaleLocationEvent args)
{
- if (AreInternalsWorking(component))
+ if (AreInternalsWorking(ent))
{
- var gasTank = Comp<GasTankComponent>(component.GasTankEntity!.Value);
- args.Gas = _gasTank.RemoveAirVolume((component.GasTankEntity.Value, gasTank), Atmospherics.BreathVolume);
+ var gasTank = Comp<GasTankComponent>(ent.Comp.GasTankEntity!.Value);
+ args.Gas = _gasTank.RemoveAirVolume((ent.Comp.GasTankEntity.Value, gasTank), Atmospherics.BreathVolume);
// TODO: Should listen to gas tank updates instead I guess?
- _alerts.ShowAlert(uid, AlertType.Internals, GetSeverity(component));
+ _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent));
}
}
public void DisconnectBreathTool(Entity<InternalsComponent> ent)
{
- var (owner, component) = ent;
- var old = component.BreathToolEntity;
- component.BreathToolEntity = null;
+ var old = ent.Comp.BreathToolEntity;
+ ent.Comp.BreathToolEntity = null;
- if (TryComp(old, out BreathToolComponent? breathTool) )
+ if (TryComp(old, out BreathToolComponent? breathTool))
{
_atmos.DisconnectInternals(breathTool);
DisconnectTank(ent);
}
- _alerts.ShowAlert(owner, AlertType.Internals, GetSeverity(component));
+ _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent));
}
public void ConnectBreathTool(Entity<InternalsComponent> ent, EntityUid toolEntity)
{
- var (owner, component) = ent;
- if (TryComp(component.BreathToolEntity, out BreathToolComponent? tool))
+ if (TryComp(ent.Comp.BreathToolEntity, out BreathToolComponent? tool))
{
_atmos.DisconnectInternals(tool);
}
- component.BreathToolEntity = toolEntity;
- _alerts.ShowAlert(owner, AlertType.Internals, GetSeverity(component));
+ ent.Comp.BreathToolEntity = toolEntity;
+ _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent));
}
public void DisconnectTank(InternalsComponent? component)
{
- if (component == null)
+ if (component is null)
return;
if (TryComp(component.GasTankEntity, out GasTankComponent? tank))
public bool TryConnectTank(Entity<InternalsComponent> ent, EntityUid tankEntity)
{
- var component = ent.Comp;
- if (component.BreathToolEntity == null)
+ if (ent.Comp.BreathToolEntity is null)
return false;
- if (TryComp(component.GasTankEntity, out GasTankComponent? tank))
- _gasTank.DisconnectFromInternals((component.GasTankEntity.Value, tank));
+ if (TryComp(ent.Comp.GasTankEntity, out GasTankComponent? tank))
+ _gasTank.DisconnectFromInternals((ent.Comp.GasTankEntity.Value, tank));
- component.GasTankEntity = tankEntity;
- _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(component));
+ ent.Comp.GasTankEntity = tankEntity;
+ _alerts.ShowAlert(ent, AlertType.Internals, GetSeverity(ent));
return true;
}
public bool AreInternalsWorking(EntityUid uid, InternalsComponent? component = null)
{
- if (!Resolve(uid, ref component, false))
- return false;
-
- return AreInternalsWorking(component);
+ return Resolve(uid, ref component, logMissing: false)
+ && AreInternalsWorking(component);
}
public bool AreInternalsWorking(InternalsComponent component)
{
- return TryComp(component.BreathToolEntity, out BreathToolComponent? breathTool) &&
- breathTool.IsFunctional &&
- TryComp(component.GasTankEntity, out GasTankComponent? _);
+ return TryComp(component.BreathToolEntity, out BreathToolComponent? breathTool)
+ && breathTool.IsFunctional
+ && HasComp<GasTankComponent>(component.GasTankEntity);
}
private short GetSeverity(InternalsComponent component)
{
- if (component.BreathToolEntity == null || !AreInternalsWorking(component))
+ if (component.BreathToolEntity is null || !AreInternalsWorking(component))
return 2;
// If pressure in the tank is below low pressure threshhold, flash warning on internals UI
- if (TryComp<GasTankComponent>(component.GasTankEntity, out var gasTank) && gasTank.IsLowPressure)
+ if (TryComp<GasTankComponent>(component.GasTankEntity, out var gasTank)
+ && gasTank.IsLowPressure)
+ {
return 0;
+ }
return 1;
}
- public Entity<GasTankComponent>? FindBestGasTank(Entity<HandsComponent?, InventoryComponent?, ContainerManagerComponent?> user)
+ public Entity<GasTankComponent>? FindBestGasTank(
+ Entity<HandsComponent?, InventoryComponent?, ContainerManagerComponent?> user)
{
// Prioritise
// 1. back equipped tanks
// 3. in-hand tanks
// 4. pocket/belt tanks
- if (!Resolve(user.Owner, ref user.Comp1, ref user.Comp2, ref user.Comp3))
+ if (!Resolve(user, ref user.Comp1, ref user.Comp2, ref user.Comp3))
return null;
- if (_inventory.TryGetSlotEntity(user.Owner, "back", out var backEntity, user.Comp2, user.Comp3) &&
+ if (_inventory.TryGetSlotEntity(user, "back", out var backEntity, user.Comp2, user.Comp3) &&
TryComp<GasTankComponent>(backEntity, out var backGasTank) &&
_gasTank.CanConnectToInternals(backGasTank))
{
return (backEntity.Value, backGasTank);
}
- if (_inventory.TryGetSlotEntity(user.Owner, "suitstorage", out var entity, user.Comp2, user.Comp3) &&
+ if (_inventory.TryGetSlotEntity(user, "suitstorage", out var entity, user.Comp2, user.Comp3) &&
TryComp<GasTankComponent>(entity, out var gasTank) &&
_gasTank.CanConnectToInternals(gasTank))
{
-using Content.Server.Atmos.Components;
+using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Components;
using Content.Server.Chemistry.Containers.EntitySystems;
SubscribeLocalEvent<BreathToolComponent, ItemMaskToggledEvent>(OnMaskToggled);
}
- private void OnGotUnequipped(EntityUid uid, BreathToolComponent component, GotUnequippedEvent args)
+ private void OnGotUnequipped(Entity<BreathToolComponent> ent, ref GotUnequippedEvent args)
{
- _atmosphereSystem.DisconnectInternals(component);
+ _atmosphereSystem.DisconnectInternals(ent);
}
- private void OnGotEquipped(EntityUid uid, BreathToolComponent component, GotEquippedEvent args)
+ private void OnGotEquipped(Entity<BreathToolComponent> ent, ref GotEquippedEvent args)
{
+ if ((args.SlotFlags & ent.Comp.AllowedSlots) == 0)
+ {
+ return;
+ }
- if ((args.SlotFlags & component.AllowedSlots) == 0) return;
- component.IsFunctional = true;
+ ent.Comp.IsFunctional = true;
if (TryComp(args.Equipee, out InternalsComponent? internals))
{
- component.ConnectedInternalsEntity = args.Equipee;
- _internals.ConnectBreathTool((args.Equipee, internals), uid);
+ ent.Comp.ConnectedInternalsEntity = args.Equipee;
+ _internals.ConnectBreathTool((args.Equipee, internals), ent);
}
}
if (moles <= 0)
continue;
var reagent = _atmosphereSystem.GasReagents[i];
- if (reagent == null) continue;
+ if (reagent is null) continue;
var amount = moles * Atmospherics.BreathMolesToReagentMultiplier;
solution.AddReagent(reagent, amount);
using Robust.Shared.Collections;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
+using Robust.Shared.Timing;
namespace Content.Server.Body.Systems
{
public sealed class MetabolizerSystem : EntitySystem
{
+ [Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
_solutionQuery = GetEntityQuery<SolutionContainerManagerComponent>();
SubscribeLocalEvent<MetabolizerComponent, ComponentInit>(OnMetabolizerInit);
+ SubscribeLocalEvent<MetabolizerComponent, MapInitEvent>(OnMapInit);
+ SubscribeLocalEvent<MetabolizerComponent, EntityUnpausedEvent>(OnUnpaused);
SubscribeLocalEvent<MetabolizerComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
}
+ private void OnMapInit(Entity<MetabolizerComponent> ent, ref MapInitEvent args)
+ {
+ ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval;
+ }
+
+ private void OnUnpaused(Entity<MetabolizerComponent> ent, ref EntityUnpausedEvent args)
+ {
+ ent.Comp.NextUpdate += args.PausedTime;
+ }
+
private void OnMetabolizerInit(Entity<MetabolizerComponent> entity, ref ComponentInit args)
{
if (!entity.Comp.SolutionOnBody)
}
}
- private void OnApplyMetabolicMultiplier(EntityUid uid, MetabolizerComponent component,
- ApplyMetabolicMultiplierEvent args)
+ private void OnApplyMetabolicMultiplier(
+ Entity<MetabolizerComponent> ent,
+ ref ApplyMetabolicMultiplierEvent args)
{
if (args.Apply)
{
- component.UpdateFrequency *= args.Multiplier;
+ ent.Comp.UpdateInterval *= args.Multiplier;
return;
}
- component.UpdateFrequency /= args.Multiplier;
- // Reset the accumulator properly
- if (component.AccumulatedFrametime >= component.UpdateFrequency)
- component.AccumulatedFrametime = component.UpdateFrequency;
+ ent.Comp.UpdateInterval /= args.Multiplier;
}
public override void Update(float frameTime)
foreach (var (uid, metab) in metabolizers)
{
- metab.AccumulatedFrametime += frameTime;
-
// Only update as frequently as it should
- if (metab.AccumulatedFrametime < metab.UpdateFrequency)
+ if (_gameTiming.CurTime < metab.NextUpdate)
continue;
- metab.AccumulatedFrametime -= metab.UpdateFrequency;
- TryMetabolize(uid, metab);
+ metab.NextUpdate += metab.UpdateInterval;
+ TryMetabolize((uid, metab));
}
}
- private void TryMetabolize(EntityUid uid, MetabolizerComponent meta, OrganComponent? organ = null)
+ private void TryMetabolize(Entity<MetabolizerComponent, OrganComponent?, SolutionContainerManagerComponent?> ent)
{
- _organQuery.Resolve(uid, ref organ, false);
+ _organQuery.Resolve(ent, ref ent.Comp2, logMissing: false);
// First step is get the solution we actually care about
+ var solutionName = ent.Comp1.SolutionName;
Solution? solution = null;
Entity<SolutionComponent>? soln = default!;
EntityUid? solutionEntityUid = null;
- SolutionContainerManagerComponent? manager = null;
-
- if (meta.SolutionOnBody)
+ if (ent.Comp1.SolutionOnBody)
{
- if (organ?.Body is { } body)
+ if (ent.Comp2?.Body is { } body)
{
- if (!_solutionQuery.Resolve(body, ref manager, false))
+ if (!_solutionQuery.Resolve(body, ref ent.Comp3, logMissing: false))
return;
- _solutionContainerSystem.TryGetSolution((body, manager), meta.SolutionName, out soln, out solution);
+ _solutionContainerSystem.TryGetSolution((body, ent.Comp3), solutionName, out soln, out solution);
solutionEntityUid = body;
}
}
else
{
- if (!_solutionQuery.Resolve(uid, ref manager, false))
+ if (!_solutionQuery.Resolve(ent, ref ent.Comp3, logMissing: false))
return;
- _solutionContainerSystem.TryGetSolution((uid, manager), meta.SolutionName, out soln, out solution);
- solutionEntityUid = uid;
+ _solutionContainerSystem.TryGetSolution((ent, ent), solutionName, out soln, out solution);
+ solutionEntityUid = ent;
}
- if (solutionEntityUid == null || soln is null || solution is null || solution.Contents.Count == 0)
+ if (solutionEntityUid is null
+ || soln is null
+ || solution is null
+ || solution.Contents.Count == 0)
+ {
return;
+ }
// randomize the reagent list so we don't have any weird quirks
// like alphabetical order or insertion order mattering for processing
continue;
var mostToRemove = FixedPoint2.Zero;
- if (proto.Metabolisms == null)
+ if (proto.Metabolisms is null)
{
- if (meta.RemoveEmpty)
+ if (ent.Comp1.RemoveEmpty)
{
solution.RemoveReagent(reagent, FixedPoint2.New(1));
}
}
// we're done here entirely if this is true
- if (reagents >= meta.MaxReagentsProcessable)
+ if (reagents >= ent.Comp1.MaxReagentsProcessable)
return;
// loop over all our groups and see which ones apply
- if (meta.MetabolismGroups == null)
+ if (ent.Comp1.MetabolismGroups is null)
continue;
- foreach (var group in meta.MetabolismGroups)
+ foreach (var group in ent.Comp1.MetabolismGroups)
{
if (!proto.Metabolisms.TryGetValue(group.Id, out var entry))
continue;
// if it's possible for them to be dead, and they are,
// then we shouldn't process any effects, but should probably
// still remove reagents
- if (EntityManager.TryGetComponent<MobStateComponent>(solutionEntityUid.Value, out var state))
+ if (TryComp<MobStateComponent>(solutionEntityUid.Value, out var state))
{
if (!proto.WorksOnTheDead && _mobStateSystem.IsDead(solutionEntityUid.Value, state))
continue;
}
- var actualEntity = organ?.Body ?? solutionEntityUid.Value;
- var args = new ReagentEffectArgs(actualEntity, uid, solution, proto, mostToRemove,
+ var actualEntity = ent.Comp2?.Body ?? solutionEntityUid.Value;
+ var args = new ReagentEffectArgs(actualEntity, ent, solution, proto, mostToRemove,
EntityManager, null, scale);
// do all effects, if conditions apply
if (effect.ShouldLog)
{
- _adminLogger.Add(LogType.ReagentEffect, effect.LogImpact,
- $"Metabolism effect {effect.GetType().Name:effect} of reagent {proto.LocalizedName:reagent} applied on entity {actualEntity:entity} at {Transform(actualEntity).Coordinates:coordinates}");
+ _adminLogger.Add(
+ LogType.ReagentEffect,
+ effect.LogImpact,
+ $"Metabolism effect {effect.GetType().Name:effect}"
+ + $" of reagent {proto.LocalizedName:reagent}"
+ + $" applied on entity {actualEntity:entity}"
+ + $" at {Transform(actualEntity).Coordinates:coordinates}"
+ );
}
effect.Effect(args);
}
}
- public sealed class ApplyMetabolicMultiplierEvent : EntityEventArgs
+ [ByRefEvent]
+ public readonly record struct ApplyMetabolicMultiplierEvent(
+ EntityUid Uid,
+ float Multiplier,
+ bool Apply)
{
- // The entity whose metabolism is being modified
- public EntityUid Uid;
-
- // What the metabolism's update rate will be multiplied by
- public float Multiplier;
-
- // Apply this multiplier or ignore / reset it?
- public bool Apply;
+ /// <summary>
+ /// The entity whose metabolism is being modified.
+ /// </summary>
+ public readonly EntityUid Uid = Uid;
+
+ /// <summary>
+ /// What the metabolism's update rate will be multiplied by.
+ /// </summary>
+ public readonly float Multiplier = Multiplier;
+
+ /// <summary>
+ /// If true, apply the multiplier. If false, revert it.
+ /// </summary>
+ public readonly bool Apply = Apply;
}
}
// We want to process lung reagents before we inhale new reagents.
UpdatesAfter.Add(typeof(MetabolizerSystem));
+ SubscribeLocalEvent<RespiratorComponent, MapInitEvent>(OnMapInit);
+ SubscribeLocalEvent<RespiratorComponent, EntityUnpausedEvent>(OnUnpaused);
SubscribeLocalEvent<RespiratorComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
}
+ private void OnMapInit(Entity<RespiratorComponent> ent, ref MapInitEvent args)
+ {
+ ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval;
+ }
+
+ private void OnUnpaused(Entity<RespiratorComponent> ent, ref EntityUnpausedEvent args)
+ {
+ ent.Comp.NextUpdate += args.PausedTime;
+ }
+
public override void Update(float frameTime)
{
base.Update(frameTime);
var query = EntityQueryEnumerator<RespiratorComponent, BodyComponent>();
while (query.MoveNext(out var uid, out var respirator, out var body))
{
- if (_mobState.IsDead(uid))
- {
+ if (_gameTiming.CurTime < respirator.NextUpdate)
continue;
- }
- respirator.AccumulatedFrametime += frameTime;
+ respirator.NextUpdate += respirator.UpdateInterval;
- if (respirator.AccumulatedFrametime < respirator.CycleDelay)
+ if (_mobState.IsDead(uid))
continue;
- respirator.AccumulatedFrametime -= respirator.CycleDelay;
- UpdateSaturation(uid, -respirator.CycleDelay, respirator);
+
+ UpdateSaturation(uid, -(float) respirator.UpdateInterval.TotalSeconds, respirator);
if (!_mobState.IsIncapacitated(uid)) // cannot breathe in crit.
{
_popupSystem.PopupEntity(Loc.GetString("lung-behavior-gasp"), uid);
}
- TakeSuffocationDamage(uid, respirator);
+ TakeSuffocationDamage((uid, respirator));
respirator.SuffocationCycles += 1;
continue;
}
- StopSuffocation(uid, respirator);
+ StopSuffocation((uid, respirator));
respirator.SuffocationCycles = 0;
}
}
public void Inhale(EntityUid uid, BodyComponent? body = null)
{
- if (!Resolve(uid, ref body, false))
+ if (!Resolve(uid, ref body, logMissing: false))
return;
var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(uid, body);
// Inhale gas
var ev = new InhaleLocationEvent();
- RaiseLocalEvent(uid, ev);
+ RaiseLocalEvent(uid, ref ev, broadcast: false);
- ev.Gas ??= _atmosSys.GetContainingMixture(uid, false, true);
+ ev.Gas ??= _atmosSys.GetContainingMixture(uid, excite: true);
- if (ev.Gas == null)
+ if (ev.Gas is null)
{
return;
}
public void Exhale(EntityUid uid, BodyComponent? body = null)
{
- if (!Resolve(uid, ref body, false))
+ if (!Resolve(uid, ref body, logMissing: false))
return;
var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(uid, body);
// exhale gas
var ev = new ExhaleLocationEvent();
- RaiseLocalEvent(uid, ev, false);
+ RaiseLocalEvent(uid, ref ev, broadcast: false);
- if (ev.Gas == null)
+ if (ev.Gas is null)
{
- ev.Gas = _atmosSys.GetContainingMixture(uid, false, true);
+ ev.Gas = _atmosSys.GetContainingMixture(uid, excite: true);
// Walls and grids without atmos comp return null. I guess it makes sense to not be able to exhale in walls,
// but this also means you cannot exhale on some grids.
_atmosSys.Merge(ev.Gas, outGas);
}
- private void TakeSuffocationDamage(EntityUid uid, RespiratorComponent respirator)
+ private void TakeSuffocationDamage(Entity<RespiratorComponent> ent)
{
- if (respirator.SuffocationCycles == 2)
- _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(uid):entity} started suffocating");
+ if (ent.Comp.SuffocationCycles == 2)
+ _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(ent):entity} started suffocating");
- if (respirator.SuffocationCycles >= respirator.SuffocationCycleThreshold)
+ if (ent.Comp.SuffocationCycles >= ent.Comp.SuffocationCycleThreshold)
{
// TODO: This is not going work with multiple different lungs, if that ever becomes a possibility
- var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(uid);
+ var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(ent);
foreach (var (comp, _) in organs)
{
- _alertsSystem.ShowAlert(uid, comp.Alert);
+ _alertsSystem.ShowAlert(ent, comp.Alert);
}
}
- _damageableSys.TryChangeDamage(uid, respirator.Damage, false, false);
+ _damageableSys.TryChangeDamage(ent, ent.Comp.Damage, interruptsDoAfters: false);
}
- private void StopSuffocation(EntityUid uid, RespiratorComponent respirator)
+ private void StopSuffocation(Entity<RespiratorComponent> ent)
{
- if (respirator.SuffocationCycles >= 2)
- _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(uid):entity} stopped suffocating");
+ if (ent.Comp.SuffocationCycles >= 2)
+ _adminLogger.Add(LogType.Asphyxiation, $"{ToPrettyString(ent):entity} stopped suffocating");
// TODO: This is not going work with multiple different lungs, if that ever becomes a possibility
- var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(uid);
+ var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(ent);
foreach (var (comp, _) in organs)
{
- _alertsSystem.ClearAlert(uid, comp.Alert);
+ _alertsSystem.ClearAlert(ent, comp.Alert);
}
- _damageableSys.TryChangeDamage(uid, respirator.DamageRecovery);
+ _damageableSys.TryChangeDamage(ent, ent.Comp.DamageRecovery);
}
public void UpdateSaturation(EntityUid uid, float amount,
Math.Clamp(respirator.Saturation, respirator.MinSaturation, respirator.MaxSaturation);
}
- private void OnApplyMetabolicMultiplier(EntityUid uid, RespiratorComponent component,
- ApplyMetabolicMultiplierEvent args)
+ private void OnApplyMetabolicMultiplier(
+ Entity<RespiratorComponent> ent,
+ ref ApplyMetabolicMultiplierEvent args)
{
if (args.Apply)
{
- component.CycleDelay *= args.Multiplier;
- component.Saturation *= args.Multiplier;
- component.MaxSaturation *= args.Multiplier;
- component.MinSaturation *= args.Multiplier;
+ ent.Comp.UpdateInterval *= args.Multiplier;
+ ent.Comp.Saturation *= args.Multiplier;
+ ent.Comp.MaxSaturation *= args.Multiplier;
+ ent.Comp.MinSaturation *= args.Multiplier;
return;
}
// This way we don't have to worry about it breaking if the stasis bed component is destroyed
- component.CycleDelay /= args.Multiplier;
- component.Saturation /= args.Multiplier;
- component.MaxSaturation /= args.Multiplier;
- component.MinSaturation /= args.Multiplier;
- // Reset the accumulator properly
- if (component.AccumulatedFrametime >= component.CycleDelay)
- component.AccumulatedFrametime = component.CycleDelay;
+ ent.Comp.UpdateInterval /= args.Multiplier;
+ ent.Comp.Saturation /= args.Multiplier;
+ ent.Comp.MaxSaturation /= args.Multiplier;
+ ent.Comp.MinSaturation /= args.Multiplier;
}
}
-public sealed class InhaleLocationEvent : EntityEventArgs
-{
- public GasMixture? Gas;
-}
+[ByRefEvent]
+public record struct InhaleLocationEvent(GasMixture? Gas);
-public sealed class ExhaleLocationEvent : EntityEventArgs
-{
- public GasMixture? Gas;
-}
+[ByRefEvent]
+public record struct ExhaleLocationEvent(GasMixture? Gas);
using Content.Shared.Body.Organ;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager;
+using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Server.Body.Systems
{
public sealed class StomachSystem : EntitySystem
{
+ [Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
public const string DefaultSolutionName = "stomach";
public override void Initialize()
{
+ SubscribeLocalEvent<StomachComponent, MapInitEvent>(OnMapInit);
+ SubscribeLocalEvent<StomachComponent, EntityUnpausedEvent>(OnUnpaused);
SubscribeLocalEvent<StomachComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
}
+ private void OnMapInit(Entity<StomachComponent> ent, ref MapInitEvent args)
+ {
+ ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval;
+ }
+
+ private void OnUnpaused(Entity<StomachComponent> ent, ref EntityUnpausedEvent args)
+ {
+ ent.Comp.NextUpdate += args.PausedTime;
+ }
+
public override void Update(float frameTime)
{
var query = EntityQueryEnumerator<StomachComponent, OrganComponent, SolutionContainerManagerComponent>();
while (query.MoveNext(out var uid, out var stomach, out var organ, out var sol))
{
- stomach.AccumulatedFrameTime += frameTime;
-
- if (stomach.AccumulatedFrameTime < stomach.UpdateInterval)
+ if (_gameTiming.CurTime < stomach.NextUpdate)
continue;
- stomach.AccumulatedFrameTime -= stomach.UpdateInterval;
+ stomach.NextUpdate += stomach.UpdateInterval;
// Get our solutions
if (!_solutionContainerSystem.ResolveSolution((uid, sol), DefaultSolutionName, ref stomach.Solution, out var stomachSolution))
}
}
- private void OnApplyMetabolicMultiplier(EntityUid uid, StomachComponent component,
- ApplyMetabolicMultiplierEvent args)
+ private void OnApplyMetabolicMultiplier(
+ Entity<StomachComponent> ent,
+ ref ApplyMetabolicMultiplierEvent args)
{
if (args.Apply)
{
- component.UpdateInterval *= args.Multiplier;
+ ent.Comp.UpdateInterval *= args.Multiplier;
return;
}
// This way we don't have to worry about it breaking if the stasis bed component is destroyed
- component.UpdateInterval /= args.Multiplier;
- // Reset the accumulator properly
- if (component.AccumulatedFrameTime >= component.UpdateInterval)
- component.AccumulatedFrameTime = component.UpdateInterval;
+ ent.Comp.UpdateInterval /= args.Multiplier;
}
- public bool CanTransferSolution(EntityUid uid, Solution solution,
+ public bool CanTransferSolution(
+ EntityUid uid,
+ Solution solution,
StomachComponent? stomach = null,
SolutionContainerManagerComponent? solutions = null)
{
- if (!Resolve(uid, ref stomach, ref solutions, false))
- return false;
-
- if (!_solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution, out var stomachSolution))
- return false;
-
- // TODO: For now no partial transfers. Potentially change by design
- if (!stomachSolution.CanAddSolution(solution))
- return false;
-
- return true;
+ return Resolve(uid, ref stomach, ref solutions, logMissing: false)
+ && _solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution, out var stomachSolution)
+ // TODO: For now no partial transfers. Potentially change by design
+ && stomachSolution.CanAddSolution(solution);
}
- public bool TryTransferSolution(EntityUid uid, Solution solution,
+ public bool TryTransferSolution(
+ EntityUid uid,
+ Solution solution,
StomachComponent? stomach = null,
SolutionContainerManagerComponent? solutions = null)
{
- if (!Resolve(uid, ref stomach, ref solutions, false))
- return false;
-
- if (!_solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution)
+ if (!Resolve(uid, ref stomach, ref solutions, logMissing: false)
+ || !_solutionContainerSystem.ResolveSolution((uid, solutions), DefaultSolutionName, ref stomach.Solution)
|| !CanTransferSolution(uid, solution, stomach, solutions))
+ {
return false;
+ }
_solutionContainerSystem.TryAddSolution(stomach.Solution.Value, solution);
// Add each reagent to ReagentDeltas. Used to track how long each reagent has been in the stomach
-using Content.Server.Body.Components;
+using Content.Server.Body.Components;
using Content.Server.Temperature.Components;
using Content.Server.Temperature.Systems;
using Content.Shared.ActionBlocker;
+using Robust.Shared.Timing;
namespace Content.Server.Body.Systems;
public sealed class ThermalRegulatorSystem : EntitySystem
{
+ [Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly TemperatureSystem _tempSys = default!;
[Dependency] private readonly ActionBlockerSystem _actionBlockerSys = default!;
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<ThermalRegulatorComponent, MapInitEvent>(OnMapInit);
+ SubscribeLocalEvent<ThermalRegulatorComponent, EntityUnpausedEvent>(OnUnpaused);
+ }
+
+ private void OnMapInit(Entity<ThermalRegulatorComponent> ent, ref MapInitEvent args)
+ {
+ ent.Comp.NextUpdate = _gameTiming.CurTime + ent.Comp.UpdateInterval;
+ }
+
+ private void OnUnpaused(Entity<ThermalRegulatorComponent> ent, ref EntityUnpausedEvent args)
+ {
+ ent.Comp.NextUpdate += args.PausedTime;
+ }
+
public override void Update(float frameTime)
{
var query = EntityQueryEnumerator<ThermalRegulatorComponent>();
while (query.MoveNext(out var uid, out var regulator))
{
- regulator.AccumulatedFrametime += frameTime;
- if (regulator.AccumulatedFrametime < 1)
+ if (_gameTiming.CurTime < regulator.NextUpdate)
continue;
- regulator.AccumulatedFrametime -= 1;
- ProcessThermalRegulation(uid, regulator);
+ regulator.NextUpdate += regulator.UpdateInterval;
+ ProcessThermalRegulation((uid, regulator));
}
}
/// <summary>
/// Processes thermal regulation for a mob
/// </summary>
- private void ProcessThermalRegulation(EntityUid uid, ThermalRegulatorComponent comp)
+ private void ProcessThermalRegulation(Entity<ThermalRegulatorComponent, TemperatureComponent?> ent)
{
- if (!EntityManager.TryGetComponent(uid, out TemperatureComponent? temperatureComponent)) return;
+ if (!Resolve(ent, ref ent.Comp2, logMissing: false))
+ return;
- var totalMetabolismTempChange = comp.MetabolismHeat - comp.RadiatedHeat;
+ var totalMetabolismTempChange = ent.Comp1.MetabolismHeat - ent.Comp1.RadiatedHeat;
// implicit heat regulation
- var tempDiff = Math.Abs(temperatureComponent.CurrentTemperature - comp.NormalBodyTemperature);
- var heatCapacity = _tempSys.GetHeatCapacity(uid, temperatureComponent);
+ var tempDiff = Math.Abs(ent.Comp2.CurrentTemperature - ent.Comp1.NormalBodyTemperature);
+ var heatCapacity = _tempSys.GetHeatCapacity(ent, ent);
var targetHeat = tempDiff * heatCapacity;
- if (temperatureComponent.CurrentTemperature > comp.NormalBodyTemperature)
+ if (ent.Comp2.CurrentTemperature > ent.Comp1.NormalBodyTemperature)
{
- totalMetabolismTempChange -= Math.Min(targetHeat, comp.ImplicitHeatRegulation);
+ totalMetabolismTempChange -= Math.Min(targetHeat, ent.Comp1.ImplicitHeatRegulation);
}
else
{
- totalMetabolismTempChange += Math.Min(targetHeat, comp.ImplicitHeatRegulation);
+ totalMetabolismTempChange += Math.Min(targetHeat, ent.Comp1.ImplicitHeatRegulation);
}
- _tempSys.ChangeHeat(uid, totalMetabolismTempChange, true, temperatureComponent);
+ _tempSys.ChangeHeat(ent, totalMetabolismTempChange, ignoreHeatResistance: true, ent);
// recalc difference and target heat
- tempDiff = Math.Abs(temperatureComponent.CurrentTemperature - comp.NormalBodyTemperature);
+ tempDiff = Math.Abs(ent.Comp2.CurrentTemperature - ent.Comp1.NormalBodyTemperature);
targetHeat = tempDiff * heatCapacity;
// if body temperature is not within comfortable, thermal regulation
// processes starts
- if (tempDiff > comp.ThermalRegulationTemperatureThreshold)
+ if (tempDiff > ent.Comp1.ThermalRegulationTemperatureThreshold)
return;
- if (temperatureComponent.CurrentTemperature > comp.NormalBodyTemperature)
+ if (ent.Comp2.CurrentTemperature > ent.Comp1.NormalBodyTemperature)
{
- if (!_actionBlockerSys.CanSweat(uid)) return;
- _tempSys.ChangeHeat(uid, -Math.Min(targetHeat, comp.SweatHeatRegulation), true,
- temperatureComponent);
+ if (!_actionBlockerSys.CanSweat(ent))
+ return;
+
+ _tempSys.ChangeHeat(ent, -Math.Min(targetHeat, ent.Comp1.SweatHeatRegulation), ignoreHeatResistance: true, ent);
}
else
{
- if (!_actionBlockerSys.CanShiver(uid)) return;
- _tempSys.ChangeHeat(uid, Math.Min(targetHeat, comp.ShiveringHeatRegulation), true,
- temperatureComponent);
+ if (!_actionBlockerSys.CanShiver(ent))
+ return;
+
+ _tempSys.ChangeHeat(ent, Math.Min(targetHeat, ent.Comp1.ShiveringHeatRegulation), ignoreHeatResistance: true, ent);
}
}
}
private void HandleBodyPartAdded(EntityUid uid, HandsComponent component, ref BodyPartAddedEvent args)
{
- if (args.Part.PartType != BodyPartType.Hand)
+ if (args.Part.Comp.PartType != BodyPartType.Hand)
return;
// If this annoys you, which it should.
// Ping Smugleaf.
- var location = args.Part.Symmetry switch
+ var location = args.Part.Comp.Symmetry switch
{
BodyPartSymmetry.None => HandLocation.Middle,
BodyPartSymmetry.Left => HandLocation.Left,
BodyPartSymmetry.Right => HandLocation.Right,
- _ => throw new ArgumentOutOfRangeException(nameof(args.Part.Symmetry))
+ _ => throw new ArgumentOutOfRangeException(nameof(args.Part.Comp.Symmetry))
};
AddHand(uid, args.Slot, location);
private void HandleBodyPartRemoved(EntityUid uid, HandsComponent component, ref BodyPartRemovedEvent args)
{
- if (args.Part.PartType != BodyPartType.Hand)
+ if (args.Part.Comp.PartType != BodyPartType.Hand)
return;
RemoveHand(uid, args.Slot);
{
base.Initialize();
- SubscribeLocalEvent<NymphComponent, RemovedFromPartInBodyEvent>(OnRemovedFromPart);
+ SubscribeLocalEvent<NymphComponent, OrganRemovedFromBodyEvent>(OnRemovedFromPart);
}
- private void OnRemovedFromPart(EntityUid uid, NymphComponent comp, RemovedFromPartInBodyEvent args)
+ private void OnRemovedFromPart(EntityUid uid, NymphComponent comp, ref OrganRemovedFromBodyEvent args)
{
if (!_timing.IsFirstTimePredicted)
return;
public bool CanShiver(EntityUid uid)
{
var ev = new ShiverAttemptEvent(uid);
- RaiseLocalEvent(uid, ev);
+ RaiseLocalEvent(uid, ref ev);
return !ev.Cancelled;
}
public bool CanSweat(EntityUid uid)
{
var ev = new SweatAttemptEvent(uid);
- RaiseLocalEvent(uid, ev);
+ RaiseLocalEvent(uid, ref ev);
return !ev.Cancelled;
}
-namespace Content.Shared.Body.Events
-{
- // All of these events are raised on a mechanism entity when added/removed to a body in different
- // ways.
-
- /// <summary>
- /// Raised on a mechanism when it is added to a body part.
- /// </summary>
- public sealed class AddedToPartEvent : EntityEventArgs
- {
- public EntityUid Part;
-
- public AddedToPartEvent(EntityUid part)
- {
- Part = part;
- }
- }
-
- /// <summary>
- /// Raised on a mechanism when it is added to a body part within a body.
- /// </summary>
- public sealed class AddedToPartInBodyEvent : EntityEventArgs
- {
- public EntityUid Body;
- public EntityUid Part;
-
- public AddedToPartInBodyEvent(EntityUid body, EntityUid part)
- {
- Body = body;
- Part = part;
- }
- }
-
- /// <summary>
- /// Raised on a mechanism when it is removed from a body part.
- /// </summary>
- public sealed class RemovedFromPartEvent : EntityEventArgs
- {
- public EntityUid OldPart;
-
- public RemovedFromPartEvent(EntityUid oldPart)
- {
- OldPart = oldPart;
- }
- }
-
- /// <summary>
- /// Raised on a mechanism when it is removed from a body part within a body.
- /// </summary>
- public sealed class RemovedFromPartInBodyEvent : EntityEventArgs
- {
- public EntityUid OldBody;
- public EntityUid OldPart;
-
- public RemovedFromPartInBodyEvent(EntityUid oldBody, EntityUid oldPart)
- {
- OldBody = oldBody;
- OldPart = oldPart;
- }
- }
-}
+namespace Content.Shared.Body.Events;
+
+// All of these events are raised on a mechanism entity when added/removed to a body in different
+// ways.
+
+/// <summary>
+/// Raised on a mechanism when it is added to a body part.
+/// </summary>
+[ByRefEvent]
+public readonly record struct OrganAddedEvent(EntityUid Part);
+
+/// <summary>
+/// Raised on a mechanism when it is added to a body part within a body.
+/// </summary>
+[ByRefEvent]
+public readonly record struct OrganAddedToBodyEvent(EntityUid Body, EntityUid Part);
+
+/// <summary>
+/// Raised on a mechanism when it is removed from a body part.
+/// </summary>
+[ByRefEvent]
+public readonly record struct OrganRemovedEvent(EntityUid OldPart);
+
+/// <summary>
+/// Raised on a mechanism when it is removed from a body part within a body.
+/// </summary>
+[ByRefEvent]
+public readonly record struct OrganRemovedFromBodyEvent(EntityUid OldBody, EntityUid OldPart);
-namespace Content.Shared.Body.Events
-{
- public sealed class ShiverAttemptEvent : CancellableEntityEventArgs
- {
- public ShiverAttemptEvent(EntityUid uid)
- {
- Uid = uid;
- }
+namespace Content.Shared.Body.Events;
- public EntityUid Uid { get; }
- }
+[ByRefEvent]
+public record struct ShiverAttemptEvent(EntityUid Uid)
+{
+ public readonly EntityUid Uid = Uid;
+ public bool Cancelled = false;
}
-namespace Content.Shared.Body.Events
-{
- public sealed class SweatAttemptEvent : CancellableEntityEventArgs
- {
- public SweatAttemptEvent(EntityUid uid)
- {
- Uid = uid;
- }
+namespace Content.Shared.Body.Events;
- public EntityUid Uid { get; }
- }
+[ByRefEvent]
+public record struct SweatAttemptEvent(EntityUid Uid)
+{
+ public readonly EntityUid Uid = Uid;
+ public bool Cancelled = false;
}
-using Content.Shared.Body.Systems;
+using Content.Shared.Body.Systems;
using Robust.Shared.Containers;
using Robust.Shared.GameStates;
/// <summary>
/// Relevant body this organ is attached to.
/// </summary>
- [DataField("body"), AutoNetworkedField]
+ [DataField, AutoNetworkedField]
public EntityUid? Body;
}
-namespace Content.Shared.Body.Part;
+namespace Content.Shared.Body.Part;
[ByRefEvent]
-public readonly record struct BodyPartAddedEvent(string Slot, BodyPartComponent Part);
+public readonly record struct BodyPartAddedEvent(string Slot, Entity<BodyPartComponent> Part);
[ByRefEvent]
-public readonly record struct BodyPartRemovedEvent(string Slot, BodyPartComponent Part);
+public readonly record struct BodyPartRemovedEvent(string Slot, Entity<BodyPartComponent> Part);
using Content.Shared.Gibbing.Events;
using Content.Shared.Gibbing.Systems;
using Content.Shared.Inventory;
-using Content.Shared.Inventory.Events;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly GibbingSystem _gibbingSystem = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
+
private const float GibletLaunchImpulse = 8;
private const float GibletLaunchImpulseVariance = 3;
+
private void InitializeBody()
{
// Body here to handle root body parts.
SubscribeLocalEvent<BodyComponent, CanDragEvent>(OnBodyCanDrag);
}
- private void OnBodyInserted(EntityUid uid, BodyComponent component, EntInsertedIntoContainerMessage args)
+ private void OnBodyInserted(Entity<BodyComponent> ent, ref EntInsertedIntoContainerMessage args)
{
// Root body part?
var slotId = args.Container.ID;
if (slotId != BodyRootContainerId)
return;
- var entity = args.Entity;
+ var insertedUid = args.Entity;
- if (TryComp(entity, out BodyPartComponent? childPart))
+ if (TryComp(insertedUid, out BodyPartComponent? part))
{
- AddPart(uid, entity, slotId, childPart);
- RecursiveBodyUpdate(entity, uid, childPart);
+ AddPart((ent, ent), (insertedUid, part), slotId);
+ RecursiveBodyUpdate((insertedUid, part), ent);
}
- if (TryComp(entity, out OrganComponent? organ))
+ if (TryComp(insertedUid, out OrganComponent? organ))
{
- AddOrgan(entity, uid, uid, organ);
+ AddOrgan((insertedUid, organ), ent, ent);
}
}
- private void OnBodyRemoved(EntityUid uid, BodyComponent component, EntRemovedFromContainerMessage args)
+ private void OnBodyRemoved(Entity<BodyComponent> ent, ref EntRemovedFromContainerMessage args)
{
// Root body part?
var slotId = args.Container.ID;
if (slotId != BodyRootContainerId)
return;
- var entity = args.Entity;
- DebugTools.Assert(!TryComp(entity, out BodyPartComponent? b) || b.Body == uid);
- DebugTools.Assert(!TryComp(entity, out OrganComponent? o) || o.Body == uid);
+ var removedUid = args.Entity;
+ DebugTools.Assert(!TryComp(removedUid, out BodyPartComponent? b) || b.Body == ent);
+ DebugTools.Assert(!TryComp(removedUid, out OrganComponent? o) || o.Body == ent);
- if (TryComp(entity, out BodyPartComponent? childPart))
+ if (TryComp(removedUid, out BodyPartComponent? part))
{
- RemovePart(uid, entity, slotId, childPart);
- RecursiveBodyUpdate(entity, null, childPart);
+ RemovePart((ent, ent), (removedUid, part), slotId);
+ RecursiveBodyUpdate((removedUid, part), null);
}
- if (TryComp(entity, out OrganComponent? organ))
- RemoveOrgan(entity, uid, organ);
+ if (TryComp(removedUid, out OrganComponent? organ))
+ RemoveOrgan((removedUid, organ), ent);
}
- private void OnBodyInit(EntityUid bodyId, BodyComponent body, ComponentInit args)
+ private void OnBodyInit(Entity<BodyComponent> ent, ref ComponentInit args)
{
// Setup the initial container.
- body.RootContainer = Containers.EnsureContainer<ContainerSlot>(bodyId, BodyRootContainerId);
+ ent.Comp.RootContainer = Containers.EnsureContainer<ContainerSlot>(ent, BodyRootContainerId);
}
- private void OnBodyMapInit(EntityUid bodyId, BodyComponent body, MapInitEvent args)
+ private void OnBodyMapInit(Entity<BodyComponent> ent, ref MapInitEvent args)
{
- if (body.Prototype == null)
+ if (ent.Comp.Prototype is null)
return;
// One-time setup
// Obviously can't run in Init to avoid double-spawns on save / load.
- var prototype = Prototypes.Index(body.Prototype.Value);
- MapInitBody(bodyId, prototype);
+ var prototype = Prototypes.Index(ent.Comp.Prototype.Value);
+ MapInitBody(ent, prototype);
}
private void MapInitBody(EntityUid bodyEntity, BodyPrototype prototype)
{
var protoRoot = prototype.Slots[prototype.Root];
- if (protoRoot.Part == null)
+ if (protoRoot.Part is null)
return;
// This should already handle adding the entity to the root.
- var rootPartEntity = SpawnInContainerOrDrop(protoRoot.Part, bodyEntity, BodyRootContainerId);
- var rootPart = Comp<BodyPartComponent>(rootPartEntity);
+ var rootPartUid = SpawnInContainerOrDrop(protoRoot.Part, bodyEntity, BodyRootContainerId);
+ var rootPart = Comp<BodyPartComponent>(rootPartUid);
rootPart.Body = bodyEntity;
- Dirty(rootPartEntity, rootPart);
+ Dirty(rootPartUid, rootPart);
// Setup the rest of the body entities.
- SetupOrgans(rootPartEntity, rootPart, protoRoot.Organs);
- MapInitParts(rootPartEntity, prototype);
+ SetupOrgans((rootPartUid, rootPart), protoRoot.Organs);
+ MapInitParts(rootPartUid, prototype);
}
- private void OnBodyCanDrag(EntityUid uid, BodyComponent component, ref CanDragEvent args)
+ private void OnBodyCanDrag(Entity<BodyComponent> ent, ref CanDragEvent args)
{
args.Handled = true;
}
var partSlot = CreatePartSlot(parentEntity, connection, childPartComponent.PartType, parentPartComponent);
var cont = Containers.GetContainer(parentEntity, GetPartSlotContainerId(connection));
- if (partSlot == null || !Containers.Insert(childPart, cont))
+ if (partSlot is null || !Containers.Insert(childPart, cont))
{
Log.Error($"Could not create slot for connection {connection} in body {prototype.ID}");
QueueDel(childPart);
}
// Add organs
- SetupOrgans(childPart, childPartComponent, connectionSlot.Organs);
+ SetupOrgans((childPart, childPartComponent), connectionSlot.Organs);
// Enqueue it so we can also get its neighbors.
frontier.Enqueue(connection);
}
}
- private void SetupOrgans(EntityUid partId, BodyPartComponent partComponent, Dictionary<string, string> organs)
+ private void SetupOrgans(Entity<BodyPartComponent> ent, Dictionary<string, string> organs)
{
foreach (var (organSlotId, organProto) in organs)
{
- var slot = CreateOrganSlot(organSlotId, partId, partComponent);
- SpawnInContainerOrDrop(organProto, partId, GetOrganContainerId(organSlotId));
+ var slot = CreateOrganSlot((ent, ent), organSlotId);
+ SpawnInContainerOrDrop(organProto, ent, GetOrganContainerId(organSlotId));
- if (slot == null)
+ if (slot is null)
{
- Log.Error($"Could not create organ for slot {organSlotId} in {ToPrettyString(partId)}");
+ Log.Error($"Could not create organ for slot {organSlotId} in {ToPrettyString(ent)}");
}
}
}
/// <summary>
/// Gets all body containers on this entity including the root one.
/// </summary>
- public IEnumerable<BaseContainer> GetBodyContainers(EntityUid id, BodyComponent? body = null,
+ public IEnumerable<BaseContainer> GetBodyContainers(
+ EntityUid id,
+ BodyComponent? body = null,
BodyPartComponent? rootPart = null)
{
- if (!Resolve(id, ref body, false) ||
- body.RootContainer.ContainedEntity == null ||
- !Resolve(body.RootContainer.ContainedEntity.Value, ref rootPart))
+ if (!Resolve(id, ref body, logMissing: false)
+ || body.RootContainer.ContainedEntity is null
+ || !Resolve(body.RootContainer.ContainedEntity.Value, ref rootPart))
{
yield break;
}
/// <summary>
/// Gets all child body parts of this entity, including the root entity.
/// </summary>
- public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyChildren(EntityUid? id, BodyComponent? body = null,
+ public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyChildren(
+ EntityUid? id,
+ BodyComponent? body = null,
BodyPartComponent? rootPart = null)
{
- if (id == null ||
- !Resolve(id.Value, ref body, false) ||
- body.RootContainer.ContainedEntity == null ||
- !Resolve(body.RootContainer.ContainedEntity.Value, ref rootPart))
+ if (id is null
+ || !Resolve(id.Value, ref body, logMissing: false)
+ || body.RootContainer.ContainedEntity is null
+ || !Resolve(body.RootContainer.ContainedEntity.Value, ref rootPart))
{
yield break;
}
}
}
- public IEnumerable<(EntityUid Id, OrganComponent Component)> GetBodyOrgans(EntityUid? bodyId, BodyComponent? body = null)
+ public IEnumerable<(EntityUid Id, OrganComponent Component)> GetBodyOrgans(
+ EntityUid? bodyId,
+ BodyComponent? body = null)
{
- if (bodyId == null || !Resolve(bodyId.Value, ref body, false))
+ if (bodyId is null || !Resolve(bodyId.Value, ref body, logMissing: false))
yield break;
foreach (var part in GetBodyChildren(bodyId, body))
/// <param name="bodyId"></param>
/// <param name="body"></param>
/// <returns></returns>
- public IEnumerable<BodyPartSlot> GetBodyAllSlots(EntityUid bodyId, BodyComponent? body = null)
+ public IEnumerable<BodyPartSlot> GetBodyAllSlots(
+ EntityUid bodyId,
+ BodyComponent? body = null)
{
- if (!Resolve(bodyId, ref body, false) || body.RootContainer.ContainedEntity == null)
+ if (!Resolve(bodyId, ref body, logMissing: false)
+ || body.RootContainer.ContainedEntity is null)
+ {
yield break;
+ }
foreach (var slot in GetAllBodyPartSlots(body.RootContainer.ContainedEntity.Value))
{
Vector2? splatDirection = null,
float splatModifier = 1,
Angle splatCone = default,
- SoundSpecifier? gibSoundOverride = null
- )
+ SoundSpecifier? gibSoundOverride = null)
{
var gibs = new HashSet<EntityUid>();
- if (!Resolve(bodyId, ref body, false))
+ if (!Resolve(bodyId, ref body, logMissing: false))
return gibs;
var root = GetRootPartOrNull(bodyId, body);
launchImpulseVariance:GibletLaunchImpulseVariance, launchCone: splatCone);
}
}
- if(TryComp<InventoryComponent>(bodyId, out var inventory))
+ if (TryComp<InventoryComponent>(bodyId, out var inventory))
{
foreach (var item in _inventory.GetHandOrInventoryEntities(bodyId))
{
-using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.CodeAnalysis;
using Content.Shared.Body.Components;
using Content.Shared.Body.Events;
using Content.Shared.Body.Organ;
public partial class SharedBodySystem
{
- private void AddOrgan(EntityUid uid, EntityUid bodyUid, EntityUid parentPartUid, OrganComponent component)
+ private void AddOrgan(
+ Entity<OrganComponent> organEnt,
+ EntityUid bodyUid,
+ EntityUid parentPartUid)
{
- component.Body = bodyUid;
- RaiseLocalEvent(uid, new AddedToPartEvent(parentPartUid));
+ organEnt.Comp.Body = bodyUid;
+ var addedEv = new OrganAddedEvent(parentPartUid);
+ RaiseLocalEvent(organEnt, ref addedEv);
- if (component.Body != null)
- RaiseLocalEvent(uid, new AddedToPartInBodyEvent(component.Body.Value, parentPartUid));
+ if (organEnt.Comp.Body is not null)
+ {
+ var addedInBodyEv = new OrganAddedToBodyEvent(bodyUid, parentPartUid);
+ RaiseLocalEvent(organEnt, ref addedInBodyEv);
+ }
- Dirty(uid, component);
+ Dirty(organEnt, organEnt.Comp);
}
- private void RemoveOrgan(EntityUid uid, EntityUid parentPartUid, OrganComponent component)
+ private void RemoveOrgan(Entity<OrganComponent> organEnt, EntityUid parentPartUid)
{
- RaiseLocalEvent(uid, new RemovedFromPartEvent(parentPartUid));
+ var removedEv = new OrganRemovedEvent(parentPartUid);
+ RaiseLocalEvent(organEnt, ref removedEv);
- if (component.Body != null)
+ if (organEnt.Comp.Body is { Valid: true } bodyUid)
{
- RaiseLocalEvent(uid, new RemovedFromPartInBodyEvent(component.Body.Value, parentPartUid));
+ var removedInBodyEv = new OrganRemovedFromBodyEvent(bodyUid, parentPartUid);
+ RaiseLocalEvent(organEnt, ref removedInBodyEv);
}
- component.Body = null;
- Dirty(uid, component);
+ organEnt.Comp.Body = null;
+ Dirty(organEnt, organEnt.Comp);
}
/// <summary>
/// Creates the specified organ slot on the parent entity.
/// </summary>
- private OrganSlot? CreateOrganSlot(string slotId, EntityUid parent, BodyPartComponent? part = null)
+ private OrganSlot? CreateOrganSlot(Entity<BodyPartComponent?> parentEnt, string slotId)
{
- if (!Resolve(parent, ref part, false))
+ if (!Resolve(parentEnt, ref parentEnt.Comp, logMissing: false))
return null;
- Containers.EnsureContainer<ContainerSlot>(parent, GetOrganContainerId(slotId));
+ Containers.EnsureContainer<ContainerSlot>(parentEnt, GetOrganContainerId(slotId));
var slot = new OrganSlot(slotId);
- part.Organs.Add(slotId, slot);
+ parentEnt.Comp.Organs.Add(slotId, slot);
return slot;
}
{
slot = null;
- if (parent == null || !Resolve(parent.Value, ref part, false))
+ if (parent is null || !Resolve(parent.Value, ref part, logMissing: false))
{
return false;
}
Containers.EnsureContainer<ContainerSlot>(parent.Value, GetOrganContainerId(slotId));
slot = new OrganSlot(slotId);
- return part.Organs.TryAdd(slotId,slot.Value);
+ return part.Organs.TryAdd(slotId, slot.Value);
}
/// <summary>
/// Returns whether the slotId exists on the partId.
/// </summary>
- public bool CanInsertOrgan(EntityUid partId, string slotId, BodyPartComponent? part = null)
+ public bool CanInsertOrgan(
+ EntityUid partId,
+ string slotId,
+ BodyPartComponent? part = null)
{
return Resolve(partId, ref part) && part.Organs.ContainsKey(slotId);
}
/// <summary>
/// Returns whether the specified organ slot exists on the partId.
/// </summary>
- public bool CanInsertOrgan(EntityUid partId, OrganSlot slot, BodyPartComponent? part = null)
+ public bool CanInsertOrgan(
+ EntityUid partId,
+ OrganSlot slot,
+ BodyPartComponent? part = null)
{
return CanInsertOrgan(partId, slot.Id, part);
}
- public bool InsertOrgan(EntityUid partId, EntityUid organId, string slotId, BodyPartComponent? part = null, OrganComponent? organ = null)
+ public bool InsertOrgan(
+ EntityUid partId,
+ EntityUid organId,
+ string slotId,
+ BodyPartComponent? part = null,
+ OrganComponent? organ = null)
{
- if (!Resolve(organId, ref organ, false) ||
- !Resolve(partId, ref part, false) ||
- !CanInsertOrgan(partId, slotId, part))
+ if (!Resolve(organId, ref organ, logMissing: false)
+ || !Resolve(partId, ref part, logMissing: false)
+ || !CanInsertOrgan(partId, slotId, part))
{
return false;
}
var containerId = GetOrganContainerId(slotId);
- if (!Containers.TryGetContainer(partId, containerId, out var container))
- return false;
-
- return Containers.Insert(organId, container);
+ return Containers.TryGetContainer(partId, containerId, out var container)
+ && Containers.Insert(organId, container);
}
/// <summary>
var parent = container.Owner;
- if (!HasComp<BodyPartComponent>(parent))
- return false;
-
- return Containers.Remove(organId, container);
+ return HasComp<BodyPartComponent>(parent)
+ && Containers.Remove(organId, container);
}
/// <summary>
BodyPartComponent? part = null,
OrganComponent? organ = null)
{
- if (!Resolve(partId, ref part, false) ||
- !Resolve(organId, ref organ, false))
+ if (!Resolve(partId, ref part, logMissing: false)
+ || !Resolve(organId, ref organ, logMissing: false))
{
return false;
}
-using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Content.Shared.Body.Components;
using Content.Shared.Body.Events;
SubscribeLocalEvent<BodyPartComponent, EntRemovedFromContainerMessage>(OnBodyPartRemoved);
}
- private void OnBodyPartInserted(EntityUid uid, BodyPartComponent component, EntInsertedIntoContainerMessage args)
+ private void OnBodyPartInserted(Entity<BodyPartComponent> ent, ref EntInsertedIntoContainerMessage args)
{
// Body part inserted into another body part.
- var entity = args.Entity;
+ var insertedUid = args.Entity;
var slotId = args.Container.ID;
- if (component.Body == null)
+ if (ent.Comp.Body is null)
return;
- if (TryComp(entity, out BodyPartComponent? childPart))
+ if (TryComp(insertedUid, out BodyPartComponent? part))
{
- AddPart(component.Body.Value, entity, slotId, childPart);
- RecursiveBodyUpdate(entity, component.Body.Value, childPart);
+ AddPart(ent.Comp.Body.Value, (insertedUid, part), slotId);
+ RecursiveBodyUpdate((insertedUid, part), ent.Comp.Body.Value);
}
- if (TryComp(entity, out OrganComponent? organ))
- AddOrgan(entity, component.Body.Value, uid, organ);
+ if (TryComp(insertedUid, out OrganComponent? organ))
+ AddOrgan((insertedUid, organ), ent.Comp.Body.Value, ent);
}
- private void OnBodyPartRemoved(EntityUid uid, BodyPartComponent component, EntRemovedFromContainerMessage args)
+ private void OnBodyPartRemoved(Entity<BodyPartComponent> ent, ref EntRemovedFromContainerMessage args)
{
// Body part removed from another body part.
- var entity = args.Entity;
+ var removedUid = args.Entity;
var slotId = args.Container.ID;
- DebugTools.Assert(!TryComp(entity, out BodyPartComponent? b) || b.Body == component.Body);
- DebugTools.Assert(!TryComp(entity, out OrganComponent? o) || o.Body == component.Body);
+ DebugTools.Assert(!TryComp(removedUid, out BodyPartComponent? b) || b.Body == ent.Comp.Body);
+ DebugTools.Assert(!TryComp(removedUid, out OrganComponent? o) || o.Body == ent.Comp.Body);
- if (TryComp(entity, out BodyPartComponent? childPart) && childPart.Body != null)
+ if (TryComp(removedUid, out BodyPartComponent? part) && part.Body is not null)
{
- RemovePart(childPart.Body.Value, entity, slotId, childPart);
- RecursiveBodyUpdate(entity, null, childPart);
+ RemovePart(part.Body.Value, (removedUid, part), slotId);
+ RecursiveBodyUpdate((removedUid, part), null);
}
- if (TryComp(entity, out OrganComponent? organ))
- RemoveOrgan(entity, uid, organ);
+ if (TryComp(removedUid, out OrganComponent? organ))
+ RemoveOrgan((removedUid, organ), ent);
}
- private void RecursiveBodyUpdate(EntityUid uid, EntityUid? bodyUid, BodyPartComponent component)
+ private void RecursiveBodyUpdate(Entity<BodyPartComponent> ent, EntityUid? bodyUid)
{
- component.Body = bodyUid;
- Dirty(uid, component);
+ ent.Comp.Body = bodyUid;
+ Dirty(ent, ent.Comp);
- foreach (var slotId in component.Organs.Keys)
+ foreach (var slotId in ent.Comp.Organs.Keys)
{
- if (!Containers.TryGetContainer(uid, GetOrganContainerId(slotId), out var container))
+ if (!Containers.TryGetContainer(ent, GetOrganContainerId(slotId), out var container))
continue;
foreach (var organ in container.ContainedEntities)
Dirty(organ, organComp);
- if (organComp.Body != null)
- RaiseLocalEvent(organ, new RemovedFromPartInBodyEvent(organComp.Body.Value, uid));
+ if (organComp.Body is { Valid: true } oldBodyUid)
+ {
+ var removedEv = new OrganRemovedFromBodyEvent(oldBodyUid, ent);
+ RaiseLocalEvent(organ, ref removedEv);
+ }
organComp.Body = bodyUid;
- if (bodyUid != null)
- RaiseLocalEvent(organ, new AddedToPartInBodyEvent(bodyUid.Value, uid));
+ if (bodyUid is not null)
+ {
+ var addedEv = new OrganAddedToBodyEvent(bodyUid.Value, ent);
+ RaiseLocalEvent(organ, ref addedEv);
+ }
}
}
- foreach (var slotId in component.Children.Keys)
+ foreach (var slotId in ent.Comp.Children.Keys)
{
- if (!Containers.TryGetContainer(uid, GetPartSlotContainerId(slotId), out var container))
+ if (!Containers.TryGetContainer(ent, GetPartSlotContainerId(slotId), out var container))
continue;
- foreach (var containedEnt in container.ContainedEntities)
+ foreach (var containedUid in container.ContainedEntities)
{
- if (TryComp(containedEnt, out BodyPartComponent? childPart))
- RecursiveBodyUpdate(containedEnt, bodyUid, childPart);
+ if (TryComp(containedUid, out BodyPartComponent? childPart))
+ RecursiveBodyUpdate((containedUid, childPart), bodyUid);
}
}
}
protected virtual void AddPart(
- EntityUid bodyUid,
- EntityUid partUid,
- string slotId,
- BodyPartComponent component,
- BodyComponent? bodyComp = null)
+ Entity<BodyComponent?> bodyEnt,
+ Entity<BodyPartComponent> partEnt,
+ string slotId)
{
- DebugTools.AssertOwner(partUid, component);
- Dirty(partUid, component);
- component.Body = bodyUid;
+ Dirty(partEnt, partEnt.Comp);
+ partEnt.Comp.Body = bodyEnt;
- var ev = new BodyPartAddedEvent(slotId, component);
- RaiseLocalEvent(bodyUid, ref ev);
+ var ev = new BodyPartAddedEvent(slotId, partEnt);
+ RaiseLocalEvent(bodyEnt, ref ev);
- AddLeg(partUid, bodyUid, component, bodyComp);
+ AddLeg(partEnt, bodyEnt);
}
protected virtual void RemovePart(
- EntityUid bodyUid,
- EntityUid partUid,
- string slotId,
- BodyPartComponent component,
- BodyComponent? bodyComp = null)
+ Entity<BodyComponent?> bodyEnt,
+ Entity<BodyPartComponent> partEnt,
+ string slotId)
{
- DebugTools.AssertOwner(partUid, component);
- Resolve(bodyUid, ref bodyComp, false);
- Dirty(partUid, component);
- component.Body = null;
+ Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false);
+ Dirty(partEnt, partEnt.Comp);
+ partEnt.Comp.Body = null;
- var ev = new BodyPartRemovedEvent(slotId, component);
- RaiseLocalEvent(bodyUid, ref ev);
+ var ev = new BodyPartRemovedEvent(slotId, partEnt);
+ RaiseLocalEvent(bodyEnt, ref ev);
- RemoveLeg(partUid, bodyUid, component);
- PartRemoveDamage(bodyUid, component, bodyComp);
+ RemoveLeg(partEnt, bodyEnt);
+ PartRemoveDamage(bodyEnt, partEnt);
}
- private void AddLeg(EntityUid uid, EntityUid bodyUid, BodyPartComponent component, BodyComponent? bodyComp = null)
+ private void AddLeg(Entity<BodyPartComponent> legEnt, Entity<BodyComponent?> bodyEnt)
{
- if (!Resolve(bodyUid, ref bodyComp, false))
+ if (!Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false))
return;
- if (component.PartType == BodyPartType.Leg)
+ if (legEnt.Comp.PartType == BodyPartType.Leg)
{
- bodyComp.LegEntities.Add(uid);
- UpdateMovementSpeed(bodyUid);
- Dirty(bodyUid, bodyComp);
+ bodyEnt.Comp.LegEntities.Add(legEnt);
+ UpdateMovementSpeed(bodyEnt);
+ Dirty(bodyEnt, bodyEnt.Comp);
}
}
- private void RemoveLeg(EntityUid uid, EntityUid bodyUid, BodyPartComponent component, BodyComponent? bodyComp = null)
+ private void RemoveLeg(Entity<BodyPartComponent> legEnt, Entity<BodyComponent?> bodyEnt)
{
- if (!Resolve(bodyUid, ref bodyComp, false))
+ if (!Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false))
return;
- if (component.PartType == BodyPartType.Leg)
+ if (legEnt.Comp.PartType == BodyPartType.Leg)
{
- bodyComp.LegEntities.Remove(uid);
- UpdateMovementSpeed(bodyUid);
- Dirty(bodyUid, bodyComp);
+ bodyEnt.Comp.LegEntities.Remove(legEnt);
+ UpdateMovementSpeed(bodyEnt);
+ Dirty(bodyEnt, bodyEnt.Comp);
- if (!bodyComp.LegEntities.Any())
+ if (!bodyEnt.Comp.LegEntities.Any())
{
- Standing.Down(bodyUid);
+ Standing.Down(bodyEnt);
}
}
}
- private void PartRemoveDamage(EntityUid parent, BodyPartComponent component, BodyComponent? bodyComp = null)
+ private void PartRemoveDamage(Entity<BodyComponent?> bodyEnt, Entity<BodyPartComponent> partEnt)
{
- if (!Resolve(parent, ref bodyComp, false))
+ if (!Resolve(bodyEnt, ref bodyEnt.Comp, logMissing: false))
return;
- if (!_timing.ApplyingState && component.IsVital && !GetBodyChildrenOfType(parent, component.PartType, bodyComp).Any())
+ if (!_timing.ApplyingState
+ && partEnt.Comp.IsVital
+ && !GetBodyChildrenOfType(bodyEnt, partEnt.Comp.PartType, bodyEnt.Comp).Any()
+ )
{
// TODO BODY SYSTEM KILL : remove this when wounding and required parts are implemented properly
var damage = new DamageSpecifier(Prototypes.Index<DamageTypePrototype>("Bloodloss"), 300);
- Damageable.TryChangeDamage(parent, damage);
+ Damageable.TryChangeDamage(bodyEnt, damage);
}
}
var parent = container.Owner;
- if (!TryComp<BodyPartComponent>(parent, out var parentBody) || !parentBody.Children.ContainsKey(slotId))
+ if (!TryComp<BodyPartComponent>(parent, out var parentBody)
+ || !parentBody.Children.ContainsKey(slotId))
return null;
return (parent, slotId);
BodyPartType partType,
BodyPartComponent? part = null)
{
- if (!Resolve(partUid, ref part, false))
+ if (!Resolve(partUid, ref part, logMissing: false))
return null;
Containers.EnsureContainer<ContainerSlot>(partUid, GetPartSlotContainerId(slotId));
{
slot = null;
- if (partId == null ||
- !Resolve(partId.Value, ref part, false))
+ if (partId is null
+ || !Resolve(partId.Value, ref part, logMissing: false))
{
return false;
}
/// <summary>
/// Returns true if the partId is the root body container for the specified bodyId.
/// </summary>
- public bool IsPartRoot(EntityUid bodyId, EntityUid partId, BodyComponent? body = null, BodyPartComponent? part = null)
+ public bool IsPartRoot(
+ EntityUid bodyId,
+ EntityUid partId,
+ BodyComponent? body = null,
+ BodyPartComponent? part = null)
{
- if (!Resolve(partId, ref part)|| !Resolve(bodyId, ref body))
- return false;
-
- return Containers.TryGetContainingContainer(bodyId, partId, out var container) && container.ID == BodyRootContainerId;
+ return Resolve(partId, ref part)
+ && Resolve(bodyId, ref body)
+ && Containers.TryGetContainingContainer(bodyId, partId, out var container)
+ && container.ID == BodyRootContainerId;
}
/// <summary>
/// Returns true if we can attach the partId to the bodyId as the root entity.
/// </summary>
- public bool CanAttachToRoot(EntityUid bodyId, EntityUid partId, BodyComponent? body = null,
+ public bool CanAttachToRoot(
+ EntityUid bodyId,
+ EntityUid partId,
+ BodyComponent? body = null,
BodyPartComponent? part = null)
{
- return Resolve(bodyId, ref body) &&
- Resolve(partId, ref part) &&
- body.RootContainer.ContainedEntity == null &&
- bodyId != part.Body;
+ return Resolve(bodyId, ref body)
+ && Resolve(partId, ref part)
+ && body.RootContainer.ContainedEntity is null
+ && bodyId != part.Body;
}
/// <summary>
/// </summary>
public (EntityUid Entity, BodyPartComponent BodyPart)? GetRootPartOrNull(EntityUid bodyId, BodyComponent? body = null)
{
- if (!Resolve(bodyId, ref body) || body.RootContainer.ContainedEntity == null)
+ if (!Resolve(bodyId, ref body)
+ || body.RootContainer.ContainedEntity is null)
+ {
return null;
+ }
return (body.RootContainer.ContainedEntity.Value,
Comp<BodyPartComponent>(body.RootContainer.ContainedEntity.Value));
BodyPartComponent? parentPart = null,
BodyPartComponent? part = null)
{
- if (!Resolve(partId, ref part, false) ||
- !Resolve(parentId, ref parentPart, false))
- {
- return false;
- }
-
- return CanAttachPart(parentId, slot.Id, partId, parentPart, part);
+ return Resolve(partId, ref part, logMissing: false)
+ && Resolve(parentId, ref parentPart, logMissing: false)
+ && CanAttachPart(parentId, slot.Id, partId, parentPart, part);
}
/// <summary>
BodyPartComponent? parentPart = null,
BodyPartComponent? part = null)
{
- if (!Resolve(partId, ref part, false) ||
- !Resolve(parentId, ref parentPart, false) ||
- !parentPart.Children.TryGetValue(slotId, out var parentSlotData))
- {
- return false;
- }
-
- return part.PartType == parentSlotData.Type &&
- Containers.TryGetContainer(parentId, GetPartSlotContainerId(slotId), out var container) &&
- Containers.CanInsert(partId, container);
+ return Resolve(partId, ref part, logMissing: false)
+ && Resolve(parentId, ref parentPart, logMissing: false)
+ && parentPart.Children.TryGetValue(slotId, out var parentSlotData)
+ && part.PartType == parentSlotData.Type
+ && Containers.TryGetContainer(parentId, GetPartSlotContainerId(slotId), out var container)
+ && Containers.CanInsert(partId, container);
}
public bool AttachPartToRoot(
BodyComponent? body = null,
BodyPartComponent? part = null)
{
- if (!Resolve(bodyId, ref body) ||
- !Resolve(partId, ref part) ||
- !CanAttachToRoot(bodyId, partId, body, part))
- {
- return false;
- }
-
- return Containers.Insert(partId, body.RootContainer);
+ return Resolve(bodyId, ref body)
+ && Resolve(partId, ref part)
+ && CanAttachToRoot(bodyId, partId, body, part)
+ && Containers.Insert(partId, body.RootContainer);
}
#endregion
/// <summary>
/// Attaches a body part to the specified body part parent.
/// </summary>
- public bool AttachPart(
- EntityUid parentPartId,
- string slotId,
- EntityUid partId,
- BodyPartComponent? parentPart = null,
- BodyPartComponent? part = null)
- {
- if (!Resolve(parentPartId, ref parentPart, false) ||
- !parentPart.Children.TryGetValue(slotId, out var slot))
- {
- return false;
- }
-
- return AttachPart(parentPartId, slot, partId, parentPart, part);
+ public bool AttachPart(
+ EntityUid parentPartId,
+ string slotId,
+ EntityUid partId,
+ BodyPartComponent? parentPart = null,
+ BodyPartComponent? part = null)
+ {
+ return Resolve(parentPartId, ref parentPart, logMissing: false)
+ && parentPart.Children.TryGetValue(slotId, out var slot)
+ && AttachPart(parentPartId, slot, partId, parentPart, part);
}
/// <summary>
BodyPartComponent? parentPart = null,
BodyPartComponent? part = null)
{
- if (!Resolve(parentPartId, ref parentPart, false) ||
- !Resolve(partId, ref part, false) ||
- !CanAttachPart(parentPartId, slot.Id, partId, parentPart, part) ||
- !parentPart.Children.ContainsKey(slot.Id))
+ if (!Resolve(parentPartId, ref parentPart, logMissing: false)
+ || !Resolve(partId, ref part, logMissing: false)
+ || !CanAttachPart(parentPartId, slot.Id, partId, parentPart, part)
+ || !parentPart.Children.ContainsKey(slot.Id))
{
return false;
}
#region Misc
- public void UpdateMovementSpeed(EntityUid bodyId, BodyComponent? body = null, MovementSpeedModifierComponent? movement = null)
+ public void UpdateMovementSpeed(
+ EntityUid bodyId,
+ BodyComponent? body = null,
+ MovementSpeedModifierComponent? movement = null)
{
- if (!Resolve(bodyId, ref body, ref movement, false))
- return;
-
- if (body.RequiredLegs <= 0)
+ if (!Resolve(bodyId, ref body, ref movement, logMissing: false)
+ || body.RequiredLegs <= 0)
+ {
return;
+ }
var walkSpeed = 0f;
var sprintSpeed = 0f;
/// </summary>
public IEnumerable<(EntityUid Id, OrganComponent Component)> GetPartOrgans(EntityUid partId, BodyPartComponent? part = null)
{
- if (!Resolve(partId, ref part, false))
+ if (!Resolve(partId, ref part, logMissing: false))
yield break;
foreach (var slotId in part.Organs.Keys)
/// </summary>
public IEnumerable<BaseContainer> GetPartContainers(EntityUid id, BodyPartComponent? part = null)
{
- if (!Resolve(id, ref part, false) ||
+ if (!Resolve(id, ref part, logMissing: false) ||
part.Children.Count == 0)
{
yield break;
/// <summary>
/// Returns all body part components for this entity including itself.
/// </summary>
- public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyPartChildren(EntityUid partId, BodyPartComponent? part = null)
+ public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyPartChildren(
+ EntityUid partId,
+ BodyPartComponent? part = null)
{
- if (!Resolve(partId, ref part, false))
+ if (!Resolve(partId, ref part, logMissing: false))
yield break;
yield return (partId, part);
/// <summary>
/// Returns all body part slots for this entity.
/// </summary>
- public IEnumerable<BodyPartSlot> GetAllBodyPartSlots(EntityUid partId, BodyPartComponent? part = null)
+ public IEnumerable<BodyPartSlot> GetAllBodyPartSlots(
+ EntityUid partId,
+ BodyPartComponent? part = null)
{
- if (!Resolve(partId, ref part, false))
+ if (!Resolve(partId, ref part, logMissing: false))
yield break;
foreach (var (slotId, slot) in part.Children)
/// <summary>
/// Returns true if the bodyId has any parts of this type.
/// </summary>
- public bool BodyHasPartType(EntityUid bodyId, BodyPartType type, BodyComponent? body = null)
+ public bool BodyHasPartType(
+ EntityUid bodyId,
+ BodyPartType type,
+ BodyComponent? body = null)
{
return GetBodyChildrenOfType(bodyId, type, body).Any();
}
BodyPartComponent? parent,
BodyPartComponent? child)
{
- if (!Resolve(parentId, ref parent, false) ||
- !Resolve(childId, ref child, false))
+ if (!Resolve(parentId, ref parent, logMissing: false)
+ || !Resolve(childId, ref child, logMissing: false))
{
return false;
}
BodyComponent? body = null,
BodyPartComponent? part = null)
{
- if (!Resolve(bodyId, ref body, false) ||
- body.RootContainer.ContainedEntity == null ||
- !Resolve(partId, ref part, false) ||
- !TryComp(body.RootContainer.ContainedEntity, out BodyPartComponent? rootPart))
- {
- return false;
- }
-
- return PartHasChild(body.RootContainer.ContainedEntity.Value, partId, rootPart, part);
+ return Resolve(bodyId, ref body, logMissing: false)
+ && body.RootContainer.ContainedEntity is not null
+ && Resolve(partId, ref part, logMissing: false)
+ && TryComp(body.RootContainer.ContainedEntity, out BodyPartComponent? rootPart)
+ && PartHasChild(body.RootContainer.ContainedEntity.Value, partId, rootPart, part);
}
public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyChildrenOfType(
/// <summary>
/// Gets the parent body part and all immediate child body parts for the partId.
/// </summary>
- public IEnumerable<EntityUid> GetBodyPartAdjacentParts(EntityUid partId, BodyPartComponent? part = null)
+ public IEnumerable<EntityUid> GetBodyPartAdjacentParts(
+ EntityUid partId,
+ BodyPartComponent? part = null)
{
- if (!Resolve(partId, ref part, false))
+ if (!Resolve(partId, ref part, logMissing: false))
yield break;
if (TryGetParentBodyPart(partId, out var parentUid, out _))
BodyPartComponent? part = null)
where T : IComponent
{
- if (!Resolve(partId, ref part, false))
+ if (!Resolve(partId, ref part, logMissing: false))
yield break;
var query = GetEntityQuery<T>();
BodyPartComponent? part = null)
where T : IComponent
{
- if (!Resolve(partId, ref part, false))
+ if (!Resolve(partId, ref part, logMissing: false))
{
comps = null;
return false;