WeatherNomadsSystem.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. using System.Collections.Generic;
  2. using System.Linq;
  3. using Content.Shared.Weather;
  4. using Robust.Shared.Map;
  5. using Robust.Shared.Prototypes;
  6. using Robust.Shared.Timing;
  7. using Robust.Shared.GameObjects;
  8. using Content.Server.Atmos.Components;
  9. using Content.Server.Atmos.EntitySystems;
  10. using Content.Shared.Atmos;
  11. using Content.Shared.Maps;
  12. using Robust.Shared.Map.Components;
  13. using Content.Server.Chat.Systems;
  14. using Content.Shared.Light.EntitySystems;
  15. using Content.Shared.Light.Components;
  16. using System;
  17. namespace Content.Server.Weather;
  18. /// <summary>
  19. /// System responsible for managing dynamic weather changes and temperature adjustments for exposed tiles in a grid.
  20. /// </summary>
  21. public sealed class WeatherNomadsSystem : EntitySystem
  22. {
  23. // Dependencies injected via IoC
  24. [Dependency] private readonly IGameTiming _timing = default!;
  25. [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
  26. [Dependency] private readonly SharedWeatherSystem _weatherSystem = default!;
  27. [Dependency] private readonly AtmosphereSystem _atmosphere = default!;
  28. [Dependency] private readonly IMapManager _mapManager = default!;
  29. [Dependency] private readonly SharedRoofSystem _roofSystem = default!;
  30. [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!;
  31. [Dependency] private readonly SharedMapSystem _mapSystem = default!;
  32. [Dependency] private readonly ChatSystem _chat = default!;
  33. /// <summary>
  34. /// Structure representing properties of a weather type.
  35. /// </summary>
  36. private class WeatherType
  37. {
  38. public string? PrototypeId { get; set; } // ID of the weather prototype, null for "Clear"
  39. public int Weight { get; set; } // Weight for weather transition order (unused now, kept for compatibility)
  40. public float MinTemperature { get; set; } // Minimum temperature in Kelvin
  41. public float MaxTemperature { get; set; } // Maximum temperature in Kelvin
  42. }
  43. /// <summary>
  44. /// Dictionary defining available weather types and their properties.
  45. /// </summary>
  46. private readonly Dictionary<string, WeatherType> _weatherTypes = new()
  47. {
  48. { "Clear", new WeatherType { PrototypeId = "", Weight = 0, MinTemperature = 293.15f, MaxTemperature = 293.15f } },
  49. { "Rain", new WeatherType { PrototypeId = "Rain", Weight = 1, MinTemperature = 278.15f, MaxTemperature = 288.15f } },
  50. { "Storm", new WeatherType { PrototypeId = "Storm", Weight = 3, MinTemperature = 273.15f, MaxTemperature = 278.15f } },
  51. { "SnowfallLight", new WeatherType { PrototypeId = "SnowfallLight", Weight = 4, MinTemperature = 268.15f, MaxTemperature = 273.15f } },
  52. { "SnowfallMedium", new WeatherType { PrototypeId = "SnowfallMedium", Weight = 5, MinTemperature = 258.15f, MaxTemperature = 268.15f } },
  53. { "SnowfallHeavy", new WeatherType { PrototypeId = "SnowfallHeavy", Weight = 6, MinTemperature = 243.15f, MaxTemperature = 258.15f } },
  54. { "Hail", new WeatherType { PrototypeId = "Hail", Weight = 7, MinTemperature = 273.15f, MaxTemperature = 278.15f } },
  55. { "Sandstorm", new WeatherType { PrototypeId = "Sandstorm", Weight = 9, MinTemperature = 293.15f, MaxTemperature = 313.15f } },
  56. { "SandstormHeavy", new WeatherType { PrototypeId = "SandstormHeavy", Weight = 10, MinTemperature = 293.15f, MaxTemperature = 313.15f } },
  57. };
  58. /// <summary>
  59. /// Dictionary mapping (Biome, Season, Precipitation) to specific weather types.
  60. /// </summary>
  61. private static readonly Dictionary<(Biome, string, Precipitation), string> _weatherTransitionMap = new()
  62. {
  63. // Summer
  64. { (Biome.Tundra, "Summer", Precipitation.Dry), "Clear" },
  65. { (Biome.Tundra, "Summer", Precipitation.LightWet), "SnowfallLight" },
  66. { (Biome.Tundra, "Summer", Precipitation.HeavyWet), "SnowfallMedium" },
  67. { (Biome.Tundra, "Summer", Precipitation.Storm), "SnowfallHeavy" },
  68. { (Biome.Taiga, "Summer", Precipitation.Dry), "Clear" },
  69. { (Biome.Taiga, "Summer", Precipitation.LightWet), "Rain" },
  70. { (Biome.Taiga, "Summer", Precipitation.HeavyWet), "Rain" },
  71. { (Biome.Taiga, "Summer", Precipitation.Storm), "Hail" },
  72. { (Biome.Temperate, "Summer", Precipitation.Dry), "Clear" },
  73. { (Biome.Temperate, "Summer", Precipitation.LightWet), "Rain" },
  74. { (Biome.Temperate, "Summer", Precipitation.HeavyWet), "Rain" },
  75. { (Biome.Temperate, "Summer", Precipitation.Storm), "Storm" },
  76. { (Biome.Sea, "Summer", Precipitation.Dry), "Clear" },
  77. { (Biome.Sea, "Summer", Precipitation.LightWet), "Rain" },
  78. { (Biome.Sea, "Summer", Precipitation.HeavyWet), "Rain" },
  79. { (Biome.Sea, "Summer", Precipitation.Storm), "Storm" },
  80. { (Biome.SemiArid, "Summer", Precipitation.Dry), "Clear" },
  81. { (Biome.SemiArid, "Summer", Precipitation.LightWet), "Clear" },
  82. { (Biome.SemiArid, "Summer", Precipitation.HeavyWet), "Rain" },
  83. { (Biome.SemiArid, "Summer", Precipitation.Storm), "Rain" },
  84. { (Biome.Desert, "Summer", Precipitation.Dry), "Clear" },
  85. { (Biome.Desert, "Summer", Precipitation.LightWet), "Clear" },
  86. { (Biome.Desert, "Summer", Precipitation.HeavyWet), "Sandstorm" },
  87. { (Biome.Desert, "Summer", Precipitation.Storm), "SandstormHeavy" },
  88. { (Biome.Savanna, "Summer", Precipitation.Dry), "Clear" },
  89. { (Biome.Savanna, "Summer", Precipitation.LightWet), "Clear" },
  90. { (Biome.Savanna, "Summer", Precipitation.HeavyWet), "Rain" },
  91. { (Biome.Savanna, "Summer", Precipitation.Storm), "Storm" },
  92. { (Biome.Jungle, "Summer", Precipitation.Dry), "Clear" },
  93. { (Biome.Jungle, "Summer", Precipitation.LightWet), "Rain" },
  94. { (Biome.Jungle, "Summer", Precipitation.HeavyWet), "Storm" },
  95. { (Biome.Jungle, "Summer", Precipitation.Storm), "Storm" },
  96. // Spring
  97. { (Biome.Tundra, "Spring", Precipitation.Dry), "Clear" },
  98. { (Biome.Tundra, "Spring", Precipitation.LightWet), "SnowfallLight" },
  99. { (Biome.Tundra, "Spring", Precipitation.HeavyWet), "SnowfallMedium" },
  100. { (Biome.Tundra, "Spring", Precipitation.Storm), "SnowfallHeavy" },
  101. { (Biome.Taiga, "Spring", Precipitation.Dry), "Clear" },
  102. { (Biome.Taiga, "Spring", Precipitation.LightWet), "Rain" },
  103. { (Biome.Taiga, "Spring", Precipitation.HeavyWet), "SnowfallLight" },
  104. { (Biome.Taiga, "Spring", Precipitation.Storm), "SnowfallHeavy" },
  105. { (Biome.Temperate, "Spring", Precipitation.Dry), "Clear" },
  106. { (Biome.Temperate, "Spring", Precipitation.LightWet), "Rain" },
  107. { (Biome.Temperate, "Spring", Precipitation.HeavyWet), "Storm" },
  108. { (Biome.Temperate, "Spring", Precipitation.Storm), "SnowfallMedium" },
  109. { (Biome.Sea, "Spring", Precipitation.Dry), "Clear" },
  110. { (Biome.Sea, "Spring", Precipitation.LightWet), "Rain" },
  111. { (Biome.Sea, "Spring", Precipitation.HeavyWet), "Rain" },
  112. { (Biome.Sea, "Spring", Precipitation.Storm), "Storm" },
  113. { (Biome.SemiArid, "Spring", Precipitation.Dry), "Clear" },
  114. { (Biome.SemiArid, "Spring", Precipitation.LightWet), "Clear" },
  115. { (Biome.SemiArid, "Spring", Precipitation.HeavyWet), "Rain" },
  116. { (Biome.SemiArid, "Spring", Precipitation.Storm), "Rain" },
  117. { (Biome.Desert, "Spring", Precipitation.Dry), "Clear" },
  118. { (Biome.Desert, "Spring", Precipitation.LightWet), "Clear" },
  119. { (Biome.Desert, "Spring", Precipitation.HeavyWet), "Rain" },
  120. { (Biome.Desert, "Spring", Precipitation.Storm), "Sandstorm" },
  121. { (Biome.Savanna, "Spring", Precipitation.Dry), "Clear" },
  122. { (Biome.Savanna, "Spring", Precipitation.LightWet), "Clear" },
  123. { (Biome.Savanna, "Spring", Precipitation.HeavyWet), "Rain" },
  124. { (Biome.Savanna, "Spring", Precipitation.Storm), "Storm" },
  125. { (Biome.Jungle, "Spring", Precipitation.Dry), "Clear" },
  126. { (Biome.Jungle, "Spring", Precipitation.LightWet), "Rain" },
  127. { (Biome.Jungle, "Spring", Precipitation.HeavyWet), "Rain" },
  128. { (Biome.Jungle, "Spring", Precipitation.Storm), "Storm" },
  129. // Autumn
  130. { (Biome.Tundra, "Autumn", Precipitation.Dry), "Clear" },
  131. { (Biome.Tundra, "Autumn", Precipitation.LightWet), "SnowfallLight" },
  132. { (Biome.Tundra, "Autumn", Precipitation.HeavyWet), "SnowfallMedium" },
  133. { (Biome.Tundra, "Autumn", Precipitation.Storm), "SnowfallHeavy" },
  134. { (Biome.Taiga, "Autumn", Precipitation.Dry), "Clear" },
  135. { (Biome.Taiga, "Autumn", Precipitation.LightWet), "Rain" },
  136. { (Biome.Taiga, "Autumn", Precipitation.HeavyWet), "SnowfallLight" },
  137. { (Biome.Taiga, "Autumn", Precipitation.Storm), "SnowfallHeavy" },
  138. { (Biome.Temperate, "Autumn", Precipitation.Dry), "Clear" },
  139. { (Biome.Temperate, "Autumn", Precipitation.LightWet), "Rain" },
  140. { (Biome.Temperate, "Autumn", Precipitation.HeavyWet), "Storm" },
  141. { (Biome.Temperate, "Autumn", Precipitation.Storm), "SnowfallMedium" },
  142. { (Biome.Sea, "Autumn", Precipitation.Dry), "Clear" },
  143. { (Biome.Sea, "Autumn", Precipitation.LightWet), "Rain" },
  144. { (Biome.Sea, "Autumn", Precipitation.HeavyWet), "Rain" },
  145. { (Biome.Sea, "Autumn", Precipitation.Storm), "Storm" },
  146. { (Biome.SemiArid, "Autumn", Precipitation.Dry), "Clear" },
  147. { (Biome.SemiArid, "Autumn", Precipitation.LightWet), "Clear" },
  148. { (Biome.SemiArid, "Autumn", Precipitation.HeavyWet), "Rain" },
  149. { (Biome.SemiArid, "Autumn", Precipitation.Storm), "Rain" },
  150. { (Biome.Desert, "Autumn", Precipitation.Dry), "Clear" },
  151. { (Biome.Desert, "Autumn", Precipitation.LightWet), "Clear" },
  152. { (Biome.Desert, "Autumn", Precipitation.HeavyWet), "Rain" },
  153. { (Biome.Desert, "Autumn", Precipitation.Storm), "Sandstorm" },
  154. { (Biome.Savanna, "Autumn", Precipitation.Dry), "Clear" },
  155. { (Biome.Savanna, "Autumn", Precipitation.LightWet), "Clear" },
  156. { (Biome.Savanna, "Autumn", Precipitation.HeavyWet), "Rain" },
  157. { (Biome.Savanna, "Autumn", Precipitation.Storm), "Storm" },
  158. { (Biome.Jungle, "Autumn", Precipitation.Dry), "Clear" },
  159. { (Biome.Jungle, "Autumn", Precipitation.LightWet), "Rain" },
  160. { (Biome.Jungle, "Autumn", Precipitation.HeavyWet), "Rain" },
  161. { (Biome.Jungle, "Autumn", Precipitation.Storm), "Storm" },
  162. // Winter
  163. { (Biome.Tundra, "Winter", Precipitation.Dry), "Clear" },
  164. { (Biome.Tundra, "Winter", Precipitation.LightWet), "SnowfallMedium" },
  165. { (Biome.Tundra, "Winter", Precipitation.HeavyWet), "SnowfallHeavy" },
  166. { (Biome.Tundra, "Winter", Precipitation.Storm), "SnowfallHeavy" },
  167. { (Biome.Taiga, "Winter", Precipitation.Dry), "Clear" },
  168. { (Biome.Taiga, "Winter", Precipitation.LightWet), "SnowfallLight" },
  169. { (Biome.Taiga, "Winter", Precipitation.HeavyWet), "SnowfallHeavy" },
  170. { (Biome.Taiga, "Winter", Precipitation.Storm), "SnowfallHeavy" },
  171. { (Biome.Temperate, "Winter", Precipitation.Dry), "Clear" },
  172. { (Biome.Temperate, "Winter", Precipitation.LightWet), "SnowfallLight" },
  173. { (Biome.Temperate, "Winter", Precipitation.HeavyWet), "SnowfallMedium" },
  174. { (Biome.Temperate, "Winter", Precipitation.Storm), "SnowfallHeavy" },
  175. { (Biome.Sea, "Winter", Precipitation.Dry), "Clear" },
  176. { (Biome.Sea, "Winter", Precipitation.LightWet), "Rain" },
  177. { (Biome.Sea, "Winter", Precipitation.HeavyWet), "Storm" },
  178. { (Biome.Sea, "Winter", Precipitation.Storm), "Storm" },
  179. { (Biome.SemiArid, "Winter", Precipitation.Dry), "Clear" },
  180. { (Biome.SemiArid, "Winter", Precipitation.LightWet), "Rain" },
  181. { (Biome.SemiArid, "Winter", Precipitation.HeavyWet), "Rain" },
  182. { (Biome.SemiArid, "Winter", Precipitation.Storm), "Storm" },
  183. { (Biome.Desert, "Winter", Precipitation.Dry), "Clear" },
  184. { (Biome.Desert, "Winter", Precipitation.LightWet), "Clear" },
  185. { (Biome.Desert, "Winter", Precipitation.HeavyWet), "Rain" },
  186. { (Biome.Desert, "Winter", Precipitation.Storm), "Rain" },
  187. { (Biome.Savanna, "Winter", Precipitation.Dry), "Clear" },
  188. { (Biome.Savanna, "Winter", Precipitation.LightWet), "Rain" },
  189. { (Biome.Savanna, "Winter", Precipitation.HeavyWet), "Storm" },
  190. { (Biome.Savanna, "Winter", Precipitation.Storm), "Storm" },
  191. { (Biome.Jungle, "Winter", Precipitation.Dry), "Clear" },
  192. { (Biome.Jungle, "Winter", Precipitation.LightWet), "Rain" },
  193. { (Biome.Jungle, "Winter", Precipitation.HeavyWet), "Storm" },
  194. { (Biome.Jungle, "Winter", Precipitation.Storm), "Storm" },
  195. };
  196. /// <summary>
  197. /// Initializes the system and subscribes to relevant events.
  198. /// </summary>
  199. public override void Initialize()
  200. {
  201. base.Initialize();
  202. SubscribeLocalEvent<WeatherNomadsComponent, MapInitEvent>(OnMapInit);
  203. }
  204. /// <summary>
  205. /// Handles the initialization of weather for a map when it is first created.
  206. /// </summary>
  207. private void OnMapInit(EntityUid uid, WeatherNomadsComponent component, MapInitEvent args)
  208. {
  209. component.CurrentPrecipitation = Precipitation.Dry;
  210. component.CurrentWeather = "Clear";
  211. component.NextSwitchTime = _timing.CurTime + TimeSpan.FromMinutes(GetRandomPrecipitationDuration(component));
  212. component.NextSeasonChange = _timing.CurTime + TimeSpan.FromMinutes(GetRandomSeasonDuration(component));
  213. Dirty(uid, component);
  214. UpdateTileWeathers(uid, component);
  215. _chat.DispatchGlobalAnnouncement($"Current season: {component.CurrentSeason}", "World", false, null, null);
  216. }
  217. /// <summary>
  218. /// Updates the weather system periodically, switching precipitation and season states as needed.
  219. /// </summary>
  220. public override void Update(float frameTime)
  221. {
  222. base.Update(frameTime);
  223. var query = EntityQueryEnumerator<WeatherNomadsComponent>();
  224. while (query.MoveNext(out var uid, out var nomads))
  225. {
  226. // Handle season changes
  227. if (_timing.CurTime >= nomads.NextSeasonChange)
  228. {
  229. var oldSeason = nomads.CurrentSeason;
  230. nomads.CurrentSeason = GetNextSeason(nomads.CurrentSeason);
  231. nomads.NextSeasonChange = _timing.CurTime + TimeSpan.FromMinutes(GetRandomSeasonDuration(nomads));
  232. Dirty(uid, nomads);
  233. _chat.DispatchGlobalAnnouncement($"Changed season to {nomads.CurrentSeason}", null, false, null, null);
  234. UpdateTileWeathers(uid, nomads);
  235. }
  236. // Handle precipitation changes
  237. if (_timing.CurTime < nomads.NextSwitchTime)
  238. {
  239. continue;
  240. }
  241. var oldPrecipitation = nomads.CurrentPrecipitation;
  242. nomads.CurrentPrecipitation = GetNextPrecipitation(nomads.CurrentPrecipitation);
  243. nomads.NextSwitchTime = _timing.CurTime + TimeSpan.FromMinutes(GetRandomPrecipitationDuration(nomads));
  244. Dirty(uid, nomads);
  245. UpdateTileWeathers(uid, nomads);
  246. }
  247. }
  248. /// <summary>
  249. /// Updates weather effects for each tile based on biome, season, and global precipitation.
  250. /// </summary>
  251. private void UpdateTileWeathers(EntityUid uid, WeatherNomadsComponent nomads)
  252. {
  253. var mapId = Transform(uid).MapID;
  254. var gridUid = GetGridUidForMap(mapId);
  255. if (gridUid == null)
  256. {
  257. Log.Warning($"No grid found for map {mapId}");
  258. return;
  259. }
  260. if (!TryComp<MapGridComponent>(gridUid.Value, out var grid))
  261. return;
  262. if (!TryComp<GridAtmosphereComponent>(gridUid.Value, out var gridAtmosphere))
  263. return;
  264. RoofComponent? roofComp = TryComp<RoofComponent>(gridUid.Value, out var rc) ? rc : null;
  265. foreach (var tile in gridAtmosphere.Tiles.Values)
  266. {
  267. var tileRef = grid.GetTileRef(tile.GridIndices);
  268. if (tileRef.Tile.IsEmpty)
  269. continue; // Skip empty tiles
  270. // Get biome from tile definition
  271. var tileDef = (ContentTileDefinition)_tileDefManager[tileRef.Tile.TypeId];
  272. if (!Enum.TryParse<Biome>(tileDef.Biome, true, out var biome))
  273. {
  274. biome = Biome.Temperate; // Fallback to Temperate if biome string is invalid
  275. Log.Warning($"Invalid biome '{tileDef.Biome}' for tile at {tileRef.GridIndices}, defaulting to Temperate");
  276. }
  277. if (_weatherTransitionMap.TryGetValue((biome, nomads.CurrentSeason, nomads.CurrentPrecipitation), out var weatherType))
  278. {
  279. ApplyWeatherToTile(uid, nomads, gridUid.Value, tileRef, weatherType, grid, gridAtmosphere, roofComp);
  280. }
  281. else
  282. {
  283. Log.Warning($"No weather mapping found for Biome: {biome}, Season: {nomads.CurrentSeason}, Precipitation: {nomads.CurrentPrecipitation}");
  284. }
  285. }
  286. }
  287. /// <summary>
  288. /// Applies weather effects and temperature to a specific tile.
  289. /// </summary>
  290. private void ApplyWeatherToTile(EntityUid weatherUid, WeatherNomadsComponent nomads, EntityUid gridUid, TileRef tileRef, string weatherType, MapGridComponent grid,
  291. GridAtmosphereComponent gridAtmosphere, RoofComponent? roofComp)
  292. {
  293. if (!CanWeatherAffect(gridUid, grid, tileRef, roofComp))
  294. return;
  295. var tile = gridAtmosphere.Tiles[tileRef.GridIndices];
  296. if (tile.Air == null)
  297. return;
  298. if (!_weatherTypes.TryGetValue(weatherType, out var weatherData))
  299. {
  300. Log.Warning($"Weather type {weatherType} not found in _weatherTypes");
  301. return;
  302. }
  303. // Update CurrentWeather if it has changed
  304. if (nomads.CurrentWeather != weatherType)
  305. {
  306. nomads.CurrentWeather = weatherType;
  307. Dirty(weatherUid, nomads);
  308. }
  309. // Apply weather visuals globally
  310. var mapId = Transform(gridUid).MapID;
  311. if (!string.IsNullOrEmpty(weatherData.PrototypeId) &&
  312. _prototypeManager.TryIndex<WeatherPrototype>(weatherData.PrototypeId, out var proto))
  313. {
  314. _weatherSystem.SetWeather(mapId, proto, null);
  315. }
  316. else
  317. {
  318. _weatherSystem.SetWeather(mapId, null, null);
  319. }
  320. // Adjust temperature
  321. var temperature = (float)(weatherData.MinTemperature +
  322. (weatherData.MaxTemperature - weatherData.MinTemperature) * Random.Shared.NextDouble());
  323. var air = tile.Air;
  324. if (air.Immutable)
  325. {
  326. var newAir = new GasMixture();
  327. newAir.CopyFrom(air);
  328. air = newAir;
  329. }
  330. air.Temperature = temperature;
  331. }
  332. /// <summary>
  333. /// Gets the next precipitation state in the cycle.
  334. /// </summary>
  335. private Precipitation GetNextPrecipitation(Precipitation current)
  336. {
  337. return current switch
  338. {
  339. Precipitation.Dry => Precipitation.LightWet,
  340. Precipitation.LightWet => Precipitation.HeavyWet,
  341. Precipitation.HeavyWet => Precipitation.Storm,
  342. Precipitation.Storm => Precipitation.Dry,
  343. _ => Precipitation.Dry // Default to Dry if something goes wrong
  344. };
  345. }
  346. /// <summary>
  347. /// Generates a random duration for a season based on component settings.
  348. /// </summary>
  349. private double GetRandomSeasonDuration(WeatherNomadsComponent component)
  350. {
  351. var duration = Random.Shared.Next(component.MinSeasonMinutes, component.MaxSeasonMinutes + 1);
  352. return duration;
  353. }
  354. /// <summary>
  355. /// Generates a random duration for a precipitation change based on component settings.
  356. /// </summary>
  357. private double GetRandomPrecipitationDuration(WeatherNomadsComponent component)
  358. {
  359. var duration = Random.Shared.Next(component.MinPrecipitationDurationMinutes, component.MaxPrecipitationDurationMinutes + 1);
  360. return duration;
  361. }
  362. /// <summary>
  363. /// Determines if weather can affect a specific tile, based on roof coverage, tile type, and blocking entities.
  364. /// </summary>
  365. private bool CanWeatherAffect(EntityUid gridUid, MapGridComponent grid, TileRef tileRef, RoofComponent? roofComp)
  366. {
  367. if (tileRef.Tile.IsEmpty)
  368. return true;
  369. if (roofComp != null && _roofSystem.IsRooved((gridUid, grid, roofComp), tileRef.GridIndices))
  370. return false;
  371. var tileDef = (ContentTileDefinition)_tileDefManager[tileRef.Tile.TypeId];
  372. if (!tileDef.Weather)
  373. return false;
  374. var anchoredEntities = _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, tileRef.GridIndices);
  375. while (anchoredEntities.MoveNext(out var ent))
  376. {
  377. if (HasComp<BlockWeatherComponent>(ent.Value))
  378. return false;
  379. }
  380. return true;
  381. }
  382. /// <summary>
  383. /// Retrieves the EntityUid of the grid associated with a given map ID.
  384. /// Assumes one grid per map for simplicity.
  385. /// </summary>
  386. private EntityUid? GetGridUidForMap(MapId mapId)
  387. {
  388. var grids = _mapManager.GetAllMapGrids(mapId);
  389. return grids.Any() ? grids.First().Owner : null;
  390. }
  391. /// <summary>
  392. /// Gets the next season in the cycle.
  393. /// </summary>
  394. private string GetNextSeason(string current)
  395. {
  396. return current switch
  397. {
  398. "Spring" => "Summer",
  399. "Summer" => "Autumn",
  400. "Autumn" => "Winter",
  401. "Winter" => "Spring",
  402. _ => "Spring" // Default to Spring if something goes wrong
  403. };
  404. }
  405. }