diff --git a/Content.Server/Explosion/Components/ExplosiveComponent.cs b/Content.Server/Explosion/Components/ExplosiveComponent.cs
index b9fb4c4eac..9752f92d3b 100644
--- a/Content.Server/Explosion/Components/ExplosiveComponent.cs
+++ b/Content.Server/Explosion/Components/ExplosiveComponent.cs
@@ -60,13 +60,20 @@ public sealed class ExplosiveComponent : Component
public float TileBreakScale = 1f;
///
- /// Maximum number of times that an explosive can break a tile. Currently, for normal space ships breaking a
- /// tile twice will result in a vacuum.
+ /// Maximum number of times that an explosive can break a tile. Currently, for normal space stations breaking a
+ /// tile twice will generally result in a vacuum.
///
[ViewVariables(VVAccess.ReadWrite)]
[DataField("maxTileBreak")]
public int MaxTileBreak = int.MaxValue;
+ ///
+ /// Whether this explosive should be able to create a vacuum by breaking tiles.
+ ///
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField("canCreateVacuum")]
+ public bool CanCreateVacuum = true;
+
///
/// Avoid somehow double-triggering this explosion (e.g. by damaging this entity from its own explosion.
///
diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs
index 973ffc5ec8..aa80aa6f74 100644
--- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs
+++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.Processing.cs
@@ -182,8 +182,7 @@ public sealed partial class ExplosionSystem : EntitySystem
string id,
EntityQuery xformQuery,
EntityQuery damageQuery,
- EntityQuery physicsQuery,
- EntityQuery metaQuery)
+ EntityQuery physicsQuery)
{
var gridBox = new Box2(tile * grid.TileSize, (tile + 1) * grid.TileSize);
@@ -216,11 +215,23 @@ public sealed partial class ExplosionSystem : EntitySystem
// process anchored entities
var tileBlocked = false;
- foreach (var entity in grid.GetAnchoredEntities(tile).ToList())
+ var anchoredList = grid.GetAnchoredEntities(tile).ToList();
+ foreach (var entity in anchoredList)
{
processed.Add(entity);
ProcessEntity(entity, epicenter, damage, throwForce, id, damageQuery, physicsQuery);
- tileBlocked |= IsBlockingTurf(entity, physicsQuery);
+ }
+
+ // 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.
+ if (anchoredList.Count > 0)
+ {
+ foreach (var entity in grid.GetAnchoredEntities(tile))
+ {
+ tileBlocked |= IsBlockingTurf(entity, physicsQuery);
+ }
}
// Next, we get the intersecting entities AGAIN, but purely for throwing. This way, glass shards spawned from
@@ -261,8 +272,7 @@ public sealed partial class ExplosionSystem : EntitySystem
string id,
EntityQuery xformQuery,
EntityQuery damageQuery,
- EntityQuery physicsQuery,
- EntityQuery metaQuery)
+ EntityQuery physicsQuery)
{
var gridBox = new Box2(tile * DefaultTileSize, (DefaultTileSize, DefaultTileSize));
var worldBox = spaceMatrix.TransformBox(gridBox);
@@ -370,10 +380,15 @@ public sealed partial class ExplosionSystem : EntitySystem
public void DamageFloorTile(TileRef tileRef,
float effectiveIntensity,
int maxTileBreak,
+ bool canCreateVacuum,
List<(Vector2i GridIndices, Tile Tile)> damagedTiles,
ExplosionPrototype type)
{
- var tileDef = _tileDefinitionManager[tileRef.Tile.TypeId];
+ if (_tileDefinitionManager[tileRef.Tile.TypeId] is not ContentTileDefinition tileDef)
+ return;
+
+ if (tileDef.IsSpace)
+ canCreateVacuum = true; // is already a vacuum.
int tileBreakages = 0;
while (maxTileBreak > tileBreakages && _robustRandom.Prob(type.TileBreakChance(effectiveIntensity)))
@@ -381,14 +396,17 @@ public sealed partial class ExplosionSystem : EntitySystem
tileBreakages++;
effectiveIntensity -= type.TileBreakRerollReduction;
- if (tileDef is not ContentTileDefinition contentTileDef)
- break;
-
// does this have a base-turf that we can break it down to?
- if (contentTileDef.BaseTurfs.Count == 0)
+ if (tileDef.BaseTurfs.Count == 0)
break;
- tileDef = _tileDefinitionManager[contentTileDef.BaseTurfs[^1]];
+ if (_tileDefinitionManager[tileDef.BaseTurfs[^1]] is not ContentTileDefinition newDef)
+ break;
+
+ if (newDef.IsSpace && !canCreateVacuum)
+ break;
+
+ tileDef = newDef;
}
if (tileDef.TileId == tileRef.Tile.TypeId)
@@ -493,7 +511,6 @@ sealed class Explosion
private readonly EntityQuery _xformQuery;
private readonly EntityQuery _physicsQuery;
private readonly EntityQuery _damageQuery;
- private readonly EntityQuery _metaQuery;
///
/// Total area that the explosion covers.
@@ -510,6 +527,11 @@ sealed class Explosion
///
private readonly int _maxTileBreak;
+ ///
+ /// Whether this explosion can turn non-vacuum tiles into vacuum-tiles.
+ ///
+ private readonly bool _canCreateVacuum;
+
private readonly IEntityManager _entMan;
private readonly ExplosionSystem _system;
@@ -526,6 +548,7 @@ sealed class Explosion
int area,
float tileBreakScale,
int maxTileBreak,
+ bool canCreateVacuum,
IEntityManager entMan,
IMapManager mapMan)
{
@@ -537,12 +560,12 @@ sealed class Explosion
_tileBreakScale = tileBreakScale;
_maxTileBreak = maxTileBreak;
+ _canCreateVacuum = canCreateVacuum;
_entMan = entMan;
_xformQuery = entMan.GetEntityQuery();
_physicsQuery = entMan.GetEntityQuery();
_damageQuery = entMan.GetEntityQuery();
- _metaQuery = entMan.GetEntityQuery();
if (spaceData != null)
{
@@ -682,12 +705,11 @@ sealed class Explosion
ExplosionType.ID,
_xformQuery,
_damageQuery,
- _physicsQuery,
- _metaQuery);
+ _physicsQuery);
// If the floor is not blocked by some dense object, damage the floor tiles.
if (canDamageFloor)
- _system.DamageFloorTile(tileRef, _currentIntensity * _tileBreakScale, _maxTileBreak, tileUpdateList, ExplosionType);
+ _system.DamageFloorTile(tileRef, _currentIntensity * _tileBreakScale, _maxTileBreak, _canCreateVacuum, tileUpdateList, ExplosionType);
}
else
{
@@ -703,8 +725,7 @@ sealed class Explosion
ExplosionType.ID,
_xformQuery,
_damageQuery,
- _physicsQuery,
- _metaQuery);
+ _physicsQuery);
}
if (!MoveNext())
diff --git a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
index a958d6684a..226e43bc73 100644
--- a/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
+++ b/Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
@@ -122,6 +122,7 @@ public sealed partial class ExplosionSystem : EntitySystem
explosive.MaxIntensity,
explosive.TileBreakScale,
explosive.MaxTileBreak,
+ explosive.CanCreateVacuum,
user);
if (delete)
@@ -190,13 +191,14 @@ public sealed partial class ExplosionSystem : EntitySystem
float maxTileIntensity,
float tileBreakScale = 1f,
int maxTileBreak = int.MaxValue,
+ bool canCreateVacuum = true,
EntityUid? user = null,
bool addLog = false)
{
var pos = Transform(uid).MapPosition;
- QueueExplosion(pos, typeId, totalIntensity, slope, maxTileIntensity, tileBreakScale, maxTileBreak, addLog: false);
+ QueueExplosion(pos, typeId, totalIntensity, slope, maxTileIntensity, tileBreakScale, maxTileBreak, canCreateVacuum, addLog: false);
if (!addLog)
return;
@@ -219,6 +221,7 @@ public sealed partial class ExplosionSystem : EntitySystem
float maxTileIntensity,
float tileBreakScale = 1f,
int maxTileBreak = int.MaxValue,
+ bool canCreateVacuum = true,
bool addLog = false)
{
if (totalIntensity <= 0 || slope <= 0)
@@ -234,7 +237,7 @@ public sealed partial class ExplosionSystem : EntitySystem
_logsSystem.Add(LogType.Explosion, LogImpact.High, $"Explosion spawned at {epicenter:coordinates} with intensity {totalIntensity} slope {slope}");
_explosionQueue.Enqueue(() => SpawnExplosion(epicenter, type, totalIntensity,
- slope, maxTileIntensity, tileBreakScale, maxTileBreak));
+ slope, maxTileIntensity, tileBreakScale, maxTileBreak, canCreateVacuum));
}
///
@@ -248,7 +251,8 @@ public sealed partial class ExplosionSystem : EntitySystem
float slope,
float maxTileIntensity,
float tileBreakScale,
- int maxTileBreak)
+ int maxTileBreak,
+ bool canCreateVacuum)
{
if (!_mapManager.MapExists(epicenter.MapId))
return null;
@@ -283,6 +287,7 @@ public sealed partial class ExplosionSystem : EntitySystem
area,
tileBreakScale,
maxTileBreak,
+ canCreateVacuum,
EntityManager,
_mapManager);
}
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml
index f0b810db00..ae8dc93588 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml
@@ -80,7 +80,7 @@
- type: entity
name: Syndicate minibomb
- description: A syndicate manufactured explosive used to sow destruction and chaos.
+ description: Creates a small but powerful explosion that is strong enough to break through walls.
parent: BaseItem
id: SyndieMiniBomb
components:
@@ -95,10 +95,11 @@
delay: 5
- type: Explosive
explosionType: Default
- # About a 3-3.5 tile radius, strong enough to break reinforced walls near centre.
- totalIntensity: 800
- intensitySlope: 15
- maxTileBreak: 1 # for destroying walls, not spacing the hallway
+ # About a 2.5 tile radius. If placed next to walls, will destroy a line of 3-normal walls and turn 3 reinforced walls into girders.
+ totalIntensity: 492
+ intensitySlope: 20.5
+ maxIntensity: 41
+ canCreateVacuum: false # for destroying walls, not spacing the hallway
- type: ExplodeOnTrigger
- type: Damageable
damageContainer: Inorganic