diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs index ca3be8c37f..9d839264e4 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Airtight.cs @@ -230,7 +230,11 @@ public sealed partial class ExplosionSystem totalDamageTarget = _destructibleSystem.DestroyedAt(uid, destructible); } - if (totalDamageTarget == FixedPoint2.MaxValue || !_injurableQuery.TryGetComponent(uid, out var injurable)) + // We are assuming airtight entities don't need to relay since they shouldn't have inventories. + var modifiers = _damageableSystem.GetDamageModifierSet(uid); + var explosionComp = _explosionResistanceQuery.CompOrNull(uid); + + if (totalDamageTarget == FixedPoint2.MaxValue || !_injurableQuery.TryComp(uid, out var injurable)) { for (var i = 0; i < explosionTolerance.Length; i++) { @@ -254,30 +258,89 @@ public sealed partial class ExplosionSystem // evaluate the damage that this damage type would do to this entity var damagePerIntensity = FixedPoint2.Zero; + + // Create a dictionary of intensity thresholds which dictates when damagePerIntensity increases! + var damageThresholds = new SortedDictionary(); foreach (var (type, value) in explosionType.DamagePerIntensity.DamageDict) { if (!_damageableSystem.CanBeDamagedBy((uid, injurable), type)) continue; - // TODO EXPLOSION SYSTEM - // add a variant of the event that gets raised once, instead of once per prototype. - // Or better yet, just calculate this manually w/o the event. - // The event mainly exists for indirect resistances via things like inventory & clothing - // But this shouldn't matter for airtight entities. - var ev = new GetExplosionResistanceEvent(explosionType.ID); - RaiseLocalEvent(uid, ref ev); + var modifier = mod; + if (explosionComp != null) + { + modifier *= explosionComp.DamageCoefficient; + if (explosionComp.Modifiers.TryGetValue(explosionType.ID, out var typeMod)) + modifier *= typeMod; + } - damagePerIntensity += value * mod * Math.Max(0, ev.DamageCoefficient); + if (modifiers != null) + { + if (modifiers.Coefficients.TryGetValue(type, out var armorMod)) + modifier *= armorMod; + + if (modifiers.FlatReduction.TryGetValue(type, out var flat)) + { + if (flat > 0) + { + // If the flat modifier is reducing damage, we cache the extra damage per intensity for later! + var intensity = flat / value; + var damage = damageThresholds.GetValueOrDefault(intensity); + damageThresholds[intensity] = value * Math.Max(0, modifier) + damage; + continue; + } + } + } + + damagePerIntensity += value * Math.Max(0, modifier); } - var toleranceValue = damagePerIntensity > 0 - ? (float) ((totalDamageTarget - _damageableSystem.GetTotalDamage(uid)) / damagePerIntensity) - : ToleranceValues.Invulnerable; - - explosionTolerance[index] = toleranceValue; + explosionTolerance[index] += GetExplosionTolerance(uid, totalDamageTarget, damagePerIntensity, damageThresholds); } } + private FixedPoint2 GetExplosionTolerance(EntityUid uid, + FixedPoint2 totalDamageTarget, + FixedPoint2 damagePerIntensity, + SortedDictionary damageThresholds) + { + return GetExplosionTolerance(totalDamageTarget - _damageableSystem.GetTotalDamage(uid), + damagePerIntensity, + damageThresholds); + } + + private FixedPoint2 GetExplosionTolerance(FixedPoint2 damageTarget, + FixedPoint2 damagePerIntensity, + SortedDictionary damageThresholds) + { + var tolerance = damagePerIntensity > 0 ? damageTarget / damagePerIntensity : ToleranceValues.Invulnerable; + var prevIntensity = FixedPoint2.Zero; + /* + * Calculated through a pretty simple equation which relies on this dictionary being sorted. + * We precalculate the intensity at which an explosion's damage type exceeds the flat reduction of an entity's armor + * That is done above and stored in our `damageThresholds` SortedDictionary. If you can find a more mem efficient way to do this be my guest, + * but these values *have* to be sorted. + */ + foreach (var (intensity, damage) in damageThresholds) + { + // Check if the object would break before hitting this threshold, if so, return the current tolerance value + if (intensity > tolerance) + return tolerance; + + /* + * If the object breaks after this threshold, reduce the HP left by the amount of HP lost between the last flat reduction and this one + * Then adjust our damagePerIntensity and new tolerance values accordingly. + * Lastly store this intensity value so we can calculate the delta next loop. + */ + damageTarget -= (intensity - prevIntensity) * damagePerIntensity; + damagePerIntensity += damage; + tolerance = intensity + damageTarget / damagePerIntensity; + prevIntensity = intensity; + } + + return tolerance; + } + private void OnAirtightGridRemoved(EntityUid entity) { if (!TryComp(entity, out ExplosionAirtightGridComponent? airtightGrid)) diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs index c33e02efa8..df00da3647 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs @@ -452,7 +452,7 @@ public sealed partial class ExplosionSystem continue; // TODO EXPLOSIONS turn explosions into entities, and pass the the entity in as the damage origin. - _damageableSystem.ChangeDamage((entity, damageable), damage, ignoreResistances: true); + _damageableSystem.ChangeDamage((entity, damageable), damage); if (_actorQuery.HasComp(entity)) { diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs index e043c89392..cdb90c781d 100644 --- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs +++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Destructible; using Content.Server.NodeContainer.EntitySystems; using Content.Server.NPC.Pathfinding; +using Content.Shared.Armor; using Content.Shared.Atmos.Components; using Content.Shared.Camera; using Content.Shared.CCVar; @@ -60,6 +61,7 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem [Dependency] private EntityQuery _actorQuery = default!; [Dependency] private EntityQuery _destructibleQuery = default!; [Dependency] private EntityQuery _damageableQuery = default!; + [Dependency] private EntityQuery _explosionResistanceQuery = default!; [Dependency] private EntityQuery _injurableQuery = default!; [Dependency] private EntityQuery _airtightQuery = default!; [Dependency] private EntityQuery _tileHistoryQuery = default!; diff --git a/Resources/Locale/en-US/explosions/explosion-resistance.ftl b/Resources/Locale/en-US/explosions/explosion-resistance.ftl index 6ebb406b9a..50268ef32c 100644 --- a/Resources/Locale/en-US/explosions/explosion-resistance.ftl +++ b/Resources/Locale/en-US/explosions/explosion-resistance.ftl @@ -1,2 +1,2 @@ -explosion-resistance-coefficient-value = - [color=orange]Explosion[/color] damage reduced by [color=lightblue]{$value}%[/color]. +explosion-resistance-coefficient-value = - [color=orange]Explosion[/color] damage reduced by an additional [color=lightblue]{$value}%[/color]. explosion-resistance-contents-coefficient-value = - [color=orange]Explosion[/color] damage [color=white]to contents[/color] reduced by [color=lightblue]{$value}%[/color]. diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml index edbe0f95e4..4c986c4881 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/armor.yml @@ -114,8 +114,6 @@ Piercing: 0.6 Heat: 0.5 Caustic: 0.9 - - type: ExplosionResistance - damageCoefficient: 0.65 - type: GroupExamine - type: entity @@ -184,8 +182,6 @@ Heat: 0.3 Radiation: 0.5 Caustic: 0.5 - - type: ExplosionResistance - damageCoefficient: 0.5 - type: FireProtection reduction: 0.85 - type: StaticPrice @@ -235,8 +231,6 @@ Piercing: 0.35 Heat: 0.35 Caustic: 0.5 - - type: ExplosionResistance - damageCoefficient: 0.35 - type: StaminaResistance # do not add these to other equipment or mobs, we don't want to microbalance these everywhere damageCoefficient: 0.45 - type: ClothingSpeedModifier @@ -422,7 +416,7 @@ sprintModifier: 0.65 - type: HeldSpeedModifier - type: ExplosionResistance - damageCoefficient: 0.5 + damageCoefficient: 0.75 - type: GroupExamine - type: ProtectedFromStepTriggers slots: WITHOUT_POCKET @@ -448,7 +442,7 @@ sprintModifier: 0.8 - type: HeldSpeedModifier - type: ExplosionResistance - damageCoefficient: 0.4 + damageCoefficient: 0.6 - type: GroupExamine - type: Construction graph: BoneArmor diff --git a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml index 26ab26bd5e..056b47da30 100644 --- a/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml +++ b/Resources/Prototypes/Entities/Clothing/OuterClothing/hardsuits.yml @@ -159,7 +159,7 @@ highPressureMultiplier: 0.5 lowPressureMultiplier: 1000 - type: ExplosionResistance - damageCoefficient: 0.3 + damageCoefficient: 0.5 - type: Armor modifiers: coefficients: @@ -228,7 +228,7 @@ highPressureMultiplier: 0.5 lowPressureMultiplier: 1000 - type: ExplosionResistance - damageCoefficient: 0.4 + damageCoefficient: 0.5 - type: Armor modifiers: coefficients: @@ -290,7 +290,7 @@ highPressureMultiplier: 0.5 lowPressureMultiplier: 1000 - type: ExplosionResistance - damageCoefficient: 0.4 + damageCoefficient: 0.5 - type: Armor modifiers: coefficients: @@ -321,7 +321,7 @@ highPressureMultiplier: 0.02 lowPressureMultiplier: 1000 - type: ExplosionResistance - damageCoefficient: 0.5 + damageCoefficient: 0.7 - type: Armor modifiers: coefficients: @@ -601,7 +601,7 @@ highPressureMultiplier: 0.02 lowPressureMultiplier: 1000 - type: ExplosionResistance - damageCoefficient: 0.2 + damageCoefficient: 0.3 - type: FireProtection reduction: 0.8 - type: StaminaResistance # do not add these to other equipment or mobs, we don't want to microbalance these everywhere @@ -683,8 +683,6 @@ - type: PressureProtection highPressureMultiplier: 0.2 lowPressureMultiplier: 1000 - - type: ExplosionResistance - damageCoefficient: 0.3 # - type: StaminaResistance # Should not have stamina resistance, this is purely so people know it was not forgotten. - type: Armor modifiers: