Gibbing refactor (Per-part gibbing and giblet throwing!) (#24989)
* Moving Gibbing rework out from medrefactor into it's own PR * Re-enabled warning for missing gibbable on TryGibEntity * Implemented better logic for gibbing failover and better logging * Allowing audio params and drop scattering customization per component. Created UnGibbable organ base types and made brains ungibbable. Removed delete brain from gibBody function. Artifact crusher does not destroy brains anymore. It only destroyed brains before not other organs which was wierd. * Update Content.Shared/Body/Systems/SharedBodySystem.Body.cs Fixing space for multiplication Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Added event raised when attempting to gib contained entities to allow modification of allowed and excluded container ids * removing audioParams var from component (sound specifier includes it) * Fixing signature --------- Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
This commit is contained in:
@@ -15,6 +15,7 @@ using Robust.Shared.Player;
|
|||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
|
using Content.Shared.Gibbing.Components;
|
||||||
using Content.Shared.Movement.Systems;
|
using Content.Shared.Movement.Systems;
|
||||||
using Robust.Shared.Audio.Systems;
|
using Robust.Shared.Audio.Systems;
|
||||||
|
|
||||||
@@ -106,7 +107,17 @@ public sealed class BodySystem : SharedBodySystem
|
|||||||
_humanoidSystem.SetLayersVisibility(bodyUid, layers, false, true, humanoid);
|
_humanoidSystem.SetLayersVisibility(bodyUid, layers, false, true, humanoid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override HashSet<EntityUid> GibBody(EntityUid bodyId, bool gibOrgans = false, BodyComponent? body = null, bool deleteItems = false, bool deleteBrain = false)
|
public override HashSet<EntityUid> GibBody(
|
||||||
|
EntityUid bodyId,
|
||||||
|
bool gibOrgans = false,
|
||||||
|
BodyComponent? body = null ,
|
||||||
|
bool deleteItems = false,
|
||||||
|
bool launchGibs = true,
|
||||||
|
Vector2? splatDirection = null,
|
||||||
|
float splatModifier = 1,
|
||||||
|
Angle splatCone = default,
|
||||||
|
SoundSpecifier? gibSoundOverride = null
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (!Resolve(bodyId, ref body, false))
|
if (!Resolve(bodyId, ref body, false))
|
||||||
return new HashSet<EntityUid>();
|
return new HashSet<EntityUid>();
|
||||||
@@ -118,28 +129,8 @@ public sealed class BodySystem : SharedBodySystem
|
|||||||
if (xform.MapUid == null)
|
if (xform.MapUid == null)
|
||||||
return new HashSet<EntityUid>();
|
return new HashSet<EntityUid>();
|
||||||
|
|
||||||
var gibs = base.GibBody(bodyId, gibOrgans, body, deleteItems, deleteBrain);
|
var gibs = base.GibBody(bodyId, gibOrgans, body, deleteItems, launchGibs: launchGibs,
|
||||||
|
splatDirection: splatDirection, splatModifier: splatModifier, splatCone:splatCone);
|
||||||
var coordinates = xform.Coordinates;
|
|
||||||
var filter = Filter.Pvs(bodyId, entityManager: EntityManager);
|
|
||||||
var audio = AudioParams.Default.WithVariation(0.025f);
|
|
||||||
|
|
||||||
_audio.PlayStatic(body.GibSound, filter, coordinates, true, audio);
|
|
||||||
|
|
||||||
foreach (var entity in gibs)
|
|
||||||
{
|
|
||||||
if (deleteItems)
|
|
||||||
{
|
|
||||||
if (!HasComp<BrainComponent>(entity) || deleteBrain)
|
|
||||||
{
|
|
||||||
QueueDel(entity);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SharedTransform.SetCoordinates(entity, coordinates.Offset(_random.NextVector2(.3f)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RaiseLocalEvent(bodyId, new BeingGibbedEvent(gibs));
|
RaiseLocalEvent(bodyId, new BeingGibbedEvent(gibs));
|
||||||
QueueDel(bodyId);
|
QueueDel(bodyId);
|
||||||
|
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ public sealed class ArtifactCrusherSystem : SharedArtifactCrusherSystem
|
|||||||
if (!TryComp<BodyComponent>(contained, out var body))
|
if (!TryComp<BodyComponent>(contained, out var body))
|
||||||
Del(contained);
|
Del(contained);
|
||||||
|
|
||||||
var gibs = _body.GibBody(contained, body: body, gibOrgans: true, deleteBrain: true);
|
var gibs = _body.GibBody(contained, body: body, gibOrgans: true);
|
||||||
foreach (var gib in gibs)
|
foreach (var gib in gibs)
|
||||||
{
|
{
|
||||||
ContainerSystem.Insert((gib, null, null, null), crusher.OutputContainer);
|
ContainerSystem.Insert((gib, null, null, null), crusher.OutputContainer);
|
||||||
|
|||||||
@@ -5,8 +5,13 @@ using Content.Shared.Body.Organ;
|
|||||||
using Content.Shared.Body.Part;
|
using Content.Shared.Body.Part;
|
||||||
using Content.Shared.Body.Prototypes;
|
using Content.Shared.Body.Prototypes;
|
||||||
using Content.Shared.DragDrop;
|
using Content.Shared.DragDrop;
|
||||||
|
using Content.Shared.Gibbing.Components;
|
||||||
|
using Content.Shared.Gibbing.Events;
|
||||||
|
using Content.Shared.Gibbing.Systems;
|
||||||
using Content.Shared.Inventory;
|
using Content.Shared.Inventory;
|
||||||
using Content.Shared.Inventory.Events;
|
using Content.Shared.Inventory.Events;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Audio.Systems;
|
||||||
using Robust.Shared.Containers;
|
using Robust.Shared.Containers;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
@@ -23,7 +28,10 @@ public partial class SharedBodySystem
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||||
|
[Dependency] private readonly GibbingSystem _gibbingSystem = default!;
|
||||||
|
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
|
||||||
|
private const float GibletLaunchImpulse = 8;
|
||||||
|
private const float GibletLaunchImpulseVariance = 3;
|
||||||
private void InitializeBody()
|
private void InitializeBody()
|
||||||
{
|
{
|
||||||
// Body here to handle root body parts.
|
// Body here to handle root body parts.
|
||||||
@@ -263,29 +271,45 @@ public partial class SharedBodySystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual HashSet<EntityUid> GibBody(EntityUid bodyId, bool gibOrgans = false,
|
public virtual HashSet<EntityUid> GibBody(
|
||||||
BodyComponent? body = null, bool deleteItems = false, bool deleteBrain = false)
|
EntityUid bodyId,
|
||||||
|
bool gibOrgans = false,
|
||||||
|
BodyComponent? body = null ,
|
||||||
|
bool deleteItems = false,
|
||||||
|
bool launchGibs = true,
|
||||||
|
Vector2? splatDirection = null,
|
||||||
|
float splatModifier = 1,
|
||||||
|
Angle splatCone = default,
|
||||||
|
SoundSpecifier? gibSoundOverride = null
|
||||||
|
)
|
||||||
{
|
{
|
||||||
var gibs = new HashSet<EntityUid>();
|
var gibs = new HashSet<EntityUid>();
|
||||||
|
|
||||||
if (!Resolve(bodyId, ref body, false))
|
if (!Resolve(bodyId, ref body, false))
|
||||||
return gibs;
|
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();
|
var parts = GetBodyChildren(bodyId, body).ToArray();
|
||||||
gibs.EnsureCapacity(parts.Length);
|
gibs.EnsureCapacity(parts.Length);
|
||||||
|
|
||||||
foreach (var part in parts)
|
foreach (var part in parts)
|
||||||
{
|
{
|
||||||
SharedTransform.AttachToGridOrMap(part.Id);
|
|
||||||
gibs.Add(part.Id);
|
_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)
|
if (!gibOrgans)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
foreach (var organ in GetPartOrgans(part.Id, part.Component))
|
foreach (var organ in GetPartOrgans(part.Id, part.Component))
|
||||||
{
|
{
|
||||||
SharedTransform.AttachToGridOrMap(organ.Id);
|
_gibbingSystem.TryGibEntityWithRef(bodyId, organ.Id, GibType.Drop, GibContentsOption.Skip,
|
||||||
gibs.Add(organ.Id);
|
ref gibs, playAudio: false, launchImpulse: GibletLaunchImpulse * splatModifier,
|
||||||
|
launchImpulseVariance:GibletLaunchImpulseVariance, launchCone: splatCone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(TryComp<InventoryComponent>(bodyId, out var inventory))
|
if(TryComp<InventoryComponent>(bodyId, out var inventory))
|
||||||
@@ -296,6 +320,7 @@ public partial class SharedBodySystem
|
|||||||
gibs.Add(item);
|
gibs.Add(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_audioSystem.PlayPredicted(gibSoundOverride, Transform(bodyId).Coordinates, null);
|
||||||
return gibs;
|
return gibs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
34
Content.Shared/Gibbing/Components/GibbableComponent.cs
Normal file
34
Content.Shared/Gibbing/Components/GibbableComponent.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
50
Content.Shared/Gibbing/Events/GibbingEvents.cs
Normal file
50
Content.Shared/Gibbing/Events/GibbingEvents.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
341
Content.Shared/Gibbing/Systems/GibbingSystem.cs
Normal file
341
Content.Shared/Gibbing/Systems/GibbingSystem.cs
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
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 IPrototypeManager _prototypeManager = 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(EntityUid 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(
|
||||||
|
EntityUid 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, 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parentXform = Transform(outerEntity);
|
||||||
|
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), parentXform, randomSpreadMod,
|
||||||
|
ref droppedEntities, launchGibs,
|
||||||
|
launchDirection, launchImpulse, launchImpulseVariance, launchCone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GibContentsOption.Gib:
|
||||||
|
{
|
||||||
|
foreach (var container in _containerSystem.GetAllContainers(gibbable))
|
||||||
|
{
|
||||||
|
foreach (var ent in container.ContainedEntities)
|
||||||
|
{
|
||||||
|
GibEntity(new Entity<GibbableComponent?>(ent, null), parentXform, randomSpreadMod,
|
||||||
|
ref droppedEntities, launchGibs,
|
||||||
|
launchDirection, launchImpulse, launchImpulseVariance, launchCone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (gibType)
|
||||||
|
{
|
||||||
|
case GibType.Skip:
|
||||||
|
break;
|
||||||
|
case GibType.Drop:
|
||||||
|
{
|
||||||
|
DropEntity(gibbable, parentXform, randomSpreadMod, ref droppedEntities, launchGibs,
|
||||||
|
launchDirection, launchImpulse, launchImpulseVariance, launchCone);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GibType.Gib:
|
||||||
|
{
|
||||||
|
GibEntity(gibbable, parentXform, randomSpreadMod, ref droppedEntities, launchGibs,
|
||||||
|
launchDirection, launchImpulse, launchImpulseVariance, launchCone);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (playAudio)
|
||||||
|
{
|
||||||
|
_audioSystem.PlayPredicted(gibbable.Comp.GibSound, parentXform.Coordinates, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gibType == GibType.Gib)
|
||||||
|
QueueDel(gibbable);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DropEntity(Entity<GibbableComponent?> gibbable, TransformComponent parentXform, 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gibAttemptEvent = new AttemptEntityGibEvent(gibbable, gibCount, GibType.Drop);
|
||||||
|
RaiseLocalEvent(gibbable, ref gibAttemptEvent);
|
||||||
|
switch (gibAttemptEvent.GibType)
|
||||||
|
{
|
||||||
|
case GibType.Skip:
|
||||||
|
return;
|
||||||
|
case GibType.Gib:
|
||||||
|
GibEntity(gibbable, parentXform, randomSpreadMod, ref droppedEntities, flingEntity, scatterDirection,
|
||||||
|
scatterImpulse, scatterImpulseVariance, scatterCone, deleteTarget: false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_transformSystem.AttachToGridOrMap(gibbable);
|
||||||
|
_transformSystem.SetCoordinates(gibbable, parentXform.Coordinates);
|
||||||
|
_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, TransformComponent parentXform,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, parentXform, 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, parentXform.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, parentXform.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)
|
||||||
|
QueueDel(gibbable);
|
||||||
|
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,5 +1,5 @@
|
|||||||
- type: entity
|
- type: entity
|
||||||
id: BaseAnimalOrgan
|
id: BaseAnimalOrganUnGibbable
|
||||||
parent: BaseItem
|
parent: BaseItem
|
||||||
abstract: true
|
abstract: true
|
||||||
components:
|
components:
|
||||||
@@ -23,6 +23,12 @@
|
|||||||
tags:
|
tags:
|
||||||
- Meat
|
- Meat
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: BaseAnimalOrgan
|
||||||
|
parent: BaseAnimalOrganUnGibbable
|
||||||
|
abstract: true
|
||||||
|
components:
|
||||||
|
- type: Gibbable
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: OrganAnimalLungs
|
id: OrganAnimalLungs
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
- type: entity
|
- type: entity
|
||||||
id: BaseHumanOrgan
|
id: BaseHumanOrganUnGibbable
|
||||||
parent: BaseItem
|
parent: BaseItem
|
||||||
abstract: true
|
abstract: true
|
||||||
components:
|
components:
|
||||||
@@ -27,9 +27,16 @@
|
|||||||
tags:
|
tags:
|
||||||
- Meat
|
- Meat
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: BaseHumanOrgan
|
||||||
|
parent: BaseHumanOrganUnGibbable
|
||||||
|
abstract: true
|
||||||
|
components:
|
||||||
|
- type: Gibbable
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: OrganHumanBrain
|
id: OrganHumanBrain
|
||||||
parent: BaseHumanOrgan
|
parent: BaseHumanOrganUnGibbable
|
||||||
name: brain
|
name: brain
|
||||||
description: "The source of incredible, unending intelligence. Honk."
|
description: "The source of incredible, unending intelligence. Honk."
|
||||||
components:
|
components:
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- Trash
|
- Trash
|
||||||
|
- type: Gibbable
|
||||||
- type: Extractable
|
- type: Extractable
|
||||||
juiceSolution:
|
juiceSolution:
|
||||||
reagents:
|
reagents:
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: Biological
|
damageContainer: Biological
|
||||||
- type: BodyPart
|
- type: BodyPart
|
||||||
|
- type: Gibbable
|
||||||
- type: ContainerContainer
|
- type: ContainerContainer
|
||||||
containers:
|
containers:
|
||||||
bodypart: !type:Container
|
bodypart: !type:Container
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
ents: []
|
ents: []
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 20
|
price: 20
|
||||||
|
- type: Gibbable
|
||||||
- type: Tag
|
- type: Tag
|
||||||
tags:
|
tags:
|
||||||
- Trash
|
- Trash
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
containers:
|
containers:
|
||||||
bodypart: !type:Container
|
bodypart: !type:Container
|
||||||
ents: []
|
ents: []
|
||||||
|
- type: Gibbable
|
||||||
- type: StaticPrice
|
- type: StaticPrice
|
||||||
price: 200
|
price: 200
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user