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:
@@ -1,21 +0,0 @@
|
|||||||
using Content.Shared.Body.Components;
|
|
||||||
using Content.Shared.DragDrop;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
|
|
||||||
namespace Content.Client.Body.Components
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
[ComponentReference(typeof(SharedBodyComponent))]
|
|
||||||
public sealed class BodyComponent : SharedBodyComponent, IDraggable
|
|
||||||
{
|
|
||||||
bool IDraggable.CanStartDrag(StartDragDropEvent args)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IDraggable.CanDrop(CanDropEvent args)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
using Content.Shared.Body.Components;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
|
|
||||||
namespace Content.Client.Body.Components
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
[ComponentReference(typeof(SharedBodyPartComponent))]
|
|
||||||
public sealed class BodyPartComponent : SharedBodyPartComponent
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
7
Content.Client/Body/Systems/BodySystem.cs
Normal file
7
Content.Client/Body/Systems/BodySystem.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
using Content.Shared.Body.Systems;
|
||||||
|
|
||||||
|
namespace Content.Client.Body.Systems;
|
||||||
|
|
||||||
|
public sealed class BodySystem : SharedBodySystem
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -1,11 +1,10 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
|
using Content.Shared.Body.Part;
|
||||||
|
using Content.Shared.Body.Systems;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Localization;
|
|
||||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||||
using static Robust.Client.UserInterface.Controls.ItemList;
|
using static Robust.Client.UserInterface.Controls.ItemList;
|
||||||
|
|
||||||
@@ -14,7 +13,8 @@ namespace Content.Client.Body.UI
|
|||||||
public sealed class BodyScannerDisplay : DefaultWindow
|
public sealed class BodyScannerDisplay : DefaultWindow
|
||||||
{
|
{
|
||||||
private EntityUid? _currentEntity;
|
private EntityUid? _currentEntity;
|
||||||
private SharedBodyPartComponent? _currentBodyPart;
|
private BodyPartComponent? _currentBodyPart;
|
||||||
|
private readonly Dictionary<int, BodyPartSlot> _bodyPartsList = new();
|
||||||
|
|
||||||
public BodyScannerDisplay(BodyScannerBoundUserInterface owner)
|
public BodyScannerDisplay(BodyScannerBoundUserInterface owner)
|
||||||
{
|
{
|
||||||
@@ -106,75 +106,79 @@ namespace Content.Client.Body.UI
|
|||||||
{
|
{
|
||||||
_currentEntity = entity;
|
_currentEntity = entity;
|
||||||
BodyPartList.Clear();
|
BodyPartList.Clear();
|
||||||
|
_bodyPartsList.Clear();
|
||||||
|
|
||||||
var body = IoCManager.Resolve<IEntityManager>().GetComponentOrNull<SharedBodyComponent>(_currentEntity);
|
var bodySystem = IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<SharedBodySystem>();
|
||||||
|
var i = 0;
|
||||||
if (body == null)
|
foreach (var part in bodySystem.GetBodyChildren(_currentEntity))
|
||||||
{
|
{
|
||||||
return;
|
_bodyPartsList[i++] = part.Component.ParentSlot!;
|
||||||
}
|
BodyPartList.AddItem(Loc.GetString(part.Component.Name));
|
||||||
|
|
||||||
foreach (var (part, _) in body.Parts)
|
|
||||||
{
|
|
||||||
BodyPartList.AddItem(Loc.GetString(part.Name));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BodyPartOnItemSelected(ItemListSelectedEventArgs args)
|
public void BodyPartOnItemSelected(ItemListSelectedEventArgs args)
|
||||||
{
|
{
|
||||||
if (!IoCManager.Resolve<IEntityManager>().TryGetComponent<SharedBodyComponent>(_currentEntity, out var body))
|
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var slot = body.SlotAt(args.ItemIndex);
|
_currentBodyPart = entMan.GetComponentOrNull<BodyPartComponent>(_bodyPartsList[args.ItemIndex].Child);
|
||||||
_currentBodyPart = body.PartAt(args.ItemIndex).Key;
|
|
||||||
|
|
||||||
if (slot.Part != null)
|
if (_currentBodyPart is {ParentSlot.Id: var slotId} part)
|
||||||
{
|
{
|
||||||
UpdateBodyPartBox(slot.Part, slot.Id);
|
UpdateBodyPartBox(part, slotId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateBodyPartBox(SharedBodyPartComponent part, string slotName)
|
private void UpdateBodyPartBox(BodyPartComponent part, string slotName)
|
||||||
{
|
{
|
||||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||||
BodyPartLabel.Text = $"{Loc.GetString(slotName)}: {Loc.GetString(entMan.GetComponent<MetaDataComponent>(part.Owner).EntityName)}";
|
BodyPartLabel.Text =
|
||||||
|
$"{Loc.GetString(slotName)}: {Loc.GetString(entMan.GetComponent<MetaDataComponent>(part.Owner).EntityName)}";
|
||||||
|
|
||||||
// TODO BODY Part damage
|
// TODO BODY Part damage
|
||||||
if (entMan.TryGetComponent(part.Owner, out DamageableComponent? damageable))
|
if (entMan.TryGetComponent(part.Owner, out DamageableComponent? damageable))
|
||||||
{
|
{
|
||||||
BodyPartHealth.Text = Loc.GetString("body-scanner-display-body-part-damage-text",("damage", damageable.TotalDamage));
|
BodyPartHealth.Text = Loc.GetString("body-scanner-display-body-part-damage-text",
|
||||||
|
("damage", damageable.TotalDamage));
|
||||||
}
|
}
|
||||||
|
|
||||||
MechanismList.Clear();
|
MechanismList.Clear();
|
||||||
|
|
||||||
foreach (var mechanism in part.Mechanisms)
|
var bodySystem = entMan.System<SharedBodySystem>();
|
||||||
|
foreach (var organ in bodySystem.GetPartOrgans(part.Owner, part))
|
||||||
{
|
{
|
||||||
MechanismList.AddItem(mechanism.Name);
|
var organName = entMan.GetComponent<MetaDataComponent>(organ.Id).EntityName;
|
||||||
|
MechanismList.AddItem(organName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO BODY Guaranteed this is going to crash when a part's mechanisms change. This part is left as an exercise for the reader.
|
// TODO BODY Guaranteed this is going to crash when a part's mechanisms change. This part is left as an exercise for the reader.
|
||||||
public void MechanismOnItemSelected(ItemListSelectedEventArgs args)
|
public void MechanismOnItemSelected(ItemListSelectedEventArgs args)
|
||||||
{
|
{
|
||||||
UpdateMechanismBox(_currentBodyPart?.Mechanisms.ElementAt(args.ItemIndex));
|
if (_currentBodyPart == null)
|
||||||
|
{
|
||||||
|
UpdateMechanismBox(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bodySystem = IoCManager.Resolve<IEntityManager>().System<SharedBodySystem>();
|
||||||
|
var organ = bodySystem.GetPartOrgans(_currentBodyPart.Owner, _currentBodyPart).ElementAt(args.ItemIndex);
|
||||||
|
UpdateMechanismBox(organ.Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateMechanismBox(MechanismComponent? mechanism)
|
private void UpdateMechanismBox(EntityUid? organ)
|
||||||
{
|
{
|
||||||
// TODO BODY Improve UI
|
// TODO BODY Improve UI
|
||||||
if (mechanism == null)
|
if (organ == null)
|
||||||
{
|
{
|
||||||
MechanismInfoLabel.SetMessage("");
|
MechanismInfoLabel.SetMessage("");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO BODY Mechanism description
|
// TODO BODY Mechanism description
|
||||||
var message =
|
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||||
Loc.GetString(
|
var organName = entMan.GetComponent<MetaDataComponent>(organ.Value).EntityName;
|
||||||
$"{mechanism.Name}\nHealth: {mechanism.CurrentDurability}/{mechanism.MaxDurability}");
|
var message = Loc.GetString($"{organName}");
|
||||||
|
|
||||||
MechanismInfoLabel.SetMessage(message);
|
MechanismInfoLabel.SetMessage(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Organ;
|
||||||
using Robust.Client.Console;
|
using Robust.Client.Console;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
|
|
||||||
namespace Content.Client.Commands
|
namespace Content.Client.Commands
|
||||||
{
|
{
|
||||||
@@ -17,18 +15,18 @@ namespace Content.Client.Commands
|
|||||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||||
{
|
{
|
||||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
var mechanisms = entityManager.EntityQuery<MechanismComponent>(true);
|
var organs = entityManager.EntityQuery<OrganComponent>(true);
|
||||||
|
|
||||||
foreach (var mechanism in mechanisms)
|
foreach (var part in organs)
|
||||||
{
|
{
|
||||||
if (!entityManager.TryGetComponent(mechanism.Owner, out SpriteComponent? sprite))
|
if (!entityManager.TryGetComponent(part.Owner, out SpriteComponent? sprite))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
sprite.ContainerOccluded = false;
|
sprite.ContainerOccluded = false;
|
||||||
|
|
||||||
var tempParent = mechanism.Owner;
|
var tempParent = part.Owner;
|
||||||
while (tempParent.TryGetContainer(out var container))
|
while (tempParent.TryGetContainer(out var container))
|
||||||
{
|
{
|
||||||
if (!container.ShowContents)
|
if (!container.ShowContents)
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Organ;
|
||||||
using Robust.Client.Console;
|
using Robust.Client.Console;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
|
|
||||||
namespace Content.Client.Commands
|
namespace Content.Client.Commands
|
||||||
{
|
{
|
||||||
@@ -19,9 +17,9 @@ namespace Content.Client.Commands
|
|||||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||||
{
|
{
|
||||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
var mechanisms = entityManager.EntityQuery<MechanismComponent>(true);
|
var organs = entityManager.EntityQuery<OrganComponent>(true);
|
||||||
|
|
||||||
foreach (var mechanism in mechanisms)
|
foreach (var mechanism in organs)
|
||||||
{
|
{
|
||||||
if (entityManager.TryGetComponent(mechanism.Owner, out SpriteComponent? sprite))
|
if (entityManager.TryGetComponent(mechanism.Owner, out SpriteComponent? sprite))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.Body.Components;
|
using Content.Server.Body.Systems;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
using Content.Shared.Body.Part;
|
using Content.Shared.Body.Part;
|
||||||
using Content.Shared.Rotation;
|
using Content.Shared.Rotation;
|
||||||
@@ -12,7 +12,7 @@ using Robust.Shared.Maths;
|
|||||||
namespace Content.IntegrationTests.Tests.Body
|
namespace Content.IntegrationTests.Tests.Body
|
||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
[TestOf(typeof(SharedBodyComponent))]
|
[TestOf(typeof(BodyPartComponent))]
|
||||||
[TestOf(typeof(BodyComponent))]
|
[TestOf(typeof(BodyComponent))]
|
||||||
public sealed class LegTest
|
public sealed class LegTest
|
||||||
{
|
{
|
||||||
@@ -23,16 +23,15 @@ namespace Content.IntegrationTests.Tests.Body
|
|||||||
components:
|
components:
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
- type: Body
|
- type: Body
|
||||||
template: HumanoidTemplate
|
prototype: Human
|
||||||
preset: HumanPreset
|
|
||||||
centerSlot: torso
|
|
||||||
- type: StandingState
|
- type: StandingState
|
||||||
";
|
";
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task RemoveLegsFallTest()
|
public async Task RemoveLegsFallTest()
|
||||||
{
|
{
|
||||||
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
|
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings
|
||||||
|
{NoClient = true, ExtraPrototypes = Prototypes});
|
||||||
var server = pairTracker.Pair.Server;
|
var server = pairTracker.Pair.Server;
|
||||||
|
|
||||||
AppearanceComponent appearance = null;
|
AppearanceComponent appearance = null;
|
||||||
@@ -44,18 +43,20 @@ namespace Content.IntegrationTests.Tests.Body
|
|||||||
var mapId = mapManager.CreateMap();
|
var mapId = mapManager.CreateMap();
|
||||||
|
|
||||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
var human = entityManager.SpawnEntity("HumanBodyAndAppearanceDummy", new MapCoordinates(Vector2.Zero, mapId));
|
var human = entityManager.SpawnEntity("HumanBodyAndAppearanceDummy",
|
||||||
|
new MapCoordinates(Vector2.Zero, mapId));
|
||||||
|
|
||||||
Assert.That(entityManager.TryGetComponent(human, out SharedBodyComponent body));
|
Assert.That(entityManager.TryGetComponent(human, out BodyComponent body));
|
||||||
Assert.That(entityManager.TryGetComponent(human, out appearance));
|
Assert.That(entityManager.TryGetComponent(human, out appearance));
|
||||||
|
|
||||||
Assert.That(!appearance.TryGetData(RotationVisuals.RotationState, out RotationState _));
|
Assert.That(!appearance.TryGetData(RotationVisuals.RotationState, out RotationState _));
|
||||||
|
|
||||||
var legs = body.GetPartsOfType(BodyPartType.Leg);
|
var bodySystem = entityManager.System<BodySystem>();
|
||||||
|
var legs = bodySystem.GetBodyChildrenOfType(body.Owner, BodyPartType.Leg, body);
|
||||||
|
|
||||||
foreach (var leg in legs)
|
foreach (var leg in legs)
|
||||||
{
|
{
|
||||||
body.RemovePart(leg);
|
bodySystem.DropPart(leg.Id, leg.Component);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,7 @@ namespace Content.IntegrationTests.Tests.Body
|
|||||||
components:
|
components:
|
||||||
- type: SolutionContainerManager
|
- type: SolutionContainerManager
|
||||||
- type: Body
|
- type: Body
|
||||||
template: HumanoidTemplate
|
prototype: Human
|
||||||
preset: HumanPreset
|
|
||||||
centerSlot: torso
|
|
||||||
- type: MobState
|
- type: MobState
|
||||||
thresholds:
|
thresholds:
|
||||||
0: Alive
|
0: Alive
|
||||||
@@ -49,7 +47,8 @@ namespace Content.IntegrationTests.Tests.Body
|
|||||||
public async Task AirConsistencyTest()
|
public async Task AirConsistencyTest()
|
||||||
{
|
{
|
||||||
// --- Setup
|
// --- Setup
|
||||||
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
|
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings
|
||||||
|
{NoClient = true, ExtraPrototypes = Prototypes});
|
||||||
var server = pairTracker.Pair.Server;
|
var server = pairTracker.Pair.Server;
|
||||||
|
|
||||||
await server.WaitIdleAsync();
|
await server.WaitIdleAsync();
|
||||||
@@ -62,7 +61,7 @@ namespace Content.IntegrationTests.Tests.Body
|
|||||||
|
|
||||||
MapId mapId;
|
MapId mapId;
|
||||||
EntityUid? grid = null;
|
EntityUid? grid = null;
|
||||||
SharedBodyComponent body = default;
|
BodyComponent body = default;
|
||||||
EntityUid human = default;
|
EntityUid human = default;
|
||||||
GridAtmosphereComponent relevantAtmos = default;
|
GridAtmosphereComponent relevantAtmos = default;
|
||||||
float startingMoles = 0.0f;
|
float startingMoles = 0.0f;
|
||||||
@@ -127,7 +126,8 @@ namespace Content.IntegrationTests.Tests.Body
|
|||||||
[Test]
|
[Test]
|
||||||
public async Task NoSuffocationTest()
|
public async Task NoSuffocationTest()
|
||||||
{
|
{
|
||||||
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
|
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings
|
||||||
|
{NoClient = true, ExtraPrototypes = Prototypes});
|
||||||
var server = pairTracker.Pair.Server;
|
var server = pairTracker.Pair.Server;
|
||||||
|
|
||||||
var mapLoader = server.ResolveDependency<IMapLoader>();
|
var mapLoader = server.ResolveDependency<IMapLoader>();
|
||||||
@@ -155,7 +155,7 @@ namespace Content.IntegrationTests.Tests.Body
|
|||||||
var coordinates = new EntityCoordinates(grid.Value, center);
|
var coordinates = new EntityCoordinates(grid.Value, center);
|
||||||
human = entityManager.SpawnEntity("HumanBodyDummy", coordinates);
|
human = entityManager.SpawnEntity("HumanBodyDummy", coordinates);
|
||||||
|
|
||||||
Assert.True(entityManager.HasComponent<SharedBodyComponent>(human));
|
Assert.True(entityManager.HasComponent<BodyComponent>(human));
|
||||||
Assert.True(entityManager.TryGetComponent(human, out respirator));
|
Assert.True(entityManager.TryGetComponent(human, out respirator));
|
||||||
Assert.False(respirator.SuffocationCycles > respirator.SuffocationCycleThreshold);
|
Assert.False(respirator.SuffocationCycles > respirator.SuffocationCycleThreshold);
|
||||||
});
|
});
|
||||||
@@ -167,7 +167,8 @@ namespace Content.IntegrationTests.Tests.Body
|
|||||||
await server.WaitRunTicks(increment);
|
await server.WaitRunTicks(increment);
|
||||||
await server.WaitAssertion(() =>
|
await server.WaitAssertion(() =>
|
||||||
{
|
{
|
||||||
Assert.False(respirator.SuffocationCycles > respirator.SuffocationCycleThreshold, $"Entity {entityManager.GetComponent<MetaDataComponent>(human).EntityName} is suffocating on tick {tick}");
|
Assert.False(respirator.SuffocationCycles > respirator.SuffocationCycleThreshold,
|
||||||
|
$"Entity {entityManager.GetComponent<MetaDataComponent>(human).EntityName} is suffocating on tick {tick}");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
148
Content.IntegrationTests/Tests/Body/SaveLoadReparentTest.cs
Normal file
148
Content.IntegrationTests/Tests/Body/SaveLoadReparentTest.cs
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Content.Shared.Body.Components;
|
||||||
|
using Content.Shared.Body.Systems;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using Robust.Server.Maps;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
|
||||||
|
namespace Content.IntegrationTests.Tests.Body;
|
||||||
|
|
||||||
|
[TestFixture]
|
||||||
|
public sealed class SaveLoadReparentTest
|
||||||
|
{
|
||||||
|
private const string Prototypes = @"
|
||||||
|
- type: entity
|
||||||
|
name: HumanBodyDummy
|
||||||
|
id: HumanBodyDummy
|
||||||
|
components:
|
||||||
|
- type: Body
|
||||||
|
prototype: Human
|
||||||
|
";
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Test()
|
||||||
|
{
|
||||||
|
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings
|
||||||
|
{
|
||||||
|
NoClient = true,
|
||||||
|
ExtraPrototypes = Prototypes
|
||||||
|
});
|
||||||
|
var server = pairTracker.Pair.Server;
|
||||||
|
|
||||||
|
var entities = server.ResolveDependency<IEntityManager>();
|
||||||
|
var maps = server.ResolveDependency<IMapManager>();
|
||||||
|
var mapLoader = server.ResolveDependency<IMapLoader>();
|
||||||
|
var bodySystem = entities.System<SharedBodySystem>();
|
||||||
|
|
||||||
|
await server.WaitAssertion(() =>
|
||||||
|
{
|
||||||
|
var mapId = maps.CreateMap();
|
||||||
|
maps.CreateGrid(mapId);
|
||||||
|
var human = entities.SpawnEntity("HumanBodyDummy", new MapCoordinates(0, 0, mapId));
|
||||||
|
|
||||||
|
Assert.That(entities.HasComponent<BodyComponent>(human), Is.True);
|
||||||
|
|
||||||
|
var parts = bodySystem.GetBodyChildren(human).ToArray();
|
||||||
|
var organs = bodySystem.GetBodyOrgans(human).ToArray();
|
||||||
|
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(parts, Is.Not.Empty);
|
||||||
|
Assert.That(organs, Is.Not.Empty);
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (var (id, component) in parts)
|
||||||
|
{
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(component.Body, Is.EqualTo(human));
|
||||||
|
Assert.That(component.ParentSlot, Is.Not.Null);
|
||||||
|
Assert.That(component.ParentSlot.Parent, Is.Not.EqualTo(default(EntityUid)));
|
||||||
|
Assert.That(component.ParentSlot.Child, Is.EqualTo(id));
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (var (slotId, slot) in component.Children)
|
||||||
|
{
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(slot.Id, Is.EqualTo(slotId));
|
||||||
|
Assert.That(slot.Parent, Is.Not.EqualTo(default(EntityUid)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (id, component) in organs)
|
||||||
|
{
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(component.Body, Is.EqualTo(human));
|
||||||
|
Assert.That(component.ParentSlot, Is.Not.Null);
|
||||||
|
Assert.That(component.ParentSlot.Parent, Is.Not.EqualTo(default(EntityUid)));
|
||||||
|
Assert.That(component.ParentSlot.Child, Is.EqualTo(id));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert.That(entities
|
||||||
|
.EntityQuery<BodyComponent>()
|
||||||
|
.Where(e => entities.GetComponent<MetaDataComponent>(e.Owner).EntityPrototype!.Name ==
|
||||||
|
"HumanBodyDummy"), Is.Not.Empty);
|
||||||
|
|
||||||
|
const string mapPath = $"/{nameof(SaveLoadReparentTest)}{nameof(Test)}map.yml";
|
||||||
|
|
||||||
|
mapLoader.SaveMap(mapId, mapPath);
|
||||||
|
maps.DeleteMap(mapId);
|
||||||
|
|
||||||
|
mapId = maps.CreateMap();
|
||||||
|
mapLoader.LoadMap(mapId, mapPath);
|
||||||
|
|
||||||
|
var query = entities
|
||||||
|
.EntityQuery<BodyComponent>()
|
||||||
|
.Where(e => entities.GetComponent<MetaDataComponent>(e.Owner).EntityPrototype!.Name == "HumanBodyDummy")
|
||||||
|
.ToArray();
|
||||||
|
|
||||||
|
Assert.That(query, Is.Not.Empty);
|
||||||
|
foreach (var body in query)
|
||||||
|
{
|
||||||
|
human = body.Owner;
|
||||||
|
parts = bodySystem.GetBodyChildren(human).ToArray();
|
||||||
|
organs = bodySystem.GetBodyOrgans(human).ToArray();
|
||||||
|
|
||||||
|
Assert.That(parts, Is.Not.Empty);
|
||||||
|
Assert.That(organs, Is.Not.Empty);
|
||||||
|
|
||||||
|
foreach (var (id, component) in parts)
|
||||||
|
{
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(component.Body, Is.EqualTo(human));
|
||||||
|
Assert.That(component.ParentSlot, Is.Not.Null);
|
||||||
|
Assert.That(component.ParentSlot.Parent, Is.Not.EqualTo(default(EntityUid)));
|
||||||
|
Assert.That(component.ParentSlot.Child, Is.EqualTo(id));
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (var (slotId, slot) in component.Children)
|
||||||
|
{
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(slot.Id, Is.EqualTo(slotId));
|
||||||
|
Assert.That(slot.Parent, Is.Not.EqualTo(default(EntityUid)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (id, component) in organs)
|
||||||
|
{
|
||||||
|
Assert.Multiple(() =>
|
||||||
|
{
|
||||||
|
Assert.That(component.Body, Is.EqualTo(human));
|
||||||
|
Assert.That(component.ParentSlot, Is.Not.Null);
|
||||||
|
Assert.That(component.ParentSlot.Parent, Is.Not.EqualTo(default(EntityUid)));
|
||||||
|
Assert.That(component.ParentSlot.Child, Is.EqualTo(id));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Content.Server.Body.Systems;
|
||||||
using Content.Server.Buckle.Components;
|
using Content.Server.Buckle.Components;
|
||||||
using Content.Server.Hands.Components;
|
using Content.Server.Hands.Components;
|
||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
@@ -9,8 +10,6 @@ using Content.Shared.Hands.EntitySystems;
|
|||||||
using Content.Shared.Standing;
|
using Content.Shared.Standing;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Buckle
|
namespace Content.IntegrationTests.Tests.Buckle
|
||||||
{
|
{
|
||||||
@@ -31,9 +30,7 @@ namespace Content.IntegrationTests.Tests.Buckle
|
|||||||
- type: Buckle
|
- type: Buckle
|
||||||
- type: Hands
|
- type: Hands
|
||||||
- type: Body
|
- type: Body
|
||||||
template: HumanoidTemplate
|
prototype: Human
|
||||||
preset: HumanPreset
|
|
||||||
centerSlot: torso
|
|
||||||
- type: StandingState
|
- type: StandingState
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -48,10 +45,12 @@ namespace Content.IntegrationTests.Tests.Buckle
|
|||||||
components:
|
components:
|
||||||
- type: Item
|
- type: Item
|
||||||
";
|
";
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task BuckleUnbuckleCooldownRangeTest()
|
public async Task BuckleUnbuckleCooldownRangeTest()
|
||||||
{
|
{
|
||||||
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{ExtraPrototypes = Prototypes});
|
await using var pairTracker =
|
||||||
|
await PoolManager.GetServerClient(new PoolSettings {ExtraPrototypes = Prototypes});
|
||||||
var server = pairTracker.Pair.Server;
|
var server = pairTracker.Pair.Server;
|
||||||
|
|
||||||
var testMap = await PoolManager.CreateTestMap(pairTracker);
|
var testMap = await PoolManager.CreateTestMap(pairTracker);
|
||||||
@@ -96,7 +95,10 @@ namespace Content.IntegrationTests.Tests.Buckle
|
|||||||
Assert.False(actionBlocker.CanMove(human));
|
Assert.False(actionBlocker.CanMove(human));
|
||||||
Assert.False(actionBlocker.CanChangeDirection(human));
|
Assert.False(actionBlocker.CanChangeDirection(human));
|
||||||
Assert.False(standingState.Down(human));
|
Assert.False(standingState.Down(human));
|
||||||
Assert.That((entityManager.GetComponent<TransformComponent>(human).WorldPosition - entityManager.GetComponent<TransformComponent>(chair).WorldPosition).Length, Is.LessThanOrEqualTo(buckle.BuckleOffset.Length));
|
Assert.That(
|
||||||
|
(entityManager.GetComponent<TransformComponent>(human).WorldPosition -
|
||||||
|
entityManager.GetComponent<TransformComponent>(chair).WorldPosition).Length,
|
||||||
|
Is.LessThanOrEqualTo(buckle.BuckleOffset.Length));
|
||||||
|
|
||||||
// Side effects of buckling for the strap
|
// Side effects of buckling for the strap
|
||||||
Assert.That(strap.BuckledEntities, Does.Contain(human));
|
Assert.That(strap.BuckledEntities, Does.Contain(human));
|
||||||
@@ -166,7 +168,8 @@ namespace Content.IntegrationTests.Tests.Buckle
|
|||||||
Assert.False(buckle.ToggleBuckle(human, chair));
|
Assert.False(buckle.ToggleBuckle(human, chair));
|
||||||
|
|
||||||
// Move near the chair
|
// Move near the chair
|
||||||
entityManager.GetComponent<TransformComponent>(human).WorldPosition = entityManager.GetComponent<TransformComponent>(chair).WorldPosition + (0.5f, 0);
|
entityManager.GetComponent<TransformComponent>(human).WorldPosition =
|
||||||
|
entityManager.GetComponent<TransformComponent>(chair).WorldPosition + (0.5f, 0);
|
||||||
|
|
||||||
// In range
|
// In range
|
||||||
Assert.True(buckle.TryBuckle(human, chair));
|
Assert.True(buckle.TryBuckle(human, chair));
|
||||||
@@ -206,7 +209,8 @@ namespace Content.IntegrationTests.Tests.Buckle
|
|||||||
[Test]
|
[Test]
|
||||||
public async Task BuckledDyingDropItemsTest()
|
public async Task BuckledDyingDropItemsTest()
|
||||||
{
|
{
|
||||||
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
|
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings
|
||||||
|
{NoClient = true, ExtraPrototypes = Prototypes});
|
||||||
var server = pairTracker.Pair.Server;
|
var server = pairTracker.Pair.Server;
|
||||||
|
|
||||||
var testMap = await PoolManager.CreateTestMap(pairTracker);
|
var testMap = await PoolManager.CreateTestMap(pairTracker);
|
||||||
@@ -215,7 +219,7 @@ namespace Content.IntegrationTests.Tests.Buckle
|
|||||||
EntityUid human = default;
|
EntityUid human = default;
|
||||||
BuckleComponent buckle = null;
|
BuckleComponent buckle = null;
|
||||||
HandsComponent hands = null;
|
HandsComponent hands = null;
|
||||||
SharedBodyComponent body = null;
|
BodyComponent body = null;
|
||||||
|
|
||||||
await server.WaitIdleAsync();
|
await server.WaitIdleAsync();
|
||||||
|
|
||||||
@@ -260,12 +264,13 @@ namespace Content.IntegrationTests.Tests.Buckle
|
|||||||
Assert.NotNull(hand.HeldEntity);
|
Assert.NotNull(hand.HeldEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
var legs = body.GetPartsOfType(BodyPartType.Leg);
|
var bodySystem = entityManager.System<BodySystem>();
|
||||||
|
var legs = bodySystem.GetBodyChildrenOfType(body.Owner, BodyPartType.Leg, body);
|
||||||
|
|
||||||
// Break our guy's kneecaps
|
// Break our guy's kneecaps
|
||||||
foreach (var leg in legs)
|
foreach (var leg in legs)
|
||||||
{
|
{
|
||||||
body.RemovePart(leg);
|
bodySystem.DropPart(leg.Id, leg.Component);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -291,7 +296,8 @@ namespace Content.IntegrationTests.Tests.Buckle
|
|||||||
[Test]
|
[Test]
|
||||||
public async Task ForceUnbuckleBuckleTest()
|
public async Task ForceUnbuckleBuckleTest()
|
||||||
{
|
{
|
||||||
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
|
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings
|
||||||
|
{NoClient = true, ExtraPrototypes = Prototypes});
|
||||||
var server = pairTracker.Pair.Server;
|
var server = pairTracker.Pair.Server;
|
||||||
|
|
||||||
var testMap = await PoolManager.CreateTestMap(pairTracker);
|
var testMap = await PoolManager.CreateTestMap(pairTracker);
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ using Content.Shared.Disposal;
|
|||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Reflection;
|
using Robust.Shared.Reflection;
|
||||||
|
|
||||||
namespace Content.IntegrationTests.Tests.Disposal
|
namespace Content.IntegrationTests.Tests.Disposal
|
||||||
@@ -78,9 +77,7 @@ namespace Content.IntegrationTests.Tests.Disposal
|
|||||||
id: HumanDummy
|
id: HumanDummy
|
||||||
components:
|
components:
|
||||||
- type: Body
|
- type: Body
|
||||||
template: HumanoidTemplate
|
prototype: Human
|
||||||
preset: HumanPreset
|
|
||||||
centerSlot: torso
|
|
||||||
- type: MobState
|
- type: MobState
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: Biological
|
damageContainer: Biological
|
||||||
@@ -127,7 +124,8 @@ namespace Content.IntegrationTests.Tests.Disposal
|
|||||||
[Test]
|
[Test]
|
||||||
public async Task Test()
|
public async Task Test()
|
||||||
{
|
{
|
||||||
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
|
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings
|
||||||
|
{NoClient = true, ExtraPrototypes = Prototypes});
|
||||||
var server = pairTracker.Pair.Server;
|
var server = pairTracker.Pair.Server;
|
||||||
|
|
||||||
var testMap = await PoolManager.CreateTestMap(pairTracker);
|
var testMap = await PoolManager.CreateTestMap(pairTracker);
|
||||||
@@ -147,7 +145,8 @@ namespace Content.IntegrationTests.Tests.Disposal
|
|||||||
human = entityManager.SpawnEntity("HumanDummy", coordinates);
|
human = entityManager.SpawnEntity("HumanDummy", coordinates);
|
||||||
wrench = entityManager.SpawnEntity("WrenchDummy", coordinates);
|
wrench = entityManager.SpawnEntity("WrenchDummy", coordinates);
|
||||||
disposalUnit = entityManager.SpawnEntity("DisposalUnitDummy", coordinates);
|
disposalUnit = entityManager.SpawnEntity("DisposalUnitDummy", coordinates);
|
||||||
disposalTrunk = entityManager.SpawnEntity("DisposalTrunkDummy", IoCManager.Resolve<IEntityManager>().GetComponent<TransformComponent>(disposalUnit).MapPosition);
|
disposalTrunk = entityManager.SpawnEntity("DisposalTrunkDummy",
|
||||||
|
IoCManager.Resolve<IEntityManager>().GetComponent<TransformComponent>(disposalUnit).MapPosition);
|
||||||
|
|
||||||
// Test for components existing
|
// Test for components existing
|
||||||
ref DisposalUnitComponent? comp = ref unit!;
|
ref DisposalUnitComponent? comp = ref unit!;
|
||||||
@@ -197,7 +196,7 @@ namespace Content.IntegrationTests.Tests.Disposal
|
|||||||
// Remove power need
|
// Remove power need
|
||||||
Assert.True(entityManager.TryGetComponent(disposalUnit, out ApcPowerReceiverComponent power));
|
Assert.True(entityManager.TryGetComponent(disposalUnit, out ApcPowerReceiverComponent power));
|
||||||
power!.NeedsPower = false;
|
power!.NeedsPower = false;
|
||||||
unit.Powered = true;//Power state changed event doesn't get fired smh
|
unit.Powered = true; //Power state changed event doesn't get fired smh
|
||||||
|
|
||||||
// Flush with a mob and an item
|
// Flush with a mob and an item
|
||||||
Flush(unit, true, human, wrench);
|
Flush(unit, true, human, wrench);
|
||||||
|
|||||||
@@ -26,9 +26,7 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
|
|||||||
- type: Cuffable
|
- type: Cuffable
|
||||||
- type: Hands
|
- type: Hands
|
||||||
- type: Body
|
- type: Body
|
||||||
template: HumanoidTemplate
|
prototype: Human
|
||||||
preset: HumanPreset
|
|
||||||
centerSlot: torso
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
name: HandcuffsDummy
|
name: HandcuffsDummy
|
||||||
@@ -36,10 +34,12 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
|
|||||||
components:
|
components:
|
||||||
- type: Handcuff
|
- type: Handcuff
|
||||||
";
|
";
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task Test()
|
public async Task Test()
|
||||||
{
|
{
|
||||||
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes});
|
await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings
|
||||||
|
{NoClient = true, ExtraPrototypes = Prototypes});
|
||||||
var server = pairTracker.Pair.Server;
|
var server = pairTracker.Pair.Server;
|
||||||
|
|
||||||
EntityUid human;
|
EntityUid human;
|
||||||
@@ -63,18 +63,21 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
|
|||||||
cuffs = entityManager.SpawnEntity("HandcuffsDummy", coordinates);
|
cuffs = entityManager.SpawnEntity("HandcuffsDummy", coordinates);
|
||||||
secondCuffs = entityManager.SpawnEntity("HandcuffsDummy", coordinates);
|
secondCuffs = entityManager.SpawnEntity("HandcuffsDummy", coordinates);
|
||||||
|
|
||||||
entityManager.GetComponent<TransformComponent>(human).WorldPosition = entityManager.GetComponent<TransformComponent>(otherHuman).WorldPosition;
|
entityManager.GetComponent<TransformComponent>(human).WorldPosition =
|
||||||
|
entityManager.GetComponent<TransformComponent>(otherHuman).WorldPosition;
|
||||||
|
|
||||||
// Test for components existing
|
// Test for components existing
|
||||||
Assert.True(entityManager.TryGetComponent(human, out cuffed!), $"Human has no {nameof(CuffableComponent)}");
|
Assert.True(entityManager.TryGetComponent(human, out cuffed!),
|
||||||
|
$"Human has no {nameof(CuffableComponent)}");
|
||||||
Assert.True(entityManager.TryGetComponent(human, out hands!), $"Human has no {nameof(HandsComponent)}");
|
Assert.True(entityManager.TryGetComponent(human, out hands!), $"Human has no {nameof(HandsComponent)}");
|
||||||
Assert.True(entityManager.TryGetComponent(human, out SharedBodyComponent? _), $"Human has no {nameof(SharedBodyComponent)}");
|
Assert.True(entityManager.TryGetComponent(human, out BodyComponent? _), $"Human has no {nameof(BodyComponent)}");
|
||||||
Assert.True(entityManager.TryGetComponent(cuffs, out HandcuffComponent? _), $"Handcuff has no {nameof(HandcuffComponent)}");
|
Assert.True(entityManager.TryGetComponent(cuffs, out HandcuffComponent? _), $"Handcuff has no {nameof(HandcuffComponent)}");
|
||||||
Assert.True(entityManager.TryGetComponent(secondCuffs, out HandcuffComponent? _), $"Second handcuffs has no {nameof(HandcuffComponent)}");
|
Assert.True(entityManager.TryGetComponent(secondCuffs, out HandcuffComponent? _), $"Second handcuffs has no {nameof(HandcuffComponent)}");
|
||||||
|
|
||||||
// Test to ensure cuffed players register the handcuffs
|
// Test to ensure cuffed players register the handcuffs
|
||||||
cuffed.TryAddNewCuffs(human, cuffs);
|
cuffed.TryAddNewCuffs(human, cuffs);
|
||||||
Assert.True(cuffed.CuffedHandCount > 0, "Handcuffing a player did not result in their hands being cuffed");
|
Assert.True(cuffed.CuffedHandCount > 0,
|
||||||
|
"Handcuffing a player did not result in their hands being cuffed");
|
||||||
|
|
||||||
// Test to ensure a player with 4 hands will still only have 2 hands cuffed
|
// Test to ensure a player with 4 hands will still only have 2 hands cuffed
|
||||||
AddHand(cuffed.Owner);
|
AddHand(cuffed.Owner);
|
||||||
@@ -86,7 +89,6 @@ namespace Content.IntegrationTests.Tests.GameObjects.Components.ActionBlocking
|
|||||||
// Test to give a player with 4 hands 2 sets of cuffs
|
// Test to give a player with 4 hands 2 sets of cuffs
|
||||||
cuffed.TryAddNewCuffs(human, secondCuffs);
|
cuffed.TryAddNewCuffs(human, secondCuffs);
|
||||||
Assert.True(cuffed.CuffedHandCount == 4, "Player doesn't have correct amount of hands cuffed");
|
Assert.True(cuffed.CuffedHandCount == 4, "Player doesn't have correct amount of hands cuffed");
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await pairTracker.CleanReturnAsync();
|
await pairTracker.CleanReturnAsync();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Server.Body.Components;
|
using Content.Server.Body.Systems;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
|
|
||||||
@@ -19,35 +19,28 @@ namespace Content.Server.Administration.Commands
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!EntityUid.TryParse(args[0], out var entityUid))
|
if (!EntityUid.TryParse(args[0], out var childId))
|
||||||
{
|
{
|
||||||
shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number"));
|
shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!EntityUid.TryParse(args[1], out var storageUid))
|
if (!EntityUid.TryParse(args[1], out var parentId))
|
||||||
{
|
{
|
||||||
shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number"));
|
shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
|
var bodySystem = entityManager.System<BodySystem>();
|
||||||
|
|
||||||
if (entityManager.TryGetComponent<BodyComponent>(storageUid, out var storage)
|
if (bodySystem.TryCreatePartSlotAndAttach(parentId, args[3], childId))
|
||||||
&& entityManager.TryGetComponent<BodyPartComponent>(entityUid, out var bodyPart))
|
|
||||||
{
|
{
|
||||||
if (storage.TryAddPart(args[3], bodyPart))
|
shell.WriteLine($@"Added {childId} to {parentId}.");
|
||||||
{
|
|
||||||
shell.WriteLine($@"Added {entityUid} to {storageUid}.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
shell.WriteError($@"Could not add {entityUid} to {storageUid}.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
shell.WriteError("Could not insert.");
|
shell.WriteError($@"Could not add {childId} to {parentId}.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Content.Server.Body.Components;
|
using Content.Server.Body.Systems;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Body.Components;
|
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
|
|
||||||
namespace Content.Server.Administration.Commands
|
namespace Content.Server.Administration.Commands
|
||||||
@@ -20,35 +19,28 @@ namespace Content.Server.Administration.Commands
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!EntityUid.TryParse(args[0], out var entityUid))
|
if (!EntityUid.TryParse(args[0], out var organId))
|
||||||
{
|
{
|
||||||
shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number"));
|
shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!EntityUid.TryParse(args[1], out var storageUid))
|
if (!EntityUid.TryParse(args[1], out var partId))
|
||||||
{
|
{
|
||||||
shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number"));
|
shell.WriteError(Loc.GetString("shell-entity-uid-must-be-number"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
|
var bodySystem = entityManager.System<BodySystem>();
|
||||||
|
|
||||||
if (entityManager.TryGetComponent<BodyPartComponent>(storageUid, out var storage)
|
if (bodySystem.AddOrganToFirstValidSlot(organId, partId))
|
||||||
&& entityManager.TryGetComponent<MechanismComponent>(entityUid, out var bodyPart))
|
|
||||||
{
|
{
|
||||||
if (storage.TryAddMechanism(bodyPart))
|
shell.WriteLine($@"Added {organId} to {partId}.");
|
||||||
{
|
|
||||||
shell.WriteLine($@"Added {entityUid} to {storageUid}.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
shell.WriteError($@"Could not add {entityUid} to {storageUid}.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
shell.WriteError("Could not insert.");
|
shell.WriteError($@"Could not add {organId} to {partId}.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Server.Body.Components;
|
using Content.Server.Body.Systems;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
|
|
||||||
@@ -26,16 +26,11 @@ namespace Content.Server.Administration.Commands
|
|||||||
}
|
}
|
||||||
|
|
||||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
|
var bodySystem = entityManager.System<BodySystem>();
|
||||||
|
|
||||||
if (!entityManager.TryGetComponent<TransformComponent>(entityUid, out var transform)) return;
|
if (bodySystem.DropPart(entityUid))
|
||||||
|
|
||||||
var parent = transform.ParentUid;
|
|
||||||
|
|
||||||
if (entityManager.TryGetComponent<BodyComponent>(parent, out var body) &&
|
|
||||||
entityManager.TryGetComponent<BodyPartComponent>(entityUid, out var part))
|
|
||||||
{
|
{
|
||||||
body.RemovePart(part);
|
shell.WriteLine($"Removed body part {entityManager.ToPrettyString(entityUid)}.");
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Content.Server.Body.Components;
|
using Content.Server.Body.Systems;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Body.Components;
|
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
|
|
||||||
namespace Content.Server.Administration.Commands
|
namespace Content.Server.Administration.Commands
|
||||||
@@ -27,15 +26,11 @@ namespace Content.Server.Administration.Commands
|
|||||||
}
|
}
|
||||||
|
|
||||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
|
var bodySystem = entityManager.System<BodySystem>();
|
||||||
|
|
||||||
if (!entityManager.TryGetComponent<TransformComponent>(entityUid, out var transform)) return;
|
if (bodySystem.DropOrgan(entityUid))
|
||||||
|
|
||||||
var parent = transform.ParentUid;
|
|
||||||
|
|
||||||
if (entityManager.TryGetComponent<BodyPartComponent>(parent, out var body) &&
|
|
||||||
entityManager.TryGetComponent<MechanismComponent>(entityUid, out var part))
|
|
||||||
{
|
{
|
||||||
body.RemoveMechanism(part);
|
shell.WriteLine($"Removed organ {entityManager.ToPrettyString(entityUid)}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -103,10 +103,7 @@ public sealed partial class AdminVerbSystem
|
|||||||
4, 1, 2, maxTileBreak: 0), // it gibs, damage doesn't need to be high.
|
4, 1, 2, maxTileBreak: 0), // it gibs, damage doesn't need to be high.
|
||||||
CancellationToken.None);
|
CancellationToken.None);
|
||||||
|
|
||||||
if (TryComp(args.Target, out SharedBodyComponent? body))
|
_bodySystem.GibBody(args.Target);
|
||||||
{
|
|
||||||
body.Gib();
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
Impact = LogImpact.Extreme,
|
Impact = LogImpact.Extreme,
|
||||||
Message = Loc.GetString("admin-smite-explode-description")
|
Message = Loc.GetString("admin-smite-explode-description")
|
||||||
@@ -308,15 +305,15 @@ public sealed partial class AdminVerbSystem
|
|||||||
Act = () =>
|
Act = () =>
|
||||||
{
|
{
|
||||||
_vomitSystem.Vomit(args.Target, -1000, -1000); // You feel hollow!
|
_vomitSystem.Vomit(args.Target, -1000, -1000); // You feel hollow!
|
||||||
var organs = _bodySystem.GetComponentsOnMechanisms<TransformComponent>(args.Target, body);
|
var organs = _bodySystem.GetBodyOrganComponents<TransformComponent>(args.Target, body);
|
||||||
var baseXform = Transform(args.Target);
|
var baseXform = Transform(args.Target);
|
||||||
foreach (var (xform, mechanism) in organs)
|
foreach (var (xform, organ) in organs)
|
||||||
{
|
{
|
||||||
if (HasComp<BrainComponent>(xform.Owner) || HasComp<EyeComponent>(xform.Owner))
|
if (HasComp<BrainComponent>(xform.Owner) || HasComp<EyeComponent>(xform.Owner))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
mechanism.Part?.RemoveMechanism(mechanism);
|
var coordinates = baseXform.Coordinates.Offset(_random.NextVector2(0.5f, 0.75f));
|
||||||
xform.Coordinates = baseXform.Coordinates.Offset(_random.NextVector2(0.5f,0.75f));
|
_bodySystem.DropOrganAt(organ.Owner, coordinates, organ);
|
||||||
}
|
}
|
||||||
|
|
||||||
_popupSystem.PopupEntity(Loc.GetString("admin-smite-vomit-organs-self"), args.Target,
|
_popupSystem.PopupEntity(Loc.GetString("admin-smite-vomit-organs-self"), args.Target,
|
||||||
@@ -337,10 +334,9 @@ public sealed partial class AdminVerbSystem
|
|||||||
Act = () =>
|
Act = () =>
|
||||||
{
|
{
|
||||||
var baseXform = Transform(args.Target);
|
var baseXform = Transform(args.Target);
|
||||||
foreach (var part in body.GetPartsOfType(BodyPartType.Hand))
|
foreach (var part in _bodySystem.GetBodyChildrenOfType(args.Target, BodyPartType.Hand))
|
||||||
{
|
{
|
||||||
body.RemovePart(part);
|
_bodySystem.DropPartAt(part.Id, baseXform.Coordinates, part.Component);
|
||||||
Transform(part.Owner).Coordinates = baseXform.Coordinates;
|
|
||||||
}
|
}
|
||||||
_popupSystem.PopupEntity(Loc.GetString("admin-smite-remove-hands-self"), args.Target,
|
_popupSystem.PopupEntity(Loc.GetString("admin-smite-remove-hands-self"), args.Target,
|
||||||
Filter.Entities(args.Target), PopupType.LargeCaution);
|
Filter.Entities(args.Target), PopupType.LargeCaution);
|
||||||
@@ -360,10 +356,9 @@ public sealed partial class AdminVerbSystem
|
|||||||
Act = () =>
|
Act = () =>
|
||||||
{
|
{
|
||||||
var baseXform = Transform(args.Target);
|
var baseXform = Transform(args.Target);
|
||||||
foreach (var part in body.GetPartsOfType(BodyPartType.Hand))
|
foreach (var part in _bodySystem.GetBodyChildrenOfType(body.Owner, BodyPartType.Hand, body))
|
||||||
{
|
{
|
||||||
body.RemovePart(part);
|
_bodySystem.DropPartAt(part.Id, baseXform.Coordinates, part.Component);
|
||||||
Transform(part.Owner).Coordinates = baseXform.Coordinates;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
_popupSystem.PopupEntity(Loc.GetString("admin-smite-remove-hands-self"), args.Target,
|
_popupSystem.PopupEntity(Loc.GetString("admin-smite-remove-hands-self"), args.Target,
|
||||||
@@ -383,13 +378,9 @@ public sealed partial class AdminVerbSystem
|
|||||||
IconTexture = "/Textures/Mobs/Species/Human/organs.rsi/stomach.png",
|
IconTexture = "/Textures/Mobs/Species/Human/organs.rsi/stomach.png",
|
||||||
Act = () =>
|
Act = () =>
|
||||||
{
|
{
|
||||||
foreach (var part in body.Parts)
|
foreach (var (component, _) in _bodySystem.GetBodyOrganComponents<StomachComponent>(args.Target, body))
|
||||||
{
|
{
|
||||||
foreach (var mechanism in part.Key.Mechanisms)
|
QueueDel(component.Owner);
|
||||||
{
|
|
||||||
if (HasComp<StomachComponent>(mechanism.Owner))
|
|
||||||
QueueDel(mechanism.Owner);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_popupSystem.PopupEntity(Loc.GetString("admin-smite-stomach-removal-self"), args.Target,
|
_popupSystem.PopupEntity(Loc.GetString("admin-smite-stomach-removal-self"), args.Target,
|
||||||
@@ -407,13 +398,9 @@ public sealed partial class AdminVerbSystem
|
|||||||
IconTexture = "/Textures/Mobs/Species/Human/organs.rsi/lung-r.png",
|
IconTexture = "/Textures/Mobs/Species/Human/organs.rsi/lung-r.png",
|
||||||
Act = () =>
|
Act = () =>
|
||||||
{
|
{
|
||||||
foreach (var part in body.Parts)
|
foreach (var (component, _) in _bodySystem.GetBodyOrganComponents<LungComponent>(args.Target, body))
|
||||||
{
|
{
|
||||||
foreach (var mechanism in part.Key.Mechanisms)
|
QueueDel(component.Owner);
|
||||||
{
|
|
||||||
if (HasComp<LungComponent>(mechanism.Owner))
|
|
||||||
QueueDel(mechanism.Owner);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_popupSystem.PopupEntity(Loc.GetString("admin-smite-lung-removal-self"), args.Target,
|
_popupSystem.PopupEntity(Loc.GetString("admin-smite-lung-removal-self"), args.Target,
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
using Content.Shared.Damage;
|
using Content.Server.Actions;
|
||||||
using Content.Server.Bed.Components;
|
using Content.Server.Bed.Components;
|
||||||
using Content.Server.Buckle.Components;
|
|
||||||
using Content.Server.Body.Systems;
|
|
||||||
using Content.Shared.Buckle.Components;
|
|
||||||
using Content.Shared.Body.Components;
|
|
||||||
using Content.Shared.Bed;
|
|
||||||
using Content.Shared.Bed.Sleep;
|
|
||||||
using Content.Server.Bed.Sleep;
|
using Content.Server.Bed.Sleep;
|
||||||
|
using Content.Server.Body.Systems;
|
||||||
|
using Content.Server.Buckle.Components;
|
||||||
|
using Content.Server.MobState;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
using Content.Shared.Emag.Systems;
|
|
||||||
using Content.Server.Actions;
|
|
||||||
using Content.Server.Construction;
|
|
||||||
using Content.Server.MobState;
|
|
||||||
using Content.Shared.Actions.ActionTypes;
|
using Content.Shared.Actions.ActionTypes;
|
||||||
|
using Content.Shared.Bed;
|
||||||
|
using Content.Shared.Bed.Sleep;
|
||||||
|
using Content.Shared.Body.Components;
|
||||||
|
using Content.Shared.Buckle.Components;
|
||||||
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Emag.Systems;
|
||||||
|
using Content.Server.Construction;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
namespace Content.Server.Bed
|
namespace Content.Server.Bed
|
||||||
@@ -26,6 +26,7 @@ namespace Content.Server.Bed
|
|||||||
[Dependency] private readonly SleepingSystem _sleepingSystem = default!;
|
[Dependency] private readonly SleepingSystem _sleepingSystem = default!;
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -94,7 +95,7 @@ namespace Content.Server.Bed
|
|||||||
{
|
{
|
||||||
// In testing this also received an unbuckle event when the bed is destroyed
|
// In testing this also received an unbuckle event when the bed is destroyed
|
||||||
// So don't worry about that
|
// So don't worry about that
|
||||||
if (!HasComp<SharedBodyComponent>(args.BuckledEntity))
|
if (!HasComp<BodyComponent>(args.BuckledEntity))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!this.IsPowered(uid, EntityManager))
|
if (!this.IsPowered(uid, EntityManager))
|
||||||
@@ -143,4 +144,3 @@ namespace Content.Server.Bed
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
using System.Linq;
|
||||||
using Content.Server.Administration;
|
using Content.Server.Administration;
|
||||||
|
using Content.Server.Body.Systems;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
|
using Content.Shared.Body.Part;
|
||||||
using Robust.Server.Player;
|
using Robust.Server.Player;
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
using Robust.Shared.Prototypes;
|
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 random = IoCManager.Resolve<IRobustRandom>();
|
||||||
var text = $"You have no body{(random.Prob(0.2f) ? " and you must scream." : ".")}";
|
var text = $"You have no body{(random.Prob(0.2f) ? " and you must scream." : ".")}";
|
||||||
@@ -127,14 +130,25 @@ namespace Content.Server.Body.Commands
|
|||||||
return;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var slot = part.GetHashCode().ToString();
|
var bodySystem = entityManager.System<BodySystem>();
|
||||||
body.SetPart(slot, part);
|
|
||||||
|
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}");
|
shell.WriteLine($"Added hand to entity {entityManager.GetComponent<MetaDataComponent>(entity).EntityName}");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
|
using System.Linq;
|
||||||
using Content.Server.Administration;
|
using Content.Server.Administration;
|
||||||
|
using Content.Server.Body.Systems;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
|
using Content.Shared.Body.Part;
|
||||||
using Robust.Server.Player;
|
using Robust.Server.Player;
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
|
|
||||||
@@ -18,7 +21,7 @@ namespace Content.Server.Body.Commands
|
|||||||
var player = shell.Player as IPlayerSession;
|
var player = shell.Player as IPlayerSession;
|
||||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
|
|
||||||
EntityUid entity;
|
EntityUid bodyId;
|
||||||
EntityUid partUid;
|
EntityUid partUid;
|
||||||
|
|
||||||
switch (args.Length)
|
switch (args.Length)
|
||||||
@@ -42,7 +45,7 @@ namespace Content.Server.Body.Commands
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
entity = player.AttachedEntity.Value;
|
bodyId = player.AttachedEntity.Value;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
@@ -64,16 +67,16 @@ namespace Content.Server.Body.Commands
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
entity = entityUid;
|
bodyId = entityUid;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
shell.WriteLine(Help);
|
shell.WriteLine(Help);
|
||||||
return;
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,19 +86,39 @@ namespace Content.Server.Body.Commands
|
|||||||
return;
|
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;
|
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;
|
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)}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Server.Administration;
|
using Content.Server.Administration;
|
||||||
|
using Content.Server.Body.Systems;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
using Robust.Server.Player;
|
using Robust.Server.Player;
|
||||||
@@ -35,7 +36,8 @@ namespace Content.Server.Body.Commands
|
|||||||
return;
|
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 random = IoCManager.Resolve<IRobustRandom>();
|
||||||
var text = $"You have no body{(random.Prob(0.2f) ? " and you must scream." : ".")}";
|
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 mechanismName = string.Join(" ", args).ToLowerInvariant();
|
||||||
|
var bodySystem = entityManager.System<BodySystem>();
|
||||||
|
|
||||||
foreach (var (part, _) in body.Parts)
|
foreach (var organ in bodySystem.GetBodyOrgans(body.Owner, body))
|
||||||
foreach (var mechanism in part.Mechanisms)
|
|
||||||
{
|
{
|
||||||
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.");
|
shell.WriteLine($"Mechanism with name {mechanismName} has been destroyed.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Server.Administration;
|
using Content.Server.Administration;
|
||||||
|
using Content.Server.Body.Systems;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
using Content.Shared.Body.Part;
|
using Content.Shared.Body.Part;
|
||||||
@@ -31,7 +32,8 @@ namespace Content.Server.Body.Commands
|
|||||||
return;
|
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 random = IoCManager.Resolve<IRobustRandom>();
|
||||||
var text = $"You have no body{(random.Prob(0.2f) ? " and you must scream." : ".")}";
|
var text = $"You have no body{(random.Prob(0.2f) ? " and you must scream." : ".")}";
|
||||||
@@ -40,15 +42,16 @@ namespace Content.Server.Body.Commands
|
|||||||
return;
|
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.");
|
shell.WriteLine("You have no hands.");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
body.RemovePart(hand);
|
bodySystem.DropPart(hand.Id, hand.Component);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11
Content.Server/Body/Components/BeingGibbedEvent.cs
Normal file
11
Content.Server/Body/Components/BeingGibbedEvent.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -26,7 +26,7 @@ namespace Content.Server.Body.Components
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Copy BodyTemplate and BodyPart data into a common data class that the client can read.
|
/// Copy BodyTemplate and BodyPart data into a common data class that the client can read.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private BodyScannerUIState InterfaceState(SharedBodyComponent body)
|
private BodyScannerUIState InterfaceState(BodyComponent body)
|
||||||
{
|
{
|
||||||
return new(body.Owner);
|
return new(body.Owner);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,119 +1,148 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Content.Server.Body.Components;
|
using Content.Server.Body.Components;
|
||||||
using Content.Server.GameTicking;
|
using Content.Server.GameTicking;
|
||||||
|
using Content.Server.Humanoid;
|
||||||
using Content.Server.Kitchen.Components;
|
using Content.Server.Kitchen.Components;
|
||||||
using Content.Server.Mind.Components;
|
using Content.Server.Mind.Components;
|
||||||
using Content.Shared.Body.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.MobState.Components;
|
||||||
using Content.Shared.Movement.Events;
|
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;
|
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!;
|
base.Initialize();
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
SubscribeLocalEvent<BodyComponent, MoveInputEvent>(OnRelayMoveInput);
|
||||||
{
|
SubscribeLocalEvent<BodyComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
|
||||||
base.Initialize();
|
SubscribeLocalEvent<BodyComponent, BeingMicrowavedEvent>(OnBeingMicrowaved);
|
||||||
SubscribeLocalEvent<BodyComponent, MoveInputEvent>(OnRelayMoveInput);
|
}
|
||||||
SubscribeLocalEvent<BodyComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
|
|
||||||
SubscribeLocalEvent<BodyComponent, BeingMicrowavedEvent>(OnBeingMicrowaved);
|
|
||||||
SubscribeLocalEvent<BodyPartComponent, MapInitEvent>((_, c, _) => c.MapInitialize());
|
|
||||||
}
|
|
||||||
|
|
||||||
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) &&
|
if (!mind.Mind!.TimeOfDeath.HasValue)
|
||||||
mobState.IsDead() &&
|
|
||||||
EntityManager.TryGetComponent<MindComponent>(uid, out var mind) &&
|
|
||||||
mind.HasMind)
|
|
||||||
{
|
{
|
||||||
if (!mind.Mind!.TimeOfDeath.HasValue)
|
mind.Mind.TimeOfDeath = _gameTiming.RealTime;
|
||||||
{
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return list;
|
_ticker.OnGhostAttempt(mind.Mind!, true);
|
||||||
}
|
|
||||||
|
|
||||||
/// <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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Content.Server.Ghost.Components;
|
|||||||
using Content.Server.Mind.Components;
|
using Content.Server.Mind.Components;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
using Content.Shared.Body.Events;
|
using Content.Shared.Body.Events;
|
||||||
|
using Content.Shared.Body.Organ;
|
||||||
using Content.Shared.Movement.Components;
|
using Content.Shared.Movement.Components;
|
||||||
|
|
||||||
namespace Content.Server.Body.Systems
|
namespace Content.Server.Body.Systems
|
||||||
@@ -13,21 +14,22 @@ namespace Content.Server.Body.Systems
|
|||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<BrainComponent, AddedToBodyEvent>((uid, _, args) => HandleMind((args.Body).Owner, uid));
|
SubscribeLocalEvent<BrainComponent, AddedToBodyEvent>((uid, _, args) => HandleMind(args.Body, uid));
|
||||||
SubscribeLocalEvent<BrainComponent, AddedToPartEvent>((uid, _, args) => HandleMind((args.Part).Owner, uid));
|
SubscribeLocalEvent<BrainComponent, AddedToPartEvent>((uid, _, args) => HandleMind(args.Part, uid));
|
||||||
SubscribeLocalEvent<BrainComponent, AddedToPartInBodyEvent>((uid, _, args) => HandleMind((args.Body).Owner, uid));
|
SubscribeLocalEvent<BrainComponent, AddedToPartInBodyEvent>((uid, _, args) => HandleMind(args.Body, uid));
|
||||||
SubscribeLocalEvent<BrainComponent, RemovedFromBodyEvent>(OnRemovedFromBody);
|
SubscribeLocalEvent<BrainComponent, RemovedFromBodyEvent>(OnRemovedFromBody);
|
||||||
SubscribeLocalEvent<BrainComponent, RemovedFromPartEvent>((uid, _, args) => HandleMind(uid, (args.Old).Owner));
|
SubscribeLocalEvent<BrainComponent, RemovedFromPartEvent>((uid, _, args) => HandleMind(uid, args.Old));
|
||||||
SubscribeLocalEvent<BrainComponent, RemovedFromPartInBodyEvent>((uid, _, args) => HandleMind((args.OldBody).Owner, uid));
|
SubscribeLocalEvent<BrainComponent, RemovedFromPartInBodyEvent>((uid, _, args) => HandleMind(args.OldBody, uid));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRemovedFromBody(EntityUid uid, BrainComponent component, RemovedFromBodyEvent args)
|
private void OnRemovedFromBody(EntityUid uid, BrainComponent component, RemovedFromBodyEvent args)
|
||||||
{
|
{
|
||||||
// This one needs to be special, okay?
|
// 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;
|
return;
|
||||||
|
|
||||||
HandleMind((mech.Part!).Owner, (args.Old).Owner);
|
HandleMind(parent, args.Old);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleMind(EntityUid newEntity, EntityUid oldEntity)
|
private void HandleMind(EntityUid newEntity, EntityUid oldEntity)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using Content.Server.Body.Components;
|
|||||||
using Content.Server.Chemistry.Components.SolutionManager;
|
using Content.Server.Chemistry.Components.SolutionManager;
|
||||||
using Content.Server.Chemistry.EntitySystems;
|
using Content.Server.Chemistry.EntitySystems;
|
||||||
using Content.Shared.Administration.Logs;
|
using Content.Shared.Administration.Logs;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Organ;
|
||||||
using Content.Shared.Chemistry.Components;
|
using Content.Shared.Chemistry.Components;
|
||||||
using Content.Shared.Chemistry.Reagent;
|
using Content.Shared.Chemistry.Reagent;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
@@ -18,6 +18,7 @@ namespace Content.Server.Body.Systems
|
|||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public sealed class MetabolizerSystem : EntitySystem
|
public sealed class MetabolizerSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
@@ -37,30 +38,27 @@ namespace Content.Server.Body.Systems
|
|||||||
{
|
{
|
||||||
_solutionContainerSystem.EnsureSolution(uid, component.SolutionName);
|
_solutionContainerSystem.EnsureSolution(uid, component.SolutionName);
|
||||||
}
|
}
|
||||||
else
|
else if (CompOrNull<OrganComponent>(uid)?.Body is { } body)
|
||||||
{
|
{
|
||||||
if (EntityManager.TryGetComponent<MechanismComponent>(uid, out var mech))
|
_solutionContainerSystem.EnsureSolution(body, component.SolutionName);
|
||||||
{
|
|
||||||
if (mech.Body != null)
|
|
||||||
{
|
|
||||||
_solutionContainerSystem.EnsureSolution((mech.Body).Owner, component.SolutionName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnApplyMetabolicMultiplier(EntityUid uid, MetabolizerComponent component, ApplyMetabolicMultiplierEvent args)
|
private void OnApplyMetabolicMultiplier(EntityUid uid, MetabolizerComponent component,
|
||||||
|
ApplyMetabolicMultiplierEvent args)
|
||||||
{
|
{
|
||||||
if (args.Apply)
|
if (args.Apply)
|
||||||
{
|
{
|
||||||
component.UpdateFrequency *= args.Multiplier;
|
component.UpdateFrequency *= args.Multiplier;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
component.UpdateFrequency /= args.Multiplier;
|
component.UpdateFrequency /= args.Multiplier;
|
||||||
// Reset the accumulator properly
|
// Reset the accumulator properly
|
||||||
if (component.AccumulatedFrametime >= component.UpdateFrequency)
|
if (component.AccumulatedFrametime >= component.UpdateFrequency)
|
||||||
component.AccumulatedFrametime = component.UpdateFrequency;
|
component.AccumulatedFrametime = component.UpdateFrequency;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
base.Update(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))
|
if (!Resolve(uid, ref meta))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Resolve(uid, ref mech, false);
|
Resolve(uid, ref organ, false);
|
||||||
|
|
||||||
// First step is get the solution we actually care about
|
// First step is get the solution we actually care about
|
||||||
Solution? solution = null;
|
Solution? solution = null;
|
||||||
EntityUid? solutionEntityUid = null;
|
EntityUid? solutionEntityUid = null;
|
||||||
EntityUid? bodyEntityUid = mech?.Body?.Owner;
|
|
||||||
|
|
||||||
SolutionContainerManagerComponent? manager = null;
|
SolutionContainerManagerComponent? manager = null;
|
||||||
|
|
||||||
if (meta.SolutionOnBody)
|
if (meta.SolutionOnBody)
|
||||||
{
|
{
|
||||||
if (mech != null)
|
if (organ?.Body is { } body)
|
||||||
{
|
{
|
||||||
var body = mech.Body;
|
if (!Resolve(body, ref manager, false))
|
||||||
|
return;
|
||||||
if (body != null)
|
_solutionContainerSystem.TryGetSolution(body, meta.SolutionName, out solution, manager);
|
||||||
{
|
solutionEntityUid = body;
|
||||||
if (!Resolve((body).Owner, ref manager, false))
|
|
||||||
return;
|
|
||||||
_solutionContainerSystem.TryGetSolution((body).Owner, meta.SolutionName, out solution, manager);
|
|
||||||
solutionEntityUid = body.Owner;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -133,7 +125,8 @@ namespace Content.Server.Body.Systems
|
|||||||
if (proto.Metabolisms == null)
|
if (proto.Metabolisms == null)
|
||||||
{
|
{
|
||||||
if (meta.RemoveEmpty)
|
if (meta.RemoveEmpty)
|
||||||
_solutionContainerSystem.TryRemoveReagent(solutionEntityUid.Value, solution, reagent.ReagentId, FixedPoint2.New(1));
|
_solutionContainerSystem.TryRemoveReagent(solutionEntityUid.Value, solution, reagent.ReagentId,
|
||||||
|
FixedPoint2.New(1));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +163,7 @@ namespace Content.Server.Body.Systems
|
|||||||
continue;
|
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,
|
var args = new ReagentEffectArgs(actualEntity, (meta).Owner, solution, proto, mostToRemove,
|
||||||
EntityManager, null, entry);
|
EntityManager, null, entry);
|
||||||
|
|
||||||
@@ -192,16 +185,20 @@ namespace Content.Server.Body.Systems
|
|||||||
|
|
||||||
// remove a certain amount of reagent
|
// remove a certain amount of reagent
|
||||||
if (mostToRemove > FixedPoint2.Zero)
|
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
|
public sealed class ApplyMetabolicMultiplierEvent : EntityEventArgs
|
||||||
{
|
{
|
||||||
// The entity whose metabolism is being modified
|
// The entity whose metabolism is being modified
|
||||||
public EntityUid Uid;
|
public EntityUid Uid;
|
||||||
|
|
||||||
// What the metabolism's update rate will be multiplied by
|
// What the metabolism's update rate will be multiplied by
|
||||||
public float Multiplier;
|
public float Multiplier;
|
||||||
|
|
||||||
// Apply this multiplier or ignore / reset it?
|
// Apply this multiplier or ignore / reset it?
|
||||||
public bool Apply;
|
public bool Apply;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ using Content.Shared.Atmos;
|
|||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.MobState.Components;
|
|
||||||
using Content.Shared.MobState.EntitySystems;
|
using Content.Shared.MobState.EntitySystems;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
@@ -42,8 +41,7 @@ namespace Content.Server.Body.Systems
|
|||||||
{
|
{
|
||||||
base.Update(frameTime);
|
base.Update(frameTime);
|
||||||
|
|
||||||
foreach (var (respirator, body) in
|
foreach (var (respirator, body) in EntityManager.EntityQuery<RespiratorComponent, BodyComponent>())
|
||||||
EntityManager.EntityQuery<RespiratorComponent, SharedBodyComponent>())
|
|
||||||
{
|
{
|
||||||
var uid = respirator.Owner;
|
var uid = respirator.Owner;
|
||||||
|
|
||||||
@@ -91,12 +89,13 @@ namespace Content.Server.Body.Systems
|
|||||||
respirator.SuffocationCycles = 0;
|
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))
|
if (!Resolve(uid, ref body, false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var organs = _bodySystem.GetComponentsOnMechanisms<LungComponent>(uid, body);
|
var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(uid, body);
|
||||||
|
|
||||||
// Inhale gas
|
// Inhale gas
|
||||||
var ev = new InhaleLocationEvent();
|
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))
|
if (!Resolve(uid, ref body, false))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var organs = _bodySystem.GetComponentsOnMechanisms<LungComponent>(uid, body);
|
var organs = _bodySystem.GetBodyOrganComponents<LungComponent>(uid, body);
|
||||||
|
|
||||||
// exhale gas
|
// exhale gas
|
||||||
|
|
||||||
@@ -187,7 +186,8 @@ namespace Content.Server.Body.Systems
|
|||||||
Math.Clamp(respirator.Saturation, respirator.MinSaturation, respirator.MaxSaturation);
|
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)
|
if (args.Apply)
|
||||||
{
|
{
|
||||||
@@ -197,6 +197,7 @@ namespace Content.Server.Body.Systems
|
|||||||
component.MinSaturation *= args.Multiplier;
|
component.MinSaturation *= args.Multiplier;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This way we don't have to worry about it breaking if the stasis bed component is destroyed
|
// This way we don't have to worry about it breaking if the stasis bed component is destroyed
|
||||||
component.CycleDelay /= args.Multiplier;
|
component.CycleDelay /= args.Multiplier;
|
||||||
component.Saturation /= args.Multiplier;
|
component.Saturation /= args.Multiplier;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Content.Server.Body.Components;
|
using Content.Server.Body.Components;
|
||||||
using Content.Server.Chemistry.Components.SolutionManager;
|
using Content.Server.Chemistry.Components.SolutionManager;
|
||||||
using Content.Server.Chemistry.EntitySystems;
|
using Content.Server.Chemistry.EntitySystems;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Organ;
|
||||||
using Content.Shared.Chemistry.Components;
|
using Content.Shared.Chemistry.Components;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
@@ -9,6 +9,7 @@ namespace Content.Server.Body.Systems
|
|||||||
{
|
{
|
||||||
public sealed class StomachSystem : EntitySystem
|
public sealed class StomachSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||||
|
|
||||||
public const string DefaultSolutionName = "stomach";
|
public const string DefaultSolutionName = "stomach";
|
||||||
@@ -21,12 +22,8 @@ namespace Content.Server.Body.Systems
|
|||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
foreach (var (stomach, mech, sol)
|
foreach (var (stomach, organ, sol)in EntityManager.EntityQuery<StomachComponent, OrganComponent, SolutionContainerManagerComponent>())
|
||||||
in EntityManager.EntityQuery<StomachComponent, MechanismComponent, SolutionContainerManagerComponent>(false))
|
|
||||||
{
|
{
|
||||||
if (mech.Body == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
stomach.AccumulatedFrameTime += frameTime;
|
stomach.AccumulatedFrameTime += frameTime;
|
||||||
|
|
||||||
if (stomach.AccumulatedFrameTime < stomach.UpdateInterval)
|
if (stomach.AccumulatedFrameTime < stomach.UpdateInterval)
|
||||||
@@ -35,12 +32,11 @@ namespace Content.Server.Body.Systems
|
|||||||
stomach.AccumulatedFrameTime -= stomach.UpdateInterval;
|
stomach.AccumulatedFrameTime -= stomach.UpdateInterval;
|
||||||
|
|
||||||
// Get our solutions
|
// Get our solutions
|
||||||
if (!_solutionContainerSystem.TryGetSolution((stomach).Owner, DefaultSolutionName,
|
if (!_solutionContainerSystem.TryGetSolution(stomach.Owner, DefaultSolutionName,
|
||||||
out var stomachSolution, sol))
|
out var stomachSolution, sol))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!_solutionContainerSystem.TryGetSolution((mech.Body).Owner, stomach.BodySolutionName,
|
if (organ.Body is not { } body || !_solutionContainerSystem.TryGetSolution(body, stomach.BodySolutionName, out var bodySolution))
|
||||||
out var bodySolution))
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var transferSolution = new Solution();
|
var transferSolution = new Solution();
|
||||||
@@ -71,23 +67,25 @@ namespace Content.Server.Body.Systems
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Transfer everything to the body solution!
|
// 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)
|
private void OnApplyMetabolicMultiplier(EntityUid uid, StomachComponent component,
|
||||||
{
|
ApplyMetabolicMultiplierEvent args)
|
||||||
if (args.Apply)
|
|
||||||
{
|
{
|
||||||
component.UpdateInterval *= args.Multiplier;
|
if (args.Apply)
|
||||||
return;
|
{
|
||||||
|
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)
|
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,
|
public bool CanTransferSolution(EntityUid uid, Solution solution,
|
||||||
SolutionContainerManagerComponent? solutions=null)
|
SolutionContainerManagerComponent? solutions = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref solutions, false))
|
if (!Resolve(uid, ref solutions, false))
|
||||||
return false;
|
return false;
|
||||||
@@ -112,8 +110,8 @@ namespace Content.Server.Body.Systems
|
|||||||
}
|
}
|
||||||
|
|
||||||
public bool TryTransferSolution(EntityUid uid, Solution solution,
|
public bool TryTransferSolution(EntityUid uid, Solution solution,
|
||||||
StomachComponent? stomach=null,
|
StomachComponent? stomach = null,
|
||||||
SolutionContainerManagerComponent? solutions=null)
|
SolutionContainerManagerComponent? solutions = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref stomach, ref solutions, false))
|
if (!Resolve(uid, ref stomach, ref solutions, false))
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Content.Server.Administration;
|
using Content.Server.Administration;
|
||||||
using Content.Server.Body.Components;
|
using Content.Server.Body.Systems;
|
||||||
using Content.Server.Cargo.Components;
|
using Content.Server.Cargo.Components;
|
||||||
using Content.Shared.Materials;
|
|
||||||
using Content.Server.Stack;
|
using Content.Server.Stack;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
|
using Content.Shared.Body.Components;
|
||||||
|
using Content.Shared.Materials;
|
||||||
using Content.Shared.MobState.Components;
|
using Content.Shared.MobState.Components;
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
@@ -22,6 +23,8 @@ public sealed class PricingSystem : EntitySystem
|
|||||||
[Dependency] private readonly IConsoleHost _consoleHost = default!;
|
[Dependency] private readonly IConsoleHost _consoleHost = default!;
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
|
||||||
|
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -84,8 +87,8 @@ public sealed class PricingSystem : EntitySystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var partList = body.Slots.ToList();
|
var partList = _bodySystem.GetBodyAllSlots(uid, body).ToList();
|
||||||
var totalPartsPresent = partList.Sum(x => x.Part != null ? 1 : 0);
|
var totalPartsPresent = partList.Sum(x => x.Child != null ? 1 : 0);
|
||||||
var totalParts = partList.Count;
|
var totalParts = partList.Count;
|
||||||
|
|
||||||
var partRatio = totalPartsPresent / (double) totalParts;
|
var partRatio = totalPartsPresent / (double) totalParts;
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
|
using Content.Server.Body.Systems;
|
||||||
using Content.Server.Climbing.Components;
|
using Content.Server.Climbing.Components;
|
||||||
using Content.Server.DoAfter;
|
using Content.Server.DoAfter;
|
||||||
using Content.Server.Interaction;
|
using Content.Server.Interaction;
|
||||||
using Content.Server.Interaction.Components;
|
|
||||||
using Content.Server.Popups;
|
using Content.Server.Popups;
|
||||||
using Content.Server.Stunnable;
|
using Content.Server.Stunnable;
|
||||||
using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems;
|
|
||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
using Content.Shared.Body.Part;
|
using Content.Shared.Body.Part;
|
||||||
@@ -15,7 +14,6 @@ using Content.Shared.Damage;
|
|||||||
using Content.Shared.DragDrop;
|
using Content.Shared.DragDrop;
|
||||||
using Content.Shared.GameTicking;
|
using Content.Shared.GameTicking;
|
||||||
using Content.Shared.IdentityManagement;
|
using Content.Shared.IdentityManagement;
|
||||||
using Content.Shared.Interaction;
|
|
||||||
using Content.Shared.Physics;
|
using Content.Shared.Physics;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
@@ -30,7 +28,6 @@ using Robust.Shared.Physics.Dynamics;
|
|||||||
using Robust.Shared.Physics.Events;
|
using Robust.Shared.Physics.Events;
|
||||||
using Robust.Shared.Physics.Systems;
|
using Robust.Shared.Physics.Systems;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using SharpZstd.Interop;
|
|
||||||
|
|
||||||
namespace Content.Server.Climbing;
|
namespace Content.Server.Climbing;
|
||||||
|
|
||||||
@@ -39,6 +36,7 @@ public sealed class ClimbSystem : SharedClimbSystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!;
|
||||||
|
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||||
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
||||||
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
||||||
[Dependency] private readonly FixtureSystem _fixtureSystem = default!;
|
[Dependency] private readonly FixtureSystem _fixtureSystem = default!;
|
||||||
@@ -286,9 +284,9 @@ public sealed class ClimbSystem : SharedClimbSystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!HasComp<ClimbingComponent>(user)
|
if (!HasComp<ClimbingComponent>(user)
|
||||||
|| !TryComp(user, out SharedBodyComponent? body)
|
|| !TryComp(user, out BodyComponent? body)
|
||||||
|| !body.HasPartOfType(BodyPartType.Leg)
|
|| !_bodySystem.BodyHasChildOfType(user, BodyPartType.Leg, body)
|
||||||
|| !body.HasPartOfType(BodyPartType.Foot))
|
|| !_bodySystem.BodyHasChildOfType(user, BodyPartType.Foot, body))
|
||||||
{
|
{
|
||||||
reason = Loc.GetString("comp-climbable-cant-climb");
|
reason = Loc.GetString("comp-climbable-cant-climb");
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
|
using Content.Server.Body.Systems;
|
||||||
|
using Content.Server.Chemistry.EntitySystems;
|
||||||
using Content.Server.Construction;
|
using Content.Server.Construction;
|
||||||
using Content.Server.Destructible.Thresholds;
|
using Content.Server.Destructible.Thresholds;
|
||||||
using Content.Server.Destructible.Thresholds.Behaviors;
|
using Content.Server.Destructible.Thresholds.Behaviors;
|
||||||
using Content.Server.Destructible.Thresholds.Triggers;
|
using Content.Server.Destructible.Thresholds.Triggers;
|
||||||
using Content.Server.Explosion.EntitySystems;
|
using Content.Server.Explosion.EntitySystems;
|
||||||
|
using Content.Server.Fluids.EntitySystems;
|
||||||
using Content.Server.Stack;
|
using Content.Server.Stack;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Destructible;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Content.Shared.Destructible;
|
|
||||||
using Content.Server.Chemistry.EntitySystems;
|
|
||||||
using Content.Server.Fluids.EntitySystems;
|
|
||||||
|
|
||||||
namespace Content.Server.Destructible
|
namespace Content.Server.Destructible
|
||||||
{
|
{
|
||||||
@@ -23,6 +24,7 @@ namespace Content.Server.Destructible
|
|||||||
public new IEntityManager EntityManager => base.EntityManager;
|
public new IEntityManager EntityManager => base.EntityManager;
|
||||||
|
|
||||||
[Dependency] public readonly AudioSystem AudioSystem = default!;
|
[Dependency] public readonly AudioSystem AudioSystem = default!;
|
||||||
|
[Dependency] public readonly BodySystem BodySystem = default!;
|
||||||
[Dependency] public readonly ConstructionSystem ConstructionSystem = default!;
|
[Dependency] public readonly ConstructionSystem ConstructionSystem = default!;
|
||||||
[Dependency] public readonly ExplosionSystem ExplosionSystem = default!;
|
[Dependency] public readonly ExplosionSystem ExplosionSystem = default!;
|
||||||
[Dependency] public readonly StackSystem StackSystem = default!;
|
[Dependency] public readonly StackSystem StackSystem = default!;
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
|
|||||||
|
|
||||||
public void Execute(EntityUid owner, DestructibleSystem system)
|
public void Execute(EntityUid owner, DestructibleSystem system)
|
||||||
{
|
{
|
||||||
if (system.EntityManager.TryGetComponent(owner, out SharedBodyComponent? body))
|
if (system.EntityManager.TryGetComponent(owner, out BodyComponent? body))
|
||||||
{
|
{
|
||||||
body.Gib(_recursive);
|
system.BodySystem.GibBody(owner, _recursive, body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Content.Server.Atmos;
|
using Content.Server.Atmos;
|
||||||
using Content.Server.Disposal.Tube.Components;
|
using Content.Server.Disposal.Tube.Components;
|
||||||
using Content.Shared.Atmos;
|
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
using Content.Shared.Item;
|
using Content.Shared.Item;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
@@ -74,7 +73,7 @@ namespace Content.Server.Disposal.Unit.Components
|
|||||||
}
|
}
|
||||||
|
|
||||||
return _entMan.HasComponent<ItemComponent>(entity) ||
|
return _entMan.HasComponent<ItemComponent>(entity) ||
|
||||||
_entMan.HasComponent<SharedBodyComponent>(entity);
|
_entMan.HasComponent<BodyComponent>(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool TryInsert(EntityUid entity)
|
public bool TryInsert(EntityUid entity)
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
using Content.Shared.Drone;
|
using Content.Server.Body.Systems;
|
||||||
using Content.Server.Drone.Components;
|
using Content.Server.Drone.Components;
|
||||||
using Content.Shared.MobState;
|
|
||||||
using Content.Shared.MobState.Components;
|
|
||||||
using Content.Shared.Interaction.Events;
|
|
||||||
using Content.Shared.Interaction.Components;
|
|
||||||
using Content.Shared.Examine;
|
|
||||||
using Content.Shared.Tag;
|
|
||||||
using Content.Shared.Throwing;
|
|
||||||
using Content.Shared.Item;
|
|
||||||
using Content.Shared.Emoting;
|
|
||||||
using Content.Shared.Body.Components;
|
|
||||||
using Content.Shared.IdentityManagement;
|
|
||||||
using Content.Shared.Popups;
|
|
||||||
using Content.Server.Popups;
|
|
||||||
using Content.Server.Mind.Components;
|
|
||||||
using Content.Server.Ghost.Components;
|
using Content.Server.Ghost.Components;
|
||||||
using Content.Server.Ghost.Roles.Components;
|
using Content.Server.Ghost.Roles.Components;
|
||||||
|
using Content.Server.Mind.Components;
|
||||||
|
using Content.Server.Popups;
|
||||||
using Content.Server.Tools.Innate;
|
using Content.Server.Tools.Innate;
|
||||||
using Content.Server.UserInterface;
|
using Content.Server.UserInterface;
|
||||||
|
using Content.Shared.Body.Components;
|
||||||
|
using Content.Shared.Drone;
|
||||||
|
using Content.Shared.Emoting;
|
||||||
|
using Content.Shared.Examine;
|
||||||
|
using Content.Shared.IdentityManagement;
|
||||||
|
using Content.Shared.Interaction.Components;
|
||||||
|
using Content.Shared.Interaction.Events;
|
||||||
|
using Content.Shared.Item;
|
||||||
|
using Content.Shared.MobState;
|
||||||
|
using Content.Shared.MobState.Components;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Tag;
|
||||||
|
using Content.Shared.Throwing;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
@@ -25,6 +26,7 @@ namespace Content.Server.Drone
|
|||||||
{
|
{
|
||||||
public sealed class DroneSystem : SharedDroneSystem
|
public sealed class DroneSystem : SharedDroneSystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||||
[Dependency] private readonly TagSystem _tagSystem = default!;
|
[Dependency] private readonly TagSystem _tagSystem = default!;
|
||||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||||
@@ -83,8 +85,8 @@ namespace Content.Server.Drone
|
|||||||
if (TryComp<InnateToolComponent>(uid, out var innate))
|
if (TryComp<InnateToolComponent>(uid, out var innate))
|
||||||
_innateToolSystem.Cleanup(uid, innate);
|
_innateToolSystem.Cleanup(uid, innate);
|
||||||
|
|
||||||
if (TryComp<SharedBodyComponent>(uid, out var body))
|
if (TryComp<BodyComponent>(uid, out var body))
|
||||||
body.Gib();
|
_bodySystem.GibBody(uid, body: body);
|
||||||
Del(uid);
|
Del(uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
using Content.Server.Body.Components;
|
using Content.Server.Body.Systems;
|
||||||
using Content.Server.Popups;
|
using Content.Server.Popups;
|
||||||
|
using Content.Shared.Body.Components;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
using Robust.Shared.Physics.Dynamics;
|
|
||||||
using Robust.Shared.Physics.Events;
|
using Robust.Shared.Physics.Events;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
@@ -16,6 +16,8 @@ public sealed class ImmovableRodSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly IMapManager _map = default!;
|
[Dependency] private readonly IMapManager _map = default!;
|
||||||
|
|
||||||
|
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||||
[Dependency] private readonly PopupSystem _popup = default!;
|
[Dependency] private readonly PopupSystem _popup = default!;
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
@@ -96,7 +98,7 @@ public sealed class ImmovableRodSystem : EntitySystem
|
|||||||
|
|
||||||
_popup.PopupEntity(Loc.GetString("immovable-rod-penetrated-mob", ("rod", uid), ("mob", ent)), uid,
|
_popup.PopupEntity(Loc.GetString("immovable-rod-penetrated-mob", ("rod", uid), ("mob", ent)), uid,
|
||||||
Filter.Pvs(uid), PopupType.LargeCaution);
|
Filter.Pvs(uid), PopupType.LargeCaution);
|
||||||
body.Gib();
|
_bodySystem.GibBody(ent, body: body);
|
||||||
}
|
}
|
||||||
|
|
||||||
QueueDel(ent);
|
QueueDel(ent);
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Content.Server.Body.Systems;
|
||||||
using Content.Server.Chemistry.Components.SolutionManager;
|
using Content.Server.Chemistry.Components.SolutionManager;
|
||||||
using Content.Server.Chemistry.EntitySystems;
|
using Content.Server.Chemistry.EntitySystems;
|
||||||
using Content.Server.Construction;
|
using Content.Server.Construction;
|
||||||
using Content.Server.Kitchen.Components;
|
|
||||||
using Content.Shared.Destructible;
|
|
||||||
using Content.Shared.Interaction;
|
|
||||||
using Content.Shared.Item;
|
|
||||||
using Content.Shared.Kitchen.Components;
|
|
||||||
using Robust.Shared.Player;
|
|
||||||
using Content.Shared.Interaction.Events;
|
|
||||||
using Content.Shared.Body.Components;
|
|
||||||
using Content.Shared.Body.Part;
|
|
||||||
using Content.Server.Hands.Systems;
|
using Content.Server.Hands.Systems;
|
||||||
|
using Content.Server.Kitchen.Components;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Temperature.Components;
|
using Content.Server.Temperature.Components;
|
||||||
using Content.Server.Temperature.Systems;
|
using Content.Server.Temperature.Systems;
|
||||||
|
using Content.Shared.Body.Components;
|
||||||
|
using Content.Shared.Body.Part;
|
||||||
|
using Content.Shared.Destructible;
|
||||||
using Content.Shared.FixedPoint;
|
using Content.Shared.FixedPoint;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Interaction.Events;
|
||||||
|
using Content.Shared.Item;
|
||||||
using Content.Shared.Kitchen;
|
using Content.Shared.Kitchen;
|
||||||
|
using Content.Shared.Kitchen.Components;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Power;
|
using Content.Shared.Power;
|
||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
@@ -24,11 +24,13 @@ using Robust.Server.Containers;
|
|||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
|
||||||
namespace Content.Server.Kitchen.EntitySystems
|
namespace Content.Server.Kitchen.EntitySystems
|
||||||
{
|
{
|
||||||
public sealed class MicrowaveSystem : EntitySystem
|
public sealed class MicrowaveSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||||
[Dependency] private readonly ContainerSystem _container = default!;
|
[Dependency] private readonly ContainerSystem _container = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||||
[Dependency] private readonly RecipeManager _recipeManager = default!;
|
[Dependency] private readonly RecipeManager _recipeManager = default!;
|
||||||
@@ -182,29 +184,19 @@ namespace Content.Server.Kitchen.EntitySystems
|
|||||||
var victim = args.Victim;
|
var victim = args.Victim;
|
||||||
var headCount = 0;
|
var headCount = 0;
|
||||||
|
|
||||||
if (TryComp<SharedBodyComponent>(victim, out var body))
|
if (TryComp<BodyComponent>(victim, out var body))
|
||||||
{
|
{
|
||||||
var headSlots = body.GetSlotsOfType(BodyPartType.Head);
|
var headSlots = _bodySystem.GetBodyChildrenOfType(victim, BodyPartType.Head, body);
|
||||||
|
|
||||||
foreach (var slot in headSlots)
|
foreach (var part in headSlots)
|
||||||
{
|
{
|
||||||
var part = slot.Part;
|
if (!_bodySystem.OrphanPart(part.Id, part.Component))
|
||||||
|
|
||||||
if (part == null ||
|
|
||||||
!body.TryDropPart(slot, out var dropped))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var droppedPart in dropped.Values)
|
component.Storage.Insert(part.Id);
|
||||||
{
|
headCount++;
|
||||||
if (droppedPart.PartType != BodyPartType.Head)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
component.Storage.Insert(droppedPart.Owner);
|
|
||||||
headCount++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using Content.Server.DoAfter;
|
using Content.Server.Body.Systems;
|
||||||
|
using Content.Server.DoAfter;
|
||||||
using Content.Server.Kitchen.Components;
|
using Content.Server.Kitchen.Components;
|
||||||
using Content.Server.MobState;
|
using Content.Server.MobState;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
@@ -16,6 +17,7 @@ namespace Content.Server.Kitchen.EntitySystems;
|
|||||||
|
|
||||||
public sealed class SharpSystem : EntitySystem
|
public sealed class SharpSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||||
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||||
[Dependency] private readonly ContainerSystem _containerSystem = default!;
|
[Dependency] private readonly ContainerSystem _containerSystem = default!;
|
||||||
@@ -95,7 +97,7 @@ public sealed class SharpSystem : EntitySystem
|
|||||||
popupEnt = Spawn(proto, coords.Offset(_robustRandom.NextVector2(0.25f)));
|
popupEnt = Spawn(proto, coords.Offset(_robustRandom.NextVector2(0.25f)));
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasBody = TryComp<SharedBodyComponent>(ev.Entity, out var body);
|
var hasBody = TryComp<BodyComponent>(ev.Entity, out var body);
|
||||||
|
|
||||||
// only show a big popup when butchering living things.
|
// only show a big popup when butchering living things.
|
||||||
var popupType = PopupType.Small;
|
var popupType = PopupType.Small;
|
||||||
@@ -107,7 +109,7 @@ public sealed class SharpSystem : EntitySystem
|
|||||||
|
|
||||||
if (hasBody)
|
if (hasBody)
|
||||||
{
|
{
|
||||||
body!.Gib();
|
_bodySystem.GibBody(body!.Owner, body: body);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Content.Server.Body.Components;
|
using Content.Server.Body.Components;
|
||||||
|
using Content.Server.Body.Systems;
|
||||||
using Content.Server.Coordinates.Helpers;
|
using Content.Server.Coordinates.Helpers;
|
||||||
using Content.Server.Decals;
|
|
||||||
using Content.Server.DoAfter;
|
using Content.Server.DoAfter;
|
||||||
using Content.Server.Doors.Components;
|
using Content.Server.Doors.Components;
|
||||||
using Content.Server.Magic.Events;
|
using Content.Server.Magic.Events;
|
||||||
using Content.Server.Popups;
|
|
||||||
using Content.Server.Spawners.Components;
|
|
||||||
using Content.Server.Weapons.Ranged.Systems;
|
using Content.Server.Weapons.Ranged.Systems;
|
||||||
using Content.Shared.Actions;
|
using Content.Shared.Actions;
|
||||||
using Content.Shared.Actions.ActionTypes;
|
using Content.Shared.Actions.ActionTypes;
|
||||||
@@ -35,6 +33,7 @@ public sealed class MagicSystem : EntitySystem
|
|||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||||
[Dependency] private readonly SharedDoorSystem _doorSystem = default!;
|
[Dependency] private readonly SharedDoorSystem _doorSystem = default!;
|
||||||
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
||||||
@@ -281,7 +280,7 @@ public sealed class MagicSystem : EntitySystem
|
|||||||
if (!TryComp<BodyComponent>(ev.Target, out var body))
|
if (!TryComp<BodyComponent>(ev.Target, out var body))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var ents = body.Gib(true);
|
var ents = _bodySystem.GibBody(ev.Target, true, body);
|
||||||
|
|
||||||
if (!ev.DeleteNonBrainParts)
|
if (!ev.DeleteNonBrainParts)
|
||||||
return;
|
return;
|
||||||
@@ -289,8 +288,7 @@ public sealed class MagicSystem : EntitySystem
|
|||||||
foreach (var part in ents)
|
foreach (var part in ents)
|
||||||
{
|
{
|
||||||
// just leaves a brain and clothes
|
// just leaves a brain and clothes
|
||||||
if ((HasComp<BodyPartComponent>(part) || HasComp<MechanismComponent>(part))
|
if (HasComp<BodyComponent>(part) && !HasComp<BrainComponent>(part))
|
||||||
&& !HasComp<BrainComponent>(part))
|
|
||||||
{
|
{
|
||||||
QueueDel(part);
|
QueueDel(part);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
using Content.Server.Nutrition.Components;
|
|
||||||
using Content.Server.Stunnable;
|
|
||||||
using Content.Server.Nutrition.EntitySystems;
|
|
||||||
using Content.Server.Body.Components;
|
using Content.Server.Body.Components;
|
||||||
using Content.Server.Fluids.Components;
|
|
||||||
using Content.Server.Chemistry.EntitySystems;
|
|
||||||
using Content.Server.Popups;
|
|
||||||
using Content.Server.Body.Systems;
|
using Content.Server.Body.Systems;
|
||||||
using Content.Shared.StatusEffect;
|
using Content.Server.Chemistry.EntitySystems;
|
||||||
|
using Content.Server.Fluids.Components;
|
||||||
|
using Content.Server.Nutrition.Components;
|
||||||
|
using Content.Server.Nutrition.EntitySystems;
|
||||||
|
using Content.Server.Popups;
|
||||||
|
using Content.Server.Stunnable;
|
||||||
using Content.Shared.Audio;
|
using Content.Shared.Audio;
|
||||||
|
using Content.Shared.IdentityManagement;
|
||||||
|
using Content.Shared.StatusEffect;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Content.Shared.IdentityManagement;
|
|
||||||
|
|
||||||
namespace Content.Server.Medical
|
namespace Content.Server.Medical
|
||||||
{
|
{
|
||||||
@@ -29,7 +29,7 @@ namespace Content.Server.Medical
|
|||||||
public void Vomit(EntityUid uid, float thirstAdded = -40f, float hungerAdded = -40f)
|
public void Vomit(EntityUid uid, float thirstAdded = -40f, float hungerAdded = -40f)
|
||||||
{
|
{
|
||||||
/// Main requirement: You have a stomach
|
/// Main requirement: You have a stomach
|
||||||
var stomachList = _bodySystem.GetComponentsOnMechanisms<StomachComponent>(uid);
|
var stomachList = _bodySystem.GetBodyOrganComponents<StomachComponent>(uid);
|
||||||
if (stomachList.Count == 0)
|
if (stomachList.Count == 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
|
using Content.Server.Body.Systems;
|
||||||
using Content.Server.Morgue.Components;
|
using Content.Server.Morgue.Components;
|
||||||
using Content.Shared.Standing;
|
|
||||||
using Content.Server.Storage.Components;
|
using Content.Server.Storage.Components;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
|
using Content.Shared.Standing;
|
||||||
|
|
||||||
namespace Content.Server.Morgue;
|
namespace Content.Server.Morgue;
|
||||||
|
|
||||||
public sealed class EntityStorageLayingDownOverrideSystem : EntitySystem
|
public sealed class EntityStorageLayingDownOverrideSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly StandingStateSystem _standing = default!;
|
[Dependency] private readonly StandingStateSystem _standing = default!;
|
||||||
|
[Dependency] private readonly BodySystem _body = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
@@ -16,10 +18,11 @@ public sealed class EntityStorageLayingDownOverrideSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<EntityStorageLayingDownOverrideComponent, StorageBeforeCloseEvent>(OnBeforeClose);
|
SubscribeLocalEvent<EntityStorageLayingDownOverrideComponent, StorageBeforeCloseEvent>(OnBeforeClose);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnBeforeClose(EntityUid uid, EntityStorageLayingDownOverrideComponent component, StorageBeforeCloseEvent args)
|
private void OnBeforeClose(EntityUid uid, EntityStorageLayingDownOverrideComponent component,
|
||||||
|
StorageBeforeCloseEvent args)
|
||||||
{
|
{
|
||||||
foreach (var ent in args.Contents)
|
foreach (var ent in args.Contents)
|
||||||
if (HasComp<SharedBodyComponent>(ent) && !_standing.IsDown(ent))
|
if (HasComp<BodyComponent>(ent) && !_standing.IsDown(ent))
|
||||||
args.Contents.Remove(ent);
|
args.Contents.Remove(ent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using Content.Server.Morgue.Components;
|
using Content.Server.Morgue.Components;
|
||||||
using Content.Shared.Morgue;
|
|
||||||
using Content.Shared.Examine;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Content.Server.Storage.Components;
|
using Content.Server.Storage.Components;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
|
using Content.Shared.Examine;
|
||||||
|
using Content.Shared.Morgue;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
|
||||||
namespace Content.Server.Morgue;
|
namespace Content.Server.Morgue;
|
||||||
|
|
||||||
@@ -61,7 +61,7 @@ public sealed class MorgueSystem : EntitySystem
|
|||||||
|
|
||||||
foreach (var ent in storage.Contents.ContainedEntities)
|
foreach (var ent in storage.Contents.ContainedEntities)
|
||||||
{
|
{
|
||||||
if (!hasMob && HasComp<SharedBodyComponent>(ent))
|
if (!hasMob && HasComp<BodyComponent>(ent))
|
||||||
hasMob = true;
|
hasMob = true;
|
||||||
|
|
||||||
if (HasComp<ActorComponent?>(ent))
|
if (HasComp<ActorComponent?>(ent))
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
SubscribeLocalEvent<DrinkComponent, GetVerbsEvent<AlternativeVerb>>(AddDrinkVerb);
|
SubscribeLocalEvent<DrinkComponent, GetVerbsEvent<AlternativeVerb>>(AddDrinkVerb);
|
||||||
SubscribeLocalEvent<DrinkComponent, ExaminedEvent>(OnExamined);
|
SubscribeLocalEvent<DrinkComponent, ExaminedEvent>(OnExamined);
|
||||||
SubscribeLocalEvent<DrinkComponent, SolutionTransferAttemptEvent>(OnTransferAttempt);
|
SubscribeLocalEvent<DrinkComponent, SolutionTransferAttemptEvent>(OnTransferAttempt);
|
||||||
SubscribeLocalEvent<SharedBodyComponent, DrinkEvent>(OnDrink);
|
SubscribeLocalEvent<BodyComponent, DrinkEvent>(OnDrink);
|
||||||
SubscribeLocalEvent<DrinkCancelledEvent>(OnDrinkCancelled);
|
SubscribeLocalEvent<DrinkCancelledEvent>(OnDrinkCancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,7 +225,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!EntityManager.HasComponent<SharedBodyComponent>(target))
|
if (!EntityManager.HasComponent<BodyComponent>(target))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!drink.Opened)
|
if (!drink.Opened)
|
||||||
@@ -286,7 +286,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Raised directed at a victim when someone has force fed them a drink.
|
/// Raised directed at a victim when someone has force fed them a drink.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnDrink(EntityUid uid, SharedBodyComponent body, DrinkEvent args)
|
private void OnDrink(EntityUid uid, BodyComponent body, DrinkEvent args)
|
||||||
{
|
{
|
||||||
if (args.Drink.Deleted)
|
if (args.Drink.Deleted)
|
||||||
return;
|
return;
|
||||||
@@ -297,7 +297,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
|
|
||||||
var forceDrink = uid != args.User;
|
var forceDrink = uid != args.User;
|
||||||
|
|
||||||
if (!_bodySystem.TryGetComponentsOnMechanisms<StomachComponent>(uid, out var stomachs, body))
|
if (!_bodySystem.TryGetBodyOrganComponents<StomachComponent>(uid, out var stomachs, body))
|
||||||
{
|
{
|
||||||
_popupSystem.PopupEntity(
|
_popupSystem.PopupEntity(
|
||||||
forceDrink ?
|
forceDrink ?
|
||||||
@@ -380,8 +380,8 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
if (uid == ev.User ||
|
if (uid == ev.User ||
|
||||||
!ev.CanInteract ||
|
!ev.CanInteract ||
|
||||||
!ev.CanAccess ||
|
!ev.CanAccess ||
|
||||||
!EntityManager.TryGetComponent(ev.User, out SharedBodyComponent? body) ||
|
!EntityManager.TryGetComponent(ev.User, out BodyComponent? body) ||
|
||||||
!_bodySystem.TryGetComponentsOnMechanisms<StomachComponent>(ev.User, out var stomachs, body))
|
!_bodySystem.TryGetBodyOrganComponents<StomachComponent>(ev.User, out var stomachs, body))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (EntityManager.TryGetComponent<MobStateComponent>(uid, out var mobState) && mobState.IsAlive())
|
if (EntityManager.TryGetComponent<MobStateComponent>(uid, out var mobState) && mobState.IsAlive())
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Threading;
|
||||||
using Content.Server.Body.Components;
|
using Content.Server.Body.Components;
|
||||||
using Content.Server.Body.Systems;
|
using Content.Server.Body.Systems;
|
||||||
using Content.Server.Chemistry.EntitySystems;
|
using Content.Server.Chemistry.EntitySystems;
|
||||||
@@ -20,7 +21,6 @@ using Content.Shared.Verbs;
|
|||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Content.Server.Nutrition.EntitySystems
|
namespace Content.Server.Nutrition.EntitySystems
|
||||||
{
|
{
|
||||||
@@ -48,7 +48,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
SubscribeLocalEvent<FoodComponent, UseInHandEvent>(OnUseFoodInHand);
|
SubscribeLocalEvent<FoodComponent, UseInHandEvent>(OnUseFoodInHand);
|
||||||
SubscribeLocalEvent<FoodComponent, AfterInteractEvent>(OnFeedFood);
|
SubscribeLocalEvent<FoodComponent, AfterInteractEvent>(OnFeedFood);
|
||||||
SubscribeLocalEvent<FoodComponent, GetVerbsEvent<AlternativeVerb>>(AddEatVerb);
|
SubscribeLocalEvent<FoodComponent, GetVerbsEvent<AlternativeVerb>>(AddEatVerb);
|
||||||
SubscribeLocalEvent<SharedBodyComponent, FeedEvent>(OnFeed);
|
SubscribeLocalEvent<BodyComponent, FeedEvent>(OnFeed);
|
||||||
SubscribeLocalEvent<ForceFeedCancelledEvent>(OnFeedCancelled);
|
SubscribeLocalEvent<ForceFeedCancelledEvent>(OnFeedCancelled);
|
||||||
SubscribeLocalEvent<InventoryComponent, IngestionAttemptEvent>(OnInventoryIngestAttempt);
|
SubscribeLocalEvent<InventoryComponent, IngestionAttemptEvent>(OnInventoryIngestAttempt);
|
||||||
}
|
}
|
||||||
@@ -88,7 +88,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Target can't be fed
|
// Target can't be fed
|
||||||
if (!EntityManager.HasComponent<SharedBodyComponent>(target))
|
if (!EntityManager.HasComponent<BodyComponent>(target))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!_solutionContainerSystem.TryGetSolution(food.Owner, food.SolutionName, out var foodSolution))
|
if (!_solutionContainerSystem.TryGetSolution(food.Owner, food.SolutionName, out var foodSolution))
|
||||||
@@ -145,14 +145,14 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnFeed(EntityUid uid, SharedBodyComponent body, FeedEvent args)
|
private void OnFeed(EntityUid uid, BodyComponent body, FeedEvent args)
|
||||||
{
|
{
|
||||||
if (args.Food.Deleted)
|
if (args.Food.Deleted)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
args.Food.CancelToken = null;
|
args.Food.CancelToken = null;
|
||||||
|
|
||||||
if (!_bodySystem.TryGetComponentsOnMechanisms<StomachComponent>(uid, out var stomachs, body))
|
if (!_bodySystem.TryGetBodyOrganComponents<StomachComponent>(uid, out var stomachs, body))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var transferAmount = args.Food.TransferAmount != null
|
var transferAmount = args.Food.TransferAmount != null
|
||||||
@@ -243,8 +243,8 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
if (uid == ev.User ||
|
if (uid == ev.User ||
|
||||||
!ev.CanInteract ||
|
!ev.CanInteract ||
|
||||||
!ev.CanAccess ||
|
!ev.CanAccess ||
|
||||||
!EntityManager.TryGetComponent(ev.User, out SharedBodyComponent? body) ||
|
!EntityManager.TryGetComponent(ev.User, out BodyComponent? body) ||
|
||||||
!_bodySystem.TryGetComponentsOnMechanisms<StomachComponent>(ev.User, out var stomachs, body))
|
!_bodySystem.TryGetBodyOrganComponents<StomachComponent>(ev.User, out var stomachs, body))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (EntityManager.TryGetComponent<MobStateComponent>(uid, out var mobState) && mobState.IsAlive())
|
if (EntityManager.TryGetComponent<MobStateComponent>(uid, out var mobState) && mobState.IsAlive())
|
||||||
@@ -279,7 +279,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
|||||||
if (!_solutionContainerSystem.TryGetSolution(uid, food.SolutionName, out var foodSolution))
|
if (!_solutionContainerSystem.TryGetSolution(uid, food.SolutionName, out var foodSolution))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!_bodySystem.TryGetComponentsOnMechanisms<StomachComponent>(target, out var stomachs, body))
|
if (!_bodySystem.TryGetBodyOrganComponents<StomachComponent>(target, out var stomachs, body))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (food.UsesRemaining <= 0)
|
if (food.UsesRemaining <= 0)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using Content.Server.Audio;
|
using Content.Server.Audio;
|
||||||
|
using Content.Server.Body.Systems;
|
||||||
using Content.Server.GameTicking;
|
using Content.Server.GameTicking;
|
||||||
using Content.Server.Players;
|
using Content.Server.Players;
|
||||||
using Content.Server.Popups;
|
using Content.Server.Popups;
|
||||||
using Content.Server.Power.Components;
|
|
||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
using Content.Server.Recycling.Components;
|
using Content.Server.Recycling.Components;
|
||||||
using Content.Shared.Audio;
|
using Content.Shared.Audio;
|
||||||
@@ -14,7 +14,6 @@ using Content.Shared.Recycling;
|
|||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Physics.Dynamics;
|
|
||||||
using Robust.Shared.Physics.Events;
|
using Robust.Shared.Physics.Events;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
@@ -25,6 +24,7 @@ namespace Content.Server.Recycling
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
[Dependency] private readonly AmbientSoundSystem _ambience = default!;
|
[Dependency] private readonly AmbientSoundSystem _ambience = default!;
|
||||||
|
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||||
[Dependency] private readonly GameTicker _ticker = default!;
|
[Dependency] private readonly GameTicker _ticker = default!;
|
||||||
[Dependency] private readonly PopupSystem _popup = default!;
|
[Dependency] private readonly PopupSystem _popup = default!;
|
||||||
[Dependency] private readonly TagSystem _tags = default!;
|
[Dependency] private readonly TagSystem _tags = default!;
|
||||||
@@ -57,9 +57,9 @@ namespace Content.Server.Recycling
|
|||||||
victim,
|
victim,
|
||||||
Filter.Pvs(victim, entityManager: EntityManager).RemoveWhereAttachedEntity(e => e == victim));
|
Filter.Pvs(victim, entityManager: EntityManager).RemoveWhereAttachedEntity(e => e == victim));
|
||||||
|
|
||||||
if (TryComp<SharedBodyComponent?>(victim, out var body))
|
if (TryComp<BodyComponent?>(victim, out var body))
|
||||||
{
|
{
|
||||||
body.Gib(true);
|
_bodySystem.GibBody(victim, true, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bloodstain(component);
|
Bloodstain(component);
|
||||||
@@ -104,7 +104,7 @@ namespace Content.Server.Recycling
|
|||||||
// Mobs are a special case!
|
// Mobs are a special case!
|
||||||
if (CanGib(component, entity))
|
if (CanGib(component, entity))
|
||||||
{
|
{
|
||||||
Comp<SharedBodyComponent>(entity).Gib(true);
|
_bodySystem.GibBody(entity, true, Comp<BodyComponent>(entity));
|
||||||
Bloodstain(component);
|
Bloodstain(component);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -123,7 +123,7 @@ namespace Content.Server.Recycling
|
|||||||
|
|
||||||
private bool CanGib(RecyclerComponent component, EntityUid entity)
|
private bool CanGib(RecyclerComponent component, EntityUid entity)
|
||||||
{
|
{
|
||||||
return HasComp<SharedBodyComponent>(entity) && !component.Safe &&
|
return HasComp<BodyComponent>(entity) && !component.Safe &&
|
||||||
this.IsPowered(component.Owner, EntityManager);
|
this.IsPowered(component.Owner, EntityManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
using Content.Shared.Damage;
|
using Content.Server.Body.Systems;
|
||||||
using Content.Server.Body.Components;
|
|
||||||
using Content.Server.MobState;
|
using Content.Server.MobState;
|
||||||
|
using Content.Shared.Body.Components;
|
||||||
|
using Content.Shared.Damage;
|
||||||
|
|
||||||
namespace Content.Server.Salvage;
|
namespace Content.Server.Salvage;
|
||||||
|
|
||||||
public sealed class SalvageMobRestrictionsSystem : EntitySystem
|
public sealed class SalvageMobRestrictionsSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||||
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
||||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -55,7 +58,7 @@ public sealed class SalvageMobRestrictionsSystem : EntitySystem
|
|||||||
if (bodyQuery.TryGetComponent(target, out var body))
|
if (bodyQuery.TryGetComponent(target, out var body))
|
||||||
{
|
{
|
||||||
// Just because.
|
// Just because.
|
||||||
body.Gib();
|
_bodySystem.GibBody(target, body: body);
|
||||||
}
|
}
|
||||||
else if (damageQuery.TryGetComponent(target, out var damageableComponent))
|
else if (damageQuery.TryGetComponent(target, out var damageableComponent))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -326,7 +326,7 @@ public sealed class EntityStorageSystem : EntitySystem
|
|||||||
if (attemptEvent.Cancelled)
|
if (attemptEvent.Cancelled)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
var targetIsMob = HasComp<SharedBodyComponent>(toInsert);
|
var targetIsMob = HasComp<BodyComponent>(toInsert);
|
||||||
var storageIsItem = HasComp<ItemComponent>(container);
|
var storageIsItem = HasComp<ItemComponent>(container);
|
||||||
var allowedToEat = whitelist?.IsValid(toInsert) ?? HasComp<ItemComponent>(toInsert);
|
var allowedToEat = whitelist?.IsValid(toInsert) ?? HasComp<ItemComponent>(toInsert);
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Content.Server.Body.Systems;
|
||||||
using Content.Server.Buckle.Components;
|
using Content.Server.Buckle.Components;
|
||||||
using Content.Server.Buckle.Systems;
|
using Content.Server.Buckle.Systems;
|
||||||
using Content.Server.Popups;
|
using Content.Server.Popups;
|
||||||
@@ -24,6 +25,7 @@ namespace Content.Server.Toilet
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
|
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
[Dependency] private readonly SecretStashSystem _secretStash = default!;
|
[Dependency] private readonly SecretStashSystem _secretStash = default!;
|
||||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||||
@@ -48,8 +50,8 @@ namespace Content.Server.Toilet
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Check that victim has a head
|
// Check that victim has a head
|
||||||
if (EntityManager.TryGetComponent<SharedBodyComponent>(args.Victim, out var body) &&
|
if (EntityManager.TryGetComponent<BodyComponent>(args.Victim, out var body) &&
|
||||||
body.HasPartOfType(BodyPartType.Head))
|
_bodySystem.BodyHasChildOfType(args.Victim, BodyPartType.Head, body))
|
||||||
{
|
{
|
||||||
var othersMessage = Loc.GetString("toilet-component-suicide-head-message-others",
|
var othersMessage = Loc.GetString("toilet-component-suicide-head-message-others",
|
||||||
("victim", Identity.Entity(args.Victim, EntityManager)), ("owner", uid));
|
("victim", Identity.Entity(args.Victim, EntityManager)), ("owner", uid));
|
||||||
|
|||||||
36
Content.Shared/Body/Components/BodyComponent.cs
Normal file
36
Content.Shared/Body/Components/BodyComponent.cs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
using Content.Shared.Body.Part;
|
||||||
|
using Content.Shared.Body.Prototypes;
|
||||||
|
using Content.Shared.Body.Systems;
|
||||||
|
using Content.Shared.DragDrop;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
|
|
||||||
|
namespace Content.Shared.Body.Components;
|
||||||
|
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
[Access(typeof(SharedBodySystem))]
|
||||||
|
public sealed class BodyComponent : Component, IDraggable
|
||||||
|
{
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("prototype", customTypeSerializer: typeof(PrototypeIdSerializer<BodyPrototype>))]
|
||||||
|
public readonly string? Prototype;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("root")]
|
||||||
|
public BodyPartSlot Root = default!;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("gibSound")]
|
||||||
|
public SoundSpecifier GibSound = new SoundCollectionSpecifier("gib");
|
||||||
|
|
||||||
|
bool IDraggable.CanStartDrag(StartDragDropEvent args)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IDraggable.CanDrop(CanDropEvent args)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
18
Content.Shared/Body/Components/BodyComponentState.cs
Normal file
18
Content.Shared/Body/Components/BodyComponentState.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using Content.Shared.Body.Part;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Body.Components;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class BodyComponentState : ComponentState
|
||||||
|
{
|
||||||
|
public readonly BodyPartSlot Root;
|
||||||
|
public readonly SoundSpecifier GibSound;
|
||||||
|
|
||||||
|
public BodyComponentState(BodyPartSlot root, SoundSpecifier gibSound)
|
||||||
|
{
|
||||||
|
Root = root;
|
||||||
|
GibSound = gibSound;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
using Content.Shared.Body.Events;
|
|
||||||
using Content.Shared.Body.Part;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
|
|
||||||
namespace Content.Shared.Body.Components
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed class MechanismComponent : Component
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
|
||||||
private SharedBodyPartComponent? _part;
|
|
||||||
|
|
||||||
public SharedBodyComponent? Body => Part?.Body;
|
|
||||||
|
|
||||||
public SharedBodyPartComponent? Part
|
|
||||||
{
|
|
||||||
get => _part;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_part == value)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var old = _part;
|
|
||||||
_part = value;
|
|
||||||
|
|
||||||
if (old != null)
|
|
||||||
{
|
|
||||||
if (old.Body == null)
|
|
||||||
{
|
|
||||||
_entMan.EventBus.RaiseLocalEvent(Owner, new RemovedFromPartEvent(old), true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_entMan.EventBus.RaiseLocalEvent(Owner, new RemovedFromPartInBodyEvent(old.Body, old), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value != null)
|
|
||||||
{
|
|
||||||
if (value.Body == null)
|
|
||||||
{
|
|
||||||
_entMan.EventBus.RaiseLocalEvent(Owner, new AddedToPartEvent(value), true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_entMan.EventBus.RaiseLocalEvent(Owner, new AddedToPartInBodyEvent(value.Body, value), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[DataField("maxDurability")] public int MaxDurability { get; set; } = 10;
|
|
||||||
|
|
||||||
[DataField("currentDurability")] public int CurrentDurability { get; set; } = 10;
|
|
||||||
|
|
||||||
[DataField("destroyThreshold")] public int DestroyThreshold { get; set; } = -10;
|
|
||||||
|
|
||||||
// TODO BODY: Surgery description and adding a message to the examine tooltip of the entity that owns this mechanism
|
|
||||||
// TODO BODY
|
|
||||||
[DataField("resistance")] public int Resistance { get; set; } = 0;
|
|
||||||
|
|
||||||
// TODO BODY OnSizeChanged
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether this
|
|
||||||
/// <see cref="MechanismComponent"/> can fit into a <see cref="SharedBodyPartComponent"/>.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("size")] public int Size { get; set; } = 1;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// What kind of <see cref="SharedBodyPartComponent"/> this
|
|
||||||
/// <see cref="MechanismComponent"/> can be easily installed into.
|
|
||||||
/// </summary>
|
|
||||||
[DataField("compatibility")]
|
|
||||||
public BodyPartCompatibility Compatibility { get; set; } = BodyPartCompatibility.Universal;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,457 +0,0 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
|
||||||
using Content.Shared.Body.Part;
|
|
||||||
using Content.Shared.Body.Prototypes;
|
|
||||||
using Content.Shared.Damage;
|
|
||||||
using Content.Shared.Damage.Prototypes;
|
|
||||||
using Content.Shared.Standing;
|
|
||||||
using Robust.Shared.GameStates;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
using Robust.Shared.Utility;
|
|
||||||
|
|
||||||
namespace Content.Shared.Body.Components
|
|
||||||
{
|
|
||||||
// TODO BODY Damage methods for collections of IDamageableComponents
|
|
||||||
|
|
||||||
[NetworkedComponent()]
|
|
||||||
public abstract class SharedBodyComponent : Component, ISerializationHooks
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("template", required: true)]
|
|
||||||
private string? TemplateId { get; }
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("preset", required: true)]
|
|
||||||
private string? PresetId { get; }
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public BodyTemplatePrototype? Template => TemplateId == null
|
|
||||||
? null
|
|
||||||
: _prototypeManager.Index<BodyTemplatePrototype>(TemplateId);
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public BodyPresetPrototype? Preset => PresetId == null
|
|
||||||
? null
|
|
||||||
: _prototypeManager.Index<BodyPresetPrototype>(PresetId);
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
private Dictionary<string, BodyPartSlot> SlotIds { get; } = new();
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
private Dictionary<SharedBodyPartComponent, BodyPartSlot> SlotParts { get; } = new();
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public IEnumerable<BodyPartSlot> Slots => SlotIds.Values;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public IEnumerable<KeyValuePair<SharedBodyPartComponent, BodyPartSlot>> Parts => SlotParts;
|
|
||||||
|
|
||||||
public BodyPartSlot? CenterSlot =>
|
|
||||||
Template?.CenterSlot is { } centerSlot
|
|
||||||
? SlotIds.GetValueOrDefault(centerSlot)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
public SharedBodyPartComponent? CenterPart => CenterSlot?.Part;
|
|
||||||
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
// TODO BODY BeforeDeserialization
|
|
||||||
// TODO BODY Move to template or somewhere else
|
|
||||||
if (TemplateId != null)
|
|
||||||
{
|
|
||||||
var template = _prototypeManager.Index<BodyTemplatePrototype>(TemplateId);
|
|
||||||
|
|
||||||
foreach (var (id, partType) in template.Slots)
|
|
||||||
{
|
|
||||||
SetSlot(id, partType);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var (slotId, connectionIds) in template.Connections)
|
|
||||||
{
|
|
||||||
var connections = connectionIds.Select(id => SlotIds[id]);
|
|
||||||
SlotIds[slotId].SetConnectionsInternal(connections);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnRemove()
|
|
||||||
{
|
|
||||||
foreach (var slot in SlotIds.Values)
|
|
||||||
{
|
|
||||||
slot.Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
base.OnRemove();
|
|
||||||
}
|
|
||||||
|
|
||||||
private BodyPartSlot SetSlot(string id, BodyPartType type)
|
|
||||||
{
|
|
||||||
var slot = new BodyPartSlot(id, type);
|
|
||||||
|
|
||||||
SlotIds[id] = slot;
|
|
||||||
slot.PartAdded += part => OnAddPart(slot, part);
|
|
||||||
slot.PartRemoved += part => OnRemovePart(slot, part);
|
|
||||||
|
|
||||||
return slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Dictionary<BodyPartSlot, SharedBodyPartComponent> GetHangingParts(BodyPartSlot from)
|
|
||||||
{
|
|
||||||
var hanging = new Dictionary<BodyPartSlot, SharedBodyPartComponent>();
|
|
||||||
|
|
||||||
foreach (var connection in from.Connections)
|
|
||||||
{
|
|
||||||
if (connection.Part != null &&
|
|
||||||
!ConnectedToCenter(connection.Part))
|
|
||||||
{
|
|
||||||
hanging.Add(connection, connection.Part);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hanging;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual bool CanAddPart(string slotId, SharedBodyPartComponent part)
|
|
||||||
{
|
|
||||||
if (!SlotIds.TryGetValue(slotId, out var slot) ||
|
|
||||||
slot.CanAddPart(part))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnAddPart(BodyPartSlot slot, SharedBodyPartComponent part)
|
|
||||||
{
|
|
||||||
SlotParts[part] = slot;
|
|
||||||
part.Body = this;
|
|
||||||
|
|
||||||
var argsAdded = new BodyPartAddedEventArgs(slot.Id, part);
|
|
||||||
|
|
||||||
// TODO: Body refactor. Somebody is doing it
|
|
||||||
// EntitySystem.Get<SharedHumanoidAppearanceSystem>().BodyPartAdded(Owner, argsAdded);
|
|
||||||
foreach (var component in IoCManager.Resolve<IEntityManager>().GetComponents<IBodyPartAdded>(Owner).ToArray())
|
|
||||||
{
|
|
||||||
component.BodyPartAdded(argsAdded);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO BODY Sort this duplicate out
|
|
||||||
OnBodyChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnRemovePart(BodyPartSlot slot, SharedBodyPartComponent part)
|
|
||||||
{
|
|
||||||
SlotParts.Remove(part);
|
|
||||||
|
|
||||||
foreach (var connectedSlot in slot.Connections)
|
|
||||||
{
|
|
||||||
if (connectedSlot.Part != null &&
|
|
||||||
!ConnectedToCenter(connectedSlot.Part))
|
|
||||||
{
|
|
||||||
RemovePart(connectedSlot.Part);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
part.Body = null;
|
|
||||||
|
|
||||||
var args = new BodyPartRemovedEventArgs(slot.Id, part);
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: Body refactor. Somebody is doing it
|
|
||||||
// EntitySystem.Get<SharedHumanoidAppearanceSystem>().BodyPartRemoved(Owner, args);
|
|
||||||
foreach (var component in IoCManager.Resolve<IEntityManager>().GetComponents<IBodyPartRemoved>(Owner))
|
|
||||||
{
|
|
||||||
component.BodyPartRemoved(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
// creadth: fall down if no legs
|
|
||||||
if (part.PartType == BodyPartType.Leg &&
|
|
||||||
GetPartsOfType(BodyPartType.Leg).ToArray().Length == 0)
|
|
||||||
{
|
|
||||||
EntitySystem.Get<StandingStateSystem>().Down(Owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (part.IsVital && SlotParts.Count(x => x.Value.PartType == part.PartType) == 0)
|
|
||||||
{
|
|
||||||
// TODO BODY SYSTEM KILL : Find a more elegant way of killing em than just dumping bloodloss damage.
|
|
||||||
var damage = new DamageSpecifier(_prototypeManager.Index<DamageTypePrototype>("Bloodloss"), 300);
|
|
||||||
EntitySystem.Get<DamageableSystem>().TryChangeDamage(part.Owner, damage);
|
|
||||||
}
|
|
||||||
|
|
||||||
OnBodyChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO BODY Sensible templates
|
|
||||||
public bool TryAddPart(string slotId, SharedBodyPartComponent part)
|
|
||||||
{
|
|
||||||
DebugTools.AssertNotNull(part);
|
|
||||||
DebugTools.AssertNotNull(slotId);
|
|
||||||
|
|
||||||
if (!CanAddPart(slotId, part))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SlotIds.TryGetValue(slotId, out var slot) &&
|
|
||||||
slot.TryAddPart(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetPart(string slotId, SharedBodyPartComponent part)
|
|
||||||
{
|
|
||||||
if (!SlotIds.TryGetValue(slotId, out var slot))
|
|
||||||
{
|
|
||||||
slot = SetSlot(slotId, part.PartType);
|
|
||||||
SlotIds[slotId] = slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
slot.SetPart(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasPart(SharedBodyPartComponent part)
|
|
||||||
{
|
|
||||||
DebugTools.AssertNotNull(part);
|
|
||||||
|
|
||||||
return SlotParts.ContainsKey(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool RemovePart(SharedBodyPartComponent part)
|
|
||||||
{
|
|
||||||
DebugTools.AssertNotNull(part);
|
|
||||||
|
|
||||||
return SlotParts.TryGetValue(part, out var slot) &&
|
|
||||||
slot.RemovePart();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryDropPart(BodyPartSlot slot, [NotNullWhen(true)] out Dictionary<BodyPartSlot, SharedBodyPartComponent>? dropped)
|
|
||||||
{
|
|
||||||
DebugTools.AssertNotNull(slot);
|
|
||||||
|
|
||||||
if (!SlotIds.TryGetValue(slot.Id, out var ownedSlot) ||
|
|
||||||
ownedSlot != slot ||
|
|
||||||
slot.Part == null)
|
|
||||||
{
|
|
||||||
dropped = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var oldPart = slot.Part;
|
|
||||||
dropped = GetHangingParts(slot);
|
|
||||||
|
|
||||||
if (!slot.RemovePart())
|
|
||||||
{
|
|
||||||
dropped = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
dropped[slot] = oldPart;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool ConnectedToCenter(SharedBodyPartComponent part)
|
|
||||||
{
|
|
||||||
return TryGetSlot(part, out var result) &&
|
|
||||||
ConnectedToCenterPartRecursion(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool ConnectedToCenterPartRecursion(BodyPartSlot slot, HashSet<BodyPartSlot>? searched = null)
|
|
||||||
{
|
|
||||||
searched ??= new HashSet<BodyPartSlot>();
|
|
||||||
|
|
||||||
if (Template?.CenterSlot == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slot.Part == CenterPart)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
searched.Add(slot);
|
|
||||||
|
|
||||||
foreach (var connection in slot.Connections)
|
|
||||||
{
|
|
||||||
if (!searched.Contains(connection) &&
|
|
||||||
ConnectedToCenterPartRecursion(connection, searched))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BodyPartSlot? GetSlot(SharedBodyPartComponent part)
|
|
||||||
{
|
|
||||||
return SlotParts.GetValueOrDefault(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryGetSlot(SharedBodyPartComponent part, [NotNullWhen(true)] out BodyPartSlot? slot)
|
|
||||||
{
|
|
||||||
return (slot = GetSlot(part)) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<BodyPartSlot> GetSlotsOfType(BodyPartType type)
|
|
||||||
{
|
|
||||||
foreach (var slot in SlotIds.Values)
|
|
||||||
{
|
|
||||||
if (slot.PartType == type)
|
|
||||||
{
|
|
||||||
yield return slot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool HasPartOfType(BodyPartType type)
|
|
||||||
{
|
|
||||||
foreach (var _ in GetPartsOfType(type))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<SharedBodyPartComponent> GetPartsOfType(BodyPartType type)
|
|
||||||
{
|
|
||||||
foreach (var slot in GetSlotsOfType(type))
|
|
||||||
{
|
|
||||||
if (slot.Part != null)
|
|
||||||
{
|
|
||||||
yield return slot.Part;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnBodyChanged()
|
|
||||||
{
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO BODY optimize this
|
|
||||||
public BodyPartSlot SlotAt(int index)
|
|
||||||
{
|
|
||||||
return SlotIds.Values.ElementAt(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public KeyValuePair<SharedBodyPartComponent, BodyPartSlot> PartAt(int index)
|
|
||||||
{
|
|
||||||
return SlotParts.ElementAt(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override ComponentState GetComponentState()
|
|
||||||
{
|
|
||||||
var parts = new (string slot, EntityUid partId)[SlotParts.Count];
|
|
||||||
|
|
||||||
var i = 0;
|
|
||||||
foreach (var (part, slot) in SlotParts)
|
|
||||||
{
|
|
||||||
parts[i] = (slot.Id, part.Owner);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new BodyComponentState(parts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
|
||||||
{
|
|
||||||
base.HandleComponentState(curState, nextState);
|
|
||||||
|
|
||||||
if (curState is not BodyComponentState state)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newParts = state.Parts();
|
|
||||||
|
|
||||||
foreach (var (oldPart, slot) in SlotParts)
|
|
||||||
{
|
|
||||||
if (!newParts.TryGetValue(slot.Id, out var newPart) ||
|
|
||||||
newPart != oldPart)
|
|
||||||
{
|
|
||||||
RemovePart(oldPart);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var (slotId, newPart) in newParts)
|
|
||||||
{
|
|
||||||
if (!SlotIds.TryGetValue(slotId, out var slot) ||
|
|
||||||
slot.Part != newPart)
|
|
||||||
{
|
|
||||||
SetPart(slotId, newPart);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual HashSet<EntityUid> Gib(bool gibParts = false)
|
|
||||||
{
|
|
||||||
var entMgr = IoCManager.Resolve<IEntityManager>();
|
|
||||||
var metaQuery = entMgr.GetEntityQuery<MetaDataComponent>();
|
|
||||||
var gibs = new HashSet<EntityUid>();
|
|
||||||
foreach (var part in SlotParts.Keys)
|
|
||||||
{
|
|
||||||
if (!metaQuery.TryGetComponent(part.Owner, out var meta) ||
|
|
||||||
meta.EntityLifeStage >= EntityLifeStage.Terminating)
|
|
||||||
{
|
|
||||||
SlotParts.Remove(part);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
gibs.Add(part.Owner);
|
|
||||||
RemovePart(part);
|
|
||||||
|
|
||||||
if (gibParts)
|
|
||||||
gibs.UnionWith(part.Gib());
|
|
||||||
}
|
|
||||||
|
|
||||||
return gibs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class BodyComponentState : ComponentState
|
|
||||||
{
|
|
||||||
private Dictionary<string, SharedBodyPartComponent>? _parts;
|
|
||||||
|
|
||||||
public readonly (string slot, EntityUid partId)[] PartIds;
|
|
||||||
|
|
||||||
public BodyComponentState((string slot, EntityUid partId)[] partIds)
|
|
||||||
{
|
|
||||||
PartIds = partIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Dictionary<string, SharedBodyPartComponent> Parts(IEntityManager? entityManager = null)
|
|
||||||
{
|
|
||||||
if (_parts != null)
|
|
||||||
{
|
|
||||||
return _parts;
|
|
||||||
}
|
|
||||||
|
|
||||||
entityManager ??= IoCManager.Resolve<IEntityManager>();
|
|
||||||
|
|
||||||
var parts = new Dictionary<string, SharedBodyPartComponent>(PartIds.Length);
|
|
||||||
|
|
||||||
foreach (var (slot, partId) in PartIds)
|
|
||||||
{
|
|
||||||
if (!entityManager.EntityExists(partId))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!entityManager.TryGetComponent(partId, out SharedBodyPartComponent? part))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
parts[slot] = part;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _parts = parts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,345 +0,0 @@
|
|||||||
using System.Linq;
|
|
||||||
using Content.Shared.Body.Events;
|
|
||||||
using Content.Shared.Body.Part;
|
|
||||||
using Robust.Shared.GameStates;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
using Robust.Shared.Utility;
|
|
||||||
|
|
||||||
namespace Content.Shared.Body.Components
|
|
||||||
{
|
|
||||||
[NetworkedComponent()]
|
|
||||||
public abstract class SharedBodyPartComponent : Component
|
|
||||||
{
|
|
||||||
public const string ContainerId = "bodypart";
|
|
||||||
|
|
||||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
|
||||||
private SharedBodyComponent? _body;
|
|
||||||
|
|
||||||
// TODO BODY Remove
|
|
||||||
[DataField("mechanisms")]
|
|
||||||
private readonly List<string> _mechanismIds = new();
|
|
||||||
public IReadOnlyList<string> MechanismIds => _mechanismIds;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
private readonly HashSet<MechanismComponent> _mechanisms = new();
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public SharedBodyComponent? Body
|
|
||||||
{
|
|
||||||
get => _body;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_body == value)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var old = _body;
|
|
||||||
_body = value;
|
|
||||||
|
|
||||||
if (old != null)
|
|
||||||
{
|
|
||||||
RemovedFromBody(old);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value != null)
|
|
||||||
{
|
|
||||||
AddedToBody(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <see cref="BodyPartType"/> that this <see cref="IBodyPart"/> is considered
|
|
||||||
/// to be.
|
|
||||||
/// For example, <see cref="BodyPartType.Arm"/>.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("partType")]
|
|
||||||
public BodyPartType PartType { get; private set; } = BodyPartType.Other;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determines how many mechanisms can be fit inside this
|
|
||||||
/// <see cref="SharedBodyPartComponent"/>.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables] [DataField("size")] public int Size { get; private set; } = 1;
|
|
||||||
|
|
||||||
[ViewVariables] public int SizeUsed { get; private set; }
|
|
||||||
|
|
||||||
// TODO BODY size used
|
|
||||||
// TODO BODY surgerydata
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// What types of BodyParts this <see cref="SharedBodyPartComponent"/> can easily attach to.
|
|
||||||
/// For the most part, most limbs aren't universal and require extra work to
|
|
||||||
/// attach between types.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("compatibility")]
|
|
||||||
public BodyPartCompatibility Compatibility = BodyPartCompatibility.Universal;
|
|
||||||
|
|
||||||
// TODO BODY Mechanisms occupying different parts at the body level
|
|
||||||
[ViewVariables]
|
|
||||||
public IReadOnlyCollection<MechanismComponent> Mechanisms => _mechanisms;
|
|
||||||
|
|
||||||
// TODO BODY Replace with a simulation of organs
|
|
||||||
/// <summary>
|
|
||||||
/// Whether or not the owning <see cref="Body"/> will die if all
|
|
||||||
/// <see cref="SharedBodyPartComponent"/>s of this type are removed from it.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("vital")]
|
|
||||||
public bool IsVital = false;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("symmetry")]
|
|
||||||
public BodyPartSymmetry Symmetry = BodyPartSymmetry.None;
|
|
||||||
|
|
||||||
protected virtual void OnAddMechanism(MechanismComponent mechanism)
|
|
||||||
{
|
|
||||||
var prototypeId = _entMan.GetComponent<MetaDataComponent>(mechanism.Owner).EntityPrototype!.ID;
|
|
||||||
|
|
||||||
if (!_mechanismIds.Contains(prototypeId))
|
|
||||||
{
|
|
||||||
_mechanismIds.Add(prototypeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
mechanism.Part = this;
|
|
||||||
SizeUsed += mechanism.Size;
|
|
||||||
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnRemoveMechanism(MechanismComponent mechanism)
|
|
||||||
{
|
|
||||||
_mechanismIds.Remove(_entMan.GetComponent<MetaDataComponent>(mechanism.Owner).EntityPrototype!.ID);
|
|
||||||
mechanism.Part = null;
|
|
||||||
SizeUsed -= mechanism.Size;
|
|
||||||
|
|
||||||
Dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override ComponentState GetComponentState()
|
|
||||||
{
|
|
||||||
var mechanismIds = new EntityUid[_mechanisms.Count];
|
|
||||||
|
|
||||||
var i = 0;
|
|
||||||
foreach (var mechanism in _mechanisms)
|
|
||||||
{
|
|
||||||
mechanismIds[i] = mechanism.Owner;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new BodyPartComponentState(mechanismIds);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
|
||||||
{
|
|
||||||
base.HandleComponentState(curState, nextState);
|
|
||||||
|
|
||||||
if (curState is not BodyPartComponentState state)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newMechanisms = state.Mechanisms();
|
|
||||||
|
|
||||||
foreach (var mechanism in _mechanisms.ToArray())
|
|
||||||
{
|
|
||||||
if (!newMechanisms.Contains(mechanism))
|
|
||||||
{
|
|
||||||
RemoveMechanism(mechanism);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var mechanism in newMechanisms)
|
|
||||||
{
|
|
||||||
if (!_mechanisms.Contains(mechanism))
|
|
||||||
{
|
|
||||||
TryAddMechanism(mechanism, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual bool CanAddMechanism(MechanismComponent mechanism)
|
|
||||||
{
|
|
||||||
return SizeUsed + mechanism.Size <= Size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to add a <see cref="MechanismComponent"/> to this part.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mechanism">The mechanism to add.</param>
|
|
||||||
/// <param name="force">
|
|
||||||
/// Whether or not to check if the mechanism is compatible.
|
|
||||||
/// Passing true does not guarantee it to be added, for example if
|
|
||||||
/// it was already added before.
|
|
||||||
/// </param>
|
|
||||||
/// <returns>true if added, false otherwise even if it was already added.</returns>
|
|
||||||
public bool TryAddMechanism(MechanismComponent mechanism, bool force = false)
|
|
||||||
{
|
|
||||||
DebugTools.AssertNotNull(mechanism);
|
|
||||||
|
|
||||||
if (!force && !CanAddMechanism(mechanism))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_mechanisms.Add(mechanism))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
OnAddMechanism(mechanism);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to remove the given <see cref="mechanism"/> from this part.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mechanism">The mechanism to remove.</param>
|
|
||||||
/// <returns>True if it was removed, false otherwise.</returns>
|
|
||||||
public bool RemoveMechanism(MechanismComponent mechanism)
|
|
||||||
{
|
|
||||||
DebugTools.AssertNotNull(mechanism);
|
|
||||||
|
|
||||||
if (!_mechanisms.Remove(mechanism))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
OnRemoveMechanism(mechanism);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to remove the given <see cref="mechanism"/> from this
|
|
||||||
/// part and drops it at the specified coordinates.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="mechanism">The mechanism to remove.</param>
|
|
||||||
/// <param name="coordinates">The coordinates to drop it at.</param>
|
|
||||||
/// <returns>True if it was removed, false otherwise.</returns>
|
|
||||||
public bool RemoveMechanism(MechanismComponent mechanism, EntityCoordinates coordinates)
|
|
||||||
{
|
|
||||||
if (RemoveMechanism(mechanism))
|
|
||||||
{
|
|
||||||
_entMan.GetComponent<TransformComponent>(mechanism.Owner).Coordinates = coordinates;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Tries to destroy the given <see cref="MechanismComponent"/> from
|
|
||||||
/// this part.
|
|
||||||
/// The mechanism won't be deleted if it is not in this body part.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// True if the mechanism was in this body part and destroyed,
|
|
||||||
/// false otherwise.
|
|
||||||
/// </returns>
|
|
||||||
public bool DeleteMechanism(MechanismComponent mechanism)
|
|
||||||
{
|
|
||||||
DebugTools.AssertNotNull(mechanism);
|
|
||||||
|
|
||||||
if (!RemoveMechanism(mechanism))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_entMan.DeleteEntity(mechanism.Owner);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddedToBody(SharedBodyComponent body)
|
|
||||||
{
|
|
||||||
OnAddedToBody(body);
|
|
||||||
|
|
||||||
foreach (var mechanism in _mechanisms)
|
|
||||||
{
|
|
||||||
_entMan.EventBus.RaiseLocalEvent(mechanism.Owner, new AddedToBodyEvent(body), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemovedFromBody(SharedBodyComponent old)
|
|
||||||
{
|
|
||||||
if (_entMan.TryGetComponent<TransformComponent>(Owner, out var transformComponent))
|
|
||||||
{
|
|
||||||
transformComponent.AttachToGridOrMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
OnRemovedFromBody(old);
|
|
||||||
|
|
||||||
foreach (var mechanism in _mechanisms)
|
|
||||||
{
|
|
||||||
_entMan.EventBus.RaiseLocalEvent(mechanism.Owner, new RemovedFromBodyEvent(old), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void OnAddedToBody(SharedBodyComponent body) { }
|
|
||||||
|
|
||||||
protected virtual void OnRemovedFromBody(SharedBodyComponent old) { }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gibs the body part.
|
|
||||||
/// </summary>
|
|
||||||
public virtual HashSet<EntityUid> Gib()
|
|
||||||
{
|
|
||||||
var gibs = new HashSet<EntityUid>();
|
|
||||||
|
|
||||||
foreach (var mechanism in _mechanisms.ToArray())
|
|
||||||
{
|
|
||||||
gibs.Add(mechanism.Owner);
|
|
||||||
RemoveMechanism(mechanism);
|
|
||||||
}
|
|
||||||
|
|
||||||
return gibs;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class BodyPartComponentState : ComponentState
|
|
||||||
{
|
|
||||||
[NonSerialized] private List<MechanismComponent>? _mechanisms;
|
|
||||||
|
|
||||||
public readonly EntityUid[] MechanismIds;
|
|
||||||
|
|
||||||
public BodyPartComponentState(EntityUid[] mechanismIds)
|
|
||||||
{
|
|
||||||
MechanismIds = mechanismIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<MechanismComponent> Mechanisms(IEntityManager? entityManager = null)
|
|
||||||
{
|
|
||||||
if (_mechanisms != null)
|
|
||||||
{
|
|
||||||
return _mechanisms;
|
|
||||||
}
|
|
||||||
|
|
||||||
IoCManager.Resolve(ref entityManager);
|
|
||||||
|
|
||||||
var mechanisms = new List<MechanismComponent>(MechanismIds.Length);
|
|
||||||
|
|
||||||
foreach (var id in MechanismIds)
|
|
||||||
{
|
|
||||||
if (!entityManager.EntityExists(id))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!entityManager.TryGetComponent(id, out MechanismComponent? mechanism))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
mechanisms.Add(mechanism);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _mechanisms = mechanisms;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
using Content.Shared.Body.Components;
|
namespace Content.Shared.Body.Events
|
||||||
|
|
||||||
namespace Content.Shared.Body.Events
|
|
||||||
{
|
{
|
||||||
// All of these events are raised on a mechanism entity when added/removed to a body in different
|
// All of these events are raised on a mechanism entity when added/removed to a body in different
|
||||||
// ways.
|
// ways.
|
||||||
@@ -10,9 +8,9 @@ namespace Content.Shared.Body.Events
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class AddedToBodyEvent : EntityEventArgs
|
public sealed class AddedToBodyEvent : EntityEventArgs
|
||||||
{
|
{
|
||||||
public SharedBodyComponent Body;
|
public EntityUid Body;
|
||||||
|
|
||||||
public AddedToBodyEvent(SharedBodyComponent body)
|
public AddedToBodyEvent(EntityUid body)
|
||||||
{
|
{
|
||||||
Body = body;
|
Body = body;
|
||||||
}
|
}
|
||||||
@@ -23,9 +21,9 @@ namespace Content.Shared.Body.Events
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class AddedToPartEvent : EntityEventArgs
|
public sealed class AddedToPartEvent : EntityEventArgs
|
||||||
{
|
{
|
||||||
public SharedBodyPartComponent Part;
|
public EntityUid Part;
|
||||||
|
|
||||||
public AddedToPartEvent(SharedBodyPartComponent part)
|
public AddedToPartEvent(EntityUid part)
|
||||||
{
|
{
|
||||||
Part = part;
|
Part = part;
|
||||||
}
|
}
|
||||||
@@ -36,10 +34,10 @@ namespace Content.Shared.Body.Events
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class AddedToPartInBodyEvent : EntityEventArgs
|
public sealed class AddedToPartInBodyEvent : EntityEventArgs
|
||||||
{
|
{
|
||||||
public SharedBodyComponent Body;
|
public EntityUid Body;
|
||||||
public SharedBodyPartComponent Part;
|
public EntityUid Part;
|
||||||
|
|
||||||
public AddedToPartInBodyEvent(SharedBodyComponent body, SharedBodyPartComponent part)
|
public AddedToPartInBodyEvent(EntityUid body, EntityUid part)
|
||||||
{
|
{
|
||||||
Body = body;
|
Body = body;
|
||||||
Part = part;
|
Part = part;
|
||||||
@@ -51,9 +49,9 @@ namespace Content.Shared.Body.Events
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class RemovedFromBodyEvent : EntityEventArgs
|
public sealed class RemovedFromBodyEvent : EntityEventArgs
|
||||||
{
|
{
|
||||||
public SharedBodyComponent Old;
|
public EntityUid Old;
|
||||||
|
|
||||||
public RemovedFromBodyEvent(SharedBodyComponent old)
|
public RemovedFromBodyEvent(EntityUid old)
|
||||||
{
|
{
|
||||||
Old = old;
|
Old = old;
|
||||||
}
|
}
|
||||||
@@ -64,9 +62,9 @@ namespace Content.Shared.Body.Events
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class RemovedFromPartEvent : EntityEventArgs
|
public sealed class RemovedFromPartEvent : EntityEventArgs
|
||||||
{
|
{
|
||||||
public SharedBodyPartComponent Old;
|
public EntityUid Old;
|
||||||
|
|
||||||
public RemovedFromPartEvent(SharedBodyPartComponent old)
|
public RemovedFromPartEvent(EntityUid old)
|
||||||
{
|
{
|
||||||
Old = old;
|
Old = old;
|
||||||
}
|
}
|
||||||
@@ -77,10 +75,10 @@ namespace Content.Shared.Body.Events
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class RemovedFromPartInBodyEvent : EntityEventArgs
|
public sealed class RemovedFromPartInBodyEvent : EntityEventArgs
|
||||||
{
|
{
|
||||||
public SharedBodyComponent OldBody;
|
public EntityUid OldBody;
|
||||||
public SharedBodyPartComponent OldPart;
|
public EntityUid OldPart;
|
||||||
|
|
||||||
public RemovedFromPartInBodyEvent(SharedBodyComponent oldBody, SharedBodyPartComponent oldPart)
|
public RemovedFromPartInBodyEvent(EntityUid oldBody, EntityUid oldPart)
|
||||||
{
|
{
|
||||||
OldBody = oldBody;
|
OldBody = oldBody;
|
||||||
OldPart = oldPart;
|
OldPart = oldPart;
|
||||||
|
|||||||
17
Content.Shared/Body/Organ/OrganComponent.cs
Normal file
17
Content.Shared/Body/Organ/OrganComponent.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using Content.Shared.Body.Systems;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Body.Organ;
|
||||||
|
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
[Access(typeof(SharedBodySystem))]
|
||||||
|
public sealed class OrganComponent : Component
|
||||||
|
{
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("body")]
|
||||||
|
public EntityUid? Body;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("parent")]
|
||||||
|
public OrganSlot? ParentSlot;
|
||||||
|
}
|
||||||
16
Content.Shared/Body/Organ/OrganComponentState.cs
Normal file
16
Content.Shared/Body/Organ/OrganComponentState.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Body.Organ;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class OrganComponentState : ComponentState
|
||||||
|
{
|
||||||
|
public readonly EntityUid? Body;
|
||||||
|
public readonly OrganSlot? Parent;
|
||||||
|
|
||||||
|
public OrganComponentState(EntityUid? body, OrganSlot? parent)
|
||||||
|
{
|
||||||
|
Body = body;
|
||||||
|
Parent = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Content.Shared/Body/Organ/OrganSlot.cs
Normal file
20
Content.Shared/Body/Organ/OrganSlot.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using Content.Shared.Body.Systems;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Body.Organ;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
[Access(typeof(SharedBodySystem))]
|
||||||
|
[DataRecord]
|
||||||
|
public sealed record OrganSlot(string Id, EntityUid Parent)
|
||||||
|
{
|
||||||
|
public EntityUid? Child { get; set; }
|
||||||
|
|
||||||
|
// Rider doesn't suggest explicit properties during deconstruction without this
|
||||||
|
public void Deconstruct(out EntityUid? child, out string id, out EntityUid parent)
|
||||||
|
{
|
||||||
|
child = Child;
|
||||||
|
id = Id;
|
||||||
|
parent = Parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
using Content.Shared.Body.Components;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
|
|
||||||
namespace Content.Shared.Body.Part
|
|
||||||
{
|
|
||||||
//TODO: This should be a prototype. --DrSmugleaf
|
|
||||||
/// <summary>
|
|
||||||
/// Determines whether two <see cref="SharedBodyPartComponent"/>s can connect.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public enum BodyPartCompatibility
|
|
||||||
{
|
|
||||||
Universal = 0,
|
|
||||||
Biological,
|
|
||||||
Mechanical,
|
|
||||||
Slime,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
44
Content.Shared/Body/Part/BodyPartComponent.cs
Normal file
44
Content.Shared/Body/Part/BodyPartComponent.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using Content.Shared.Body.Components;
|
||||||
|
using Content.Shared.Body.Organ;
|
||||||
|
using Content.Shared.Body.Systems;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Body.Part;
|
||||||
|
|
||||||
|
[RegisterComponent, NetworkedComponent]
|
||||||
|
[Access(typeof(SharedBodySystem))]
|
||||||
|
public sealed class BodyPartComponent : Component
|
||||||
|
{
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("body")]
|
||||||
|
public EntityUid? Body;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("parent")]
|
||||||
|
public BodyPartSlot? ParentSlot;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("children")]
|
||||||
|
public Dictionary<string, BodyPartSlot> Children = new();
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("organs")]
|
||||||
|
public Dictionary<string, OrganSlot> Organs = new();
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("partType")]
|
||||||
|
public BodyPartType PartType = BodyPartType.Other;
|
||||||
|
|
||||||
|
// TODO BODY Replace with a simulation of organs
|
||||||
|
/// <summary>
|
||||||
|
/// Whether or not the owning <see cref="Body"/> will die if all
|
||||||
|
/// <see cref="BodyComponent"/>s of this type are removed from it.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("vital")]
|
||||||
|
public bool IsVital;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("symmetry")]
|
||||||
|
public BodyPartSymmetry Symmetry = BodyPartSymmetry.None;
|
||||||
|
}
|
||||||
34
Content.Shared/Body/Part/BodyPartComponentState.cs
Normal file
34
Content.Shared/Body/Part/BodyPartComponentState.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using Content.Shared.Body.Organ;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Body.Part;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class BodyPartComponentState : ComponentState
|
||||||
|
{
|
||||||
|
public readonly EntityUid? Body;
|
||||||
|
public readonly BodyPartSlot? ParentSlot;
|
||||||
|
public readonly Dictionary<string, BodyPartSlot> Children;
|
||||||
|
public readonly Dictionary<string, OrganSlot> Organs;
|
||||||
|
public readonly BodyPartType PartType;
|
||||||
|
public readonly bool IsVital;
|
||||||
|
public readonly BodyPartSymmetry Symmetry;
|
||||||
|
|
||||||
|
public BodyPartComponentState(
|
||||||
|
EntityUid? body,
|
||||||
|
BodyPartSlot? parentSlot,
|
||||||
|
Dictionary<string, BodyPartSlot> children,
|
||||||
|
Dictionary<string, OrganSlot> organs,
|
||||||
|
BodyPartType partType,
|
||||||
|
bool isVital,
|
||||||
|
BodyPartSymmetry symmetry)
|
||||||
|
{
|
||||||
|
ParentSlot = parentSlot;
|
||||||
|
Children = children;
|
||||||
|
Organs = organs;
|
||||||
|
PartType = partType;
|
||||||
|
IsVital = isVital;
|
||||||
|
Symmetry = symmetry;
|
||||||
|
Body = body;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,104 +1,21 @@
|
|||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Systems;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
namespace Content.Shared.Body.Part
|
namespace Content.Shared.Body.Part;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
[Access(typeof(SharedBodySystem))]
|
||||||
|
[DataRecord]
|
||||||
|
public sealed record BodyPartSlot(string Id, EntityUid Parent, BodyPartType? Type)
|
||||||
{
|
{
|
||||||
public sealed class BodyPartSlot
|
public EntityUid? Child { get; set; }
|
||||||
|
|
||||||
|
// Rider doesn't suggest explicit properties during deconstruction without this
|
||||||
|
public void Deconstruct(out EntityUid? child, out string id, out EntityUid parent, out BodyPartType? type)
|
||||||
{
|
{
|
||||||
public BodyPartSlot(string id, BodyPartType partType, IEnumerable<BodyPartSlot> connections)
|
child = Child;
|
||||||
{
|
id = Id;
|
||||||
Id = id;
|
parent = Parent;
|
||||||
PartType = partType;
|
type = Type;
|
||||||
Connections = new HashSet<BodyPartSlot>(connections);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BodyPartSlot(string id, BodyPartType partType)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
PartType = partType;
|
|
||||||
Connections = new HashSet<BodyPartSlot>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The ID of this slot.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
public string Id { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The part type that this slot accepts.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
public BodyPartType PartType { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The part currently in this slot, if any.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
public SharedBodyPartComponent? Part { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// List of slots that this slot connects to.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
public HashSet<BodyPartSlot> Connections { get; private set; }
|
|
||||||
|
|
||||||
public event Action<SharedBodyPartComponent>? PartAdded;
|
|
||||||
|
|
||||||
public event Action<SharedBodyPartComponent>? PartRemoved;
|
|
||||||
|
|
||||||
internal void SetConnectionsInternal(IEnumerable<BodyPartSlot> connections)
|
|
||||||
{
|
|
||||||
Connections = new HashSet<BodyPartSlot>(connections);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CanAddPart(SharedBodyPartComponent part)
|
|
||||||
{
|
|
||||||
return Part == null && part.PartType == PartType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryAddPart(SharedBodyPartComponent part)
|
|
||||||
{
|
|
||||||
if (!CanAddPart(part))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetPart(part);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetPart(SharedBodyPartComponent part)
|
|
||||||
{
|
|
||||||
if (Part != null)
|
|
||||||
{
|
|
||||||
RemovePart();
|
|
||||||
}
|
|
||||||
|
|
||||||
Part = part;
|
|
||||||
PartAdded?.Invoke(part);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool RemovePart()
|
|
||||||
{
|
|
||||||
if (Part == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var old = Part;
|
|
||||||
Part = null;
|
|
||||||
|
|
||||||
PartRemoved?.Invoke(old);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Shutdown()
|
|
||||||
{
|
|
||||||
Part = null;
|
|
||||||
Connections.Clear();
|
|
||||||
PartAdded = null;
|
|
||||||
PartRemoved = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Robust.Shared.Serialization;
|
|||||||
namespace Content.Shared.Body.Part
|
namespace Content.Shared.Body.Part
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines the symmetry of a <see cref="SharedBodyPartComponent"/>.
|
/// Defines the symmetry of a <see cref="BodyComponent"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public enum BodyPartSymmetry
|
public enum BodyPartSymmetry
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ using Robust.Shared.Serialization;
|
|||||||
namespace Content.Shared.Body.Part
|
namespace Content.Shared.Body.Part
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines the type of a <see cref="SharedBodyPartComponent"/>.
|
/// Defines the type of a <see cref="BodyComponent"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public enum BodyPartType
|
public enum BodyPartType
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
|
|
||||||
namespace Content.Shared.Body.Part
|
namespace Content.Shared.Body.Part
|
||||||
{
|
{
|
||||||
@@ -10,18 +9,16 @@ namespace Content.Shared.Body.Part
|
|||||||
public interface IBodyPartAdded : IComponent
|
public interface IBodyPartAdded : IComponent
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when a <see cref="SharedBodyPartComponent"/> is added to the
|
/// Called when a <see cref="BodyComponent"/> is added to the
|
||||||
/// entity owning this component.
|
/// entity owning this component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="args">Information about the part that was added.</param>
|
/// <param name="args">Information about the part that was added.</param>
|
||||||
void BodyPartAdded(BodyPartAddedEventArgs args);
|
void BodyPartAdded(BodyPartAddedEventArgs args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class BodyPartAddedEventArgs : EventArgs
|
public sealed class BodyPartAddedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public BodyPartAddedEventArgs(string slot, SharedBodyPartComponent part)
|
public BodyPartAddedEventArgs(string slot, BodyPartComponent part)
|
||||||
{
|
{
|
||||||
Slot = slot;
|
Slot = slot;
|
||||||
Part = part;
|
Part = part;
|
||||||
@@ -35,6 +32,6 @@ namespace Content.Shared.Body.Part
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The part that was added.
|
/// The part that was added.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SharedBodyPartComponent Part { get; }
|
public BodyPartComponent Part { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
|
|
||||||
namespace Content.Shared.Body.Part
|
namespace Content.Shared.Body.Part
|
||||||
{
|
{
|
||||||
@@ -10,17 +9,16 @@ namespace Content.Shared.Body.Part
|
|||||||
public interface IBodyPartRemoved
|
public interface IBodyPartRemoved
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when a <see cref="SharedBodyPartComponent"/> is removed from the
|
/// Called when a <see cref="BodyComponent"/> is removed from the
|
||||||
/// entity owning this component.
|
/// entity owning this component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="args">Information about the part that was removed.</param>
|
/// <param name="args">Information about the part that was removed.</param>
|
||||||
void BodyPartRemoved(BodyPartRemovedEventArgs args);
|
void BodyPartRemoved(BodyPartRemovedEventArgs args);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class BodyPartRemovedEventArgs : EventArgs
|
public sealed class BodyPartRemovedEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public BodyPartRemovedEventArgs(string slot, SharedBodyPartComponent part)
|
public BodyPartRemovedEventArgs(string slot, BodyPartComponent part)
|
||||||
{
|
{
|
||||||
Slot = slot;
|
Slot = slot;
|
||||||
Part = part;
|
Part = part;
|
||||||
@@ -34,6 +32,6 @@ namespace Content.Shared.Body.Part
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The part that was removed.
|
/// The part that was removed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public SharedBodyPartComponent Part { get; }
|
public BodyPartComponent Part { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
|
|
||||||
namespace Content.Shared.Body.Prototypes
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Defines the parts used in a body.
|
|
||||||
/// </summary>
|
|
||||||
[Prototype("bodyPreset")]
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class BodyPresetPrototype : IPrototype
|
|
||||||
{
|
|
||||||
[ViewVariables]
|
|
||||||
[IdDataFieldAttribute]
|
|
||||||
public string ID { get; } = default!;
|
|
||||||
|
|
||||||
[DataField("partIDs")]
|
|
||||||
private Dictionary<string, string> _partIDs = new();
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("name")]
|
|
||||||
public string Name { get; } = string.Empty;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public Dictionary<string, string> PartIDs => new(_partIDs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
54
Content.Shared/Body/Prototypes/BodyPrototype.cs
Normal file
54
Content.Shared/Body/Prototypes/BodyPrototype.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.Body.Prototypes;
|
||||||
|
|
||||||
|
[Prototype("body")]
|
||||||
|
public sealed class BodyPrototype : IPrototype
|
||||||
|
{
|
||||||
|
[IdDataField] public string ID { get; } = default!;
|
||||||
|
|
||||||
|
private string _name = string.Empty;
|
||||||
|
|
||||||
|
[DataField("name")]
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get => _name;
|
||||||
|
private set => _name = Loc.GetString(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataField("root")] public string Root { get; } = string.Empty;
|
||||||
|
|
||||||
|
[DataField("slots")] public Dictionary<string, BodyPrototypeSlot> Slots { get; } = new();
|
||||||
|
|
||||||
|
private BodyPrototype() { }
|
||||||
|
|
||||||
|
public BodyPrototype(string id, string name, string root, Dictionary<string, BodyPrototypeSlot> slots)
|
||||||
|
{
|
||||||
|
ID = id;
|
||||||
|
Name = name;
|
||||||
|
Root = root;
|
||||||
|
Slots = slots;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataRecord]
|
||||||
|
public sealed record BodyPrototypeSlot
|
||||||
|
{
|
||||||
|
[DataField("part", required: true)] public readonly string Part = default!;
|
||||||
|
public readonly HashSet<string> Connections = new();
|
||||||
|
public readonly Dictionary<string, string> Organs = new();
|
||||||
|
|
||||||
|
public BodyPrototypeSlot(string part, HashSet<string>? connections, Dictionary<string, string>? organs)
|
||||||
|
{
|
||||||
|
Part = part;
|
||||||
|
Connections = connections ?? new HashSet<string>();
|
||||||
|
Organs = organs ?? new Dictionary<string, string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Deconstruct(out string part, out HashSet<string> connections, out Dictionary<string, string> organs)
|
||||||
|
{
|
||||||
|
part = Part;
|
||||||
|
connections = Connections;
|
||||||
|
organs = Organs;
|
||||||
|
}
|
||||||
|
}
|
||||||
191
Content.Shared/Body/Prototypes/BodyPrototypeSerializer.cs
Normal file
191
Content.Shared/Body/Prototypes/BodyPrototypeSerializer.cs
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using Content.Shared.Body.Organ;
|
||||||
|
using Content.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization.Manager;
|
||||||
|
using Robust.Shared.Serialization.Markdown.Mapping;
|
||||||
|
using Robust.Shared.Serialization.Markdown.Sequence;
|
||||||
|
using Robust.Shared.Serialization.Markdown.Validation;
|
||||||
|
using Robust.Shared.Serialization.Markdown.Value;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Interfaces;
|
||||||
|
|
||||||
|
namespace Content.Shared.Body.Prototypes;
|
||||||
|
|
||||||
|
[TypeSerializer]
|
||||||
|
public sealed class BodyPrototypeSerializer : ITypeReader<BodyPrototype, MappingDataNode>
|
||||||
|
{
|
||||||
|
private (ValidationNode Node, List<string> Connections) ValidateSlot(ISerializationManager serializationManager, MappingDataNode slot, string slotId, IDependencyCollection dependencies)
|
||||||
|
{
|
||||||
|
var nodes = new List<ValidationNode>();
|
||||||
|
var prototypes = dependencies.Resolve<IPrototypeManager>();
|
||||||
|
if (!slot.TryGet("part", out ValueDataNode? part))
|
||||||
|
{
|
||||||
|
nodes.Add(new ErrorNode(slot, $"No part value data node found in root slot {slotId}"));
|
||||||
|
}
|
||||||
|
else if (!prototypes.HasIndex<EntityPrototype>(part.Value))
|
||||||
|
{
|
||||||
|
nodes.Add(new ErrorNode(slot, $"No entity prototype found with id {part.Value} for root slot {slotId}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
var connections = new List<string>();
|
||||||
|
if (slot.TryGet("connections", out SequenceDataNode? connectionsNode))
|
||||||
|
{
|
||||||
|
foreach (var node in connectionsNode)
|
||||||
|
{
|
||||||
|
if (node is not ValueDataNode connection)
|
||||||
|
{
|
||||||
|
nodes.Add(new ErrorNode(node, $"Connection is not a value data node"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
connections.Add(connection.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (slot.TryGet("organs", out MappingDataNode? organsNode))
|
||||||
|
{
|
||||||
|
foreach (var (key, value) in organsNode)
|
||||||
|
{
|
||||||
|
if (key is not ValueDataNode)
|
||||||
|
{
|
||||||
|
nodes.Add(new ErrorNode(key, $"Key is not a value data node"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is not ValueDataNode organ)
|
||||||
|
{
|
||||||
|
nodes.Add(new ErrorNode(value, $"Value is not a value data node"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!prototypes.TryIndex(organ.Value, out EntityPrototype? organPrototype))
|
||||||
|
{
|
||||||
|
nodes.Add(new ErrorNode(value, $"No organ entity prototype found with id {organ.Value}"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!organPrototype.HasComponent<OrganComponent>())
|
||||||
|
{
|
||||||
|
nodes.Add(new ErrorNode(value, $"Organ {organ.Value} does not have a body component"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var validation = new ValidatedSequenceNode(nodes);
|
||||||
|
return (validation, connections);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValidationNode Validate(ISerializationManager serializationManager, MappingDataNode node,
|
||||||
|
IDependencyCollection dependencies, ISerializationContext? context = null)
|
||||||
|
{
|
||||||
|
var nodes = new List<ValidationNode>();
|
||||||
|
if (!node.TryGet("id", out ValueDataNode? id))
|
||||||
|
nodes.Add(new ErrorNode(node, "No id value data node found"));
|
||||||
|
|
||||||
|
if (!node.TryGet("root", out ValueDataNode? root))
|
||||||
|
nodes.Add(new ErrorNode(node, $"No root value data node found"));
|
||||||
|
|
||||||
|
if (!node.TryGet("slots", out MappingDataNode? slots))
|
||||||
|
{
|
||||||
|
nodes.Add(new ErrorNode(node, $"No slots mapping data node found"));
|
||||||
|
}
|
||||||
|
else if (root != null)
|
||||||
|
{
|
||||||
|
if (!slots.TryGet(root.Value, out MappingDataNode? _))
|
||||||
|
{
|
||||||
|
nodes.Add(new ErrorNode(slots, $"No slot found with id {root.Value}"));
|
||||||
|
return new ValidatedSequenceNode(nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (key, value) in slots)
|
||||||
|
{
|
||||||
|
if (key is not ValueDataNode)
|
||||||
|
{
|
||||||
|
nodes.Add(new ErrorNode(key, $"Key is not a value data node"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value is not MappingDataNode slot)
|
||||||
|
{
|
||||||
|
nodes.Add(new ErrorNode(value, $"Slot is not a mapping data node"));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var result = ValidateSlot(serializationManager, slot, root.Value, dependencies);
|
||||||
|
nodes.Add(result.Node);
|
||||||
|
|
||||||
|
foreach (var connection in result.Connections)
|
||||||
|
{
|
||||||
|
if (!slots.TryGet(connection, out MappingDataNode? _))
|
||||||
|
nodes.Add(new ErrorNode(slots, $"No slot found with id {connection}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ValidatedSequenceNode(nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BodyPrototype Read(ISerializationManager serializationManager, MappingDataNode node, IDependencyCollection dependencies,
|
||||||
|
bool skipHook, ISerializationContext? context = null, BodyPrototype? value = default)
|
||||||
|
{
|
||||||
|
var id = node.Get<ValueDataNode>("id").Value;
|
||||||
|
var name = node.Get<ValueDataNode>("name").Value;
|
||||||
|
var root = node.Get<ValueDataNode>("root").Value;
|
||||||
|
var slotNodes = node.Get<MappingDataNode>("slots");
|
||||||
|
var allConnections = new Dictionary<string, (string Part, HashSet<string>? Connections, Dictionary<string, string>? Organs)>();
|
||||||
|
|
||||||
|
foreach (var (keyNode, valueNode) in slotNodes)
|
||||||
|
{
|
||||||
|
var slotId = ((ValueDataNode) keyNode).Value;
|
||||||
|
var slot = ((MappingDataNode) valueNode);
|
||||||
|
var part = slot.Get<ValueDataNode>("part").Value;
|
||||||
|
|
||||||
|
HashSet<string>? connections = null;
|
||||||
|
if (slot.TryGet("connections", out SequenceDataNode? slotConnectionsNode))
|
||||||
|
{
|
||||||
|
connections = new HashSet<string>();
|
||||||
|
|
||||||
|
foreach (var connection in slotConnectionsNode.Cast<ValueDataNode>())
|
||||||
|
{
|
||||||
|
connections.Add(connection.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<string, string>? organs = null;
|
||||||
|
if (slot.TryGet("organs", out MappingDataNode? slotOrgansNode))
|
||||||
|
{
|
||||||
|
organs = new Dictionary<string, string>();
|
||||||
|
|
||||||
|
foreach (var (organKeyNode, organValueNode) in slotOrgansNode)
|
||||||
|
{
|
||||||
|
organs.Add(((ValueDataNode) organKeyNode).Value, ((ValueDataNode) organValueNode).Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allConnections.Add(slotId, (part, connections, organs));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (slotId, (_, connections, _)) in allConnections)
|
||||||
|
{
|
||||||
|
if (connections == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foreach (var connection in connections)
|
||||||
|
{
|
||||||
|
var other = allConnections[connection];
|
||||||
|
other.Connections ??= new HashSet<string>();
|
||||||
|
other.Connections.Add(slotId);
|
||||||
|
allConnections[connection] = other;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var slots = new Dictionary<string, BodyPrototypeSlot>();
|
||||||
|
foreach (var (slotId, (part, connections, organs)) in allConnections)
|
||||||
|
{
|
||||||
|
var slot = new BodyPrototypeSlot(part, connections, organs);
|
||||||
|
slots.Add(slotId, slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new BodyPrototype(id, name, root, slots);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
using Content.Shared.Body.Part;
|
|
||||||
using Robust.Shared.Prototypes;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
|
|
||||||
namespace Content.Shared.Body.Prototypes
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Defines the layout of a body.
|
|
||||||
/// </summary>
|
|
||||||
[Prototype("bodyTemplate")]
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class BodyTemplatePrototype : IPrototype, ISerializationHooks
|
|
||||||
{
|
|
||||||
[DataField("slots")]
|
|
||||||
private Dictionary<string, BodyPartType> _slots = new();
|
|
||||||
|
|
||||||
[DataField("connections")]
|
|
||||||
private Dictionary<string, List<string>> _rawConnections = new();
|
|
||||||
|
|
||||||
[DataField("layers")]
|
|
||||||
private Dictionary<string, string> _layers = new();
|
|
||||||
|
|
||||||
[DataField("mechanismLayers")]
|
|
||||||
private Dictionary<string, string> _mechanismLayers = new();
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
[IdDataFieldAttribute]
|
|
||||||
public string ID { get; } = default!;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("name")]
|
|
||||||
public string Name { get; } = string.Empty;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("centerSlot")]
|
|
||||||
public string CenterSlot { get; } = string.Empty;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public Dictionary<string, BodyPartType> Slots => new(_slots);
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public Dictionary<string, HashSet<string>> Connections { get; set; } = new();
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public Dictionary<string, string> Layers => new(_layers);
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public Dictionary<string, string> MechanismLayers => new(_mechanismLayers);
|
|
||||||
|
|
||||||
void ISerializationHooks.AfterDeserialization()
|
|
||||||
{
|
|
||||||
//Our prototypes don't force the user to define a BodyPart connection twice. E.g. Head: Torso v.s. Torso: Head.
|
|
||||||
//The user only has to do one. We want it to be that way in the code, though, so this cleans that up.
|
|
||||||
var cleanedConnections = new Dictionary<string, HashSet<string>>();
|
|
||||||
|
|
||||||
foreach (var targetSlotName in _slots.Keys)
|
|
||||||
{
|
|
||||||
var tempConnections = new HashSet<string>();
|
|
||||||
foreach (var (slotName, slotConnections) in _rawConnections)
|
|
||||||
{
|
|
||||||
if (slotName == targetSlotName)
|
|
||||||
{
|
|
||||||
foreach (var connection in slotConnections)
|
|
||||||
{
|
|
||||||
if (!tempConnections.Contains(connection))
|
|
||||||
{
|
|
||||||
tempConnections.Add(connection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (slotConnections.Contains(targetSlotName))
|
|
||||||
{
|
|
||||||
tempConnections.Add(slotName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tempConnections.Count > 0)
|
|
||||||
{
|
|
||||||
cleanedConnections.Add(targetSlotName, tempConnections);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections = cleanedConnections;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
213
Content.Shared/Body/Systems/SharedBodySystem.Body.cs
Normal file
213
Content.Shared/Body/Systems/SharedBodySystem.Body.cs
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
using Content.Shared.Body.Components;
|
||||||
|
using Content.Shared.Body.Organ;
|
||||||
|
using Content.Shared.Body.Part;
|
||||||
|
using Content.Shared.Body.Prototypes;
|
||||||
|
using Content.Shared.Coordinates;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Body.Systems;
|
||||||
|
|
||||||
|
public partial class SharedBodySystem
|
||||||
|
{
|
||||||
|
public void InitializeBody()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<BodyComponent, MapInitEvent>(OnBodyMapInit);
|
||||||
|
SubscribeLocalEvent<BodyComponent, ComponentInit>(OnBodyInit);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<BodyComponent, ComponentGetState>(OnBodyGetState);
|
||||||
|
SubscribeLocalEvent<BodyComponent, ComponentHandleState>(OnBodyHandleState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBodyMapInit(EntityUid bodyId, BodyComponent body, MapInitEvent args)
|
||||||
|
{
|
||||||
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||||
|
if (body.Prototype == null || body.Root != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var prototype = Prototypes.Index<BodyPrototype>(body.Prototype);
|
||||||
|
InitBody(body, prototype);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBodyInit(EntityUid bodyId, BodyComponent body, ComponentInit args)
|
||||||
|
{
|
||||||
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||||
|
if (body.Prototype == null || body.Root != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var prototype = Prototypes.Index<BodyPrototype>(body.Prototype);
|
||||||
|
InitBody(body, prototype);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBodyGetState(EntityUid uid, BodyComponent body, ref ComponentGetState args)
|
||||||
|
{
|
||||||
|
args.State = new BodyComponentState(body.Root, body.GibSound);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBodyHandleState(EntityUid uid, BodyComponent body, ref ComponentHandleState args)
|
||||||
|
{
|
||||||
|
if (args.Current is not BodyComponentState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
body.Root = state.Root;
|
||||||
|
body.GibSound = state.GibSound;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryCreateBodyRootSlot(
|
||||||
|
EntityUid? bodyId,
|
||||||
|
string slotId,
|
||||||
|
[NotNullWhen(true)] out BodyPartSlot? slot,
|
||||||
|
BodyComponent? body = null)
|
||||||
|
{
|
||||||
|
slot = null;
|
||||||
|
|
||||||
|
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
|
||||||
|
if (bodyId == null ||
|
||||||
|
!Resolve(bodyId.Value, ref body, false) ||
|
||||||
|
body.Root != null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
slot = new BodyPartSlot(slotId, bodyId.Value, null);
|
||||||
|
body.Root = slot;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitBody(BodyComponent body, BodyPrototype prototype)
|
||||||
|
{
|
||||||
|
var root = prototype.Slots[prototype.Root];
|
||||||
|
var bodyId = Spawn(root.Part, body.Owner.ToCoordinates());
|
||||||
|
var partComponent = Comp<BodyPartComponent>(bodyId);
|
||||||
|
var slot = new BodyPartSlot(root.Part, body.Owner, partComponent.PartType);
|
||||||
|
body.Root = slot;
|
||||||
|
partComponent.Body = bodyId;
|
||||||
|
|
||||||
|
Containers.EnsureContainer<Container>(body.Owner, BodyContainerId);
|
||||||
|
|
||||||
|
AttachPart(bodyId, slot, partComponent);
|
||||||
|
InitPart(partComponent, prototype, prototype.Root);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitPart(BodyPartComponent parent, BodyPrototype prototype, string slotId, HashSet<string>? initialized = null)
|
||||||
|
{
|
||||||
|
initialized ??= new HashSet<string>();
|
||||||
|
|
||||||
|
if (initialized.Contains(slotId))
|
||||||
|
return;
|
||||||
|
|
||||||
|
initialized.Add(slotId);
|
||||||
|
|
||||||
|
var (_, connections, organs) = prototype.Slots[slotId];
|
||||||
|
connections = new HashSet<string>(connections);
|
||||||
|
connections.ExceptWith(initialized);
|
||||||
|
|
||||||
|
var coordinates = parent.Owner.ToCoordinates();
|
||||||
|
var subConnections = new List<(BodyPartComponent child, string slotId)>();
|
||||||
|
|
||||||
|
Containers.EnsureContainer<Container>(parent.Owner, BodyContainerId);
|
||||||
|
|
||||||
|
foreach (var connection in connections)
|
||||||
|
{
|
||||||
|
var childSlot = prototype.Slots[connection];
|
||||||
|
var childPart = Spawn(childSlot.Part, coordinates);
|
||||||
|
var childPartComponent = Comp<BodyPartComponent>(childPart);
|
||||||
|
var slot = CreatePartSlot(connection, parent.Owner, childPartComponent.PartType, parent);
|
||||||
|
if (slot == null)
|
||||||
|
{
|
||||||
|
Logger.Error($"Could not create slot for connection {connection} in body {prototype.ID}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
AttachPart(childPart, slot, childPartComponent);
|
||||||
|
subConnections.Add((childPartComponent, connection));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (organSlotId, organId) in organs)
|
||||||
|
{
|
||||||
|
var organ = Spawn(organId, coordinates);
|
||||||
|
var organComponent = Comp<OrganComponent>(organ);
|
||||||
|
|
||||||
|
var slot = CreateOrganSlot(organSlotId, parent.Owner, parent);
|
||||||
|
if (slot == null)
|
||||||
|
{
|
||||||
|
Logger.Error($"Could not create slot for connection {organSlotId} in body {prototype.ID}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
InsertOrgan(organ, slot, organComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var connection in subConnections)
|
||||||
|
{
|
||||||
|
InitPart(connection.child, prototype, connection.slotId, initialized);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyChildren(EntityUid? id, BodyComponent? body = null)
|
||||||
|
{
|
||||||
|
if (id == null ||
|
||||||
|
!Resolve(id.Value, ref body, false) ||
|
||||||
|
!TryComp(body.Root.Child, out BodyPartComponent? part))
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
yield return (body.Root.Child.Value, part);
|
||||||
|
|
||||||
|
foreach (var child in GetPartChildren(body.Root.Child))
|
||||||
|
{
|
||||||
|
yield return child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<(EntityUid Id, OrganComponent Component)> GetBodyOrgans(EntityUid? bodyId, BodyComponent? body = null)
|
||||||
|
{
|
||||||
|
if (bodyId == null || !Resolve(bodyId.Value, ref body, false))
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
foreach (var part in GetBodyChildren(bodyId, body))
|
||||||
|
{
|
||||||
|
foreach (var organ in GetPartOrgans(part.Id, part.Component))
|
||||||
|
{
|
||||||
|
yield return organ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<BodyPartSlot> GetBodyAllSlots(EntityUid? bodyId, BodyComponent? body = null)
|
||||||
|
{
|
||||||
|
if (bodyId == null || !Resolve(bodyId.Value, ref body, false))
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
foreach (var slot in GetPartAllSlots(body.Root.Child))
|
||||||
|
{
|
||||||
|
yield return slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual HashSet<EntityUid> GibBody(EntityUid? partId, bool gibOrgans = false,
|
||||||
|
BodyComponent? body = null)
|
||||||
|
{
|
||||||
|
if (partId == null || !Resolve(partId.Value, ref body, false))
|
||||||
|
return new HashSet<EntityUid>();
|
||||||
|
|
||||||
|
var parts = GetBodyChildren(partId, body).ToArray();
|
||||||
|
var gibs = new HashSet<EntityUid>(parts.Length);
|
||||||
|
|
||||||
|
foreach (var part in parts)
|
||||||
|
{
|
||||||
|
DropPart(part.Id, part.Component);
|
||||||
|
gibs.Add(part.Id);
|
||||||
|
|
||||||
|
if (!gibOrgans)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foreach (var organ in GetPartOrgans(part.Id, part.Component))
|
||||||
|
{
|
||||||
|
DropOrgan(organ.Id, organ.Component);
|
||||||
|
gibs.Add(organ.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gibs;
|
||||||
|
}
|
||||||
|
}
|
||||||
230
Content.Shared/Body/Systems/SharedBodySystem.Organs.cs
Normal file
230
Content.Shared/Body/Systems/SharedBodySystem.Organs.cs
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Content.Shared.Body.Components;
|
||||||
|
using Content.Shared.Body.Events;
|
||||||
|
using Content.Shared.Body.Organ;
|
||||||
|
using Content.Shared.Body.Part;
|
||||||
|
using Content.Shared.Random.Helpers;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
|
||||||
|
namespace Content.Shared.Body.Systems;
|
||||||
|
|
||||||
|
public partial class SharedBodySystem
|
||||||
|
{
|
||||||
|
private void InitializeOrgans()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<OrganComponent, ComponentGetState>(OnOrganGetState);
|
||||||
|
SubscribeLocalEvent<OrganComponent, ComponentHandleState>(OnOrganHandleState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private OrganSlot? CreateOrganSlot(string slotId, EntityUid parent, BodyPartComponent? part = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(parent, ref part, false))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var slot = new OrganSlot(slotId, parent);
|
||||||
|
part.Organs.Add(slotId, slot);
|
||||||
|
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CanInsertOrgan(EntityUid? organId, OrganSlot slot, OrganComponent? organ = null)
|
||||||
|
{
|
||||||
|
return organId != null &&
|
||||||
|
slot.Child == null &&
|
||||||
|
Resolve(organId.Value, ref organ, false) &&
|
||||||
|
Containers.TryGetContainer(slot.Parent, BodyContainerId, out var container) &&
|
||||||
|
container.CanInsert(organId.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnOrganGetState(EntityUid uid, OrganComponent organ, ref ComponentGetState args)
|
||||||
|
{
|
||||||
|
args.State = new OrganComponentState(organ.Body, organ.ParentSlot);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnOrganHandleState(EntityUid uid, OrganComponent organ, ref ComponentHandleState args)
|
||||||
|
{
|
||||||
|
if (args.Current is not OrganComponentState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
organ.Body = state.Body;
|
||||||
|
organ.ParentSlot = state.Parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool InsertOrgan(EntityUid? organId, OrganSlot slot, OrganComponent? organ = null)
|
||||||
|
{
|
||||||
|
if (organId == null ||
|
||||||
|
!Resolve(organId.Value, ref organ, false) ||
|
||||||
|
!CanInsertOrgan(organId, slot, organ))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DropOrgan(slot.Child);
|
||||||
|
DropOrgan(organId, organ);
|
||||||
|
|
||||||
|
var container = Containers.EnsureContainer<Container>(slot.Parent, BodyContainerId);
|
||||||
|
if (!container.Insert(organId.Value))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
slot.Child = organId;
|
||||||
|
organ.ParentSlot = slot;
|
||||||
|
organ.Body = CompOrNull<BodyPartComponent>(slot.Parent)?.Body;
|
||||||
|
|
||||||
|
Dirty(slot.Parent);
|
||||||
|
Dirty(organId.Value);
|
||||||
|
|
||||||
|
if (organ.Body == null)
|
||||||
|
{
|
||||||
|
RaiseLocalEvent(organId.Value, new AddedToPartEvent(slot.Parent));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RaiseLocalEvent(organId.Value, new AddedToPartInBodyEvent(organ.Body.Value, slot.Parent));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool AddOrganToFirstValidSlot(
|
||||||
|
EntityUid? childId,
|
||||||
|
EntityUid? parentId,
|
||||||
|
OrganComponent? child = null,
|
||||||
|
BodyPartComponent? parent = null)
|
||||||
|
{
|
||||||
|
if (childId == null ||
|
||||||
|
!Resolve(childId.Value, ref child, false) ||
|
||||||
|
parentId == null ||
|
||||||
|
!Resolve(parentId.Value, ref parent, false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
foreach (var slot in parent.Organs.Values)
|
||||||
|
{
|
||||||
|
if (slot.Child == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
InsertOrgan(childId, slot, child);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DropOrgan(EntityUid? organId, OrganComponent? organ = null)
|
||||||
|
{
|
||||||
|
if (organId == null ||
|
||||||
|
!Resolve(organId.Value, ref organ, false) ||
|
||||||
|
organ.ParentSlot is not { } slot)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var oldParent = CompOrNull<BodyPartComponent>(organ.ParentSlot.Parent);
|
||||||
|
|
||||||
|
slot.Child = null;
|
||||||
|
organ.ParentSlot = null;
|
||||||
|
organ.Body = null;
|
||||||
|
|
||||||
|
if (Containers.TryGetContainer(slot.Parent, BodyContainerId, out var container))
|
||||||
|
container.Remove(organId.Value);
|
||||||
|
|
||||||
|
if (TryComp(organId, out TransformComponent? transform))
|
||||||
|
transform.AttachToGridOrMap();
|
||||||
|
|
||||||
|
organ.Owner.RandomOffset(0.25f);
|
||||||
|
|
||||||
|
if (oldParent == null)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (oldParent.Body != null)
|
||||||
|
{
|
||||||
|
RaiseLocalEvent(organId.Value, new RemovedFromPartInBodyEvent(oldParent.Body.Value, oldParent.Owner));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RaiseLocalEvent(organId.Value, new RemovedFromPartEvent(oldParent.Owner));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DropOrganAt(EntityUid? organId, EntityCoordinates dropAt, OrganComponent? organ = null)
|
||||||
|
{
|
||||||
|
if (organId == null || !DropOrgan(organId, organ))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (TryComp(organId.Value, out TransformComponent? transform))
|
||||||
|
transform.Coordinates = dropAt;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DeleteOrgan(EntityUid? id, OrganComponent? part = null)
|
||||||
|
{
|
||||||
|
if (id == null || !Resolve(id.Value, ref part, false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DropOrgan(id, part);
|
||||||
|
|
||||||
|
if (Deleted(id.Value))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Del(id.Value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns a list of ValueTuples of <see cref="T"/> and OrganComponent on each organ
|
||||||
|
/// 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 organs on.</param>
|
||||||
|
/// <typeparam name="T">The component to check for.</typeparam>
|
||||||
|
public List<(T Comp, OrganComponent Organ)> GetBodyOrganComponents<T>(
|
||||||
|
EntityUid uid,
|
||||||
|
BodyComponent? body = null)
|
||||||
|
where T : Component
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref body))
|
||||||
|
return new List<(T Comp, OrganComponent Organ)>();
|
||||||
|
|
||||||
|
var query = EntityManager.GetEntityQuery<T>();
|
||||||
|
var list = new List<(T Comp, OrganComponent Organ)>(3);
|
||||||
|
foreach (var organ in GetBodyOrgans(uid, body))
|
||||||
|
{
|
||||||
|
if (query.TryGetComponent(organ.Id, out var comp))
|
||||||
|
list.Add((comp, organ.Component));
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to get a list of ValueTuples of <see cref="T"/> and OrganComponent on each organs
|
||||||
|
/// 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 organs on.</param>
|
||||||
|
/// <typeparam name="T">The component to check for.</typeparam>
|
||||||
|
/// <returns>Whether any were found.</returns>
|
||||||
|
public bool TryGetBodyOrganComponents<T>(
|
||||||
|
EntityUid uid,
|
||||||
|
[NotNullWhen(true)] out List<(T Comp, OrganComponent Organ)>? comps,
|
||||||
|
BodyComponent? body = null)
|
||||||
|
where T : Component
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref body))
|
||||||
|
{
|
||||||
|
comps = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
comps = GetBodyOrganComponents<T>(uid, body);
|
||||||
|
|
||||||
|
if (comps.Count == 0)
|
||||||
|
{
|
||||||
|
comps = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
359
Content.Shared/Body/Systems/SharedBodySystem.Parts.cs
Normal file
359
Content.Shared/Body/Systems/SharedBodySystem.Parts.cs
Normal file
@@ -0,0 +1,359 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
using Content.Shared.Body.Components;
|
||||||
|
using Content.Shared.Body.Events;
|
||||||
|
using Content.Shared.Body.Organ;
|
||||||
|
using Content.Shared.Body.Part;
|
||||||
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Damage.Prototypes;
|
||||||
|
using Content.Shared.Random.Helpers;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
|
||||||
|
namespace Content.Shared.Body.Systems;
|
||||||
|
|
||||||
|
public partial class SharedBodySystem
|
||||||
|
{
|
||||||
|
private void InitializeParts()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<BodyPartComponent, ComponentRemove>(OnPartRemoved);
|
||||||
|
SubscribeLocalEvent<BodyPartComponent, ComponentGetState>(OnPartGetState);
|
||||||
|
SubscribeLocalEvent<BodyPartComponent, ComponentHandleState>(OnPartHandleState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPartGetState(EntityUid uid, BodyPartComponent part, ref ComponentGetState args)
|
||||||
|
{
|
||||||
|
args.State = new BodyPartComponentState(
|
||||||
|
part.Body,
|
||||||
|
part.ParentSlot,
|
||||||
|
part.Children,
|
||||||
|
part.Organs,
|
||||||
|
part.PartType,
|
||||||
|
part.IsVital,
|
||||||
|
part.Symmetry
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPartHandleState(EntityUid uid, BodyPartComponent part, ref ComponentHandleState args)
|
||||||
|
{
|
||||||
|
if (args.Current is not BodyPartComponentState state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
part.Body = state.Body;
|
||||||
|
part.ParentSlot = state.ParentSlot;
|
||||||
|
part.Children = state.Children;
|
||||||
|
part.Organs = state.Organs;
|
||||||
|
part.PartType = state.PartType;
|
||||||
|
part.IsVital = state.IsVital;
|
||||||
|
part.Symmetry = state.Symmetry;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPartRemoved(EntityUid uid, BodyPartComponent part, ComponentRemove args)
|
||||||
|
{
|
||||||
|
if (part.ParentSlot is { } slot)
|
||||||
|
{
|
||||||
|
slot.Child = null;
|
||||||
|
Dirty(slot.Parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var childSlot in part.Children.Values.ToArray())
|
||||||
|
{
|
||||||
|
DropPart(childSlot.Child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private BodyPartSlot? CreatePartSlot(
|
||||||
|
string slotId,
|
||||||
|
EntityUid parent,
|
||||||
|
BodyPartType partType,
|
||||||
|
BodyPartComponent? part = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(parent, ref part, false))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var slot = new BodyPartSlot(slotId, parent, partType);
|
||||||
|
part.Children.Add(slotId, slot);
|
||||||
|
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryCreatePartSlot(
|
||||||
|
EntityUid? parentId,
|
||||||
|
string id,
|
||||||
|
[NotNullWhen(true)] out BodyPartSlot? slot,
|
||||||
|
BodyPartComponent? parent = null)
|
||||||
|
{
|
||||||
|
slot = null;
|
||||||
|
|
||||||
|
if (parentId == null ||
|
||||||
|
!Resolve(parentId.Value, ref parent, false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
slot = new BodyPartSlot(id, parentId.Value, null);
|
||||||
|
if (!parent.Children.TryAdd(id, slot))
|
||||||
|
{
|
||||||
|
slot = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryCreatePartSlotAndAttach(
|
||||||
|
EntityUid? parentId,
|
||||||
|
string id,
|
||||||
|
EntityUid? childId,
|
||||||
|
BodyPartComponent? parent = null,
|
||||||
|
BodyPartComponent? child = null)
|
||||||
|
{
|
||||||
|
return TryCreatePartSlot(parentId, id, out var slot, parent) && AttachPart(childId, slot, child);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetPartChildren(EntityUid? id, BodyPartComponent? part = null)
|
||||||
|
{
|
||||||
|
if (id == null || !Resolve(id.Value, ref part, false))
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
foreach (var slot in part.Children.Values)
|
||||||
|
{
|
||||||
|
if (!TryComp(slot.Child, out BodyPartComponent? childPart))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
yield return (slot.Child.Value, childPart);
|
||||||
|
|
||||||
|
foreach (var subChild in GetPartChildren(slot.Child, childPart))
|
||||||
|
{
|
||||||
|
yield return subChild;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<(EntityUid Id, OrganComponent Component)> GetPartOrgans(EntityUid? partId, BodyPartComponent? part = null)
|
||||||
|
{
|
||||||
|
if (partId == null || !Resolve(partId.Value, ref part, false))
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
foreach (var slot in part.Organs.Values)
|
||||||
|
{
|
||||||
|
if (!TryComp(slot.Child, out OrganComponent? organ))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
yield return (slot.Child.Value, organ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<BodyPartSlot> GetPartAllSlots(EntityUid? partId, BodyPartComponent? part = null)
|
||||||
|
{
|
||||||
|
if (partId == null ||
|
||||||
|
!Resolve(partId.Value, ref part, false))
|
||||||
|
yield break;
|
||||||
|
|
||||||
|
foreach (var slot in part.Children.Values)
|
||||||
|
{
|
||||||
|
yield return slot;
|
||||||
|
|
||||||
|
if (!TryComp(slot.Child, out BodyComponent? childPart))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foreach (var subChild in GetBodyAllSlots(slot.Child, childPart))
|
||||||
|
{
|
||||||
|
yield return subChild;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanAttachPart([NotNullWhen(true)] EntityUid? partId, BodyPartSlot slot, BodyPartComponent? part = null)
|
||||||
|
{
|
||||||
|
return partId != null &&
|
||||||
|
slot.Child == null &&
|
||||||
|
Resolve(partId.Value, ref part, false) &&
|
||||||
|
(slot.Type == null || slot.Type == part.PartType) &&
|
||||||
|
Containers.TryGetContainer(slot.Parent, BodyContainerId, out var container) &&
|
||||||
|
container.CanInsert(partId.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool AttachPart(
|
||||||
|
EntityUid? partId,
|
||||||
|
BodyPartSlot slot,
|
||||||
|
[NotNullWhen(true)] BodyPartComponent? part = null)
|
||||||
|
{
|
||||||
|
if (partId == null ||
|
||||||
|
!Resolve(partId.Value, ref part, false) ||
|
||||||
|
!CanAttachPart(partId, slot, part))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DropPart(slot.Child);
|
||||||
|
DropPart(partId, part);
|
||||||
|
|
||||||
|
var container = Containers.EnsureContainer<Container>(slot.Parent, BodyContainerId);
|
||||||
|
if (!container.Insert(partId.Value))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
slot.Child = partId;
|
||||||
|
part.ParentSlot = slot;
|
||||||
|
|
||||||
|
if (TryComp(slot.Parent, out BodyPartComponent? parentPart))
|
||||||
|
{
|
||||||
|
part.Body = parentPart.Body;
|
||||||
|
}
|
||||||
|
else if (TryComp(slot.Parent, out BodyComponent? parentBody))
|
||||||
|
{
|
||||||
|
part.Body = parentBody.Owner;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
part.Body = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dirty(slot.Parent);
|
||||||
|
Dirty(partId.Value);
|
||||||
|
|
||||||
|
if (part.Body is { } newBody)
|
||||||
|
{
|
||||||
|
var argsAdded = new BodyPartAddedEventArgs(slot.Id, part);
|
||||||
|
|
||||||
|
// TODO: Body refactor. Somebody is doing it
|
||||||
|
// EntitySystem.Get<SharedHumanoidAppearanceSystem>().BodyPartAdded(Owner, argsAdded);
|
||||||
|
foreach (var component in AllComps<IBodyPartAdded>(newBody).ToArray())
|
||||||
|
{
|
||||||
|
component.BodyPartAdded(argsAdded);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var organ in GetPartOrgans(partId, part))
|
||||||
|
{
|
||||||
|
RaiseLocalEvent(organ.Id, new AddedToBodyEvent(newBody), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Dirty(newBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool DropPart(EntityUid? partId, [NotNullWhen(true)] BodyPartComponent? part = null)
|
||||||
|
{
|
||||||
|
if (partId == null ||
|
||||||
|
!Resolve(partId.Value, ref part, false) ||
|
||||||
|
part.ParentSlot is not { } slot)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var oldBody = part.Body;
|
||||||
|
|
||||||
|
slot.Child = null;
|
||||||
|
part.ParentSlot = null;
|
||||||
|
part.Body = null;
|
||||||
|
|
||||||
|
if (Containers.TryGetContainer(slot.Parent, BodyContainerId, out var container))
|
||||||
|
container.Remove(partId.Value);
|
||||||
|
|
||||||
|
if (TryComp(partId, out TransformComponent? transform))
|
||||||
|
transform.AttachToGridOrMap();
|
||||||
|
|
||||||
|
part.Owner.RandomOffset(0.25f);
|
||||||
|
|
||||||
|
if (oldBody != null)
|
||||||
|
{
|
||||||
|
var args = new BodyPartRemovedEventArgs(slot.Id, part);
|
||||||
|
foreach (var component in AllComps<IBodyPartRemoved>(oldBody.Value))
|
||||||
|
{
|
||||||
|
component.BodyPartRemoved(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (part.PartType == BodyPartType.Leg &&
|
||||||
|
!GetBodyChildrenOfType(oldBody, BodyPartType.Leg).Any())
|
||||||
|
{
|
||||||
|
Standing.Down(oldBody.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (part.IsVital && !GetBodyChildrenOfType(oldBody, part.PartType).Any())
|
||||||
|
{
|
||||||
|
// TODO BODY SYSTEM KILL : Find a more elegant way of killing em than just dumping bloodloss damage.
|
||||||
|
var damage = new DamageSpecifier(Prototypes.Index<DamageTypePrototype>("Bloodloss"), 300);
|
||||||
|
Damageable.TryChangeDamage(part.Owner, damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var organSlot in part.Organs.Values)
|
||||||
|
{
|
||||||
|
if (organSlot.Child is not { } child)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
RaiseLocalEvent(child, new RemovedFromBodyEvent(oldBody.Value), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dirty(slot.Parent);
|
||||||
|
Dirty(partId.Value);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DropPartAt(EntityUid? partId, EntityCoordinates dropAt, BodyPartComponent? part = null)
|
||||||
|
{
|
||||||
|
if (partId == null || !DropPart(partId, part))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (TryComp(partId.Value, out TransformComponent? transform))
|
||||||
|
transform.Coordinates = dropAt;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool OrphanPart(EntityUid? partId, BodyPartComponent? part = null)
|
||||||
|
{
|
||||||
|
if (partId == null || !Resolve(partId.Value, ref part, false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DropPart(partId, part);
|
||||||
|
|
||||||
|
foreach (var slot in part.Children.Values)
|
||||||
|
{
|
||||||
|
DropPart(slot.Child);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DeletePart(EntityUid? id, BodyPartComponent? part = null)
|
||||||
|
{
|
||||||
|
if (id == null || !Resolve(id.Value, ref part, false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
DropPart(id, part);
|
||||||
|
|
||||||
|
if (Deleted(id.Value))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Del(id.Value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<(EntityUid Id, BodyPartComponent Component)> GetBodyChildrenOfType(EntityUid? bodyId, BodyPartType type, BodyComponent? body = null)
|
||||||
|
{
|
||||||
|
foreach (var part in GetBodyChildren(bodyId, body))
|
||||||
|
{
|
||||||
|
if (part.Component.PartType == type)
|
||||||
|
yield return part;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool BodyHasChildOfType(EntityUid? bodyId, BodyPartType type, BodyComponent? body = null)
|
||||||
|
{
|
||||||
|
return GetBodyChildrenOfType(bodyId, type, body).Any();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool BodyHasChild(
|
||||||
|
EntityUid? parentId,
|
||||||
|
EntityUid? childId,
|
||||||
|
BodyComponent? parent = null,
|
||||||
|
BodyPartComponent? child = null)
|
||||||
|
{
|
||||||
|
if (parentId == null ||
|
||||||
|
!Resolve(parentId.Value, ref parent, false) ||
|
||||||
|
childId == null ||
|
||||||
|
!Resolve(childId.Value, ref child, false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return child.ParentSlot?.Child == parentId;
|
||||||
|
}
|
||||||
|
}
|
||||||
26
Content.Shared/Body/Systems/SharedBodySystem.cs
Normal file
26
Content.Shared/Body/Systems/SharedBodySystem.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using Content.Shared.Damage;
|
||||||
|
using Content.Shared.Standing;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.Body.Systems;
|
||||||
|
|
||||||
|
public abstract partial class SharedBodySystem : EntitySystem
|
||||||
|
{
|
||||||
|
private const string BodyContainerId = "BodyContainer";
|
||||||
|
|
||||||
|
[Dependency] protected readonly IPrototypeManager Prototypes = default!;
|
||||||
|
|
||||||
|
[Dependency] protected readonly SharedContainerSystem Containers = default!;
|
||||||
|
[Dependency] protected readonly DamageableSystem Damageable = default!;
|
||||||
|
[Dependency] protected readonly StandingStateSystem Standing = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
InitializeBody();
|
||||||
|
InitializeParts();
|
||||||
|
InitializeOrgans();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,14 +4,19 @@ namespace Content.Shared.Coordinates
|
|||||||
{
|
{
|
||||||
public static class EntityCoordinatesExtensions
|
public static class EntityCoordinatesExtensions
|
||||||
{
|
{
|
||||||
|
public static EntityCoordinates ToCoordinates(this EntityUid id)
|
||||||
|
{
|
||||||
|
return new EntityCoordinates(id, new Vector2(0, 0));
|
||||||
|
}
|
||||||
|
|
||||||
public static EntityCoordinates ToCoordinates(this EntityUid id, Vector2 offset)
|
public static EntityCoordinates ToCoordinates(this EntityUid id, Vector2 offset)
|
||||||
{
|
{
|
||||||
return new(id, offset);
|
return new EntityCoordinates(id, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EntityCoordinates ToCoordinates(this EntityUid id, float x, float y)
|
public static EntityCoordinates ToCoordinates(this EntityUid id, float x, float y)
|
||||||
{
|
{
|
||||||
return new(id, x, y);
|
return new EntityCoordinates(id, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EntityCoordinates ToCoordinates(this IMapGrid grid, Vector2 offset)
|
public static EntityCoordinates ToCoordinates(this IMapGrid grid, Vector2 offset)
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ using Content.Shared.MobState.Components;
|
|||||||
using Content.Shared.MobState.EntitySystems;
|
using Content.Shared.MobState.EntitySystems;
|
||||||
using Content.Shared.Throwing;
|
using Content.Shared.Throwing;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.Physics;
|
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
using Robust.Shared.Physics.Dynamics;
|
|
||||||
using Robust.Shared.Physics.Events;
|
using Robust.Shared.Physics.Events;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
@@ -65,7 +63,7 @@ namespace Content.Shared.Disposal
|
|||||||
|
|
||||||
// TODO: Probably just need a disposable tag.
|
// TODO: Probably just need a disposable tag.
|
||||||
if (!EntityManager.TryGetComponent(entity, out ItemComponent? storable) &&
|
if (!EntityManager.TryGetComponent(entity, out ItemComponent? storable) &&
|
||||||
!EntityManager.HasComponent<SharedBodyComponent>(entity))
|
!EntityManager.HasComponent<BodyComponent>(entity))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ namespace Content.Shared.Humanoid
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static HumanoidVisualLayers? ToHumanoidLayers(this SharedBodyPartComponent part)
|
public static HumanoidVisualLayers? ToHumanoidLayers(this BodyPartComponent part)
|
||||||
{
|
{
|
||||||
switch (part.PartType)
|
switch (part.PartType)
|
||||||
{
|
{
|
||||||
@@ -85,6 +85,7 @@ namespace Content.Shared.Humanoid
|
|||||||
case BodyPartSymmetry.Right:
|
case BodyPartSymmetry.Right:
|
||||||
return HumanoidVisualLayers.RArm;
|
return HumanoidVisualLayers.RArm;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case BodyPartType.Hand:
|
case BodyPartType.Hand:
|
||||||
switch (part.Symmetry)
|
switch (part.Symmetry)
|
||||||
@@ -96,6 +97,7 @@ namespace Content.Shared.Humanoid
|
|||||||
case BodyPartSymmetry.Right:
|
case BodyPartSymmetry.Right:
|
||||||
return HumanoidVisualLayers.RHand;
|
return HumanoidVisualLayers.RHand;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case BodyPartType.Leg:
|
case BodyPartType.Leg:
|
||||||
switch (part.Symmetry)
|
switch (part.Symmetry)
|
||||||
@@ -107,6 +109,7 @@ namespace Content.Shared.Humanoid
|
|||||||
case BodyPartSymmetry.Right:
|
case BodyPartSymmetry.Right:
|
||||||
return HumanoidVisualLayers.RLeg;
|
return HumanoidVisualLayers.RLeg;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case BodyPartType.Foot:
|
case BodyPartType.Foot:
|
||||||
switch (part.Symmetry)
|
switch (part.Symmetry)
|
||||||
@@ -118,6 +121,7 @@ namespace Content.Shared.Humanoid
|
|||||||
case BodyPartSymmetry.Right:
|
case BodyPartSymmetry.Right:
|
||||||
return HumanoidVisualLayers.RFoot;
|
return HumanoidVisualLayers.RFoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ namespace Content.Shared.MedicalScanner
|
|||||||
|
|
||||||
public bool CanInsert(EntityUid entity)
|
public bool CanInsert(EntityUid entity)
|
||||||
{
|
{
|
||||||
return IoCManager.Resolve<IEntityManager>().HasComponent<SharedBodyComponent>(entity);
|
return IoCManager.Resolve<IEntityManager>().HasComponent<BodyComponent>(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IDragDropOn.CanDragDropOn(DragDropEvent eventArgs)
|
bool IDragDropOn.CanDragDropOn(DragDropEvent eventArgs)
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
reassemble-action = Reassemble
|
|
||||||
reassemble-description = Reassemble the pieces of your body.
|
|
||||||
reassemble-fail = Parts are missing!
|
|
||||||
reassemble-success = {CAPITALIZE(THE($user))} was put back together.
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
## Entity
|
|
||||||
|
|
||||||
bodypart-component-no-way-to-install-message = You see no way to install the {$partName}
|
|
||||||
bodypart-component-no-way-to-attach-message = You see no useful way to attach the {$partName} anymore.
|
|
||||||
bodypart-component-attach-success-message = You attach {$partName}
|
|
||||||
bodypart-component-attach-fail-message = You can't attach {$partName}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
## Entity
|
|
||||||
|
|
||||||
mechanism-component-cannot-fit-message = You can't fit it in!
|
|
||||||
mechanism-component-no-way-to-install-message = You see no way to install the {$partName}
|
|
||||||
mechanism-component-no-useful-way-to-use-message = You see no useful way to use the {$partName} anymore.
|
|
||||||
mechanism-component-jam-inside-message = You jam {$ownerName} inside {$them}.
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
biological-surgery-data-component-has-incision-not-clamped-message = The skin on their {$bodyPart} has an incision, but it is prone to bleeding.
|
|
||||||
biological-surgery-data-component-has-clamped-incision-not-retracted-message = The skin on their {$bodyPart} has an incision, but it is not retracted.
|
|
||||||
biological-surgery-data-component-has-fully-open-incision-message = There is an incision on their {$bodyPart}.
|
|
||||||
biological-surgery-data-component-part-is-loose-message = {$owner}'s {$bodyPart} is loose.
|
|
||||||
biological-surgery-data-component-open-skin-message = Cut open the skin...
|
|
||||||
biological-surgery-data-component-clamp-vessels-message = Clamp the vessels...
|
|
||||||
biological-surgery-data-component-retract-skin-message = Retracting the skin...
|
|
||||||
biological-surgery-data-component-cauterize-incision-message = Cauterizing the incision...
|
|
||||||
biological-surgery-data-component-loosen-organ-message = Loosening the organ...
|
|
||||||
biological-surgery-data-component-remove-organ-message = Removing the organ...
|
|
||||||
biological-surgery-data-component-remove-bodypart-message = Sawing off the limb!
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
surgery-tool-component-not-useful-message = You see no useful way to use {$bodyPart}.
|
|
||||||
surgery-tool-component-not-useful-anymore-message = You see no useful way to use {$bodyPart} anymore.
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
## UI
|
|
||||||
|
|
||||||
surgery-window-title = Surgery
|
|
||||||
surgery-window-not-available-button-text = N/A
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
- type: entity
|
|
||||||
id: MechanismEMPStriker
|
|
||||||
name: "EMP striker"
|
|
||||||
description: "When activated, this arm implant will apply a small EMP on the target of a physical strike for 10 watts per use."
|
|
||||||
noSpawn: true
|
|
||||||
components:
|
|
||||||
- type: Sprite
|
|
||||||
- type: Icon
|
|
||||||
- type: Mechanism
|
|
||||||
size: 4
|
|
||||||
compatibility: Universal
|
|
||||||
|
|
||||||
- type: entity
|
|
||||||
id: MechanismHonkModule
|
|
||||||
name: "HONK module 3000"
|
|
||||||
description: "Mandatory implant for all clowns after the Genevo Convention of 2459."
|
|
||||||
noSpawn: true
|
|
||||||
components:
|
|
||||||
- type: Sprite
|
|
||||||
- type: Icon
|
|
||||||
- type: Mechanism
|
|
||||||
size: 3
|
|
||||||
compatibility: Universal
|
|
||||||
90
Resources/Prototypes/Body/Organs/animal.yml
Normal file
90
Resources/Prototypes/Body/Organs/animal.yml
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
- type: entity
|
||||||
|
id: BaseAnimalOrgan
|
||||||
|
parent: BaseItem
|
||||||
|
abstract: true
|
||||||
|
components:
|
||||||
|
- type: Organ
|
||||||
|
- type: Sprite
|
||||||
|
netsync: false
|
||||||
|
sprite: Mobs/Species/Human/organs.rsi
|
||||||
|
- type: StaticPrice
|
||||||
|
price: 50
|
||||||
|
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: OrganAnimalLungs
|
||||||
|
parent: BaseAnimalOrgan
|
||||||
|
name: lungs
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: Organ
|
||||||
|
- type: Lung
|
||||||
|
- type: Metabolizer
|
||||||
|
removeEmpty: true
|
||||||
|
solutionOnBody: false
|
||||||
|
solution: "Lung"
|
||||||
|
metabolizerTypes: [ Animal ]
|
||||||
|
groups:
|
||||||
|
- id: Gas
|
||||||
|
rateModifier: 100.0
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: OrganAnimalStomach
|
||||||
|
parent: BaseAnimalOrgan
|
||||||
|
name: stomach
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: Organ
|
||||||
|
- type: SolutionContainerManager
|
||||||
|
solutions:
|
||||||
|
stomach:
|
||||||
|
maxVol: 100
|
||||||
|
- type: Stomach
|
||||||
|
maxVolume: 10
|
||||||
|
- type: Metabolizer
|
||||||
|
maxReagents: 3
|
||||||
|
metabolizerTypes: [ Animal ]
|
||||||
|
groups:
|
||||||
|
- id: Food
|
||||||
|
- id: Drink
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: OrganAnimalLiver
|
||||||
|
parent: BaseAnimalOrgan
|
||||||
|
name: liver
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: Organ
|
||||||
|
- type: Metabolizer
|
||||||
|
maxReagents: 1
|
||||||
|
metabolizerTypes: [ Animal ]
|
||||||
|
groups:
|
||||||
|
- id: Alcohol
|
||||||
|
rateModifier: 0.1
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: OrganAnimalHeart
|
||||||
|
parent: BaseAnimalOrgan
|
||||||
|
name: heart
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: Organ
|
||||||
|
- type: Metabolizer
|
||||||
|
maxReagents: 2
|
||||||
|
metabolizerTypes: [ Animal ]
|
||||||
|
groups:
|
||||||
|
- id: Medicine
|
||||||
|
- id: Poison
|
||||||
|
- id: Narcotic
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: OrganAnimalKidneys
|
||||||
|
parent: BaseAnimalOrgan
|
||||||
|
name: kidneys
|
||||||
|
noSpawn: true
|
||||||
|
components:
|
||||||
|
- type: Organ
|
||||||
|
- type: Metabolizer
|
||||||
|
maxReagents: 5
|
||||||
|
metabolizerTypes: [ Animal ]
|
||||||
|
removeEmpty: true
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
- type: Sprite
|
- type: Sprite
|
||||||
netsync: false
|
netsync: false
|
||||||
sprite: Mobs/Species/Human/organs.rsi
|
sprite: Mobs/Species/Human/organs.rsi
|
||||||
- type: Mechanism
|
- type: Organ
|
||||||
- type: Food
|
- type: Food
|
||||||
- type: Extractable
|
- type: Extractable
|
||||||
grindableSolutionName: organ
|
grindableSolutionName: organ
|
||||||
@@ -25,9 +25,7 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
state: brain
|
state: brain
|
||||||
- type: Mechanism
|
- type: Organ
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
- type: Input
|
- type: Input
|
||||||
context: "ghost"
|
context: "ghost"
|
||||||
- type: InputMover
|
- type: InputMover
|
||||||
@@ -47,9 +45,7 @@
|
|||||||
layers:
|
layers:
|
||||||
- state: eyeball-l
|
- state: eyeball-l
|
||||||
- state: eyeball-r
|
- state: eyeball-r
|
||||||
- type: Mechanism
|
- type: Organ
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: OrganHumanTongue
|
id: OrganHumanTongue
|
||||||
@@ -59,9 +55,7 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
state: tongue
|
state: tongue
|
||||||
- type: Mechanism
|
- type: Organ
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: OrganHumanAppendix
|
id: OrganHumanAppendix
|
||||||
@@ -73,9 +67,7 @@
|
|||||||
- state: appendix
|
- state: appendix
|
||||||
- state: appendix-inflamed
|
- state: appendix-inflamed
|
||||||
visible: false
|
visible: false
|
||||||
- type: Mechanism
|
- type: Organ
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: OrganHumanEars
|
id: OrganHumanEars
|
||||||
@@ -85,9 +77,7 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
state: ears
|
state: ears
|
||||||
- type: Mechanism
|
- type: Organ
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: OrganHumanLungs
|
id: OrganHumanLungs
|
||||||
@@ -99,9 +89,7 @@
|
|||||||
layers:
|
layers:
|
||||||
- state: lung-l
|
- state: lung-l
|
||||||
- state: lung-r
|
- state: lung-r
|
||||||
- type: Mechanism
|
- type: Organ
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
- type: Lung
|
- type: Lung
|
||||||
- type: Metabolizer
|
- type: Metabolizer
|
||||||
removeEmpty: true
|
removeEmpty: true
|
||||||
@@ -120,9 +108,7 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
state: heart-on
|
state: heart-on
|
||||||
- type: Mechanism
|
- type: Organ
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
# The heart 'metabolizes' medicines and poisons that aren't filtered out by other organs.
|
# The heart 'metabolizes' medicines and poisons that aren't filtered out by other organs.
|
||||||
# This is done because these chemicals need to have some effect even if they aren't being filtered out of your body.
|
# This is done because these chemicals need to have some effect even if they aren't being filtered out of your body.
|
||||||
# You're technically 'immune to poison' without a heart, but.. uhh, you'll have bigger problems on your hands.
|
# You're technically 'immune to poison' without a heart, but.. uhh, you'll have bigger problems on your hands.
|
||||||
@@ -142,9 +128,7 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
state: stomach
|
state: stomach
|
||||||
- type: Mechanism
|
- type: Organ
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
- type: SolutionContainerManager
|
- type: SolutionContainerManager
|
||||||
solutions:
|
solutions:
|
||||||
stomach:
|
stomach:
|
||||||
@@ -169,9 +153,7 @@
|
|||||||
components:
|
components:
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
state: liver
|
state: liver
|
||||||
- type: Mechanism
|
- type: Organ
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
- type: Metabolizer # The liver metabolizes certain chemicals only, like alcohol.
|
- type: Metabolizer # The liver metabolizes certain chemicals only, like alcohol.
|
||||||
maxReagents: 1
|
maxReagents: 1
|
||||||
metabolizerTypes: [Human]
|
metabolizerTypes: [Human]
|
||||||
@@ -189,9 +171,7 @@
|
|||||||
layers:
|
layers:
|
||||||
- state: kidney-l
|
- state: kidney-l
|
||||||
- state: kidney-r
|
- state: kidney-r
|
||||||
- type: Mechanism
|
- type: Organ
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
# The kidneys just remove anything that doesn't currently have any metabolisms, as a stopgap.
|
# The kidneys just remove anything that doesn't currently have any metabolisms, as a stopgap.
|
||||||
- type: Metabolizer
|
- type: Metabolizer
|
||||||
maxReagents: 5
|
maxReagents: 5
|
||||||
@@ -8,9 +8,7 @@
|
|||||||
netsync: false
|
netsync: false
|
||||||
sprite: Mobs/Species/Human/organs.rsi
|
sprite: Mobs/Species/Human/organs.rsi
|
||||||
state: brain
|
state: brain
|
||||||
- type: Mechanism
|
- type: Organ
|
||||||
size: 5
|
|
||||||
compatibility: Slime
|
|
||||||
- type: Brain
|
- type: Brain
|
||||||
- type: Stomach
|
- type: Stomach
|
||||||
- type: Metabolizer
|
- type: Metabolizer
|
||||||
@@ -34,9 +32,7 @@
|
|||||||
layers:
|
layers:
|
||||||
- state: lung-l
|
- state: lung-l
|
||||||
- state: lung-r
|
- state: lung-r
|
||||||
- type: Mechanism
|
- type: Organ
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
- type: Lung
|
- type: Lung
|
||||||
- type: Metabolizer
|
- type: Metabolizer
|
||||||
removeEmpty: true
|
removeEmpty: true
|
||||||
@@ -27,8 +27,6 @@
|
|||||||
components:
|
components:
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Hand
|
partType: Hand
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -39,8 +37,6 @@
|
|||||||
components:
|
components:
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Leg
|
partType: Leg
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: FeetAnimal
|
id: FeetAnimal
|
||||||
@@ -50,8 +46,6 @@
|
|||||||
components:
|
components:
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Foot
|
partType: Foot
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: TorsoAnimal
|
id: TorsoAnimal
|
||||||
@@ -61,113 +55,5 @@
|
|||||||
components:
|
components:
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Torso
|
partType: Torso
|
||||||
size: 7
|
|
||||||
compatibility: Biological
|
|
||||||
mechanisms:
|
|
||||||
- OrganAnimalLungs
|
|
||||||
- OrganAnimalStomach
|
|
||||||
- OrganAnimalLiver
|
|
||||||
- OrganAnimalHeart
|
|
||||||
- OrganAnimalKidneys
|
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: Biological
|
damageContainer: Biological
|
||||||
|
|
||||||
- type: entity
|
|
||||||
id: BaseAnimalOrgan
|
|
||||||
parent: BaseItem
|
|
||||||
abstract: true
|
|
||||||
components:
|
|
||||||
- type: Mechanism
|
|
||||||
- type: Sprite
|
|
||||||
netsync: false
|
|
||||||
sprite: Mobs/Species/Human/organs.rsi
|
|
||||||
- type: StaticPrice
|
|
||||||
price: 50
|
|
||||||
|
|
||||||
- type: entity
|
|
||||||
id: OrganAnimalLungs
|
|
||||||
parent: BaseAnimalOrgan
|
|
||||||
name: lungs
|
|
||||||
noSpawn: true
|
|
||||||
components:
|
|
||||||
- type: Mechanism
|
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
- type: Lung
|
|
||||||
- type: Metabolizer
|
|
||||||
removeEmpty: true
|
|
||||||
solutionOnBody: false
|
|
||||||
solution: "Lung"
|
|
||||||
metabolizerTypes: [ Animal ]
|
|
||||||
groups:
|
|
||||||
- id: Gas
|
|
||||||
rateModifier: 100.0
|
|
||||||
|
|
||||||
- type: entity
|
|
||||||
id: OrganAnimalStomach
|
|
||||||
parent: BaseAnimalOrgan
|
|
||||||
name: stomach
|
|
||||||
noSpawn: true
|
|
||||||
components:
|
|
||||||
- type: Mechanism
|
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
- type: SolutionContainerManager
|
|
||||||
solutions:
|
|
||||||
stomach:
|
|
||||||
maxVol: 100
|
|
||||||
- type: Stomach
|
|
||||||
maxVolume: 10
|
|
||||||
- type: Metabolizer
|
|
||||||
maxReagents: 3
|
|
||||||
metabolizerTypes: [Animal]
|
|
||||||
groups:
|
|
||||||
- id: Food
|
|
||||||
- id: Drink
|
|
||||||
|
|
||||||
- type: entity
|
|
||||||
id: OrganAnimalLiver
|
|
||||||
parent: BaseAnimalOrgan
|
|
||||||
name: liver
|
|
||||||
noSpawn: true
|
|
||||||
components:
|
|
||||||
- type: Mechanism
|
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
- type: Metabolizer
|
|
||||||
maxReagents: 1
|
|
||||||
metabolizerTypes: [Animal]
|
|
||||||
groups:
|
|
||||||
- id: Alcohol
|
|
||||||
rateModifier: 0.1
|
|
||||||
|
|
||||||
- type: entity
|
|
||||||
id: OrganAnimalHeart
|
|
||||||
parent: BaseAnimalOrgan
|
|
||||||
name: heart
|
|
||||||
noSpawn: true
|
|
||||||
components:
|
|
||||||
- type: Mechanism
|
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
- type: Metabolizer
|
|
||||||
maxReagents: 2
|
|
||||||
metabolizerTypes: [Animal]
|
|
||||||
groups:
|
|
||||||
- id: Medicine
|
|
||||||
- id: Poison
|
|
||||||
- id: Narcotic
|
|
||||||
|
|
||||||
- type: entity
|
|
||||||
id: OrganAnimalKidneys
|
|
||||||
parent: BaseAnimalOrgan
|
|
||||||
name: kidneys
|
|
||||||
noSpawn: true
|
|
||||||
components:
|
|
||||||
- type: Mechanism
|
|
||||||
size: 1
|
|
||||||
compatibility: Biological
|
|
||||||
- type: Metabolizer
|
|
||||||
maxReagents: 5
|
|
||||||
metabolizerTypes: [Animal]
|
|
||||||
removeEmpty: true
|
|
||||||
|
|||||||
@@ -30,16 +30,6 @@
|
|||||||
state: "torso_m"
|
state: "torso_m"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Torso
|
partType: Torso
|
||||||
size: 14
|
|
||||||
compatibility: Biological
|
|
||||||
mechanisms:
|
|
||||||
- OrganHumanHeart
|
|
||||||
- OrganHumanLungs
|
|
||||||
- OrganHumanStomach
|
|
||||||
- OrganHumanLiver
|
|
||||||
- OrganHumanKidneys
|
|
||||||
# criticalThreshold: 100
|
|
||||||
# deadThreshold: 150
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: HeadHuman
|
id: HeadHuman
|
||||||
@@ -55,14 +45,7 @@
|
|||||||
state: "head_m"
|
state: "head_m"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Head
|
partType: Head
|
||||||
size: 7
|
|
||||||
compatibility: Biological
|
|
||||||
vital: true
|
vital: true
|
||||||
mechanisms:
|
|
||||||
- OrganHumanBrain
|
|
||||||
- OrganHumanEyes
|
|
||||||
# criticalThreshold: 50
|
|
||||||
# deadThreshold: 120
|
|
||||||
- type: Input
|
- type: Input
|
||||||
context: "ghost"
|
context: "ghost"
|
||||||
- type: MovementSpeedModifier
|
- type: MovementSpeedModifier
|
||||||
@@ -88,11 +71,7 @@
|
|||||||
state: "l_arm"
|
state: "l_arm"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Arm
|
partType: Arm
|
||||||
size: 5
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
# criticalThreshold: 40
|
|
||||||
# deadThreshold: 80
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: RightArmHuman
|
id: RightArmHuman
|
||||||
@@ -108,11 +87,7 @@
|
|||||||
state: "r_arm"
|
state: "r_arm"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Arm
|
partType: Arm
|
||||||
size: 5
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
# criticalThreshold: 40
|
|
||||||
# deadThreshold: 80
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: LeftHandHuman
|
id: LeftHandHuman
|
||||||
@@ -128,11 +103,7 @@
|
|||||||
state: "l_hand"
|
state: "l_hand"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Hand
|
partType: Hand
|
||||||
size: 3
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
# criticalThreshold: 30
|
|
||||||
# deadThreshold: 60
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: RightHandHuman
|
id: RightHandHuman
|
||||||
@@ -148,11 +119,7 @@
|
|||||||
state: "r_hand"
|
state: "r_hand"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Hand
|
partType: Hand
|
||||||
size: 3
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
# criticalThreshold: 30
|
|
||||||
# deadThreshold: 60
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: LeftLegHuman
|
id: LeftLegHuman
|
||||||
@@ -168,11 +135,7 @@
|
|||||||
state: "l_leg"
|
state: "l_leg"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Leg
|
partType: Leg
|
||||||
size: 6
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
# criticalThreshold: 45
|
|
||||||
# deadThreshold: 90
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: RightLegHuman
|
id: RightLegHuman
|
||||||
@@ -188,11 +151,7 @@
|
|||||||
state: "r_leg"
|
state: "r_leg"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Leg
|
partType: Leg
|
||||||
size: 6
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
# criticalThreshold: 45
|
|
||||||
# deadThreshold: 90
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: LeftFootHuman
|
id: LeftFootHuman
|
||||||
@@ -208,11 +167,7 @@
|
|||||||
state: "l_foot"
|
state: "l_foot"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Foot
|
partType: Foot
|
||||||
size: 2
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
# criticalThreshold: 30
|
|
||||||
# deadThreshold: 60
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: RightFootHuman
|
id: RightFootHuman
|
||||||
@@ -228,8 +183,4 @@
|
|||||||
state: "r_foot"
|
state: "r_foot"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Foot
|
partType: Foot
|
||||||
size: 2
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
# criticalThreshold: 30
|
|
||||||
# deadThreshold: 60
|
|
||||||
|
|||||||
@@ -8,13 +8,5 @@
|
|||||||
components:
|
components:
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Torso
|
partType: Torso
|
||||||
size: 7
|
|
||||||
compatibility: Biological
|
|
||||||
mechanisms:
|
|
||||||
- OrganRatLungs
|
|
||||||
- OrganRatStomach
|
|
||||||
- OrganAnimalLiver
|
|
||||||
- OrganAnimalHeart
|
|
||||||
- OrganAnimalKidneys
|
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: Biological
|
damageContainer: Biological
|
||||||
|
|||||||
@@ -28,16 +28,6 @@
|
|||||||
state: "torso_m"
|
state: "torso_m"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Torso
|
partType: Torso
|
||||||
size: 14
|
|
||||||
compatibility: Biological
|
|
||||||
mechanisms:
|
|
||||||
- OrganAnimalHeart
|
|
||||||
- OrganHumanLungs
|
|
||||||
- OrganReptilianStomach
|
|
||||||
- OrganAnimalLiver
|
|
||||||
- OrganHumanKidneys
|
|
||||||
# criticalThreshold: 100
|
|
||||||
# deadThreshold: 150
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: HeadReptilian
|
id: HeadReptilian
|
||||||
@@ -53,14 +43,7 @@
|
|||||||
state: "head_m"
|
state: "head_m"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Head
|
partType: Head
|
||||||
size: 7
|
|
||||||
compatibility: Biological
|
|
||||||
vital: true
|
vital: true
|
||||||
mechanisms:
|
|
||||||
- OrganHumanBrain
|
|
||||||
- OrganHumanEyes
|
|
||||||
# criticalThreshold: 50
|
|
||||||
# deadThreshold: 120
|
|
||||||
- type: Input
|
- type: Input
|
||||||
context: "ghost"
|
context: "ghost"
|
||||||
- type: MovementSpeedModifier
|
- type: MovementSpeedModifier
|
||||||
@@ -86,11 +69,7 @@
|
|||||||
state: "l_arm"
|
state: "l_arm"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Arm
|
partType: Arm
|
||||||
size: 5
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
# criticalThreshold: 40
|
|
||||||
# deadThreshold: 80
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: RightArmReptilian
|
id: RightArmReptilian
|
||||||
@@ -106,11 +85,7 @@
|
|||||||
state: "r_arm"
|
state: "r_arm"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Arm
|
partType: Arm
|
||||||
size: 5
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
# criticalThreshold: 40
|
|
||||||
# deadThreshold: 80
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: LeftHandReptilian
|
id: LeftHandReptilian
|
||||||
@@ -126,11 +101,7 @@
|
|||||||
state: "l_hand"
|
state: "l_hand"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Hand
|
partType: Hand
|
||||||
size: 3
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
# criticalThreshold: 30
|
|
||||||
# deadThreshold: 60
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: RightHandReptilian
|
id: RightHandReptilian
|
||||||
@@ -146,11 +117,7 @@
|
|||||||
state: "r_hand"
|
state: "r_hand"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Hand
|
partType: Hand
|
||||||
size: 3
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
# criticalThreshold: 30
|
|
||||||
# deadThreshold: 60
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: LeftLegReptilian
|
id: LeftLegReptilian
|
||||||
@@ -166,11 +133,7 @@
|
|||||||
state: "l_leg"
|
state: "l_leg"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Leg
|
partType: Leg
|
||||||
size: 6
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
# criticalThreshold: 45
|
|
||||||
# deadThreshold: 90
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: RightLegReptilian
|
id: RightLegReptilian
|
||||||
@@ -186,11 +149,7 @@
|
|||||||
state: "r_leg"
|
state: "r_leg"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Leg
|
partType: Leg
|
||||||
size: 6
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
# criticalThreshold: 45
|
|
||||||
# deadThreshold: 90
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: LeftFootReptilian
|
id: LeftFootReptilian
|
||||||
@@ -206,11 +165,7 @@
|
|||||||
state: "l_foot"
|
state: "l_foot"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Foot
|
partType: Foot
|
||||||
size: 2
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
# criticalThreshold: 30
|
|
||||||
# deadThreshold: 60
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: RightFootReptilian
|
id: RightFootReptilian
|
||||||
@@ -226,8 +181,4 @@
|
|||||||
state: "r_foot"
|
state: "r_foot"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Foot
|
partType: Foot
|
||||||
size: 2
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
# criticalThreshold: 30
|
|
||||||
# deadThreshold: 60
|
|
||||||
|
|||||||
@@ -28,8 +28,6 @@
|
|||||||
state: "l_hand"
|
state: "l_hand"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Hand
|
partType: Hand
|
||||||
size: 3
|
|
||||||
compatibility: Biological ##Does this do anything? Revisit when surgery is in
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
@@ -51,8 +49,6 @@
|
|||||||
state: "r_hand"
|
state: "r_hand"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Hand
|
partType: Hand
|
||||||
size: 3
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
|
|||||||
@@ -29,8 +29,6 @@
|
|||||||
state: "torso_m"
|
state: "torso_m"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Torso
|
partType: Torso
|
||||||
size: 14
|
|
||||||
compatibility: Biological
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: HeadSkeleton
|
id: HeadSkeleton
|
||||||
@@ -47,8 +45,6 @@
|
|||||||
state: "skull_icon"
|
state: "skull_icon"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Head
|
partType: Head
|
||||||
size: 7
|
|
||||||
compatibility: Biological
|
|
||||||
- type: Input
|
- type: Input
|
||||||
context: "human"
|
context: "human"
|
||||||
- type: MovementSpeedModifier
|
- type: MovementSpeedModifier
|
||||||
@@ -88,8 +84,6 @@
|
|||||||
state: "l_arm"
|
state: "l_arm"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Arm
|
partType: Arm
|
||||||
size: 5
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -106,8 +100,6 @@
|
|||||||
state: "r_arm"
|
state: "r_arm"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Arm
|
partType: Arm
|
||||||
size: 5
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -124,8 +116,6 @@
|
|||||||
state: "l_hand"
|
state: "l_hand"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Hand
|
partType: Hand
|
||||||
size: 3
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -142,8 +132,6 @@
|
|||||||
state: "r_hand"
|
state: "r_hand"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Hand
|
partType: Hand
|
||||||
size: 3
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -160,7 +148,7 @@
|
|||||||
state: "l_leg"
|
state: "l_leg"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Leg
|
partType: Leg
|
||||||
size: 6
|
symmetry: Left
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: RightLegSkeleton
|
id: RightLegSkeleton
|
||||||
@@ -176,8 +164,6 @@
|
|||||||
state: "r_leg"
|
state: "r_leg"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Leg
|
partType: Leg
|
||||||
size: 6
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -194,8 +180,6 @@
|
|||||||
state: "l_foot"
|
state: "l_foot"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Foot
|
partType: Foot
|
||||||
size: 2
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -212,6 +196,4 @@
|
|||||||
state: "r_foot"
|
state: "r_foot"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Foot
|
partType: Foot
|
||||||
size: 2
|
symmetry: Right
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Right
|
|
||||||
|
|||||||
@@ -29,13 +29,6 @@
|
|||||||
state: "torso_m"
|
state: "torso_m"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Torso
|
partType: Torso
|
||||||
size: 14
|
|
||||||
compatibility: Slime
|
|
||||||
mechanisms:
|
|
||||||
- SentientSlimeCore
|
|
||||||
- OrganSlimeLungs
|
|
||||||
# criticalThreshold: 100
|
|
||||||
# deadThreshold: 150
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: HeadSlime
|
id: HeadSlime
|
||||||
@@ -51,11 +44,7 @@
|
|||||||
state: "head_m"
|
state: "head_m"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Head
|
partType: Head
|
||||||
size: 7
|
|
||||||
compatibility: Slime
|
|
||||||
vital: true
|
vital: true
|
||||||
# criticalThreshold: 50
|
|
||||||
# deadThreshold: 120
|
|
||||||
- type: Input
|
- type: Input
|
||||||
context: "ghost"
|
context: "ghost"
|
||||||
- type: MovementSpeedModifier
|
- type: MovementSpeedModifier
|
||||||
@@ -81,11 +70,7 @@
|
|||||||
state: "l_arm"
|
state: "l_arm"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Arm
|
partType: Arm
|
||||||
size: 5
|
|
||||||
compatibility: Slime
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
# criticalThreshold: 40
|
|
||||||
# deadThreshold: 80
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: RightArmSlime
|
id: RightArmSlime
|
||||||
@@ -101,11 +86,7 @@
|
|||||||
state: "r_arm"
|
state: "r_arm"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Arm
|
partType: Arm
|
||||||
size: 5
|
|
||||||
compatibility: Slime
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
# criticalThreshold: 40
|
|
||||||
# deadThreshold: 80
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: LeftHandSlime
|
id: LeftHandSlime
|
||||||
@@ -121,11 +102,7 @@
|
|||||||
state: "l_hand"
|
state: "l_hand"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Hand
|
partType: Hand
|
||||||
size: 3
|
|
||||||
compatibility: Slime
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
# criticalThreshold: 30
|
|
||||||
# deadThreshold: 60
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: RightHandSlime
|
id: RightHandSlime
|
||||||
@@ -141,11 +118,7 @@
|
|||||||
state: "r_hand"
|
state: "r_hand"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Hand
|
partType: Hand
|
||||||
size: 3
|
|
||||||
compatibility: Slime
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
# criticalThreshold: 30
|
|
||||||
# deadThreshold: 60
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: LeftLegSlime
|
id: LeftLegSlime
|
||||||
@@ -161,8 +134,6 @@
|
|||||||
state: "l_leg"
|
state: "l_leg"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Leg
|
partType: Leg
|
||||||
size: 6
|
|
||||||
compatibility: Slime
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
@@ -179,11 +150,7 @@
|
|||||||
state: "r_leg"
|
state: "r_leg"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Leg
|
partType: Leg
|
||||||
size: 6
|
|
||||||
compatibility: Slime
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
# criticalThreshold: 45
|
|
||||||
# deadThreshold: 90
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: LeftFootSlime
|
id: LeftFootSlime
|
||||||
@@ -199,11 +166,7 @@
|
|||||||
state: "l_foot"
|
state: "l_foot"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Foot
|
partType: Foot
|
||||||
size: 2
|
|
||||||
compatibility: Slime
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
# criticalThreshold: 30
|
|
||||||
# deadThreshold: 60
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: RightFootSlime
|
id: RightFootSlime
|
||||||
@@ -219,8 +182,4 @@
|
|||||||
state: "r_foot"
|
state: "r_foot"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Foot
|
partType: Foot
|
||||||
size: 2
|
|
||||||
compatibility: Slime
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
# criticalThreshold: 30
|
|
||||||
# deadThreshold: 60
|
|
||||||
|
|||||||
@@ -30,17 +30,6 @@
|
|||||||
state: "torso_m"
|
state: "torso_m"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Torso
|
partType: Torso
|
||||||
size: 14
|
|
||||||
compatibility: Biological
|
|
||||||
mechanisms:
|
|
||||||
- OrganHumanHeart
|
|
||||||
- OrganVoxLungs
|
|
||||||
- OrganHumanStomach
|
|
||||||
- OrganHumanLiver
|
|
||||||
- OrganHumanKidneys
|
|
||||||
# TODO BODY DettachableDamageableComponent?
|
|
||||||
# criticalThreshold: 100
|
|
||||||
# deadThreshold: 150
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: HeadVox
|
id: HeadVox
|
||||||
@@ -56,14 +45,7 @@
|
|||||||
state: "head_m"
|
state: "head_m"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Head
|
partType: Head
|
||||||
size: 7
|
|
||||||
compatibility: Biological
|
|
||||||
vital: true
|
vital: true
|
||||||
mechanisms:
|
|
||||||
- OrganHumanBrain
|
|
||||||
- OrganHumanEyes
|
|
||||||
# criticalThreshold: 50
|
|
||||||
# deadThreshold: 120
|
|
||||||
- type: Input
|
- type: Input
|
||||||
context: "ghost"
|
context: "ghost"
|
||||||
- type: MovementSpeedModifier
|
- type: MovementSpeedModifier
|
||||||
@@ -89,11 +71,7 @@
|
|||||||
state: "l_arm"
|
state: "l_arm"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Arm
|
partType: Arm
|
||||||
size: 5
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
# criticalThreshold: 40
|
|
||||||
# deadThreshold: 80
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: RightArmVox
|
id: RightArmVox
|
||||||
@@ -109,11 +87,7 @@
|
|||||||
state: "r_arm"
|
state: "r_arm"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Arm
|
partType: Arm
|
||||||
size: 5
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
# criticalThreshold: 40
|
|
||||||
# deadThreshold: 80
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: LeftHandVox
|
id: LeftHandVox
|
||||||
@@ -129,11 +103,7 @@
|
|||||||
state: "l_hand"
|
state: "l_hand"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Hand
|
partType: Hand
|
||||||
size: 3
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
# criticalThreshold: 30
|
|
||||||
# deadThreshold: 60
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: RightHandVox
|
id: RightHandVox
|
||||||
@@ -149,11 +119,7 @@
|
|||||||
state: "r_hand"
|
state: "r_hand"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Hand
|
partType: Hand
|
||||||
size: 3
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
# criticalThreshold: 30
|
|
||||||
# deadThreshold: 60
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: LeftLegVox
|
id: LeftLegVox
|
||||||
@@ -169,11 +135,7 @@
|
|||||||
state: "l_leg"
|
state: "l_leg"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Leg
|
partType: Leg
|
||||||
size: 6
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
# criticalThreshold: 45
|
|
||||||
# deadThreshold: 90
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: RightLegVox
|
id: RightLegVox
|
||||||
@@ -189,11 +151,7 @@
|
|||||||
state: "r_leg"
|
state: "r_leg"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Leg
|
partType: Leg
|
||||||
size: 6
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
# criticalThreshold: 45
|
|
||||||
# deadThreshold: 90
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: LeftFootVox
|
id: LeftFootVox
|
||||||
@@ -209,11 +167,7 @@
|
|||||||
state: "l_foot"
|
state: "l_foot"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Foot
|
partType: Foot
|
||||||
size: 2
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Left
|
symmetry: Left
|
||||||
# criticalThreshold: 30
|
|
||||||
# deadThreshold: 60
|
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: RightFootVox
|
id: RightFootVox
|
||||||
@@ -229,8 +183,4 @@
|
|||||||
state: "r_foot"
|
state: "r_foot"
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
partType: Foot
|
partType: Foot
|
||||||
size: 2
|
|
||||||
compatibility: Biological
|
|
||||||
symmetry: Right
|
symmetry: Right
|
||||||
# criticalThreshold: 30
|
|
||||||
# deadThreshold: 60
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user