2021-11-29 02:34:44 +13:00
using Content.Server.Administration.Logs ;
2023-07-27 07:37:09 +00:00
using Content.Server.Light.Components ;
2021-10-25 16:21:56 +02:00
using Content.Server.NodeContainer ;
using Content.Server.NodeContainer.EntitySystems ;
using Content.Server.NodeContainer.NodeGroups ;
using Content.Server.NodeContainer.Nodes ;
using Content.Server.Power.Components ;
using Content.Server.Power.EntitySystems ;
using Content.Server.Power.NodeGroups ;
2023-05-28 03:03:25 -04:00
using Content.Server.Weapons.Melee ;
2021-10-25 16:21:56 +02:00
using Content.Shared.Damage ;
using Content.Shared.Damage.Prototypes ;
2021-11-28 14:56:53 +01:00
using Content.Shared.Database ;
2021-10-25 16:21:56 +02:00
using Content.Shared.Electrocution ;
using Content.Shared.Interaction ;
2022-09-06 04:48:35 +02:00
using Content.Shared.Inventory ;
2021-10-25 16:21:56 +02:00
using Content.Shared.Jittering ;
using Content.Shared.Maps ;
using Content.Shared.Popups ;
using Content.Shared.Pulling.Components ;
using Content.Shared.Speech.EntitySystems ;
using Content.Shared.StatusEffect ;
using Content.Shared.Stunnable ;
2022-02-13 21:20:47 -06:00
using Content.Shared.Tag ;
2022-09-29 15:51:59 +10:00
using Content.Shared.Weapons.Melee.Events ;
2022-05-09 18:40:15 -07:00
using Robust.Shared.Audio ;
2023-08-06 15:11:43 +10:00
using Robust.Shared.Map ;
2022-09-14 17:26:26 +10:00
using Robust.Shared.Physics.Events ;
2021-10-25 16:21:56 +02:00
using Robust.Shared.Player ;
using Robust.Shared.Prototypes ;
using Robust.Shared.Random ;
2023-04-29 23:05:10 +03:00
namespace Content.Server.Electrocution ;
public sealed class ElectrocutionSystem : SharedElectrocutionSystem
2021-10-25 16:21:56 +02:00
{
2023-08-06 15:11:43 +10:00
[Dependency] private readonly IAdminLogManager _adminLogger = default ! ;
[Dependency] private readonly IMapManager _mapManager = default ! ;
2023-04-29 23:05:10 +03:00
[Dependency] private readonly IPrototypeManager _prototypeManager = default ! ;
2023-08-06 15:11:43 +10:00
[Dependency] private readonly IRobustRandom _random = default ! ;
[Dependency] private readonly DamageableSystem _damageable = default ! ;
[Dependency] private readonly EntityLookupSystem _entityLookup = default ! ;
[Dependency] private readonly MeleeWeaponSystem _meleeWeapon = default ! ;
[Dependency] private readonly NodeContainerSystem _nodeContainer = default ! ;
[Dependency] private readonly NodeGroupSystem _nodeGroup = default ! ;
[Dependency] private readonly SharedAppearanceSystem _appearance = default ! ;
[Dependency] private readonly SharedAudioSystem _audio = default ! ;
2023-04-29 23:05:10 +03:00
[Dependency] private readonly StatusEffectsSystem _statusEffects = default ! ;
[Dependency] private readonly SharedJitteringSystem _jittering = default ! ;
2023-08-06 15:11:43 +10:00
[Dependency] private readonly SharedPopupSystem _popup = default ! ;
2023-04-29 23:05:10 +03:00
[Dependency] private readonly SharedStunSystem _stun = default ! ;
[Dependency] private readonly SharedStutteringSystem _stuttering = default ! ;
[Dependency] private readonly TagSystem _tag = default ! ;
2023-08-28 11:20:31 +02:00
[Dependency] private readonly MetaDataSystem _metaData = default ! ;
2023-04-29 23:05:10 +03:00
2023-08-13 20:26:59 -04:00
[ValidatePrototypeId<StatusEffectPrototype>]
2023-04-29 23:05:10 +03:00
private const string StatusEffectKey = "Electrocution" ;
2023-08-13 20:26:59 -04:00
[ValidatePrototypeId<DamageTypePrototype>]
2023-04-29 23:05:10 +03:00
private const string DamageType = "Shock" ;
// Yes, this is absurdly small for a reason.
private const float ElectrifiedDamagePerWatt = 0.0015f ;
private const float RecursiveDamageMultiplier = 0.75f ;
private const float RecursiveTimeMultiplier = 0.8f ;
private const float ParalyzeTimeMultiplier = 1f ;
private const float StutteringTimeMultiplier = 1.5f ;
private const float JitterTimeMultiplier = 0.75f ;
private const float JitterAmplitude = 80f ;
private const float JitterFrequency = 8f ;
public override void Initialize ( )
2021-10-25 16:21:56 +02:00
{
2023-04-29 23:05:10 +03:00
base . Initialize ( ) ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
SubscribeLocalEvent < ElectrifiedComponent , StartCollideEvent > ( OnElectrifiedStartCollide ) ;
SubscribeLocalEvent < ElectrifiedComponent , AttackedEvent > ( OnElectrifiedAttacked ) ;
SubscribeLocalEvent < ElectrifiedComponent , InteractHandEvent > ( OnElectrifiedHandInteract ) ;
SubscribeLocalEvent < ElectrifiedComponent , InteractUsingEvent > ( OnElectrifiedInteractUsing ) ;
SubscribeLocalEvent < RandomInsulationComponent , MapInitEvent > ( OnRandomInsulationMapInit ) ;
2023-07-27 07:37:09 +00:00
SubscribeLocalEvent < PoweredLightComponent , AttackedEvent > ( OnLightAttacked ) ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
UpdatesAfter . Add ( typeof ( PowerNetSystem ) ) ;
}
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
public override void Update ( float frameTime )
{
UpdateElectrocutions ( frameTime ) ;
UpdateState ( frameTime ) ;
}
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
private void UpdateElectrocutions ( float frameTime )
{
var query = EntityQueryEnumerator < ElectrocutionComponent , PowerConsumerComponent > ( ) ;
while ( query . MoveNext ( out var uid , out var electrocution , out var consumer ) )
{
var timePassed = Math . Min ( frameTime , electrocution . TimeLeft ) ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
electrocution . TimeLeft - = timePassed ;
electrocution . AccumulatedDamage + = consumer . ReceivedPower * ElectrifiedDamagePerWatt * timePassed ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
if ( ! MathHelper . CloseTo ( electrocution . TimeLeft , 0 ) )
continue ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
if ( EntityManager . EntityExists ( electrocution . Electrocuting ) )
{
// TODO: damage should be scaled by shock damage multiplier
// TODO: better paralyze/jitter timing
var damage = new DamageSpecifier ( _prototypeManager . Index < DamageTypePrototype > ( DamageType ) , ( int ) electrocution . AccumulatedDamage ) ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
var actual = _damageable . TryChangeDamage ( electrocution . Electrocuting , damage , origin : electrocution . Source ) ;
if ( actual ! = null )
{
_adminLogger . Add ( LogType . Electrocution ,
$"{ToPrettyString(electrocution.Electrocuting):entity} received {actual.Total:damage} powered electrocution damage from {ToPrettyString(electrocution.Source):source}" ) ;
}
}
QueueDel ( uid ) ;
2021-10-25 16:21:56 +02:00
}
2023-04-29 23:05:10 +03:00
}
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
private void UpdateState ( float frameTime )
{
var query = EntityQueryEnumerator < ActivatedElectrifiedComponent , ElectrifiedComponent , TransformComponent > ( ) ;
while ( query . MoveNext ( out var uid , out var activated , out var electrified , out var transform ) )
2021-10-25 16:21:56 +02:00
{
2023-04-29 23:05:10 +03:00
activated . TimeLeft - = frameTime ;
if ( activated . TimeLeft < = 0 | | ! IsPowered ( uid , electrified , transform ) )
2021-10-25 16:21:56 +02:00
{
2023-04-29 23:05:10 +03:00
_appearance . SetData ( uid , ElectrifiedVisuals . IsPowered , false ) ;
RemComp < ActivatedElectrifiedComponent > ( uid ) ;
2021-10-25 16:21:56 +02:00
}
2023-04-29 23:05:10 +03:00
}
}
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
private bool IsPowered ( EntityUid uid , ElectrifiedComponent electrified , TransformComponent transform )
{
if ( ! electrified . Enabled )
return false ;
if ( electrified . NoWindowInTile )
{
2023-08-06 15:11:43 +10:00
var tileRef = transform . Coordinates . GetTileRef ( EntityManager , _mapManager ) ;
if ( tileRef ! = null )
2021-10-25 16:21:56 +02:00
{
2023-08-06 15:11:43 +10:00
foreach ( var entity in _entityLookup . GetEntitiesIntersecting ( tileRef . Value , flags : LookupFlags . StaticSundries ) )
{
if ( _tag . HasTag ( entity , "Window" ) )
return false ;
}
2021-10-25 16:21:56 +02:00
}
}
2023-04-29 23:05:10 +03:00
if ( electrified . UsesApcPower )
2021-10-25 16:21:56 +02:00
{
2023-04-29 23:05:10 +03:00
if ( ! this . IsPowered ( uid , EntityManager ) )
return false ;
}
else if ( electrified . RequirePower & & PoweredNode ( uid , electrified ) = = null )
return false ;
return true ;
}
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
private void OnElectrifiedStartCollide ( EntityUid uid , ElectrifiedComponent electrified , ref StartCollideEvent args )
{
if ( electrified . OnBump )
2023-05-09 19:21:26 +12:00
TryDoElectrifiedAct ( uid , args . OtherEntity , 1 , electrified ) ;
2023-04-29 23:05:10 +03:00
}
2021-10-25 16:21:56 +02:00
2023-07-27 07:37:09 +00:00
private void OnElectrifiedAttacked ( EntityUid uid , ElectrifiedComponent electrified , AttackedEvent args )
{
if ( ! electrified . OnAttacked )
return ;
2021-10-25 16:21:56 +02:00
2023-07-27 07:37:09 +00:00
if ( _meleeWeapon . GetDamage ( args . Used , args . User ) . Total = = 0 )
return ;
2023-04-12 02:11:55 +02:00
2023-07-27 07:37:09 +00:00
TryDoElectrifiedAct ( uid , args . User , 1 , electrified ) ;
2023-04-29 23:05:10 +03:00
}
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
private void OnElectrifiedHandInteract ( EntityUid uid , ElectrifiedComponent electrified , InteractHandEvent args )
{
if ( electrified . OnHandInteract )
2022-01-15 15:34:43 +13:00
TryDoElectrifiedAct ( uid , args . User , 1 , electrified ) ;
2023-04-29 23:05:10 +03:00
}
2021-10-25 16:21:56 +02:00
2023-07-27 07:37:09 +00:00
private void OnLightAttacked ( EntityUid uid , PoweredLightComponent component , AttackedEvent args )
{
if ( _meleeWeapon . GetDamage ( args . Used , args . User ) . Total = = 0 )
return ;
if ( args . Used ! = args . User )
return ;
if ( component . CurrentLit = = false )
return ;
DoCommonElectrocution ( args . User , uid , component . UnarmedHitShock , component . UnarmedHitStun , false , 1 ) ;
}
2023-04-29 23:05:10 +03:00
private void OnElectrifiedInteractUsing ( EntityUid uid , ElectrifiedComponent electrified , InteractUsingEvent args )
{
if ( ! electrified . OnInteractUsing )
return ;
2021-12-04 01:59:09 -08:00
2023-04-29 23:05:10 +03:00
var siemens = TryComp < InsulatedComponent > ( args . Used , out var insulation )
2023-09-28 16:20:29 -07:00
? insulation . Coefficient
2023-04-29 23:05:10 +03:00
: 1 ;
2022-01-15 15:34:43 +13:00
2023-04-29 23:05:10 +03:00
TryDoElectrifiedAct ( uid , args . User , siemens , electrified ) ;
}
2021-12-04 01:59:09 -08:00
2023-04-29 23:05:10 +03:00
public bool TryDoElectrifiedAct ( EntityUid uid , EntityUid targetUid ,
float siemens = 1 ,
ElectrifiedComponent ? electrified = null ,
NodeContainerComponent ? nodeContainer = null ,
TransformComponent ? transform = null )
{
if ( ! Resolve ( uid , ref electrified , ref transform , false ) )
return false ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
if ( ! IsPowered ( uid , electrified , transform ) )
return false ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
EnsureComp < ActivatedElectrifiedComponent > ( uid ) ;
_appearance . SetData ( uid , ElectrifiedVisuals . IsPowered , true ) ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
siemens * = electrified . SiemensCoefficient ;
if ( siemens < = 0 | | ! DoCommonElectrocutionAttempt ( targetUid , uid , ref siemens ) )
return false ; // If electrocution would fail, do nothing.
2021-10-28 11:50:31 +02:00
2023-04-29 23:05:10 +03:00
var targets = new List < ( EntityUid entity , int depth ) > ( ) ;
GetChainedElectrocutionTargets ( targetUid , targets ) ;
if ( ! electrified . RequirePower | | electrified . UsesApcPower )
{
var lastRet = true ;
for ( var i = targets . Count - 1 ; i > = 0 ; i - - )
2021-10-25 16:21:56 +02:00
{
2023-04-29 23:05:10 +03:00
var ( entity , depth ) = targets [ i ] ;
lastRet = TryDoElectrocution (
entity ,
uid ,
( int ) ( electrified . ShockDamage * MathF . Pow ( RecursiveDamageMultiplier , depth ) ) ,
2023-05-28 03:03:25 -04:00
TimeSpan . FromSeconds ( electrified . ShockTime * MathF . Pow ( RecursiveTimeMultiplier , depth ) ) ,
2023-04-29 23:05:10 +03:00
true ,
electrified . SiemensCoefficient
) ;
2021-10-25 16:21:56 +02:00
}
2023-04-29 23:05:10 +03:00
return lastRet ;
}
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
var node = PoweredNode ( uid , electrified , nodeContainer ) ;
if ( node = = null )
return false ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
var ( damageMult , timeMult ) = node . NodeGroupID switch
{
NodeGroupID . HVPower = > ( electrified . HighVoltageDamageMultiplier , electrified . HighVoltageTimeMultiplier ) ,
NodeGroupID . MVPower = > ( electrified . MediumVoltageDamageMultiplier , electrified . MediumVoltageTimeMultiplier ) ,
_ = > ( 1f , 1f )
} ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
{
var lastRet = true ;
for ( var i = targets . Count - 1 ; i > = 0 ; i - - )
2021-10-25 16:21:56 +02:00
{
2023-04-29 23:05:10 +03:00
var ( entity , depth ) = targets [ i ] ;
lastRet = TryDoElectrocutionPowered (
entity ,
uid ,
node ,
( int ) ( electrified . ShockDamage * MathF . Pow ( RecursiveDamageMultiplier , depth ) * damageMult ) ,
2023-05-28 03:03:25 -04:00
TimeSpan . FromSeconds ( electrified . ShockTime * MathF . Pow ( RecursiveTimeMultiplier , depth ) * timeMult ) ,
2023-04-29 23:05:10 +03:00
true ,
electrified . SiemensCoefficient ) ;
2021-10-25 16:21:56 +02:00
}
2023-04-29 23:05:10 +03:00
return lastRet ;
}
}
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
private Node ? PoweredNode ( EntityUid uid , ElectrifiedComponent electrified , NodeContainerComponent ? nodeContainer = null )
{
if ( ! Resolve ( uid , ref nodeContainer , false ) )
return null ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
return TryNode ( electrified . HighVoltageNode ) ? ? TryNode ( electrified . MediumVoltageNode ) ? ? TryNode ( electrified . LowVoltageNode ) ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
Node ? TryNode ( string? id )
{
if ( id ! = null & &
2023-06-28 14:28:38 +03:00
_nodeContainer . TryGetNode < Node > ( nodeContainer , id , out var tryNode ) & &
2023-04-29 23:05:10 +03:00
tryNode . NodeGroup is IBasePowerNet { NetworkNode : { LastCombinedSupply : > 0 } } )
{
return tryNode ;
2021-10-25 16:21:56 +02:00
}
2023-04-29 23:05:10 +03:00
return null ;
2021-10-25 16:21:56 +02:00
}
2023-04-29 23:05:10 +03:00
}
2021-10-25 16:21:56 +02:00
2023-09-10 07:20:27 +01:00
/// <inheritdoc/>
public override bool TryDoElectrocution (
2021-12-07 09:18:07 +03:00
EntityUid uid , EntityUid ? sourceUid , int shockDamage , TimeSpan time , bool refresh , float siemensCoefficient = 1f ,
2022-09-06 04:48:35 +02:00
StatusEffectsComponent ? statusEffects = null , bool ignoreInsulation = false )
2021-10-25 16:21:56 +02:00
{
2022-09-06 04:48:35 +02:00
if ( ! DoCommonElectrocutionAttempt ( uid , sourceUid , ref siemensCoefficient , ignoreInsulation )
2022-01-05 00:19:23 -08:00
| | ! DoCommonElectrocution ( uid , sourceUid , shockDamage , time , refresh , siemensCoefficient , statusEffects ) )
2021-10-25 16:21:56 +02:00
return false ;
2022-06-22 09:53:41 +10:00
RaiseLocalEvent ( uid , new ElectrocutedEvent ( uid , sourceUid , siemensCoefficient ) , true ) ;
2021-10-25 16:21:56 +02:00
return true ;
}
2023-04-29 23:05:10 +03:00
private bool TryDoElectrocutionPowered (
EntityUid uid ,
EntityUid sourceUid ,
Node node ,
int shockDamage ,
TimeSpan time ,
bool refresh ,
float siemensCoefficient = 1f ,
StatusEffectsComponent ? statusEffects = null ,
TransformComponent ? sourceTransform = null )
{
if ( ! DoCommonElectrocutionAttempt ( uid , sourceUid , ref siemensCoefficient ) )
return false ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
// Coefficient needs to be higher than this to do a powered electrocution!
if ( siemensCoefficient < = 0.5f )
return DoCommonElectrocution ( uid , sourceUid , shockDamage , time , refresh , siemensCoefficient , statusEffects ) ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
if ( ! DoCommonElectrocution ( uid , sourceUid , null , time , refresh , siemensCoefficient , statusEffects ) )
return false ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
if ( ! Resolve ( sourceUid , ref sourceTransform ) ) // This shouldn't really happen, but just in case...
return true ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
var electrocutionEntity = Spawn ( $"VirtualElectrocutionLoad{node.NodeGroupID}" , sourceTransform . Coordinates ) ;
2023-06-28 14:28:38 +03:00
var nodeContainer = Comp < NodeContainerComponent > ( electrocutionEntity ) ;
if ( ! _nodeContainer . TryGetNode < ElectrocutionNode > ( nodeContainer , "electrocution" , out var electrocutionNode ) )
return false ;
2023-04-29 23:05:10 +03:00
var electrocutionComponent = Comp < ElectrocutionComponent > ( electrocutionEntity ) ;
2021-10-25 16:21:56 +02:00
2023-06-03 21:13:30 +02:00
// This shows up in the power monitor.
// Yes. Yes exactly.
2023-08-28 11:20:31 +02:00
_metaData . SetEntityName ( electrocutionEntity , MetaData ( uid ) . EntityName ) ;
2023-06-03 21:13:30 +02:00
2023-04-29 23:05:10 +03:00
electrocutionNode . CableEntity = sourceUid ;
electrocutionNode . NodeName = node . Name ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
_nodeGroup . QueueReflood ( electrocutionNode ) ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
electrocutionComponent . TimeLeft = 1f ;
electrocutionComponent . Electrocuting = uid ;
electrocutionComponent . Source = sourceUid ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
RaiseLocalEvent ( uid , new ElectrocutedEvent ( uid , sourceUid , siemensCoefficient ) , true ) ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
return true ;
}
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
private bool DoCommonElectrocutionAttempt ( EntityUid uid , EntityUid ? sourceUid , ref float siemensCoefficient , bool ignoreInsulation = false )
{
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
var attemptEvent = new ElectrocutionAttemptEvent ( uid , sourceUid , siemensCoefficient ,
ignoreInsulation ? SlotFlags . NONE : ~ SlotFlags . POCKET ) ;
RaiseLocalEvent ( uid , attemptEvent , true ) ;
2022-09-06 04:48:35 +02:00
2023-04-29 23:05:10 +03:00
// Cancel the electrocution early, so we don't recursively electrocute anything.
if ( attemptEvent . Cancelled )
return false ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
siemensCoefficient = attemptEvent . SiemensCoefficient ;
return true ;
}
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
private bool DoCommonElectrocution ( EntityUid uid , EntityUid ? sourceUid ,
int? shockDamage , TimeSpan time , bool refresh , float siemensCoefficient = 1f ,
StatusEffectsComponent ? statusEffects = null )
{
if ( siemensCoefficient < = 0 )
return false ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
if ( shockDamage ! = null )
2021-10-25 16:21:56 +02:00
{
2023-04-29 23:05:10 +03:00
shockDamage = ( int ) ( shockDamage * siemensCoefficient ) ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
if ( shockDamage . Value < = 0 )
2021-10-25 16:21:56 +02:00
return false ;
2023-04-29 23:05:10 +03:00
}
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
if ( ! Resolve ( uid , ref statusEffects , false ) | |
! _statusEffects . CanApplyEffect ( uid , StatusEffectKey , statusEffects ) )
{
return false ;
}
2023-05-28 03:03:25 -04:00
2023-04-29 23:05:10 +03:00
if ( ! _statusEffects . TryAddStatusEffect < ElectrocutedComponent > ( uid , StatusEffectKey , time , refresh , statusEffects ) )
return false ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
var shouldStun = siemensCoefficient > 0.5f ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
if ( shouldStun )
_stun . TryParalyze ( uid , time * ParalyzeTimeMultiplier , refresh , statusEffects ) ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
// TODO: Sparks here.
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
if ( shockDamage is { } dmg )
{
var actual = _damageable . TryChangeDamage ( uid ,
new DamageSpecifier ( _prototypeManager . Index < DamageTypePrototype > ( DamageType ) , dmg ) , origin : sourceUid ) ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
if ( actual ! = null )
{
_adminLogger . Add ( LogType . Electrocution ,
$"{ToPrettyString(uid):entity} received {actual.Total:damage} powered electrocution damage{(sourceUid != null ? " from " + ToPrettyString(sourceUid.Value) : " "):source}" ) ;
2021-11-29 02:34:44 +13:00
}
2023-04-29 23:05:10 +03:00
}
2021-11-29 02:34:44 +13:00
2023-04-29 23:05:10 +03:00
_stuttering . DoStutter ( uid , time * StutteringTimeMultiplier , refresh , statusEffects ) ;
_jittering . DoJitter ( uid , time * JitterTimeMultiplier , refresh , JitterAmplitude , JitterFrequency , true , statusEffects ) ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
_popup . PopupEntity ( Loc . GetString ( "electrocuted-component-mob-shocked-popup-player" ) , uid , uid ) ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
var filter = Filter . PvsExcept ( uid , entityManager : EntityManager ) ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
// TODO: Allow being able to pass EntityUid to Loc...
if ( sourceUid ! = null )
{
_popup . PopupEntity ( Loc . GetString ( "electrocuted-component-mob-shocked-by-source-popup-others" ,
( "mob" , uid ) , ( "source" , ( sourceUid . Value ) ) ) , uid , filter , true ) ;
PlayElectrocutionSound ( uid , sourceUid . Value ) ;
2021-10-25 16:21:56 +02:00
}
2023-04-29 23:05:10 +03:00
else
2021-10-25 16:21:56 +02:00
{
2023-04-29 23:05:10 +03:00
_popup . PopupEntity ( Loc . GetString ( "electrocuted-component-mob-shocked-popup-others" ,
( "mob" , uid ) ) , uid , filter , true ) ;
2021-10-25 16:21:56 +02:00
}
2023-04-29 23:05:10 +03:00
return true ;
}
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
private void GetChainedElectrocutionTargets ( EntityUid source , List < ( EntityUid entity , int depth ) > all )
{
var visited = new HashSet < EntityUid > ( ) ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
GetChainedElectrocutionTargetsRecurse ( source , 1 , visited , all ) ;
}
private void GetChainedElectrocutionTargetsRecurse (
EntityUid entity ,
int depth ,
HashSet < EntityUid > visited ,
List < ( EntityUid entity , int depth ) > all )
{
all . Add ( ( entity , depth ) ) ;
visited . Add ( entity ) ;
if ( TryComp < SharedPullableComponent > ( entity , out var pullable ) & &
pullable . Puller is { Valid : true } pullerId & &
! visited . Contains ( pullerId ) )
{
GetChainedElectrocutionTargetsRecurse ( pullerId , depth + 1 , visited , all ) ;
2021-10-25 16:21:56 +02:00
}
2023-04-29 23:05:10 +03:00
if ( TryComp < SharedPullerComponent > ( entity , out var puller ) & &
puller . Pulling is { Valid : true } pullingId & &
! visited . Contains ( pullingId ) )
2021-10-25 16:21:56 +02:00
{
2023-04-29 23:05:10 +03:00
GetChainedElectrocutionTargetsRecurse ( pullingId , depth + 1 , visited , all ) ;
}
}
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
private void OnRandomInsulationMapInit ( EntityUid uid , RandomInsulationComponent randomInsulation ,
MapInitEvent args )
{
if ( ! TryComp < InsulatedComponent > ( uid , out var insulated ) )
return ;
2021-10-25 16:21:56 +02:00
2023-04-29 23:05:10 +03:00
if ( randomInsulation . List . Length = = 0 )
return ;
2022-05-09 18:40:15 -07:00
2023-04-29 23:05:10 +03:00
SetInsulatedSiemensCoefficient ( uid , _random . Pick ( randomInsulation . List ) , insulated ) ;
}
2022-05-09 18:40:15 -07:00
2023-04-29 23:05:10 +03:00
private void PlayElectrocutionSound ( EntityUid targetUid , EntityUid sourceUid , ElectrifiedComponent ? electrified = null )
{
if ( ! Resolve ( sourceUid , ref electrified ) | | ! electrified . PlaySoundOnShock )
{
return ;
2022-05-09 18:40:15 -07:00
}
2023-04-29 23:05:10 +03:00
_audio . PlayPvs ( electrified . ShockNoises , targetUid , AudioParams . Default . WithVolume ( electrified . ShockVolume ) ) ;
2021-10-25 16:21:56 +02:00
}
}