Optimize & clean up RadiationSystem (#34459)
* Optimize & clean up RadiationSystem * comments * Update Content.Server/Radiation/Systems/RadiationSystem.GridCast.cs Co-authored-by: Thomas <87614336+Aeshus@users.noreply.github.com> --------- Co-authored-by: Thomas <87614336+Aeshus@users.noreply.github.com>
This commit is contained in:
@@ -9,7 +9,7 @@ public sealed class RadiationSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
[Dependency] private readonly IOverlayManager _overlayMan = default!;
|
||||||
|
|
||||||
public List<RadiationRay>? Rays;
|
public List<DebugRadiationRay>? Rays;
|
||||||
public Dictionary<NetEntity, Dictionary<Vector2i, float>>? ResistanceGrids;
|
public Dictionary<NetEntity, Dictionary<Vector2i, float>>? ResistanceGrids;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Content.Shared.Administration;
|
|||||||
using Content.Shared.Radiation.Events;
|
using Content.Shared.Radiation.Events;
|
||||||
using Content.Shared.Radiation.Systems;
|
using Content.Shared.Radiation.Systems;
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
|
using Robust.Shared.Debugging;
|
||||||
using Robust.Shared.Enums;
|
using Robust.Shared.Enums;
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
@@ -42,12 +43,12 @@ public partial class RadiationSystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateDebugOverlay(EntityEventArgs ev)
|
private void UpdateDebugOverlay(EntityEventArgs ev)
|
||||||
{
|
{
|
||||||
var sessions = _debugSessions.ToArray();
|
foreach (var session in _debugSessions)
|
||||||
foreach (var session in sessions)
|
|
||||||
{
|
{
|
||||||
if (session.Status != SessionStatus.InGame)
|
if (session.Status != SessionStatus.InGame)
|
||||||
_debugSessions.Remove(session);
|
_debugSessions.Remove(session);
|
||||||
RaiseNetworkEvent(ev, session.Channel);
|
else
|
||||||
|
RaiseNetworkEvent(ev, session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,13 +71,16 @@ public partial class RadiationSystem
|
|||||||
UpdateDebugOverlay(ev);
|
UpdateDebugOverlay(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateGridcastDebugOverlay(double elapsedTime, int totalSources,
|
private void UpdateGridcastDebugOverlay(
|
||||||
int totalReceivers, List<RadiationRay> rays)
|
double elapsedTime,
|
||||||
|
int totalSources,
|
||||||
|
int totalReceivers,
|
||||||
|
List<DebugRadiationRay>? rays)
|
||||||
{
|
{
|
||||||
if (_debugSessions.Count == 0)
|
if (_debugSessions.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var ev = new OnRadiationOverlayUpdateEvent(elapsedTime, totalSources, totalReceivers, rays);
|
var ev = new OnRadiationOverlayUpdateEvent(elapsedTime, totalSources, totalReceivers, rays ?? new());
|
||||||
UpdateDebugOverlay(ev);
|
UpdateDebugOverlay(ev);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
using System.Linq;
|
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Content.Server.Radiation.Components;
|
using Content.Server.Radiation.Components;
|
||||||
using Content.Server.Radiation.Events;
|
using Content.Server.Radiation.Events;
|
||||||
using Content.Shared.Radiation.Components;
|
using Content.Shared.Radiation.Components;
|
||||||
using Content.Shared.Radiation.Systems;
|
using Content.Shared.Radiation.Systems;
|
||||||
using Content.Shared.Stacks;
|
|
||||||
using Robust.Shared.Collections;
|
using Robust.Shared.Collections;
|
||||||
using Robust.Shared.Containers;
|
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
@@ -16,68 +13,86 @@ namespace Content.Server.Radiation.Systems;
|
|||||||
// main algorithm that fire radiation rays to target
|
// main algorithm that fire radiation rays to target
|
||||||
public partial class RadiationSystem
|
public partial class RadiationSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly SharedStackSystem _stack = default!;
|
private List<Entity<MapGridComponent>> _grids = new();
|
||||||
[Dependency] private readonly SharedContainerSystem _container = default!;
|
|
||||||
|
|
||||||
private EntityQuery<RadiationBlockingContainerComponent> _radiationBlockingContainers;
|
private readonly record struct SourceData(
|
||||||
|
float Intensity,
|
||||||
|
Entity<RadiationSourceComponent, TransformComponent> Entity,
|
||||||
|
Vector2 WorldPosition)
|
||||||
|
{
|
||||||
|
public EntityUid? GridUid => Entity.Comp2.GridUid;
|
||||||
|
public float Slope => Entity.Comp1.Slope;
|
||||||
|
public TransformComponent Transform => Entity.Comp2;
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateGridcast()
|
private void UpdateGridcast()
|
||||||
{
|
{
|
||||||
// should we save debug information into rays?
|
// should we save debug information into rays?
|
||||||
// if there is no debug sessions connected - just ignore it
|
// if there is no debug sessions connected - just ignore it
|
||||||
var saveVisitedTiles = _debugSessions.Count > 0;
|
var debug = _debugSessions.Count > 0;
|
||||||
|
|
||||||
var stopwatch = new Stopwatch();
|
var stopwatch = new Stopwatch();
|
||||||
stopwatch.Start();
|
stopwatch.Start();
|
||||||
|
|
||||||
|
_sources.Clear();
|
||||||
|
_sources.EnsureCapacity(EntityManager.Count<RadiationSourceComponent>());
|
||||||
|
|
||||||
var sources = EntityQueryEnumerator<RadiationSourceComponent, TransformComponent>();
|
var sources = EntityQueryEnumerator<RadiationSourceComponent, TransformComponent>();
|
||||||
var destinations = EntityQueryEnumerator<RadiationReceiverComponent, TransformComponent>();
|
var destinations = EntityQueryEnumerator<RadiationReceiverComponent, TransformComponent>();
|
||||||
var resistanceQuery = GetEntityQuery<RadiationGridResistanceComponent>();
|
|
||||||
var transformQuery = GetEntityQuery<TransformComponent>();
|
|
||||||
var gridQuery = GetEntityQuery<MapGridComponent>();
|
|
||||||
var stackQuery = GetEntityQuery<StackComponent>();
|
|
||||||
|
|
||||||
_radiationBlockingContainers = GetEntityQuery<RadiationBlockingContainerComponent>();
|
while (sources.MoveNext(out var uid, out var source, out var xform))
|
||||||
|
|
||||||
// precalculate world positions for each source
|
|
||||||
// so we won't need to calc this in cycle over and over again
|
|
||||||
var sourcesData = new ValueList<(EntityUid, RadiationSourceComponent, TransformComponent, Vector2)>();
|
|
||||||
while (sources.MoveNext(out var uid, out var source, out var sourceTrs))
|
|
||||||
{
|
{
|
||||||
if (!source.Enabled)
|
if (!source.Enabled)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var worldPos = _transform.GetWorldPosition(sourceTrs, transformQuery);
|
var worldPos = _transform.GetWorldPosition(xform);
|
||||||
var data = (uid, source, sourceTrs, worldPos);
|
|
||||||
sourcesData.Add(data);
|
// Intensity is scaled by stack size.
|
||||||
|
var intensity = source.Intensity * _stack.GetCount(uid);
|
||||||
|
|
||||||
|
// Apply rad modifier if the source is enclosed within a radiation blocking container
|
||||||
|
// Note that this also applies to receivers, and it doesn't bother to check if the container sits between them.
|
||||||
|
// I.e., a source & receiver in the same blocking container will get double-blocked, when no blocking should be applied.
|
||||||
|
intensity = GetAdjustedRadiationIntensity(uid, intensity);
|
||||||
|
|
||||||
|
_sources.Add(new(intensity, (uid, source, xform), worldPos));
|
||||||
}
|
}
|
||||||
|
|
||||||
// trace all rays from rad source to rad receivers
|
var debugRays = debug ? new List<DebugRadiationRay>() : null;
|
||||||
var rays = new List<RadiationRay>();
|
|
||||||
var receiversTotalRads = new ValueList<(Entity<RadiationReceiverComponent>, float)>();
|
var receiversTotalRads = new ValueList<(Entity<RadiationReceiverComponent>, float)>();
|
||||||
|
|
||||||
|
// TODO RADIATION Parallelize
|
||||||
|
// Would need to give receiversTotalRads a fixed size.
|
||||||
|
// Also the _grids list needs to be local to a job. (or better yet cached in SourceData)
|
||||||
|
// And I guess disable parallelization when debugging to make populating the debug List<RadiationRay> easier.
|
||||||
|
// Or just make it threadsafe?
|
||||||
while (destinations.MoveNext(out var destUid, out var dest, out var destTrs))
|
while (destinations.MoveNext(out var destUid, out var dest, out var destTrs))
|
||||||
{
|
{
|
||||||
var destWorld = _transform.GetWorldPosition(destTrs, transformQuery);
|
var destWorld = _transform.GetWorldPosition(destTrs);
|
||||||
|
|
||||||
var rads = 0f;
|
var rads = 0f;
|
||||||
foreach (var (uid, source, sourceTrs, sourceWorld) in sourcesData)
|
foreach (var source in _sources)
|
||||||
{
|
{
|
||||||
stackQuery.TryGetComponent(uid, out var stack);
|
|
||||||
var intensity = source.Intensity * _stack.GetCount(uid, stack);
|
|
||||||
|
|
||||||
// send ray towards destination entity
|
// send ray towards destination entity
|
||||||
var ray = Irradiate(uid, sourceTrs, sourceWorld,
|
if (Irradiate(source, destUid, destTrs, destWorld, debug) is not {} ray)
|
||||||
destUid, destTrs, destWorld,
|
|
||||||
intensity, source.Slope, saveVisitedTiles, resistanceQuery, transformQuery, gridQuery);
|
|
||||||
if (ray == null)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// save ray for debug
|
|
||||||
rays.Add(ray);
|
|
||||||
|
|
||||||
// add rads to total rad exposure
|
// add rads to total rad exposure
|
||||||
if (ray.ReachedDestination)
|
if (ray.ReachedDestination)
|
||||||
rads += ray.Rads;
|
rads += ray.Rads;
|
||||||
|
|
||||||
|
if (!debug)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
debugRays!.Add(new DebugRadiationRay(
|
||||||
|
ray.MapId,
|
||||||
|
GetNetEntity(ray.SourceUid),
|
||||||
|
ray.Source,
|
||||||
|
GetNetEntity(ray.DestinationUid),
|
||||||
|
ray.Destination,
|
||||||
|
ray.Rads,
|
||||||
|
ray.Blockers ?? new())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply modifier if the destination entity is hidden within a radiation blocking container
|
// Apply modifier if the destination entity is hidden within a radiation blocking container
|
||||||
@@ -88,9 +103,9 @@ public partial class RadiationSystem
|
|||||||
|
|
||||||
// update information for debug overlay
|
// update information for debug overlay
|
||||||
var elapsedTime = stopwatch.Elapsed.TotalMilliseconds;
|
var elapsedTime = stopwatch.Elapsed.TotalMilliseconds;
|
||||||
var totalSources = sourcesData.Count;
|
var totalSources = _sources.Count;
|
||||||
var totalReceivers = receiversTotalRads.Count;
|
var totalReceivers = receiversTotalRads.Count;
|
||||||
UpdateGridcastDebugOverlay(elapsedTime, totalSources, totalReceivers, rays);
|
UpdateGridcastDebugOverlay(elapsedTime, totalSources, totalReceivers, debugRays);
|
||||||
|
|
||||||
// send rads to each entity
|
// send rads to each entity
|
||||||
foreach (var (receiver, rads) in receiversTotalRads)
|
foreach (var (receiver, rads) in receiversTotalRads)
|
||||||
@@ -108,19 +123,20 @@ public partial class RadiationSystem
|
|||||||
RaiseLocalEvent(new RadiationSystemUpdatedEvent());
|
RaiseLocalEvent(new RadiationSystemUpdatedEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
private RadiationRay? Irradiate(EntityUid sourceUid, TransformComponent sourceTrs, Vector2 sourceWorld,
|
private RadiationRay? Irradiate(SourceData source,
|
||||||
EntityUid destUid, TransformComponent destTrs, Vector2 destWorld,
|
EntityUid destUid,
|
||||||
float incomingRads, float slope, bool saveVisitedTiles,
|
TransformComponent destTrs,
|
||||||
EntityQuery<RadiationGridResistanceComponent> resistanceQuery,
|
Vector2 destWorld,
|
||||||
EntityQuery<TransformComponent> transformQuery, EntityQuery<MapGridComponent> gridQuery)
|
bool saveVisitedTiles)
|
||||||
{
|
{
|
||||||
// lets first check that source and destination on the same map
|
// lets first check that source and destination on the same map
|
||||||
if (sourceTrs.MapID != destTrs.MapID)
|
if (source.Transform.MapID != destTrs.MapID)
|
||||||
return null;
|
return null;
|
||||||
var mapId = sourceTrs.MapID;
|
|
||||||
|
var mapId = destTrs.MapID;
|
||||||
|
|
||||||
// get direction from rad source to destination and its distance
|
// get direction from rad source to destination and its distance
|
||||||
var dir = destWorld - sourceWorld;
|
var dir = destWorld - source.WorldPosition;
|
||||||
var dist = dir.Length();
|
var dist = dir.Length();
|
||||||
|
|
||||||
// check if receiver is too far away
|
// check if receiver is too far away
|
||||||
@@ -128,41 +144,42 @@ public partial class RadiationSystem
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
// will it even reach destination considering distance penalty
|
// will it even reach destination considering distance penalty
|
||||||
var rads = incomingRads - slope * dist;
|
var rads = source.Intensity - source.Slope * dist;
|
||||||
|
if (rads < MinIntensity)
|
||||||
// Apply rad modifier if the source is enclosed within a radiation blocking container
|
|
||||||
rads = GetAdjustedRadiationIntensity(sourceUid, rads);
|
|
||||||
|
|
||||||
if (rads <= MinIntensity)
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// create a new radiation ray from source to destination
|
// create a new radiation ray from source to destination
|
||||||
// at first we assume that it doesn't hit any radiation blockers
|
// at first we assume that it doesn't hit any radiation blockers
|
||||||
// and has only distance penalty
|
// and has only distance penalty
|
||||||
var ray = new RadiationRay(mapId, GetNetEntity(sourceUid), sourceWorld, GetNetEntity(destUid), destWorld, rads);
|
var ray = new RadiationRay(mapId, source.Entity, source.WorldPosition, destUid, destWorld, rads);
|
||||||
|
|
||||||
// if source and destination on the same grid it's possible that
|
// if source and destination on the same grid it's possible that
|
||||||
// between them can be another grid (ie. shuttle in center of donut station)
|
// between them can be another grid (ie. shuttle in center of donut station)
|
||||||
// however we can do simplification and ignore that case
|
// however we can do simplification and ignore that case
|
||||||
if (GridcastSimplifiedSameGrid && sourceTrs.GridUid != null && sourceTrs.GridUid == destTrs.GridUid)
|
if (GridcastSimplifiedSameGrid && destTrs.GridUid is {} gridUid && source.GridUid == gridUid)
|
||||||
{
|
{
|
||||||
if (!gridQuery.TryGetComponent(sourceTrs.GridUid.Value, out var gridComponent))
|
if (!_gridQuery.TryGetComponent(gridUid, out var gridComponent))
|
||||||
return ray;
|
return ray;
|
||||||
return Gridcast((sourceTrs.GridUid.Value, gridComponent), ray, saveVisitedTiles, resistanceQuery, sourceTrs, destTrs, transformQuery.GetComponent(sourceTrs.GridUid.Value));
|
return Gridcast((gridUid, gridComponent, Transform(gridUid)), ref ray, saveVisitedTiles, source.Transform, destTrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// lets check how many grids are between source and destination
|
// lets check how many grids are between source and destination
|
||||||
// do a box intersection test between target and destination
|
// do a box intersection test between target and destination
|
||||||
// it's not very precise, but really cheap
|
// it's not very precise, but really cheap
|
||||||
var box = Box2.FromTwoPoints(sourceWorld, destWorld);
|
|
||||||
var grids = new List<Entity<MapGridComponent>>();
|
// TODO RADIATION
|
||||||
_mapManager.FindGridsIntersecting(mapId, box, ref grids, true);
|
// Consider caching this in SourceData?
|
||||||
|
// I.e., make the lookup for grids as large as the sources's max distance and store the result in SourceData.
|
||||||
|
// Avoids having to do a lookup per source*receiver.
|
||||||
|
var box = Box2.FromTwoPoints(source.WorldPosition, destWorld);
|
||||||
|
_grids.Clear();
|
||||||
|
_mapManager.FindGridsIntersecting(mapId, box, ref _grids, true);
|
||||||
|
|
||||||
// gridcast through each grid and try to hit some radiation blockers
|
// gridcast through each grid and try to hit some radiation blockers
|
||||||
// the ray will be updated with each grid that has some blockers
|
// the ray will be updated with each grid that has some blockers
|
||||||
foreach (var grid in grids)
|
foreach (var grid in _grids)
|
||||||
{
|
{
|
||||||
ray = Gridcast(grid, ray, saveVisitedTiles, resistanceQuery, sourceTrs, destTrs, transformQuery.GetComponent(grid));
|
ray = Gridcast((grid.Owner, grid.Comp, Transform(grid)), ref ray, saveVisitedTiles, source.Transform, destTrs);
|
||||||
|
|
||||||
// looks like last grid blocked all radiation
|
// looks like last grid blocked all radiation
|
||||||
// we can return right now
|
// we can return right now
|
||||||
@@ -170,20 +187,23 @@ public partial class RadiationSystem
|
|||||||
return ray;
|
return ray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_grids.Clear();
|
||||||
|
|
||||||
return ray;
|
return ray;
|
||||||
}
|
}
|
||||||
|
|
||||||
private RadiationRay Gridcast(Entity<MapGridComponent> grid, RadiationRay ray, bool saveVisitedTiles,
|
private RadiationRay Gridcast(
|
||||||
EntityQuery<RadiationGridResistanceComponent> resistanceQuery,
|
Entity<MapGridComponent, TransformComponent> grid,
|
||||||
|
ref RadiationRay ray,
|
||||||
|
bool saveVisitedTiles,
|
||||||
TransformComponent sourceTrs,
|
TransformComponent sourceTrs,
|
||||||
TransformComponent destTrs,
|
TransformComponent destTrs)
|
||||||
TransformComponent gridTrs)
|
|
||||||
{
|
{
|
||||||
var blockers = new List<(Vector2i, float)>();
|
var blockers = saveVisitedTiles ? new List<(Vector2i, float)>() : null;
|
||||||
|
|
||||||
// if grid doesn't have resistance map just apply distance penalty
|
// if grid doesn't have resistance map just apply distance penalty
|
||||||
var gridUid = grid.Owner;
|
var gridUid = grid.Owner;
|
||||||
if (!resistanceQuery.TryGetComponent(gridUid, out var resistance))
|
if (!_resistanceQuery.TryGetComponent(gridUid, out var resistance))
|
||||||
return ray;
|
return ray;
|
||||||
var resistanceMap = resistance.ResistancePerTile;
|
var resistanceMap = resistance.ResistancePerTile;
|
||||||
|
|
||||||
@@ -195,19 +215,19 @@ public partial class RadiationSystem
|
|||||||
|
|
||||||
Vector2 srcLocal = sourceTrs.ParentUid == grid.Owner
|
Vector2 srcLocal = sourceTrs.ParentUid == grid.Owner
|
||||||
? sourceTrs.LocalPosition
|
? sourceTrs.LocalPosition
|
||||||
: Vector2.Transform(ray.Source, gridTrs.InvLocalMatrix);
|
: Vector2.Transform(ray.Source, grid.Comp2.InvLocalMatrix);
|
||||||
|
|
||||||
Vector2 dstLocal = destTrs.ParentUid == grid.Owner
|
Vector2 dstLocal = destTrs.ParentUid == grid.Owner
|
||||||
? destTrs.LocalPosition
|
? destTrs.LocalPosition
|
||||||
: Vector2.Transform(ray.Destination, gridTrs.InvLocalMatrix);
|
: Vector2.Transform(ray.Destination, grid.Comp2.InvLocalMatrix);
|
||||||
|
|
||||||
Vector2i sourceGrid = new(
|
Vector2i sourceGrid = new(
|
||||||
(int) Math.Floor(srcLocal.X / grid.Comp.TileSize),
|
(int) Math.Floor(srcLocal.X / grid.Comp1.TileSize),
|
||||||
(int) Math.Floor(srcLocal.Y / grid.Comp.TileSize));
|
(int) Math.Floor(srcLocal.Y / grid.Comp1.TileSize));
|
||||||
|
|
||||||
Vector2i destGrid = new(
|
Vector2i destGrid = new(
|
||||||
(int) Math.Floor(dstLocal.X / grid.Comp.TileSize),
|
(int) Math.Floor(dstLocal.X / grid.Comp1.TileSize),
|
||||||
(int) Math.Floor(dstLocal.Y / grid.Comp.TileSize));
|
(int) Math.Floor(dstLocal.Y / grid.Comp1.TileSize));
|
||||||
|
|
||||||
// iterate tiles in grid line from source to destination
|
// iterate tiles in grid line from source to destination
|
||||||
var line = new GridLineEnumerator(sourceGrid, destGrid);
|
var line = new GridLineEnumerator(sourceGrid, destGrid);
|
||||||
@@ -220,7 +240,7 @@ public partial class RadiationSystem
|
|||||||
|
|
||||||
// save data for debug
|
// save data for debug
|
||||||
if (saveVisitedTiles)
|
if (saveVisitedTiles)
|
||||||
blockers.Add((point, ray.Rads));
|
blockers!.Add((point, ray.Rads));
|
||||||
|
|
||||||
// no intensity left after blocker
|
// no intensity left after blocker
|
||||||
if (ray.Rads <= MinIntensity)
|
if (ray.Rads <= MinIntensity)
|
||||||
@@ -230,21 +250,45 @@ public partial class RadiationSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!saveVisitedTiles || blockers!.Count <= 0)
|
||||||
|
return ray;
|
||||||
|
|
||||||
// save data for debug if needed
|
// save data for debug if needed
|
||||||
if (saveVisitedTiles && blockers.Count > 0)
|
ray.Blockers ??= new();
|
||||||
ray.Blockers.Add(GetNetEntity(gridUid), blockers);
|
ray.Blockers.Add(GetNetEntity(gridUid), blockers);
|
||||||
|
|
||||||
return ray;
|
return ray;
|
||||||
}
|
}
|
||||||
|
|
||||||
private float GetAdjustedRadiationIntensity(EntityUid uid, float rads)
|
private float GetAdjustedRadiationIntensity(EntityUid uid, float rads)
|
||||||
{
|
{
|
||||||
var radblockingComps = new List<RadiationBlockingContainerComponent>();
|
var child = uid;
|
||||||
if (_container.TryFindComponentsOnEntityContainerOrParent(uid, _radiationBlockingContainers, radblockingComps))
|
var xform = Transform(uid);
|
||||||
|
var parent = xform.ParentUid;
|
||||||
|
|
||||||
|
while (parent.IsValid())
|
||||||
{
|
{
|
||||||
rads -= radblockingComps.Sum(x => x.RadResistance);
|
var parentXform = Transform(parent);
|
||||||
|
var childMeta = MetaData(child);
|
||||||
|
|
||||||
|
if ((childMeta.Flags & MetaDataFlags.InContainer) != MetaDataFlags.InContainer)
|
||||||
|
{
|
||||||
|
child = parent;
|
||||||
|
parent = parentXform.ParentUid;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_blockerQuery.TryComp(xform.ParentUid, out var blocker))
|
||||||
|
{
|
||||||
|
rads -= blocker.RadResistance;
|
||||||
|
if (rads < 0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
child = parent;
|
||||||
|
parent = parentXform.ParentUid;
|
||||||
}
|
}
|
||||||
|
|
||||||
return float.Max(rads, 0);
|
return rads;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
using Content.Server.Radiation.Components;
|
using Content.Server.Radiation.Components;
|
||||||
using Content.Shared.Radiation.Components;
|
using Content.Shared.Radiation.Components;
|
||||||
using Content.Shared.Radiation.Events;
|
using Content.Shared.Radiation.Events;
|
||||||
|
using Content.Shared.Stacks;
|
||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
|
||||||
namespace Content.Server.Radiation.Systems;
|
namespace Content.Server.Radiation.Systems;
|
||||||
|
|
||||||
@@ -11,14 +13,26 @@ public sealed partial class RadiationSystem : EntitySystem
|
|||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
|
[Dependency] private readonly SharedStackSystem _stack = default!;
|
||||||
|
|
||||||
|
private EntityQuery<RadiationBlockingContainerComponent> _blockerQuery;
|
||||||
|
private EntityQuery<RadiationGridResistanceComponent> _resistanceQuery;
|
||||||
|
private EntityQuery<MapGridComponent> _gridQuery;
|
||||||
|
private EntityQuery<StackComponent> _stackQuery;
|
||||||
|
|
||||||
private float _accumulator;
|
private float _accumulator;
|
||||||
|
private List<SourceData> _sources = new();
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
SubscribeCvars();
|
SubscribeCvars();
|
||||||
InitRadBlocking();
|
InitRadBlocking();
|
||||||
|
|
||||||
|
_blockerQuery = GetEntityQuery<RadiationBlockingContainerComponent>();
|
||||||
|
_resistanceQuery = GetEntityQuery<RadiationGridResistanceComponent>();
|
||||||
|
_gridQuery = GetEntityQuery<MapGridComponent>();
|
||||||
|
_stackQuery = GetEntityQuery<StackComponent>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
|
|||||||
@@ -4,17 +4,11 @@
|
|||||||
/// Raised on entity when it was irradiated
|
/// Raised on entity when it was irradiated
|
||||||
/// by some radiation source.
|
/// by some radiation source.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class OnIrradiatedEvent : EntityEventArgs
|
public readonly record struct OnIrradiatedEvent(float FrameTime, float RadsPerSecond)
|
||||||
{
|
{
|
||||||
public readonly float FrameTime;
|
public readonly float FrameTime = FrameTime;
|
||||||
|
|
||||||
public readonly float RadsPerSecond;
|
public readonly float RadsPerSecond = RadsPerSecond;
|
||||||
|
|
||||||
public float TotalRads => RadsPerSecond * FrameTime;
|
public float TotalRads => RadsPerSecond * FrameTime;
|
||||||
|
|
||||||
public OnIrradiatedEvent(float frameTime, float radsPerSecond)
|
|
||||||
{
|
|
||||||
FrameTime = frameTime;
|
|
||||||
RadsPerSecond = radsPerSecond;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,36 +13,33 @@ namespace Content.Shared.Radiation.Events;
|
|||||||
/// Will be sent only to clients that activated radiation view using console command.
|
/// Will be sent only to clients that activated radiation view using console command.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class OnRadiationOverlayUpdateEvent : EntityEventArgs
|
public sealed class OnRadiationOverlayUpdateEvent(
|
||||||
|
double elapsedTimeMs,
|
||||||
|
int sourcesCount,
|
||||||
|
int receiversCount,
|
||||||
|
List<DebugRadiationRay> rays)
|
||||||
|
: EntityEventArgs
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Total time in milliseconds that server took to do radiation processing.
|
/// Total time in milliseconds that server took to do radiation processing.
|
||||||
/// Exclude time of entities reacting to <see cref="OnIrradiatedEvent"/>.
|
/// Exclude time of entities reacting to <see cref="OnIrradiatedEvent"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly double ElapsedTimeMs;
|
public readonly double ElapsedTimeMs = elapsedTimeMs;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Total count of entities with <see cref="RadiationSourceComponent"/> on all maps.
|
/// Total count of entities with <see cref="RadiationSourceComponent"/> on all maps.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly int SourcesCount;
|
public readonly int SourcesCount = sourcesCount;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Total count of entities with radiation receiver on all maps.
|
/// Total count of entities with radiation receiver on all maps.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly int ReceiversCount;
|
public readonly int ReceiversCount = receiversCount;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All radiation rays that was processed by radiation system.
|
/// All radiation rays that was processed by radiation system.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public readonly List<RadiationRay> Rays;
|
public readonly List<DebugRadiationRay> Rays = rays;
|
||||||
|
|
||||||
public OnRadiationOverlayUpdateEvent(double elapsedTimeMs, int sourcesCount, int receiversCount, List<RadiationRay> rays)
|
|
||||||
{
|
|
||||||
ElapsedTimeMs = elapsedTimeMs;
|
|
||||||
SourcesCount = sourcesCount;
|
|
||||||
ReceiversCount = receiversCount;
|
|
||||||
Rays = rays;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -9,33 +9,38 @@ namespace Content.Shared.Radiation.Systems;
|
|||||||
/// Ray emitted by radiation source towards radiation receiver.
|
/// Ray emitted by radiation source towards radiation receiver.
|
||||||
/// Contains all information about encountered radiation blockers.
|
/// Contains all information about encountered radiation blockers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
public struct RadiationRay(
|
||||||
public sealed class RadiationRay
|
MapId mapId,
|
||||||
|
EntityUid sourceUid,
|
||||||
|
Vector2 source,
|
||||||
|
EntityUid destinationUid,
|
||||||
|
Vector2 destination,
|
||||||
|
float rads)
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Map on which source and receiver are placed.
|
/// Map on which source and receiver are placed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public MapId MapId;
|
public MapId MapId = mapId;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Uid of entity with <see cref="RadiationSourceComponent"/>.
|
/// Uid of entity with <see cref="RadiationSourceComponent"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public NetEntity SourceUid;
|
public EntityUid SourceUid = sourceUid;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// World coordinates of radiation source.
|
/// World coordinates of radiation source.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Vector2 Source;
|
public Vector2 Source = source;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Uid of entity with radiation receiver component.
|
/// Uid of entity with radiation receiver component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public NetEntity DestinationUid;
|
public EntityUid DestinationUid = destinationUid;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// World coordinates of radiation receiver.
|
/// World coordinates of radiation receiver.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Vector2 Destination;
|
public Vector2 Destination = destination;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How many rads intensity reached radiation receiver.
|
/// How many rads intensity reached radiation receiver.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float Rads;
|
public float Rads = rads;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Has rad ray reached destination or lost all intensity after blockers?
|
/// Has rad ray reached destination or lost all intensity after blockers?
|
||||||
@@ -43,23 +48,27 @@ public sealed class RadiationRay
|
|||||||
public bool ReachedDestination => Rads > 0;
|
public bool ReachedDestination => Rads > 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// All blockers visited by gridcast. Key is uid of grid. Values are pairs
|
/// All blockers visited by gridcast, used for debug overlays. Key is uid of grid. Values are pairs
|
||||||
/// of tile indices and floats with updated radiation value.
|
/// of tile indices and floats with updated radiation value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// Last tile may have negative value if ray has lost all intensity.
|
/// Last tile may have negative value if ray has lost all intensity.
|
||||||
/// Grid traversal order isn't guaranteed.
|
/// Grid traversal order isn't guaranteed.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public Dictionary<NetEntity, List<(Vector2i, float)>> Blockers = new();
|
public Dictionary<NetEntity, List<(Vector2i, float)>>? Blockers;
|
||||||
|
|
||||||
public RadiationRay(MapId mapId, NetEntity sourceUid, Vector2 source,
|
}
|
||||||
NetEntity destinationUid, Vector2 destination, float rads)
|
|
||||||
{
|
// Variant of RadiationRay that uses NetEntities.
|
||||||
MapId = mapId;
|
[Serializable, NetSerializable]
|
||||||
SourceUid = sourceUid;
|
public readonly record struct DebugRadiationRay(
|
||||||
Source = source;
|
MapId MapId,
|
||||||
DestinationUid = destinationUid;
|
NetEntity SourceUid,
|
||||||
Destination = destination;
|
Vector2 Source,
|
||||||
Rads = rads;
|
NetEntity DestinationUid,
|
||||||
}
|
Vector2 Destination,
|
||||||
|
float Rads,
|
||||||
|
Dictionary<NetEntity, List<(Vector2i, float)>> Blockers)
|
||||||
|
{
|
||||||
|
public bool ReachedDestination => Rads > 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user