2024-08-10 20:05:54 -07:00
using Content.Shared.Damage ;
using Content.Shared.Damage.Prototypes ;
using Content.Shared.Mobs.Components ;
using Robust.Shared.Prototypes ;
using System.Linq ;
namespace Content.Shared.Chat ;
public sealed class SharedSuicideSystem : EntitySystem
{
2025-07-04 16:55:45 -04:00
private static readonly ProtoId < DamageTypePrototype > FallbackDamageType = "Blunt" ;
2024-08-10 20:05:54 -07:00
[Dependency] private readonly DamageableSystem _damageableSystem = default ! ;
[Dependency] private readonly IPrototypeManager _prototypeManager = default ! ;
/// <summary>
/// Applies lethal damage spread out across the damage types given.
/// </summary>
public void ApplyLethalDamage ( Entity < DamageableComponent > target , DamageSpecifier damageSpecifier )
{
// Create a new damageSpecifier so that we don't make alterations to the original DamageSpecifier
// Failing to do this will permanently change a weapon's damage making it insta-kill people
var appliedDamageSpecifier = new DamageSpecifier ( damageSpecifier ) ;
if ( ! TryComp < MobThresholdsComponent > ( target , out var mobThresholds ) )
return ;
// Mob thresholds are sorted from alive -> crit -> dead,
// grabbing the last key will give us how much damage is needed to kill a target from zero
// The exact lethal damage amount is adjusted based on their current damage taken
var lethalAmountOfDamage = mobThresholds . Thresholds . Keys . Last ( ) - target . Comp . TotalDamage ;
var totalDamage = appliedDamageSpecifier . GetTotal ( ) ;
// Removing structural because it causes issues against entities that cannot take structural damage,
// then getting the total to use in calculations for spreading out damage.
appliedDamageSpecifier . DamageDict . Remove ( "Structural" ) ;
// Split the total amount of damage needed to kill the target by every damage type in the DamageSpecifier
foreach ( var ( key , value ) in appliedDamageSpecifier . DamageDict )
{
appliedDamageSpecifier . DamageDict [ key ] = Math . Ceiling ( ( double ) ( value * lethalAmountOfDamage / totalDamage ) ) ;
}
_damageableSystem . TryChangeDamage ( target , appliedDamageSpecifier , true , origin : target ) ;
}
/// <summary>
/// Applies lethal damage in a single type, specified by a single damage type.
/// </summary>
public void ApplyLethalDamage ( Entity < DamageableComponent > target , ProtoId < DamageTypePrototype > ? damageType )
{
if ( ! TryComp < MobThresholdsComponent > ( target , out var mobThresholds ) )
return ;
// Mob thresholds are sorted from alive -> crit -> dead,
// grabbing the last key will give us how much damage is needed to kill a target from zero
// The exact lethal damage amount is adjusted based on their current damage taken
var lethalAmountOfDamage = mobThresholds . Thresholds . Keys . Last ( ) - target . Comp . TotalDamage ;
// We don't want structural damage for the same reasons listed above
if ( ! _prototypeManager . TryIndex ( damageType , out var damagePrototype ) | | damagePrototype . ID = = "Structural" )
{
2025-07-04 16:55:45 -04:00
Log . Error ( $"{nameof(SharedSuicideSystem)} could not find the damage type prototype associated with {damageType}. Falling back to {FallbackDamageType}" ) ;
damagePrototype = _prototypeManager . Index ( FallbackDamageType ) ;
2024-08-10 20:05:54 -07:00
}
var damage = new DamageSpecifier ( damagePrototype , lethalAmountOfDamage ) ;
_damageableSystem . TryChangeDamage ( target , damage , true , origin : target ) ;
}
}