--- /dev/null
+using Content.Server.Explosion.EntitySystems;
+using Content.Shared.EntityEffects;
+using Content.Shared.EntityEffects.Effects.Transform;
+using Content.Shared.Explosion.Components;
+
+namespace Content.Server.EntityEffects.Effects;
+
+/// <summary>
+/// Makes this entity explode using its <see cref="ExplosiveComponent"/>.
+/// </summary>
+/// <inheritdoc cref="EntityEffectSystem{T,TEffect}"/>
+public sealed partial class ExplodeEntityEffectSystem : EntityEffectSystem<ExplosiveComponent, ExplodeEffect>
+{
+ [Dependency] private readonly ExplosionSystem _explosion = default!;
+
+ protected override void Effect(Entity<ExplosiveComponent> entity, ref EntityEffectEvent<ExplodeEffect> args)
+ {
+ _explosion.TriggerExplosive(entity, entity, args.Effect.Delete, args.Effect.Intensity, args.Effect.Radius, args.User);
+ }
+}
{
var otherUid = args.Tripper;
- _entityEffects.ApplyEffects(otherUid, ent.Comp.Effects.ToArray());
+ _entityEffects.ApplyEffects(otherUid, ent.Comp.Effects.ToArray(), user: otherUid);
}
}
/// <summary>
/// Force entity to be destroyed and deleted.
/// </summary>
- public bool DestroyEntity(EntityUid owner)
+ public bool DestroyEntity(Entity<MetaDataComponent?> owner)
{
var ev = new DestructionAttemptEvent();
RaiseLocalEvent(owner, ev);
--- /dev/null
+using Content.Shared.Database;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects.Transform;
+
+/// <inheritdoc cref="EntityEffect"/>
+/// <seealso cref="ExplosionEffect">
+public sealed partial class ExplodeEffect : EntityEffectBase<ExplodeEffect>
+{
+ /// <summary>
+ /// Optional override for the explosion intensity.
+ /// </summary>
+ [DataField]
+ public float? Intensity;
+
+ /// <summary>
+ /// Optional override for the explosion radius.
+ /// </summary>
+ [DataField]
+ public float? Radius;
+
+ /// <summary>
+ /// Delete the entity with the explosion?
+ /// </summary>
+ [DataField]
+ public bool Delete = true;
+
+ public override string EntityEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ => Loc.GetString("entity-effect-guidebook-explosion", ("chance", Probability));
+
+ public override LogImpact? Impact => LogImpact.High;
+}
--- /dev/null
+using Content.Shared.Destructible;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects.Effects.MetaData;
+
+
+/// <inheritdoc cref="EntityEffectSystem{T,TEffect}"/>
+public sealed partial class DestructibleActEntityEffectSystem : EntityEffectSystem<MetaDataComponent, DestructibleAct>
+{
+ [Dependency] private readonly SharedDestructibleSystem _destructible = default!;
+
+ protected override void Effect(Entity<MetaDataComponent> entity, ref EntityEffectEvent<DestructibleAct> args)
+ {
+ if ((args.Effect.Acts & ThresholdActs.Breakage) != 0)
+ _destructible.BreakEntity(entity);
+
+ if ((args.Effect.Acts & ThresholdActs.Destruction) != 0)
+ _destructible.DestroyEntity(entity.AsNullable());
+ }
+}
+
+/// <summary>
+/// Destroys or breaks an entity.
+/// </summary>
+public sealed partial class DestructibleAct : EntityEffectBase<DestructibleAct>
+{
+ /// <summary>
+ /// What acts should be triggered upon activation.
+ /// </summary>
+ [DataField]
+ public ThresholdActs Acts;
+
+ public override string EntityEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
+ {
+ if ((Acts & ThresholdActs.Destruction) != 0)
+ return Loc.GetString("entity-effect-guidebook-destroy", ("chance", Probability));
+
+ return Loc.GetString("entity-effect-guidebook-break", ("chance", Probability));
+ }
+}
namespace Content.Shared.EntityEffects.Effects.Transform;
/// <inheritdoc cref="EntityEffect"/>
+/// <seealso cref="ExplodeEffect">
public sealed partial class ExplosionEffect : EntityEffectBase<ExplosionEffect>
{
/// <summary>
public float TileBreakScale = 1f;
public override string EntityEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys)
- => Loc.GetString("entity-effect-guidebook-explosion-reaction-effect", ("chance", Probability));
+ => Loc.GetString("entity-effect-guidebook-explosion", ("chance", Probability));
public override LogImpact? Impact => LogImpact.High;
}
using Content.Shared.Popups;
using Robust.Shared.Network;
using Robust.Shared.Random;
+using Robust.Shared.Serialization;
namespace Content.Shared.EntityEffects.Effects.Transform;
if (_net.IsClient)
return;
- var msg = _random.Pick(args.Effect.Messages);
+ var msg = Loc.GetString(_random.Pick(args.Effect.Messages), ("entity", entity));
- switch (args.Effect.Type)
+ switch ((args.Effect.Method, args.Effect.Type))
{
- case PopupRecipients.Local:
- _popup.PopupEntity(Loc.GetString(msg, ("entity", entity)), entity, entity, args.Effect.VisualType);
+ case (PopupMethod.PopupEntity, PopupRecipients.Local):
+ _popup.PopupEntity(msg, entity, entity, args.Effect.VisualType);
break;
- case PopupRecipients.Pvs:
- _popup.PopupEntity(Loc.GetString(msg, ("entity", entity)), entity, args.Effect.VisualType);
+ case (PopupMethod.PopupEntity, PopupRecipients.Pvs):
+ _popup.PopupEntity(msg, entity, args.Effect.VisualType);
+ break;
+ case (PopupMethod.PopupCoordinates, PopupRecipients.Local):
+ _popup.PopupCoordinates(msg, Transform(entity).Coordinates, entity, args.Effect.VisualType);
+ break;
+ case (PopupMethod.PopupCoordinates, PopupRecipients.Pvs):
+ _popup.PopupCoordinates(msg, Transform(entity).Coordinates, args.Effect.VisualType);
break;
}
}
[DataField]
public PopupRecipients Type = PopupRecipients.Local;
+ /// <summary>
+ /// Which popup API method to use.
+ /// Use PopupCoordinates in case the entity will be deleted while the popup is shown.
+ /// </summary>
+ [DataField]
+ public PopupMethod Method = PopupMethod.PopupEntity;
+
/// <summary>
/// Size of the popup.
/// </summary>
public PopupType VisualType = PopupType.Small;
}
-public enum PopupRecipients
+[Serializable, NetSerializable]
+public enum PopupRecipients : byte
{
Pvs,
- Local
+ Local,
+}
+
+[Serializable, NetSerializable]
+public enum PopupMethod : byte
+{
+ PopupEntity,
+ PopupCoordinates,
}
--- /dev/null
+using Content.Shared.Database;
+using Content.Shared.EntityConditions;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.EntityEffects;
+
+/// <summary>
+/// A basic instantaneous effect which can be applied to an entity via events.
+/// </summary>
+[ImplicitDataDefinitionForInheritors]
+public abstract partial class EntityEffect
+{
+ public abstract void RaiseEvent(EntityUid target, IEntityEffectRaiser raiser, float scale, EntityUid? user);
+
+ [DataField]
+ public EntityCondition[]? Conditions;
+
+ /// <summary>
+ /// If our scale is less than this value, the effect fails.
+ /// </summary>
+ [DataField]
+ public virtual float MinScale { get; private set; }
+
+ /// <summary>
+ /// If true, then it allows the scale multiplier to go above 1.
+ /// </summary>
+ [DataField]
+ public virtual bool Scaling { get; private set; }
+
+ // TODO: This should be an entity condition but guidebook relies on it heavily for formatting...
+ /// <summary>
+ /// Probability of the effect occuring.
+ /// </summary>
+ [DataField]
+ public float Probability = 1.0f;
+
+ public virtual string? EntityEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => null;
+
+ /// <summary>
+ /// If this effect is logged, how important is the log?
+ /// </summary>
+ [ViewVariables]
+ public virtual LogImpact? Impact => null;
+
+ [ViewVariables]
+ public virtual LogType LogType => LogType.EntityEffect;
+}
+
+/// <summary>
+/// Used to store an <see cref="EntityEffect"/> so it can be raised without losing the type of the condition.
+/// </summary>
+/// <typeparam name="T">The Condition wer are raising.</typeparam>
+public abstract partial class EntityEffectBase<T> : EntityEffect where T : EntityEffectBase<T>
+{
+ public override void RaiseEvent(EntityUid target, IEntityEffectRaiser raiser, float scale, EntityUid? user)
+ {
+ if (this is not T type)
+ return;
+
+ raiser.RaiseEffectEvent(target, type, scale, user);
+ }
+}
--- /dev/null
+namespace Content.Shared.EntityEffects;
+
+/// <summary>
+/// An Event carrying an entity effect.
+/// </summary>
+/// <param name="Effect">The Effect</param>
+/// <param name="Scale">A strength scalar for the effect, defaults to 1 and typically only goes under for incomplete reactions.</param>
+/// <param name="User">The entity causing the effect.</param>
+[ByRefEvent, Access(typeof(SharedEntityEffectsSystem))]
+public readonly record struct EntityEffectEvent<T>(T Effect, float Scale, EntityUid? User) where T : EntityEffectBase<T>
+{
+ /// <summary>
+ /// The Condition being raised in this event
+ /// </summary>
+ public readonly T Effect = Effect;
+
+ /// <summary>
+ /// The Scale modifier of this Effect.
+ /// </summary>
+ public readonly float Scale = Scale;
+
+ /// <summary>
+ /// The entity that caused this effect.
+ /// Used for admin logs and prediction purposes.
+ /// </summary>
+ public readonly EntityUid? User = User;
+}
-using System.Diagnostics.CodeAnalysis;
using Content.Shared.Administration.Logs;
using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Reaction;
-using Content.Shared.Database;
using Content.Shared.EntityConditions;
using Content.Shared.Random.Helpers;
-using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
/// <param name="target">Entity being targeted by the effects</param>
/// <param name="effects">Effects we're applying to the entity</param>
/// <param name="scale">Optional scale multiplier for the effects</param>
- public void ApplyEffects(EntityUid target, EntityEffect[] effects, float scale = 1f)
+ /// <param name="user">The entity causing the effect.</param>
+ public void ApplyEffects(EntityUid target, EntityEffect[] effects, float scale = 1f, EntityUid? user = null)
{
// do all effects, if conditions apply
foreach (var effect in effects)
{
- TryApplyEffect(target, effect, scale);
+ TryApplyEffect(target, effect, scale, user);
}
}
/// </summary>
/// <param name="target">Target we're applying an effect to</param>
/// <param name="effect">Effect we're applying</param>
- /// <param name="scale">Optional scale multiplier for the effect. Not all </param>
+ /// <param name="scale">Optional scale multiplier for the effect.</param>
+ /// <param name="user">The entity causing the effect.</param>
/// <returns>True if all conditions pass!</returns>
- public bool TryApplyEffect(EntityUid target, EntityEffect effect, float scale = 1f)
+ public bool TryApplyEffect(EntityUid target, EntityEffect effect, float scale = 1f, EntityUid? user = null)
{
if (scale < effect.MinScale)
return false;
if (!_condition.TryConditions(target, effect.Conditions))
return false;
- ApplyEffect(target, effect, scale);
+ ApplyEffect(target, effect, scale, user);
return true;
}
/// </summary>
/// <param name="target">Target we're applying an effect to</param>
/// <param name="effect">Effect we're applying</param>
- /// <param name="scale">Optional scale multiplier for the effect. Not all </param>
- public void ApplyEffect(EntityUid target, EntityEffect effect, float scale = 1f)
+ /// <param name="scale">Optional scale multiplier for the effect.</param>
+ /// <param name="user">The entity causing the effect.</param>
+ public void ApplyEffect(EntityUid target, EntityEffect effect, float scale = 1f, EntityUid? user = null)
{
// Clamp the scale if the effect doesn't allow scaling.
if (!effect.Scaling)
scale = Math.Min(scale, 1f);
- if (effect.Impact is {} level)
+ if (effect.Impact is { } level)
{
_adminLog.Add(
effect.LogType,
);
}
- effect.RaiseEvent(target, this, scale);
+ effect.RaiseEvent(target, this, scale, user);
}
/// <summary>
/// Raises an effect to an entity. You should not be calling this unless you know what you're doing.
/// </summary>
- public void RaiseEffectEvent<T>(EntityUid target, T effect, float scale) where T : EntityEffectBase<T>
+ public void RaiseEffectEvent<T>(EntityUid target, T effect, float scale, EntityUid? user) where T : EntityEffectBase<T>
{
- var effectEv = new EntityEffectEvent<T>(effect, scale);
+ var effectEv = new EntityEffectEvent<T>(effect, scale, user);
RaiseLocalEvent(target, ref effectEv);
}
}
/// </summary>
public interface IEntityEffectRaiser
{
- void RaiseEffectEvent<T>(EntityUid target, T effect, float scale) where T : EntityEffectBase<T>;
-}
-
-/// <summary>
-/// Used to store an <see cref="EntityEffect"/> so it can be raised without losing the type of the condition.
-/// </summary>
-/// <typeparam name="T">The Condition wer are raising.</typeparam>
-public abstract partial class EntityEffectBase<T> : EntityEffect where T : EntityEffectBase<T>
-{
- public override void RaiseEvent(EntityUid target, IEntityEffectRaiser raiser, float scale)
- {
- if (this is not T type)
- return;
-
- raiser.RaiseEffectEvent(target, type, scale);
- }
-}
-
-/// <summary>
-/// A basic instantaneous effect which can be applied to an entity via events.
-/// </summary>
-[ImplicitDataDefinitionForInheritors]
-public abstract partial class EntityEffect
-{
- public abstract void RaiseEvent(EntityUid target, IEntityEffectRaiser raiser, float scale);
-
- [DataField]
- public EntityCondition[]? Conditions;
-
- /// <summary>
- /// If our scale is less than this value, the effect fails.
- /// </summary>
- [DataField]
- public virtual float MinScale { get; private set; }
-
- /// <summary>
- /// If true, then it allows the scale multiplier to go above 1.
- /// </summary>
- [DataField]
- public virtual bool Scaling { get; private set; }
-
- // TODO: This should be an entity condition but guidebook relies on it heavily for formatting...
- /// <summary>
- /// Probability of the effect occuring.
- /// </summary>
- [DataField]
- public float Probability = 1.0f;
-
- public virtual string? EntityEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) => null;
-
- /// <summary>
- /// If this effect is logged, how important is the log?
- /// </summary>
- [ViewVariables]
- public virtual LogImpact? Impact => null;
-
- [ViewVariables]
- public virtual LogType LogType => LogType.EntityEffect;
-}
-
-/// <summary>
-/// An Event carrying an entity effect.
-/// </summary>
-/// <param name="Effect">The Effect</param>
-/// <param name="Scale">A strength scalar for the effect, defaults to 1 and typically only goes under for incomplete reactions.</param>
-[ByRefEvent, Access(typeof(SharedEntityEffectsSystem))]
-public readonly record struct EntityEffectEvent<T>(T Effect, float Scale) where T : EntityEffectBase<T>
-{
- /// <summary>
- /// The Condition being raised in this event
- /// </summary>
- public readonly T Effect = Effect;
-
- /// <summary>
- /// The Scale modifier of this Effect.
- /// </summary>
- public readonly float Scale = Scale;
+ void RaiseEffectEvent<T>(EntityUid target, T effect, float scale, EntityUid? user) where T : EntityEffectBase<T>;
}
*[other] {$amount} {MAKEPLURAL($entname)}
}
+entity-effect-guidebook-destroy =
+ { $chance ->
+ [1] Destroys
+ *[other] destroy
+ } the object
+
+entity-effect-guidebook-break =
+ { $chance ->
+ [1] Breaks
+ *[other] break
+ } the object
+
entity-effect-guidebook-explosion =
{ $chance ->
[1] Causes