SharedWeatherSystem.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. using Content.Shared.Light.Components;
  2. using Content.Shared.Light.EntitySystems;
  3. using Content.Shared.Maps;
  4. using Robust.Shared.Audio.Systems;
  5. using Robust.Shared.Map;
  6. using Robust.Shared.Map.Components;
  7. using Robust.Shared.Prototypes;
  8. using Robust.Shared.Serialization;
  9. using Robust.Shared.Timing;
  10. namespace Content.Shared.Weather;
  11. public abstract class SharedWeatherSystem : EntitySystem
  12. {
  13. [Dependency] protected readonly IGameTiming Timing = default!;
  14. [Dependency] protected readonly IMapManager MapManager = default!;
  15. [Dependency] protected readonly IPrototypeManager ProtoMan = default!;
  16. [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!;
  17. [Dependency] private readonly MetaDataSystem _metadata = default!;
  18. [Dependency] private readonly SharedAudioSystem _audio = default!;
  19. [Dependency] private readonly SharedMapSystem _mapSystem = default!;
  20. [Dependency] private readonly SharedRoofSystem _roof = default!;
  21. private EntityQuery<BlockWeatherComponent> _blockQuery;
  22. public override void Initialize()
  23. {
  24. base.Initialize();
  25. _blockQuery = GetEntityQuery<BlockWeatherComponent>();
  26. SubscribeLocalEvent<WeatherComponent, EntityUnpausedEvent>(OnWeatherUnpaused);
  27. }
  28. private void OnWeatherUnpaused(EntityUid uid, WeatherComponent component, ref EntityUnpausedEvent args)
  29. {
  30. foreach (var weather in component.Weather.Values)
  31. {
  32. weather.StartTime += args.PausedTime;
  33. if (weather.EndTime != null)
  34. weather.EndTime = weather.EndTime.Value + args.PausedTime;
  35. }
  36. }
  37. public bool CanWeatherAffect(EntityUid uid, MapGridComponent grid, TileRef tileRef, RoofComponent? roofComp = null)
  38. {
  39. if (tileRef.Tile.IsEmpty)
  40. return true;
  41. if (Resolve(uid, ref roofComp, false) && _roof.IsRooved((uid, grid, roofComp), tileRef.GridIndices))
  42. return false;
  43. var tileDef = (ContentTileDefinition)_tileDefManager[tileRef.Tile.TypeId];
  44. if (!tileDef.Weather)
  45. return false;
  46. var anchoredEntities = _mapSystem.GetAnchoredEntitiesEnumerator(uid, grid, tileRef.GridIndices);
  47. while (anchoredEntities.MoveNext(out var ent))
  48. {
  49. if (_blockQuery.HasComponent(ent.Value))
  50. return false;
  51. }
  52. return true;
  53. }
  54. public float GetPercent(WeatherData component, EntityUid mapUid)
  55. {
  56. var pauseTime = _metadata.GetPauseTime(mapUid);
  57. var elapsed = Timing.CurTime - (component.StartTime + pauseTime);
  58. var duration = component.Duration;
  59. var remaining = duration - elapsed;
  60. float alpha;
  61. if (remaining < WeatherComponent.ShutdownTime)
  62. {
  63. alpha = (float)(remaining / WeatherComponent.ShutdownTime);
  64. }
  65. else if (elapsed < WeatherComponent.StartupTime)
  66. {
  67. alpha = (float)(elapsed / WeatherComponent.StartupTime);
  68. }
  69. else
  70. {
  71. alpha = 1f;
  72. }
  73. return alpha;
  74. }
  75. public override void Update(float frameTime)
  76. {
  77. base.Update(frameTime);
  78. if (!Timing.IsFirstTimePredicted)
  79. return;
  80. var curTime = Timing.CurTime;
  81. var query = EntityQueryEnumerator<WeatherComponent>();
  82. while (query.MoveNext(out var uid, out var comp))
  83. {
  84. if (comp.Weather.Count == 0)
  85. continue;
  86. foreach (var (proto, weather) in comp.Weather)
  87. {
  88. var endTime = weather.EndTime;
  89. // Ended
  90. if (endTime != null && endTime < curTime)
  91. {
  92. EndWeather(uid, comp, proto);
  93. continue;
  94. }
  95. var remainingTime = endTime - curTime;
  96. // Admin messed up or the likes.
  97. if (!ProtoMan.TryIndex<WeatherPrototype>(proto, out var weatherProto))
  98. {
  99. Log.Error($"Unable to find weather prototype for {comp.Weather}, ending!");
  100. EndWeather(uid, comp, proto);
  101. continue;
  102. }
  103. // Shutting down
  104. if (endTime != null && remainingTime < WeatherComponent.ShutdownTime)
  105. {
  106. SetState(uid, WeatherState.Ending, comp, weather, weatherProto);
  107. }
  108. // Starting up
  109. else
  110. {
  111. var startTime = weather.StartTime;
  112. var elapsed = Timing.CurTime - startTime;
  113. if (elapsed < WeatherComponent.StartupTime)
  114. {
  115. SetState(uid, WeatherState.Starting, comp, weather, weatherProto);
  116. }
  117. }
  118. // Run whatever code we need.
  119. Run(uid, weather, weatherProto, frameTime);
  120. }
  121. }
  122. }
  123. /// <summary>
  124. /// Shuts down all existing weather and starts the new one if applicable.
  125. /// </summary>
  126. public void SetWeather(MapId mapId, WeatherPrototype? proto, TimeSpan? endTime)
  127. {
  128. if (!_mapSystem.TryGetMap(mapId, out var mapUid))
  129. return;
  130. var weatherComp = EnsureComp<WeatherComponent>(mapUid.Value);
  131. foreach (var (eProto, weather) in weatherComp.Weather)
  132. {
  133. // if we turn off the weather, we don't want endTime = null
  134. if (proto == null)
  135. endTime ??= Timing.CurTime + WeatherComponent.ShutdownTime;
  136. // Reset cooldown if it's an existing one.
  137. if (proto is not null && eProto == proto.ID)
  138. {
  139. weather.EndTime = endTime;
  140. if (weather.State == WeatherState.Ending)
  141. weather.State = WeatherState.Running;
  142. Dirty(mapUid.Value, weatherComp);
  143. continue;
  144. }
  145. // Speedrun
  146. var end = Timing.CurTime + WeatherComponent.ShutdownTime;
  147. if (weather.EndTime == null || weather.EndTime > end)
  148. {
  149. weather.EndTime = end;
  150. Dirty(mapUid.Value, weatherComp);
  151. }
  152. }
  153. Log.Info("Setting weather to {0}", proto?.ID);
  154. if (proto != null)
  155. StartWeather(mapUid.Value, weatherComp, proto, endTime);
  156. }
  157. /// <summary>
  158. /// Run every tick when the weather is running.
  159. /// </summary>
  160. protected virtual void Run(EntityUid uid, WeatherData weather, WeatherPrototype weatherProto, float frameTime) { }
  161. protected void StartWeather(EntityUid uid, WeatherComponent component, WeatherPrototype weather, TimeSpan? endTime)
  162. {
  163. if (component.Weather.ContainsKey(weather.ID))
  164. return;
  165. var data = new WeatherData()
  166. {
  167. StartTime = Timing.CurTime,
  168. EndTime = endTime,
  169. };
  170. component.Weather.Add(weather.ID, data);
  171. Dirty(uid, component);
  172. }
  173. protected virtual void EndWeather(EntityUid uid, WeatherComponent component, string proto)
  174. {
  175. if (!component.Weather.TryGetValue(proto, out var data))
  176. return;
  177. _audio.Stop(data.Stream);
  178. data.Stream = null;
  179. component.Weather.Remove(proto);
  180. Dirty(uid, component);
  181. }
  182. protected virtual bool SetState(EntityUid uid, WeatherState state, WeatherComponent component, WeatherData weather, WeatherPrototype weatherProto)
  183. {
  184. if (weather.State.Equals(state))
  185. return false;
  186. weather.State = state;
  187. Dirty(uid, component);
  188. return true;
  189. }
  190. [Serializable, NetSerializable]
  191. protected sealed class WeatherComponentState : ComponentState
  192. {
  193. public Dictionary<ProtoId<WeatherPrototype>, WeatherData> Weather;
  194. public WeatherComponentState(Dictionary<ProtoId<WeatherPrototype>, WeatherData> weather)
  195. {
  196. Weather = weather;
  197. }
  198. }
  199. }