mirror of
https://github.com/space-wizards/space-station-14.git
synced 2026-02-14 19:29:53 +01:00
Decouple gibbing from the body system (#42405)
* Decouple gibbing from the body system * allow gibs that don't drop giblets * pass through user * prediction gon * comment * destructible * playpvs * very very very very very very very minor cleanup --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
0af56cefcb
commit
fb133494cc
@@ -1,43 +0,0 @@
|
||||
#nullable enable
|
||||
using Content.Server.Body.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Body;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class GibTest
|
||||
{
|
||||
[Test]
|
||||
public async Task TestGib()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true });
|
||||
var (server, client) = (pair.Server, pair.Client);
|
||||
var map = await pair.CreateTestMap();
|
||||
|
||||
EntityUid target1 = default;
|
||||
EntityUid target2 = default;
|
||||
|
||||
await server.WaitAssertion(() => target1 = server.EntMan.Spawn("MobHuman", map.MapCoords));
|
||||
await server.WaitAssertion(() => target2 = server.EntMan.Spawn("MobHuman", map.MapCoords));
|
||||
await pair.WaitCommand($"setoutfit {server.EntMan.GetNetEntity(target1)} CaptainGear");
|
||||
await pair.WaitCommand($"setoutfit {server.EntMan.GetNetEntity(target2)} CaptainGear");
|
||||
|
||||
await pair.RunTicksSync(5);
|
||||
var nuid1 = pair.ToClientUid(target1);
|
||||
var nuid2 = pair.ToClientUid(target2);
|
||||
Assert.That(client.EntMan.EntityExists(nuid1));
|
||||
Assert.That(client.EntMan.EntityExists(nuid2));
|
||||
|
||||
await server.WaitAssertion(() => server.System<BodySystem>().GibBody(target1, gibOrgans: false));
|
||||
await server.WaitAssertion(() => server.System<BodySystem>().GibBody(target2, gibOrgans: true));
|
||||
|
||||
await pair.RunTicksSync(5);
|
||||
await pair.WaitCommand("dirty");
|
||||
await pair.RunTicksSync(5);
|
||||
|
||||
Assert.That(!client.EntMan.EntityExists(nuid1));
|
||||
Assert.That(!client.EntMan.EntityExists(nuid2));
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
}
|
||||
36
Content.IntegrationTests/Tests/Gibbing/GibTest.cs
Normal file
36
Content.IntegrationTests/Tests/Gibbing/GibTest.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
#nullable enable
|
||||
using Content.Shared.Gibbing;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Body;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class GibTest
|
||||
{
|
||||
[Test]
|
||||
public async Task TestGib()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient(new PoolSettings { Connected = true });
|
||||
var (server, client) = (pair.Server, pair.Client);
|
||||
var map = await pair.CreateTestMap();
|
||||
|
||||
EntityUid target = default;
|
||||
|
||||
await server.WaitAssertion(() => target = server.EntMan.Spawn("MobHuman", map.MapCoords));
|
||||
await pair.WaitCommand($"setoutfit {server.EntMan.GetNetEntity(target)} CaptainGear");
|
||||
|
||||
await pair.RunTicksSync(5);
|
||||
var nuid = pair.ToClientUid(target);
|
||||
Assert.That(client.EntMan.EntityExists(nuid));
|
||||
|
||||
await server.WaitAssertion(() => server.System<GibbingSystem>().Gib(target));
|
||||
|
||||
await pair.RunTicksSync(5);
|
||||
await pair.WaitCommand("dirty");
|
||||
await pair.RunTicksSync(5);
|
||||
|
||||
Assert.That(!client.EntMan.EntityExists(nuid));
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Electrocution;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Gravity;
|
||||
using Content.Shared.Interaction.Components;
|
||||
using Content.Shared.Inventory;
|
||||
@@ -92,6 +93,7 @@ public sealed partial class AdminVerbSystem
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly SuperBonkSystem _superBonkSystem = default!;
|
||||
[Dependency] private readonly SlipperySystem _slipperySystem = default!;
|
||||
[Dependency] private readonly GibbingSystem _gibbing = default!;
|
||||
|
||||
private readonly EntProtoId _actionViewLawsProtoId = "ActionViewLaws";
|
||||
private readonly ProtoId<SiliconLawsetPrototype> _crewsimovLawset = "Crewsimov";
|
||||
@@ -128,7 +130,7 @@ public sealed partial class AdminVerbSystem
|
||||
4, 1, 2, args.Target, maxTileBreak: 0), // it gibs, damage doesn't need to be high.
|
||||
CancellationToken.None);
|
||||
|
||||
_bodySystem.GibBody(args.Target);
|
||||
_gibbing.Gib(args.Target);
|
||||
},
|
||||
Impact = LogImpact.Extreme,
|
||||
Message = string.Join(": ", explodeName, Loc.GetString("admin-smite-explode-description")) // we do this so the description tells admins the Text to run it via console.
|
||||
|
||||
@@ -10,6 +10,7 @@ using Content.Shared.Anomaly.Effects;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Chat;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Whitelist;
|
||||
@@ -25,7 +26,7 @@ public sealed class InnerBodyAnomalySystem : SharedInnerBodyAnomalySystem
|
||||
[Dependency] private readonly IAdminLogManager _adminLog = default!;
|
||||
[Dependency] private readonly AnomalySystem _anomaly = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly BodySystem _body = default!;
|
||||
[Dependency] private readonly GibbingSystem _gibbing = default!;
|
||||
[Dependency] private readonly IChatManager _chat = default!;
|
||||
[Dependency] private readonly ISharedPlayerManager _player = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
|
||||
@@ -134,7 +135,7 @@ public sealed class InnerBodyAnomalySystem : SharedInnerBodyAnomalySystem
|
||||
if (!TryComp<BodyComponent>(ent, out var body))
|
||||
return;
|
||||
|
||||
_body.GibBody(ent, true, body, splatModifier: 5f);
|
||||
_gibbing.Gib(ent.Owner);
|
||||
}
|
||||
|
||||
private void OnSeverityChanged(Entity<InnerBodyAnomalyComponent> ent, ref AnomalySeverityChangedEvent args)
|
||||
|
||||
@@ -3,6 +3,7 @@ using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Rotting;
|
||||
using Content.Shared.Body.Events;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Temperature.Components;
|
||||
using Robust.Server.Containers;
|
||||
using Robust.Shared.Physics.Components;
|
||||
@@ -21,12 +22,12 @@ public sealed class RottingSystem : SharedRottingSystem
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<RottingComponent, BeingGibbedEvent>(OnGibbed);
|
||||
SubscribeLocalEvent<RottingComponent, GibbedBeforeDeletionEvent>(OnGibbed);
|
||||
|
||||
SubscribeLocalEvent<TemperatureComponent, IsRottingEvent>(OnTempIsRotting);
|
||||
}
|
||||
|
||||
private void OnGibbed(EntityUid uid, RottingComponent component, BeingGibbedEvent args)
|
||||
private void OnGibbed(EntityUid uid, RottingComponent component, GibbedBeforeDeletionEvent args)
|
||||
{
|
||||
if (!TryComp<PhysicsComponent>(uid, out var physics))
|
||||
return;
|
||||
|
||||
@@ -92,40 +92,4 @@ public sealed class BodySystem : SharedBodySystem
|
||||
var layers = HumanoidVisualLayersExtension.Sublayers(layer.Value);
|
||||
_humanoidSystem.SetLayersVisibility((bodyEnt, humanoid), layers, visible: false);
|
||||
}
|
||||
|
||||
public override HashSet<EntityUid> GibBody(
|
||||
EntityUid bodyId,
|
||||
bool gibOrgans = false,
|
||||
BodyComponent? body = null,
|
||||
bool launchGibs = true,
|
||||
Vector2? splatDirection = null,
|
||||
float splatModifier = 1,
|
||||
Angle splatCone = default,
|
||||
SoundSpecifier? gibSoundOverride = null
|
||||
)
|
||||
{
|
||||
if (!Resolve(bodyId, ref body, logMissing: false)
|
||||
|| TerminatingOrDeleted(bodyId)
|
||||
|| EntityManager.IsQueuedForDeletion(bodyId))
|
||||
{
|
||||
return new HashSet<EntityUid>();
|
||||
}
|
||||
|
||||
if (HasComp<GodmodeComponent>(bodyId))
|
||||
return new HashSet<EntityUid>();
|
||||
|
||||
var xform = Transform(bodyId);
|
||||
if (xform.MapUid is null)
|
||||
return new HashSet<EntityUid>();
|
||||
|
||||
var gibs = base.GibBody(bodyId, gibOrgans, body, launchGibs: launchGibs,
|
||||
splatDirection: splatDirection, splatModifier: splatModifier, splatCone:splatCone);
|
||||
|
||||
var ev = new BeingGibbedEvent(gibs);
|
||||
RaiseLocalEvent(bodyId, ref ev);
|
||||
|
||||
QueueDel(bodyId);
|
||||
|
||||
return gibs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Construction;
|
||||
using Content.Server.Destructible.Thresholds;
|
||||
using Content.Server.Destructible.Thresholds.Behaviors;
|
||||
@@ -16,6 +15,7 @@ using Content.Shared.Database;
|
||||
using Content.Shared.Destructible;
|
||||
using Content.Shared.Destructible.Thresholds.Triggers;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Trigger.Systems;
|
||||
using JetBrains.Annotations;
|
||||
@@ -34,7 +34,7 @@ namespace Content.Server.Destructible
|
||||
|
||||
[Dependency] public readonly AtmosphereSystem AtmosphereSystem = default!;
|
||||
[Dependency] public readonly AudioSystem AudioSystem = default!;
|
||||
[Dependency] public readonly BodySystem BodySystem = default!;
|
||||
[Dependency] public readonly GibbingSystem Gibbing = default!;
|
||||
[Dependency] public readonly ConstructionSystem ConstructionSystem = default!;
|
||||
[Dependency] public readonly ExplosionSystem ExplosionSystem = default!;
|
||||
[Dependency] public readonly StackSystem StackSystem = default!;
|
||||
|
||||
@@ -14,10 +14,7 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
|
||||
|
||||
public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null)
|
||||
{
|
||||
if (system.EntityManager.TryGetComponent(owner, out BodyComponent? body))
|
||||
{
|
||||
system.BodySystem.GibBody(owner, _recursive, body);
|
||||
}
|
||||
system.Gibbing.Gib(owner, _recursive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ using Content.Shared.DoAfter;
|
||||
using Content.Shared.Forensics;
|
||||
using Content.Shared.Forensics.Components;
|
||||
using Content.Shared.Forensics.Systems;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Inventory;
|
||||
@@ -39,7 +40,7 @@ namespace Content.Server.Forensics
|
||||
// The solution entities are spawned on MapInit as well, so we have to wait for that to be able to set the DNA in the bloodstream correctly without ResolveSolution failing
|
||||
SubscribeLocalEvent<DnaComponent, MapInitEvent>(OnDNAInit, after: new[] { typeof(BloodstreamSystem) });
|
||||
|
||||
SubscribeLocalEvent<ForensicsComponent, BeingGibbedEvent>(OnBeingGibbed);
|
||||
SubscribeLocalEvent<ForensicsComponent, GibbedBeforeDeletionEvent>(OnBeingGibbed);
|
||||
SubscribeLocalEvent<ForensicsComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
SubscribeLocalEvent<ForensicsComponent, GotRehydratedEvent>(OnRehydrated);
|
||||
SubscribeLocalEvent<CleansForensicsComponent, AfterInteractEvent>(OnAfterInteract, after: new[] { typeof(AbsorbentSystem) });
|
||||
@@ -85,14 +86,14 @@ namespace Content.Server.Forensics
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBeingGibbed(EntityUid uid, ForensicsComponent component, BeingGibbedEvent args)
|
||||
private void OnBeingGibbed(Entity<ForensicsComponent> ent, ref GibbedBeforeDeletionEvent args)
|
||||
{
|
||||
string dna = Loc.GetString("forensics-dna-unknown");
|
||||
|
||||
if (TryComp(uid, out DnaComponent? dnaComp) && dnaComp.DNA != null)
|
||||
if (TryComp(ent, out DnaComponent? dnaComp) && dnaComp.DNA != null)
|
||||
dna = dnaComp.DNA;
|
||||
|
||||
foreach (EntityUid part in args.GibbedParts)
|
||||
foreach (var part in args.Giblets)
|
||||
{
|
||||
var partComp = EnsureComp<ForensicsComponent>(part);
|
||||
partComp.DNAs.Add(dna);
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
using Content.Shared.Gibbing.Components;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Objectives.Systems;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Shared.Gibbing;
|
||||
|
||||
namespace Content.Server.Gibbing.Systems;
|
||||
public sealed class GibOnRoundEndSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly BodySystem _body = default!;
|
||||
[Dependency] private readonly GibbingSystem _gibbing = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
[Dependency] private readonly SharedObjectivesSystem _objectives = default!;
|
||||
|
||||
@@ -49,7 +49,7 @@ public sealed class GibOnRoundEndSystem : EntitySystem
|
||||
if (gibComp.SpawnProto != null)
|
||||
SpawnAtPosition(gibComp.SpawnProto, Transform(uid).Coordinates);
|
||||
|
||||
_body.GibBody(uid, splatModifier: 5f);
|
||||
_gibbing.Gib(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Guardian;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
@@ -31,7 +31,7 @@ namespace Content.Server.Guardian
|
||||
[Dependency] private readonly SharedActionsSystem _actionSystem = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||
[Dependency] private readonly GibbingSystem _gibbing = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Content.Server.Guardian
|
||||
|
||||
// Ensure held items are dropped before deleting guardian.
|
||||
if (HasComp<HandsComponent>(guardian))
|
||||
_bodySystem.GibBody(component.HostedGuardian.Value);
|
||||
_gibbing.Gib(component.HostedGuardian.Value);
|
||||
|
||||
QueueDel(guardian);
|
||||
QueueDel(component.ActionEntity);
|
||||
|
||||
@@ -5,6 +5,7 @@ using Content.Server.Popups;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Map;
|
||||
@@ -20,7 +21,7 @@ public sealed class ImmovableRodSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||
[Dependency] private readonly GibbingSystem _gibbing = default!;
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
@@ -125,7 +126,7 @@ public sealed class ImmovableRodSystem : EntitySystem
|
||||
return;
|
||||
}
|
||||
|
||||
_bodySystem.GibBody(ent, body: body);
|
||||
_gibbing.Gib(ent);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Content.Shared.Body.Events;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Implants.Components;
|
||||
using Content.Shared.Storage;
|
||||
using Robust.Shared.Containers;
|
||||
@@ -11,7 +12,7 @@ public sealed partial class ImplanterSystem
|
||||
{
|
||||
SubscribeLocalEvent<ImplantedComponent, ComponentInit>(OnImplantedInit);
|
||||
SubscribeLocalEvent<ImplantedComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<ImplantedComponent, BeingGibbedEvent>(OnGibbed);
|
||||
SubscribeLocalEvent<ImplantedComponent, GibbedBeforeDeletionEvent>(OnGibbed);
|
||||
}
|
||||
|
||||
private void OnImplantedInit(Entity<ImplantedComponent> ent, ref ComponentInit args)
|
||||
@@ -26,7 +27,7 @@ public sealed partial class ImplanterSystem
|
||||
_container.CleanContainer(ent.Comp.ImplantContainer);
|
||||
}
|
||||
|
||||
private void OnGibbed(Entity<ImplantedComponent> ent, ref BeingGibbedEvent args)
|
||||
private void OnGibbed(Entity<ImplantedComponent> ent, ref GibbedBeforeDeletionEvent args)
|
||||
{
|
||||
// Drop the storage implant contents before the implants are deleted by the body being gibbed
|
||||
foreach (var implant in ent.Comp.ImplantContainer.ContainedEntities)
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Destructible;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Kitchen;
|
||||
@@ -24,7 +23,7 @@ namespace Content.Server.Kitchen.EntitySystems;
|
||||
|
||||
public sealed class SharpSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly BodySystem _bodySystem = default!;
|
||||
[Dependency] private readonly GibbingSystem _gibbing = default!;
|
||||
[Dependency] private readonly SharedDestructibleSystem _destructibleSystem = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
@@ -134,7 +133,7 @@ public sealed class SharpSystem : EntitySystem
|
||||
args.Args.User,
|
||||
popupType);
|
||||
|
||||
_bodySystem.GibBody(args.Args.Target.Value); // does nothing if ent can't be gibbed
|
||||
_gibbing.Gib(args.Args.Target.Value); // does nothing if ent can't be gibbed
|
||||
_destructibleSystem.DestroyEntity(args.Args.Target.Value);
|
||||
|
||||
args.Handled = true;
|
||||
|
||||
@@ -4,7 +4,6 @@ using Content.Server.Ghost;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Stack;
|
||||
using Content.Server.Wires;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Components.SolutionManager;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
@@ -25,6 +24,7 @@ using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Linq;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Humanoid;
|
||||
|
||||
namespace Content.Server.Materials;
|
||||
@@ -39,7 +39,7 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem
|
||||
[Dependency] private readonly OpenableSystem _openable = default!;
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
|
||||
[Dependency] private readonly SharedBodySystem _body = default!; //bobby
|
||||
[Dependency] private readonly GibbingSystem _gibbing = default!;
|
||||
[Dependency] private readonly PuddleSystem _puddle = default!;
|
||||
[Dependency] private readonly StackSystem _stack = default!;
|
||||
[Dependency] private readonly SharedMindSystem _mind = default!;
|
||||
@@ -112,7 +112,7 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem
|
||||
Filter.PvsExcept(victim, entityManager: EntityManager),
|
||||
true);
|
||||
|
||||
_body.GibBody(victim, true);
|
||||
_gibbing.Gib(victim);
|
||||
_appearance.SetData(entity.Owner, RecyclerVisuals.Bloody, true);
|
||||
args.Handled = true;
|
||||
}
|
||||
@@ -193,7 +193,7 @@ public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem
|
||||
_adminLogger.Add(LogType.Gib, logImpact, $"{ToPrettyString(item):victim} was gibbed by {ToPrettyString(uid):entity} ");
|
||||
if (component.ReclaimSolutions)
|
||||
SpawnChemicalsFromComposition(uid, item, completion, false, component, xform);
|
||||
_body.GibBody(item, true);
|
||||
_gibbing.Gib(item);
|
||||
_appearance.SetData(uid, RecyclerVisuals.Bloody, true);
|
||||
}
|
||||
else
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
using System.Linq;
|
||||
using Content.Shared.Body.Events;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Tag;
|
||||
@@ -21,19 +21,19 @@ public sealed class TransferMindOnGibSystem : EntitySystem
|
||||
/// <inheritdoc/>
|
||||
public override void Initialize()
|
||||
{
|
||||
SubscribeLocalEvent<TransferMindOnGibComponent, BeingGibbedEvent>(OnGib);
|
||||
SubscribeLocalEvent<TransferMindOnGibComponent, GibbedBeforeDeletionEvent>(OnGib);
|
||||
}
|
||||
|
||||
private void OnGib(EntityUid uid, TransferMindOnGibComponent component, BeingGibbedEvent args)
|
||||
private void OnGib(Entity<TransferMindOnGibComponent> ent, ref GibbedBeforeDeletionEvent args)
|
||||
{
|
||||
if (!_mindSystem.TryGetMind(uid, out var mindId, out var mind))
|
||||
if (!_mindSystem.TryGetMind(ent, out var mindId, out var mind))
|
||||
return;
|
||||
|
||||
var validParts = args.GibbedParts.Where(p => _tag.HasTag(p, component.TargetTag)).ToHashSet();
|
||||
var validParts = args.Giblets.Where(p => _tag.HasTag(p, ent.Comp.TargetTag)).ToHashSet();
|
||||
if (!validParts.Any())
|
||||
return;
|
||||
|
||||
var ent = _random.Pick(validParts);
|
||||
_mindSystem.TransferTo(mindId, ent, mind: mind);
|
||||
var transfer = _random.Pick(validParts);
|
||||
_mindSystem.TransferTo(mindId, transfer, mind: mind);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1002,7 +1002,7 @@ public sealed partial class ShuttleSystem
|
||||
{
|
||||
_logger.Add(LogType.Gib, LogImpact.Extreme, $"{ToPrettyString(ent):player} got gibbed by the shuttle" +
|
||||
$" {ToPrettyString(uid)} arriving from FTL at {xform.Coordinates:coordinates}");
|
||||
var gibs = _bobby.GibBody(ent, body: mob);
|
||||
var gibs = _gibbing.Gib(ent);
|
||||
_immuneEnts.UnionWith(gibs);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Buckle.Systems;
|
||||
using Content.Server.Parallax;
|
||||
using Content.Server.Procedural;
|
||||
@@ -9,6 +8,7 @@ using Content.Server.Station.Systems;
|
||||
using Content.Server.Stunnable;
|
||||
using Content.Shared.Buckle.Components;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Light.Components;
|
||||
using Content.Shared.Movement.Events;
|
||||
using Content.Shared.Salvage;
|
||||
@@ -42,7 +42,7 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly BiomeSystem _biomes = default!;
|
||||
[Dependency] private readonly BodySystem _bobby = default!;
|
||||
[Dependency] private readonly GibbingSystem _gibbing = default!;
|
||||
[Dependency] private readonly BuckleSystem _buckle = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageSys = default!;
|
||||
[Dependency] private readonly DockingSystem _dockSystem = default!;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Stack;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Storage.Components;
|
||||
using Content.Shared.Whitelist;
|
||||
using Content.Shared.Xenoarchaeology.Equipment;
|
||||
@@ -14,7 +15,7 @@ namespace Content.Server.Xenoarchaeology.Equipment.Systems;
|
||||
public sealed class ArtifactCrusherSystem : SharedArtifactCrusherSystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly BodySystem _body = default!;
|
||||
[Dependency] private readonly GibbingSystem _gibbing = default!;
|
||||
[Dependency] private readonly StackSystem _stack = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
|
||||
|
||||
@@ -44,7 +45,7 @@ public sealed class ArtifactCrusherSystem : SharedArtifactCrusherSystem
|
||||
if (!TryComp<BodyComponent>(contained, out var body))
|
||||
Del(contained);
|
||||
|
||||
var gibs = _body.GibBody(contained, body: body, gibOrgans: true);
|
||||
var gibs = _gibbing.Gib(contained);
|
||||
foreach (var gib in gibs)
|
||||
{
|
||||
ContainerSystem.Insert((gib, null, null, null), crusher.OutputContainer);
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
namespace Content.Shared.Body.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when a body gets gibbed, before it is deleted.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public readonly record struct BeingGibbedEvent(HashSet<EntityUid> GibbedParts);
|
||||
@@ -11,6 +11,7 @@ using Content.Shared.EntityEffects.Effects.Solution;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Fluids;
|
||||
using Content.Shared.Forensics.Components;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.HealthExaminable;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Popups;
|
||||
@@ -50,7 +51,7 @@ public abstract class SharedBloodstreamSystem : EntitySystem
|
||||
SubscribeLocalEvent<BloodstreamComponent, SolutionRelayEvent<ReactionAttemptEvent>>(OnReactionAttempt);
|
||||
SubscribeLocalEvent<BloodstreamComponent, DamageChangedEvent>(OnDamageChanged);
|
||||
SubscribeLocalEvent<BloodstreamComponent, HealthBeingExaminedEvent>(OnHealthBeingExamined);
|
||||
SubscribeLocalEvent<BloodstreamComponent, BeingGibbedEvent>(OnBeingGibbed);
|
||||
SubscribeLocalEvent<BloodstreamComponent, GibbedBeforeDeletionEvent>(OnBeingGibbed);
|
||||
SubscribeLocalEvent<BloodstreamComponent, ApplyMetabolicMultiplierEvent>(OnApplyMetabolicMultiplier);
|
||||
SubscribeLocalEvent<BloodstreamComponent, RejuvenateEvent>(OnRejuvenate);
|
||||
SubscribeLocalEvent<BloodstreamComponent, MetabolismExclusionEvent>(OnMetabolismExclusion);
|
||||
@@ -262,7 +263,7 @@ public abstract class SharedBloodstreamSystem : EntitySystem
|
||||
}
|
||||
}
|
||||
|
||||
private void OnBeingGibbed(Entity<BloodstreamComponent> ent, ref BeingGibbedEvent args)
|
||||
private void OnBeingGibbed(Entity<BloodstreamComponent> ent, ref GibbedBeforeDeletionEvent args)
|
||||
{
|
||||
SpillAllSolutions(ent.AsNullable());
|
||||
}
|
||||
|
||||
@@ -5,11 +5,8 @@ using Content.Shared.Body.Organ;
|
||||
using Content.Shared.Body.Part;
|
||||
using Content.Shared.Body.Prototypes;
|
||||
using Content.Shared.DragDrop;
|
||||
using Content.Shared.Gibbing.Components;
|
||||
using Content.Shared.Gibbing.Events;
|
||||
using Content.Shared.Gibbing.Systems;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Inventory;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
@@ -27,7 +24,6 @@ public partial class SharedBodySystem
|
||||
*/
|
||||
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
[Dependency] private readonly GibbingSystem _gibbingSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||
|
||||
private const float GibletLaunchImpulse = 8;
|
||||
@@ -42,6 +38,7 @@ public partial class SharedBodySystem
|
||||
SubscribeLocalEvent<BodyComponent, ComponentInit>(OnBodyInit);
|
||||
SubscribeLocalEvent<BodyComponent, MapInitEvent>(OnBodyMapInit);
|
||||
SubscribeLocalEvent<BodyComponent, CanDragEvent>(OnBodyCanDrag);
|
||||
SubscribeLocalEvent<BodyComponent, BeingGibbedEvent>(OnBeingGibbed);
|
||||
}
|
||||
|
||||
private void OnBodyInserted(Entity<BodyComponent> ent, ref EntInsertedIntoContainerMessage args)
|
||||
@@ -283,56 +280,22 @@ public partial class SharedBodySystem
|
||||
}
|
||||
}
|
||||
|
||||
public virtual HashSet<EntityUid> GibBody(
|
||||
EntityUid bodyId,
|
||||
bool gibOrgans = false,
|
||||
BodyComponent? body = null,
|
||||
bool launchGibs = true,
|
||||
Vector2? splatDirection = null,
|
||||
float splatModifier = 1,
|
||||
Angle splatCone = default,
|
||||
SoundSpecifier? gibSoundOverride = null)
|
||||
private void OnBeingGibbed(Entity<BodyComponent> ent, ref BeingGibbedEvent args)
|
||||
{
|
||||
var gibs = new HashSet<EntityUid>();
|
||||
|
||||
if (!Resolve(bodyId, ref body, logMissing: false))
|
||||
return gibs;
|
||||
|
||||
var root = GetRootPartOrNull(bodyId, body);
|
||||
if (root != null && TryComp(root.Value.Entity, out GibbableComponent? gibbable))
|
||||
{
|
||||
gibSoundOverride ??= gibbable.GibSound;
|
||||
}
|
||||
var parts = GetBodyChildren(bodyId, body).ToArray();
|
||||
gibs.EnsureCapacity(parts.Length);
|
||||
var parts = GetBodyChildren(ent, ent).ToArray();
|
||||
args.Giblets.EnsureCapacity(args.Giblets.Capacity + parts.Length);
|
||||
foreach (var part in parts)
|
||||
{
|
||||
|
||||
_gibbingSystem.TryGibEntityWithRef(bodyId, part.Id, GibType.Gib, GibContentsOption.Skip, ref gibs,
|
||||
playAudio: false, launchGibs:true, launchDirection:splatDirection, launchImpulse: GibletLaunchImpulse * splatModifier,
|
||||
launchImpulseVariance:GibletLaunchImpulseVariance, launchCone: splatCone);
|
||||
|
||||
if (!gibOrgans)
|
||||
continue;
|
||||
|
||||
foreach (var organ in GetPartOrgans(part.Id, part.Component))
|
||||
{
|
||||
_gibbingSystem.TryGibEntityWithRef(bodyId, organ.Id, GibType.Drop, GibContentsOption.Skip,
|
||||
ref gibs, playAudio: false, launchImpulse: GibletLaunchImpulse * splatModifier,
|
||||
launchImpulseVariance:GibletLaunchImpulseVariance, launchCone: splatCone);
|
||||
args.Giblets.Add(organ.Id);
|
||||
}
|
||||
PredictedQueueDel(part.Id);
|
||||
}
|
||||
|
||||
var bodyTransform = Transform(bodyId);
|
||||
if (TryComp<InventoryComponent>(bodyId, out var inventory))
|
||||
foreach (var item in _inventory.GetHandOrInventoryEntities(ent.Owner))
|
||||
{
|
||||
foreach (var item in _inventory.GetHandOrInventoryEntities(bodyId))
|
||||
{
|
||||
SharedTransform.DropNextTo(item, (bodyId, bodyTransform));
|
||||
gibs.Add(item);
|
||||
}
|
||||
args.Giblets.Add(item);
|
||||
}
|
||||
_audioSystem.PlayPredicted(gibSoundOverride, bodyTransform.Coordinates, null);
|
||||
return gibs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Body.Events;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Devour.Components;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Popups;
|
||||
@@ -33,7 +33,7 @@ public sealed class DevourSystem : EntitySystem
|
||||
SubscribeLocalEvent<DevourerComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<DevourerComponent, DevourActionEvent>(OnDevourAction);
|
||||
SubscribeLocalEvent<DevourerComponent, DevourDoAfterEvent>(OnDoAfter);
|
||||
SubscribeLocalEvent<DevourerComponent, BeingGibbedEvent>(OnGibContents);
|
||||
SubscribeLocalEvent<DevourerComponent, GibbedBeforeDeletionEvent>(OnGibContents);
|
||||
}
|
||||
|
||||
private void OnStartup(Entity<DevourerComponent> ent, ref ComponentStartup args)
|
||||
@@ -127,13 +127,11 @@ public sealed class DevourSystem : EntitySystem
|
||||
_audioSystem.PlayPredicted(ent.Comp.SoundDevour, ent.Owner, ent.Owner);
|
||||
}
|
||||
|
||||
private void OnGibContents(Entity<DevourerComponent> ent, ref BeingGibbedEvent args)
|
||||
private void OnGibContents(Entity<DevourerComponent> ent, ref GibbedBeforeDeletionEvent args)
|
||||
{
|
||||
if (ent.Comp.StomachStorageWhitelist == null)
|
||||
return;
|
||||
|
||||
// For some reason we have two different systems that should handle gibbing,
|
||||
// and for some another reason GibbingSystem, which should empty all containers, doesn't get involved in this process
|
||||
_containerSystem.EmptyContainer(ent.Comp.Stomach);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
using Content.Shared.Gibbing.Systems;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameStates;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.Gibbing.Components;
|
||||
|
||||
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(GibbingSystem))]
|
||||
public sealed partial class GibbableComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Giblet entity prototypes to randomly select from when spawning additional giblets
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public List<EntProtoId> GibPrototypes = new();
|
||||
|
||||
/// <summary>
|
||||
/// Number of giblet entities to spawn in addition to entity contents
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public int GibCount;
|
||||
|
||||
/// <summary>
|
||||
/// Sound to be played when this entity is gibbed, only played when playsound is true on the gibbing function
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public SoundSpecifier? GibSound = new SoundCollectionSpecifier("gib", AudioParams.Default.WithVariation(0.025f));
|
||||
|
||||
/// <summary>
|
||||
/// Max distance giblets can be dropped from an entity when NOT using physics-based scattering
|
||||
/// </summary>
|
||||
[DataField, AutoNetworkedField]
|
||||
public float GibScatterRange = 0.3f;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Gibbing.Events;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called just before we actually gib the target entity
|
||||
/// </summary>
|
||||
/// <param name="Target">The entity being gibed</param>
|
||||
/// <param name="GibType">What type of gibbing is occuring</param>
|
||||
/// <param name="AllowedContainers">Containers we are allow to gib</param>
|
||||
/// <param name="ExcludedContainers">Containers we are allow not allowed to gib</param>
|
||||
[ByRefEvent] public record struct AttemptEntityContentsGibEvent(
|
||||
EntityUid Target,
|
||||
GibContentsOption GibType,
|
||||
List<string>? AllowedContainers,
|
||||
List<string>? ExcludedContainers
|
||||
);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Called just before we actually gib the target entity
|
||||
/// </summary>
|
||||
/// <param name="Target">The entity being gibed</param>
|
||||
/// <param name="GibletCount">how many giblets to spawn</param>
|
||||
/// <param name="GibType">What type of gibbing is occuring</param>
|
||||
[ByRefEvent] public record struct AttemptEntityGibEvent(EntityUid Target, int GibletCount, GibType GibType);
|
||||
|
||||
/// <summary>
|
||||
/// Called immediately after we gib the target entity
|
||||
/// </summary>
|
||||
/// <param name="Target">The entity being gibbed</param>
|
||||
/// <param name="DroppedEntities">Any entities that are spilled out (if any)</param>
|
||||
[ByRefEvent] public record struct EntityGibbedEvent(EntityUid Target, List<EntityUid> DroppedEntities);
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum GibType : byte
|
||||
{
|
||||
Skip,
|
||||
Drop,
|
||||
Gib,
|
||||
}
|
||||
|
||||
public enum GibContentsOption : byte
|
||||
{
|
||||
Skip,
|
||||
Drop,
|
||||
Gib
|
||||
}
|
||||
84
Content.Shared/Gibbing/GibbingSystem.cs
Normal file
84
Content.Shared/Gibbing/GibbingSystem.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using Content.Shared.Destructible;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Shared.Gibbing;
|
||||
|
||||
public sealed class GibbingSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedDestructibleSystem _destructible = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
|
||||
private static readonly SoundSpecifier? GibSound = new SoundCollectionSpecifier("gib", AudioParams.Default.WithVariation(0.025f));
|
||||
|
||||
/// <summary>
|
||||
/// Gibs an entity.
|
||||
/// </summary>
|
||||
/// <param name="ent">The entity to gib.</param>
|
||||
/// <param name="dropGiblets">Whether or not to drop giblets.</param>
|
||||
/// <param name="user">The user gibbing the entity, if any.</param>
|
||||
/// <returns>The set of giblets for this entity, if any.</returns>
|
||||
public HashSet<EntityUid> Gib(EntityUid ent, bool dropGiblets = true, EntityUid? user = null)
|
||||
{
|
||||
// user is unused because of prediction woes, eventually it'll be used for audio
|
||||
|
||||
// BodySystem handles prediction rather poorly and causes client-sided bugs when we gib on the client
|
||||
// This guard can be removed once it is gone and replaced by a prediction-safe system.
|
||||
if (!_net.IsServer)
|
||||
return new();
|
||||
|
||||
if (!_destructible.DestroyEntity(ent))
|
||||
return new();
|
||||
|
||||
_audio.PlayPvs(GibSound, ent);
|
||||
|
||||
var gibbed = new HashSet<EntityUid>();
|
||||
var beingGibbed = new BeingGibbedEvent(gibbed);
|
||||
RaiseLocalEvent(ent, ref beingGibbed);
|
||||
|
||||
if (dropGiblets)
|
||||
{
|
||||
foreach (var giblet in gibbed)
|
||||
{
|
||||
_transform.DropNextTo(giblet, ent);
|
||||
FlingDroppedEntity(giblet);
|
||||
}
|
||||
}
|
||||
|
||||
var beforeDeletion = new GibbedBeforeDeletionEvent(gibbed);
|
||||
RaiseLocalEvent(ent, ref beforeDeletion);
|
||||
|
||||
return gibbed;
|
||||
}
|
||||
|
||||
private const float GibletLaunchImpulse = 8;
|
||||
private const float GibletLaunchImpulseVariance = 3;
|
||||
|
||||
private void FlingDroppedEntity(EntityUid target)
|
||||
{
|
||||
var impulse = GibletLaunchImpulse + _random.NextFloat(GibletLaunchImpulseVariance);
|
||||
var scatterVec = _random.NextAngle().ToVec() * impulse;
|
||||
_physics.ApplyLinearImpulse(target, scatterVec);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised on an entity when it is being gibbed.
|
||||
/// </summary>
|
||||
/// <param name="Giblets">If a component wants to provide giblets to scatter, add them to this hashset.</param>
|
||||
[ByRefEvent]
|
||||
public readonly record struct BeingGibbedEvent(HashSet<EntityUid> Giblets);
|
||||
|
||||
/// <summary>
|
||||
/// Raised on an entity when it is about to be deleted after being gibbed.
|
||||
/// </summary>
|
||||
/// <param name="Giblets">The set of giblets this entity produced.</param>
|
||||
[ByRefEvent]
|
||||
public readonly record struct GibbedBeforeDeletionEvent(HashSet<EntityUid> Giblets);
|
||||
@@ -1,344 +0,0 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Numerics;
|
||||
using Content.Shared.Gibbing.Components;
|
||||
using Content.Shared.Gibbing.Events;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Shared.Gibbing.Systems;
|
||||
|
||||
public sealed class GibbingSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physicsSystem = default!;
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
//TODO: (future optimization) implement a system that "caps" giblet entities by deleting the oldest ones once we reach a certain limit, customizable via CVAR
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to gib a specified entity. That entity must have a gibable components. This method is NOT recursive will only
|
||||
/// work on the target and any entities it contains (depending on gibContentsOption)
|
||||
/// </summary>
|
||||
/// <param name="outerEntity">The outermost entity we care about, used to place the dropped items</param>
|
||||
/// <param name="gibbable">Target entity/comp we wish to gib</param>
|
||||
/// <param name="gibType">What type of gibing are we performing</param>
|
||||
/// <param name="gibContentsOption">What type of gibing do we perform on any container contents?</param>
|
||||
/// <param name="droppedEntities">a hashset containing all the entities that have been dropped/created</param>
|
||||
/// <param name="randomSpreadMod">How much to multiply the random spread on dropped giblets(if we are dropping them!)</param>
|
||||
/// <param name="playAudio">Should we play audio</param>
|
||||
/// <param name="allowedContainers">A list of containerIds on the target that permit gibing</param>
|
||||
/// <param name="excludedContainers">A list of containerIds on the target that DO NOT permit gibing</param>
|
||||
/// <param name="launchCone">The cone we are launching giblets in (if we are launching them!)</param>
|
||||
/// <param name="launchGibs">Should we launch giblets or just drop them</param>
|
||||
/// <param name="launchDirection">The direction to launch giblets (if we are launching them!)</param>
|
||||
/// <param name="launchImpulse">The impluse to launch giblets at(if we are launching them!)</param>
|
||||
/// /// <param name="logMissingGibable">Should we log if we are missing a gibbableComp when we call this function</param>
|
||||
/// <param name="launchImpulseVariance">The variation in giblet launch impulse (if we are launching them!)</param>
|
||||
/// <returns>True if successful, false if not</returns>
|
||||
public bool TryGibEntity(Entity<TransformComponent?> outerEntity, Entity<GibbableComponent?> gibbable, GibType gibType,
|
||||
GibContentsOption gibContentsOption,
|
||||
out HashSet<EntityUid> droppedEntities, bool launchGibs = true,
|
||||
Vector2 launchDirection = default, float launchImpulse = 0f, float launchImpulseVariance = 0f,
|
||||
Angle launchCone = default,
|
||||
float randomSpreadMod = 1.0f, bool playAudio = true, List<string>? allowedContainers = null,
|
||||
List<string>? excludedContainers = null, bool logMissingGibable = false)
|
||||
{
|
||||
droppedEntities = new();
|
||||
return TryGibEntityWithRef(outerEntity, gibbable, gibType, gibContentsOption, ref droppedEntities,
|
||||
launchGibs, launchDirection, launchImpulse, launchImpulseVariance, launchCone, randomSpreadMod, playAudio,
|
||||
allowedContainers, excludedContainers, logMissingGibable);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to gib a specified entity. That entity must have a gibable components. This method is NOT recursive will only
|
||||
/// work on the target and any entities it contains (depending on gibContentsOption)
|
||||
/// </summary>
|
||||
/// <param name="outerEntity">The outermost entity we care about, used to place the dropped items</param>
|
||||
/// <param name="gibbable">Target entity/comp we wish to gib</param>
|
||||
/// <param name="gibType">What type of gibing are we performing</param>
|
||||
/// <param name="gibContentsOption">What type of gibing do we perform on any container contents?</param>
|
||||
/// <param name="droppedEntities">a hashset containing all the entities that have been dropped/created</param>
|
||||
/// <param name="randomSpreadMod">How much to multiply the random spread on dropped giblets(if we are dropping them!)</param>
|
||||
/// <param name="playAudio">Should we play audio</param>
|
||||
/// <param name="allowedContainers">A list of containerIds on the target that permit gibing</param>
|
||||
/// <param name="excludedContainers">A list of containerIds on the target that DO NOT permit gibing</param>
|
||||
/// <param name="launchCone">The cone we are launching giblets in (if we are launching them!)</param>
|
||||
/// <param name="launchGibs">Should we launch giblets or just drop them</param>
|
||||
/// <param name="launchDirection">The direction to launch giblets (if we are launching them!)</param>
|
||||
/// <param name="launchImpulse">The impluse to launch giblets at(if we are launching them!)</param>
|
||||
/// <param name="launchImpulseVariance">The variation in giblet launch impulse (if we are launching them!)</param>
|
||||
/// <param name="logMissingGibable">Should we log if we are missing a gibbableComp when we call this function</param>
|
||||
/// <returns>True if successful, false if not</returns>
|
||||
public bool TryGibEntityWithRef(
|
||||
Entity<TransformComponent?> outerEntity,
|
||||
Entity<GibbableComponent?> gibbable,
|
||||
GibType gibType,
|
||||
GibContentsOption gibContentsOption,
|
||||
ref HashSet<EntityUid> droppedEntities,
|
||||
bool launchGibs = true,
|
||||
Vector2? launchDirection = null,
|
||||
float launchImpulse = 0f,
|
||||
float launchImpulseVariance = 0f,
|
||||
Angle launchCone = default,
|
||||
float randomSpreadMod = 1.0f,
|
||||
bool playAudio = true,
|
||||
List<string>? allowedContainers = null,
|
||||
List<string>? excludedContainers = null,
|
||||
bool logMissingGibable = false)
|
||||
{
|
||||
if (!Resolve(gibbable, ref gibbable.Comp, logMissing: false))
|
||||
{
|
||||
DropEntity(gibbable, (outerEntity, Transform(outerEntity)), randomSpreadMod, ref droppedEntities,
|
||||
launchGibs, launchDirection, launchImpulse, launchImpulseVariance, launchCone);
|
||||
if (logMissingGibable)
|
||||
{
|
||||
Log.Warning($"{ToPrettyString(gibbable)} does not have a GibbableComponent! " +
|
||||
$"This is not required but may cause issues contained items to not be dropped.");
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gibType == GibType.Skip && gibContentsOption == GibContentsOption.Skip)
|
||||
return true;
|
||||
if (launchGibs)
|
||||
{
|
||||
randomSpreadMod = 0;
|
||||
}
|
||||
|
||||
HashSet<BaseContainer> validContainers = new();
|
||||
var gibContentsAttempt =
|
||||
new AttemptEntityContentsGibEvent(gibbable, gibContentsOption, allowedContainers, excludedContainers);
|
||||
RaiseLocalEvent(gibbable, ref gibContentsAttempt);
|
||||
|
||||
foreach (var container in _containerSystem.GetAllContainers(gibbable))
|
||||
{
|
||||
var valid = true;
|
||||
if (allowedContainers != null)
|
||||
valid = allowedContainers.Contains(container.ID);
|
||||
if (excludedContainers != null)
|
||||
valid = valid && !excludedContainers.Contains(container.ID);
|
||||
if (valid)
|
||||
validContainers.Add(container);
|
||||
}
|
||||
|
||||
switch (gibContentsOption)
|
||||
{
|
||||
case GibContentsOption.Skip:
|
||||
break;
|
||||
case GibContentsOption.Drop:
|
||||
{
|
||||
foreach (var container in validContainers)
|
||||
{
|
||||
foreach (var ent in container.ContainedEntities)
|
||||
{
|
||||
DropEntity(new Entity<GibbableComponent?>(ent, null), outerEntity, randomSpreadMod,
|
||||
ref droppedEntities, launchGibs,
|
||||
launchDirection, launchImpulse, launchImpulseVariance, launchCone);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case GibContentsOption.Gib:
|
||||
{
|
||||
foreach (var container in validContainers)
|
||||
{
|
||||
foreach (var ent in container.ContainedEntities)
|
||||
{
|
||||
GibEntity(new Entity<GibbableComponent?>(ent, null), outerEntity, randomSpreadMod,
|
||||
ref droppedEntities, launchGibs,
|
||||
launchDirection, launchImpulse, launchImpulseVariance, launchCone);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
switch (gibType)
|
||||
{
|
||||
case GibType.Skip:
|
||||
break;
|
||||
case GibType.Drop:
|
||||
{
|
||||
DropEntity(gibbable, outerEntity, randomSpreadMod, ref droppedEntities, launchGibs,
|
||||
launchDirection, launchImpulse, launchImpulseVariance, launchCone);
|
||||
break;
|
||||
}
|
||||
case GibType.Gib:
|
||||
{
|
||||
GibEntity(gibbable, outerEntity, randomSpreadMod, ref droppedEntities, launchGibs,
|
||||
launchDirection, launchImpulse, launchImpulseVariance, launchCone);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (playAudio)
|
||||
{
|
||||
_audioSystem.PlayPredicted(gibbable.Comp.GibSound, outerEntity, null);
|
||||
}
|
||||
|
||||
if (gibType == GibType.Gib)
|
||||
PredictedQueueDel(gibbable.Owner);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void DropEntity(Entity<GibbableComponent?> gibbable, Entity<TransformComponent?> parent, float randomSpreadMod,
|
||||
ref HashSet<EntityUid> droppedEntities, bool flingEntity, Vector2? scatterDirection, float scatterImpulse,
|
||||
float scatterImpulseVariance, Angle scatterCone)
|
||||
{
|
||||
var gibCount = 0;
|
||||
if (Resolve(gibbable, ref gibbable.Comp, logMissing: false))
|
||||
{
|
||||
gibCount = gibbable.Comp.GibCount;
|
||||
}
|
||||
|
||||
if (!Resolve(parent, ref parent.Comp, logMissing: false))
|
||||
return;
|
||||
|
||||
var gibAttemptEvent = new AttemptEntityGibEvent(gibbable, gibCount, GibType.Drop);
|
||||
RaiseLocalEvent(gibbable, ref gibAttemptEvent);
|
||||
switch (gibAttemptEvent.GibType)
|
||||
{
|
||||
case GibType.Skip:
|
||||
return;
|
||||
case GibType.Gib:
|
||||
GibEntity(gibbable, parent, randomSpreadMod, ref droppedEntities, flingEntity, scatterDirection,
|
||||
scatterImpulse, scatterImpulseVariance, scatterCone, deleteTarget: false);
|
||||
return;
|
||||
}
|
||||
|
||||
_transformSystem.DropNextTo(gibbable.Owner, parent);
|
||||
_transformSystem.SetWorldRotation(gibbable, _random.NextAngle());
|
||||
droppedEntities.Add(gibbable);
|
||||
if (flingEntity)
|
||||
{
|
||||
FlingDroppedEntity(gibbable, scatterDirection, scatterImpulse, scatterImpulseVariance, scatterCone);
|
||||
}
|
||||
|
||||
var gibbedEvent = new EntityGibbedEvent(gibbable, new List<EntityUid> {gibbable});
|
||||
RaiseLocalEvent(gibbable, ref gibbedEvent);
|
||||
}
|
||||
|
||||
private List<EntityUid> GibEntity(Entity<GibbableComponent?> gibbable, Entity<TransformComponent?> parent,
|
||||
float randomSpreadMod,
|
||||
ref HashSet<EntityUid> droppedEntities, bool flingEntity, Vector2? scatterDirection, float scatterImpulse,
|
||||
float scatterImpulseVariance, Angle scatterCone, bool deleteTarget = true)
|
||||
{
|
||||
var localGibs = new List<EntityUid>();
|
||||
var gibCount = 0;
|
||||
var gibProtoCount = 0;
|
||||
if (Resolve(gibbable, ref gibbable.Comp, logMissing: false))
|
||||
{
|
||||
gibCount = gibbable.Comp.GibCount;
|
||||
gibProtoCount = gibbable.Comp.GibPrototypes.Count;
|
||||
}
|
||||
|
||||
if (!Resolve(parent, ref parent.Comp, logMissing: false))
|
||||
return [];
|
||||
|
||||
var gibAttemptEvent = new AttemptEntityGibEvent(gibbable, gibCount, GibType.Drop);
|
||||
RaiseLocalEvent(gibbable, ref gibAttemptEvent);
|
||||
switch (gibAttemptEvent.GibType)
|
||||
{
|
||||
case GibType.Skip:
|
||||
return localGibs;
|
||||
case GibType.Drop:
|
||||
DropEntity(gibbable, parent, randomSpreadMod, ref droppedEntities, flingEntity,
|
||||
scatterDirection, scatterImpulse, scatterImpulseVariance, scatterCone);
|
||||
localGibs.Add(gibbable);
|
||||
return localGibs;
|
||||
}
|
||||
|
||||
if (gibbable.Comp != null && gibProtoCount > 0)
|
||||
{
|
||||
if (flingEntity)
|
||||
{
|
||||
for (var i = 0; i < gibAttemptEvent.GibletCount; i++)
|
||||
{
|
||||
if (!TryCreateRandomGiblet(gibbable.Comp, parent.Comp.Coordinates, false, out var giblet,
|
||||
randomSpreadMod))
|
||||
continue;
|
||||
FlingDroppedEntity(giblet.Value, scatterDirection, scatterImpulse, scatterImpulseVariance,
|
||||
scatterCone);
|
||||
droppedEntities.Add(giblet.Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < gibAttemptEvent.GibletCount; i++)
|
||||
{
|
||||
if (TryCreateRandomGiblet(gibbable.Comp, parent.Comp.Coordinates, false, out var giblet,
|
||||
randomSpreadMod))
|
||||
droppedEntities.Add(giblet.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_transformSystem.AttachToGridOrMap(gibbable, Transform(gibbable));
|
||||
if (flingEntity)
|
||||
{
|
||||
FlingDroppedEntity(gibbable, scatterDirection, scatterImpulse, scatterImpulseVariance, scatterCone);
|
||||
}
|
||||
|
||||
var gibbedEvent = new EntityGibbedEvent(gibbable, localGibs);
|
||||
RaiseLocalEvent(gibbable, ref gibbedEvent);
|
||||
if (deleteTarget)
|
||||
PredictedQueueDel(gibbable.Owner);
|
||||
return localGibs;
|
||||
}
|
||||
|
||||
|
||||
public bool TryCreateRandomGiblet(Entity<GibbableComponent?> gibbable, [NotNullWhen(true)] out EntityUid? gibletEntity,
|
||||
float randomSpreadModifier = 1.0f, bool playSound = true)
|
||||
{
|
||||
gibletEntity = null;
|
||||
return Resolve(gibbable, ref gibbable.Comp) && TryCreateRandomGiblet(gibbable.Comp, Transform(gibbable).Coordinates,
|
||||
playSound, out gibletEntity, randomSpreadModifier);
|
||||
}
|
||||
|
||||
public bool TryCreateAndFlingRandomGiblet(Entity<GibbableComponent?> gibbable, [NotNullWhen(true)] out EntityUid? gibletEntity,
|
||||
Vector2 scatterDirection, float force, float scatterImpulseVariance, Angle scatterCone = default,
|
||||
bool playSound = true)
|
||||
{
|
||||
gibletEntity = null;
|
||||
if (!Resolve(gibbable, ref gibbable.Comp) ||
|
||||
!TryCreateRandomGiblet(gibbable.Comp, Transform(gibbable).Coordinates, playSound, out gibletEntity))
|
||||
return false;
|
||||
FlingDroppedEntity(gibletEntity.Value, scatterDirection, force, scatterImpulseVariance, scatterCone);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void FlingDroppedEntity(EntityUid target, Vector2? direction, float impulse, float impulseVariance,
|
||||
Angle scatterConeAngle)
|
||||
{
|
||||
var scatterAngle = direction?.ToAngle() ?? _random.NextAngle();
|
||||
var scatterVector = _random.NextAngle(scatterAngle - scatterConeAngle / 2, scatterAngle + scatterConeAngle / 2)
|
||||
.ToVec() * (impulse + _random.NextFloat(impulseVariance));
|
||||
_physicsSystem.ApplyLinearImpulse(target, scatterVector);
|
||||
}
|
||||
|
||||
private bool TryCreateRandomGiblet(GibbableComponent gibbable, EntityCoordinates coords,
|
||||
bool playSound, [NotNullWhen(true)] out EntityUid? gibletEntity, float? randomSpreadModifier = null)
|
||||
{
|
||||
gibletEntity = null;
|
||||
if (gibbable.GibPrototypes.Count == 0)
|
||||
return false;
|
||||
gibletEntity = Spawn(gibbable.GibPrototypes[_random.Next(0, gibbable.GibPrototypes.Count)],
|
||||
randomSpreadModifier == null
|
||||
? coords
|
||||
: coords.Offset(_random.NextVector2(gibbable.GibScatterRange * randomSpreadModifier.Value)));
|
||||
if (playSound)
|
||||
_audioSystem.PlayPredicted(gibbable.GibSound, coords, null);
|
||||
_transformSystem.SetWorldRotation(gibletEntity.Value, _random.NextAngle());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Destructible;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.DragDrop;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Hands;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.IdentityManagement;
|
||||
@@ -41,7 +41,7 @@ public sealed class SharedKitchenSpikeSystem : EntitySystem
|
||||
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||
[Dependency] private readonly SharedBodySystem _bodySystem = default!;
|
||||
[Dependency] private readonly GibbingSystem _gibbing = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly SharedInteractionSystem _interaction = default!;
|
||||
@@ -317,7 +317,7 @@ public sealed class SharedKitchenSpikeSystem : EntitySystem
|
||||
// Gib the victim if there is nothing else to butcher.
|
||||
if (butcherable.SpawnedEntities.Count == 0)
|
||||
{
|
||||
_bodySystem.GibBody(args.Target.Value, true);
|
||||
_gibbing.Gib(args.Target.Value);
|
||||
|
||||
var logSeverity = HasComp<HumanoidAppearanceComponent>(args.Target) ? LogImpact.Extreme : LogImpact.High;
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
using System.Numerics;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Charges.Components;
|
||||
using Content.Shared.Charges.Systems;
|
||||
using Content.Shared.Coordinates.Helpers;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Doors.Systems;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Hands.Components;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.Interaction;
|
||||
@@ -53,7 +52,7 @@ public abstract class SharedMagicSystem : EntitySystem
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||
[Dependency] private readonly INetManager _net = default!;
|
||||
[Dependency] private readonly SharedBodySystem _body = default!;
|
||||
[Dependency] private readonly GibbingSystem _gibbing = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly SharedDoorSystem _door = default!;
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
@@ -390,11 +389,7 @@ public abstract class SharedMagicSystem : EntitySystem
|
||||
var impulseVector = direction * 10000;
|
||||
|
||||
_physics.ApplyLinearImpulse(ev.Target, impulseVector);
|
||||
|
||||
if (!TryComp<BodyComponent>(ev.Target, out var body))
|
||||
return;
|
||||
|
||||
_body.GibBody(ev.Target, true, body);
|
||||
_gibbing.Gib(ev.Target);
|
||||
}
|
||||
|
||||
// End Touch Spells
|
||||
|
||||
@@ -4,6 +4,7 @@ using Content.Shared.Administration.Logs;
|
||||
using Content.Shared.Body.Events;
|
||||
using Content.Shared.Containers.ItemSlots;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Hands.EntitySystems;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Interaction;
|
||||
@@ -88,7 +89,7 @@ public abstract partial class SharedBorgSystem : EntitySystem
|
||||
SubscribeLocalEvent<BorgChassisComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovementSpeedModifiers);
|
||||
SubscribeLocalEvent<BorgChassisComponent, ActivatableUIOpenAttemptEvent>(OnUIOpenAttempt);
|
||||
SubscribeLocalEvent<BorgChassisComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||
SubscribeLocalEvent<BorgChassisComponent, BeingGibbedEvent>(OnBeingGibbed);
|
||||
SubscribeLocalEvent<BorgChassisComponent, GibbedBeforeDeletionEvent>(OnBeingGibbed);
|
||||
SubscribeLocalEvent<BorgChassisComponent, GetCharactedDeadIcEvent>(OnGetDeadIC);
|
||||
SubscribeLocalEvent<BorgChassisComponent, GetCharacterUnrevivableIcEvent>(OnGetUnrevivableIC);
|
||||
SubscribeLocalEvent<BorgChassisComponent, PowerCellSlotEmptyEvent>(OnPowerCellSlotEmpty);
|
||||
@@ -293,7 +294,7 @@ public abstract partial class SharedBorgSystem : EntitySystem
|
||||
SetActive(chassis, false, user: args.Origin);
|
||||
}
|
||||
|
||||
private void OnBeingGibbed(Entity<BorgChassisComponent> chassis, ref BeingGibbedEvent args)
|
||||
private void OnBeingGibbed(Entity<BorgChassisComponent> chassis, ref GibbedBeforeDeletionEvent args)
|
||||
{
|
||||
// Don't use the ItemSlotsSystem eject method since we don't want to play a sound and want we to eject the battery even if the slot is locked.
|
||||
if (TryComp<PowerCellSlotComponent>(chassis, out var slotComp) &&
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Shared.Species.Components;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Mobs;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Popups;
|
||||
@@ -12,7 +13,7 @@ namespace Content.Shared.Species;
|
||||
public sealed partial class GibActionSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedActionsSystem _actionsSystem = default!;
|
||||
[Dependency] private readonly SharedBodySystem _bodySystem = default!;
|
||||
[Dependency] private readonly GibbingSystem _gibbing = default!;
|
||||
[Dependency] private readonly IPrototypeManager _protoManager = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||
|
||||
@@ -26,7 +27,7 @@ public sealed partial class GibActionSystem : EntitySystem
|
||||
|
||||
private void OnMobStateChanged(EntityUid uid, GibActionComponent comp, MobStateChangedEvent args)
|
||||
{
|
||||
// When the mob changes state, check if they're dead and give them the action if so.
|
||||
// When the mob changes state, check if they're dead and give them the action if so.
|
||||
if (!TryComp<MobStateComponent>(uid, out var mobState))
|
||||
return;
|
||||
|
||||
@@ -47,15 +48,15 @@ public sealed partial class GibActionSystem : EntitySystem
|
||||
// If they aren't given the action, remove it.
|
||||
_actionsSystem.RemoveAction(uid, comp.ActionEntity);
|
||||
}
|
||||
|
||||
|
||||
private void OnGibAction(EntityUid uid, GibActionComponent comp, GibActionEvent args)
|
||||
{
|
||||
// When they use the action, gib them.
|
||||
_popupSystem.PopupClient(Loc.GetString(comp.PopupText, ("name", uid)), uid, uid);
|
||||
_bodySystem.GibBody(uid, true);
|
||||
_gibbing.Gib(uid, user: args.Performer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public sealed partial class GibActionEvent : InstantActionEvent { }
|
||||
|
||||
public sealed partial class GibActionEvent : InstantActionEvent { }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Shared.Body.Systems;
|
||||
using Content.Shared.Gibbing;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Trigger.Components.Effects;
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Content.Shared.Trigger.Systems;
|
||||
|
||||
public sealed class GibOnTriggerSystem : XOnTriggerSystem<GibOnTriggerComponent>
|
||||
{
|
||||
[Dependency] private readonly SharedBodySystem _body = default!;
|
||||
[Dependency] private readonly GibbingSystem _gibbing = default!;
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
|
||||
protected override void OnTrigger(Entity<GibOnTriggerComponent> ent, EntityUid target, ref TriggerEvent args)
|
||||
@@ -20,7 +20,7 @@ public sealed class GibOnTriggerSystem : XOnTriggerSystem<GibOnTriggerComponent>
|
||||
}
|
||||
}
|
||||
|
||||
_body.GibBody(target, true);
|
||||
_gibbing.Gib(target, user: args.User);
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
- type: entity
|
||||
id: BaseAnimalOrganUnGibbable
|
||||
id: BaseAnimalOrgan
|
||||
parent: BaseItem
|
||||
abstract: true
|
||||
components:
|
||||
@@ -23,13 +23,6 @@
|
||||
tags:
|
||||
- Meat
|
||||
|
||||
- type: entity
|
||||
id: BaseAnimalOrgan
|
||||
parent: BaseAnimalOrganUnGibbable
|
||||
abstract: true
|
||||
components:
|
||||
- type: Gibbable
|
||||
|
||||
- type: entity
|
||||
id: OrganAnimalLungs
|
||||
parent: BaseAnimalOrgan
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
- type: entity
|
||||
id: BaseHumanOrganUnGibbable
|
||||
id: BaseHumanOrgan
|
||||
parent: BaseItem
|
||||
abstract: true
|
||||
components:
|
||||
@@ -27,16 +27,9 @@
|
||||
tags:
|
||||
- Meat
|
||||
|
||||
- type: entity
|
||||
id: BaseHumanOrgan
|
||||
parent: BaseHumanOrganUnGibbable
|
||||
abstract: true
|
||||
components:
|
||||
- type: Gibbable
|
||||
|
||||
- type: entity
|
||||
id: OrganHumanBrain
|
||||
parent: BaseHumanOrganUnGibbable
|
||||
parent: BaseHumanOrgan
|
||||
name: brain
|
||||
description: "The source of incredible, unending intelligence. Honk."
|
||||
components:
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
- type: Tag
|
||||
tags:
|
||||
- Trash
|
||||
- type: Gibbable
|
||||
- type: Extractable
|
||||
juiceSolution:
|
||||
reagents:
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
- type: Damageable
|
||||
damageContainer: Biological
|
||||
- type: BodyPart
|
||||
- type: Gibbable
|
||||
- type: ContainerContainer
|
||||
containers:
|
||||
bodypart: !type:Container
|
||||
|
||||
Reference in New Issue
Block a user