mirror of
https://github.com/space-wizards/space-station-14.git
synced 2026-06-09 10:06:43 +02:00
[MICROBALANCE PR] Explosives no longer pierce armor (#40090)
* Explosiv nerf * Minor changes * Whoops * whoopsie doodles. * Address half of the review. * forgot this too (flat reductions are applied before the modifier so we need to account for that :P) * fix merge conflicts * it's time * whoops * me when I logmissing true * they call me the forgor cause I forgor --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
b2e9308ca0
commit
b5a8acdf9f
@@ -1,4 +1,3 @@
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Explosion.Components;
|
||||
@@ -214,12 +213,16 @@ public sealed partial class ExplosionSystem
|
||||
// that this will result in a non-airtight entity.Entities that ONLY break via construction graph node changes
|
||||
// are currently effectively "invincible" as far as this is concerned. This really should be done more rigorously.
|
||||
var totalDamageTarget = FixedPoint2.MaxValue;
|
||||
if (_destructibleQuery.TryGetComponent(uid, out var destructible))
|
||||
if (_destructibleQuery.TryComp(uid, out var destructible))
|
||||
{
|
||||
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++)
|
||||
{
|
||||
@@ -238,35 +241,94 @@ public sealed partial class ExplosionSystem
|
||||
{
|
||||
// TODO EXPLOSION SYSTEM
|
||||
// cache explosion type damage.
|
||||
if (!_prototypeManager.Resolve(id, out ExplosionPrototype? explosionType))
|
||||
if (!_prototypeManager.Resolve(id, out var explosionType))
|
||||
continue;
|
||||
|
||||
// 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<FixedPoint2, FixedPoint2>();
|
||||
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<FixedPoint2, FixedPoint2> damageThresholds)
|
||||
{
|
||||
return GetExplosionTolerance(totalDamageTarget - _damageableSystem.GetTotalDamage(uid),
|
||||
damagePerIntensity,
|
||||
damageThresholds);
|
||||
}
|
||||
|
||||
private FixedPoint2 GetExplosionTolerance(FixedPoint2 damageTarget,
|
||||
FixedPoint2 damagePerIntensity,
|
||||
SortedDictionary<FixedPoint2, FixedPoint2> 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))
|
||||
|
||||
@@ -1,24 +1,20 @@
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Explosion;
|
||||
using Content.Shared.Explosion.Components;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Physics;
|
||||
using Content.Shared.Projectiles;
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
using System.Numerics;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
using TimedDespawnComponent = Robust.Shared.Spawners.TimedDespawnComponent;
|
||||
|
||||
namespace Content.Server.Explosion.EntitySystems;
|
||||
@@ -228,34 +224,25 @@ public sealed partial class ExplosionSystem
|
||||
ProcessEntity(uid, epicenter, damage, throwForce, id, xform, fireStacks, cause);
|
||||
}
|
||||
|
||||
// process anchored entities
|
||||
var tileBlocked = false;
|
||||
_anchored.Clear();
|
||||
_map.GetAnchoredEntities(grid, tile, _anchored);
|
||||
foreach (var entity in _anchored)
|
||||
{
|
||||
processed.Add(entity);
|
||||
ProcessEntity(entity, epicenter, damage, throwForce, id, null, fireStacks, cause);
|
||||
}
|
||||
|
||||
// heat the atmosphere
|
||||
if (temperature != null)
|
||||
{
|
||||
_atmosphere.HotspotExpose(grid.Owner, tile, temperature.Value, currentIntensity, cause, true);
|
||||
}
|
||||
|
||||
// We process anchored entities last, these should've been caught by the lookups earlier.
|
||||
// Walls and reinforced walls will break into girders. These girders will also be considered turf-blocking for
|
||||
// the purposes of destroying floors. Again, ideally the process of damaging an entity should somehow return
|
||||
// information about the entities that were spawned as a result, but without that information we just have to
|
||||
// re-check for new anchored entities. Compared to entity spawning & deleting, this should still be relatively minor.
|
||||
var tileBlocked = false;
|
||||
_map.GetAnchoredEntities(grid, tile, _anchored);
|
||||
if (_anchored.Count > 0)
|
||||
{
|
||||
_anchored.Clear();
|
||||
_map.GetAnchoredEntities(grid, tile, _anchored);
|
||||
foreach (var entity in _anchored)
|
||||
{
|
||||
tileBlocked |= IsBlockingTurf(entity);
|
||||
}
|
||||
_anchored.Clear();
|
||||
}
|
||||
|
||||
// Next, we get the intersecting entities AGAIN, but purely for throwing. This way, glass shards spawned from
|
||||
@@ -441,7 +428,7 @@ public sealed partial class ExplosionSystem
|
||||
DamageSpecifier? originalDamage,
|
||||
float throwForce,
|
||||
string id,
|
||||
TransformComponent? xform,
|
||||
TransformComponent xform,
|
||||
float? fireStacksOnIgnite,
|
||||
EntityUid? cause)
|
||||
{
|
||||
@@ -454,7 +441,7 @@ public sealed partial class ExplosionSystem
|
||||
continue;
|
||||
|
||||
// TODO EXPLOSIONS turn explosions into entities, and pass the the entity in as the damage origin.
|
||||
_damageableSystem.TryChangeDamage((entity, damageable), damage, ignoreResistances: true, ignoreGlobalModifiers: true);
|
||||
_damageableSystem.ChangeDamage((entity, damageable), damage);
|
||||
|
||||
if (_actorQuery.HasComp(entity))
|
||||
{
|
||||
@@ -478,8 +465,7 @@ public sealed partial class ExplosionSystem
|
||||
}
|
||||
|
||||
// throw
|
||||
if (xform != null // null implies anchored or in a container
|
||||
&& !xform.Anchored
|
||||
if (!xform.Anchored
|
||||
&& throwForce > 0
|
||||
&& !EntityManager.IsQueuedForDeletion(uid)
|
||||
&& _physicsQuery.TryGetComponent(uid, out var physics)
|
||||
@@ -704,7 +690,7 @@ sealed class Explosion
|
||||
private readonly IEntityManager _entMan;
|
||||
private readonly ExplosionSystem _system;
|
||||
private readonly SharedMapSystem _mapSystem;
|
||||
private readonly Shared.Damage.Systems.DamageableSystem _damageable;
|
||||
private readonly DamageableSystem _damageable;
|
||||
|
||||
public readonly EntityUid VisualEnt;
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -64,6 +65,7 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
|
||||
[Dependency] private EntityQuery<ActorComponent> _actorQuery = default!;
|
||||
[Dependency] private EntityQuery<DestructibleComponent> _destructibleQuery = default!;
|
||||
[Dependency] private EntityQuery<DamageableComponent> _damageableQuery = default!;
|
||||
[Dependency] private EntityQuery<ExplosionResistanceComponent> _explosionResistanceQuery = default!;
|
||||
[Dependency] private EntityQuery<InjurableComponent> _injurableQuery = default!;
|
||||
[Dependency] private EntityQuery<AirtightComponent> _airtightQuery = default!;
|
||||
[Dependency] private EntityQuery<TileHistoryComponent> _tileHistoryQuery = default!;
|
||||
|
||||
@@ -17,6 +17,17 @@ public sealed partial class DamageableSystem
|
||||
return _supportedTypesByContainer[container.Value].Contains(type);
|
||||
}
|
||||
|
||||
public DamageModifierSet? GetDamageModifierSet(Entity<DamageableComponent?> entity)
|
||||
{
|
||||
if (!_damageableQuery.Resolve(entity, ref entity.Comp, false)
|
||||
|| entity.Comp.DamageModifierSetId is not { } proto
|
||||
|| !_prototypeManager.Resolve(proto, out var modifierSet)
|
||||
)
|
||||
return null;
|
||||
|
||||
return modifierSet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Directly sets the damage in a damageable component.
|
||||
/// </summary>
|
||||
@@ -132,10 +143,7 @@ public sealed partial class DamageableSystem
|
||||
// Apply resistances
|
||||
if (!ignoreResistances)
|
||||
{
|
||||
if (
|
||||
ent.Comp.DamageModifierSetId != null &&
|
||||
_prototypeManager.Resolve(ent.Comp.DamageModifierSetId, out var modifierSet)
|
||||
)
|
||||
if (GetDamageModifierSet(ent) is { } modifierSet)
|
||||
damage = DamageSpecifier.ApplyModifierSet(damage, modifierSet);
|
||||
|
||||
// TODO DAMAGE
|
||||
|
||||
@@ -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].
|
||||
|
||||
@@ -114,8 +114,6 @@
|
||||
Piercing: 0.6
|
||||
Heat: 0.5
|
||||
Caustic: 0.9
|
||||
- type: ExplosionResistance
|
||||
damageCoefficient: 0.65
|
||||
- type: GroupExamine
|
||||
|
||||
- type: entity
|
||||
@@ -187,8 +185,6 @@
|
||||
Heat: 0.3
|
||||
Radiation: 0.5
|
||||
Caustic: 0.5
|
||||
- type: ExplosionResistance
|
||||
damageCoefficient: 0.5
|
||||
- type: FireProtection
|
||||
reduction: 0.85
|
||||
- type: StaticPrice
|
||||
@@ -238,8 +234,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
|
||||
@@ -425,7 +419,7 @@
|
||||
sprintModifier: 0.65
|
||||
- type: HeldSpeedModifier
|
||||
- type: ExplosionResistance
|
||||
damageCoefficient: 0.5
|
||||
damageCoefficient: 0.75
|
||||
- type: GroupExamine
|
||||
- type: ProtectedFromStepTriggers
|
||||
slots: WITHOUT_POCKET
|
||||
@@ -451,7 +445,7 @@
|
||||
sprintModifier: 0.8
|
||||
- type: HeldSpeedModifier
|
||||
- type: ExplosionResistance
|
||||
damageCoefficient: 0.4
|
||||
damageCoefficient: 0.6
|
||||
- type: GroupExamine
|
||||
- type: Construction
|
||||
graph: BoneArmor
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
highPressureMultiplier: 0.5
|
||||
lowPressureMultiplier: 1000
|
||||
- type: ExplosionResistance
|
||||
damageCoefficient: 0.3
|
||||
damageCoefficient: 0.5
|
||||
- type: Armor
|
||||
modifiers:
|
||||
coefficients:
|
||||
@@ -234,7 +234,7 @@
|
||||
highPressureMultiplier: 0.5
|
||||
lowPressureMultiplier: 1000
|
||||
- type: ExplosionResistance
|
||||
damageCoefficient: 0.4
|
||||
damageCoefficient: 0.5
|
||||
- type: Armor
|
||||
modifiers:
|
||||
coefficients:
|
||||
@@ -296,7 +296,7 @@
|
||||
highPressureMultiplier: 0.5
|
||||
lowPressureMultiplier: 1000
|
||||
- type: ExplosionResistance
|
||||
damageCoefficient: 0.4
|
||||
damageCoefficient: 0.5
|
||||
- type: Armor
|
||||
modifiers:
|
||||
coefficients:
|
||||
@@ -327,7 +327,7 @@
|
||||
highPressureMultiplier: 0.02
|
||||
lowPressureMultiplier: 1000
|
||||
- type: ExplosionResistance
|
||||
damageCoefficient: 0.5
|
||||
damageCoefficient: 0.7
|
||||
- type: Armor
|
||||
modifiers:
|
||||
coefficients:
|
||||
@@ -608,7 +608,7 @@
|
||||
heatingCoefficient: 0.001
|
||||
coolingCoefficient: 0.001
|
||||
- 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
|
||||
@@ -688,8 +688,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:
|
||||
|
||||
Reference in New Issue
Block a user