Separate destructible component threshold into behaviors (#2818)
* WIP changes, add behaviors * Fix behavior typing, namespace and test * NO SPACES
This commit is contained in:
@@ -1,4 +0,0 @@
|
||||
namespace Content.Server.GameObjects.Components.Destructible
|
||||
{
|
||||
public sealed class ActsFlags { }
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Destructible.Thresholds;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Random;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
@@ -19,9 +18,7 @@ namespace Content.Server.GameObjects.Components.Destructible
|
||||
[RegisterComponent]
|
||||
public class DestructibleComponent : Component
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
private ActSystem _actSystem = default!;
|
||||
private DestructibleSystem _destructibleSystem = default!;
|
||||
|
||||
public override string Name => "Destructible";
|
||||
|
||||
@@ -47,7 +44,7 @@ namespace Content.Server.GameObjects.Components.Destructible
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_actSystem = EntitySystem.Get<ActSystem>();
|
||||
_destructibleSystem = EntitySystem.Get<DestructibleSystem>();
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
||||
@@ -83,7 +80,7 @@ namespace Content.Server.GameObjects.Components.Destructible
|
||||
var thresholdMessage = new DestructibleThresholdReachedMessage(this, threshold, msg.Damageable.TotalDamage, damage);
|
||||
SendMessage(thresholdMessage);
|
||||
|
||||
threshold.Trigger(Owner, _random, _actSystem);
|
||||
threshold.Trigger(Owner, _destructibleSystem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Content.Server.GameObjects.Components.Destructible.Thresholds;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Destructible
|
||||
{
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Stack;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.Utility;
|
||||
using Robust.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.GameObjects.Systems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Random;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Destructible
|
||||
{
|
||||
public class Threshold : IExposeData
|
||||
{
|
||||
/// <summary>
|
||||
/// Entities spawned on reaching this threshold, from a min to a max.
|
||||
/// </summary>
|
||||
[ViewVariables] public Dictionary<string, MinMax>? Spawn;
|
||||
|
||||
/// <summary>
|
||||
/// Sound played upon destruction.
|
||||
/// </summary>
|
||||
[ViewVariables] public string Sound = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Used instead of <see cref="Sound"/> if specified.
|
||||
/// </summary>
|
||||
[ViewVariables] public string SoundCollection = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// What acts this threshold should trigger upon activation.
|
||||
/// See <see cref="ActSystem"/>.
|
||||
/// </summary>
|
||||
[ViewVariables] public int Acts;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not this threshold has already been triggered.
|
||||
/// </summary>
|
||||
[ViewVariables] public bool Triggered;
|
||||
|
||||
/// <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>
|
||||
[ViewVariables] public bool TriggersOnce;
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref Spawn, "spawn", null);
|
||||
serializer.DataField(ref Sound, "sound", string.Empty);
|
||||
serializer.DataField(ref SoundCollection, "soundCollection", string.Empty);
|
||||
serializer.DataField(ref Acts, "acts", 0, WithFormat.Flags<ActsFlags>());
|
||||
serializer.DataField(ref Triggered, "triggered", false);
|
||||
serializer.DataField(ref TriggersOnce, "triggersOnce", false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers this threshold.
|
||||
/// </summary>
|
||||
/// <param name="owner">The entity that owns this threshold.</param>
|
||||
/// <param name="random">
|
||||
/// An instance of <see cref="IRobustRandom"/> to get randomness from, if relevant.
|
||||
/// </param>
|
||||
/// <param name="actSystem">
|
||||
/// An instance of <see cref="ActSystem"/> to call acts on, if relevant.
|
||||
/// </param>
|
||||
public void Trigger(IEntity owner, IRobustRandom random, ActSystem actSystem)
|
||||
{
|
||||
Triggered = true;
|
||||
|
||||
PlaySound(owner);
|
||||
DoSpawn(owner, random);
|
||||
DoActs(owner, actSystem);
|
||||
}
|
||||
|
||||
private void PlaySound(IEntity owner)
|
||||
{
|
||||
var pos = owner.Transform.Coordinates;
|
||||
var actualSound = string.Empty;
|
||||
|
||||
if (SoundCollection != string.Empty)
|
||||
{
|
||||
actualSound = AudioHelpers.GetRandomFileFromSoundCollection(SoundCollection);
|
||||
}
|
||||
else if (Sound != string.Empty)
|
||||
{
|
||||
actualSound = Sound;
|
||||
}
|
||||
|
||||
if (actualSound != string.Empty)
|
||||
{
|
||||
EntitySystem.Get<AudioSystem>().PlayAtCoords(actualSound, pos, AudioHelpers.WithVariation(0.125f));
|
||||
}
|
||||
}
|
||||
|
||||
private void DoSpawn(IEntity owner, IRobustRandom random)
|
||||
{
|
||||
if (Spawn == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var (key, value) in Spawn)
|
||||
{
|
||||
var count = value.Min >= value.Max
|
||||
? value.Min
|
||||
: random.Next(value.Min, value.Max + 1);
|
||||
|
||||
if (count == 0) continue;
|
||||
|
||||
if (EntityPrototypeHelpers.HasComponent<StackComponent>(key))
|
||||
{
|
||||
var spawned = owner.EntityManager.SpawnEntity(key, owner.Transform.Coordinates);
|
||||
var stack = spawned.GetComponent<StackComponent>();
|
||||
stack.Count = count;
|
||||
spawned.RandomOffset(0.5f);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var spawned = owner.EntityManager.SpawnEntity(key, owner.Transform.Coordinates);
|
||||
spawned.RandomOffset(0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DoActs(IEntity owner, ActSystem acts)
|
||||
{
|
||||
if ((Acts & (int) ThresholdActs.Breakage) != 0)
|
||||
{
|
||||
acts.HandleBreakage(owner);
|
||||
}
|
||||
|
||||
if ((Acts & (int) ThresholdActs.Destruction) != 0)
|
||||
{
|
||||
acts.HandleDestruction(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
namespace Content.Server.GameObjects.Components.Destructible.Thresholds
|
||||
{
|
||||
public sealed class ActsFlags { }
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior
|
||||
{
|
||||
public class DoActsBehavior : IThresholdBehavior
|
||||
{
|
||||
private int _acts;
|
||||
|
||||
/// <summary>
|
||||
/// What acts should be triggered upon activation.
|
||||
/// See <see cref="ActSystem"/>.
|
||||
/// </summary>
|
||||
public ThresholdActs Acts
|
||||
{
|
||||
get => (ThresholdActs) _acts;
|
||||
set => _acts = (int) value;
|
||||
}
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref _acts, "acts", 0, WithFormat.Flags<ActsFlags>());
|
||||
}
|
||||
|
||||
public bool HasAct(ThresholdActs act)
|
||||
{
|
||||
return (_acts & (int) act) != 0;
|
||||
}
|
||||
|
||||
public void Trigger(IEntity owner, DestructibleSystem system)
|
||||
{
|
||||
if (HasAct(ThresholdActs.Breakage))
|
||||
{
|
||||
system.ActSystem.HandleBreakage(owner);
|
||||
}
|
||||
|
||||
if (HasAct(ThresholdActs.Destruction))
|
||||
{
|
||||
system.ActSystem.HandleDestruction(owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior
|
||||
{
|
||||
public interface IThresholdBehavior : IExposeData
|
||||
{
|
||||
void Trigger(IEntity owner, DestructibleSystem system);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Audio;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior
|
||||
{
|
||||
public class PlaySoundBehavior : IThresholdBehavior
|
||||
{
|
||||
/// <summary>
|
||||
/// Sound played upon destruction.
|
||||
/// </summary>
|
||||
public string Sound { get; set; }
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(this, x => x.Sound, "sound", string.Empty);
|
||||
}
|
||||
|
||||
public void Trigger(IEntity owner, DestructibleSystem system)
|
||||
{
|
||||
if (string.IsNullOrEmpty(Sound))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var pos = owner.Transform.Coordinates;
|
||||
|
||||
system.AudioSystem.PlayAtCoords(Sound, pos, AudioHelpers.WithVariation(0.125f));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Audio;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior
|
||||
{
|
||||
public class PlaySoundCollectionBehavior : IThresholdBehavior
|
||||
{
|
||||
/// <summary>
|
||||
/// Sound collection from which to pick a random sound to play.
|
||||
/// </summary>
|
||||
private string SoundCollection { get; set; }
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(this, x => x.SoundCollection, "soundCollection", string.Empty);
|
||||
}
|
||||
|
||||
public void Trigger(IEntity owner, DestructibleSystem system)
|
||||
{
|
||||
if (string.IsNullOrEmpty(SoundCollection))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sound = AudioHelpers.GetRandomFileFromSoundCollection(SoundCollection);
|
||||
var pos = owner.Transform.Coordinates;
|
||||
|
||||
system.AudioSystem.PlayAtCoords(sound, pos, AudioHelpers.WithVariation(0.125f));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Stack;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Utility;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior
|
||||
{
|
||||
public class SpawnEntitiesBehavior : IThresholdBehavior
|
||||
{
|
||||
/// <summary>
|
||||
/// Entities spawned on reaching this threshold, from a min to a max.
|
||||
/// </summary>
|
||||
public Dictionary<string, MinMax> Spawn { get; set; } = new();
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(this, x => x.Spawn, "spawn", new Dictionary<string, MinMax>());
|
||||
}
|
||||
|
||||
public void Trigger(IEntity owner, DestructibleSystem system)
|
||||
{
|
||||
foreach (var (entityId, minMax) in Spawn)
|
||||
{
|
||||
var count = minMax.Min >= minMax.Max
|
||||
? minMax.Min
|
||||
: system.Random.Next(minMax.Min, minMax.Max + 1);
|
||||
|
||||
if (count == 0) continue;
|
||||
|
||||
if (EntityPrototypeHelpers.HasComponent<StackComponent>(entityId))
|
||||
{
|
||||
var spawned = owner.EntityManager.SpawnEntity(entityId, owner.Transform.Coordinates);
|
||||
var stack = spawned.GetComponent<StackComponent>();
|
||||
stack.Count = count;
|
||||
spawned.RandomOffset(0.5f);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var spawned = owner.EntityManager.SpawnEntity(entityId, owner.Transform.Coordinates);
|
||||
spawned.RandomOffset(0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Destructible
|
||||
namespace Content.Server.GameObjects.Components.Destructible.Thresholds
|
||||
{
|
||||
public struct MinMax : IExposeData
|
||||
{
|
||||
@@ -0,0 +1,57 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Destructible.Thresholds.Behavior;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Robust.Shared.Interfaces.GameObjects;
|
||||
using Robust.Shared.Interfaces.Serialization;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Destructible.Thresholds
|
||||
{
|
||||
public class Threshold : IExposeData
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether or not this threshold has already been triggered.
|
||||
/// </summary>
|
||||
[ViewVariables] public bool Triggered;
|
||||
|
||||
/// <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>
|
||||
[ViewVariables] public bool TriggersOnce;
|
||||
|
||||
/// <summary>
|
||||
/// Behaviors to activate once this threshold is triggered.
|
||||
/// </summary>
|
||||
[ViewVariables] public List<IThresholdBehavior> Behaviors = new();
|
||||
|
||||
public void ExposeData(ObjectSerializer serializer)
|
||||
{
|
||||
serializer.DataField(ref Triggered, "triggered", false);
|
||||
serializer.DataField(ref TriggersOnce, "triggersOnce", false);
|
||||
serializer.DataField(ref Behaviors, "behaviors", new List<IThresholdBehavior>());
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public void Trigger(IEntity owner, DestructibleSystem system)
|
||||
{
|
||||
Triggered = true;
|
||||
|
||||
foreach (var behavior in Behaviors)
|
||||
{
|
||||
behavior.Trigger(owner, system);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
using System;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Destructible
|
||||
namespace Content.Server.GameObjects.Components.Destructible.Thresholds
|
||||
{
|
||||
[Flags, FlagsFor(typeof(ActsFlags))]
|
||||
[Serializable]
|
||||
public enum ThresholdActs
|
||||
{
|
||||
Invalid = 0,
|
||||
None = 0,
|
||||
Breakage,
|
||||
Destruction
|
||||
}
|
||||
Reference in New Issue
Block a user