Damage rework (#2525)

* Make damage work through messages and events, make destructible not inherit ruinable or reference damageable

* Copy sound logic to destructible component for now

* Fix typo

* Fix prototype error

* Remove breakable component damageable reference

* Remove breakable construction reference

* Remove ruinable component

* Move thresholds to individual components and away from damageable

* Add threshold property to damageable component code

* Add thresholds to destructible component, add states to damageable, remove damage container, fix up mob states

* Being alive isn't normal

* Fix not reading the id

* Merge fixes

* YAML fixes

* Grammar moment

* Remove unnecessary dependency

* Update thresholds doc

* Change naming of thresholds to states in MobStateComponent

* Being alive is once again normal

* Make DamageState a byte

* Bring out classes structs and enums from DestructibleComponent

* Add test for destructible thresholds

* Merge fixes

* More merge fixes and fix rejuvenate test

* Remove IMobState.IsConscious

* More merge fixes someone please god review this shit already

* Fix rejuvenate test

* Update outdated destructible in YAML

* Fix repeatedly entering the current state

* Fix repeatedly entering the current state, add Threshold.TriggersOnce and expand test

* Update saltern
This commit is contained in:
DrSmugleaf
2020-12-07 14:52:55 +01:00
committed by GitHub
parent 9a187629ba
commit 02bca4c0d8
133 changed files with 3195 additions and 5897 deletions

View File

@@ -1,72 +1,10 @@
using System.Collections.Generic;
using Content.Shared.Audio;
using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.EntitySystems;
using Robust.Server.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.Log;
using Robust.Shared.Random;
using Robust.Shared.GameObjects;
namespace Content.Server.GameObjects.Components.Damage
{
// TODO: Repair needs to set CurrentDamageState to DamageState.Alive, but it doesn't exist... should be easy enough if it's just an interface you can slap on BreakableComponent
/// <summary>
/// When attached to an <see cref="IEntity"/>, allows it to take damage and sets it to a "broken state" after taking
/// enough damage.
/// </summary>
[RegisterComponent]
[ComponentReference(typeof(IDamageableComponent))]
public class BreakableComponent : RuinableComponent, IExAct
public class BreakableComponent : Component
{
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
public override string Name => "Breakable";
private ActSystem _actSystem;
public override List<DamageState> SupportedDamageStates =>
new() {DamageState.Alive, DamageState.Dead};
void IExAct.OnExplosion(ExplosionEventArgs eventArgs)
{
switch (eventArgs.Severity)
{
case ExplosionSeverity.Destruction:
case ExplosionSeverity.Heavy:
PerformDestruction();
break;
case ExplosionSeverity.Light:
if (_random.Prob(0.5f))
{
PerformDestruction();
}
break;
}
}
public override void Initialize()
{
base.Initialize();
_actSystem = _entitySystemManager.GetEntitySystem<ActSystem>();
}
// Might want to move this down and have a more standardized method of revival
public void FixAllDamage()
{
Heal();
CurrentState = DamageState.Alive;
}
protected override void DestructionBehavior()
{
_actSystem.HandleBreakage(Owner);
}
}
}

View File

@@ -1,7 +1,5 @@
#nullable enable
using System;
using Content.Server.GameObjects.Components.Construction;
using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.GameObjects.Systems;
@@ -10,11 +8,8 @@ using Robust.Shared.Serialization;
namespace Content.Server.GameObjects.Components.Damage
{
[RegisterComponent]
[ComponentReference(typeof(IDamageableComponent))]
public class BreakableConstructionComponent : RuinableComponent
public class BreakableConstructionComponent : Component, IDestroyAct
{
private ActSystem _actSystem = default!;
public override string Name => "BreakableConstruction";
public override void ExposeData(ObjectSerializer serializer)
@@ -24,20 +19,16 @@ namespace Content.Server.GameObjects.Components.Damage
serializer.DataField(this, x => x.Node, "node", string.Empty);
}
public override void Initialize()
{
base.Initialize();
_actSystem = EntitySystem.Get<ActSystem>();
}
public string Node { get; private set; } = string.Empty;
protected override async void DestructionBehavior()
async void IDestroyAct.OnDestroy(DestructionEventArgs eventArgs)
{
if (Owner.Deleted || !Owner.TryGetComponent(out ConstructionComponent? construction) || string.IsNullOrEmpty(Node)) return;
_actSystem.HandleBreakage(Owner);
if (Owner.Deleted ||
!Owner.TryGetComponent(out ConstructionComponent? construction) ||
string.IsNullOrEmpty(Node))
{
return;
}
await construction.ChangeNode(Node);
}

View File

@@ -1,101 +0,0 @@
using System.Collections.Generic;
using Content.Server.GameObjects.Components.Stack;
using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Utility;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.IoC;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
namespace Content.Server.GameObjects.Components.Damage
{
/// <summary>
/// When attached to an <see cref="IEntity"/>, allows it to take damage and deletes it after taking enough damage.
/// </summary>
[RegisterComponent]
[ComponentReference(typeof(IDamageableComponent))]
public class DestructibleComponent : RuinableComponent, IDestroyAct
{
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
protected ActSystem ActSystem;
/// <inheritdoc />
public override string Name => "Destructible";
/// <summary>
/// Entities spawned on destruction plus the min and max amount spawned.
/// </summary>
public Dictionary<string, MinMax> SpawnOnDestroy { get; private set; }
void IDestroyAct.OnDestroy(DestructionEventArgs eventArgs)
{
if (SpawnOnDestroy == null || !eventArgs.IsSpawnWreck) return;
foreach (var (key, value) in SpawnOnDestroy)
{
int count;
if (value.Min >= value.Max)
{
count = value.Min;
}
else
{
count = _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);
}
}
}
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(this, d => d.SpawnOnDestroy, "spawnOnDestroy", null);
}
public override void Initialize()
{
base.Initialize();
ActSystem = _entitySystemManager.GetEntitySystem<ActSystem>();
}
protected override void DestructionBehavior()
{
if (!Owner.Deleted)
{
var pos = Owner.Transform.Coordinates;
ActSystem.HandleDestruction(Owner,
true); //This will call IDestroyAct.OnDestroy on this component (and all other components on this entity)
}
}
public struct MinMax
{
public int Min;
public int Max;
}
}
}

View File

@@ -1,104 +0,0 @@
using System.Collections.Generic;
using Content.Shared.Audio;
using Content.Shared.GameObjects.Components.Damage;
using Robust.Server.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;
using Logger = Robust.Shared.Log.Logger;
namespace Content.Server.GameObjects.Components.Damage
{
/// <summary>
/// When attached to an <see cref="IEntity"/>, allows it to take damage and
/// "ruins" or "destroys" it after enough damage is taken.
/// </summary>
[ComponentReference(typeof(IDamageableComponent))]
public abstract class RuinableComponent : DamageableComponent
{
/// <summary>
/// Sound played upon destruction.
/// </summary>
[ViewVariables]
protected string DestroySound { get; private set; }
/// <summary>
/// Used instead of <see cref="DestroySound"/> if specified.
/// </summary>
[ViewVariables]
protected string DestroySoundCollection { get; private set; }
public override List<DamageState> SupportedDamageStates =>
new() {DamageState.Alive, DamageState.Dead};
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataReadWriteFunction(
"deadThreshold",
100,
t =>
{
if (t == null)
{
return;
}
Thresholds[DamageState.Dead] = t.Value;
},
() => Thresholds.TryGetValue(DamageState.Dead, out var value) ? value : (int?) null);
serializer.DataField(this, ruinable => ruinable.DestroySound, "destroySound", string.Empty);
serializer.DataField(this, ruinable => ruinable.DestroySoundCollection, "destroySoundCollection", string.Empty);
}
protected override void EnterState(DamageState state)
{
base.EnterState(state);
if (state == DamageState.Dead)
{
PerformDestruction();
}
}
/// <summary>
/// Destroys the Owner <see cref="IEntity"/>, setting
/// <see cref="IDamageableComponent.CurrentState"/> to
/// <see cref="Shared.GameObjects.Components.Damage.DamageState.Dead"/>
/// </summary>
protected void PerformDestruction()
{
CurrentState = DamageState.Dead;
if (!Owner.Deleted)
{
var pos = Owner.Transform.Coordinates;
string sound = string.Empty;
if (DestroySoundCollection != string.Empty)
{
sound = AudioHelpers.GetRandomFileFromSoundCollection(DestroySoundCollection);
}
else if (DestroySound != string.Empty)
{
sound = DestroySound;
}
if (sound != string.Empty)
{
Logger.Debug("Playing destruction sound");
EntitySystem.Get<AudioSystem>().PlayAtCoords(sound, pos, AudioHelpers.WithVariation(0.125f));
}
}
DestructionBehavior();
}
protected abstract void DestructionBehavior();
}
}