2022-08-07 20:21:56 -03:00
using System.Linq ;
2021-12-05 15:34:32 +13:00
using Content.Server.Administration.Logs ;
2022-11-20 01:49:37 -05:00
using Content.Server.Body.Systems ;
2022-08-07 20:21:56 -03:00
using Content.Server.Chemistry.Components.SolutionManager ;
2021-07-25 20:09:08 +10:00
using Content.Server.Explosion.Components ;
2021-09-09 16:20:41 +02:00
using Content.Server.Flash ;
2021-07-25 20:09:08 +10:00
using Content.Server.Flash.Components ;
2023-07-16 04:59:46 +02:00
using Content.Server.Radio.EntitySystems ;
2022-03-25 17:17:29 +13:00
using Content.Shared.Database ;
2022-11-20 01:49:37 -05:00
using Content.Shared.Implants.Components ;
2022-05-29 02:29:10 -04:00
using Content.Shared.Interaction ;
2022-08-07 20:21:56 -03:00
using Content.Shared.Payload.Components ;
2023-07-16 04:59:46 +02:00
using Robust.Shared.Prototypes ;
using Content.Shared.Radio ;
2023-03-23 18:54:14 +03:00
using Content.Shared.Slippery ;
2022-07-10 02:28:37 -07:00
using Content.Shared.StepTrigger.Systems ;
2022-11-20 21:51:44 -05:00
using Content.Shared.Trigger ;
using JetBrains.Annotations ;
using Robust.Shared.Audio ;
2022-08-07 20:21:56 -03:00
using Robust.Shared.Containers ;
2022-09-14 17:26:26 +10:00
using Robust.Shared.Physics.Events ;
using Robust.Shared.Physics.Systems ;
2023-07-16 04:59:46 +02:00
using Content.Shared.Mobs ;
using Content.Shared.Mobs.Components ;
2023-08-01 21:38:22 +02:00
using Content.Shared.Weapons.Ranged.Events ;
2019-06-07 16:15:20 +05:00
2021-11-09 21:24:35 +01:00
namespace Content.Server.Explosion.EntitySystems
2019-06-07 16:15:20 +05:00
{
/// <summary>
2021-07-25 20:09:08 +10:00
/// Raised whenever something is Triggered on the entity.
2019-06-07 16:15:20 +05:00
/// </summary>
2022-02-06 15:59:41 +00:00
public sealed class TriggerEvent : HandledEntityEventArgs
2019-06-07 16:15:20 +05:00
{
2021-12-05 18:09:01 +01:00
public EntityUid Triggered { get ; }
2021-12-05 15:34:32 +13:00
public EntityUid ? User { get ; }
2019-06-07 16:15:20 +05:00
2021-12-07 17:48:49 +01:00
public TriggerEvent ( EntityUid triggered , EntityUid ? user = null )
2021-03-16 15:50:20 +01:00
{
2021-07-25 20:09:08 +10:00
Triggered = triggered ;
2021-03-16 15:50:20 +01:00
User = user ;
}
2019-06-07 16:15:20 +05:00
}
2023-04-22 14:40:36 +03:00
/// <summary>
/// Raised when timer trigger becomes active.
/// </summary>
[ByRefEvent]
public readonly record struct ActiveTimerTriggerEvent ( EntityUid Triggered , EntityUid ? User ) ;
2019-06-07 16:15:20 +05:00
[UsedImplicitly]
2022-01-29 22:45:50 +11:00
public sealed partial class TriggerSystem : EntitySystem
2019-06-07 16:15:20 +05:00
{
2021-11-09 21:24:35 +01:00
[Dependency] private readonly ExplosionSystem _explosions = default ! ;
2022-02-06 15:59:41 +00:00
[Dependency] private readonly FixtureSystem _fixtures = default ! ;
2021-09-09 16:20:41 +02:00
[Dependency] private readonly FlashSystem _flashSystem = default ! ;
2022-02-06 15:59:41 +00:00
[Dependency] private readonly SharedBroadphaseSystem _broadphase = default ! ;
2022-08-07 20:21:56 -03:00
[Dependency] private readonly IAdminLogManager _adminLogger = default ! ;
[Dependency] private readonly SharedContainerSystem _container = default ! ;
2022-11-20 01:49:37 -05:00
[Dependency] private readonly BodySystem _body = default ! ;
2023-05-02 18:13:39 +03:00
[Dependency] private readonly SharedAudioSystem _audio = default ! ;
2023-08-10 21:29:47 +07:00
[Dependency] private readonly SharedTransformSystem _transformSystem = default ! ;
2023-07-16 04:59:46 +02:00
[Dependency] private readonly RadioSystem _radioSystem = default ! ;
[Dependency] private readonly IPrototypeManager _prototypeManager = default ! ;
2021-09-09 16:20:41 +02:00
2021-07-25 20:09:08 +10:00
public override void Initialize ( )
2019-06-07 16:15:20 +05:00
{
2021-07-25 20:09:08 +10:00
base . Initialize ( ) ;
2022-01-29 22:45:50 +11:00
2022-02-06 15:59:41 +00:00
InitializeProximity ( ) ;
2022-01-29 22:45:50 +11:00
InitializeOnUse ( ) ;
2022-04-22 02:54:39 -04:00
InitializeSignal ( ) ;
2022-05-29 02:29:10 -04:00
InitializeTimedCollide ( ) ;
2022-10-16 13:44:50 -04:00
InitializeVoice ( ) ;
2022-11-20 21:51:44 -05:00
InitializeMobstate ( ) ;
2022-01-29 22:45:50 +11:00
2022-02-06 15:59:41 +00:00
SubscribeLocalEvent < TriggerOnCollideComponent , StartCollideEvent > ( OnTriggerCollide ) ;
2022-05-29 02:29:10 -04:00
SubscribeLocalEvent < TriggerOnActivateComponent , ActivateInWorldEvent > ( OnActivate ) ;
2022-11-20 01:49:37 -05:00
SubscribeLocalEvent < TriggerImplantActionComponent , ActivateImplantEvent > ( OnImplantTrigger ) ;
2022-07-10 02:28:37 -07:00
SubscribeLocalEvent < TriggerOnStepTriggerComponent , StepTriggeredEvent > ( OnStepTriggered ) ;
2023-03-23 18:54:14 +03:00
SubscribeLocalEvent < TriggerOnSlipComponent , SlipEvent > ( OnSlipTriggered ) ;
2023-08-01 21:38:22 +02:00
SubscribeLocalEvent < TriggerWhenEmptyComponent , OnEmptyGunShotEvent > ( OnEmptyTriggered ) ;
2019-06-07 16:15:20 +05:00
2023-04-26 13:51:48 +10:00
SubscribeLocalEvent < SpawnOnTriggerComponent , TriggerEvent > ( OnSpawnTrigger ) ;
2021-07-25 20:09:08 +10:00
SubscribeLocalEvent < DeleteOnTriggerComponent , TriggerEvent > ( HandleDeleteTrigger ) ;
SubscribeLocalEvent < ExplodeOnTriggerComponent , TriggerEvent > ( HandleExplodeTrigger ) ;
SubscribeLocalEvent < FlashOnTriggerComponent , TriggerEvent > ( HandleFlashTrigger ) ;
2022-11-20 01:49:37 -05:00
SubscribeLocalEvent < GibOnTriggerComponent , TriggerEvent > ( HandleGibTrigger ) ;
2023-08-10 21:29:47 +07:00
SubscribeLocalEvent < AnchorOnTriggerComponent , TriggerEvent > ( OnAnchorTrigger ) ;
SubscribeLocalEvent < SoundOnTriggerComponent , TriggerEvent > ( OnSoundTrigger ) ;
2023-07-16 04:59:46 +02:00
SubscribeLocalEvent < RattleComponent , TriggerEvent > ( HandleRattleTrigger ) ;
2021-07-25 20:09:08 +10:00
}
2023-08-10 21:29:47 +07:00
private void OnSoundTrigger ( EntityUid uid , SoundOnTriggerComponent component , TriggerEvent args )
{
_audio . PlayPvs ( component . Sound , uid ) ;
if ( component . RemoveOnTrigger )
RemCompDeferred < SoundOnTriggerComponent > ( uid ) ;
}
private void OnAnchorTrigger ( EntityUid uid , AnchorOnTriggerComponent component , TriggerEvent args )
{
var xform = Transform ( uid ) ;
if ( xform . Anchored )
return ;
_transformSystem . AnchorEntity ( uid , xform ) ;
2023-09-04 19:23:48 +07:00
if ( component . RemoveOnTrigger )
2023-08-10 21:29:47 +07:00
RemCompDeferred < AnchorOnTriggerComponent > ( uid ) ;
}
2023-04-26 13:51:48 +10:00
private void OnSpawnTrigger ( EntityUid uid , SpawnOnTriggerComponent component , TriggerEvent args )
{
var xform = Transform ( uid ) ;
var coords = xform . Coordinates ;
if ( ! coords . IsValid ( EntityManager ) )
return ;
Spawn ( component . Proto , coords ) ;
}
2021-07-25 20:09:08 +10:00
private void HandleExplodeTrigger ( EntityUid uid , ExplodeOnTriggerComponent component , TriggerEvent args )
{
2022-04-01 15:39:26 +13:00
_explosions . TriggerExplosive ( uid , user : args . User ) ;
2022-06-01 01:39:06 -07:00
args . Handled = true ;
2021-07-25 20:09:08 +10:00
}
private void HandleFlashTrigger ( EntityUid uid , FlashOnTriggerComponent component , TriggerEvent args )
2021-09-09 16:20:41 +02:00
{
// TODO Make flash durations sane ffs.
2021-12-03 15:53:09 +01:00
_flashSystem . FlashArea ( uid , args . User , component . Range , component . Duration * 1000f ) ;
2022-06-01 01:39:06 -07:00
args . Handled = true ;
2021-09-09 16:20:41 +02:00
}
2021-07-25 20:09:08 +10:00
private void HandleDeleteTrigger ( EntityUid uid , DeleteOnTriggerComponent component , TriggerEvent args )
{
EntityManager . QueueDeleteEntity ( uid ) ;
2022-06-01 01:39:06 -07:00
args . Handled = true ;
2021-07-25 20:09:08 +10:00
}
2022-11-20 01:49:37 -05:00
private void HandleGibTrigger ( EntityUid uid , GibOnTriggerComponent component , TriggerEvent args )
{
if ( ! TryComp < TransformComponent > ( uid , out var xform ) )
return ;
_body . GibBody ( xform . ParentUid , deleteItems : component . DeleteItems ) ;
args . Handled = true ;
}
2023-07-16 04:59:46 +02:00
private void HandleRattleTrigger ( EntityUid uid , RattleComponent component , TriggerEvent args )
{
if ( ! TryComp < SubdermalImplantComponent ? > ( uid , out var implanted ) )
return ;
if ( implanted . ImplantedEntity = = null )
return ;
// Gets location of the implant
var ownerXform = Transform ( uid ) ;
var pos = ownerXform . MapPosition ;
var x = ( int ) pos . X ;
var y = ( int ) pos . Y ;
var posText = $"({x}, {y})" ;
var critMessage = Loc . GetString ( component . CritMessage , ( "user" , implanted . ImplantedEntity . Value ) , ( "position" , posText ) ) ;
var deathMessage = Loc . GetString ( component . DeathMessage , ( "user" , implanted . ImplantedEntity . Value ) , ( "position" , posText ) ) ;
if ( ! TryComp < MobStateComponent > ( implanted . ImplantedEntity , out var mobstate ) )
return ;
// Sends a message to the radio channel specified by the implant
if ( mobstate . CurrentState = = MobState . Critical )
_radioSystem . SendRadioMessage ( uid , critMessage , _prototypeManager . Index < RadioChannelPrototype > ( component . RadioChannel ) , uid ) ;
if ( mobstate . CurrentState = = MobState . Dead )
_radioSystem . SendRadioMessage ( uid , deathMessage , _prototypeManager . Index < RadioChannelPrototype > ( component . RadioChannel ) , uid ) ;
args . Handled = true ;
}
2022-11-20 01:49:37 -05:00
2022-09-14 17:26:26 +10:00
private void OnTriggerCollide ( EntityUid uid , TriggerOnCollideComponent component , ref StartCollideEvent args )
2021-07-25 20:09:08 +10:00
{
2023-08-23 18:55:58 +10:00
if ( args . OurFixtureId = = component . FixtureID & & ( ! component . IgnoreOtherNonHard | | args . OtherFixture . Hard ) )
Trigger ( uid ) ;
2021-07-25 20:09:08 +10:00
}
2022-05-29 02:29:10 -04:00
private void OnActivate ( EntityUid uid , TriggerOnActivateComponent component , ActivateInWorldEvent args )
{
2023-08-23 18:55:58 +10:00
Trigger ( uid , args . User ) ;
2022-06-01 01:39:06 -07:00
args . Handled = true ;
2022-05-29 02:29:10 -04:00
}
2021-12-07 17:48:49 +01:00
2022-11-20 01:49:37 -05:00
private void OnImplantTrigger ( EntityUid uid , TriggerImplantActionComponent component , ActivateImplantEvent args )
{
2023-08-14 09:04:41 -07:00
args . Handled = Trigger ( uid ) ;
2022-11-20 01:49:37 -05:00
}
2022-07-10 02:28:37 -07:00
private void OnStepTriggered ( EntityUid uid , TriggerOnStepTriggerComponent component , ref StepTriggeredEvent args )
{
Trigger ( uid , args . Tripper ) ;
}
2023-03-23 18:54:14 +03:00
private void OnSlipTriggered ( EntityUid uid , TriggerOnSlipComponent component , ref SlipEvent args )
{
Trigger ( uid , args . Slipped ) ;
}
2023-08-01 21:38:22 +02:00
private void OnEmptyTriggered ( EntityUid uid , TriggerWhenEmptyComponent component , ref OnEmptyGunShotEvent args )
{
Trigger ( uid , args . EmptyGun ) ;
}
2022-06-01 01:39:06 -07:00
public bool Trigger ( EntityUid trigger , EntityUid ? user = null )
2021-07-25 20:09:08 +10:00
{
var triggerEvent = new TriggerEvent ( trigger , user ) ;
2022-06-22 09:53:41 +10:00
EntityManager . EventBus . RaiseLocalEvent ( trigger , triggerEvent , true ) ;
2022-06-01 01:39:06 -07:00
return triggerEvent . Handled ;
2021-07-25 20:09:08 +10:00
}
2023-05-02 18:13:39 +03:00
public void HandleTimerTrigger ( EntityUid uid , EntityUid ? user , float delay , float beepInterval , float? initialBeepDelay , SoundSpecifier ? beepSound )
2021-07-25 20:09:08 +10:00
{
2022-03-25 17:17:29 +13:00
if ( delay < = 0 )
2021-07-25 20:09:08 +10:00
{
2022-03-25 17:17:29 +13:00
RemComp < ActiveTimerTriggerComponent > ( uid ) ;
Trigger ( uid , user ) ;
2021-07-25 20:09:08 +10:00
return ;
}
2022-03-25 17:17:29 +13:00
if ( HasComp < ActiveTimerTriggerComponent > ( uid ) )
return ;
if ( user ! = null )
2021-07-25 20:09:08 +10:00
{
2022-08-07 20:21:56 -03:00
// Check if entity is bomb/mod. grenade/etc
2023-09-10 21:46:36 +10:00
if ( _container . TryGetContainer ( uid , "payload" , out IContainer ? container ) & &
2022-08-29 11:48:49 +12:00
container . ContainedEntities . Count > 0 & &
TryComp ( container . ContainedEntities [ 0 ] , out ChemicalPayloadComponent ? chemicalPayloadComponent ) )
2022-08-07 20:21:56 -03:00
{
// If a beaker is missing, the entity won't explode, so no reason to log it
if ( ! TryComp ( chemicalPayloadComponent ? . BeakerSlotA . Item , out SolutionContainerManagerComponent ? beakerA ) | |
! TryComp ( chemicalPayloadComponent ? . BeakerSlotB . Item , out SolutionContainerManagerComponent ? beakerB ) )
return ;
_adminLogger . Add ( LogType . Trigger ,
$"{ToPrettyString(user.Value):user} started a {delay} second timer trigger on entity {ToPrettyString(uid):timer}, which contains [{string.Join(" , ", beakerA.Solutions.Values.First())}] in one beaker and [{string.Join(" , ", beakerB.Solutions.Values.First())}] in the other." ) ;
}
else
{
_adminLogger . Add ( LogType . Trigger ,
$"{ToPrettyString(user.Value):user} started a {delay} second timer trigger on entity {ToPrettyString(uid):timer}" ) ;
}
2022-03-25 17:17:29 +13:00
}
else
{
2022-05-28 23:41:17 -07:00
_adminLogger . Add ( LogType . Trigger ,
2022-03-25 17:17:29 +13:00
$"{delay} second timer trigger started on entity {ToPrettyString(uid):timer}" ) ;
}
var active = AddComp < ActiveTimerTriggerComponent > ( uid ) ;
active . TimeRemaining = delay ;
active . User = user ;
active . BeepSound = beepSound ;
active . BeepInterval = beepInterval ;
active . TimeUntilBeep = initialBeepDelay = = null ? active . BeepInterval : initialBeepDelay . Value ;
2023-04-22 14:40:36 +03:00
var ev = new ActiveTimerTriggerEvent ( uid , user ) ;
RaiseLocalEvent ( uid , ref ev ) ;
2022-03-25 17:17:29 +13:00
if ( TryComp < AppearanceComponent > ( uid , out var appearance ) )
2023-02-02 17:34:53 +01:00
_appearance . SetData ( uid , TriggerVisuals . VisualState , TriggerVisualState . Primed , appearance ) ;
2019-06-07 16:15:20 +05:00
}
2022-02-06 15:59:41 +00:00
public override void Update ( float frameTime )
{
base . Update ( frameTime ) ;
2023-06-27 21:17:06 -04:00
UpdateProximity ( ) ;
2022-03-25 17:17:29 +13:00
UpdateTimer ( frameTime ) ;
2022-05-29 02:29:10 -04:00
UpdateTimedCollide ( frameTime ) ;
2022-03-25 17:17:29 +13:00
}
private void UpdateTimer ( float frameTime )
{
HashSet < EntityUid > toRemove = new ( ) ;
2023-05-02 18:13:39 +03:00
var query = EntityQueryEnumerator < ActiveTimerTriggerComponent > ( ) ;
while ( query . MoveNext ( out var uid , out var timer ) )
2022-03-25 17:17:29 +13:00
{
timer . TimeRemaining - = frameTime ;
timer . TimeUntilBeep - = frameTime ;
if ( timer . TimeRemaining < = 0 )
{
2023-05-02 18:13:39 +03:00
Trigger ( uid , timer . User ) ;
toRemove . Add ( uid ) ;
2022-03-25 17:17:29 +13:00
continue ;
}
if ( timer . BeepSound = = null | | timer . TimeUntilBeep > 0 )
continue ;
timer . TimeUntilBeep + = timer . BeepInterval ;
2023-05-02 18:13:39 +03:00
_audio . PlayPvs ( timer . BeepSound , uid , timer . BeepSound . Params ) ;
2022-03-25 17:17:29 +13:00
}
foreach ( var uid in toRemove )
{
RemComp < ActiveTimerTriggerComponent > ( uid ) ;
// In case this is a re-usable grenade, un-prime it.
if ( TryComp < AppearanceComponent > ( uid , out var appearance ) )
2023-02-02 17:34:53 +01:00
_appearance . SetData ( uid , TriggerVisuals . VisualState , TriggerVisualState . Unprimed , appearance ) ;
2022-03-25 17:17:29 +13:00
}
2022-02-06 15:59:41 +00:00
}
2019-06-07 16:15:20 +05:00
}
2020-07-06 14:27:03 -07:00
}