1818using Robust . Shared . Prototypes ;
1919using Robust . Shared . Random ;
2020using Robust . Shared . Utility ;
21+ #region Starlight
22+ using Content . Shared . DoAfter ;
23+ using Content . Shared . Projectiles ;
24+ using Content . Shared . Singularity . Components ;
25+ using Content . Server . Singularity . EntitySystems ;
26+ using Content . Shared . Power . EntitySystems ;
27+ using Content . Shared . _Starlight . CosmicCult . Components ;
28+ #endregion
2129
2230namespace Content . Server . Anomaly ;
2331
@@ -39,6 +47,11 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
3947 [ Dependency ] private readonly RadiationSystem _radiation = default ! ;
4048 [ Dependency ] private readonly SharedAudioSystem _audio = default ! ;
4149 [ Dependency ] private readonly UserInterfaceSystem _ui = default ! ;
50+ #region Starlight
51+ [ Dependency ] private SharedDoAfterSystem _doAfter = default ! ;
52+ [ Dependency ] private EmitterSystem _emitter = default ! ;
53+ [ Dependency ] private SharedPowerReceiverSystem _powerReceiver = default ! ;
54+ #endregion
4255
4356 public const float MinParticleVariation = 0.8f ;
4457 public const float MaxParticleVariation = 1.2f ;
@@ -53,6 +66,9 @@ public override void Initialize()
5366 SubscribeLocalEvent < AnomalyComponent , ComponentShutdown > ( OnShutdown ) ;
5467 SubscribeLocalEvent < AnomalyComponent , StartCollideEvent > ( OnStartCollide ) ;
5568 SubscribeLocalEvent < AnomalyStabilityChangedEvent > ( OnVesselAnomalyStabilityChanged ) ;
69+ #region Starlight
70+ SubscribeLocalEvent < AnomalyComponent , AnomalyParticleInteractionDoAfterEvent > ( OnParticleInteractionDoAfter ) ;
71+ #endregion
5672
5773 InitializeGenerator ( ) ;
5874 InitializeVessel ( ) ;
@@ -61,13 +77,22 @@ public override void Initialize()
6177
6278 private void OnMapInit ( Entity < AnomalyComponent > anomaly , ref MapInitEvent args )
6379 {
64- anomaly . Comp . NextPulseTime = Timing . CurTime + GetPulseLength ( anomaly . Comp ) * 3 ; // longer the first time
80+ // Starlight edit Start
81+ if ( anomaly . Comp . CanPulse )
82+ anomaly . Comp . NextPulseTime = Timing . CurTime + GetPulseLength ( anomaly . Comp ) * 3 ; // longer the first time
83+ // Starlight edit End
6584 ChangeAnomalyStability ( anomaly , Random . NextFloat ( anomaly . Comp . InitialStabilityRange . Item1 , anomaly . Comp . InitialStabilityRange . Item2 ) , anomaly . Comp ) ;
6685 ChangeAnomalySeverity ( anomaly , Random . NextFloat ( anomaly . Comp . InitialSeverityRange . Item1 , anomaly . Comp . InitialSeverityRange . Item2 ) , anomaly . Comp ) ;
6786
68- ShuffleParticlesEffect ( anomaly ) ;
87+ // Starlight edit Start
88+ if ( anomaly . Comp . ShuffleParticlesOnMapInit )
89+ ShuffleParticlesEffect ( anomaly ) ;
90+
6991 anomaly . Comp . Continuity = _random . NextFloat ( anomaly . Comp . MinContituty , anomaly . Comp . MaxContituty ) ;
70- SetBehavior ( anomaly , GetRandomBehavior ( ) ) ;
92+
93+ if ( anomaly . Comp . RandomBehaviorOnMapInit )
94+ SetBehavior ( anomaly , GetRandomBehavior ( ) ) ;
95+ // Starlight edit End
7196 }
7297
7398 public void ShuffleParticlesEffect ( Entity < AnomalyComponent > anomaly )
@@ -112,26 +137,38 @@ private void OnStartCollide(Entity<AnomalyComponent> anomaly, ref StartCollideEv
112137 // small function to randomize because it's easier to read like this
113138 float VaryValue ( float v ) => v * behaviorMod * Random . NextFloat ( MinParticleVariation , MaxParticleVariation ) ;
114139
115- if ( particle . ParticleType == anomaly . Comp . DestabilizingParticleType || particle . DestabilzingOverride )
140+ #region Starlight Edit
141+ if ( anomaly . Comp . UseNormalUnstableParticle &&
142+ ( particle . ParticleType == anomaly . Comp . DestabilizingParticleType || particle . DestabilzingOverride ) )
116143 {
117144 ChangeAnomalyStability ( anomaly , VaryValue ( particle . StabilityPerDestabilizingHit ) , anomaly . Comp ) ;
118145 }
119- if ( particle . ParticleType == anomaly . Comp . SeverityParticleType || particle . SeverityOverride )
146+
147+ if ( anomaly . Comp . UseNormalDangerParticle &&
148+ ( particle . ParticleType == anomaly . Comp . SeverityParticleType || particle . SeverityOverride ) )
120149 {
121150 ChangeAnomalySeverity ( anomaly , VaryValue ( particle . SeverityPerSeverityHit ) , anomaly . Comp ) ;
122151 }
123- if ( particle . ParticleType == anomaly . Comp . WeakeningParticleType || particle . WeakeningOverride )
152+
153+ if ( anomaly . Comp . UseNormalContainmentParticle &&
154+ ( particle . ParticleType == anomaly . Comp . WeakeningParticleType || particle . WeakeningOverride ) )
124155 {
125156 ChangeAnomalyHealth ( anomaly , VaryValue ( particle . HealthPerWeakeningeHit ) , anomaly . Comp ) ;
126157 ChangeAnomalyStability ( anomaly , VaryValue ( particle . StabilityPerWeakeningeHit ) , anomaly . Comp ) ;
127158 }
128- if ( particle . ParticleType == anomaly . Comp . TransformationParticleType || particle . TransmutationOverride )
159+
160+ if ( anomaly . Comp . UseNormalTransformationParticle &&
161+ ( particle . ParticleType == anomaly . Comp . TransformationParticleType || particle . TransmutationOverride ) )
129162 {
130163 ChangeAnomalySeverity ( anomaly , VaryValue ( particle . SeverityPerSeverityHit ) , anomaly . Comp ) ;
164+
131165 if ( _random . Prob ( anomaly . Comp . Continuity ) )
132166 SetBehavior ( anomaly , GetRandomBehavior ( ) ) ;
133167 }
134168
169+ TryStartParticleInteraction ( anomaly , particle , args . OtherEntity ) ;
170+ #endregion
171+
135172 var ev = new AnomalyAffectedByParticleEvent ( anomaly , args . OtherEntity ) ;
136173 RaiseLocalEvent ( anomaly , ref ev ) ;
137174 }
@@ -223,6 +260,165 @@ private void RemoveBehavior(Entity<AnomalyComponent> anomaly, ProtoId<AnomalyBeh
223260
224261 EntityManager . RemoveComponents ( anomaly , behavior . Components ) ;
225262 }
263+
264+ #region Starlight
265+ private void TryStartParticleInteraction (
266+ Entity < AnomalyComponent > anomaly ,
267+ AnomalousParticleComponent particle ,
268+ EntityUid particleUid )
269+ {
270+ if ( anomaly . Comp . ParticleInteractions . Count == 0 )
271+ return ;
272+
273+ if ( _doAfter . IsRunning ( anomaly . Comp . ParticleInteractionDoAfter ) )
274+ return ;
275+
276+ var interactionIndex = GetMatchingParticleInteraction ( anomaly . Comp , particle ) ;
277+
278+ if ( interactionIndex == null )
279+ return ;
280+
281+ var interaction = anomaly . Comp . ParticleInteractions [ interactionIndex . Value ] ;
282+
283+ if ( ! TryComp < ProjectileComponent > ( particleUid , out var projectile ) ||
284+ projectile . Shooter is not { } shooter )
285+ return ;
286+
287+ var source = GetPoweredNullspaceStabilizerSource ( shooter ) ;
288+ if ( source == null )
289+ return ;
290+
291+ if ( interaction . Delay <= TimeSpan . Zero )
292+ {
293+ ApplyParticleInteraction ( anomaly , interaction , shooter ) ;
294+ return ;
295+ }
296+
297+ var doAfterArgs = new DoAfterArgs (
298+ EntityManager ,
299+ shooter ,
300+ interaction . Delay ,
301+ new AnomalyParticleInteractionDoAfterEvent ( ) ,
302+ anomaly ,
303+ anomaly )
304+ {
305+ NeedHand = false ,
306+ BreakOnWeightlessMove = true ,
307+ BreakOnMove = true ,
308+ BreakOnHandChange = false ,
309+ BreakOnDropItem = false ,
310+ BreakOnDamage = false ,
311+ RequireCanInteract = false ,
312+ DistanceThreshold = interaction . DistanceThreshold ,
313+ } ;
314+
315+ if ( ! _doAfter . TryStartDoAfter ( doAfterArgs , out var doAfterId ) )
316+ return ;
317+
318+ anomaly . Comp . ParticleInteractionDoAfter = doAfterId ;
319+ anomaly . Comp . ActiveParticleInteraction = interactionIndex ;
320+ anomaly . Comp . ActiveParticleInteractionSource = shooter ;
321+
322+ source . DoAfterId = doAfterId ;
323+ }
324+
325+ private static int ? GetMatchingParticleInteraction (
326+ AnomalyComponent anomaly ,
327+ AnomalousParticleComponent particle )
328+ {
329+ for ( var i = 0 ; i < anomaly . ParticleInteractions . Count ; i ++ )
330+ {
331+ var interaction = anomaly . ParticleInteractions [ i ] ;
332+
333+ if ( interaction . InteractionKey != null &&
334+ interaction . InteractionKey == particle . InteractionKey )
335+ return i ;
336+
337+ if ( interaction . ParticleType != null &&
338+ interaction . ParticleType == particle . ParticleType )
339+ return i ;
340+ }
341+
342+ return null ;
343+ }
344+
345+ private void OnParticleInteractionDoAfter (
346+ Entity < AnomalyComponent > anomaly ,
347+ ref AnomalyParticleInteractionDoAfterEvent args )
348+ {
349+ var source = anomaly . Comp . ActiveParticleInteractionSource ;
350+
351+ anomaly . Comp . ParticleInteractionDoAfter = null ;
352+ anomaly . Comp . ActiveParticleInteractionSource = null ;
353+
354+ if ( source is { } sourceUid &&
355+ TryComp < CosmicLambdaParticleSourceComponent > ( sourceUid , out var sourceComp ) )
356+ {
357+ sourceComp . DoAfterId = null ;
358+ }
359+
360+ if ( args . Cancelled || args . Handled )
361+ {
362+ anomaly . Comp . ActiveParticleInteraction = null ;
363+ return ;
364+ }
365+
366+ args . Handled = true ;
367+
368+ if ( anomaly . Comp . ActiveParticleInteraction is not { } index ||
369+ index < 0 ||
370+ index >= anomaly . Comp . ParticleInteractions . Count )
371+ {
372+ anomaly . Comp . ActiveParticleInteraction = null ;
373+ return ;
374+ }
375+
376+ var interaction = anomaly . Comp . ParticleInteractions [ index ] ;
377+
378+ anomaly . Comp . ActiveParticleInteraction = null ;
379+
380+ ApplyParticleInteraction ( anomaly , interaction , args . User ) ;
381+ }
382+
383+ private void ApplyParticleInteraction (
384+ Entity < AnomalyComponent > anomaly ,
385+ AnomalyParticleInteraction interaction ,
386+ EntityUid user )
387+ {
388+ var coords = Transform ( anomaly ) . Coordinates ;
389+
390+ if ( TryComp < EmitterComponent > ( user , out var emitter ) )
391+ _emitter . PowerOff ( user , emitter ) ;
392+
393+ if ( interaction . VisualEffect is { } effectProto )
394+ Spawn ( effectProto , coords ) ;
395+
396+ if ( interaction . Sound is { } sound )
397+ _audio . PlayPvs ( sound , coords ) ;
398+
399+ switch ( interaction . Effect )
400+ {
401+ case AnomalyParticleInteractionEffect . EndAnomaly :
402+ EndAnomaly ( anomaly , anomaly . Comp , spawnCore : interaction . SpawnCore ) ;
403+
404+ if ( interaction . DeleteEntityAfterEnd && ! TerminatingOrDeleted ( anomaly . Owner ) )
405+ QueueDel ( anomaly . Owner ) ;
406+
407+ break ;
408+
409+ case AnomalyParticleInteractionEffect . DeleteEntity :
410+ QueueDel ( anomaly ) ;
411+ break ;
412+ }
413+ }
414+
415+ private CosmicLambdaParticleSourceComponent ? GetPoweredNullspaceStabilizerSource ( EntityUid uid )
416+ => ! TryComp < CosmicLambdaParticleSourceComponent > ( uid , out var source )
417+ ? null : ! _powerReceiver . IsPowered ( uid )
418+ ? null : source ;
419+
420+ #endregion
421+
226422 #endregion
227423
228424 #region Information
@@ -318,7 +514,13 @@ public FormattedMessage GetScannerMessage(AnomalyScannerComponent component)
318514 msg . AddMarkupOrThrow ( Loc . GetString ( "anomaly-scanner-particle-containment-unknown" ) ) ;
319515 else
320516 {
321- var text = Loc . GetString ( "anomaly-scanner-particle-containment" , ( "type" , GetParticleLocale ( anomalyComp . WeakeningParticleType ) ) ) ;
517+ // Starlight edit Start
518+ var particleName = anomalyComp . ScannerContainmentParticleReadout is { } containmentReadout
519+ ? Loc . GetString ( containmentReadout )
520+ : GetParticleLocale ( anomalyComp . WeakeningParticleType ) ;
521+
522+ var text = Loc . GetString ( "anomaly-scanner-particle-containment" , ( "type" , particleName ) ) ;
523+ // Starlight edit End
322524 if ( secret != null && secret . Secret . Contains ( AnomalySecretData . ParticleContainment ) )
323525 text += " " + Loc . GetString ( "anomaly-secret-admin" ) ;
324526 msg . AddMarkupOrThrow ( text ) ;
0 commit comments