EmergencyLightSystem.cs 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. using Content.Server.AlertLevel;
  2. using Content.Server.Audio;
  3. using Content.Server.Light.Components;
  4. using Content.Server.Power.Components;
  5. using Content.Server.Power.EntitySystems;
  6. using Content.Server.Station.Systems;
  7. using Content.Shared.Examine;
  8. using Content.Shared.Light;
  9. using Content.Shared.Light.Components;
  10. using Content.Shared.Power;
  11. using Content.Shared.Station.Components;
  12. using Robust.Server.GameObjects;
  13. using Color = Robust.Shared.Maths.Color;
  14. namespace Content.Server.Light.EntitySystems;
  15. public sealed class EmergencyLightSystem : SharedEmergencyLightSystem
  16. {
  17. [Dependency] private readonly AmbientSoundSystem _ambient = default!;
  18. [Dependency] private readonly BatterySystem _battery = default!;
  19. [Dependency] private readonly PointLightSystem _pointLight = default!;
  20. [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
  21. [Dependency] private readonly StationSystem _station = default!;
  22. public override void Initialize()
  23. {
  24. base.Initialize();
  25. SubscribeLocalEvent<EmergencyLightComponent, EmergencyLightEvent>(OnEmergencyLightEvent);
  26. SubscribeLocalEvent<AlertLevelChangedEvent>(OnAlertLevelChanged);
  27. SubscribeLocalEvent<EmergencyLightComponent, ExaminedEvent>(OnEmergencyExamine);
  28. SubscribeLocalEvent<EmergencyLightComponent, PowerChangedEvent>(OnEmergencyPower);
  29. }
  30. private void OnEmergencyPower(Entity<EmergencyLightComponent> entity, ref PowerChangedEvent args)
  31. {
  32. var meta = MetaData(entity.Owner);
  33. // TODO: PowerChangedEvent shouldn't be issued for paused ents but this is the world we live in.
  34. if (meta.EntityLifeStage >= EntityLifeStage.Terminating ||
  35. meta.EntityPaused)
  36. {
  37. return;
  38. }
  39. UpdateState(entity);
  40. }
  41. private void OnEmergencyExamine(EntityUid uid, EmergencyLightComponent component, ExaminedEvent args)
  42. {
  43. using (args.PushGroup(nameof(EmergencyLightComponent)))
  44. {
  45. args.PushMarkup(
  46. Loc.GetString("emergency-light-component-on-examine",
  47. ("batteryStateText",
  48. Loc.GetString(component.BatteryStateText[component.State]))));
  49. // Show alert level on the light itself.
  50. if (!TryComp<AlertLevelComponent>(_station.GetOwningStation(uid), out var alerts))
  51. return;
  52. if (alerts.AlertLevels == null)
  53. return;
  54. var name = alerts.CurrentLevel;
  55. var color = Color.White;
  56. if (alerts.AlertLevels.Levels.TryGetValue(alerts.CurrentLevel, out var details))
  57. color = details.Color;
  58. args.PushMarkup(
  59. Loc.GetString("emergency-light-component-on-examine-alert",
  60. ("color", color.ToHex()),
  61. ("level", Loc.GetString($"alert-level-{name.ToString().ToLower()}"))));
  62. }
  63. }
  64. private void OnEmergencyLightEvent(EntityUid uid, EmergencyLightComponent component, EmergencyLightEvent args)
  65. {
  66. switch (args.State)
  67. {
  68. case EmergencyLightState.On:
  69. case EmergencyLightState.Charging:
  70. EnsureComp<ActiveEmergencyLightComponent>(uid);
  71. break;
  72. case EmergencyLightState.Full:
  73. case EmergencyLightState.Empty:
  74. RemComp<ActiveEmergencyLightComponent>(uid);
  75. break;
  76. default:
  77. throw new ArgumentOutOfRangeException();
  78. }
  79. }
  80. private void OnAlertLevelChanged(AlertLevelChangedEvent ev)
  81. {
  82. if (!TryComp<AlertLevelComponent>(ev.Station, out var alert))
  83. return;
  84. if (alert.AlertLevels == null || !alert.AlertLevels.Levels.TryGetValue(ev.AlertLevel, out var details))
  85. return;
  86. var query = EntityQueryEnumerator<EmergencyLightComponent, PointLightComponent, AppearanceComponent, TransformComponent>();
  87. while (query.MoveNext(out var uid, out var light, out var pointLight, out var appearance, out var xform))
  88. {
  89. if (CompOrNull<StationMemberComponent>(xform.GridUid)?.Station != ev.Station)
  90. continue;
  91. _pointLight.SetColor(uid, details.EmergencyLightColor, pointLight);
  92. _appearance.SetData(uid, EmergencyLightVisuals.Color, details.EmergencyLightColor, appearance);
  93. if (details.ForceEnableEmergencyLights && !light.ForciblyEnabled)
  94. {
  95. light.ForciblyEnabled = true;
  96. TurnOn((uid, light));
  97. }
  98. else if (!details.ForceEnableEmergencyLights && light.ForciblyEnabled)
  99. {
  100. // Previously forcibly enabled, and we went down an alert level.
  101. light.ForciblyEnabled = false;
  102. UpdateState((uid, light));
  103. }
  104. }
  105. }
  106. public void SetState(EntityUid uid, EmergencyLightComponent component, EmergencyLightState state)
  107. {
  108. if (component.State == state) return;
  109. component.State = state;
  110. RaiseLocalEvent(uid, new EmergencyLightEvent(state));
  111. }
  112. public override void Update(float frameTime)
  113. {
  114. var query = EntityQueryEnumerator<ActiveEmergencyLightComponent, EmergencyLightComponent, BatteryComponent>();
  115. while (query.MoveNext(out var uid, out _, out var emergencyLight, out var battery))
  116. {
  117. Update((uid, emergencyLight), battery, frameTime);
  118. }
  119. }
  120. private void Update(Entity<EmergencyLightComponent> entity, BatteryComponent battery, float frameTime)
  121. {
  122. if (entity.Comp.State == EmergencyLightState.On)
  123. {
  124. if (!_battery.TryUseCharge(entity.Owner, entity.Comp.Wattage * frameTime, battery))
  125. {
  126. SetState(entity.Owner, entity.Comp, EmergencyLightState.Empty);
  127. TurnOff(entity);
  128. }
  129. }
  130. else
  131. {
  132. _battery.SetCharge(entity.Owner, battery.CurrentCharge + entity.Comp.ChargingWattage * frameTime * entity.Comp.ChargingEfficiency, battery);
  133. if (_battery.IsFull(entity, battery))
  134. {
  135. if (TryComp<ApcPowerReceiverComponent>(entity.Owner, out var receiver))
  136. {
  137. receiver.Load = 1;
  138. }
  139. SetState(entity.Owner, entity.Comp, EmergencyLightState.Full);
  140. }
  141. }
  142. }
  143. /// <summary>
  144. /// Updates the light's power drain, battery drain, sprite and actual light state.
  145. /// </summary>
  146. public void UpdateState(Entity<EmergencyLightComponent> entity)
  147. {
  148. if (!TryComp<ApcPowerReceiverComponent>(entity.Owner, out var receiver))
  149. return;
  150. if (!TryComp<AlertLevelComponent>(_station.GetOwningStation(entity.Owner), out var alerts))
  151. return;
  152. if (alerts.AlertLevels == null || !alerts.AlertLevels.Levels.TryGetValue(alerts.CurrentLevel, out var details))
  153. {
  154. TurnOff(entity, Color.Red); // if no alert, default to off red state
  155. return;
  156. }
  157. if (receiver.Powered && !entity.Comp.ForciblyEnabled) // Green alert
  158. {
  159. receiver.Load = (int) Math.Abs(entity.Comp.Wattage);
  160. TurnOff(entity, details.Color);
  161. SetState(entity.Owner, entity.Comp, EmergencyLightState.Charging);
  162. }
  163. else if (!receiver.Powered) // If internal battery runs out it will end in off red state
  164. {
  165. TurnOn(entity, Color.Red);
  166. SetState(entity.Owner, entity.Comp, EmergencyLightState.On);
  167. }
  168. else // Powered and enabled
  169. {
  170. TurnOn(entity, details.Color);
  171. SetState(entity.Owner, entity.Comp, EmergencyLightState.On);
  172. }
  173. }
  174. private void TurnOff(Entity<EmergencyLightComponent> entity)
  175. {
  176. _pointLight.SetEnabled(entity.Owner, false);
  177. _appearance.SetData(entity.Owner, EmergencyLightVisuals.On, false);
  178. _ambient.SetAmbience(entity.Owner, false);
  179. }
  180. /// <summary>
  181. /// Turn off emergency light and set color.
  182. /// </summary>
  183. private void TurnOff(Entity<EmergencyLightComponent> entity, Color color)
  184. {
  185. _pointLight.SetEnabled(entity.Owner, false);
  186. _pointLight.SetColor(entity.Owner, color);
  187. _appearance.SetData(entity.Owner, EmergencyLightVisuals.Color, color);
  188. _appearance.SetData(entity.Owner, EmergencyLightVisuals.On, false);
  189. _ambient.SetAmbience(entity.Owner, false);
  190. }
  191. private void TurnOn(Entity<EmergencyLightComponent> entity)
  192. {
  193. _pointLight.SetEnabled(entity.Owner, true);
  194. _appearance.SetData(entity.Owner, EmergencyLightVisuals.On, true);
  195. _ambient.SetAmbience(entity.Owner, true);
  196. }
  197. /// <summary>
  198. /// Turn on emergency light and set color.
  199. /// </summary>
  200. private void TurnOn(Entity<EmergencyLightComponent> entity, Color color)
  201. {
  202. _pointLight.SetEnabled(entity.Owner, true);
  203. _pointLight.SetColor(entity.Owner, color);
  204. _appearance.SetData(entity.Owner, EmergencyLightVisuals.Color, color);
  205. _appearance.SetData(entity.Owner, EmergencyLightVisuals.On, true);
  206. _ambient.SetAmbience(entity.Owner, true);
  207. }
  208. }