2023-03-06 22:05:12 +03:00
using Content.Server.Explosion.EntitySystems ;
2023-05-07 01:26:04 +10:00
using Content.Server.Power.EntitySystems ;
using Content.Server.Radio ;
using Content.Server.SurveillanceCamera ;
using Content.Shared.Emp ;
using Content.Shared.Examine ;
2023-03-06 22:05:12 +03:00
using Robust.Shared.Map ;
namespace Content.Server.Emp ;
2023-05-07 01:26:04 +10:00
public sealed class EmpSystem : SharedEmpSystem
2023-03-06 22:05:12 +03:00
{
[Dependency] private readonly EntityLookupSystem _lookup = default ! ;
public const string EmpPulseEffectPrototype = "EffectEmpPulse" ;
public override void Initialize ( )
{
base . Initialize ( ) ;
2023-05-07 01:26:04 +10:00
SubscribeLocalEvent < EmpDisabledComponent , ExaminedEvent > ( OnExamine ) ;
2023-03-06 22:05:12 +03:00
SubscribeLocalEvent < EmpOnTriggerComponent , TriggerEvent > ( HandleEmpTrigger ) ;
2023-05-07 01:26:04 +10:00
SubscribeLocalEvent < EmpDisabledComponent , RadioSendAttemptEvent > ( OnRadioSendAttempt ) ;
SubscribeLocalEvent < EmpDisabledComponent , RadioReceiveAttemptEvent > ( OnRadioReceiveAttempt ) ;
SubscribeLocalEvent < EmpDisabledComponent , ApcToggleMainBreakerAttemptEvent > ( OnApcToggleMainBreaker ) ;
SubscribeLocalEvent < EmpDisabledComponent , SurveillanceCameraSetActiveAttemptEvent > ( OnCameraSetActive ) ;
2023-03-06 22:05:12 +03:00
}
2023-09-18 08:28:05 -04:00
/// <summary>
/// Triggers an EMP pulse at the given location, by first raising an <see cref="EmpAttemptEvent"/>, then a raising <see cref="EmpPulseEvent"/> on all entities in range.
/// </summary>
/// <param name="coordinates">The location to trigger the EMP pulse at.</param>
/// <param name="range">The range of the EMP pulse.</param>
/// <param name="energyConsumption">The amount of energy consumed by the EMP pulse.</param>
/// <param name="duration">The duration of the EMP effects.</param>
2023-05-07 01:26:04 +10:00
public void EmpPulse ( MapCoordinates coordinates , float range , float energyConsumption , float duration )
2023-03-06 22:05:12 +03:00
{
foreach ( var uid in _lookup . GetEntitiesInRange ( coordinates , range ) )
{
2023-09-18 08:28:05 -04:00
TryEmpEffects ( uid , energyConsumption , duration ) ;
2023-03-06 22:05:12 +03:00
}
Spawn ( EmpPulseEffectPrototype , coordinates ) ;
}
2023-09-18 08:28:05 -04:00
/// <summary>
/// Attempts to apply the effects of an EMP pulse onto an entity by first raising an <see cref="EmpAttemptEvent"/>, followed by raising a <see cref="EmpPulseEvent"/> on it.
/// </summary>
/// <param name="uid">The entity to apply the EMP effects on.</param>
/// <param name="energyConsumption">The amount of energy consumed by the EMP.</param>
/// <param name="duration">The duration of the EMP effects.</param>
public void TryEmpEffects ( EntityUid uid , float energyConsumption , float duration )
{
var attemptEv = new EmpAttemptEvent ( ) ;
RaiseLocalEvent ( uid , attemptEv ) ;
if ( attemptEv . Cancelled )
return ;
DoEmpEffects ( uid , energyConsumption , duration ) ;
}
/// <summary>
/// Applies the effects of an EMP pulse onto an entity by raising a <see cref="EmpPulseEvent"/> on it.
/// </summary>
/// <param name="uid">The entity to apply the EMP effects on.</param>
/// <param name="energyConsumption">The amount of energy consumed by the EMP.</param>
/// <param name="duration">The duration of the EMP effects.</param>
public void DoEmpEffects ( EntityUid uid , float energyConsumption , float duration )
{
var ev = new EmpPulseEvent ( energyConsumption , false , false , TimeSpan . FromSeconds ( duration ) ) ;
RaiseLocalEvent ( uid , ref ev ) ;
if ( ev . Affected )
{
Spawn ( EmpDisabledEffectPrototype , Transform ( uid ) . Coordinates ) ;
}
if ( ev . Disabled )
{
var disabled = EnsureComp < EmpDisabledComponent > ( uid ) ;
disabled . DisabledUntil = Timing . CurTime + TimeSpan . FromSeconds ( duration ) ;
}
}
2023-05-07 01:26:04 +10:00
public override void Update ( float frameTime )
{
base . Update ( frameTime ) ;
var query = EntityQueryEnumerator < EmpDisabledComponent > ( ) ;
while ( query . MoveNext ( out var uid , out var comp ) )
{
if ( comp . DisabledUntil < Timing . CurTime )
{
RemComp < EmpDisabledComponent > ( uid ) ;
var ev = new EmpDisabledRemoved ( ) ;
RaiseLocalEvent ( uid , ref ev ) ;
}
}
}
private void OnExamine ( EntityUid uid , EmpDisabledComponent component , ExaminedEvent args )
{
args . PushMarkup ( Loc . GetString ( "emp-disabled-comp-on-examine" ) ) ;
}
2023-03-06 22:05:12 +03:00
private void HandleEmpTrigger ( EntityUid uid , EmpOnTriggerComponent comp , TriggerEvent args )
{
2024-02-28 00:51:20 +11:00
EmpPulse ( Transform ( uid ) . MapPosition , comp . Range , comp . EnergyConsumption , comp . DisableDuration ) ;
2023-03-06 22:05:12 +03:00
args . Handled = true ;
}
2023-05-07 01:26:04 +10:00
private void OnRadioSendAttempt ( EntityUid uid , EmpDisabledComponent component , ref RadioSendAttemptEvent args )
{
args . Cancelled = true ;
}
private void OnRadioReceiveAttempt ( EntityUid uid , EmpDisabledComponent component , ref RadioReceiveAttemptEvent args )
{
args . Cancelled = true ;
}
private void OnApcToggleMainBreaker ( EntityUid uid , EmpDisabledComponent component , ref ApcToggleMainBreakerAttemptEvent args )
{
args . Cancelled = true ;
}
private void OnCameraSetActive ( EntityUid uid , EmpDisabledComponent component , ref SurveillanceCameraSetActiveAttemptEvent args )
{
args . Cancelled = true ;
}
2023-03-06 22:05:12 +03:00
}
2023-09-10 07:20:27 +01:00
/// <summary>
/// Raised on an entity before <see cref="EmpPulseEvent"/>. Cancel this to prevent the emp event being raised.
/// </summary>
public sealed partial class EmpAttemptEvent : CancellableEntityEventArgs
{
}
2023-03-06 22:05:12 +03:00
[ByRefEvent]
2023-09-18 08:28:05 -04:00
public record struct EmpPulseEvent ( float EnergyConsumption , bool Affected , bool Disabled , TimeSpan Duration ) ;
2023-05-07 01:26:04 +10:00
[ByRefEvent]
public record struct EmpDisabledRemoved ( ) ;