Fixes to explosionhelper (#2819)
* Revert "Make handheld explosives affect tiles (#2806)"
This reverts commit 005e142949.
* Fixes tiles being destroyed under walls by an explosion
* Extra imports removed
* Handles explosion in space and different grids
This handle explosions across different grids, and tiles are still protected if there is an entity airtight and currently blocking air on top of them that survived the explosion.
* Some bug fixes
- The way tiles were being protected was silly.
- Big explosions cause a lot of objects to trigger multiple events and at the same time they are destroyed.
- Explosions spawning inside containers like closets work now.
* Range bug fixes
* Explosive
The explosion works even if the entity exploding is inside multiple 'layers' of containers like.
bomb -> survival box -> tool box -> closet
* Explosions are different now
Explosion can't jump over walls now.
Explosions work like rays now, if an explosion breaks a wall it can scatter inside the room.
If entities are behind impassable entities that survive the blast they are left unscathed.
* Little fix
* Remove the extra lookup of tiles
* Another small change
* Restore the second lookup
I thought this was extra, but this protects the tile under it if there is an Impassable entity on top. None wants anchored girders on top of lattice/space
* Changing order of conditions
IsBlockedTurf is cheaper to run than InRangeUnobstructed.
* Yep
This commit is contained in:
committed by
GitHub
parent
cd9e5a590b
commit
cc4669244d
@@ -1,20 +1,33 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Content.Server.GameObjects.Components.Atmos;
|
||||||
|
using Content.Server.GameObjects.Components.Explosion;
|
||||||
|
using Content.Server.GameObjects.Components.Items.Storage;
|
||||||
using Content.Server.GameObjects.Components.Mobs;
|
using Content.Server.GameObjects.Components.Mobs;
|
||||||
using Content.Shared.GameObjects.EntitySystems;
|
using Content.Shared.GameObjects.EntitySystems;
|
||||||
using Content.Shared.Maps;
|
using Content.Shared.Maps;
|
||||||
|
using Content.Shared.Physics;
|
||||||
|
using Content.Shared.Utility;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Robust.Server.GameObjects.EntitySystems;
|
using Robust.Server.GameObjects.EntitySystems;
|
||||||
using Robust.Server.Interfaces.GameObjects;
|
using Robust.Server.Interfaces.GameObjects;
|
||||||
using Robust.Server.Interfaces.Player;
|
using Robust.Server.Interfaces.Player;
|
||||||
|
using Robust.Shared.Containers;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects.Components;
|
||||||
using Robust.Shared.GameObjects.EntitySystemMessages;
|
using Robust.Shared.GameObjects.EntitySystemMessages;
|
||||||
using Robust.Shared.Interfaces.GameObjects;
|
using Robust.Shared.Interfaces.GameObjects;
|
||||||
using Robust.Shared.Interfaces.Map;
|
using Robust.Shared.Interfaces.Map;
|
||||||
using Robust.Shared.Interfaces.Random;
|
using Robust.Shared.Interfaces.Random;
|
||||||
using Robust.Shared.Interfaces.Timing;
|
using Robust.Shared.Interfaces.Timing;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Log;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Physics;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Server.Explosions
|
namespace Content.Server.Explosions
|
||||||
{
|
{
|
||||||
@@ -26,62 +39,162 @@ namespace Content.Server.Explosions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private static readonly Vector2 EpicenterDistance = (0.1f, 0.1f);
|
private static readonly Vector2 EpicenterDistance = (0.1f, 0.1f);
|
||||||
|
|
||||||
public static void SpawnExplosion(this EntityCoordinates coords, int devastationRange, int heavyImpactRange, int lightImpactRange, int flashRange)
|
/// <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 bool IgnoreExplosivePassable(IEntity e) => (e.GetComponent<IPhysicsComponent>().CollisionLayer & (int) CollisionGroup.ExplosivePassable) != 0;
|
||||||
|
|
||||||
|
private static ExplosionSeverity CalculateSeverity(float distance, float devastationRange, float heaveyRange)
|
||||||
{
|
{
|
||||||
var tileDefinitionManager = IoCManager.Resolve<ITileDefinitionManager>();
|
|
||||||
var serverEntityManager = IoCManager.Resolve<IServerEntityManager>();
|
|
||||||
var entitySystemManager = IoCManager.Resolve<IEntitySystemManager>();
|
|
||||||
var mapManager = IoCManager.Resolve<IMapManager>();
|
|
||||||
var robustRandom = IoCManager.Resolve<IRobustRandom>();
|
|
||||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
|
||||||
|
|
||||||
var maxRange = MathHelper.Max(devastationRange, heavyImpactRange, lightImpactRange, 0f);
|
|
||||||
//Entity damage calculation
|
|
||||||
var entitiesAll = serverEntityManager.GetEntitiesInRange(coords, maxRange).ToList();
|
|
||||||
|
|
||||||
foreach (var entity in entitiesAll)
|
|
||||||
{
|
|
||||||
if (entity.Deleted)
|
|
||||||
continue;
|
|
||||||
if (!entity.Transform.IsMapTransform)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!entity.Transform.Coordinates.TryDistance(entityManager, coords, out var distance))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExplosionSeverity severity;
|
|
||||||
if (distance < devastationRange)
|
if (distance < devastationRange)
|
||||||
{
|
{
|
||||||
severity = ExplosionSeverity.Destruction;
|
return ExplosionSeverity.Destruction;
|
||||||
}
|
}
|
||||||
else if (distance < heavyImpactRange)
|
else if (distance < heaveyRange)
|
||||||
{
|
{
|
||||||
severity = ExplosionSeverity.Heavy;
|
return ExplosionSeverity.Heavy;
|
||||||
}
|
|
||||||
else if (distance < lightImpactRange)
|
|
||||||
{
|
|
||||||
severity = ExplosionSeverity.Light;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
continue;
|
return ExplosionSeverity.Light;
|
||||||
}
|
}
|
||||||
var exAct = entitySystemManager.GetEntitySystem<ActSystem>();
|
|
||||||
//exAct.HandleExplosion(Owner, entity, severity);
|
|
||||||
exAct.HandleExplosion(coords, entity, severity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//Tile damage calculation mockup
|
/// <summary>
|
||||||
//TODO: make it into some sort of actual damage component or whatever the boys think is appropriate
|
/// Damage entities inside the range. The damage depends on a discrete
|
||||||
if (mapManager.TryGetGrid(coords.GetGridId(entityManager), out var mapGrid))
|
/// 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)
|
||||||
{
|
{
|
||||||
var circle = new Circle(coords.ToMapPos(entityManager), maxRange);
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
var tiles = mapGrid?.GetTilesIntersecting(circle);
|
var serverEntityManager = IoCManager.Resolve<IServerEntityManager>();
|
||||||
foreach (var tile in tiles)
|
var entitySystemManager = IoCManager.Resolve<IEntitySystemManager>();
|
||||||
|
|
||||||
|
var exAct = entitySystemManager.GetEntitySystem<ActSystem>();
|
||||||
|
|
||||||
|
var entitiesInRange = serverEntityManager.GetEntitiesInRange(mapId, boundingBox, 0).ToList();
|
||||||
|
|
||||||
|
var impassableEntities = new List<Tuple<IEntity, float>>();
|
||||||
|
var nonImpassableEntities = new List<Tuple<IEntity, float>>();
|
||||||
|
|
||||||
|
// 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.Transform.IsMapTransform)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entity.Transform.Coordinates.TryDistance(entityManager, epicenter, out var distance) || distance > maxRange)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!entity.TryGetComponent(out IPhysicsComponent body) || body.PhysicsShapes.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 layer ExplosivePassable, and the predicate make it so the entities with this layer 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);
|
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 tileDef = (ContentTileDefinition) tileDefinitionManager[tile.Tile.TypeId];
|
||||||
var baseTurfs = tileDef.BaseTurfs;
|
var baseTurfs = tileDef.BaseTurfs;
|
||||||
if (baseTurfs.Count == 0)
|
if (baseTurfs.Count == 0)
|
||||||
@@ -89,66 +202,48 @@ namespace Content.Server.Explosions
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!tileLoc.TryDistance(entityManager, coords, out var distance))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var zeroTile = new Tile(tileDefinitionManager[baseTurfs[0]].TileId);
|
var zeroTile = new Tile(tileDefinitionManager[baseTurfs[0]].TileId);
|
||||||
var previousTile = new Tile(tileDefinitionManager[baseTurfs[^1]].TileId);
|
var previousTile = new Tile(tileDefinitionManager[baseTurfs[^1]].TileId);
|
||||||
|
|
||||||
switch (distance)
|
var severity = CalculateSeverity(distance, devastationRange, heaveyRange);
|
||||||
|
|
||||||
|
switch (severity)
|
||||||
{
|
{
|
||||||
case var d when d < devastationRange:
|
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);
|
mapGrid.SetTile(tileLoc, zeroTile);
|
||||||
break;
|
break;
|
||||||
case var d when d < heavyImpactRange
|
|
||||||
&& !previousTile.IsEmpty
|
|
||||||
&& robustRandom.Prob(0.8f):
|
|
||||||
mapGrid.SetTile(tileLoc, previousTile);
|
|
||||||
break;
|
|
||||||
case var d when d < lightImpactRange
|
|
||||||
&& !previousTile.IsEmpty
|
|
||||||
&& robustRandom.Prob(0.5f):
|
|
||||||
mapGrid.SetTile(tileLoc, previousTile);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Effects and sounds
|
private static void CameraShakeInRange(EntityCoordinates epicenter, float maxRange)
|
||||||
var time = IoCManager.Resolve<IGameTiming>().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),
|
|
||||||
Coordinates = coords,
|
|
||||||
//Rotated from east facing
|
|
||||||
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);
|
|
||||||
entitySystemManager.GetEntitySystem<AudioSystem>().PlayAtCoords("/Audio/Effects/explosion.ogg", coords);
|
|
||||||
|
|
||||||
// Knock back cameras of all players in the area.
|
|
||||||
|
|
||||||
var playerManager = IoCManager.Resolve<IPlayerManager>();
|
var playerManager = IoCManager.Resolve<IPlayerManager>();
|
||||||
foreach (var player in playerManager.GetAllPlayers())
|
var players = playerManager.GetPlayersInRange(epicenter, (int) Math.Ceiling(maxRange));
|
||||||
|
foreach (var player in players)
|
||||||
{
|
{
|
||||||
if (player.AttachedEntity == null
|
if (player.AttachedEntity == null || !player.AttachedEntity.TryGetComponent(out CameraRecoilComponent recoil))
|
||||||
|| player.AttachedEntity.Transform.MapID != mapGrid.ParentMapId
|
|
||||||
|| !player.AttachedEntity.TryGetComponent(out CameraRecoilComponent recoil))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
|
|
||||||
var playerPos = player.AttachedEntity.Transform.WorldPosition;
|
var playerPos = player.AttachedEntity.Transform.WorldPosition;
|
||||||
var delta = coords.ToMapPos(entityManager) - playerPos;
|
var delta = epicenter.ToMapPos(entityManager) - playerPos;
|
||||||
|
|
||||||
//Change if zero. Will result in a NaN later breaking camera shake if not changed
|
//Change if zero. Will result in a NaN later breaking camera shake if not changed
|
||||||
if (delta.EqualsApprox((0.0f, 0.0f)))
|
if (delta.EqualsApprox((0.0f, 0.0f)))
|
||||||
delta = EpicenterDistance;
|
delta = EpicenterDistance;
|
||||||
@@ -163,10 +258,75 @@ namespace Content.Server.Explosions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SpawnExplosion(this IEntity entity, int devastationRange, int heavyImpactRange,
|
private static void FlashInRange(EntityCoordinates epicenter, float flashrange)
|
||||||
int lightImpactRange, int flashRange)
|
|
||||||
{
|
{
|
||||||
entity.Transform.Coordinates.SpawnExplosion(devastationRange, heavyImpactRange, lightImpactRange, flashRange);
|
if (flashrange > 0)
|
||||||
|
{
|
||||||
|
var entitySystemManager = IoCManager.Resolve<IEntitySystemManager>();
|
||||||
|
var time = IoCManager.Resolve<IGameTiming>().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),
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Detonate(IEntity source, int devastationRange, int heavyImpactRange, int lightImpactRange, int flashRange)
|
||||||
|
{
|
||||||
|
var mapId = source.Transform.MapID;
|
||||||
|
if (mapId == MapId.Nullspace)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var maxRange = MathHelper.Max(devastationRange, heavyImpactRange, lightImpactRange, 0);
|
||||||
|
|
||||||
|
while(source.TryGetContainer(out var cont))
|
||||||
|
{
|
||||||
|
source = cont.Owner;
|
||||||
|
}
|
||||||
|
var epicenter = source.Transform.Coordinates;
|
||||||
|
|
||||||
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
|
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||||
|
|
||||||
|
var epicenterMapPos = epicenter.ToMapPos(entityManager);
|
||||||
|
var boundingBox = new Box2(epicenterMapPos - new Vector2(maxRange, maxRange), epicenterMapPos + new Vector2(maxRange, maxRange));
|
||||||
|
|
||||||
|
DamageEntitiesInRange(epicenter, boundingBox, devastationRange, heavyImpactRange, maxRange, mapId);
|
||||||
|
|
||||||
|
var mapGridsNear = mapManager.FindGridsIntersecting(mapId, boundingBox);
|
||||||
|
|
||||||
|
foreach (var gridId in mapGridsNear)
|
||||||
|
{
|
||||||
|
DamageTilesInRange(epicenter, gridId.Index, boundingBox, devastationRange, heavyImpactRange, maxRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
CameraShakeInRange(epicenter, maxRange);
|
||||||
|
FlashInRange(epicenter, flashRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SpawnExplosion(this IEntity entity, int devastationRange = 0, int heavyImpactRange = 0, int lightImpactRange = 0, int flashRange = 0)
|
||||||
|
{
|
||||||
|
// If you want to directly set off the explosive
|
||||||
|
if (!entity.Deleted && entity.TryGetComponent(out ExplosiveComponent explosive) && !explosive.Exploding)
|
||||||
|
{
|
||||||
|
explosive.Explosion();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Detonate(entity, devastationRange, heavyImpactRange, lightImpactRange, flashRange);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Server.Explosions;
|
using Content.Server.Explosions;
|
||||||
using Content.Server.GameObjects.EntitySystems;
|
using Content.Server.GameObjects.EntitySystems;
|
||||||
using Content.Shared.GameObjects.EntitySystems;
|
using Content.Shared.GameObjects.EntitySystems;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
@@ -16,7 +16,7 @@ namespace Content.Server.GameObjects.Components.Explosion
|
|||||||
public int LightImpactRange = 0;
|
public int LightImpactRange = 0;
|
||||||
public int FlashRange = 0;
|
public int FlashRange = 0;
|
||||||
|
|
||||||
private bool _beingExploded = false;
|
public bool Exploding { get; private set; } = false;
|
||||||
|
|
||||||
public override void ExposeData(ObjectSerializer serializer)
|
public override void ExposeData(ObjectSerializer serializer)
|
||||||
{
|
{
|
||||||
@@ -30,15 +30,18 @@ namespace Content.Server.GameObjects.Components.Explosion
|
|||||||
|
|
||||||
public bool Explosion()
|
public bool Explosion()
|
||||||
{
|
{
|
||||||
//Prevent adjacent explosives from infinitely blowing each other up.
|
if (Exploding)
|
||||||
if (_beingExploded) return true;
|
{
|
||||||
_beingExploded = true;
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Exploding = true;
|
||||||
Owner.SpawnExplosion(DevastationRange, HeavyImpactRange, LightImpactRange, FlashRange);
|
Owner.SpawnExplosion(DevastationRange, HeavyImpactRange, LightImpactRange, FlashRange);
|
||||||
|
|
||||||
Owner.Delete();
|
Owner.Delete();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool ITimerTrigger.Trigger(TimerTriggerEventArgs eventArgs)
|
bool ITimerTrigger.Trigger(TimerTriggerEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#nullable enable
|
#nullable enable
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -470,7 +470,8 @@ namespace Content.Server.GameObjects.Components.Items.Storage
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var entity in Contents.ContainedEntities)
|
var containedEntities = Contents.ContainedEntities.ToList();
|
||||||
|
foreach (var entity in containedEntities)
|
||||||
{
|
{
|
||||||
var exActs = entity.GetAllComponents<IExAct>().ToArray();
|
var exActs = entity.GetAllComponents<IExAct>().ToArray();
|
||||||
foreach (var exAct in exActs)
|
foreach (var exAct in exActs)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace Content.Server.GameObjects.Components.Projectiles
|
|||||||
|
|
||||||
void ICollideBehavior.CollideWith(IEntity entity)
|
void ICollideBehavior.CollideWith(IEntity entity)
|
||||||
{
|
{
|
||||||
if (!_shouldCollide) return;
|
if (!_shouldCollide || entity.Deleted) return;
|
||||||
if (entity.TryGetComponent(out PhysicsComponent collid))
|
if (entity.TryGetComponent(out PhysicsComponent collid))
|
||||||
{
|
{
|
||||||
if (!collid.Hard) // ignore non hard
|
if (!collid.Hard) // ignore non hard
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using Content.Server.GameObjects.Components.Projectiles;
|
using Content.Server.GameObjects.Components.Projectiles;
|
||||||
using Content.Shared.GameObjects.Components.Movement;
|
using Content.Shared.GameObjects.Components.Movement;
|
||||||
using Content.Shared.GameObjects.EntitySystems;
|
using Content.Shared.GameObjects.EntitySystems;
|
||||||
@@ -43,10 +43,21 @@ namespace Content.Server.Throw
|
|||||||
/// </param>
|
/// </param>
|
||||||
public static void Throw(this IEntity thrownEnt, float throwForce, EntityCoordinates targetLoc, EntityCoordinates sourceLoc, bool spread = false, IEntity throwSourceEnt = null)
|
public static void Throw(this IEntity thrownEnt, float throwForce, EntityCoordinates targetLoc, EntityCoordinates sourceLoc, bool spread = false, IEntity throwSourceEnt = null)
|
||||||
{
|
{
|
||||||
|
if (thrownEnt.Deleted)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!thrownEnt.TryGetComponent(out IPhysicsComponent colComp))
|
if (!thrownEnt.TryGetComponent(out IPhysicsComponent colComp))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
|
var direction_vector = targetLoc.ToMapPos(entityManager) - sourceLoc.ToMapPos(entityManager);
|
||||||
|
|
||||||
|
if (direction_vector.Length == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
colComp.CanCollide = true;
|
colComp.CanCollide = true;
|
||||||
// I can now collide with player, so that i can do damage.
|
// I can now collide with player, so that i can do damage.
|
||||||
@@ -61,8 +72,8 @@ namespace Content.Server.Throw
|
|||||||
colComp.PhysicsShapes[0].CollisionMask |= (int) CollisionGroup.ThrownItem;
|
colComp.PhysicsShapes[0].CollisionMask |= (int) CollisionGroup.ThrownItem;
|
||||||
colComp.Status = BodyStatus.InAir;
|
colComp.Status = BodyStatus.InAir;
|
||||||
}
|
}
|
||||||
var angle = new Angle(targetLoc.ToMapPos(entityManager) - sourceLoc.ToMapPos(entityManager));
|
|
||||||
|
|
||||||
|
var angle = new Angle(direction_vector);
|
||||||
if (spread)
|
if (spread)
|
||||||
{
|
{
|
||||||
var spreadRandom = IoCManager.Resolve<IRobustRandom>();
|
var spreadRandom = IoCManager.Resolve<IRobustRandom>();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
@@ -23,6 +23,7 @@ namespace Content.Shared.Physics
|
|||||||
GhostImpassable = 1 << 6, // 64 Things impassible by ghosts/observers, ie blessed tiles or forcefields
|
GhostImpassable = 1 << 6, // 64 Things impassible by ghosts/observers, ie blessed tiles or forcefields
|
||||||
Underplating = 1 << 7, // 128 Things that are under plating
|
Underplating = 1 << 7, // 128 Things that are under plating
|
||||||
Passable = 1 << 8, // 256 Things that are passable
|
Passable = 1 << 8, // 256 Things that are passable
|
||||||
|
ExplosivePassable = 1 << 9, // 512 Things that let the pressure of a explosion through
|
||||||
MapGrid = MapGridHelpers.CollisionGroup, // Map grids, like shuttles. This is the actual grid itself, not the walls or other entities connected to the grid.
|
MapGrid = MapGridHelpers.CollisionGroup, // Map grids, like shuttles. This is the actual grid itself, not the walls or other entities connected to the grid.
|
||||||
|
|
||||||
MobMask = Impassable | MobImpassable | VaultImpassable | SmallImpassable,
|
MobMask = Impassable | MobImpassable | VaultImpassable | SmallImpassable,
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
- MobImpassable
|
- MobImpassable
|
||||||
- VaultImpassable
|
- VaultImpassable
|
||||||
- SmallImpassable
|
- SmallImpassable
|
||||||
|
- ExplosivePassable
|
||||||
- type: Pullable
|
- type: Pullable
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
resistances: metallicResistances
|
resistances: metallicResistances
|
||||||
|
|||||||
Reference in New Issue
Block a user