Files
tbd-station-14/Content.Server/Explosions/ExplosionHelper.cs
moneyl 427836fec9 Add basic chemical reactions (#376)
* Add basic chemical reaction system

What it adds:
- Reactions defined in yaml with an arbitrary amount of reactants (can be catalysts), products, and effects.

What it doesn't add:
- Temperature dependent reactions
- Metabolism or other medical/health effects

* Add many common SS13 chemicals and reactions

Added many of the common SS13 medicines and other chemicals, and their chemical reactions. Note that many of them are lacking their effects since we don't have medical yet.

* Add ExplosiveReactionEffect

Shows how IReactionEffect can be implemented to have effects that occur with a reaction by adding ExplosionReactionEffect and the potassium + water explosion reaction.

* Move ReactionSystem logic into SolutionComponent

No need for this to be a system currently so the behavior for reaction checking has been moved into SolutionComponent. Now it only checks for reactions when a reagent or solution is added to a solution. Also fixed a bug with SolutionValidReaction incorrectly returning true.

* Move explosion logic out of ExplosiveComponent

Allows you to create explosions without needing to add an ExplosiveComponent to an entity first.

* Add SolutionComponent.SolutionChanged event. Trigger dispenser ui updates with it.

This removes the need for SolutionComponent having a reference to the dispenser it's in. Instead the dispenser subscribes to the event and updates it's UI whenever the event is triggered.

* Add forgotten checks

`SolutionComponent.TryAddReagent` and `SolutionComponent.TryAddSolution` now check to see if `skipReactionCheck` is false before checking for a chemical reaction to avoid unnecessarily checking themselves for a reaction again when `SolutionComponent.PerformReaction` calls either of them.

* Change SolutionComponent.SolutionChanged to an Action

The arguments for event handler have no use here, and any class that can access SolutionChanged can access the SolutionComponent so it can just be an Action with no arguments instead.
2019-10-11 22:57:16 +02:00

149 lines
6.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Content.Server.GameObjects.Components.Mobs;
using Content.Server.GameObjects.EntitySystems;
using Content.Shared.Maps;
using Robust.Server.GameObjects.EntitySystems;
using Robust.Server.Interfaces.GameObjects;
using Robust.Server.Interfaces.Player;
using Robust.Shared.GameObjects.EntitySystemMessages;
using Robust.Shared.Interfaces.GameObjects;
using Robust.Shared.Interfaces.Map;
using Robust.Shared.Interfaces.Random;
using Robust.Shared.Interfaces.Timing;
using Robust.Shared.IoC;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Random;
namespace Content.Server.Explosions
{
public static class ExplosionHelper
{
public static void SpawnExplosion(GridCoordinates coords, int devastationRange, int heavyImpactRange, int lightImpactRange, int flashRange)
{
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 maxRange = MathHelper.Max(devastationRange, heavyImpactRange, lightImpactRange, 0f);
//Entity damage calculation
var entitiesAll = serverEntityManager.GetEntitiesInRange(coords, maxRange).ToList();
foreach (var entity in entitiesAll)
{
//if (entity == Owner)
// continue;
if (!entity.Transform.IsMapTransform)
continue;
var distanceFromEntity = (int)entity.Transform.GridPosition.Distance(mapManager, coords);
var exAct = entitySystemManager.GetEntitySystem<ActSystem>();
var severity = ExplosionSeverity.Destruction;
if (distanceFromEntity < devastationRange)
{
severity = ExplosionSeverity.Destruction;
}
else if (distanceFromEntity < heavyImpactRange)
{
severity = ExplosionSeverity.Heavy;
}
else if (distanceFromEntity < lightImpactRange)
{
severity = ExplosionSeverity.Light;
}
else
{
continue;
}
//exAct.HandleExplosion(Owner, entity, severity);
exAct.HandleExplosion(null, entity, severity);
}
//Tile damage calculation mockup
//TODO: make it into some sort of actual damage component or whatever the boys think is appropriate
var mapGrid = mapManager.GetGrid(coords.GridID);
var circle = new Circle(coords.Position, maxRange);
var tiles = mapGrid.GetTilesIntersecting(circle);
foreach (var tile in tiles)
{
var tileLoc = mapGrid.GridTileToLocal(tile.GridIndices);
var tileDef = (ContentTileDefinition)tileDefinitionManager[tile.Tile.TypeId];
var distanceFromTile = (int)tileLoc.Distance(mapManager, coords);
if (!string.IsNullOrWhiteSpace(tileDef.SubFloor))
{
if (distanceFromTile < devastationRange)
mapGrid.SetTile(tileLoc, new Tile(tileDefinitionManager["space"].TileId));
if (distanceFromTile < heavyImpactRange)
{
if (robustRandom.Prob(80))
{
mapGrid.SetTile(tileLoc, new Tile(tileDefinitionManager[tileDef.SubFloor].TileId));
}
else
{
mapGrid.SetTile(tileLoc, new Tile(tileDefinitionManager["space"].TileId));
}
}
if (distanceFromTile < lightImpactRange)
{
if (robustRandom.Prob(50))
{
mapGrid.SetTile(tileLoc, new Tile(tileDefinitionManager[tileDef.SubFloor].TileId));
}
}
}
}
//Effects and sounds
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>().Play("/Audio/effects/explosion.ogg", coords);
// Knock back cameras of all players in the area.
var playerManager = IoCManager.Resolve<IPlayerManager>();
//var selfPos = Owner.Transform.WorldPosition; //vec2
var selfPos = coords.ToWorld(mapManager).Position;
foreach (var player in playerManager.GetAllPlayers())
{
if (player.AttachedEntity == null
|| player.AttachedEntity.Transform.MapID != mapGrid.ParentMapId
|| !player.AttachedEntity.TryGetComponent(out CameraRecoilComponent recoil))
{
continue;
}
var playerPos = player.AttachedEntity.Transform.WorldPosition;
var delta = selfPos - playerPos;
var distance = delta.LengthSquared;
var effect = 1 / (1 + 0.2f * distance);
if (effect > 0.01f)
{
var kick = -delta.Normalized * effect;
recoil.Kick(kick);
}
}
}
}
}