1
0

AnomalySystem.Generator.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. using Content.Server.Anomaly.Components;
  2. using Content.Server.Power.EntitySystems;
  3. using Content.Server.Station.Components;
  4. using Content.Shared.Anomaly;
  5. using Content.Shared.CCVar;
  6. using Content.Shared.Materials;
  7. using Content.Shared.Radio;
  8. using Robust.Shared.Audio;
  9. using Content.Shared.Physics;
  10. using Robust.Shared.Map.Components;
  11. using Robust.Shared.Physics;
  12. using Robust.Shared.Physics.Components;
  13. using Content.Shared.Power;
  14. namespace Content.Server.Anomaly;
  15. /// <summary>
  16. /// This handles anomalous vessel as well as
  17. /// the calculations for how many points they
  18. /// should produce.
  19. /// </summary>
  20. public sealed partial class AnomalySystem
  21. {
  22. [Dependency] private readonly SharedMapSystem _mapSystem = default!;
  23. [Dependency] private readonly SharedTransformSystem _transform = default!;
  24. private void InitializeGenerator()
  25. {
  26. SubscribeLocalEvent<AnomalyGeneratorComponent, BoundUIOpenedEvent>(OnGeneratorBUIOpened);
  27. SubscribeLocalEvent<AnomalyGeneratorComponent, MaterialAmountChangedEvent>(OnGeneratorMaterialAmountChanged);
  28. SubscribeLocalEvent<AnomalyGeneratorComponent, AnomalyGeneratorGenerateButtonPressedEvent>(OnGenerateButtonPressed);
  29. SubscribeLocalEvent<AnomalyGeneratorComponent, PowerChangedEvent>(OnGeneratorPowerChanged);
  30. SubscribeLocalEvent<GeneratingAnomalyGeneratorComponent, ComponentStartup>(OnGeneratingStartup);
  31. }
  32. private void OnGeneratorPowerChanged(EntityUid uid, AnomalyGeneratorComponent component, ref PowerChangedEvent args)
  33. {
  34. _ambient.SetAmbience(uid, args.Powered);
  35. }
  36. private void OnGeneratorBUIOpened(EntityUid uid, AnomalyGeneratorComponent component, BoundUIOpenedEvent args)
  37. {
  38. UpdateGeneratorUi(uid, component);
  39. }
  40. private void OnGeneratorMaterialAmountChanged(EntityUid uid, AnomalyGeneratorComponent component, ref MaterialAmountChangedEvent args)
  41. {
  42. UpdateGeneratorUi(uid, component);
  43. }
  44. private void OnGenerateButtonPressed(EntityUid uid, AnomalyGeneratorComponent component, AnomalyGeneratorGenerateButtonPressedEvent args)
  45. {
  46. TryGeneratorCreateAnomaly(uid, component);
  47. }
  48. public void UpdateGeneratorUi(EntityUid uid, AnomalyGeneratorComponent component)
  49. {
  50. var materialAmount = _material.GetMaterialAmount(uid, component.RequiredMaterial);
  51. var state = new AnomalyGeneratorUserInterfaceState(component.CooldownEndTime, materialAmount, component.MaterialPerAnomaly);
  52. _ui.SetUiState(uid, AnomalyGeneratorUiKey.Key, state);
  53. }
  54. public void TryGeneratorCreateAnomaly(EntityUid uid, AnomalyGeneratorComponent? component = null)
  55. {
  56. if (!Resolve(uid, ref component))
  57. return;
  58. if (!this.IsPowered(uid, EntityManager))
  59. return;
  60. if (Timing.CurTime < component.CooldownEndTime)
  61. return;
  62. if (!_material.TryChangeMaterialAmount(uid, component.RequiredMaterial, -component.MaterialPerAnomaly))
  63. return;
  64. var generating = EnsureComp<GeneratingAnomalyGeneratorComponent>(uid);
  65. generating.EndTime = Timing.CurTime + component.GenerationLength;
  66. generating.AudioStream = Audio.PlayPvs(component.GeneratingSound, uid, AudioParams.Default.WithLoop(true))?.Entity;
  67. component.CooldownEndTime = Timing.CurTime + component.CooldownLength;
  68. UpdateGeneratorUi(uid, component);
  69. }
  70. public void SpawnOnRandomGridLocation(EntityUid grid, string toSpawn)
  71. {
  72. if (!TryComp<MapGridComponent>(grid, out var gridComp))
  73. return;
  74. var xform = Transform(grid);
  75. var targetCoords = xform.Coordinates;
  76. var gridBounds = gridComp.LocalAABB.Scale(_configuration.GetCVar(CCVars.AnomalyGenerationGridBoundsScale));
  77. for (var i = 0; i < 25; i++)
  78. {
  79. var randomX = Random.Next((int) gridBounds.Left, (int) gridBounds.Right);
  80. var randomY = Random.Next((int) gridBounds.Bottom, (int)gridBounds.Top);
  81. var tile = new Vector2i(randomX, randomY);
  82. // no air-blocked areas.
  83. if (_atmosphere.IsTileSpace(grid, xform.MapUid, tile) ||
  84. _atmosphere.IsTileAirBlocked(grid, tile, mapGridComp: gridComp))
  85. {
  86. continue;
  87. }
  88. // don't spawn inside of solid objects
  89. var physQuery = GetEntityQuery<PhysicsComponent>();
  90. var valid = true;
  91. // TODO: This should be using static lookup.
  92. foreach (var ent in _mapSystem.GetAnchoredEntities(grid, gridComp, tile))
  93. {
  94. if (!physQuery.TryGetComponent(ent, out var body))
  95. continue;
  96. if (body.BodyType != BodyType.Static ||
  97. !body.Hard ||
  98. (body.CollisionLayer & (int) CollisionGroup.Impassable) == 0)
  99. continue;
  100. valid = false;
  101. break;
  102. }
  103. if (!valid)
  104. continue;
  105. var pos = _mapSystem.GridTileToLocal(grid, gridComp, tile);
  106. var mapPos = _transform.ToMapCoordinates(pos);
  107. // don't spawn in AntiAnomalyZones
  108. var antiAnomalyZonesQueue = AllEntityQuery<AntiAnomalyZoneComponent, TransformComponent>();
  109. while (antiAnomalyZonesQueue.MoveNext(out _, out var zone, out var antiXform))
  110. {
  111. if (antiXform.MapID != mapPos.MapId)
  112. continue;
  113. var antiCoordinates = _transform.GetWorldPosition(antiXform);
  114. var delta = antiCoordinates - mapPos.Position;
  115. if (delta.LengthSquared() < zone.ZoneRadius * zone.ZoneRadius)
  116. {
  117. valid = false;
  118. break;
  119. }
  120. }
  121. if (!valid)
  122. continue;
  123. targetCoords = pos;
  124. break;
  125. }
  126. Spawn(toSpawn, targetCoords);
  127. }
  128. private void OnGeneratingStartup(EntityUid uid, GeneratingAnomalyGeneratorComponent component, ComponentStartup args)
  129. {
  130. Appearance.SetData(uid, AnomalyGeneratorVisuals.Generating, true);
  131. }
  132. private void OnGeneratingFinished(EntityUid uid, AnomalyGeneratorComponent component)
  133. {
  134. var xform = Transform(uid);
  135. if (_station.GetStationInMap(xform.MapID) is not { } station ||
  136. !TryComp<StationDataComponent>(station, out var data) ||
  137. _station.GetLargestGrid(data) is not { } grid)
  138. {
  139. if (xform.GridUid == null)
  140. return;
  141. grid = xform.GridUid.Value;
  142. }
  143. SpawnOnRandomGridLocation(grid, component.SpawnerPrototype);
  144. RemComp<GeneratingAnomalyGeneratorComponent>(uid);
  145. Appearance.SetData(uid, AnomalyGeneratorVisuals.Generating, false);
  146. Audio.PlayPvs(component.GeneratingFinishedSound, uid);
  147. var message = Loc.GetString("anomaly-generator-announcement");
  148. _radio.SendRadioMessage(uid, message, _prototype.Index<RadioChannelPrototype>(component.ScienceChannel), uid);
  149. }
  150. private void UpdateGenerator()
  151. {
  152. var query = EntityQueryEnumerator<GeneratingAnomalyGeneratorComponent, AnomalyGeneratorComponent>();
  153. while (query.MoveNext(out var ent, out var active, out var gen))
  154. {
  155. if (Timing.CurTime < active.EndTime)
  156. continue;
  157. active.AudioStream = _audio.Stop(active.AudioStream);
  158. OnGeneratingFinished(ent, gen);
  159. }
  160. }
  161. }