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()
{
base.Initialize();
return r0 * (MathF.Sqrt(12 * totalIntensity / v0 - 3) / 6 + 0.5f);
}
- /// <summary>
- /// Queue an explosions, centered on some entity.
- /// </summary>
- public void QueueExplosion(EntityUid uid,
+ /// <inheritdoc />
+ public override void QueueExplosion(EntityUid uid,
string typeId,
float totalIntensity,
float slope,
using Content.Shared.Armor;
using Content.Shared.Explosion.Components;
+using Robust.Shared.Prototypes;
namespace Content.Shared.Explosion.EntitySystems;
+// TODO some sort of struct like DamageSpecifier but for explosions.
/// <summary>
/// Lets code in shared trigger explosions and handles explosion resistance examining.
/// All processing is still done clientside.
/// </summary>
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()
{
base.Initialize();
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)
+ {
+ }
}
+using Content.Shared.Explosion.Components;
using Robust.Shared.GameStates;
namespace Content.Shared.Trigger.Components.Effects;
/// TargetUser will only work of the user has ExplosiveComponent as well.
/// The User will be logged in the admin logs.
/// </summary>
-/// <summary>
-/// TODO: Allow this to work without an ExplosiveComponent on the user via QueueExplosion.
-/// </summary>
+/// <seealso cref="ExplosionOnTriggerComponent"/>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class ExplodeOnTriggerComponent : BaseXOnTriggerComponent;
--- /dev/null
+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;
+}
--- /dev/null
+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;
{
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))
return;
_explosion.TriggerExplosive(target.Value, user: args.User);
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;
+ }
}
-using Content.Shared.Trigger.Components.Effects;
+using Content.Shared.GameTicking;
+using Content.Shared.Trigger.Components.Effects;
using Content.Shared.Trigger.Components.Triggers;
using Robust.Shared.Prototypes;
private void InitializeSpawn()
{
SubscribeLocalEvent<TriggerOnSpawnComponent, MapInitEvent>(OnSpawnInit);
+ SubscribeLocalEvent<TriggerOnPlayerSpawnCompleteComponent, PlayerSpawnCompleteEvent>(OnPlayerSpawn);
SubscribeLocalEvent<SpawnOnTriggerComponent, TriggerEvent>(HandleSpawnOnTrigger);
SubscribeLocalEvent<SpawnEntityTableOnTriggerComponent, TriggerEvent>(HandleSpawnTableOnTrigger);
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)
{
if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key))