2023-07-08 14:08:32 +10:00
using System.Numerics ;
2022-08-31 12:24:21 +02:00
using Content.Shared.Radiation.Components ;
2021-02-11 01:13:03 -08:00
using Robust.Client.Graphics ;
2021-03-09 04:33:41 -06:00
using Robust.Shared.Enums ;
2023-09-11 16:15:23 +10:00
using Robust.Shared.Graphics ;
2021-02-11 01:13:03 -08:00
using Robust.Shared.Map ;
2021-12-01 20:21:17 +00:00
using Robust.Shared.Prototypes ;
2021-12-05 18:09:01 +01:00
using Robust.Shared.Timing ;
2020-08-14 06:52:17 +10:00
2022-10-11 05:09:10 +02:00
namespace Content.Client.Radiation.Overlays
2020-08-14 06:52:17 +10:00
{
2022-02-16 00:23:23 -07:00
public sealed class RadiationPulseOverlay : Overlay
2020-08-14 06:52:17 +10:00
{
2021-09-28 13:35:29 +02:00
[Dependency] private readonly IEntityManager _entityManager = default ! ;
2021-12-01 20:21:17 +00:00
[Dependency] private readonly IPrototypeManager _prototypeManager = default ! ;
2020-08-14 06:52:17 +10:00
[Dependency] private readonly IGameTiming _gameTiming = default ! ;
2020-09-21 01:49:40 +02:00
2021-12-01 20:21:17 +00:00
private const float MaxDist = 15.0f ;
2020-08-14 06:52:17 +10:00
2021-12-01 20:21:17 +00:00
public override OverlaySpace Space = > OverlaySpace . WorldSpace ;
public override bool RequestScreenTexture = > true ;
2020-08-14 06:52:17 +10:00
2021-12-01 20:21:17 +00:00
private readonly ShaderInstance _baseShader ;
private readonly Dictionary < EntityUid , ( ShaderInstance shd , RadiationShaderInstance instance ) > _pulses = new ( ) ;
2020-08-14 06:52:17 +10:00
2021-03-09 04:33:41 -06:00
public RadiationPulseOverlay ( )
2020-08-14 06:52:17 +10:00
{
IoCManager . InjectDependencies ( this ) ;
2021-12-01 20:21:17 +00:00
_baseShader = _prototypeManager . Index < ShaderPrototype > ( "Radiation" ) . Instance ( ) . Duplicate ( ) ;
2020-08-14 06:52:17 +10:00
}
2022-06-20 12:16:32 +12:00
protected override bool BeforeDraw ( in OverlayDrawArgs args )
2020-08-14 06:52:17 +10:00
{
2021-12-01 20:21:17 +00:00
RadiationQuery ( args . Viewport . Eye ) ;
2022-06-20 12:16:32 +12:00
return _pulses . Count > 0 ;
}
2020-09-21 01:49:40 +02:00
2022-06-20 12:16:32 +12:00
protected override void Draw ( in OverlayDrawArgs args )
{
2021-12-01 20:21:17 +00:00
if ( ScreenTexture = = null )
return ;
2020-08-14 06:52:17 +10:00
2021-12-01 20:21:17 +00:00
var worldHandle = args . WorldHandle ;
var viewport = args . Viewport ;
2020-08-14 06:52:17 +10:00
2021-12-01 20:21:17 +00:00
foreach ( ( var shd , var instance ) in _pulses . Values )
2020-08-14 06:52:17 +10:00
{
2022-09-20 11:49:02 +12:00
if ( instance . CurrentMapCoords . MapId ! = args . MapId )
continue ;
2021-12-01 20:21:17 +00:00
// To be clear, this needs to use "inside-viewport" pixels.
// In other words, specifically NOT IViewportControl.WorldToScreen (which uses outer coordinates).
2022-09-20 11:49:02 +12:00
var tempCoords = viewport . WorldToLocal ( instance . CurrentMapCoords . Position ) ;
2021-12-01 20:21:17 +00:00
tempCoords . Y = viewport . Size . Y - tempCoords . Y ;
shd ? . SetParameter ( "renderScale" , viewport . RenderScale ) ;
shd ? . SetParameter ( "positionInput" , tempCoords ) ;
shd ? . SetParameter ( "range" , instance . Range ) ;
2022-08-31 12:24:21 +02:00
var life = ( _gameTiming . RealTime - instance . Start ) . TotalSeconds / instance . Duration ;
2021-12-01 20:21:17 +00:00
shd ? . SetParameter ( "life" , ( float ) life ) ;
// There's probably a very good reason not to do this.
// Oh well!
shd ? . SetParameter ( "SCREEN_TEXTURE" , viewport . RenderTarget . Texture ) ;
worldHandle . UseShader ( shd ) ;
2022-09-20 11:49:02 +12:00
worldHandle . DrawRect ( Box2 . CenteredAround ( instance . CurrentMapCoords . Position , new Vector2 ( instance . Range , instance . Range ) * 2f ) , Color . White ) ;
2020-08-14 06:52:17 +10:00
}
2022-08-13 10:58:53 +10:00
worldHandle . UseShader ( null ) ;
2020-08-14 06:52:17 +10:00
}
2021-12-01 20:21:17 +00:00
//Queries all pulses on the map and either adds or removes them from the list of rendered pulses based on whether they should be drawn (in range? on the same z-level/map? pulse entity still exists?)
private void RadiationQuery ( IEye ? currentEye )
2020-08-14 06:52:17 +10:00
{
2024-02-28 00:51:20 +11:00
if ( currentEye = = null )
2020-08-14 06:52:17 +10:00
{
2021-12-01 20:21:17 +00:00
_pulses . Clear ( ) ;
return ;
2020-08-14 06:52:17 +10:00
}
2020-09-21 01:49:40 +02:00
2021-12-01 20:21:17 +00:00
var currentEyeLoc = currentEye . Position ;
2020-08-14 06:52:17 +10:00
2023-10-19 12:34:31 -07:00
var pulses = _entityManager . EntityQueryEnumerator < RadiationPulseComponent > ( ) ;
//Add all pulses that are not added yet but qualify
while ( pulses . MoveNext ( out var pulseEntity , out var pulse ) )
2020-08-14 06:52:17 +10:00
{
2022-12-05 00:46:52 +01:00
if ( ! _pulses . ContainsKey ( pulseEntity ) & & PulseQualifies ( pulseEntity , currentEyeLoc ) )
2021-12-01 20:21:17 +00:00
{
_pulses . Add (
2021-12-03 15:53:09 +01:00
pulseEntity ,
2021-12-01 20:21:17 +00:00
(
_baseShader . Duplicate ( ) ,
new RadiationShaderInstance (
2024-02-28 00:51:20 +11:00
_entityManager . GetComponent < TransformComponent > ( pulseEntity ) . MapPosition ,
2022-08-31 12:24:21 +02:00
pulse . VisualRange ,
2021-12-01 20:21:17 +00:00
pulse . StartTime ,
2022-08-31 12:24:21 +02:00
pulse . VisualDuration
2021-12-01 20:21:17 +00:00
)
)
) ;
}
2021-11-27 22:53:43 -08:00
}
2021-12-01 20:21:17 +00:00
var activeShaderIds = _pulses . Keys ;
2021-12-05 18:09:01 +01:00
foreach ( var pulseEntity in activeShaderIds ) //Remove all pulses that are added and no longer qualify
2020-08-14 06:52:17 +10:00
{
2021-12-05 18:09:01 +01:00
if ( _entityManager . EntityExists ( pulseEntity ) & &
2021-12-01 20:21:17 +00:00
PulseQualifies ( pulseEntity , currentEyeLoc ) & &
2023-08-24 03:10:55 -07:00
_entityManager . TryGetComponent ( pulseEntity , out RadiationPulseComponent ? pulse ) )
2020-08-14 06:52:17 +10:00
{
2021-12-05 18:09:01 +01:00
var shaderInstance = _pulses [ pulseEntity ] ;
2024-02-28 00:51:20 +11:00
shaderInstance . instance . CurrentMapCoords = _entityManager . GetComponent < TransformComponent > ( pulseEntity ) . MapPosition ;
2022-08-31 12:24:21 +02:00
shaderInstance . instance . Range = pulse . VisualRange ;
2021-12-01 20:21:17 +00:00
} else {
2021-12-05 18:09:01 +01:00
_pulses [ pulseEntity ] . shd . Dispose ( ) ;
_pulses . Remove ( pulseEntity ) ;
2020-08-14 06:52:17 +10:00
}
}
2021-12-01 20:21:17 +00:00
}
2021-12-05 18:09:01 +01:00
private bool PulseQualifies ( EntityUid pulseEntity , MapCoordinates currentEyeLoc )
2021-12-01 20:21:17 +00:00
{
2024-03-20 21:59:56 -04:00
var transformComponent = _entityManager . GetComponent < TransformComponent > ( pulseEntity ) ;
var transformSystem = _entityManager . System < SharedTransformSystem > ( ) ;
return transformComponent . MapID = = currentEyeLoc . MapId
& & transformComponent . Coordinates . InRange ( _entityManager , transformSystem , EntityCoordinates . FromMap ( transformComponent . ParentUid , currentEyeLoc , transformSystem , _entityManager ) , MaxDist ) ;
2020-08-14 06:52:17 +10:00
}
2021-12-01 20:21:17 +00:00
2022-09-20 11:49:02 +12:00
private sealed record RadiationShaderInstance ( MapCoordinates CurrentMapCoords , float Range , TimeSpan Start , float Duration )
2021-12-01 20:21:17 +00:00
{
2022-09-20 11:49:02 +12:00
public MapCoordinates CurrentMapCoords = CurrentMapCoords ;
2021-12-01 20:21:17 +00:00
public float Range = Range ;
public TimeSpan Start = Start ;
2022-08-31 12:24:21 +02:00
public float Duration = Duration ;
2021-12-01 20:21:17 +00:00
} ;
2020-08-14 06:52:17 +10:00
}
2020-09-21 01:49:40 +02:00
}
2021-12-01 20:21:17 +00:00