Remove most IEntity usages from explosions (#5240)

* Remove most IEntity usages from Destructible and Explosions

* Perform a minute amount of cleanup

* Fix build
This commit is contained in:
Javier Guardia Fernández
2021-11-09 21:24:35 +01:00
committed by GitHub
parent 3a4186f6f6
commit 42aaba9a5d
28 changed files with 294 additions and 277 deletions

View File

@@ -2,12 +2,12 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.AME.Components;
using Content.Server.Explosion;
using Content.Server.Explosion.EntitySystems;
using Content.Server.NodeContainer.NodeGroups;
using Content.Server.NodeContainer.Nodes;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Random;
using Robust.Shared.ViewVariables;
@@ -177,7 +177,7 @@ namespace Content.Server.AME
intensity = Math.Min(intensity, 8);
epicenter.Owner.SpawnExplosion(intensity / 2, intensity, intensity * 2, intensity * 3);
EntitySystem.Get<ExplosionSystem>().SpawnExplosion(epicenter.Owner.Uid, intensity / 2, intensity, intensity * 2, intensity * 3);
}
}
}

View File

@@ -8,7 +8,7 @@ using Content.Server.Chemistry.EntitySystems;
using Content.Server.Configurable;
using Content.Server.Disposal.Tube.Components;
using Content.Server.EUI;
using Content.Server.Explosion;
using Content.Server.Explosion.EntitySystems;
using Content.Server.Ghost.Roles;
using Content.Server.Inventory.Components;
using Content.Server.Mind.Commands;
@@ -40,6 +40,7 @@ namespace Content.Server.Administration
[Dependency] private readonly IAdminManager _adminManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly EuiManager _euiManager = default!;
[Dependency] private readonly ExplosionSystem _explosions = default!;
[Dependency] private readonly GhostRoleSystem _ghostRoleSystem = default!;
private readonly Dictionary<IPlayerSession, EditSolutionsEui> _openSolutionUis = new();
@@ -118,7 +119,7 @@ namespace Content.Server.Administration
verb.Act = () =>
{
var coords = args.Target.Transform.Coordinates;
Timer.Spawn(_gameTiming.TickPeriod, () => ExplosionHelper.SpawnExplosion(coords, 0, 1, 2, 1), CancellationToken.None);
Timer.Spawn(_gameTiming.TickPeriod, () => _explosions.SpawnExplosion(coords, 0, 1, 2, 1), CancellationToken.None);
if (args.Target.TryGetComponent(out SharedBodyComponent? body))
{
body.Gib();

View File

@@ -1,10 +1,10 @@
using Content.Server.Explosion;
using Content.Server.Explosion.EntitySystems;
using Content.Shared.Administration;
using Robust.Server.Player;
using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
namespace Content.Server.Administration.Commands
{
[AdminCommand(AdminFlags.Fun)]
@@ -35,7 +35,7 @@ namespace Content.Server.Administration.Commands
var mapTransform = player.AttachedEntity.Transform.GetMapTransform();
var coords = new EntityCoordinates(mapTransform.Owner.Uid, x, y);
ExplosionHelper.SpawnExplosion(coords, dev, hvy, lgh, fla);
EntitySystem.Get<ExplosionSystem>().SpawnExplosion(coords, dev, hvy, lgh, fla);
}
}
}

View File

@@ -1,9 +1,7 @@
using System;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Body.Respiratory;
using Content.Server.Explosion;
using Content.Server.NodeContainer;
using Content.Server.NodeContainer.Nodes;
using Content.Server.Explosion.EntitySystems;
using Content.Server.UserInterface;
using Content.Shared.ActionBlocker;
using Content.Shared.Actions;
@@ -12,7 +10,6 @@ using Content.Shared.Actions.Components;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Audio;
using Content.Shared.DragDrop;
using Content.Shared.Examine;
using Content.Shared.Interaction;
using Content.Shared.Sound;
@@ -271,7 +268,7 @@ namespace Content.Server.Atmos.Components
range = MaxExplosionRange;
}
Owner.SpawnExplosion((int) (range * 0.25f), (int) (range * 0.5f), (int) (range * 1.5f), 1);
EntitySystem.Get<ExplosionSystem>().SpawnExplosion(OwnerUid, (int) (range * 0.25f), (int) (range * 0.5f), (int) (range * 1.5f), 1);
Owner.QueueDelete();
return;

View File

@@ -1,7 +1,6 @@
using System;
using Content.Server.Chemistry.Components;
using Content.Server.Chemistry.Components.SolutionManager;
using Content.Server.Explosion;
using Content.Server.Explosion.EntitySystems;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reaction;
using Robust.Shared.GameObjects;
@@ -50,7 +49,7 @@ namespace Content.Server.Chemistry.ReactionEffects
var finalHeavyImpactRange = (int)MathF.Round(_heavyImpactRange * floatIntensity);
var finalLightImpactRange = (int)MathF.Round(_lightImpactRange * floatIntensity);
var finalFlashRange = (int)MathF.Round(_flashRange * floatIntensity);
solutionEntity.SpawnExplosion(finalDevastationRange,
EntitySystem.Get<ExplosionSystem>().SpawnExplosion(solutionEntity, finalDevastationRange,
finalHeavyImpactRange, finalLightImpactRange, finalFlashRange);
}
}

View File

@@ -1,4 +1,6 @@
using Content.Server.Construction;
using Content.Server.Destructible.Thresholds;
using Content.Server.Explosion.EntitySystems;
using Content.Shared.Acts;
using Content.Shared.Damage;
using JetBrains.Annotations;
@@ -13,8 +15,12 @@ namespace Content.Server.Destructible
public class DestructibleSystem : EntitySystem
{
[Dependency] public readonly IRobustRandom Random = default!;
[Dependency] public readonly AudioSystem AudioSystem = default!;
public new IEntityManager EntityManager => base.EntityManager;
[Dependency] public readonly ActSystem ActSystem = default!;
[Dependency] public readonly AudioSystem AudioSystem = default!;
[Dependency] public readonly ConstructionSystem ConstructionSystem = default!;
[Dependency] public readonly ExplosionSystem ExplosionSystem = default!;
public override void Initialize()
{

View File

@@ -1,5 +1,4 @@
using System;
using Content.Server.Construction;
using Content.Server.Construction.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
@@ -13,12 +12,12 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
[DataField("node")]
public string Node { get; private set; } = string.Empty;
public void Execute(EntityUid owner, DestructibleSystem system, IEntityManager entityManager)
public void Execute(EntityUid owner, DestructibleSystem system)
{
if (string.IsNullOrEmpty(Node) || !entityManager.TryGetComponent(owner, out ConstructionComponent? construction))
if (string.IsNullOrEmpty(Node) || !system.EntityManager.TryGetComponent(owner, out ConstructionComponent? construction))
return;
EntitySystem.Get<ConstructionSystem>().ChangeNode(owner, null, Node, true, construction);
system.ConstructionSystem.ChangeNode(owner, null, Node, true, construction);
}
}
}

View File

@@ -21,7 +21,7 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
return (Acts & act) != 0;
}
public void Execute(EntityUid owner, DestructibleSystem system, IEntityManager entityManager)
public void Execute(EntityUid owner, DestructibleSystem system)
{
if (HasAct(ThresholdActs.Breakage))
{

View File

@@ -1,5 +1,4 @@
using System;
using Content.Server.Atmos.Piping.Unary.Components;
using Content.Server.Atmos.Piping.Unary.EntitySystems;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
@@ -8,13 +7,11 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
{
[Serializable]
[DataDefinition]
public class DumpCanisterBehavior: IThresholdBehavior
public class DumpCanisterBehavior : IThresholdBehavior
{
public void Execute(EntityUid owner, DestructibleSystem system, IEntityManager entityManager)
public void Execute(EntityUid owner, DestructibleSystem system)
{
var gasCanisterSystem = entityManager.EntitySysManager.GetEntitySystem<GasCanisterSystem>();
gasCanisterSystem.PurgeContents(owner);
system.EntityManager.EntitySysManager.GetEntitySystem<GasCanisterSystem>().PurgeContents(owner);
}
}
}

View File

@@ -10,14 +10,14 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
[DataDefinition]
public class EmptyAllContainersBehaviour : IThresholdBehavior
{
public void Execute(EntityUid owner, DestructibleSystem system, IEntityManager entityManager)
public void Execute(EntityUid owner, DestructibleSystem system)
{
if (!entityManager.TryGetComponent<ContainerManagerComponent>(owner, out var containerManager))
if (!system.EntityManager.TryGetComponent<ContainerManagerComponent>(owner, out var containerManager))
return;
foreach (var container in containerManager.GetAllContainers())
{
container.EmptyContainer(true, entityManager.GetComponent<TransformComponent>(owner).Coordinates);
container.EmptyContainer(true, system.EntityManager.GetComponent<TransformComponent>(owner).Coordinates);
}
}
}

View File

@@ -1,4 +1,3 @@
using Content.Server.Explosion;
using Content.Server.Explosion.Components;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
@@ -13,9 +12,9 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
[DataDefinition]
public class ExplodeBehavior : IThresholdBehavior
{
public void Execute(EntityUid owner, DestructibleSystem system, IEntityManager entityManager)
public void Execute(EntityUid owner, DestructibleSystem system)
{
owner.SpawnExplosion(entityManager:entityManager);
system.ExplosionSystem.SpawnExplosion(owner);
}
}
}

View File

@@ -11,9 +11,9 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
{
[DataField("recursive")] private bool _recursive = true;
public void Execute(EntityUid owner, DestructibleSystem system, IEntityManager entityManager)
public void Execute(EntityUid owner, DestructibleSystem system)
{
if (entityManager.TryGetComponent(owner, out SharedBodyComponent? body))
if (system.EntityManager.TryGetComponent(owner, out SharedBodyComponent? body))
{
body.Gib(_recursive);
}

View File

@@ -12,7 +12,6 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
/// An instance of <see cref="DestructibleSystem"/> to pull dependencies
/// and other systems from.
/// </param>
/// <param name="entityManager"></param>
void Execute(EntityUid owner, DestructibleSystem system, IEntityManager entityManager);
void Execute(EntityUid owner, DestructibleSystem system);
}
}

View File

@@ -17,9 +17,9 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
/// </summary>
[DataField("sound", required: true)] public SoundSpecifier Sound { get; set; } = default!;
public void Execute(EntityUid owner, DestructibleSystem system, IEntityManager entityManager)
public void Execute(EntityUid owner, DestructibleSystem system)
{
var pos = entityManager.GetComponent<TransformComponent>(owner).Coordinates;
var pos = system.EntityManager.GetComponent<TransformComponent>(owner).Coordinates;
SoundSystem.Play(Filter.Pvs(pos), Sound.GetSound(), pos, AudioHelpers.WithVariation(0.125f));
}
}

View File

@@ -3,7 +3,6 @@ using System.Collections.Generic;
using Content.Server.Stack;
using Content.Shared.Prototypes;
using Content.Shared.Random.Helpers;
using Content.Shared.Stacks;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
@@ -19,9 +18,9 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
[DataField("spawn")]
public Dictionary<string, MinMax> Spawn { get; set; } = new();
public void Execute(EntityUid owner, DestructibleSystem system, IEntityManager entityManager)
public void Execute(EntityUid owner, DestructibleSystem system)
{
var position = entityManager.GetComponent<TransformComponent>(owner).MapPosition;
var position = system.EntityManager.GetComponent<TransformComponent>(owner).MapPosition;
foreach (var (entityId, minMax) in Spawn)
{
@@ -33,7 +32,7 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
if (EntityPrototypeHelpers.HasComponent<StackComponent>(entityId))
{
var spawned = entityManager.SpawnEntity(entityId, position);
var spawned = system.EntityManager.SpawnEntity(entityId, position);
var stack = spawned.GetComponent<StackComponent>();
EntitySystem.Get<StackSystem>().SetCount(spawned.Uid, count, stack);
spawned.RandomOffset(0.5f);
@@ -42,7 +41,7 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
{
for (var i = 0; i < count; i++)
{
var spawned = entityManager.SpawnEntity(entityId, position);
var spawned = system.EntityManager.SpawnEntity(entityId, position);
spawned.RandomOffset(0.5f);
}
}

View File

@@ -21,13 +21,13 @@ namespace Content.Server.Destructible.Thresholds.Behaviors
/// <param name="owner">Entity on which behavior is executed</param>
/// <param name="system">system calling the behavior</param>
/// <param name="entityManager"></param>
public void Execute(EntityUid owner, DestructibleSystem system, IEntityManager entityManager)
public void Execute(EntityUid owner, DestructibleSystem system)
{
var solutionContainerSystem = EntitySystem.Get<SolutionContainerSystem>();
var coordinates = entityManager.GetComponent<TransformComponent>(owner).Coordinates;
var coordinates = system.EntityManager.GetComponent<TransformComponent>(owner).Coordinates;
if (entityManager.TryGetComponent(owner, out SpillableComponent? spillableComponent) &&
if (system.EntityManager.TryGetComponent(owner, out SpillableComponent? spillableComponent) &&
solutionContainerSystem.TryGetSolution(owner, spillableComponent.SolutionName,
out var compSolution))
{

View File

@@ -94,7 +94,7 @@ namespace Content.Server.Destructible.Thresholds
if (!entityManager.EntityExists(owner))
return;
behavior.Execute(owner, system, entityManager);
behavior.Execute(owner, system);
}
}
}

View File

@@ -1,6 +1,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Content.Server.Explosion.EntitySystems;
using Content.Server.Flash.Components;
using Content.Server.Throwing;
using Content.Shared.Explosion;

View File

@@ -1,3 +1,4 @@
using Content.Server.Explosion.EntitySystems;
using Robust.Shared.GameObjects;
namespace Content.Server.Explosion.Components

View File

@@ -15,7 +15,7 @@ namespace Content.Server.Explosion.Components
return;
var sourceLocation = eventArgs.Source;
var targetLocation = eventArgs.Target.Transform.Coordinates;
var targetLocation = Owner.EntityManager.GetComponent<TransformComponent>(eventArgs.Target).Coordinates;
if (sourceLocation.Equals(targetLocation)) return;

View File

@@ -1,4 +1,5 @@
using System;
using Content.Server.Explosion.EntitySystems;
using Content.Shared.Interaction;
using Content.Shared.Trigger;
using Robust.Server.GameObjects;

View File

@@ -1,3 +1,4 @@
using Content.Server.Explosion.EntitySystems;
using Content.Shared.Sound;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Content.Server.Camera;
@@ -21,9 +21,9 @@ using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Server.Explosion
namespace Content.Server.Explosion.EntitySystems
{
public static class ExplosionHelper
public class ExplosionSystem : EntitySystem
{
/// <summary>
/// Distance used for camera shake when distance from explosion is (0.0, 0.0).
@@ -34,19 +34,35 @@ namespace Content.Server.Explosion
/// <summary>
/// Chance of a tile breaking if the severity is Light and Heavy
/// </summary>
private static readonly float LightBreakChance = 0.3f;
private static readonly float HeavyBreakChance = 0.8f;
private static SoundSpecifier _explosionSound = new SoundCollectionSpecifier("explosion");
private const float LightBreakChance = 0.3f;
private const float HeavyBreakChance = 0.8f;
private static bool IgnoreExplosivePassable(IEntity e) => e.HasTag("ExplosivePassable");
// TODO move this to the component
private static readonly SoundSpecifier ExplosionSound = new SoundCollectionSpecifier("explosion");
private static ExplosionSeverity CalculateSeverity(float distance, float devastationRange, float heaveyRange)
[Dependency] private readonly IEntityLookup _entityLookup = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IMapManager _maps = default!;
[Dependency] private readonly IPlayerManager _players = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ITileDefinitionManager _tiles = default!;
[Dependency] private readonly ActSystem _acts = default!;
[Dependency] private readonly EffectSystem _effects = default!;
[Dependency] private readonly TriggerSystem _triggers = default!;
private bool IgnoreExplosivePassable(IEntity e)
{
return e.HasTag("ExplosivePassable");
}
private ExplosionSeverity CalculateSeverity(float distance, float devastationRange, float heavyRange)
{
if (distance < devastationRange)
{
return ExplosionSeverity.Destruction;
}
else if (distance < heaveyRange)
else if (distance < heavyRange)
{
return ExplosionSeverity.Heavy;
}
@@ -56,174 +72,10 @@ namespace Content.Server.Explosion
}
}
/// <summary>
/// Damage entities inside the range. The damage depends on a discrete
/// damage bracket [light, heavy, devastation] and the distance from the epicenter
/// </summary>
/// <returns>
/// A dictionary of coordinates relative to the parents of every grid of entities that survived the explosion,
/// have an airtight component and are currently blocking air. Like a wall.
/// </returns>
private static void DamageEntitiesInRange(EntityCoordinates epicenter, Box2 boundingBox,
float devastationRange,
float heaveyRange,
float maxRange,
MapId mapId)
private void CameraShakeInRange(EntityCoordinates epicenter, float maxRange)
{
var entityManager = IoCManager.Resolve<IEntityManager>();
var players = _players.GetPlayersInRange(epicenter, (int) Math.Ceiling(maxRange));
var exAct = EntitySystem.Get<ActSystem>();
var entitiesInRange = IoCManager.Resolve<IEntityLookup>().GetEntitiesInRange(mapId, boundingBox, 0).ToList();
var impassableEntities = new List<Tuple<IEntity, float>>();
var nonImpassableEntities = new List<Tuple<IEntity, float>>();
// TODO: Given this seems to rely on physics it should just query directly like everything else.
// The entities are paired with their distance to the epicenter
// and splitted into two lists based on if they are Impassable or not
foreach (var entity in entitiesInRange)
{
if (entity.Deleted || entity.IsInContainer())
{
continue;
}
if (!entity.Transform.Coordinates.TryDistance(entityManager, epicenter, out var distance) || distance > maxRange)
{
continue;
}
if (!entity.TryGetComponent(out PhysicsComponent? body) || body.Fixtures.Count < 1)
{
continue;
}
if ((body.CollisionLayer & (int) CollisionGroup.Impassable) != 0)
{
impassableEntities.Add(Tuple.Create(entity, distance));
}
else
{
nonImpassableEntities.Add(Tuple.Create(entity, distance));
}
}
// The Impassable entities are sorted in descending order
// Entities closer to the epicenter are first
impassableEntities.Sort((x, y) => x.Item2.CompareTo(y.Item2));
// Impassable entities are handled first. If they are damaged enough, they are destroyed and they may
// be able to spawn a new entity. I.e Wall -> Girder.
// Girder has a tag ExplosivePassable, and the predicate make it so the entities with this tag are ignored
var epicenterMapPos = epicenter.ToMap(entityManager);
foreach (var (entity, distance) in impassableEntities)
{
if (!entity.InRangeUnobstructed(epicenterMapPos, maxRange, ignoreInsideBlocker: true, predicate: IgnoreExplosivePassable))
{
continue;
}
exAct.HandleExplosion(epicenter, entity, CalculateSeverity(distance, devastationRange, heaveyRange));
}
// Impassable entities were handled first so NonImpassable entities have a bigger chance to get hit. As now
// there are probably more ExplosivePassable entities around
foreach (var (entity, distance) in nonImpassableEntities)
{
if (!entity.InRangeUnobstructed(epicenterMapPos, maxRange, ignoreInsideBlocker: true, predicate: IgnoreExplosivePassable))
{
continue;
}
exAct.HandleExplosion(epicenter, entity, CalculateSeverity(distance, devastationRange, heaveyRange));
}
}
/// <summary>
/// Damage tiles inside the range. The type of tile can change depending on a discrete
/// damage bracket [light, heavy, devastation], the distance from the epicenter and
/// a probabilty bracket [<see cref="LightBreakChance"/>, <see cref="HeavyBreakChance"/>, 1.0].
/// </summary>
///
private static void DamageTilesInRange(EntityCoordinates epicenter,
GridId gridId,
Box2 boundingBox,
float devastationRange,
float heaveyRange,
float maxRange)
{
var mapManager = IoCManager.Resolve<IMapManager>();
if (!mapManager.TryGetGrid(gridId, out var mapGrid))
{
return;
}
var entityManager = IoCManager.Resolve<IEntityManager>();
if (!entityManager.TryGetEntity(mapGrid.GridEntityId, out var grid))
{
return;
}
var robustRandom = IoCManager.Resolve<IRobustRandom>();
var tileDefinitionManager = IoCManager.Resolve<ITileDefinitionManager>();
var tilesInGridAndCircle = mapGrid.GetTilesIntersecting(boundingBox);
var epicenterMapPos = epicenter.ToMap(entityManager);
foreach (var tile in tilesInGridAndCircle)
{
var tileLoc = mapGrid.GridTileToLocal(tile.GridIndices);
if (!tileLoc.TryDistance(entityManager, epicenter, out var distance) || distance > maxRange)
{
continue;
}
if (tile.IsBlockedTurf(false))
{
continue;
}
if (!tileLoc.ToMap(entityManager).InRangeUnobstructed(epicenterMapPos, maxRange, ignoreInsideBlocker: false, predicate: IgnoreExplosivePassable))
{
continue;
}
var tileDef = (ContentTileDefinition) tileDefinitionManager[tile.Tile.TypeId];
var baseTurfs = tileDef.BaseTurfs;
if (baseTurfs.Count == 0)
{
continue;
}
var zeroTile = new Robust.Shared.Map.Tile(tileDefinitionManager[baseTurfs[0]].TileId);
var previousTile = new Robust.Shared.Map.Tile(tileDefinitionManager[baseTurfs[^1]].TileId);
var severity = CalculateSeverity(distance, devastationRange, heaveyRange);
switch (severity)
{
case ExplosionSeverity.Light:
if (!previousTile.IsEmpty && robustRandom.Prob(LightBreakChance))
{
mapGrid.SetTile(tileLoc, previousTile);
}
break;
case ExplosionSeverity.Heavy:
if (!previousTile.IsEmpty && robustRandom.Prob(HeavyBreakChance))
{
mapGrid.SetTile(tileLoc, previousTile);
}
break;
case ExplosionSeverity.Destruction:
mapGrid.SetTile(tileLoc, zeroTile);
break;
}
}
}
private static void CameraShakeInRange(EntityCoordinates epicenter, float maxRange)
{
var playerManager = IoCManager.Resolve<IPlayerManager>();
var players = playerManager.GetPlayersInRange(epicenter, (int) Math.Ceiling(maxRange));
foreach (var player in players)
{
if (player.AttachedEntity == null || !player.AttachedEntity.TryGetComponent(out CameraRecoilComponent? recoil))
@@ -231,10 +83,8 @@ namespace Content.Server.Explosion
continue;
}
var entityManager = IoCManager.Resolve<IEntityManager>();
var playerPos = player.AttachedEntity.Transform.WorldPosition;
var delta = epicenter.ToMapPos(entityManager) - playerPos;
var delta = epicenter.ToMapPos(EntityManager) - playerPos;
//Change if zero. Will result in a NaN later breaking camera shake if not changed
if (delta.EqualsApprox((0.0f, 0.0f)))
@@ -250,83 +100,250 @@ namespace Content.Server.Explosion
}
}
private static void FlashInRange(EntityCoordinates epicenter, float flashrange)
/// <summary>
/// Damage entities inside the range. The damage depends on a discrete
/// damage bracket [light, heavy, devastation] and the distance from the epicenter
/// </summary>
/// <returns>
/// A dictionary of coordinates relative to the parents of every grid of entities that survived the explosion,
/// have an airtight component and are currently blocking air. Like a wall.
/// </returns>
private void DamageEntitiesInRange(
EntityCoordinates epicenter,
Box2 boundingBox,
float devastationRange,
float heavyRange,
float maxRange,
MapId mapId)
{
if (flashrange > 0)
var entitiesInRange = _entityLookup.GetEntitiesInRange(mapId, boundingBox, 0).ToList();
var impassableEntities = new List<(IEntity, float)>();
var nonImpassableEntities = new List<(IEntity, float)>();
// TODO: Given this seems to rely on physics it should just query directly like everything else.
// The entities are paired with their distance to the epicenter
// and splitted into two lists based on if they are Impassable or not
foreach (var entity in entitiesInRange)
{
var entitySystemManager = IoCManager.Resolve<IEntitySystemManager>();
var time = IoCManager.Resolve<IGameTiming>().CurTime;
if (entity.Deleted || entity.IsInContainer())
{
continue;
}
if (!entity.Transform.Coordinates.TryDistance(EntityManager, epicenter, out var distance) ||
distance > maxRange)
{
continue;
}
if (!entity.TryGetComponent(out PhysicsComponent? body) || body.Fixtures.Count < 1)
{
continue;
}
if ((body.CollisionLayer & (int) CollisionGroup.Impassable) != 0)
{
impassableEntities.Add((entity, distance));
}
else
{
nonImpassableEntities.Add((entity, distance));
}
}
// The Impassable entities are sorted in descending order
// Entities closer to the epicenter are first
impassableEntities.Sort((x, y) => x.Item2.CompareTo(y.Item2));
// Impassable entities are handled first. If they are damaged enough, they are destroyed and they may
// be able to spawn a new entity. I.e Wall -> Girder.
// Girder has a tag ExplosivePassable, and the predicate make it so the entities with this tag are ignored
var epicenterMapPos = epicenter.ToMap(EntityManager);
foreach (var (entity, distance) in impassableEntities)
{
if (!entity.InRangeUnobstructed(epicenterMapPos, maxRange, ignoreInsideBlocker: true, predicate: IgnoreExplosivePassable))
{
continue;
}
_acts.HandleExplosion(epicenter, entity.Uid, CalculateSeverity(distance, devastationRange, heavyRange));
}
// Impassable entities were handled first so NonImpassable entities have a bigger chance to get hit. As now
// there are probably more ExplosivePassable entities around
foreach (var (entity, distance) in nonImpassableEntities)
{
if (!entity.InRangeUnobstructed(epicenterMapPos, maxRange, ignoreInsideBlocker: true, predicate: IgnoreExplosivePassable))
{
continue;
}
_acts.HandleExplosion(epicenter, entity.Uid, CalculateSeverity(distance, devastationRange, heavyRange));
}
}
/// <summary>
/// Damage tiles inside the range. The type of tile can change depending on a discrete
/// damage bracket [light, heavy, devastation], the distance from the epicenter and
/// a probability bracket [<see cref="LightBreakChance"/>, <see cref="HeavyBreakChance"/>, 1.0].
/// </summary>
///
private void DamageTilesInRange(EntityCoordinates epicenter,
GridId gridId,
Box2 boundingBox,
float devastationRange,
float heaveyRange,
float maxRange)
{
if (!_maps.TryGetGrid(gridId, out var mapGrid))
{
return;
}
if (!EntityManager.EntityExists(mapGrid.GridEntityId))
{
return;
}
var tilesInGridAndCircle = mapGrid.GetTilesIntersecting(boundingBox);
var epicenterMapPos = epicenter.ToMap(EntityManager);
foreach (var tile in tilesInGridAndCircle)
{
var tileLoc = mapGrid.GridTileToLocal(tile.GridIndices);
if (!tileLoc.TryDistance(EntityManager, epicenter, out var distance) ||
distance > maxRange)
{
continue;
}
if (tile.IsBlockedTurf(false))
{
continue;
}
if (!tileLoc.ToMap(EntityManager).InRangeUnobstructed(epicenterMapPos, maxRange, ignoreInsideBlocker: false, predicate: IgnoreExplosivePassable))
{
continue;
}
var tileDef = (ContentTileDefinition) _tiles[tile.Tile.TypeId];
var baseTurfs = tileDef.BaseTurfs;
if (baseTurfs.Count == 0)
{
continue;
}
var zeroTile = new Tile(_tiles[baseTurfs[0]].TileId);
var previousTile = new Tile(_tiles[baseTurfs[^1]].TileId);
var severity = CalculateSeverity(distance, devastationRange, heaveyRange);
switch (severity)
{
case ExplosionSeverity.Light:
if (!previousTile.IsEmpty && _random.Prob(LightBreakChance))
{
mapGrid.SetTile(tileLoc, previousTile);
}
break;
case ExplosionSeverity.Heavy:
if (!previousTile.IsEmpty && _random.Prob(HeavyBreakChance))
{
mapGrid.SetTile(tileLoc, previousTile);
}
break;
case ExplosionSeverity.Destruction:
mapGrid.SetTile(tileLoc, zeroTile);
break;
}
}
}
private void FlashInRange(EntityCoordinates epicenter, float flashRange)
{
if (flashRange > 0)
{
var time = _timing.CurTime;
var message = new EffectSystemMessage
{
EffectSprite = "Effects/explosion.rsi",
RsiState = "explosionfast",
Born = time,
DeathTime = time + TimeSpan.FromSeconds(5),
Size = new Vector2(flashrange / 2, flashrange / 2),
Size = new Vector2(flashRange / 2, flashRange / 2),
Coordinates = epicenter,
Rotation = 0f,
ColorDelta = new Vector4(0, 0, 0, -1500f),
Color = Vector4.Multiply(new Vector4(255, 255, 255, 750), 0.5f),
Shaded = false
};
entitySystemManager.GetEntitySystem<EffectSystem>().CreateParticle(message);
_effects.CreateParticle(message);
}
}
// TODO: remove this shit
public static void SpawnExplosion(this EntityUid uid, int devastationRange = 0, int heavyImpactRange = 0,
int lightImpactRange = 0, int flashRange = 0, IEntityManager? entityManager = null)
public void SpawnExplosion(
EntityUid entity,
int devastationRange = 0,
int heavyImpactRange = 0,
int lightImpactRange = 0,
int flashRange = 0,
ExplosiveComponent? explosive = null,
TransformComponent? transform = null)
{
entityManager ??= IoCManager.Resolve<IEntityManager>();
SpawnExplosion(entityManager.GetEntity(uid), devastationRange, heavyImpactRange, lightImpactRange, flashRange);
if (!Resolve(entity, ref transform))
{
return;
}
public static void SpawnExplosion(this IEntity entity, int devastationRange = 0, int heavyImpactRange = 0,
int lightImpactRange = 0, int flashRange = 0)
{
// TODO: Need to refactor this stufferino
Resolve(entity, ref explosive, false);
// If you want to directly set off the explosive
if (!entity.Deleted && entity.TryGetComponent(out ExplosiveComponent? explosive) && !explosive.Exploding)
if (explosive is { Exploding: false })
{
EntitySystem.Get<TriggerSystem>().Explode(entity.Uid, explosive);
_triggers.Explode(entity, explosive);
}
else
{
while (entity.TryGetContainer(out var cont))
while (EntityManager.TryGetComponent(entity, out ContainerManagerComponent? container))
{
entity = cont.Owner;
entity = container.OwnerUid;
}
var epicenter = entity.Transform.Coordinates;
if (!EntityManager.TryGetComponent(entity, out transform))
{
return;
}
var epicenter = transform.Coordinates;
SpawnExplosion(epicenter, devastationRange, heavyImpactRange, lightImpactRange, flashRange);
}
}
public static void SpawnExplosion(EntityCoordinates epicenter, int devastationRange = 0,
int heavyImpactRange = 0, int lightImpactRange = 0, int flashRange = 0)
public void SpawnExplosion(
EntityCoordinates epicenter,
int devastationRange = 0,
int heavyImpactRange = 0,
int lightImpactRange = 0,
int flashRange = 0)
{
var mapId = epicenter.GetMapId(IoCManager.Resolve<IEntityManager>());
var mapId = epicenter.GetMapId(EntityManager);
if (mapId == MapId.Nullspace)
{
return;
}
var maxRange = MathHelper.Max(devastationRange, heavyImpactRange, lightImpactRange, 0);
var entityManager = IoCManager.Resolve<IEntityManager>();
var mapManager = IoCManager.Resolve<IMapManager>();
var epicenterMapPos = epicenter.ToMapPos(entityManager);
var epicenterMapPos = epicenter.ToMapPos(EntityManager);
var boundingBox = new Box2(epicenterMapPos - new Vector2(maxRange, maxRange),
epicenterMapPos + new Vector2(maxRange, maxRange));
SoundSystem.Play(Filter.Broadcast(), _explosionSound.GetSound(), epicenter);
SoundSystem.Play(Filter.Broadcast(), ExplosionSound.GetSound(), epicenter);
DamageEntitiesInRange(epicenter, boundingBox, devastationRange, heavyImpactRange, maxRange, mapId);
var mapGridsNear = mapManager.FindGridsIntersecting(mapId, boundingBox);
var mapGridsNear = _maps.FindGridsIntersecting(mapId, boundingBox);
foreach (var gridId in mapGridsNear)
{

View File

@@ -3,7 +3,6 @@ using Content.Server.Doors.Components;
using Content.Server.Explosion.Components;
using Content.Server.Flash;
using Content.Server.Flash.Components;
using Content.Shared.Acts;
using Content.Shared.Audio;
using Content.Shared.Doors;
using JetBrains.Annotations;
@@ -14,7 +13,7 @@ using Robust.Shared.Physics.Dynamics;
using Robust.Shared.Player;
using Robust.Shared.Timing;
namespace Content.Server.Explosion
namespace Content.Server.Explosion.EntitySystems
{
/// <summary>
/// Raised whenever something is Triggered on the entity.
@@ -34,6 +33,7 @@ namespace Content.Server.Explosion
[UsedImplicitly]
public sealed class TriggerSystem : EntitySystem
{
[Dependency] private readonly ExplosionSystem _explosions = default!;
[Dependency] private readonly FlashSystem _flashSystem = default!;
public override void Initialize()
@@ -65,7 +65,7 @@ namespace Content.Server.Explosion
}
component.Exploding = true;
component.Owner.SpawnExplosion(component.DevastationRange, component.HeavyImpactRange, component.LightImpactRange, component.FlashRange);
_explosions.SpawnExplosion(uid, component.DevastationRange, component.HeavyImpactRange, component.LightImpactRange, component.FlashRange);
EntityManager.QueueDeleteEntity(uid);
}
#endregion

View File

@@ -1,4 +1,4 @@
using Content.Server.Explosion;
using Content.Server.Explosion.EntitySystems;
using Content.Server.MachineLinking.Components;
using Content.Server.MachineLinking.Events;
using Robust.Shared.GameObjects;

View File

@@ -1,5 +1,5 @@
using System.Linq;
using Content.Server.Explosion;
using Content.Server.Explosion.EntitySystems;
using Content.Server.Pointing.Components;
using Content.Shared.MobState.Components;
using Content.Shared.Pointing.Components;
@@ -22,6 +22,8 @@ namespace Content.Server.Pointing.EntitySystems
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly ExplosionSystem _explosions = default!;
public override void Initialize()
{
base.Initialize();
@@ -106,7 +108,7 @@ namespace Content.Server.Pointing.EntitySystems
return;
}
component.Owner.SpawnExplosion(0, 2, 1, 1);
_explosions.SpawnExplosion(uid, 0, 2, 1, 1);
SoundSystem.Play(Filter.Pvs(uid, entityManager: EntityManager), component.ExplosionSound.GetSound(), uid);
EntityManager.QueueDeleteEntity(uid);

View File

@@ -1,8 +1,6 @@
using System;
using Content.Server.Chemistry.Components;
using Content.Server.Explosion;
using Content.Server.Explosion.EntitySystems;
using Content.Server.Power.Components;
using Content.Shared.Chemistry;
using Content.Shared.Examine;
using Content.Shared.PowerCell;
using Content.Shared.Rounding;
@@ -75,7 +73,7 @@ namespace Content.Server.PowerCell.Components
var light = (int) Math.Ceiling(Math.Sqrt(CurrentCharge) / 30);
CurrentCharge = 0;
Owner.SpawnExplosion(0, heavy, light, light*2);
EntitySystem.Get<ExplosionSystem>().SpawnExplosion(OwnerUid, 0, heavy, light, light*2);
Owner.Delete();
}

View File

@@ -46,7 +46,7 @@ namespace Content.Shared.Acts
public class ExplosionEventArgs : EventArgs
{
public EntityCoordinates Source { get; set; }
public IEntity Target { get; set; } = default!;
public EntityUid Target { get; set; }
public ExplosionSeverity Severity { get; set; }
}
@@ -70,7 +70,7 @@ namespace Content.Shared.Acts
EntityManager.QueueDeleteEntity(owner);
}
public void HandleExplosion(EntityCoordinates source, IEntity target, ExplosionSeverity severity)
public void HandleExplosion(EntityCoordinates source, EntityUid target, ExplosionSeverity severity)
{
var eventArgs = new ExplosionEventArgs
{
@@ -78,7 +78,7 @@ namespace Content.Shared.Acts
Target = target,
Severity = severity
};
var exActs = target.GetAllComponents<IExAct>().ToList();
var exActs = EntityManager.GetComponents<IExAct>(target).ToList();
foreach (var exAct in exActs)
{