Clean up some parts of ExplosionSystem (#40485)

* Clean up some parts of ExplosionSystem

* Update Content.Shared/Damage/DamageSpecifier.cs

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>

* Review

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
This commit is contained in:
Leon Friedrich
2025-09-29 05:23:40 +13:00
committed by GitHub
parent fd5f9d7f60
commit f2d4317258
10 changed files with 109 additions and 54 deletions

View File

@@ -123,7 +123,7 @@ namespace Content.Server.Atmos.EntitySystems
public void InvalidatePosition(Entity<MapGridComponent?> grid, Vector2i pos) public void InvalidatePosition(Entity<MapGridComponent?> grid, Vector2i pos)
{ {
var query = GetEntityQuery<AirtightComponent>(); var query = GetEntityQuery<AirtightComponent>();
_explosionSystem.UpdateAirtightMap(grid, pos, grid, query); _explosionSystem.UpdateAirtightMap(grid, pos, grid);
_atmosphereSystem.InvalidateTile(grid.Owner, pos); _atmosphereSystem.InvalidateTile(grid.Owner, pos);
} }

View File

@@ -11,8 +11,6 @@ namespace Content.Server.Explosion.EntitySystems;
public sealed partial class ExplosionSystem public sealed partial class ExplosionSystem
{ {
[Dependency] private readonly DestructibleSystem _destructibleSystem = default!;
private readonly Dictionary<string, int> _explosionTypes = new(); private readonly Dictionary<string, int> _explosionTypes = new();
private void InitAirtightMap() private void InitAirtightMap()
@@ -26,6 +24,8 @@ public sealed partial class ExplosionSystem
int index = 0; int index = 0;
foreach (var prototype in _prototypeManager.EnumeratePrototypes<ExplosionPrototype>()) foreach (var prototype in _prototypeManager.EnumeratePrototypes<ExplosionPrototype>())
{ {
// TODO EXPLOSION
// just make this a field on the prototype
_explosionTypes.Add(prototype.ID, index); _explosionTypes.Add(prototype.ID, index);
index++; index++;
} }
@@ -42,10 +42,10 @@ public sealed partial class ExplosionSystem
// indices to this tile-data struct. // indices to this tile-data struct.
private Dictionary<EntityUid, Dictionary<Vector2i, TileData>> _airtightMap = new(); private Dictionary<EntityUid, Dictionary<Vector2i, TileData>> _airtightMap = new();
public void UpdateAirtightMap(EntityUid gridId, Vector2i tile, MapGridComponent? grid = null, EntityQuery<AirtightComponent>? query = null) public void UpdateAirtightMap(EntityUid gridId, Vector2i tile, MapGridComponent? grid = null)
{ {
if (Resolve(gridId, ref grid, false)) if (Resolve(gridId, ref grid, false))
UpdateAirtightMap(gridId, grid, tile, query); UpdateAirtightMap(gridId, grid, tile);
} }
/// <summary> /// <summary>
@@ -58,7 +58,7 @@ public sealed partial class ExplosionSystem
/// something like a normal and a reinforced windoor on the same tile. But given that this is a pretty rare /// something like a normal and a reinforced windoor on the same tile. But given that this is a pretty rare
/// occurrence, I am fine with this. /// occurrence, I am fine with this.
/// </remarks> /// </remarks>
public void UpdateAirtightMap(EntityUid gridId, MapGridComponent grid, Vector2i tile, EntityQuery<AirtightComponent>? query = null) public void UpdateAirtightMap(EntityUid gridId, MapGridComponent grid, Vector2i tile)
{ {
var tolerance = new float[_explosionTypes.Count]; var tolerance = new float[_explosionTypes.Count];
var blockedDirections = AtmosDirection.Invalid; var blockedDirections = AtmosDirection.Invalid;
@@ -66,18 +66,15 @@ public sealed partial class ExplosionSystem
if (!_airtightMap.ContainsKey(gridId)) if (!_airtightMap.ContainsKey(gridId))
_airtightMap[gridId] = new(); _airtightMap[gridId] = new();
query ??= GetEntityQuery<AirtightComponent>(); var anchoredEnumerator = _map.GetAnchoredEntitiesEnumerator(gridId, grid, tile);
var damageQuery = GetEntityQuery<DamageableComponent>();
var destructibleQuery = GetEntityQuery<DestructibleComponent>();
var anchoredEnumerator = _mapSystem.GetAnchoredEntitiesEnumerator(gridId, grid, tile);
while (anchoredEnumerator.MoveNext(out var uid)) while (anchoredEnumerator.MoveNext(out var uid))
{ {
if (!query.Value.TryGetComponent(uid, out var airtight) || !airtight.AirBlocked) if (!_airtightQuery.TryGetComponent(uid, out var airtight) || !airtight.AirBlocked)
continue; continue;
blockedDirections |= airtight.AirBlockedDirection; blockedDirections |= airtight.AirBlockedDirection;
var entityTolerances = GetExplosionTolerance(uid.Value, damageQuery, destructibleQuery); var entityTolerances = GetExplosionTolerance(uid.Value);
for (var i = 0; i < tolerance.Length; i++) for (var i = 0; i < tolerance.Length; i++)
{ {
tolerance[i] = Math.Max(tolerance[i], entityTolerances[i]); tolerance[i] = Math.Max(tolerance[i], entityTolerances[i]);
@@ -105,28 +102,25 @@ public sealed partial class ExplosionSystem
if (!TryComp<MapGridComponent>(transform.GridUid, out var grid)) if (!TryComp<MapGridComponent>(transform.GridUid, out var grid))
return; return;
UpdateAirtightMap(transform.GridUid.Value, grid, _mapSystem.CoordinatesToTile(transform.GridUid.Value, grid, transform.Coordinates)); UpdateAirtightMap(transform.GridUid.Value, grid, _map.CoordinatesToTile(transform.GridUid.Value, grid, transform.Coordinates));
} }
/// <summary> /// <summary>
/// Return a dictionary that specifies how intense a given explosion type needs to be in order to destroy an entity. /// Return a dictionary that specifies how intense a given explosion type needs to be in order to destroy an entity.
/// </summary> /// </summary>
public float[] GetExplosionTolerance( public float[] GetExplosionTolerance(EntityUid uid)
EntityUid uid,
EntityQuery<DamageableComponent> damageQuery,
EntityQuery<DestructibleComponent> destructibleQuery)
{ {
// How much total damage is needed to destroy this entity? This also includes "break" behaviors. This ASSUMES // How much total damage is needed to destroy this entity? This also includes "break" behaviors. This ASSUMES
// that this will result in a non-airtight entity.Entities that ONLY break via construction graph node changes // 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. // are currently effectively "invincible" as far as this is concerned. This really should be done more rigorously.
var totalDamageTarget = FixedPoint2.MaxValue; var totalDamageTarget = FixedPoint2.MaxValue;
if (destructibleQuery.TryGetComponent(uid, out var destructible)) if (_destructibleQuery.TryGetComponent(uid, out var destructible))
{ {
totalDamageTarget = _destructibleSystem.DestroyedAt(uid, destructible); totalDamageTarget = _destructibleSystem.DestroyedAt(uid, destructible);
} }
var explosionTolerance = new float[_explosionTypes.Count]; var explosionTolerance = new float[_explosionTypes.Count];
if (totalDamageTarget == FixedPoint2.MaxValue || !damageQuery.TryGetComponent(uid, out var damageable)) if (totalDamageTarget == FixedPoint2.MaxValue || !_damageableQuery.TryGetComponent(uid, out var damageable))
{ {
for (var i = 0; i < explosionTolerance.Length; i++) for (var i = 0; i < explosionTolerance.Length; i++)
{ {
@@ -139,9 +133,12 @@ public sealed partial class ExplosionSystem
// does not support entities dynamically changing explosive resistances (e.g. via clothing). But these probably // does not support entities dynamically changing explosive resistances (e.g. via clothing). But these probably
// shouldn't be airtight structures anyways.... // shouldn't be airtight structures anyways....
var mod = _damageableSystem.UniversalAllDamageModifier * _damageableSystem.UniversalExplosionDamageModifier;
foreach (var (id, index) in _explosionTypes) foreach (var (id, index) in _explosionTypes)
{ {
if (!_prototypeManager.TryIndex<ExplosionPrototype>(id, out var explosionType)) // TODO EXPLOSION SYSTEM
// cache explosion type damage.
if (!_prototypeManager.Resolve(id, out ExplosionPrototype? explosionType))
continue; continue;
// evaluate the damage that this damage type would do to this entity // evaluate the damage that this damage type would do to this entity
@@ -151,10 +148,15 @@ public sealed partial class ExplosionSystem
if (!damageable.Damage.DamageDict.ContainsKey(type)) if (!damageable.Damage.DamageDict.ContainsKey(type))
continue; 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); var ev = new GetExplosionResistanceEvent(explosionType.ID);
RaiseLocalEvent(uid, ref ev); RaiseLocalEvent(uid, ref ev);
damagePerIntensity += value * Math.Max(0, ev.DamageCoefficient); damagePerIntensity += value * mod * Math.Max(0, ev.DamageCoefficient);
} }
explosionTolerance[index] = damagePerIntensity > 0 explosionTolerance[index] = damagePerIntensity > 0
@@ -179,4 +181,16 @@ public sealed partial class ExplosionSystem
public float[] ExplosionTolerance; public float[] ExplosionTolerance;
public AtmosDirection BlockedDirections = AtmosDirection.Invalid; public AtmosDirection BlockedDirections = AtmosDirection.Invalid;
} }
public override void ReloadMap()
{
foreach (var(grid, dict) in _airtightMap)
{
var comp = Comp<MapGridComponent>(grid);
foreach (var index in dict.Keys)
{
UpdateAirtightMap(grid, comp, index);
}
}
}
} }

View File

@@ -258,7 +258,7 @@ public sealed partial class ExplosionSystem
{ {
var neighbourIndex = change.GridIndices + NeighbourVectors[i]; var neighbourIndex = change.GridIndices + NeighbourVectors[i];
if (_mapSystem.TryGetTileRef(ev.Entity, grid, neighbourIndex, out var neighbourTile) && !neighbourTile.Tile.IsEmpty) if (_map.TryGetTileRef(ev.Entity, grid, neighbourIndex, out var neighbourTile) && !neighbourTile.Tile.IsEmpty)
{ {
var oppositeDirection = (NeighborFlag)(1 << ((i + 4) % 8)); var oppositeDirection = (NeighborFlag)(1 << ((i + 4) % 8));
edges[neighbourIndex] = edges.GetValueOrDefault(neighbourIndex) | oppositeDirection; edges[neighbourIndex] = edges.GetValueOrDefault(neighbourIndex) | oppositeDirection;
@@ -307,7 +307,7 @@ public sealed partial class ExplosionSystem
spaceDirections = NeighborFlag.Invalid; spaceDirections = NeighborFlag.Invalid;
for (var i = 0; i < NeighbourVectors.Length; i++) for (var i = 0; i < NeighbourVectors.Length; i++)
{ {
if (!_mapSystem.TryGetTileRef(grid, grid.Comp, index + NeighbourVectors[i], out var neighborTile) || neighborTile.Tile.IsEmpty) if (!_map.TryGetTileRef(grid, grid.Comp, index + NeighbourVectors[i], out var neighborTile) || neighborTile.Tile.IsEmpty)
spaceDirections |= (NeighborFlag) (1 << i); spaceDirections |= (NeighborFlag) (1 << i);
} }

View File

@@ -27,8 +27,6 @@ namespace Content.Server.Explosion.EntitySystems;
public sealed partial class ExplosionSystem public sealed partial class ExplosionSystem
{ {
[Dependency] private readonly FlammableSystem _flammableSystem = default!;
/// <summary> /// <summary>
/// Used to limit explosion processing time. See <see cref="MaxProcessingTime"/>. /// Used to limit explosion processing time. See <see cref="MaxProcessingTime"/>.
/// </summary> /// </summary>
@@ -446,7 +444,7 @@ public sealed partial class ExplosionSystem
GetEntitiesToDamage(uid, originalDamage, id); GetEntitiesToDamage(uid, originalDamage, id);
foreach (var (entity, damage) in _toDamage) foreach (var (entity, damage) in _toDamage)
{ {
if (damage.GetTotal() > 0 && TryComp<ActorComponent>(entity, out var actorComponent)) if (_actorQuery.HasComp(entity))
{ {
// Log damage to player entities only, cause this will create a massive amount of log spam otherwise. // Log damage to player entities only, cause this will create a massive amount of log spam otherwise.
if (cause != null) if (cause != null)
@@ -461,7 +459,7 @@ public sealed partial class ExplosionSystem
} }
// TODO EXPLOSIONS turn explosions into entities, and pass the the entity in as the damage origin. // TODO EXPLOSIONS turn explosions into entities, and pass the the entity in as the damage origin.
_damageableSystem.TryChangeDamage(entity, damage * _damageableSystem.UniversalExplosionDamageModifier, ignoreResistances: true); _damageableSystem.TryChangeDamage(entity, damage, ignoreResistances: true, ignoreGlobalModifiers: true);
} }
} }
@@ -668,6 +666,7 @@ sealed class Explosion
private readonly IEntityManager _entMan; private readonly IEntityManager _entMan;
private readonly ExplosionSystem _system; private readonly ExplosionSystem _system;
private readonly SharedMapSystem _mapSystem; private readonly SharedMapSystem _mapSystem;
private readonly DamageableSystem _damageable;
public readonly EntityUid VisualEnt; public readonly EntityUid VisualEnt;
@@ -688,10 +687,10 @@ sealed class Explosion
int maxTileBreak, int maxTileBreak,
bool canCreateVacuum, bool canCreateVacuum,
IEntityManager entMan, IEntityManager entMan,
IMapManager mapMan,
EntityUid visualEnt, EntityUid visualEnt,
EntityUid? cause, EntityUid? cause,
SharedMapSystem mapSystem) SharedMapSystem mapSystem,
DamageableSystem damageable)
{ {
VisualEnt = visualEnt; VisualEnt = visualEnt;
Cause = cause; Cause = cause;
@@ -706,6 +705,7 @@ sealed class Explosion
_maxTileBreak = maxTileBreak; _maxTileBreak = maxTileBreak;
_canCreateVacuum = canCreateVacuum; _canCreateVacuum = canCreateVacuum;
_entMan = entMan; _entMan = entMan;
_damageable = damageable;
_xformQuery = entMan.GetEntityQuery<TransformComponent>(); _xformQuery = entMan.GetEntityQuery<TransformComponent>();
_physicsQuery = entMan.GetEntityQuery<PhysicsComponent>(); _physicsQuery = entMan.GetEntityQuery<PhysicsComponent>();
@@ -760,8 +760,10 @@ sealed class Explosion
_expectedDamage = ExplosionType.DamagePerIntensity * _currentIntensity; _expectedDamage = ExplosionType.DamagePerIntensity * _currentIntensity;
} }
#endif #endif
var modifier = _currentIntensity
_currentDamage = ExplosionType.DamagePerIntensity * _currentIntensity; * _damageable.UniversalExplosionDamageModifier
* _damageable.UniversalAllDamageModifier;
_currentDamage = ExplosionType.DamagePerIntensity * modifier;
// only throw if either the explosion is small, or if this is the outer ring of a large explosion. // only throw if either the explosion is small, or if this is the outer ring of a large explosion.
var doThrow = Area < _system.ThrowLimit || CurrentIteration > _tileSetIntensity.Count - 6; var doThrow = Area < _system.ThrowLimit || CurrentIteration > _tileSetIntensity.Count - 6;

View File

@@ -52,7 +52,7 @@ public sealed partial class ExplosionSystem
// get the epicenter tile indices // get the epicenter tile indices
if (_mapManager.TryFindGridAt(epicenter, out var gridUid, out var candidateGrid) && if (_mapManager.TryFindGridAt(epicenter, out var gridUid, out var candidateGrid) &&
_mapSystem.TryGetTileRef(gridUid, candidateGrid, _mapSystem.WorldToTile(gridUid, candidateGrid, epicenter.Position), out var tileRef) && _map.TryGetTileRef(gridUid, candidateGrid, _map.WorldToTile(gridUid, candidateGrid, epicenter.Position), out var tileRef) &&
!tileRef.Tile.IsEmpty) !tileRef.Tile.IsEmpty)
{ {
epicentreGrid = gridUid; epicentreGrid = gridUid;
@@ -62,7 +62,7 @@ public sealed partial class ExplosionSystem
{ {
// reference grid defines coordinate system that the explosion in space will use // reference grid defines coordinate system that the explosion in space will use
var gridComp = Comp<MapGridComponent>(referenceGrid.Value); var gridComp = Comp<MapGridComponent>(referenceGrid.Value);
initialTile = _mapSystem.WorldToTile(referenceGrid.Value, gridComp, epicenter.Position); initialTile = _map.WorldToTile(referenceGrid.Value, gridComp, epicenter.Position);
} }
else else
{ {

View File

@@ -2,6 +2,8 @@ using System.Linq;
using System.Numerics; using System.Numerics;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Atmos.Components; using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Destructible;
using Content.Server.NodeContainer.EntitySystems; using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NPC.Pathfinding; using Content.Server.NPC.Pathfinding;
using Content.Shared.Atmos.Components; using Content.Shared.Atmos.Components;
@@ -16,7 +18,6 @@ using Content.Shared.GameTicking;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Projectiles; using Content.Shared.Projectiles;
using Content.Shared.Throwing; using Content.Shared.Throwing;
using Robust.Server.GameObjects;
using Robust.Server.GameStates; using Robust.Server.GameStates;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
@@ -38,23 +39,28 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!; [Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly MapSystem _mapSystem = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!; [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly NodeGroupSystem _nodeGroupSystem = default!; [Dependency] private readonly NodeGroupSystem _nodeGroupSystem = default!;
[Dependency] private readonly PathfindingSystem _pathfindingSystem = default!; [Dependency] private readonly PathfindingSystem _pathfindingSystem = default!;
[Dependency] private readonly SharedCameraRecoilSystem _recoilSystem = default!; [Dependency] private readonly SharedCameraRecoilSystem _recoilSystem = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly ThrowingSystem _throwingSystem = default!; [Dependency] private readonly ThrowingSystem _throwingSystem = default!;
[Dependency] private readonly PvsOverrideSystem _pvsSys = default!; [Dependency] private readonly PvsOverrideSystem _pvsSys = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedTransformSystem _transformSystem = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
[Dependency] private readonly SharedMapSystem _map = default!; [Dependency] private readonly SharedMapSystem _map = default!;
[Dependency] private readonly FlammableSystem _flammableSystem = default!;
[Dependency] private readonly DestructibleSystem _destructibleSystem = default!;
private EntityQuery<FlammableComponent> _flammableQuery; private EntityQuery<FlammableComponent> _flammableQuery;
private EntityQuery<PhysicsComponent> _physicsQuery; private EntityQuery<PhysicsComponent> _physicsQuery;
private EntityQuery<ProjectileComponent> _projectileQuery; private EntityQuery<ProjectileComponent> _projectileQuery;
private EntityQuery<ActorComponent> _actorQuery;
private EntityQuery<DestructibleComponent> _destructibleQuery;
private EntityQuery<DamageableComponent> _damageableQuery;
private EntityQuery<AirtightComponent> _airtightQuery;
/// <summary> /// <summary>
/// "Tile-size" for space when there are no nearby grids to use as a reference. /// "Tile-size" for space when there are no nearby grids to use as a reference.
@@ -93,6 +99,10 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
_flammableQuery = GetEntityQuery<FlammableComponent>(); _flammableQuery = GetEntityQuery<FlammableComponent>();
_physicsQuery = GetEntityQuery<PhysicsComponent>(); _physicsQuery = GetEntityQuery<PhysicsComponent>();
_projectileQuery = GetEntityQuery<ProjectileComponent>(); _projectileQuery = GetEntityQuery<ProjectileComponent>();
_actorQuery = GetEntityQuery<ActorComponent>();
_destructibleQuery = GetEntityQuery<DestructibleComponent>();
_damageableQuery = GetEntityQuery<DamageableComponent>();
_airtightQuery = GetEntityQuery<AirtightComponent>();
} }
private void OnReset(RoundRestartCleanupEvent ev) private void OnReset(RoundRestartCleanupEvent ev)
@@ -317,7 +327,7 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
private Explosion? SpawnExplosion(QueuedExplosion queued) private Explosion? SpawnExplosion(QueuedExplosion queued)
{ {
var pos = queued.Epicenter; var pos = queued.Epicenter;
if (!_mapSystem.MapExists(pos.MapId)) if (!_map.MapExists(pos.MapId))
return null; return null;
var results = GetExplosionTiles(pos, queued.Proto.ID, queued.TotalIntensity, queued.Slope, queued.MaxTileIntensity); var results = GetExplosionTiles(pos, queued.Proto.ID, queued.TotalIntensity, queued.Slope, queued.MaxTileIntensity);
@@ -333,7 +343,7 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
CameraShake(iterationIntensity.Count * 4f, pos, queued.TotalIntensity); CameraShake(iterationIntensity.Count * 4f, pos, queued.TotalIntensity);
//For whatever bloody reason, sound system requires ENTITY coordinates. //For whatever bloody reason, sound system requires ENTITY coordinates.
var mapEntityCoords = _transformSystem.ToCoordinates(_mapSystem.GetMap(pos.MapId), pos); var mapEntityCoords = _transformSystem.ToCoordinates(_map.GetMap(pos.MapId), pos);
// play sound. // play sound.
// for the normal audio, we want everyone in pvs range // for the normal audio, we want everyone in pvs range
@@ -376,10 +386,10 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
queued.MaxTileBreak, queued.MaxTileBreak,
queued.CanCreateVacuum, queued.CanCreateVacuum,
EntityManager, EntityManager,
_mapManager,
visualEnt, visualEnt,
queued.Cause, queued.Cause,
_map); _map,
_damageableSystem);
} }
private void CameraShake(float range, MapCoordinates epicenter, float totalIntensity) private void CameraShake(float range, MapCoordinates epicenter, float totalIntensity)

View File

@@ -44,7 +44,7 @@ namespace Content.Shared.Damage
/// <remarks> /// <remarks>
/// If this data-field is specified, this allows damageable components to be initialized with non-zero damage. /// If this data-field is specified, this allows damageable components to be initialized with non-zero damage.
/// </remarks> /// </remarks>
[DataField(readOnly: true)] //todo remove this readonly when implementing writing to damagespecifier [DataField(readOnly: true)] // TODO FULL GAME SAVE
public DamageSpecifier Damage = new(); public DamageSpecifier Damage = new();
/// <summary> /// <summary>

View File

@@ -20,6 +20,11 @@ namespace Content.Shared.Damage
[DataDefinition, Serializable, NetSerializable] [DataDefinition, Serializable, NetSerializable]
public sealed partial class DamageSpecifier : IEquatable<DamageSpecifier> public sealed partial class DamageSpecifier : IEquatable<DamageSpecifier>
{ {
// For the record I regret so many of the decisions i made when rewriting damageable
// Why is it just shitting out dictionaries left and right
// One day Arrays, stackalloc spans, and SIMD will save the day.
// TODO DAMAGEABLE REFACTOR
// These exist solely so the wiki works. Please do not touch them or use them. // These exist solely so the wiki works. Please do not touch them or use them.
[JsonPropertyName("types")] [JsonPropertyName("types")]
[DataField("types", customTypeSerializer: typeof(PrototypeIdDictionarySerializer<FixedPoint2, DamageTypePrototype>))] [DataField("types", customTypeSerializer: typeof(PrototypeIdDictionarySerializer<FixedPoint2, DamageTypePrototype>))]

View File

@@ -2,9 +2,9 @@ using System.Linq;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.Damage.Prototypes; using Content.Shared.Damage.Prototypes;
using Content.Shared.Explosion.EntitySystems;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Mind.Components;
using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems; using Content.Shared.Mobs.Systems;
using Content.Shared.Radiation.Events; using Content.Shared.Radiation.Events;
@@ -25,10 +25,10 @@ namespace Content.Shared.Damage
[Dependency] private readonly MobThresholdSystem _mobThreshold = default!; [Dependency] private readonly MobThresholdSystem _mobThreshold = default!;
[Dependency] private readonly IConfigurationManager _config = default!; [Dependency] private readonly IConfigurationManager _config = default!;
[Dependency] private readonly SharedChemistryGuideDataSystem _chemistryGuideData = default!; [Dependency] private readonly SharedChemistryGuideDataSystem _chemistryGuideData = default!;
[Dependency] private readonly SharedExplosionSystem _explosion = default!;
private EntityQuery<AppearanceComponent> _appearanceQuery; private EntityQuery<AppearanceComponent> _appearanceQuery;
private EntityQuery<DamageableComponent> _damageableQuery; private EntityQuery<DamageableComponent> _damageableQuery;
private EntityQuery<MindContainerComponent> _mindContainerQuery;
public float UniversalAllDamageModifier { get; private set; } = 1f; public float UniversalAllDamageModifier { get; private set; } = 1f;
public float UniversalAllHealModifier { get; private set; } = 1f; public float UniversalAllHealModifier { get; private set; } = 1f;
@@ -52,7 +52,6 @@ namespace Content.Shared.Damage
_appearanceQuery = GetEntityQuery<AppearanceComponent>(); _appearanceQuery = GetEntityQuery<AppearanceComponent>();
_damageableQuery = GetEntityQuery<DamageableComponent>(); _damageableQuery = GetEntityQuery<DamageableComponent>();
_mindContainerQuery = GetEntityQuery<MindContainerComponent>();
// Damage modifier CVars are updated and stored here to be queried in other systems. // Damage modifier CVars are updated and stored here to be queried in other systems.
// Note that certain modifiers requires reloading the guidebook. // Note that certain modifiers requires reloading the guidebook.
@@ -60,6 +59,7 @@ namespace Content.Shared.Damage
{ {
UniversalAllDamageModifier = value; UniversalAllDamageModifier = value;
_chemistryGuideData.ReloadAllReagentPrototypes(); _chemistryGuideData.ReloadAllReagentPrototypes();
_explosion.ReloadMap();
}, true); }, true);
Subs.CVar(_config, CCVars.PlaytestAllHealModifier, value => Subs.CVar(_config, CCVars.PlaytestAllHealModifier, value =>
{ {
@@ -80,7 +80,11 @@ namespace Content.Shared.Damage
UniversalReagentHealModifier = value; UniversalReagentHealModifier = value;
_chemistryGuideData.ReloadAllReagentPrototypes(); _chemistryGuideData.ReloadAllReagentPrototypes();
}, true); }, true);
Subs.CVar(_config, CCVars.PlaytestExplosionDamageModifier, value => UniversalExplosionDamageModifier = value, true); Subs.CVar(_config, CCVars.PlaytestExplosionDamageModifier, value =>
{
UniversalExplosionDamageModifier = value;
_explosion.ReloadMap();
}, true);
Subs.CVar(_config, CCVars.PlaytestThrownDamageModifier, value => UniversalThrownDamageModifier = value, true); Subs.CVar(_config, CCVars.PlaytestThrownDamageModifier, value => UniversalThrownDamageModifier = value, true);
Subs.CVar(_config, CCVars.PlaytestTopicalsHealModifier, value => UniversalTopicalsHealModifier = value, true); Subs.CVar(_config, CCVars.PlaytestTopicalsHealModifier, value => UniversalTopicalsHealModifier = value, true);
Subs.CVar(_config, CCVars.PlaytestMobDamageModifier, value => UniversalMobDamageModifier = value, true); Subs.CVar(_config, CCVars.PlaytestMobDamageModifier, value => UniversalMobDamageModifier = value, true);
@@ -156,6 +160,9 @@ namespace Content.Shared.Damage
var data = new DamageVisualizerGroupData(component.DamagePerGroup.Keys.ToList()); var data = new DamageVisualizerGroupData(component.DamagePerGroup.Keys.ToList());
_appearance.SetData(uid, DamageVisualizerKeys.DamageUpdateGroups, data, appearance); _appearance.SetData(uid, DamageVisualizerKeys.DamageUpdateGroups, data, appearance);
} }
// TODO DAMAGE
// byref struct event.
RaiseLocalEvent(uid, new DamageChangedEvent(component, damageDelta, interruptsDoAfters, origin)); RaiseLocalEvent(uid, new DamageChangedEvent(component, damageDelta, interruptsDoAfters, origin));
} }
@@ -171,12 +178,24 @@ namespace Content.Shared.Damage
/// Returns a <see cref="DamageSpecifier"/> with information about the actual damage changes. This will be /// Returns a <see cref="DamageSpecifier"/> with information about the actual damage changes. This will be
/// null if the user had no applicable components that can take damage. /// null if the user had no applicable components that can take damage.
/// </returns> /// </returns>
public DamageSpecifier? TryChangeDamage(EntityUid? uid, DamageSpecifier damage, bool ignoreResistances = false, /// <param name="ignoreResistances">If true, this will ignore the entity's damage modifier (<see cref="DamageableComponent.DamageModifierSetId"/> and skip raising a <see cref="DamageModifyEvent"/>.</param>
bool interruptsDoAfters = true, DamageableComponent? damageable = null, EntityUid? origin = null) /// <param name="interruptsDoAfters">Whether the damage should cancel any damage sensitive do-afters</param>
/// <param name="origin">The entity that is causing this damage</param>
/// <param name="ignoreGlobalModifiers">If true, this will skip over applying the universal damage modifiers (see <see cref="ApplyUniversalAllModifiers"/>).</param>
/// <returns></returns>
public DamageSpecifier? TryChangeDamage(
EntityUid? uid,
DamageSpecifier damage,
bool ignoreResistances = false,
bool interruptsDoAfters = true,
DamageableComponent? damageable = null,
EntityUid? origin = null,
bool ignoreGlobalModifiers = false)
{ {
if (!uid.HasValue || !_damageableQuery.Resolve(uid.Value, ref damageable, false)) if (!uid.HasValue || !_damageableQuery.Resolve(uid.Value, ref damageable, false))
{ {
// TODO BODY SYSTEM pass damage onto body system // TODO BODY SYSTEM pass damage onto body system
// BOBBY WHEN?
return null; return null;
} }
@@ -195,13 +214,13 @@ namespace Content.Shared.Damage
if (!ignoreResistances) if (!ignoreResistances)
{ {
if (damageable.DamageModifierSetId != null && if (damageable.DamageModifierSetId != null &&
_prototypeManager.Resolve<DamageModifierSetPrototype>(damageable.DamageModifierSetId, out var modifierSet)) _prototypeManager.Resolve(damageable.DamageModifierSetId, out var modifierSet))
{ {
// TODO DAMAGE PERFORMANCE
// use a local private field instead of creating a new dictionary here..
damage = DamageSpecifier.ApplyModifierSet(damage, modifierSet); damage = DamageSpecifier.ApplyModifierSet(damage, modifierSet);
} }
// TODO DAMAGE
// byref struct event.
var ev = new DamageModifyEvent(damage, origin); var ev = new DamageModifyEvent(damage, origin);
RaiseLocalEvent(uid.Value, ev); RaiseLocalEvent(uid.Value, ev);
damage = ev.Damage; damage = ev.Damage;
@@ -212,11 +231,9 @@ namespace Content.Shared.Damage
} }
} }
if (!ignoreGlobalModifiers)
damage = ApplyUniversalAllModifiers(damage); damage = ApplyUniversalAllModifiers(damage);
// TODO DAMAGE PERFORMANCE
// Consider using a local private field instead of creating a new dictionary here.
// Would need to check that nothing ever tries to cache the delta.
var delta = new DamageSpecifier(); var delta = new DamageSpecifier();
delta.DamageDict.EnsureCapacity(damage.DamageDict.Count); delta.DamageDict.EnsureCapacity(damage.DamageDict.Count);

View File

@@ -69,4 +69,11 @@ public abstract class SharedExplosionSystem : EntitySystem
bool addLog = true) bool addLog = true)
{ {
} }
/// <summary>
/// This forces the explosion system to re-calculate the explosion intensity required to destroy all airtight entities.
/// </summary>
public virtual void ReloadMap()
{
}
} }