ECS and cleanup body system, merge body templates and presets into body prototypes (#11991)

Co-authored-by: Jezithyr <Jezithyr@gmail.com>
This commit is contained in:
DrSmugleaf
2022-10-23 00:46:28 +02:00
committed by GitHub
parent 9a38736c3c
commit f323fb7644
140 changed files with 2478 additions and 2571 deletions

View File

@@ -1,6 +1,9 @@
using System.Linq;
using Content.Server.Administration;
using Content.Server.Body.Systems;
using Content.Shared.Administration;
using Content.Shared.Body.Components;
using Content.Shared.Body.Part;
using Robust.Server.Player;
using Robust.Shared.Console;
using Robust.Shared.Prototypes;
@@ -118,7 +121,7 @@ namespace Content.Server.Body.Commands
}
}
if (!entityManager.TryGetComponent(entity, out SharedBodyComponent? body))
if (!entityManager.TryGetComponent(entity, out BodyComponent? body) || body.Root == null)
{
var random = IoCManager.Resolve<IRobustRandom>();
var text = $"You have no body{(random.Prob(0.2f) ? " and you must scream." : ".")}";
@@ -127,14 +130,25 @@ namespace Content.Server.Body.Commands
return;
}
if (!entityManager.TryGetComponent(hand, out SharedBodyPartComponent? part))
if (!entityManager.TryGetComponent(hand, out BodyPartComponent? part))
{
shell.WriteLine($"Hand entity {hand} does not have a {nameof(SharedBodyPartComponent)} component.");
shell.WriteLine($"Hand entity {hand} does not have a {nameof(BodyPartComponent)} component.");
return;
}
var slot = part.GetHashCode().ToString();
body.SetPart(slot, part);
var bodySystem = entityManager.System<BodySystem>();
var attachAt = bodySystem.GetBodyChildrenOfType(entity, BodyPartType.Arm, body).FirstOrDefault();
if (attachAt == default)
attachAt = bodySystem.GetBodyChildren(entity, body).First();
var slotId = part.GetHashCode().ToString();
if (!bodySystem.TryCreatePartSlotAndAttach(attachAt.Id, slotId, hand, attachAt.Component, part))
{
shell.WriteError($"Couldn't create a slot with id {slotId} on entity {entityManager.ToPrettyString(entity)}");
return;
}
shell.WriteLine($"Added hand to entity {entityManager.GetComponent<MetaDataComponent>(entity).EntityName}");
}

View File

@@ -1,6 +1,9 @@
using System.Linq;
using Content.Server.Administration;
using Content.Server.Body.Systems;
using Content.Shared.Administration;
using Content.Shared.Body.Components;
using Content.Shared.Body.Part;
using Robust.Server.Player;
using Robust.Shared.Console;
@@ -18,7 +21,7 @@ namespace Content.Server.Body.Commands
var player = shell.Player as IPlayerSession;
var entityManager = IoCManager.Resolve<IEntityManager>();
EntityUid entity;
EntityUid bodyId;
EntityUid partUid;
switch (args.Length)
@@ -42,7 +45,7 @@ namespace Content.Server.Body.Commands
return;
}
entity = player.AttachedEntity.Value;
bodyId = player.AttachedEntity.Value;
break;
case 2:
@@ -64,16 +67,16 @@ namespace Content.Server.Body.Commands
return;
}
entity = entityUid;
bodyId = entityUid;
break;
default:
shell.WriteLine(Help);
return;
}
if (!entityManager.TryGetComponent(entity, out SharedBodyComponent? body))
if (!entityManager.TryGetComponent(bodyId, out BodyComponent? body))
{
shell.WriteLine($"Entity {entityManager.GetComponent<MetaDataComponent>(entity).EntityName} with uid {entity} does not have a {nameof(SharedBodyComponent)} component.");
shell.WriteLine($"Entity {entityManager.GetComponent<MetaDataComponent>(bodyId).EntityName} with uid {bodyId} does not have a {nameof(BodyComponent)}.");
return;
}
@@ -83,19 +86,39 @@ namespace Content.Server.Body.Commands
return;
}
if (!entityManager.TryGetComponent(partUid, out SharedBodyPartComponent? part))
if (!entityManager.TryGetComponent(partUid, out BodyPartComponent? part))
{
shell.WriteLine($"Entity {entityManager.GetComponent<MetaDataComponent>(partUid).EntityName} with uid {args[0]} does not have a {nameof(SharedBodyPartComponent)} component.");
shell.WriteLine($"Entity {entityManager.GetComponent<MetaDataComponent>(partUid).EntityName} with uid {args[0]} does not have a {nameof(BodyPartComponent)}.");
return;
}
if (body.HasPart(part))
var bodySystem = entityManager.System<BodySystem>();
if (bodySystem.BodyHasChild(bodyId, partUid, body, part))
{
shell.WriteLine($"Body part {entityManager.GetComponent<MetaDataComponent>(partUid).EntityName} with uid {partUid} is already attached to entity {entityManager.GetComponent<MetaDataComponent>(entity).EntityName} with uid {entity}");
shell.WriteLine($"Body part {entityManager.GetComponent<MetaDataComponent>(partUid).EntityName} with uid {partUid} is already attached to entity {entityManager.GetComponent<MetaDataComponent>(bodyId).EntityName} with uid {bodyId}");
return;
}
body.SetPart($"AttachBodyPartVerb-{partUid}", part);
var slotId = $"AttachBodyPartVerb-{partUid}";
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
if (bodySystem.TryCreateBodyRootSlot(bodyId, slotId, out var rootSlot, body))
{
bodySystem.DropPart(partUid, part);
bodySystem.AttachPart(partUid, rootSlot, part);
}
else
{
var attachAt = bodySystem.GetBodyChildren(bodyId, body).First();
if (!bodySystem.TryCreatePartSlotAndAttach(attachAt.Id, slotId, partUid, attachAt.Component, part))
{
shell.WriteError($"Could not create slot {slotId} on entity {entityManager.ToPrettyString(bodyId)}");
return;
}
}
shell.WriteLine($"Attached part {entityManager.ToPrettyString(partUid)} to {entityManager.ToPrettyString(bodyId)}");
}
}
}

View File

@@ -1,4 +1,5 @@
using Content.Server.Administration;
using Content.Server.Body.Systems;
using Content.Shared.Administration;
using Content.Shared.Body.Components;
using Robust.Server.Player;
@@ -35,7 +36,8 @@ namespace Content.Server.Body.Commands
return;
}
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent(attached, out SharedBodyComponent? body))
var entityManager = IoCManager.Resolve<IEntityManager>();
if (!entityManager.TryGetComponent(attached, out BodyComponent? body))
{
var random = IoCManager.Resolve<IRobustRandom>();
var text = $"You have no body{(random.Prob(0.2f) ? " and you must scream." : ".")}";
@@ -45,13 +47,13 @@ namespace Content.Server.Body.Commands
}
var mechanismName = string.Join(" ", args).ToLowerInvariant();
var bodySystem = entityManager.System<BodySystem>();
foreach (var (part, _) in body.Parts)
foreach (var mechanism in part.Mechanisms)
foreach (var organ in bodySystem.GetBodyOrgans(body.Owner, body))
{
if (mechanism.Name.ToLowerInvariant() == mechanismName)
if (organ.Component.Name.ToLowerInvariant() == mechanismName)
{
part.DeleteMechanism(mechanism);
bodySystem.DeleteOrgan(organ.Id, organ.Component);
shell.WriteLine($"Mechanism with name {mechanismName} has been destroyed.");
return;
}

View File

@@ -1,5 +1,6 @@
using System.Linq;
using Content.Server.Administration;
using Content.Server.Body.Systems;
using Content.Shared.Administration;
using Content.Shared.Body.Components;
using Content.Shared.Body.Part;
@@ -31,7 +32,8 @@ namespace Content.Server.Body.Commands
return;
}
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent(player.AttachedEntity, out SharedBodyComponent? body))
var entityManager = IoCManager.Resolve<IEntityManager>();
if (!entityManager.TryGetComponent(player.AttachedEntity, out BodyComponent? body))
{
var random = IoCManager.Resolve<IRobustRandom>();
var text = $"You have no body{(random.Prob(0.2f) ? " and you must scream." : ".")}";
@@ -40,15 +42,16 @@ namespace Content.Server.Body.Commands
return;
}
var hand = body.GetPartsOfType(BodyPartType.Hand).FirstOrDefault();
var bodySystem = entityManager.System<BodySystem>();
var hand = bodySystem.GetBodyChildrenOfType(player.AttachedEntity, BodyPartType.Hand, body).FirstOrDefault();
if (hand == null)
if (hand == default)
{
shell.WriteLine("You have no hands.");
}
else
{
body.RemovePart(hand);
bodySystem.DropPart(hand.Id, hand.Component);
}
}
}

View File

@@ -0,0 +1,11 @@
namespace Content.Server.Body.Components;
public sealed class BeingGibbedEvent : EntityEventArgs
{
public readonly HashSet<EntityUid> GibbedParts;
public BeingGibbedEvent(HashSet<EntityUid> gibbedParts)
{
GibbedParts = gibbedParts;
}
}

View File

@@ -1,162 +0,0 @@
using Content.Server.Humanoid;
using Content.Shared.Audio;
using Content.Shared.Body.Components;
using Content.Shared.Body.Part;
using Content.Shared.Humanoid;
using Content.Shared.Random.Helpers;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.Player;
namespace Content.Server.Body.Components
{
[RegisterComponent]
[ComponentReference(typeof(SharedBodyComponent))]
public sealed class BodyComponent : SharedBodyComponent
{
[Dependency] private readonly IEntityManager _entMan = default!;
private Container _partContainer = default!;
[DataField("gibSound")] private SoundSpecifier _gibSound = new SoundCollectionSpecifier("gib");
protected override bool CanAddPart(string slotId, SharedBodyPartComponent part)
{
return base.CanAddPart(slotId, part) &&
_partContainer.CanInsert(part.Owner);
}
protected override void OnAddPart(BodyPartSlot slot, SharedBodyPartComponent part)
{
base.OnAddPart(slot, part);
_partContainer.Insert(part.Owner);
if (_entMan.TryGetComponent<HumanoidComponent>(Owner, out var humanoid))
{
var layer = part.ToHumanoidLayers();
if (layer != null)
{
var layers = HumanoidVisualLayersExtension.Sublayers(layer.Value);
_entMan.System<HumanoidSystem>().SetLayersVisibility(Owner, layers, true, true, humanoid);
}
}
}
protected override void OnRemovePart(BodyPartSlot slot, SharedBodyPartComponent part)
{
base.OnRemovePart(slot, part);
_partContainer.ForceRemove(part.Owner);
part.Owner.RandomOffset(0.25f);
if (_entMan.TryGetComponent<HumanoidComponent>(Owner, out var humanoid))
{
var layer = part.ToHumanoidLayers();
if (layer != null)
{
var layers = HumanoidVisualLayersExtension.Sublayers(layer.Value);
_entMan.System<HumanoidSystem>().SetLayersVisibility(Owner, layers, false, true, humanoid);
}
}
}
protected override void Initialize()
{
base.Initialize();
_partContainer = Owner.EnsureContainer<Container>($"{Name}-{nameof(BodyComponent)}");
var preset = Preset;
if (preset != null)
{
foreach (var slot in Slots)
{
// Using MapPosition instead of Coordinates here prevents
// a crash within the character preview menu in the lobby
var entity = _entMan.SpawnEntity(preset.PartIDs[slot.Id], _entMan.GetComponent<TransformComponent>(Owner).MapPosition);
if (!_entMan.TryGetComponent(entity, out SharedBodyPartComponent? part))
{
Logger.Error($"Entity {slot.Id} does not have a {nameof(SharedBodyPartComponent)} component.");
continue;
}
SetPart(slot.Id, part);
}
}
}
protected override void Startup()
{
base.Startup();
// This is ran in Startup as entities spawned in Initialize
// are not synced to the client since they are assumed to be
// identical on it
foreach (var (part, _) in Parts)
{
part.Dirty();
}
}
public override HashSet<EntityUid> Gib(bool gibParts = false)
{
var gibs = base.Gib(gibParts);
var xform = _entMan.GetComponent<TransformComponent>(Owner);
var coordinates = xform.Coordinates;
// These have already been forcefully removed from containers so run it here.
foreach (var part in gibs)
{
_entMan.EventBus.RaiseLocalEvent(part, new PartGibbedEvent(Owner, gibs), true);
}
SoundSystem.Play(_gibSound.GetSound(), Filter.Pvs(Owner, entityManager: _entMan), coordinates, AudioHelpers.WithVariation(0.025f));
if (_entMan.TryGetComponent(Owner, out ContainerManagerComponent? container))
{
foreach (var cont in container.GetAllContainers())
{
foreach (var ent in cont.ContainedEntities)
{
cont.ForceRemove(ent);
_entMan.GetComponent<TransformComponent>(ent).Coordinates = coordinates;
ent.RandomOffset(0.25f);
}
}
}
_entMan.EventBus.RaiseLocalEvent(Owner, new BeingGibbedEvent(gibs), false);
_entMan.QueueDeleteEntity(Owner);
return gibs;
}
}
public sealed class BeingGibbedEvent : EntityEventArgs
{
public readonly HashSet<EntityUid> GibbedParts;
public BeingGibbedEvent(HashSet<EntityUid> gibbedParts)
{
GibbedParts = gibbedParts;
}
}
/// <summary>
/// An event raised on all the parts of an entity when it's gibbed
/// </summary>
public sealed class PartGibbedEvent : EntityEventArgs
{
public EntityUid EntityToGib;
public readonly HashSet<EntityUid> GibbedParts;
public PartGibbedEvent(EntityUid entityToGib, HashSet<EntityUid> gibbedParts)
{
EntityToGib = entityToGib;
GibbedParts = gibbedParts;
}
}
}

View File

@@ -1,59 +0,0 @@
using Content.Shared.Body.Components;
using Content.Shared.Random.Helpers;
using Robust.Shared.Containers;
namespace Content.Server.Body.Components
{
[RegisterComponent]
[ComponentReference(typeof(SharedBodyPartComponent))]
public sealed class BodyPartComponent : SharedBodyPartComponent
{
[Dependency] private readonly IEntityManager _entMan = default!;
private Container _mechanismContainer = default!;
public override bool CanAddMechanism(MechanismComponent mechanism)
{
return base.CanAddMechanism(mechanism) &&
_mechanismContainer.CanInsert(mechanism.Owner);
}
protected override void OnAddMechanism(MechanismComponent mechanism)
{
base.OnAddMechanism(mechanism);
_mechanismContainer.Insert(mechanism.Owner);
}
protected override void OnRemoveMechanism(MechanismComponent mechanism)
{
base.OnRemoveMechanism(mechanism);
_mechanismContainer.Remove(mechanism.Owner);
mechanism.Owner.RandomOffset(0.25f);
}
public void MapInitialize()
{
base.Initialize();
_mechanismContainer = Owner.EnsureContainer<Container>(ContainerId);
// This is ran in Startup as entities spawned in Initialize
// are not synced to the client since they are assumed to be
// identical on it
foreach (var mechanismId in MechanismIds)
{
var entity = _entMan.SpawnEntity(mechanismId, _entMan.GetComponent<TransformComponent>(Owner).MapPosition);
if (!_entMan.TryGetComponent(entity, out MechanismComponent? mechanism))
{
Logger.Error($"Entity {mechanismId} does not have a {nameof(MechanismComponent)} component.");
continue;
}
TryAddMechanism(mechanism, true);
}
}
}
}

View File

@@ -26,7 +26,7 @@ namespace Content.Server.Body.Components
/// <summary>
/// Copy BodyTemplate and BodyPart data into a common data class that the client can read.
/// </summary>
private BodyScannerUIState InterfaceState(SharedBodyComponent body)
private BodyScannerUIState InterfaceState(BodyComponent body)
{
return new(body.Owner);
}

View File

@@ -1,119 +1,148 @@
using System.Diagnostics.CodeAnalysis;
using Content.Server.Body.Components;
using Content.Server.GameTicking;
using Content.Server.Humanoid;
using Content.Server.Kitchen.Components;
using Content.Server.Mind.Components;
using Content.Shared.Body.Components;
using Content.Shared.Body.Part;
using Content.Shared.Body.Systems;
using Content.Shared.Humanoid;
using Content.Shared.MobState.Components;
using Content.Shared.Movement.Events;
using Content.Shared.Random.Helpers;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.Player;
using Robust.Shared.Timing;
namespace Content.Server.Body.Systems
namespace Content.Server.Body.Systems;
public sealed class BodySystem : SharedBodySystem
{
public sealed class BodySystem : EntitySystem
[Dependency] private readonly GameTicker _ticker = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly HumanoidSystem _humanoidSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
public override void Initialize()
{
[Dependency] private readonly GameTicker _ticker = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
base.Initialize();
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<BodyComponent, MoveInputEvent>(OnRelayMoveInput);
SubscribeLocalEvent<BodyComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
SubscribeLocalEvent<BodyComponent, BeingMicrowavedEvent>(OnBeingMicrowaved);
SubscribeLocalEvent<BodyPartComponent, MapInitEvent>((_, c, _) => c.MapInitialize());
}
SubscribeLocalEvent<BodyComponent, MoveInputEvent>(OnRelayMoveInput);
SubscribeLocalEvent<BodyComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
SubscribeLocalEvent<BodyComponent, BeingMicrowavedEvent>(OnBeingMicrowaved);
}
private void OnRelayMoveInput(EntityUid uid, BodyComponent component, ref MoveInputEvent args)
private void OnRelayMoveInput(EntityUid uid, BodyComponent component, ref MoveInputEvent args)
{
if (EntityManager.TryGetComponent<MobStateComponent>(uid, out var mobState) &&
mobState.IsDead() &&
EntityManager.TryGetComponent<MindComponent>(uid, out var mind) &&
mind.HasMind)
{
if (EntityManager.TryGetComponent<MobStateComponent>(uid, out var mobState) &&
mobState.IsDead() &&
EntityManager.TryGetComponent<MindComponent>(uid, out var mind) &&
mind.HasMind)
if (!mind.Mind!.TimeOfDeath.HasValue)
{
if (!mind.Mind!.TimeOfDeath.HasValue)
{
mind.Mind.TimeOfDeath = _gameTiming.RealTime;
}
_ticker.OnGhostAttempt(mind.Mind!, true);
}
}
private void OnApplyMetabolicMultiplier(EntityUid uid, BodyComponent component, ApplyMetabolicMultiplierEvent args)
{
foreach (var (part, _) in component.Parts)
foreach (var mechanism in part.Mechanisms)
{
RaiseLocalEvent(mechanism.Owner, args, false);
}
}
private void OnBeingMicrowaved(EntityUid uid, BodyComponent component, BeingMicrowavedEvent args)
{
if (args.Handled)
return;
// Don't microwave animals, kids
Transform(uid).AttachToGridOrMap();
component.Gib();
args.Handled = true;
}
/// <summary>
/// Returns a list of ValueTuples of <see cref="T"/> and MechanismComponent on each mechanism
/// in the given body.
/// </summary>
/// <param name="uid">The entity to check for the component on.</param>
/// <param name="body">The body to check for mechanisms on.</param>
/// <typeparam name="T">The component to check for.</typeparam>
public List<(T Comp, MechanismComponent Mech)> GetComponentsOnMechanisms<T>(EntityUid uid,
SharedBodyComponent? body=null) where T : Component
{
if (!Resolve(uid, ref body))
return new();
var query = EntityManager.GetEntityQuery<T>();
var list = new List<(T Comp, MechanismComponent Mech)>(3);
foreach (var (part, _) in body.Parts)
foreach (var mechanism in part.Mechanisms)
{
if (query.TryGetComponent(mechanism.Owner, out var comp))
list.Add((comp, mechanism));
mind.Mind.TimeOfDeath = _gameTiming.RealTime;
}
return list;
}
/// <summary>
/// Tries to get a list of ValueTuples of <see cref="T"/> and MechanismComponent on each mechanism
/// in the given body.
/// </summary>
/// <param name="uid">The entity to check for the component on.</param>
/// <param name="comps">The list of components.</param>
/// <param name="body">The body to check for mechanisms on.</param>
/// <typeparam name="T">The component to check for.</typeparam>
/// <returns>Whether any were found.</returns>
public bool TryGetComponentsOnMechanisms<T>(EntityUid uid,
[NotNullWhen(true)] out List<(T Comp, MechanismComponent Mech)>? comps,
SharedBodyComponent? body=null) where T: Component
{
if (!Resolve(uid, ref body))
{
comps = null;
return false;
}
comps = GetComponentsOnMechanisms<T>(uid, body);
if (comps.Count == 0)
{
comps = null;
return false;
}
return true;
_ticker.OnGhostAttempt(mind.Mind!, true);
}
}
private void OnApplyMetabolicMultiplier(EntityUid uid, BodyComponent component,
ApplyMetabolicMultiplierEvent args)
{
foreach (var organ in GetBodyOrgans(uid, component))
{
RaiseLocalEvent(organ.Id, args);
}
}
private void OnBeingMicrowaved(EntityUid uid, BodyComponent component, BeingMicrowavedEvent args)
{
if (args.Handled)
return;
// Don't microwave animals, kids
Transform(uid).AttachToGridOrMap();
GibBody(uid, false, component);
args.Handled = true;
}
public override bool AttachPart(
EntityUid? partId,
BodyPartSlot slot,
[NotNullWhen(true)] BodyPartComponent? part = null)
{
if (!base.AttachPart(partId, slot, part))
return false;
if (part.Body is { } body &&
TryComp<HumanoidComponent>(body, out var humanoid))
{
var layer = part.ToHumanoidLayers();
if (layer != null)
{
var layers = HumanoidVisualLayersExtension.Sublayers(layer.Value);
_humanoidSystem.SetLayersVisibility(body, layers, true, true, humanoid);
}
}
return true;
}
public override bool DropPart(EntityUid? partId, BodyPartComponent? part = null)
{
var oldBody = CompOrNull<BodyPartComponent>(partId)?.Body;
if (!base.DropPart(partId, part))
return false;
if (oldBody == null || !TryComp<HumanoidComponent>(oldBody, out var humanoid))
return true;
var layer = part.ToHumanoidLayers();
if (layer == null)
return true;
var layers = HumanoidVisualLayersExtension.Sublayers(layer.Value);
_humanoidSystem.SetLayersVisibility(oldBody.Value, layers, false, true, humanoid);
return true;
}
public override HashSet<EntityUid> GibBody(EntityUid? bodyId, bool gibOrgans = false, BodyComponent? body = null)
{
if (bodyId == null || !Resolve(bodyId.Value, ref body, false))
return new HashSet<EntityUid>();
var gibs = base.GibBody(bodyId, gibOrgans, body);
var xform = Transform(bodyId.Value);
var coordinates = xform.Coordinates;
var filter = Filter.Pvs(bodyId.Value, entityManager: EntityManager);
var audio = AudioParams.Default.WithVariation(0.025f);
_audio.Play(body.GibSound, filter, coordinates, audio);
if (TryComp(bodyId, out ContainerManagerComponent? container))
{
foreach (var cont in container.GetAllContainers())
{
foreach (var ent in cont.ContainedEntities)
{
cont.ForceRemove(ent);
Transform(ent).Coordinates = coordinates;
ent.RandomOffset(0.25f);
}
}
}
RaiseLocalEvent(bodyId.Value, new BeingGibbedEvent(gibs));
QueueDel(bodyId.Value);
return gibs;
}
}

View File

@@ -3,6 +3,7 @@ using Content.Server.Ghost.Components;
using Content.Server.Mind.Components;
using Content.Shared.Body.Components;
using Content.Shared.Body.Events;
using Content.Shared.Body.Organ;
using Content.Shared.Movement.Components;
namespace Content.Server.Body.Systems
@@ -13,21 +14,22 @@ namespace Content.Server.Body.Systems
{
base.Initialize();
SubscribeLocalEvent<BrainComponent, AddedToBodyEvent>((uid, _, args) => HandleMind((args.Body).Owner, uid));
SubscribeLocalEvent<BrainComponent, AddedToPartEvent>((uid, _, args) => HandleMind((args.Part).Owner, uid));
SubscribeLocalEvent<BrainComponent, AddedToPartInBodyEvent>((uid, _, args) => HandleMind((args.Body).Owner, uid));
SubscribeLocalEvent<BrainComponent, AddedToBodyEvent>((uid, _, args) => HandleMind(args.Body, uid));
SubscribeLocalEvent<BrainComponent, AddedToPartEvent>((uid, _, args) => HandleMind(args.Part, uid));
SubscribeLocalEvent<BrainComponent, AddedToPartInBodyEvent>((uid, _, args) => HandleMind(args.Body, uid));
SubscribeLocalEvent<BrainComponent, RemovedFromBodyEvent>(OnRemovedFromBody);
SubscribeLocalEvent<BrainComponent, RemovedFromPartEvent>((uid, _, args) => HandleMind(uid, (args.Old).Owner));
SubscribeLocalEvent<BrainComponent, RemovedFromPartInBodyEvent>((uid, _, args) => HandleMind((args.OldBody).Owner, uid));
SubscribeLocalEvent<BrainComponent, RemovedFromPartEvent>((uid, _, args) => HandleMind(uid, args.Old));
SubscribeLocalEvent<BrainComponent, RemovedFromPartInBodyEvent>((uid, _, args) => HandleMind(args.OldBody, uid));
}
private void OnRemovedFromBody(EntityUid uid, BrainComponent component, RemovedFromBodyEvent args)
{
// This one needs to be special, okay?
if (!EntityManager.TryGetComponent(uid, out MechanismComponent? mech))
if (!EntityManager.TryGetComponent(uid, out OrganComponent? organ) ||
organ.ParentSlot is not {Parent: var parent})
return;
HandleMind((mech.Part!).Owner, (args.Old).Owner);
HandleMind(parent, args.Old);
}
private void HandleMind(EntityUid newEntity, EntityUid oldEntity)

View File

@@ -3,7 +3,7 @@ using Content.Server.Body.Components;
using Content.Server.Chemistry.Components.SolutionManager;
using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Administration.Logs;
using Content.Shared.Body.Components;
using Content.Shared.Body.Organ;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Database;
@@ -18,6 +18,7 @@ namespace Content.Server.Body.Systems
[UsedImplicitly]
public sealed class MetabolizerSystem : EntitySystem
{
[Dependency] private readonly BodySystem _bodySystem = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
@@ -37,30 +38,27 @@ namespace Content.Server.Body.Systems
{
_solutionContainerSystem.EnsureSolution(uid, component.SolutionName);
}
else
else if (CompOrNull<OrganComponent>(uid)?.Body is { } body)
{
if (EntityManager.TryGetComponent<MechanismComponent>(uid, out var mech))
{
if (mech.Body != null)
{
_solutionContainerSystem.EnsureSolution((mech.Body).Owner, component.SolutionName);
}
}
_solutionContainerSystem.EnsureSolution(body, component.SolutionName);
}
}
private void OnApplyMetabolicMultiplier(EntityUid uid, MetabolizerComponent component, ApplyMetabolicMultiplierEvent args)
private void OnApplyMetabolicMultiplier(EntityUid uid, MetabolizerComponent component,
ApplyMetabolicMultiplierEvent args)
{
if (args.Apply)
{
component.UpdateFrequency *= args.Multiplier;
return;
}
component.UpdateFrequency /= args.Multiplier;
// Reset the accumulator properly
if (component.AccumulatedFrametime >= component.UpdateFrequency)
component.AccumulatedFrametime = component.UpdateFrequency;
}
public override void Update(float frameTime)
{
base.Update(frameTime);
@@ -78,33 +76,27 @@ namespace Content.Server.Body.Systems
}
}
private void TryMetabolize(EntityUid uid, MetabolizerComponent? meta=null, MechanismComponent? mech=null)
private void TryMetabolize(EntityUid uid, MetabolizerComponent? meta = null, OrganComponent? organ = null)
{
if (!Resolve(uid, ref meta))
return;
Resolve(uid, ref mech, false);
Resolve(uid, ref organ, false);
// First step is get the solution we actually care about
Solution? solution = null;
EntityUid? solutionEntityUid = null;
EntityUid? bodyEntityUid = mech?.Body?.Owner;
SolutionContainerManagerComponent? manager = null;
if (meta.SolutionOnBody)
{
if (mech != null)
if (organ?.Body is { } body)
{
var body = mech.Body;
if (body != null)
{
if (!Resolve((body).Owner, ref manager, false))
return;
_solutionContainerSystem.TryGetSolution((body).Owner, meta.SolutionName, out solution, manager);
solutionEntityUid = body.Owner;
}
if (!Resolve(body, ref manager, false))
return;
_solutionContainerSystem.TryGetSolution(body, meta.SolutionName, out solution, manager);
solutionEntityUid = body;
}
}
else
@@ -133,7 +125,8 @@ namespace Content.Server.Body.Systems
if (proto.Metabolisms == null)
{
if (meta.RemoveEmpty)
_solutionContainerSystem.TryRemoveReagent(solutionEntityUid.Value, solution, reagent.ReagentId, FixedPoint2.New(1));
_solutionContainerSystem.TryRemoveReagent(solutionEntityUid.Value, solution, reagent.ReagentId,
FixedPoint2.New(1));
continue;
}
@@ -170,7 +163,7 @@ namespace Content.Server.Body.Systems
continue;
}
var actualEntity = bodyEntityUid != null ? bodyEntityUid.Value : solutionEntityUid.Value;
var actualEntity = organ?.Body ?? solutionEntityUid.Value;
var args = new ReagentEffectArgs(actualEntity, (meta).Owner, solution, proto, mostToRemove,
EntityManager, null, entry);
@@ -192,16 +185,20 @@ namespace Content.Server.Body.Systems
// remove a certain amount of reagent
if (mostToRemove > FixedPoint2.Zero)
_solutionContainerSystem.TryRemoveReagent(solutionEntityUid.Value, solution, reagent.ReagentId, mostToRemove);
_solutionContainerSystem.TryRemoveReagent(solutionEntityUid.Value, solution, reagent.ReagentId,
mostToRemove);
}
}
}
public sealed class ApplyMetabolicMultiplierEvent : EntityEventArgs
{
// The entity whose metabolism is being modified
public EntityUid Uid;
public EntityUid Uid;
// What the metabolism's update rate will be multiplied by
public float Multiplier;
public float Multiplier;
// Apply this multiplier or ignore / reset it?
public bool Apply;
}

View File

@@ -8,7 +8,6 @@ using Content.Shared.Atmos;
using Content.Shared.Body.Components;
using Content.Shared.Damage;
using Content.Shared.Database;
using Content.Shared.MobState.Components;
using Content.Shared.MobState.EntitySystems;
using JetBrains.Annotations;
using Robust.Shared.Player;
@@ -42,8 +41,7 @@ namespace Content.Server.Body.Systems
{
base.Update(frameTime);
foreach (var (respirator, body) in
EntityManager.EntityQuery<RespiratorComponent, SharedBodyComponent>())
foreach (var (respirator, body) in EntityManager.EntityQuery<RespiratorComponent, BodyComponent>())
{
var uid = respirator.Owner;
@@ -91,12 +89,13 @@ namespace Content.Server.Body.Systems
respirator.SuffocationCycles = 0;
}
}
public void Inhale(EntityUid uid, SharedBodyComponent? body=null)
public void Inhale(EntityUid uid, BodyComponent? body = null)
{
if (!Resolve(uid, ref body, false))
return;
var organs = _bodySystem.GetComponentsOnMechanisms<LungComponent>(uid, body);
var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(uid, body);
// Inhale gas
var ev = new InhaleLocationEvent();
@@ -121,12 +120,12 @@ namespace Content.Server.Body.Systems
}
}
public void Exhale(EntityUid uid, SharedBodyComponent? body=null)
public void Exhale(EntityUid uid, BodyComponent? body = null)
{
if (!Resolve(uid, ref body, false))
return;
var organs = _bodySystem.GetComponentsOnMechanisms<LungComponent>(uid, body);
var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(uid, body);
// exhale gas
@@ -187,7 +186,8 @@ namespace Content.Server.Body.Systems
Math.Clamp(respirator.Saturation, respirator.MinSaturation, respirator.MaxSaturation);
}
private void OnApplyMetabolicMultiplier(EntityUid uid, RespiratorComponent component, ApplyMetabolicMultiplierEvent args)
private void OnApplyMetabolicMultiplier(EntityUid uid, RespiratorComponent component,
ApplyMetabolicMultiplierEvent args)
{
if (args.Apply)
{
@@ -197,6 +197,7 @@ namespace Content.Server.Body.Systems
component.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;

View File

@@ -1,7 +1,7 @@
using Content.Server.Body.Components;
using Content.Server.Chemistry.Components.SolutionManager;
using Content.Server.Chemistry.EntitySystems;
using Content.Shared.Body.Components;
using Content.Shared.Body.Organ;
using Content.Shared.Chemistry.Components;
using Robust.Shared.Utility;
@@ -9,6 +9,7 @@ namespace Content.Server.Body.Systems
{
public sealed class StomachSystem : EntitySystem
{
[Dependency] private readonly BodySystem _bodySystem = default!;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
public const string DefaultSolutionName = "stomach";
@@ -21,12 +22,8 @@ namespace Content.Server.Body.Systems
public override void Update(float frameTime)
{
foreach (var (stomach, mech, sol)
in EntityManager.EntityQuery<StomachComponent, MechanismComponent, SolutionContainerManagerComponent>(false))
foreach (var (stomach, organ, sol)in EntityManager.EntityQuery<StomachComponent, OrganComponent, SolutionContainerManagerComponent>())
{
if (mech.Body == null)
continue;
stomach.AccumulatedFrameTime += frameTime;
if (stomach.AccumulatedFrameTime < stomach.UpdateInterval)
@@ -35,12 +32,11 @@ namespace Content.Server.Body.Systems
stomach.AccumulatedFrameTime -= stomach.UpdateInterval;
// Get our solutions
if (!_solutionContainerSystem.TryGetSolution((stomach).Owner, DefaultSolutionName,
out var stomachSolution, sol))
if (!_solutionContainerSystem.TryGetSolution(stomach.Owner, DefaultSolutionName,
out var stomachSolution, sol))
continue;
if (!_solutionContainerSystem.TryGetSolution((mech.Body).Owner, stomach.BodySolutionName,
out var bodySolution))
if (organ.Body is not { } body || !_solutionContainerSystem.TryGetSolution(body, stomach.BodySolutionName, out var bodySolution))
continue;
var transferSolution = new Solution();
@@ -71,23 +67,25 @@ namespace Content.Server.Body.Systems
}
// Transfer everything to the body solution!
_solutionContainerSystem.TryAddSolution((mech.Body).Owner, bodySolution, transferSolution);
_solutionContainerSystem.TryAddSolution(body, bodySolution, transferSolution);
}
}
private void OnApplyMetabolicMultiplier(EntityUid uid, StomachComponent component, ApplyMetabolicMultiplierEvent args)
{
if (args.Apply)
private void OnApplyMetabolicMultiplier(EntityUid uid, StomachComponent component,
ApplyMetabolicMultiplierEvent args)
{
component.UpdateInterval *= args.Multiplier;
return;
if (args.Apply)
{
component.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;
}
// 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;
}
private void OnComponentInit(EntityUid uid, StomachComponent component, ComponentInit args)
{
@@ -96,7 +94,7 @@ namespace Content.Server.Body.Systems
}
public bool CanTransferSolution(EntityUid uid, Solution solution,
SolutionContainerManagerComponent? solutions=null)
SolutionContainerManagerComponent? solutions = null)
{
if (!Resolve(uid, ref solutions, false))
return false;
@@ -112,8 +110,8 @@ namespace Content.Server.Body.Systems
}
public bool TryTransferSolution(EntityUid uid, Solution solution,
StomachComponent? stomach=null,
SolutionContainerManagerComponent? solutions=null)
StomachComponent? stomach = null,
SolutionContainerManagerComponent? solutions = null)
{
if (!Resolve(uid, ref stomach, ref solutions, false))
return false;