Bodysystem and damagesystem rework (#1544)

* Things and stuff with grids, unfinished w/ code debug changes.

* Updated submodule and also lost some progress cause I fucked it up xd

* First unfinished draft of the BodySystem. Doesn't compile.

* More changes to make it compile, but still just a framework. Doesn't do anything at the moment.

* Many cleanup changes.

* Revert "Merge branch 'master' of https://github.com/GlassEclipse/space-station-14 into body_system"

This reverts commit ddd4aebbc76cf2a0b7b102f72b93d55a0816c88c, reversing
changes made to 12d0dd752706bdda8879393bd8191a1199a0c978.

* Commit human.yml

* Updated a lot of things to be more classy, more progress overall, etc. etc.

* Latest update with many changes

* Minor changes

* Fixed Travis build bug

* Adds first draft of Body Scanner console, apparently I also forgot to tie Mechanisms into body parts so now a heart just sits in the Torso like a good boy :)

* Commit rest of stuff

* Latest changes

* Latest changes again

* 14 naked cowboys

* Yay!

* Latest changes (probably doesnt compile)

* Surgery!!!!!!!!!~1116y

* Cleaned some stuff up

* More cleanup

* Refactoring of code. Basic surgery path now done.

* Removed readme, has been added to HackMD

* Fixes typo (and thus test errors)

* WIP changes, committing so I can pull latest master changes

* Still working on that god awful merge

* Latest changes

* Latest changes!!

* Beginning of refactor to BoundUserInterface

* Surgery!

* Latest changes - fixes pr change requests and random fixes

* oops

* Fixes bodypart recursion

* Beginning of work on revamping the damage system.

* More latest changes

* Latest changes

* Finished merge

* Commit before removing old healthcode

* Almost done with removing speciescomponent...

* It compiles!!!

* yahoo more work

* Fixes to make it work

* Merge conflict fixes

* Deleting species visualizer was a mistake

* IDE warnings are VERBOTEN

* makes the server not kill itself on startup, some cleanup (#1)

* Namespaces, comments and exception fixes

* Fix conveyor and conveyor switch serialization

SS14 in reactive when

* Move damage, acts and body to shared

Damage cleanup
Comment cleanup

* Rename SpeciesComponent to RotationComponent and cleanup

Damage cleanup
Comment cleanup

* Fix nullable warnings

* Address old reviews

Fix off welder suicide damage type, deathmatch and suspicion

* Fix new test fail with units being able to accept items when unpowered

* Remove RotationComponent, change references to IBodyManagerComponent

* Add a bloodstream to humans

* More cleanups

* Add body conduits, connections, connectors substances and valves

* Revert "Add body conduits, connections, connectors substances and valves"

This reverts commit 9ab0b50e6b15fe98852d7b0836c0cdbf4bd76d20.

* Implement the heart mechanism behavior with the circulatory network

* Added network property to mechanism behaviors

* Changed human organ sprites and added missing ones

* Fix tests

* Add individual body part sprite rendering

* Fix error where dropped mechanisms are not initialized

* Implement client/server body damage

* Make DamageContainer take care of raising events

* Reimplement medical scanner with the new body system

* Improve the medical scanner ui

* Merge conflict fixes

* Fix crash when colliding with something

* Fix microwave suicides and eyes sprite rendering

* Fix nullable reference error

* Fix up surgery client side

* Fix missing using from merge conflict

* Add breathing

*inhale

* Merge conflict fixes

* Fix accumulatedframetime being reset to 0 instead of decreased by the threshold

https://github.com/space-wizards/space-station-14/pull/1617

* Use and add to the new AtmosHelpers

* Fix feet

* Add proper coloring to dropped body parts

* Fix Urist's lungs being too strong

* Merge conflict fixes

* Merge conflict fixes

* Merge conflict fixes

Co-authored-by: GlassEclipse <tsymall5@gmail.com>
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
Co-authored-by: AJCM-git <60196617+AJCM-git@users.noreply.github.com>
This commit is contained in:
DrSmugleaf
2020-08-17 01:42:42 +02:00
committed by GitHub
parent c17dd97383
commit b051261485
276 changed files with 7853 additions and 4737 deletions

View File

@@ -1,39 +1,59 @@
using System.Collections.Generic;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces.GameObjects;
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.Random;
using Robust.Shared.Serialization;
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]
public class BreakableComponent : Component, IOnDamageBehavior, IExAct
[ComponentReference(typeof(IDamageableComponent))]
public class BreakableComponent : RuinableComponent, IExAct
{
#pragma warning disable 649
#pragma warning disable 649
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
#pragma warning restore 649
/// <inheritdoc />
public override string Name => "Breakable";
public DamageThreshold Threshold { get; private set; }
[Dependency] private readonly IRobustRandom _random;
#pragma warning restore 649
public DamageType damageType = DamageType.Total;
public int damageValue = 0;
public bool broken = false;
public override string Name => "Breakable";
private ActSystem _actSystem;
private DamageState _currentDamageState;
public override void ExposeData(ObjectSerializer serializer)
public override List<DamageState> SupportedDamageStates =>
new List<DamageState> {DamageState.Alive, DamageState.Dead};
public override DamageState CurrentDamageState => _currentDamageState;
void IExAct.OnExplosion(ExplosionEventArgs eventArgs)
{
base.ExposeData(serializer);
switch (eventArgs.Severity)
{
case ExplosionSeverity.Destruction:
PerformDestruction();
break;
case ExplosionSeverity.Heavy:
PerformDestruction();
break;
case ExplosionSeverity.Light:
if (_random.Prob(0.5f))
{
PerformDestruction();
}
serializer.DataField(ref damageValue, "thresholdvalue", 100);
serializer.DataField(ref damageType, "thresholdtype", DamageType.Total);
break;
}
}
public override void Initialize()
@@ -42,38 +62,21 @@ namespace Content.Server.GameObjects.Components.Damage
_actSystem = _entitySystemManager.GetEntitySystem<ActSystem>();
}
public List<DamageThreshold> GetAllDamageThresholds()
// Might want to move this down and have a more standardized method of revival
public void FixAllDamage()
{
Threshold = new DamageThreshold(damageType, damageValue, ThresholdType.Breakage);
return new List<DamageThreshold>() {Threshold};
Heal();
_currentDamageState = DamageState.Alive;
}
public void OnDamageThresholdPassed(object obj, DamageThresholdPassedEventArgs e)
protected override void DestructionBehavior()
{
if (e.Passed && e.DamageThreshold == Threshold && broken == false)
_actSystem.HandleBreakage(Owner);
if (!Owner.Deleted && DestroySound != string.Empty)
{
broken = true;
_actSystem.HandleBreakage(Owner);
var pos = Owner.Transform.GridPosition;
EntitySystem.Get<AudioSystem>().PlayAtCoords(DestroySound, pos);
}
}
public void OnExplosion(ExplosionEventArgs eventArgs)
{
var prob = IoCManager.Resolve<IRobustRandom>();
switch (eventArgs.Severity)
{
case ExplosionSeverity.Destruction:
_actSystem.HandleBreakage(Owner);
break;
case ExplosionSeverity.Heavy:
_actSystem.HandleBreakage(Owner);
break;
case ExplosionSeverity.Light:
if(prob.Prob(0.4f))
_actSystem.HandleBreakage(Owner);
break;
}
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using Content.Server.GameObjects.Components.Mobs;
using Content.Shared.Audio;
using Content.Shared.Damage;
using Content.Shared.GameObjects.Components.Damage;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Shared.GameObjects;
@@ -23,7 +24,7 @@ namespace Content.Server.GameObjects.Components.Damage
public override string Name => "DamageOnHighSpeedImpact";
public DamageType Damage { get; set; } = DamageType.Brute;
public DamageType Damage { get; set; } = DamageType.Blunt;
public float MinimumSpeed { get; set; } = 20f;
public int BaseDamage { get; set; } = 5;
public float Factor { get; set; } = 0.75f;
@@ -38,7 +39,7 @@ namespace Content.Server.GameObjects.Components.Damage
{
base.ExposeData(serializer);
serializer.DataField(this, x => Damage, "damage", DamageType.Brute);
serializer.DataField(this, x => Damage, "damage", DamageType.Blunt);
serializer.DataField(this, x => MinimumSpeed, "minimumSpeed", 20f);
serializer.DataField(this, x => BaseDamage, "baseDamage", 5);
serializer.DataField(this, x => Factor, "factor", 1f);
@@ -51,7 +52,7 @@ namespace Content.Server.GameObjects.Components.Damage
public void CollideWith(IEntity collidedWith)
{
if (!Owner.TryGetComponent(out ICollidableComponent collidable) || !Owner.TryGetComponent(out DamageableComponent damageable)) return;
if (!Owner.TryGetComponent(out ICollidableComponent collidable) || !Owner.TryGetComponent(out IDamageableComponent damageable)) return;
var speed = collidable.LinearVelocity.Length;
@@ -70,7 +71,7 @@ namespace Content.Server.GameObjects.Components.Damage
if (Owner.TryGetComponent(out StunnableComponent stun) && _robustRandom.Prob(StunChance))
stun.Stun(StunSeconds);
damageable.TakeDamage(Damage, damage, collidedWith, Owner);
damageable.ChangeDamage(Damage, damage, false, collidedWith);
}
}
}

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic;
using Content.Server.GameObjects.Components.Interactable;
using Content.Shared.GameObjects.Components.Damage;
using Content.Shared.Damage;
using Content.Shared.GameObjects.Components.Interactable;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Shared.GameObjects;
@@ -9,7 +9,7 @@ using Robust.Shared.Serialization;
namespace Content.Server.GameObjects.Components.Damage
{
[RegisterComponent]
class DamageOnToolInteractComponent : Component, IInteractUsing
public class DamageOnToolInteractComponent : Component, IInteractUsing
{
public override string Name => "DamageOnToolInteract";
@@ -29,7 +29,7 @@ namespace Content.Server.GameObjects.Components.Damage
public override void Initialize()
{
base.Initialize();
Owner.EnsureComponent<DamageableComponent>();
Owner.EnsureComponent<DestructibleComponent>();
}
public bool InteractUsing(InteractUsingEventArgs eventArgs)
@@ -40,12 +40,12 @@ namespace Content.Server.GameObjects.Components.Damage
{
if (tool.HasQuality(ToolQuality.Welding) && toolQuality == ToolQuality.Welding)
{
if (eventArgs.Using.TryGetComponent<WelderComponent>(out WelderComponent welder))
{
if (eventArgs.Using.TryGetComponent(out WelderComponent welder))
{
if (welder.WelderLit) return CallDamage(eventArgs, tool);
}
}
break; //If the tool quality is welding and its not lit or its not actually a welder that can be lit then its pointless to continue.
}
}
if (tool.HasQuality(toolQuality)) return CallDamage(eventArgs, tool);
}
@@ -55,14 +55,17 @@ namespace Content.Server.GameObjects.Components.Damage
protected bool CallDamage(InteractUsingEventArgs eventArgs, ToolComponent tool)
{
if (eventArgs.Target.TryGetComponent<DamageableComponent>(out var damageable))
if (eventArgs.Target.TryGetComponent<DestructibleComponent>(out var damageable))
{
if(tool.HasQuality(ToolQuality.Welding)) damageable.TakeDamage(DamageType.Heat, Damage, eventArgs.Using, eventArgs.User);
else
damageable.TakeDamage(DamageType.Brute, Damage, eventArgs.Using, eventArgs.User);
damageable.ChangeDamage(tool.HasQuality(ToolQuality.Welding)
? DamageType.Heat
: DamageType.Blunt,
Damage, false, eventArgs.User);
return true;
}
return false;
return false;
}
}
}

View File

@@ -1,98 +0,0 @@
using System;
using Content.Shared.GameObjects.Components.Damage;
using Robust.Shared.Interfaces.GameObjects;
namespace Content.Server.GameObjects.Components.Damage
{
/// <summary>
/// Triggers an event when values rise above or drop below this threshold
/// </summary>
public struct DamageThreshold
{
public DamageType DamageType { get; }
public int Value { get; }
public ThresholdType ThresholdType { get; }
public DamageThreshold(DamageType damageType, int value, ThresholdType thresholdType)
{
DamageType = damageType;
Value = value;
ThresholdType = thresholdType;
}
public override bool Equals(Object obj)
{
return obj is DamageThreshold threshold && this == threshold;
}
public override int GetHashCode()
{
return DamageType.GetHashCode() ^ Value.GetHashCode();
}
public static bool operator ==(DamageThreshold x, DamageThreshold y)
{
return x.DamageType == y.DamageType && x.Value == y.Value;
}
public static bool operator !=(DamageThreshold x, DamageThreshold y)
{
return !(x == y);
}
}
public enum ThresholdType
{
None,
Destruction,
Death,
Critical,
HUDUpdate,
Breakage,
}
public class DamageThresholdPassedEventArgs : EventArgs
{
public DamageThreshold DamageThreshold { get; }
public bool Passed { get; }
public int ExcessDamage { get; }
public DamageThresholdPassedEventArgs(DamageThreshold threshold, bool passed, int excess)
{
DamageThreshold = threshold;
Passed = passed;
ExcessDamage = excess;
}
}
public class DamageEventArgs : EventArgs
{
/// <summary>
/// Type of damage.
/// </summary>
public DamageType Type { get; }
/// <summary>
/// Change in damage.
/// </summary>
public int Damage { get; }
/// <summary>
/// The entity that damaged this one.
/// Could be null.
/// </summary>
public IEntity Source { get; }
/// <summary>
/// The mob entity that damaged this one.
/// Could be null.
/// </summary>
public IEntity SourceMob { get; }
public DamageEventArgs(DamageType type, int damage, IEntity source, IEntity sourceMob)
{
Type = type;
Damage = damage;
Source = source;
SourceMob = sourceMob;
}
}
}

View File

@@ -1,212 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.Interfaces.GameObjects;
using Content.Server.Interfaces.GameObjects.Components.Damage;
using Content.Shared.GameObjects.Components.Damage;
using Robust.Shared.GameObjects;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Damage
{
//TODO: add support for component add/remove
/// <summary>
/// A component that handles receiving damage and healing,
/// as well as informing other components of it.
/// </summary>
[RegisterComponent]
public class DamageableComponent : SharedDamageableComponent, IDamageableComponent
{
/// <inheritdoc />
public override string Name => "Damageable";
/// <summary>
/// The resistance set of this object.
/// Affects receiving damage of various types.
/// </summary>
[ViewVariables]
public ResistanceSet Resistances { get; private set; }
[ViewVariables]
public IReadOnlyDictionary<DamageType, int> CurrentDamage => _currentDamage;
private Dictionary<DamageType, int> _currentDamage = new Dictionary<DamageType, int>();
[ViewVariables]
public Dictionary<DamageType, List<DamageThreshold>> Thresholds = new Dictionary<DamageType, List<DamageThreshold>>();
public event EventHandler<DamageThresholdPassedEventArgs> DamageThresholdPassed;
public event EventHandler<DamageEventArgs> Damaged;
public override ComponentState GetComponentState()
{
return new DamageComponentState(_currentDamage);
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(this, x => Resistances, "resistances", ResistanceSet.DefaultResistanceSet);
}
public bool IsDead()
{
var currentDamage = _currentDamage[DamageType.Total];
foreach (var threshold in Thresholds[DamageType.Total])
{
if (threshold.Value <= currentDamage)
{
if (threshold.ThresholdType != ThresholdType.Death) continue;
return true;
}
}
return false;
}
/// <inheritdoc />
public override void Initialize()
{
base.Initialize();
InitializeDamageType(DamageType.Total);
foreach (var damagebehavior in Owner.GetAllComponents<IOnDamageBehavior>())
{
AddThresholdsFrom(damagebehavior);
Damaged += damagebehavior.OnDamaged;
}
RecalculateComponentThresholds();
}
/// <inheritdoc />
public void TakeDamage(DamageType damageType, int amount, IEntity source = null, IEntity sourceMob = null)
{
if (damageType == DamageType.Total)
{
foreach (DamageType e in Enum.GetValues(typeof(DamageType)))
{
if (e == damageType) continue;
TakeDamage(e, amount, source, sourceMob);
}
return;
}
InitializeDamageType(damageType);
int oldValue = _currentDamage[damageType];
int oldTotalValue = -1;
if (amount == 0)
{
return;
}
amount = Resistances.CalculateDamage(damageType, amount);
_currentDamage[damageType] = Math.Max(0, _currentDamage[damageType] + amount);
UpdateForDamageType(damageType, oldValue);
Damaged?.Invoke(this, new DamageEventArgs(damageType, amount, source, sourceMob));
if (Resistances.AppliesToTotal(damageType))
{
oldTotalValue = _currentDamage[DamageType.Total];
_currentDamage[DamageType.Total] = Math.Max(0, _currentDamage[DamageType.Total] + amount);
UpdateForDamageType(DamageType.Total, oldTotalValue);
}
}
/// <inheritdoc />
public void TakeHealing(DamageType damageType, int amount, IEntity source = null, IEntity sourceMob = null)
{
if (damageType == DamageType.Total)
{
throw new ArgumentException("Cannot heal for DamageType.Total");
}
TakeDamage(damageType, -amount, source, sourceMob);
}
public void HealAllDamage()
{
var values = Enum.GetValues(typeof(DamageType)).Cast<DamageType>();
foreach (var damageType in values)
{
if (CurrentDamage.ContainsKey(damageType) && damageType != DamageType.Total)
{
TakeHealing(damageType, CurrentDamage[damageType]);
}
}
}
void UpdateForDamageType(DamageType damageType, int oldValue)
{
int change = _currentDamage[damageType] - oldValue;
if (change == 0)
{
return;
}
int changeSign = Math.Sign(change);
foreach (var threshold in Thresholds[damageType])
{
var value = threshold.Value;
if (((value * changeSign) > (oldValue * changeSign)) && ((value * changeSign) <= (_currentDamage[damageType] * changeSign)))
{
var excessDamage = change - value;
var typeOfDamage = damageType;
if (change - value < 0)
{
excessDamage = 0;
}
var args = new DamageThresholdPassedEventArgs(threshold, (changeSign > 0), excessDamage);
DamageThresholdPassed?.Invoke(this, args);
}
}
}
void RecalculateComponentThresholds()
{
foreach (IOnDamageBehavior onDamageBehaviorComponent in Owner.GetAllComponents<IOnDamageBehavior>())
{
AddThresholdsFrom(onDamageBehaviorComponent);
}
}
void AddThresholdsFrom(IOnDamageBehavior onDamageBehavior)
{
if (onDamageBehavior == null)
{
throw new ArgumentNullException(nameof(onDamageBehavior));
}
List<DamageThreshold> thresholds = onDamageBehavior.GetAllDamageThresholds();
if (thresholds == null)
return;
foreach (DamageThreshold threshold in thresholds)
{
if (!Thresholds[threshold.DamageType].Contains(threshold))
{
Thresholds[threshold.DamageType].Add(threshold);
}
}
DamageThresholdPassed += onDamageBehavior.OnDamageThresholdPassed;
}
void InitializeDamageType(DamageType damageType)
{
if (!_currentDamage.ContainsKey(damageType))
{
_currentDamage.Add(damageType, 0);
Thresholds.Add(damageType, new List<DamageThreshold>());
}
}
}
}

View File

@@ -1,55 +1,49 @@
using System.Collections.Generic;
using Content.Server.GameObjects.EntitySystems;
using Content.Server.Interfaces.GameObjects;
using Content.Shared.GameObjects.Components.Damage;
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.Random;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Damage
{
/// <summary>
/// Deletes the entity once a certain damage threshold has been reached.
/// When attached to an <see cref="IEntity"/>, allows it to take damage and deletes it after taking enough damage.
/// </summary>
[RegisterComponent]
public class DestructibleComponent : Component, IOnDamageBehavior, IDestroyAct, IExAct
[ComponentReference(typeof(IDamageableComponent))]
public class DestructibleComponent : RuinableComponent, IDestroyAct
{
#pragma warning disable 649
#pragma warning disable 649
[Dependency] private readonly IEntitySystemManager _entitySystemManager;
#pragma warning restore 649
#pragma warning restore 649
protected ActSystem _actSystem;
protected string _spawnOnDestroy;
/// <inheritdoc />
public override string Name => "Destructible";
/// <summary>
/// Damage threshold calculated from the values
/// given in the prototype declaration.
/// Entity spawned upon destruction.
/// </summary>
[ViewVariables]
public DamageThreshold Threshold { get; private set; }
public string SpawnOnDestroy => _spawnOnDestroy;
public DamageType damageType = DamageType.Total;
public int damageValue = 0;
public string spawnOnDestroy = "";
public string destroySound = "";
public bool destroyed = false;
ActSystem _actSystem;
void IDestroyAct.OnDestroy(DestructionEventArgs eventArgs)
{
if (!string.IsNullOrWhiteSpace(_spawnOnDestroy) && eventArgs.IsSpawnWreck)
{
Owner.EntityManager.SpawnEntity(_spawnOnDestroy, Owner.Transform.GridPosition);
}
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref damageValue, "thresholdvalue", 100);
serializer.DataField(ref damageType, "thresholdtype", DamageType.Total);
serializer.DataField(ref spawnOnDestroy, "spawnondestroy", "");
serializer.DataField(ref destroySound, "destroysound", "");
serializer.DataField(ref _spawnOnDestroy, "spawnondestroy", string.Empty);
}
public override void Initialize()
@@ -58,57 +52,18 @@ namespace Content.Server.GameObjects.Components.Damage
_actSystem = _entitySystemManager.GetEntitySystem<ActSystem>();
}
/// <inheritdoc />
List<DamageThreshold> IOnDamageBehavior.GetAllDamageThresholds()
{
Threshold = new DamageThreshold(damageType, damageValue, ThresholdType.Destruction);
return new List<DamageThreshold>() { Threshold };
}
/// <inheritdoc />
void IOnDamageBehavior.OnDamageThresholdPassed(object obj, DamageThresholdPassedEventArgs e)
protected override void DestructionBehavior()
{
if (e.Passed && e.DamageThreshold == Threshold && destroyed == false)
if (!Owner.Deleted)
{
destroyed = true;
var pos = Owner.Transform.GridPosition;
_actSystem.HandleDestruction(Owner, true);
if(destroySound != string.Empty)
_actSystem.HandleDestruction(Owner,
true); //This will call IDestroyAct.OnDestroy on this component (and all other components on this entity)
if (DestroySound != string.Empty)
{
EntitySystem.Get<AudioSystem>().PlayAtCoords(destroySound, pos);
EntitySystem.Get<AudioSystem>().PlayAtCoords(DestroySound, pos);
}
}
}
void IExAct.OnExplosion(ExplosionEventArgs eventArgs)
{
var prob = IoCManager.Resolve<IRobustRandom>();
switch (eventArgs.Severity)
{
case ExplosionSeverity.Destruction:
_actSystem.HandleDestruction(Owner, false);
break;
case ExplosionSeverity.Heavy:
var spawnWreckOnHeavy = prob.Prob(0.5f);
_actSystem.HandleDestruction(Owner, spawnWreckOnHeavy);
break;
case ExplosionSeverity.Light:
if (prob.Prob(0.4f))
_actSystem.HandleDestruction(Owner, true);
break;
}
}
void IDestroyAct.OnDestroy(DestructionEventArgs eventArgs)
{
if (!string.IsNullOrWhiteSpace(spawnOnDestroy) && eventArgs.IsSpawnWreck)
{
Owner.EntityManager.SpawnEntity(spawnOnDestroy, Owner.Transform.GridPosition);
}
}
}

View File

@@ -1,86 +0,0 @@
using System;
using System.Collections.Generic;
using Content.Shared.GameObjects.Components.Damage;
using Robust.Shared.Interfaces.Serialization;
using Robust.Shared.Serialization;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Damage
{
/// <summary>
/// Resistance set used by damageable objects.
/// For each damage type, has a coefficient, damage reduction and "included in total" value.
/// </summary>
public class ResistanceSet : IExposeData
{
public static ResistanceSet DefaultResistanceSet = new ResistanceSet();
[ViewVariables]
private readonly Dictionary<DamageType, ResistanceSetSettings> _resistances = new Dictionary<DamageType, ResistanceSetSettings>();
public ResistanceSet()
{
foreach (DamageType damageType in Enum.GetValues(typeof(DamageType)))
{
_resistances[damageType] = new ResistanceSetSettings();
}
}
public void ExposeData(ObjectSerializer serializer)
{
foreach (DamageType damageType in Enum.GetValues(typeof(DamageType)))
{
var resistanceName = damageType.ToString().ToLower();
serializer.DataReadFunction(resistanceName, new ResistanceSetSettings(), resistanceSetting =>
{
_resistances[damageType] = resistanceSetting;
});
}
}
/// <summary>
/// Adjusts input damage with the resistance set values.
/// </summary>
/// <param name="damageType">Type of the damage.</param>
/// <param name="amount">Incoming amount of the damage.</param>
/// <returns>Damage adjusted by the resistance set.</returns>
public int CalculateDamage(DamageType damageType, int amount)
{
if (amount > 0) //if it's damage, reduction applies
{
amount -= _resistances[damageType].DamageReduction;
if (amount <= 0)
return 0;
}
amount = (int)Math.Floor(amount * _resistances[damageType].Coefficient);
return amount;
}
public bool AppliesToTotal(DamageType damageType)
{
//Damage that goes straight to total (for whatever reason) never applies twice
return damageType != DamageType.Total && _resistances[damageType].AppliesToTotal;
}
/// <summary>
/// Settings for a specific damage type in a resistance set.
/// </summary>
public class ResistanceSetSettings : IExposeData
{
public float Coefficient { get; private set; } = 1;
public int DamageReduction { get; private set; } = 0;
public bool AppliesToTotal { get; private set; } = true;
public void ExposeData(ObjectSerializer serializer)
{
serializer.DataField(this, x => Coefficient, "coefficient", 1);
serializer.DataField(this, x => DamageReduction, "damageReduction", 0);
serializer.DataField(this, x => AppliesToTotal, "appliesToTotal", true);
}
}
}
}

View File

@@ -0,0 +1,84 @@
using System.Collections.Generic;
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.Serialization;
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
{
private DamageState _currentDamageState;
/// <summary>
/// How much HP this component can sustain before triggering
/// <see cref="PerformDestruction"/>.
/// </summary>
public int MaxHp { get; private set; }
/// <summary>
/// Sound played upon destruction.
/// </summary>
protected string DestroySound { get; private set; }
public override List<DamageState> SupportedDamageStates =>
new List<DamageState> {DamageState.Alive, DamageState.Dead};
public override DamageState CurrentDamageState => _currentDamageState;
public override void Initialize()
{
base.Initialize();
HealthChangedEvent += OnHealthChanged;
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(this, ruinable => ruinable.MaxHp, "maxHP", 100);
serializer.DataField(this, ruinable => ruinable.DestroySound, "destroySound", string.Empty);
}
public override void OnRemove()
{
base.OnRemove();
HealthChangedEvent -= OnHealthChanged;
}
private void OnHealthChanged(HealthChangedEventArgs e)
{
if (CurrentDamageState != DamageState.Dead && TotalDamage >= MaxHp)
{
PerformDestruction();
}
}
/// <summary>
/// Destroys the Owner <see cref="IEntity"/>, setting
/// <see cref="IDamageableComponent.CurrentDamageState"/> to
/// <see cref="DamageState.Dead"/>
/// </summary>
protected void PerformDestruction()
{
_currentDamageState = DamageState.Dead;
if (!Owner.Deleted && DestroySound != string.Empty)
{
var pos = Owner.Transform.GridPosition;
EntitySystem.Get<AudioSystem>().PlayAtCoords(DestroySound, pos);
}
DestructionBehavior();
}
protected abstract void DestructionBehavior();
}
}