@@ -1,10 +1,8 @@
|
||||
using Content.Server.Destructible.Thresholds.Triggers;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.Destructible.Thresholds.Triggers;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
||||
|
||||
@@ -91,7 +89,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
||||
Assert.That(threshold.Trigger, Is.InstanceOf<AndTrigger>());
|
||||
});
|
||||
|
||||
var trigger = (AndTrigger) threshold.Trigger;
|
||||
var trigger = (AndTrigger)threshold.Trigger;
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@@ -162,7 +160,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
||||
Assert.That(threshold.Trigger, Is.InstanceOf<AndTrigger>());
|
||||
});
|
||||
|
||||
trigger = (AndTrigger) threshold.Trigger;
|
||||
trigger = (AndTrigger)threshold.Trigger;
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using Content.Server.Destructible.Thresholds.Triggers;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.Destructible.Thresholds.Triggers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
||||
@@ -86,7 +86,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
||||
Assert.That(threshold.Trigger, Is.InstanceOf<AndTrigger>());
|
||||
});
|
||||
|
||||
var trigger = (AndTrigger) threshold.Trigger;
|
||||
var trigger = (AndTrigger)threshold.Trigger;
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@@ -154,7 +154,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
||||
Assert.That(threshold.Trigger, Is.InstanceOf<AndTrigger>());
|
||||
});
|
||||
|
||||
trigger = (AndTrigger) threshold.Trigger;
|
||||
trigger = (AndTrigger)threshold.Trigger;
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Destructible.Thresholds;
|
||||
using Content.Server.Destructible.Thresholds.Behaviors;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
@@ -59,7 +58,7 @@ namespace Content.IntegrationTests.Tests.Destructible
|
||||
Assert.That(threshold.Behaviors, Has.Count.EqualTo(3));
|
||||
});
|
||||
|
||||
var spawnEntitiesBehavior = (SpawnEntitiesBehavior) threshold.Behaviors.Single(b => b is SpawnEntitiesBehavior);
|
||||
var spawnEntitiesBehavior = (SpawnEntitiesBehavior)threshold.Behaviors.Single(b => b is SpawnEntitiesBehavior);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
|
||||
@@ -2,12 +2,12 @@ using System.Linq;
|
||||
using Content.Server.Destructible;
|
||||
using Content.Server.Destructible.Thresholds;
|
||||
using Content.Server.Destructible.Thresholds.Behaviors;
|
||||
using Content.Server.Destructible.Thresholds.Triggers;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Content.Shared.Destructible;
|
||||
using Content.Shared.Destructible.Thresholds.Triggers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
using static Content.IntegrationTests.Tests.Destructible.DestructibleTestPrototypes;
|
||||
@@ -99,9 +99,9 @@ namespace Content.IntegrationTests.Tests.Destructible
|
||||
// Check that it matches the YAML prototype
|
||||
Assert.That(threshold.Behaviors, Has.Count.EqualTo(3));
|
||||
|
||||
var soundThreshold = (PlaySoundBehavior) threshold.Behaviors[0];
|
||||
var spawnThreshold = (SpawnEntitiesBehavior) threshold.Behaviors[1];
|
||||
var actsThreshold = (DoActsBehavior) threshold.Behaviors[2];
|
||||
var soundThreshold = (PlaySoundBehavior)threshold.Behaviors[0];
|
||||
var spawnThreshold = (SpawnEntitiesBehavior)threshold.Behaviors[1];
|
||||
var actsThreshold = (DoActsBehavior)threshold.Behaviors[2];
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@@ -164,9 +164,9 @@ namespace Content.IntegrationTests.Tests.Destructible
|
||||
// Check that it matches the YAML prototype
|
||||
Assert.That(threshold.Behaviors, Has.Count.EqualTo(3));
|
||||
|
||||
soundThreshold = (PlaySoundBehavior) threshold.Behaviors[0];
|
||||
spawnThreshold = (SpawnEntitiesBehavior) threshold.Behaviors[1];
|
||||
actsThreshold = (DoActsBehavior) threshold.Behaviors[2];
|
||||
soundThreshold = (PlaySoundBehavior)threshold.Behaviors[0];
|
||||
spawnThreshold = (SpawnEntitiesBehavior)threshold.Behaviors[1];
|
||||
actsThreshold = (DoActsBehavior)threshold.Behaviors[2];
|
||||
|
||||
// Check that it matches the YAML prototype
|
||||
Assert.Multiple(() =>
|
||||
@@ -201,11 +201,11 @@ namespace Content.IntegrationTests.Tests.Destructible
|
||||
|
||||
// Verify the first one, should be the lowest one (20)
|
||||
msg = sTestThresholdListenerSystem.ThresholdsReached[0];
|
||||
var trigger = (DamageTrigger) msg.Threshold.Trigger;
|
||||
var trigger = (DamageTrigger)msg.Threshold.Trigger;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(trigger, Is.Not.Null);
|
||||
Assert.That(trigger.Damage, Is.EqualTo(20));
|
||||
Assert.That(trigger.Damage, Is.EqualTo(FixedPoint2.New(20)));
|
||||
});
|
||||
|
||||
threshold = msg.Threshold;
|
||||
@@ -215,20 +215,20 @@ namespace Content.IntegrationTests.Tests.Destructible
|
||||
|
||||
// Verify the second one, should be the highest one (50)
|
||||
msg = sTestThresholdListenerSystem.ThresholdsReached[1];
|
||||
trigger = (DamageTrigger) msg.Threshold.Trigger;
|
||||
trigger = (DamageTrigger)msg.Threshold.Trigger;
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(trigger, Is.Not.Null);
|
||||
Assert.That(trigger.Damage, Is.EqualTo(50));
|
||||
Assert.That(trigger.Damage, Is.EqualTo(FixedPoint2.New(50)));
|
||||
});
|
||||
|
||||
threshold = msg.Threshold;
|
||||
|
||||
Assert.That(threshold.Behaviors, Has.Count.EqualTo(3));
|
||||
|
||||
soundThreshold = (PlaySoundBehavior) threshold.Behaviors[0];
|
||||
spawnThreshold = (SpawnEntitiesBehavior) threshold.Behaviors[1];
|
||||
actsThreshold = (DoActsBehavior) threshold.Behaviors[2];
|
||||
soundThreshold = (PlaySoundBehavior)threshold.Behaviors[0];
|
||||
spawnThreshold = (SpawnEntitiesBehavior)threshold.Behaviors[1];
|
||||
actsThreshold = (DoActsBehavior)threshold.Behaviors[2];
|
||||
|
||||
// Check that it matches the YAML prototype
|
||||
Assert.Multiple(() =>
|
||||
|
||||
@@ -6,7 +6,6 @@ using Content.Server.Body.Systems;
|
||||
using Content.Server.Construction;
|
||||
using Content.Server.Destructible.Thresholds;
|
||||
using Content.Server.Destructible.Thresholds.Behaviors;
|
||||
using Content.Server.Destructible.Thresholds.Triggers;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Server.Fluids.EntitySystems;
|
||||
using Content.Server.Stack;
|
||||
@@ -14,6 +13,7 @@ using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Destructible;
|
||||
using Content.Shared.Destructible.Thresholds.Triggers;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Humanoid;
|
||||
using Content.Shared.Trigger.Systems;
|
||||
@@ -42,24 +42,24 @@ namespace Content.Server.Destructible
|
||||
[Dependency] public readonly PuddleSystem PuddleSystem = default!;
|
||||
[Dependency] public readonly SharedContainerSystem ContainerSystem = default!;
|
||||
[Dependency] public readonly IPrototypeManager PrototypeManager = default!;
|
||||
[Dependency] public readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] public readonly IAdminLogManager AdminLogger = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<DestructibleComponent, DamageChangedEvent>(Execute);
|
||||
SubscribeLocalEvent<DestructibleComponent, DamageChangedEvent>(OnDamageChanged);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if any thresholds were reached. if they were, execute them.
|
||||
/// Check if any thresholds were reached. if they were, execute them.
|
||||
/// </summary>
|
||||
public void Execute(EntityUid uid, DestructibleComponent component, DamageChangedEvent args)
|
||||
private void OnDamageChanged(EntityUid uid, DestructibleComponent component, DamageChangedEvent args)
|
||||
{
|
||||
component.IsBroken = false;
|
||||
|
||||
foreach (var threshold in component.Thresholds)
|
||||
{
|
||||
if (threshold.Reached(args.Damageable, this))
|
||||
if (Triggered(threshold, (uid, args.Damageable)))
|
||||
{
|
||||
RaiseLocalEvent(uid, new DamageThresholdReached(component, threshold), true);
|
||||
|
||||
@@ -82,18 +82,18 @@ namespace Content.Server.Destructible
|
||||
|
||||
if (args.Origin != null)
|
||||
{
|
||||
_adminLogger.Add(LogType.Damaged,
|
||||
AdminLogger.Add(LogType.Damaged,
|
||||
logImpact,
|
||||
$"{ToPrettyString(args.Origin.Value):actor} caused {ToPrettyString(uid):subject} to trigger [{triggeredBehaviors}]");
|
||||
}
|
||||
else
|
||||
{
|
||||
_adminLogger.Add(LogType.Damaged,
|
||||
AdminLogger.Add(LogType.Damaged,
|
||||
logImpact,
|
||||
$"Unknown damage source caused {ToPrettyString(uid):subject} to trigger [{triggeredBehaviors}]");
|
||||
}
|
||||
|
||||
threshold.Execute(uid, this, EntityManager, args.Origin);
|
||||
Execute(threshold, uid, args.Origin);
|
||||
}
|
||||
|
||||
if (threshold.OldTriggered)
|
||||
@@ -108,6 +108,61 @@ namespace Content.Server.Destructible
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the given threshold should trigger.
|
||||
/// </summary>
|
||||
public bool Triggered(DamageThreshold threshold, Entity<DamageableComponent> owner)
|
||||
{
|
||||
if (threshold.Trigger == null)
|
||||
return false;
|
||||
|
||||
if (threshold.Triggered && threshold.TriggersOnce)
|
||||
return false;
|
||||
|
||||
if (threshold.OldTriggered)
|
||||
{
|
||||
threshold.OldTriggered = threshold.Trigger.Reached(owner, this);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!threshold.Trigger.Reached(owner, this))
|
||||
return false;
|
||||
|
||||
threshold.OldTriggered = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if the conditions for the given threshold are currently true.
|
||||
/// </summary>
|
||||
public bool Reached(DamageThreshold threshold, Entity<DamageableComponent> owner)
|
||||
{
|
||||
if (threshold.Trigger == null)
|
||||
return false;
|
||||
|
||||
return threshold.Trigger.Reached(owner, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers this threshold.
|
||||
/// </summary>
|
||||
/// <param name="owner">The entity that owns this threshold.</param>
|
||||
/// <param name="cause">The entity that caused this threshold to trigger.</param>
|
||||
public void Execute(DamageThreshold threshold, EntityUid owner, EntityUid? cause = null)
|
||||
{
|
||||
threshold.Triggered = true;
|
||||
|
||||
foreach (var behavior in threshold.Behaviors)
|
||||
{
|
||||
// The owner has been deleted. We stop execution of behaviors here.
|
||||
if (!Exists(owner))
|
||||
return;
|
||||
|
||||
// TODO: Replace with EntityEffects.
|
||||
behavior.Execute(owner, this, cause);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetDestroyedAt(Entity<DestructibleComponent?> ent, [NotNullWhen(true)] out FixedPoint2? destroyedAt)
|
||||
{
|
||||
destroyedAt = null;
|
||||
@@ -145,7 +200,7 @@ namespace Content.Server.Destructible
|
||||
if (behavior is DoActsBehavior actBehavior &&
|
||||
actBehavior.HasAct(ThresholdActs.Destruction | ThresholdActs.Breakage))
|
||||
{
|
||||
damageNeeded = Math.Min(damageNeeded.Float(), trigger.Damage);
|
||||
damageNeeded = FixedPoint2.Min(damageNeeded, trigger.Damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
namespace Content.Server.Destructible.Thresholds
|
||||
{
|
||||
public sealed class ActsFlags { }
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Content.Server.Destructible.Thresholds.Behaviors
|
||||
using Content.Shared.Destructible;
|
||||
|
||||
namespace Content.Server.Destructible.Thresholds.Behaviors
|
||||
{
|
||||
[Serializable]
|
||||
[DataDefinition]
|
||||
|
||||
@@ -1,96 +1,43 @@
|
||||
using Content.Server.Destructible.Thresholds.Behaviors;
|
||||
using Content.Server.Destructible.Thresholds.Triggers;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Destructible.Thresholds.Triggers;
|
||||
|
||||
namespace Content.Server.Destructible.Thresholds
|
||||
namespace Content.Server.Destructible.Thresholds;
|
||||
|
||||
[DataDefinition]
|
||||
public sealed partial class DamageThreshold
|
||||
{
|
||||
[DataDefinition]
|
||||
public sealed partial class DamageThreshold
|
||||
{
|
||||
[DataField("behaviors")]
|
||||
private List<IThresholdBehavior> _behaviors = new();
|
||||
/// <summary>
|
||||
/// Whether or not this threshold was triggered in the previous call to
|
||||
/// <see cref="Reached"/>.
|
||||
/// </summary>
|
||||
[ViewVariables] public bool OldTriggered;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this threshold was triggered in the previous call to
|
||||
/// <see cref="Reached"/>.
|
||||
/// </summary>
|
||||
[ViewVariables] public bool OldTriggered { get; private set; }
|
||||
/// <summary>
|
||||
/// Whether or not this threshold has already been triggered.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Triggered;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this threshold has already been triggered.
|
||||
/// </summary>
|
||||
[DataField("triggered")]
|
||||
public bool Triggered { get; private set; }
|
||||
/// <summary>
|
||||
/// Whether or not this threshold only triggers once.
|
||||
/// If false, it will trigger again once the entity is healed
|
||||
/// and then damaged to reach this threshold once again.
|
||||
/// It will not repeatedly trigger as damage rises beyond that.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool TriggersOnce;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this threshold only triggers once.
|
||||
/// If false, it will trigger again once the entity is healed
|
||||
/// and then damaged to reach this threshold once again.
|
||||
/// It will not repeatedly trigger as damage rises beyond that.
|
||||
/// </summary>
|
||||
[DataField("triggersOnce")]
|
||||
public bool TriggersOnce { get; set; }
|
||||
/// <summary>
|
||||
/// The condition that decides if this threshold has been reached.
|
||||
/// Gets evaluated each time the entity's damage changes.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public IThresholdTrigger? Trigger;
|
||||
|
||||
/// <summary>
|
||||
/// The trigger that decides if this threshold has been reached.
|
||||
/// </summary>
|
||||
[DataField("trigger")]
|
||||
public IThresholdTrigger? Trigger { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Behaviors to activate once this threshold is triggered.
|
||||
/// </summary>
|
||||
[ViewVariables] public IReadOnlyList<IThresholdBehavior> Behaviors => _behaviors;
|
||||
|
||||
public bool Reached(DamageableComponent damageable, DestructibleSystem system)
|
||||
{
|
||||
if (Trigger == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Triggered && TriggersOnce)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (OldTriggered)
|
||||
{
|
||||
OldTriggered = Trigger.Reached(damageable, system);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Trigger.Reached(damageable, system))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
OldTriggered = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers this threshold.
|
||||
/// </summary>
|
||||
/// <param name="owner">The entity that owns this threshold.</param>
|
||||
/// <param name="system">
|
||||
/// An instance of <see cref="DestructibleSystem"/> to get dependency and
|
||||
/// system references from, if relevant.
|
||||
/// </param>
|
||||
/// <param name="entityManager"></param>
|
||||
/// <param name="cause"></param>
|
||||
public void Execute(EntityUid owner, DestructibleSystem system, IEntityManager entityManager, EntityUid? cause)
|
||||
{
|
||||
Triggered = true;
|
||||
|
||||
foreach (var behavior in Behaviors)
|
||||
{
|
||||
// The owner has been deleted. We stop execution of behaviors here.
|
||||
if (!entityManager.EntityExists(owner))
|
||||
return;
|
||||
|
||||
behavior.Execute(owner, system, cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Behaviors to activate once this threshold is triggered.
|
||||
/// TODO: Replace with EntityEffects.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<IThresholdBehavior> Behaviors = new();
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Destructible.Thresholds
|
||||
{
|
||||
[Flags, FlagsFor(typeof(ActsFlags))]
|
||||
[Serializable]
|
||||
public enum ThresholdActs
|
||||
{
|
||||
None = 0,
|
||||
Breakage,
|
||||
Destruction
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using Content.Shared.Damage;
|
||||
|
||||
namespace Content.Server.Destructible.Thresholds.Triggers
|
||||
{
|
||||
/// <summary>
|
||||
/// A trigger that will activate when all of its triggers have activated.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataDefinition]
|
||||
public sealed partial class AndTrigger : IThresholdTrigger
|
||||
{
|
||||
[DataField("triggers")]
|
||||
public List<IThresholdTrigger> Triggers { get; set; } = new();
|
||||
|
||||
public bool Reached(DamageableComponent damageable, DestructibleSystem system)
|
||||
{
|
||||
foreach (var trigger in Triggers)
|
||||
{
|
||||
if (!trigger.Reached(damageable, system))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using Content.Shared.Damage;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
|
||||
namespace Content.Server.Destructible.Thresholds.Triggers
|
||||
{
|
||||
/// <summary>
|
||||
/// A trigger that will activate when the amount of damage received
|
||||
/// of the specified class is above the specified threshold.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataDefinition]
|
||||
public sealed partial class DamageGroupTrigger : IThresholdTrigger
|
||||
{
|
||||
[DataField("damageGroup", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<DamageGroupPrototype>))]
|
||||
public string DamageGroup { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of damage at which this threshold will trigger.
|
||||
/// </summary>
|
||||
[DataField("damage", required: true)]
|
||||
public int Damage { get; set; } = default!;
|
||||
|
||||
public bool Reached(DamageableComponent damageable, DestructibleSystem system)
|
||||
{
|
||||
return damageable.DamagePerGroup[DamageGroup] >= Damage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using Content.Shared.Damage;
|
||||
|
||||
namespace Content.Server.Destructible.Thresholds.Triggers
|
||||
{
|
||||
/// <summary>
|
||||
/// A trigger that will activate when the amount of damage received
|
||||
/// is above the specified threshold.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataDefinition]
|
||||
public sealed partial class DamageTrigger : IThresholdTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// The amount of damage at which this threshold will trigger.
|
||||
/// </summary>
|
||||
[DataField("damage", required: true)]
|
||||
public int Damage { get; set; } = default!;
|
||||
|
||||
public bool Reached(DamageableComponent damageable, DestructibleSystem system)
|
||||
{
|
||||
return damageable.TotalDamage >= Damage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using Content.Shared.Damage;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
|
||||
namespace Content.Server.Destructible.Thresholds.Triggers
|
||||
{
|
||||
/// <summary>
|
||||
/// A trigger that will activate when the amount of damage received
|
||||
/// of the specified type is above the specified threshold.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataDefinition]
|
||||
public sealed partial class DamageTypeTrigger : IThresholdTrigger
|
||||
{
|
||||
[DataField("damageType", required:true, customTypeSerializer: typeof(PrototypeIdSerializer<DamageTypePrototype>))]
|
||||
public string DamageType { get; set; } = default!;
|
||||
|
||||
[DataField("damage", required: true)]
|
||||
public int Damage { get; set; } = default!;
|
||||
|
||||
public bool Reached(DamageableComponent damageable, DestructibleSystem system)
|
||||
{
|
||||
return damageable.Damage.DamageDict.TryGetValue(DamageType, out var damageReceived) &&
|
||||
damageReceived >= Damage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using Content.Shared.Damage;
|
||||
|
||||
namespace Content.Server.Destructible.Thresholds.Triggers
|
||||
{
|
||||
public interface IThresholdTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if this trigger has been reached.
|
||||
/// </summary>
|
||||
/// <param name="damageable">The damageable component to check with.</param>
|
||||
/// <param name="system">
|
||||
/// An instance of <see cref="DestructibleSystem"/> to pull
|
||||
/// dependencies from, if any.
|
||||
/// </param>
|
||||
/// <returns>true if this trigger has been reached, false otherwise.</returns>
|
||||
bool Reached(DamageableComponent damageable, DestructibleSystem system);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using Content.Shared.Damage;
|
||||
|
||||
namespace Content.Server.Destructible.Thresholds.Triggers
|
||||
{
|
||||
/// <summary>
|
||||
/// A trigger that will activate when any of its triggers have activated.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataDefinition]
|
||||
public sealed partial class OrTrigger : IThresholdTrigger
|
||||
{
|
||||
[DataField("triggers")]
|
||||
public List<IThresholdTrigger> Triggers { get; private set; } = new();
|
||||
|
||||
public bool Reached(DamageableComponent damageable, DestructibleSystem system)
|
||||
{
|
||||
foreach (var trigger in Triggers)
|
||||
{
|
||||
if (trigger.Reached(damageable, system))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Content.Shared/Destructible/ThresholdActs.cs
Normal file
12
Content.Shared/Destructible/ThresholdActs.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Destructible;
|
||||
|
||||
[Flags]
|
||||
[Serializable, NetSerializable]
|
||||
public enum ThresholdActs : byte
|
||||
{
|
||||
None = 0,
|
||||
Breakage = 1 << 0,
|
||||
Destruction = 1 << 1,
|
||||
}
|
||||
28
Content.Shared/Destructible/Triggers/AndTrigger.cs
Normal file
28
Content.Shared/Destructible/Triggers/AndTrigger.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Content.Shared.Damage;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Destructible.Thresholds.Triggers;
|
||||
|
||||
/// <summary>
|
||||
/// A trigger that will activate when all of its triggers have activated.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
[DataDefinition]
|
||||
public sealed partial class AndTrigger : IThresholdTrigger
|
||||
{
|
||||
[DataField]
|
||||
public List<IThresholdTrigger> Triggers = new();
|
||||
|
||||
public bool Reached(Entity<DamageableComponent> damageable, SharedDestructibleSystem system)
|
||||
{
|
||||
foreach (var trigger in Triggers)
|
||||
{
|
||||
if (!trigger.Reached(damageable, system))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
33
Content.Shared/Destructible/Triggers/DamageGroupTrigger.cs
Normal file
33
Content.Shared/Destructible/Triggers/DamageGroupTrigger.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Destructible.Thresholds.Triggers;
|
||||
|
||||
/// <summary>
|
||||
/// A trigger that will activate when the amount of damage received
|
||||
/// of the specified class is above the specified threshold.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
[DataDefinition]
|
||||
public sealed partial class DamageGroupTrigger : IThresholdTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// The damage group to check for.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public ProtoId<DamageGroupPrototype> DamageGroup = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of damage at which this threshold will trigger.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public FixedPoint2 Damage = default!;
|
||||
|
||||
public bool Reached(Entity<DamageableComponent> damageable, SharedDestructibleSystem system)
|
||||
{
|
||||
return damageable.Comp.DamagePerGroup[DamageGroup] >= Damage;
|
||||
}
|
||||
}
|
||||
25
Content.Shared/Destructible/Triggers/DamageTrigger.cs
Normal file
25
Content.Shared/Destructible/Triggers/DamageTrigger.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Destructible.Thresholds.Triggers;
|
||||
|
||||
/// <summary>
|
||||
/// A trigger that will activate when the total amount of damage received
|
||||
/// is above the specified threshold.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
[DataDefinition]
|
||||
public sealed partial class DamageTrigger : IThresholdTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// The amount of damage at which this threshold will trigger.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public FixedPoint2 Damage = default!;
|
||||
|
||||
public bool Reached(Entity<DamageableComponent> damageable, SharedDestructibleSystem system)
|
||||
{
|
||||
return damageable.Comp.TotalDamage >= Damage;
|
||||
}
|
||||
}
|
||||
34
Content.Shared/Destructible/Triggers/DamageTypeTrigger.cs
Normal file
34
Content.Shared/Destructible/Triggers/DamageTypeTrigger.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Destructible.Thresholds.Triggers;
|
||||
|
||||
/// <summary>
|
||||
/// A trigger that will activate when the amount of damage received
|
||||
/// of the specified type is above the specified threshold.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
[DataDefinition]
|
||||
public sealed partial class DamageTypeTrigger : IThresholdTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// The damage type to check for.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public ProtoId<DamageTypePrototype> DamageType = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The amount of damage at which this threshold will trigger.
|
||||
/// </summary>
|
||||
[DataField(required: true)]
|
||||
public FixedPoint2 Damage = default!;
|
||||
|
||||
public bool Reached(Entity<DamageableComponent> damageable, SharedDestructibleSystem system)
|
||||
{
|
||||
return damageable.Comp.Damage.DamageDict.TryGetValue(DamageType, out var damageReceived) &&
|
||||
damageReceived >= Damage;
|
||||
}
|
||||
}
|
||||
27
Content.Shared/Destructible/Triggers/IThresholdTrigger.cs
Normal file
27
Content.Shared/Destructible/Triggers/IThresholdTrigger.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using Content.Shared.Damage;
|
||||
|
||||
namespace Content.Shared.Destructible.Thresholds.Triggers;
|
||||
|
||||
/// <summary>
|
||||
/// A condition for triggering a <see cref="DamageThreshold">.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// I decided against converting these into EntityEffectConditions for performance reasons
|
||||
/// (although I did not do any benchmarks, so it might be fine).
|
||||
/// Entity effects will raise a separate event for each entity and each condition, which can become a huge number
|
||||
/// for cases like nuke explosions or shuttle collisions where there are lots of DamageChangedEvents at once.
|
||||
/// IThresholdTriggers on the other hand are directly checked in a foreach loop without raising events.
|
||||
/// And there are only few of these conditions, so there is only a minor amount of code duplication.
|
||||
/// </remarks>
|
||||
public interface IThresholdTrigger
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if this trigger has been reached.
|
||||
/// </summary>
|
||||
/// <param name="damageable">The damageable component to check with.</param>
|
||||
/// <param name="system">
|
||||
/// An instance of <see cref="SharedDestructibleSystem"/> to pull dependencies from, if any.
|
||||
/// </param>
|
||||
/// <returns>true if this trigger has been reached, false otherwise.</returns>
|
||||
bool Reached(Entity<DamageableComponent> damageable, SharedDestructibleSystem system);
|
||||
}
|
||||
28
Content.Shared/Destructible/Triggers/OrTrigger.cs
Normal file
28
Content.Shared/Destructible/Triggers/OrTrigger.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Content.Shared.Damage;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Shared.Destructible.Thresholds.Triggers;
|
||||
|
||||
/// <summary>
|
||||
/// A trigger that will activate when any of its triggers have activated.
|
||||
/// </summary>
|
||||
[Serializable, NetSerializable]
|
||||
[DataDefinition]
|
||||
public sealed partial class OrTrigger : IThresholdTrigger
|
||||
{
|
||||
[DataField]
|
||||
public List<IThresholdTrigger> Triggers = new();
|
||||
|
||||
public bool Reached(Entity<DamageableComponent> damageable, SharedDestructibleSystem system)
|
||||
{
|
||||
foreach (var trigger in Triggers)
|
||||
{
|
||||
if (trigger.Reached(damageable, system))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.EntityConditions.Conditions;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this entity can take damage and if its damage of a given damage group is within a specified minimum and maximum.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="EntityConditionSystem{T, TCondition}"/>
|
||||
public sealed partial class DamageGroupEntityConditionSystem : EntityConditionSystem<DamageableComponent, DamageGroupCondition>
|
||||
{
|
||||
protected override void Condition(Entity<DamageableComponent> entity, ref EntityConditionEvent<DamageGroupCondition> args)
|
||||
{
|
||||
var value = entity.Comp.DamagePerGroup[args.Condition.DamageGroup];
|
||||
args.Result = value >= args.Condition.Min && value <= args.Condition.Max;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="EntityCondition"/>
|
||||
public sealed partial class DamageGroupCondition : EntityConditionBase<DamageGroupCondition>
|
||||
{
|
||||
[DataField]
|
||||
public FixedPoint2 Max = FixedPoint2.MaxValue;
|
||||
|
||||
[DataField]
|
||||
public FixedPoint2 Min = FixedPoint2.Zero;
|
||||
|
||||
[DataField(required: true)]
|
||||
public ProtoId<DamageGroupPrototype> DamageGroup;
|
||||
|
||||
public override string EntityConditionGuidebookText(IPrototypeManager prototype) =>
|
||||
Loc.GetString("reagent-effect-condition-guidebook-group-damage",
|
||||
("max", Max == FixedPoint2.MaxValue ? int.MaxValue : Max.Float()),
|
||||
("min", Min.Float()),
|
||||
("type", prototype.Index(DamageGroup).LocalizedName));
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Shared.EntityConditions.Conditions;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if this entity can take damage and if its damage of a given damage type is within a specified minimum and maximum.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="EntityConditionSystem{T, TCondition}"/>
|
||||
public sealed partial class DamageTypeEntityConditionSystem : EntityConditionSystem<DamageableComponent, DamageTypeCondition>
|
||||
{
|
||||
protected override void Condition(Entity<DamageableComponent> entity, ref EntityConditionEvent<DamageTypeCondition> args)
|
||||
{
|
||||
var value = entity.Comp.Damage.DamageDict.GetValueOrDefault(args.Condition.DamageType);
|
||||
args.Result = value >= args.Condition.Min && value <= args.Condition.Max;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="EntityCondition"/>
|
||||
public sealed partial class DamageTypeCondition : EntityConditionBase<DamageTypeCondition>
|
||||
{
|
||||
[DataField]
|
||||
public FixedPoint2 Max = FixedPoint2.MaxValue;
|
||||
|
||||
[DataField]
|
||||
public FixedPoint2 Min = FixedPoint2.Zero;
|
||||
|
||||
[DataField(required: true)]
|
||||
public ProtoId<DamageTypePrototype> DamageType;
|
||||
|
||||
public override string EntityConditionGuidebookText(IPrototypeManager prototype) =>
|
||||
Loc.GetString("reagent-effect-condition-guidebook-type-damage",
|
||||
("max", Max == FixedPoint2.MaxValue ? int.MaxValue : Max.Float()),
|
||||
("min", Min.Float()),
|
||||
("type", prototype.Index(DamageType).LocalizedName));
|
||||
}
|
||||
@@ -7,6 +7,24 @@ reagent-effect-condition-guidebook-total-damage =
|
||||
}
|
||||
}
|
||||
|
||||
reagent-effect-condition-guidebook-type-damage =
|
||||
{ $max ->
|
||||
[2147483648] it has at least {NATURALFIXED($min, 2)} of {$type} damage
|
||||
*[other] { $min ->
|
||||
[0] it has at most {NATURALFIXED($max, 2)} of {$type} damage
|
||||
*[other] it has between {NATURALFIXED($min, 2)} and {NATURALFIXED($max, 2)} of {$type} damage
|
||||
}
|
||||
}
|
||||
|
||||
reagent-effect-condition-guidebook-group-damage =
|
||||
{ $max ->
|
||||
[2147483648] it has at least {NATURALFIXED($min, 2)} of {$type} damage.
|
||||
*[other] { $min ->
|
||||
[0] it has at most {NATURALFIXED($max, 2)} of {$type} damage.
|
||||
*[other] it has between {NATURALFIXED($min, 2)} and {NATURALFIXED($max, 2)} of {$type} damage
|
||||
}
|
||||
}
|
||||
|
||||
reagent-effect-condition-guidebook-total-hunger =
|
||||
{ $max ->
|
||||
[2147483648] the target has at least {NATURALFIXED($min, 2)} total hunger
|
||||
|
||||
Reference in New Issue
Block a user