TriggerOnPlayerSpawnComplete and ExplosionOnTrigger (#39820)

This commit is contained in:
āda
2025-09-23 15:24:45 -05:00
committed by GitHub
parent 3ee7d81944
commit eee5751a22
7 changed files with 127 additions and 20 deletions

View File

@@ -63,16 +63,6 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
public const int MaxExplosionAudioRange = 30; public const int MaxExplosionAudioRange = 30;
/// <summary>
/// The "default" explosion prototype.
/// </summary>
/// <remarks>
/// Generally components should specify an explosion prototype via a yaml datafield, so that the yaml-linter can
/// find errors. However some components, like rogue arrows, or some commands like the admin-smite need to have
/// a "default" option specified outside of yaml data-fields. Hence this const string.
/// </remarks>
public static readonly ProtoId<ExplosionPrototype> DefaultExplosionPrototypeId = "Default";
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -222,10 +212,8 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
return r0 * (MathF.Sqrt(12 * totalIntensity / v0 - 3) / 6 + 0.5f); return r0 * (MathF.Sqrt(12 * totalIntensity / v0 - 3) / 6 + 0.5f);
} }
/// <summary> /// <inheritdoc />
/// Queue an explosions, centered on some entity. public override void QueueExplosion(EntityUid uid,
/// </summary>
public void QueueExplosion(EntityUid uid,
string typeId, string typeId,
float totalIntensity, float totalIntensity,
float slope, float slope,

View File

@@ -1,14 +1,26 @@
using Content.Shared.Armor; using Content.Shared.Armor;
using Content.Shared.Explosion.Components; using Content.Shared.Explosion.Components;
using Robust.Shared.Prototypes;
namespace Content.Shared.Explosion.EntitySystems; namespace Content.Shared.Explosion.EntitySystems;
// TODO some sort of struct like DamageSpecifier but for explosions.
/// <summary> /// <summary>
/// Lets code in shared trigger explosions and handles explosion resistance examining. /// Lets code in shared trigger explosions and handles explosion resistance examining.
/// All processing is still done clientside. /// All processing is still done clientside.
/// </summary> /// </summary>
public abstract class SharedExplosionSystem : EntitySystem public abstract class SharedExplosionSystem : EntitySystem
{ {
/// <summary>
/// The "default" explosion prototype.
/// </summary>
/// <remarks>
/// Generally components should specify an explosion prototype via a yaml datafield, so that the yaml-linter can
/// find errors. However some components, like rogue arrows, or some commands like the admin-smite need to have
/// a "default" option specified outside of yaml data-fields. Hence this const string.
/// </remarks>
public static readonly ProtoId<ExplosionPrototype> DefaultExplosionPrototypeId = "Default";
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -37,4 +49,24 @@ public abstract class SharedExplosionSystem : EntitySystem
public virtual void TriggerExplosive(EntityUid uid, ExplosiveComponent? explosive = null, bool delete = true, float? totalIntensity = null, float? radius = null, EntityUid? user = null) public virtual void TriggerExplosive(EntityUid uid, ExplosiveComponent? explosive = null, bool delete = true, float? totalIntensity = null, float? radius = null, EntityUid? user = null)
{ {
} }
/// <summary>
/// Queue an explosion centered on some entity. Bypasses needing <see cref="ExplosiveComponent"/>.
/// </summary>
/// <param name="uid">Where the explosion happens.</param>
/// <param name="typeId">A ProtoId of type <see cref="ExplosionPrototype"/>.</param>
/// <param name="user">The entity which caused the explosion.</param>
/// <param name="addLog">Whether to add an admin log about this explosion. Includes user.</param>
public virtual void QueueExplosion(EntityUid uid,
string typeId,
float totalIntensity,
float slope,
float maxTileIntensity,
float tileBreakScale = 1f,
int maxTileBreak = int.MaxValue,
bool canCreateVacuum = true,
EntityUid? user = null,
bool addLog = true)
{
}
} }

View File

@@ -1,3 +1,4 @@
using Content.Shared.Explosion.Components;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
namespace Content.Shared.Trigger.Components.Effects; namespace Content.Shared.Trigger.Components.Effects;
@@ -7,8 +8,6 @@ namespace Content.Shared.Trigger.Components.Effects;
/// TargetUser will only work of the user has ExplosiveComponent as well. /// TargetUser will only work of the user has ExplosiveComponent as well.
/// The User will be logged in the admin logs. /// The User will be logged in the admin logs.
/// </summary> /// </summary>
/// <summary> /// <seealso cref="ExplosionOnTriggerComponent"/>
/// TODO: Allow this to work without an ExplosiveComponent on the user via QueueExplosion.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] [RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class ExplodeOnTriggerComponent : BaseXOnTriggerComponent; public sealed partial class ExplodeOnTriggerComponent : BaseXOnTriggerComponent;

View File

@@ -0,0 +1,46 @@
using Content.Shared.Explosion;
using Content.Shared.Explosion.Components;
using Content.Shared.Explosion.EntitySystems;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
namespace Content.Shared.Trigger.Components.Effects;
// TODO some sort of struct like DamageSpecifier but for explosions.
/// <summary>
/// Will explode the entity using this component's explosion specifications.
/// If TargetUser is true, they'll explode instead.
/// The User will be logged in the admin logs.
/// </summary>
/// <seealso cref="ExplodeOnTriggerComponent"/>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class ExplosionOnTriggerComponent : BaseXOnTriggerComponent
{
/// <inheritdoc cref="ExplosiveComponent.ExplosionType"/>
[DataField, AutoNetworkedField]
public ProtoId<ExplosionPrototype> ExplosionType = SharedExplosionSystem.DefaultExplosionPrototypeId;
/// <inheritdoc cref="ExplosiveComponent.MaxIntensity"/>
[DataField, AutoNetworkedField]
public float MaxTileIntensity = 4;
/// <inheritdoc cref="ExplosiveComponent.IntensitySlope"/>
[DataField, AutoNetworkedField]
public float IntensitySlope = 1;
/// <inheritdoc cref="ExplosiveComponent.TotalIntensity"/>
[DataField, AutoNetworkedField]
public float TotalIntensity = 10;
/// <inheritdoc cref="ExplosiveComponent.TileBreakScale"/>
[DataField, AutoNetworkedField]
public float TileBreakScale = 1f;
/// <inheritdoc cref="ExplosiveComponent.MaxTileBreak"/>
[DataField, AutoNetworkedField]
public int MaxTileBreak = int.MaxValue;
/// <inheritdoc cref="ExplosiveComponent.CanCreateVacuum"/>
[DataField, AutoNetworkedField]
public bool CanCreateVacuum = true;
}

View File

@@ -0,0 +1,11 @@
using Content.Shared.GameTicking;
using Robust.Shared.GameStates;
namespace Content.Shared.Trigger.Components.Triggers;
/// <summary>
/// A trigger which occurs on <see cref="PlayerSpawnCompleteEvent"/>.
/// </summary>
/// <remarks>This does not work with <see cref="TraitSystem"/>, as it would add this component while the event is getting raised.</remarks>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class TriggerOnPlayerSpawnCompleteComponent : BaseTriggerOnXComponent;

View File

@@ -11,10 +11,11 @@ public sealed class ExplodeOnTriggerSystem : EntitySystem
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<ExplodeOnTriggerComponent, TriggerEvent>(OnTrigger); SubscribeLocalEvent<ExplodeOnTriggerComponent, TriggerEvent>(OnExplodeTrigger);
SubscribeLocalEvent<ExplosionOnTriggerComponent, TriggerEvent>(OnQueueExplosionTrigger);
} }
private void OnTrigger(Entity<ExplodeOnTriggerComponent> ent, ref TriggerEvent args) private void OnExplodeTrigger(Entity<ExplodeOnTriggerComponent> ent, ref TriggerEvent args)
{ {
if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key)) if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key))
return; return;
@@ -27,4 +28,27 @@ public sealed class ExplodeOnTriggerSystem : EntitySystem
_explosion.TriggerExplosive(target.Value, user: args.User); _explosion.TriggerExplosive(target.Value, user: args.User);
args.Handled = true; args.Handled = true;
} }
private void OnQueueExplosionTrigger(Entity<ExplosionOnTriggerComponent> ent, ref TriggerEvent args)
{
var (uid, comp) = ent;
if (args.Key != null && !comp.KeysIn.Contains(args.Key))
return;
var target = comp.TargetUser ? args.User : uid;
if (target == null)
return;
_explosion.QueueExplosion(target.Value,
comp.ExplosionType,
comp.TotalIntensity,
comp.IntensitySlope,
comp.MaxTileIntensity,
comp.TileBreakScale,
comp.MaxTileBreak,
comp.CanCreateVacuum,
args.User);
args.Handled = true;
}
} }

View File

@@ -1,4 +1,5 @@
using Content.Shared.Trigger.Components.Effects; using Content.Shared.GameTicking;
using Content.Shared.Trigger.Components.Effects;
using Content.Shared.Trigger.Components.Triggers; using Content.Shared.Trigger.Components.Triggers;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -9,6 +10,7 @@ public sealed partial class TriggerSystem
private void InitializeSpawn() private void InitializeSpawn()
{ {
SubscribeLocalEvent<TriggerOnSpawnComponent, MapInitEvent>(OnSpawnInit); SubscribeLocalEvent<TriggerOnSpawnComponent, MapInitEvent>(OnSpawnInit);
SubscribeLocalEvent<TriggerOnPlayerSpawnCompleteComponent, PlayerSpawnCompleteEvent>(OnPlayerSpawn);
SubscribeLocalEvent<SpawnOnTriggerComponent, TriggerEvent>(HandleSpawnOnTrigger); SubscribeLocalEvent<SpawnOnTriggerComponent, TriggerEvent>(HandleSpawnOnTrigger);
SubscribeLocalEvent<SpawnEntityTableOnTriggerComponent, TriggerEvent>(HandleSpawnTableOnTrigger); SubscribeLocalEvent<SpawnEntityTableOnTriggerComponent, TriggerEvent>(HandleSpawnTableOnTrigger);
@@ -20,6 +22,11 @@ public sealed partial class TriggerSystem
Trigger(ent.Owner, null, ent.Comp.KeyOut); Trigger(ent.Owner, null, ent.Comp.KeyOut);
} }
private void OnPlayerSpawn(Entity<TriggerOnPlayerSpawnCompleteComponent> ent, ref PlayerSpawnCompleteEvent args)
{
Trigger(ent.Owner, null, ent.Comp.KeyOut);
}
private void HandleSpawnOnTrigger(Entity<SpawnOnTriggerComponent> ent, ref TriggerEvent args) private void HandleSpawnOnTrigger(Entity<SpawnOnTriggerComponent> ent, ref TriggerEvent args)
{ {
if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key)) if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key))