using System.Collections.Generic; using System.Linq; using Content.Shared.Weather; using Robust.Shared.Map; using Robust.Shared.Prototypes; using Robust.Shared.Timing; using Robust.Shared.GameObjects; using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Shared.Atmos; using Content.Shared.Maps; using Robust.Shared.Map.Components; using Content.Server.Chat.Systems; using Content.Shared.Light.EntitySystems; using Content.Shared.Light.Components; using System; namespace Content.Server.Weather; /// /// System responsible for managing dynamic weather changes and temperature adjustments for exposed tiles in a grid. /// public sealed class WeatherNomadsSystem : EntitySystem { // Dependencies injected via IoC [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly SharedWeatherSystem _weatherSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphere = default!; [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly SharedRoofSystem _roofSystem = default!; [Dependency] private readonly ITileDefinitionManager _tileDefManager = default!; [Dependency] private readonly SharedMapSystem _mapSystem = default!; [Dependency] private readonly ChatSystem _chat = default!; /// /// Structure representing properties of a weather type. /// private class WeatherType { public string? PrototypeId { get; set; } // ID of the weather prototype, null for "Clear" public int Weight { get; set; } // Weight for weather transition order (unused now, kept for compatibility) public float MinTemperature { get; set; } // Minimum temperature in Kelvin public float MaxTemperature { get; set; } // Maximum temperature in Kelvin } /// /// Dictionary defining available weather types and their properties. /// private readonly Dictionary _weatherTypes = new() { { "Clear", new WeatherType { PrototypeId = "", Weight = 0, MinTemperature = 293.15f, MaxTemperature = 293.15f } }, { "Rain", new WeatherType { PrototypeId = "Rain", Weight = 1, MinTemperature = 278.15f, MaxTemperature = 288.15f } }, { "Storm", new WeatherType { PrototypeId = "Storm", Weight = 3, MinTemperature = 273.15f, MaxTemperature = 278.15f } }, { "SnowfallLight", new WeatherType { PrototypeId = "SnowfallLight", Weight = 4, MinTemperature = 268.15f, MaxTemperature = 273.15f } }, { "SnowfallMedium", new WeatherType { PrototypeId = "SnowfallMedium", Weight = 5, MinTemperature = 258.15f, MaxTemperature = 268.15f } }, { "SnowfallHeavy", new WeatherType { PrototypeId = "SnowfallHeavy", Weight = 6, MinTemperature = 243.15f, MaxTemperature = 258.15f } }, { "Hail", new WeatherType { PrototypeId = "Hail", Weight = 7, MinTemperature = 273.15f, MaxTemperature = 278.15f } }, { "Sandstorm", new WeatherType { PrototypeId = "Sandstorm", Weight = 9, MinTemperature = 293.15f, MaxTemperature = 313.15f } }, { "SandstormHeavy", new WeatherType { PrototypeId = "SandstormHeavy", Weight = 10, MinTemperature = 293.15f, MaxTemperature = 313.15f } }, }; /// /// Dictionary mapping (Biome, Season, Precipitation) to specific weather types. /// private static readonly Dictionary<(Biome, string, Precipitation), string> _weatherTransitionMap = new() { // Summer { (Biome.Tundra, "Summer", Precipitation.Dry), "Clear" }, { (Biome.Tundra, "Summer", Precipitation.LightWet), "SnowfallLight" }, { (Biome.Tundra, "Summer", Precipitation.HeavyWet), "SnowfallMedium" }, { (Biome.Tundra, "Summer", Precipitation.Storm), "SnowfallHeavy" }, { (Biome.Taiga, "Summer", Precipitation.Dry), "Clear" }, { (Biome.Taiga, "Summer", Precipitation.LightWet), "Rain" }, { (Biome.Taiga, "Summer", Precipitation.HeavyWet), "Rain" }, { (Biome.Taiga, "Summer", Precipitation.Storm), "Hail" }, { (Biome.Temperate, "Summer", Precipitation.Dry), "Clear" }, { (Biome.Temperate, "Summer", Precipitation.LightWet), "Rain" }, { (Biome.Temperate, "Summer", Precipitation.HeavyWet), "Rain" }, { (Biome.Temperate, "Summer", Precipitation.Storm), "Storm" }, { (Biome.Sea, "Summer", Precipitation.Dry), "Clear" }, { (Biome.Sea, "Summer", Precipitation.LightWet), "Rain" }, { (Biome.Sea, "Summer", Precipitation.HeavyWet), "Rain" }, { (Biome.Sea, "Summer", Precipitation.Storm), "Storm" }, { (Biome.SemiArid, "Summer", Precipitation.Dry), "Clear" }, { (Biome.SemiArid, "Summer", Precipitation.LightWet), "Clear" }, { (Biome.SemiArid, "Summer", Precipitation.HeavyWet), "Rain" }, { (Biome.SemiArid, "Summer", Precipitation.Storm), "Rain" }, { (Biome.Desert, "Summer", Precipitation.Dry), "Clear" }, { (Biome.Desert, "Summer", Precipitation.LightWet), "Clear" }, { (Biome.Desert, "Summer", Precipitation.HeavyWet), "Sandstorm" }, { (Biome.Desert, "Summer", Precipitation.Storm), "SandstormHeavy" }, { (Biome.Savanna, "Summer", Precipitation.Dry), "Clear" }, { (Biome.Savanna, "Summer", Precipitation.LightWet), "Clear" }, { (Biome.Savanna, "Summer", Precipitation.HeavyWet), "Rain" }, { (Biome.Savanna, "Summer", Precipitation.Storm), "Storm" }, { (Biome.Jungle, "Summer", Precipitation.Dry), "Clear" }, { (Biome.Jungle, "Summer", Precipitation.LightWet), "Rain" }, { (Biome.Jungle, "Summer", Precipitation.HeavyWet), "Storm" }, { (Biome.Jungle, "Summer", Precipitation.Storm), "Storm" }, // Spring { (Biome.Tundra, "Spring", Precipitation.Dry), "Clear" }, { (Biome.Tundra, "Spring", Precipitation.LightWet), "SnowfallLight" }, { (Biome.Tundra, "Spring", Precipitation.HeavyWet), "SnowfallMedium" }, { (Biome.Tundra, "Spring", Precipitation.Storm), "SnowfallHeavy" }, { (Biome.Taiga, "Spring", Precipitation.Dry), "Clear" }, { (Biome.Taiga, "Spring", Precipitation.LightWet), "Rain" }, { (Biome.Taiga, "Spring", Precipitation.HeavyWet), "SnowfallLight" }, { (Biome.Taiga, "Spring", Precipitation.Storm), "SnowfallHeavy" }, { (Biome.Temperate, "Spring", Precipitation.Dry), "Clear" }, { (Biome.Temperate, "Spring", Precipitation.LightWet), "Rain" }, { (Biome.Temperate, "Spring", Precipitation.HeavyWet), "Storm" }, { (Biome.Temperate, "Spring", Precipitation.Storm), "SnowfallMedium" }, { (Biome.Sea, "Spring", Precipitation.Dry), "Clear" }, { (Biome.Sea, "Spring", Precipitation.LightWet), "Rain" }, { (Biome.Sea, "Spring", Precipitation.HeavyWet), "Rain" }, { (Biome.Sea, "Spring", Precipitation.Storm), "Storm" }, { (Biome.SemiArid, "Spring", Precipitation.Dry), "Clear" }, { (Biome.SemiArid, "Spring", Precipitation.LightWet), "Clear" }, { (Biome.SemiArid, "Spring", Precipitation.HeavyWet), "Rain" }, { (Biome.SemiArid, "Spring", Precipitation.Storm), "Rain" }, { (Biome.Desert, "Spring", Precipitation.Dry), "Clear" }, { (Biome.Desert, "Spring", Precipitation.LightWet), "Clear" }, { (Biome.Desert, "Spring", Precipitation.HeavyWet), "Rain" }, { (Biome.Desert, "Spring", Precipitation.Storm), "Sandstorm" }, { (Biome.Savanna, "Spring", Precipitation.Dry), "Clear" }, { (Biome.Savanna, "Spring", Precipitation.LightWet), "Clear" }, { (Biome.Savanna, "Spring", Precipitation.HeavyWet), "Rain" }, { (Biome.Savanna, "Spring", Precipitation.Storm), "Storm" }, { (Biome.Jungle, "Spring", Precipitation.Dry), "Clear" }, { (Biome.Jungle, "Spring", Precipitation.LightWet), "Rain" }, { (Biome.Jungle, "Spring", Precipitation.HeavyWet), "Rain" }, { (Biome.Jungle, "Spring", Precipitation.Storm), "Storm" }, // Autumn { (Biome.Tundra, "Autumn", Precipitation.Dry), "Clear" }, { (Biome.Tundra, "Autumn", Precipitation.LightWet), "SnowfallLight" }, { (Biome.Tundra, "Autumn", Precipitation.HeavyWet), "SnowfallMedium" }, { (Biome.Tundra, "Autumn", Precipitation.Storm), "SnowfallHeavy" }, { (Biome.Taiga, "Autumn", Precipitation.Dry), "Clear" }, { (Biome.Taiga, "Autumn", Precipitation.LightWet), "Rain" }, { (Biome.Taiga, "Autumn", Precipitation.HeavyWet), "SnowfallLight" }, { (Biome.Taiga, "Autumn", Precipitation.Storm), "SnowfallHeavy" }, { (Biome.Temperate, "Autumn", Precipitation.Dry), "Clear" }, { (Biome.Temperate, "Autumn", Precipitation.LightWet), "Rain" }, { (Biome.Temperate, "Autumn", Precipitation.HeavyWet), "Storm" }, { (Biome.Temperate, "Autumn", Precipitation.Storm), "SnowfallMedium" }, { (Biome.Sea, "Autumn", Precipitation.Dry), "Clear" }, { (Biome.Sea, "Autumn", Precipitation.LightWet), "Rain" }, { (Biome.Sea, "Autumn", Precipitation.HeavyWet), "Rain" }, { (Biome.Sea, "Autumn", Precipitation.Storm), "Storm" }, { (Biome.SemiArid, "Autumn", Precipitation.Dry), "Clear" }, { (Biome.SemiArid, "Autumn", Precipitation.LightWet), "Clear" }, { (Biome.SemiArid, "Autumn", Precipitation.HeavyWet), "Rain" }, { (Biome.SemiArid, "Autumn", Precipitation.Storm), "Rain" }, { (Biome.Desert, "Autumn", Precipitation.Dry), "Clear" }, { (Biome.Desert, "Autumn", Precipitation.LightWet), "Clear" }, { (Biome.Desert, "Autumn", Precipitation.HeavyWet), "Rain" }, { (Biome.Desert, "Autumn", Precipitation.Storm), "Sandstorm" }, { (Biome.Savanna, "Autumn", Precipitation.Dry), "Clear" }, { (Biome.Savanna, "Autumn", Precipitation.LightWet), "Clear" }, { (Biome.Savanna, "Autumn", Precipitation.HeavyWet), "Rain" }, { (Biome.Savanna, "Autumn", Precipitation.Storm), "Storm" }, { (Biome.Jungle, "Autumn", Precipitation.Dry), "Clear" }, { (Biome.Jungle, "Autumn", Precipitation.LightWet), "Rain" }, { (Biome.Jungle, "Autumn", Precipitation.HeavyWet), "Rain" }, { (Biome.Jungle, "Autumn", Precipitation.Storm), "Storm" }, // Winter { (Biome.Tundra, "Winter", Precipitation.Dry), "Clear" }, { (Biome.Tundra, "Winter", Precipitation.LightWet), "SnowfallMedium" }, { (Biome.Tundra, "Winter", Precipitation.HeavyWet), "SnowfallHeavy" }, { (Biome.Tundra, "Winter", Precipitation.Storm), "SnowfallHeavy" }, { (Biome.Taiga, "Winter", Precipitation.Dry), "Clear" }, { (Biome.Taiga, "Winter", Precipitation.LightWet), "SnowfallLight" }, { (Biome.Taiga, "Winter", Precipitation.HeavyWet), "SnowfallHeavy" }, { (Biome.Taiga, "Winter", Precipitation.Storm), "SnowfallHeavy" }, { (Biome.Temperate, "Winter", Precipitation.Dry), "Clear" }, { (Biome.Temperate, "Winter", Precipitation.LightWet), "SnowfallLight" }, { (Biome.Temperate, "Winter", Precipitation.HeavyWet), "SnowfallMedium" }, { (Biome.Temperate, "Winter", Precipitation.Storm), "SnowfallHeavy" }, { (Biome.Sea, "Winter", Precipitation.Dry), "Clear" }, { (Biome.Sea, "Winter", Precipitation.LightWet), "Rain" }, { (Biome.Sea, "Winter", Precipitation.HeavyWet), "Storm" }, { (Biome.Sea, "Winter", Precipitation.Storm), "Storm" }, { (Biome.SemiArid, "Winter", Precipitation.Dry), "Clear" }, { (Biome.SemiArid, "Winter", Precipitation.LightWet), "Rain" }, { (Biome.SemiArid, "Winter", Precipitation.HeavyWet), "Rain" }, { (Biome.SemiArid, "Winter", Precipitation.Storm), "Storm" }, { (Biome.Desert, "Winter", Precipitation.Dry), "Clear" }, { (Biome.Desert, "Winter", Precipitation.LightWet), "Clear" }, { (Biome.Desert, "Winter", Precipitation.HeavyWet), "Rain" }, { (Biome.Desert, "Winter", Precipitation.Storm), "Rain" }, { (Biome.Savanna, "Winter", Precipitation.Dry), "Clear" }, { (Biome.Savanna, "Winter", Precipitation.LightWet), "Rain" }, { (Biome.Savanna, "Winter", Precipitation.HeavyWet), "Storm" }, { (Biome.Savanna, "Winter", Precipitation.Storm), "Storm" }, { (Biome.Jungle, "Winter", Precipitation.Dry), "Clear" }, { (Biome.Jungle, "Winter", Precipitation.LightWet), "Rain" }, { (Biome.Jungle, "Winter", Precipitation.HeavyWet), "Storm" }, { (Biome.Jungle, "Winter", Precipitation.Storm), "Storm" }, }; /// /// Dictionary that maps each season to its transformation rules for tiles and entities. /// Each entry contains a tuple with two dictionaries: /// - Tile transformation dictionary: Maps original tile names to their transformed counterparts. /// - Entity transformation dictionary: Maps original entity prototype IDs to their transformed counterparts. /// Seasons without transformations have empty dictionaries. /// private readonly Dictionary TileTransformations, Dictionary EntityTransformations)> _seasonBasedTransformationRules = new() { ["Winter"] = ( TileTransformations: new Dictionary { { "FloorPlanetGrass", "FloorSnowGrass" }, { "FloorDirt", "FloorSnow" }, { "FloorSand", "FloorSnowSand" }, }, EntityTransformations: new Dictionary { { "TreeTemperate", "TreeTemperateSnow" } // Temperate trees get a snowy variant } ), ["Summer"] = ( TileTransformations: new Dictionary(), EntityTransformations: new Dictionary() ), ["Spring"] = ( TileTransformations: new Dictionary { { "FloorSnowGrass", "FloorPlanetGrass" }, { "FloorSnow", "FloorDirt" }, { "FloorSnowSand", "FloorSand" }, }, EntityTransformations: new Dictionary { { "TreeTemperateSnow", "TreeTemperate" } } ), ["Autumn"] = ( TileTransformations: new Dictionary(), EntityTransformations: new Dictionary() ) }; /// /// Initializes the system and subscribes to relevant events. /// public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnMapInit); } /// /// Handles the initialization of weather for a map when it is first created. /// private void OnMapInit(EntityUid uid, WeatherNomadsComponent component, MapInitEvent args) { component.CurrentPrecipitation = Precipitation.Dry; component.CurrentWeather = "Clear"; component.NextSwitchTime = _timing.CurTime + TimeSpan.FromMinutes(GetRandomPrecipitationDuration(component)); component.NextSeasonChange = _timing.CurTime + TimeSpan.FromMinutes(GetRandomSeasonDuration(component)); Dirty(uid, component); UpdateTileWeathers(uid, component); _chat.DispatchGlobalAnnouncement($"Current season: {component.CurrentSeason}", "World", false, null, null); } /// /// Updates the weather system periodically, switching precipitation and season states as needed. /// public override void Update(float frameTime) { base.Update(frameTime); var query = EntityQueryEnumerator(); while (query.MoveNext(out var uid, out var nomads)) { // Handle season changes if (_timing.CurTime >= nomads.NextSeasonChange) { var oldSeason = nomads.CurrentSeason; nomads.CurrentSeason = GetNextSeason(nomads.CurrentSeason); nomads.NextSeasonChange = _timing.CurTime + TimeSpan.FromMinutes(GetRandomSeasonDuration(nomads)); Dirty(uid, nomads); _chat.DispatchGlobalAnnouncement($"Changed season to {nomads.CurrentSeason}", null, false, null, null); Log.Debug($"Season changed from {oldSeason} to {nomads.CurrentSeason} for entity {uid}, triggering UpdateTileWeathers"); UpdateTileWeathers(uid, nomads); } // Handle precipitation changes if (_timing.CurTime < nomads.NextSwitchTime) { continue; } var oldPrecipitation = nomads.CurrentPrecipitation; nomads.CurrentPrecipitation = GetNextPrecipitation(nomads.CurrentPrecipitation); nomads.NextSwitchTime = _timing.CurTime + TimeSpan.FromMinutes(GetRandomPrecipitationDuration(nomads)); Dirty(uid, nomads); Log.Debug($"Precipitation changed from {oldPrecipitation} to {nomads.CurrentPrecipitation} for entity {uid}, triggering UpdateTileWeathers"); UpdateTileWeathers(uid, nomads); } } /// /// Updates weather effects for each tile based on biome, season, and global precipitation. /// Also applies transformations to tiles and entities based on the current season if transformation rules are defined. /// private void UpdateTileWeathers(EntityUid uid, WeatherNomadsComponent nomads) { Log.Debug($"Starting UpdateTileWeathers for entity {uid}, season: {nomads.CurrentSeason}"); var mapId = Transform(uid).MapID; var gridUid = GetGridUidForMap(mapId); if (gridUid == null) { Log.Warning($"No grid found for map {mapId}"); return; } Log.Debug($"Grid found for map {mapId}: {gridUid}"); if (!TryComp(gridUid.Value, out var grid)) { Log.Warning($"No MapGridComponent found for grid {gridUid}"); return; } if (!TryComp(gridUid.Value, out var gridAtmosphere)) { Log.Warning($"No GridAtmosphereComponent found for grid {gridUid}"); return; } RoofComponent? roofComp = TryComp(gridUid.Value, out var rc) ? rc : null; // Retrieve transformation rules for the current season, defaulting to empty dictionaries if not found if (!_seasonBasedTransformationRules.TryGetValue(nomads.CurrentSeason, out var transformationRules)) { Log.Warning($"No transformation rules defined for season {nomads.CurrentSeason}"); transformationRules = (TileTransformations: new Dictionary(), EntityTransformations: new Dictionary()); } Log.Debug($"Transformation rules retrieved for season {nomads.CurrentSeason}: {transformationRules.TileTransformations.Count} tile rules, {transformationRules.EntityTransformations.Count} entity rules"); var tileTransformationDictionary = transformationRules.TileTransformations; var entityTransformationDictionary = transformationRules.EntityTransformations; // Apply tile transformations only if there are rules defined if (tileTransformationDictionary.Count > 0) { Log.Debug($"Processing {gridAtmosphere.Tiles.Count} tiles for transformation in season {nomads.CurrentSeason}"); foreach (var tile in gridAtmosphere.Tiles.Values) { var tileRef = grid.GetTileRef(tile.GridIndices); if (tileRef.Tile.IsEmpty) { continue; // Skip empty tiles } var tileDef = (ContentTileDefinition)_tileDefManager[tileRef.Tile.TypeId]; if (tileTransformationDictionary.TryGetValue(tileDef.ID, out var transformedTileName)) { if (tileDef.ID == transformedTileName) { continue; } var newTileDefinition = _tileDefManager[transformedTileName]; var newTile = new Tile(newTileDefinition.TileId); grid.SetTile(tileRef.GridIndices, newTile); Log.Debug($"Transformed tile at {tileRef.GridIndices} from {tileDef.ID} to {transformedTileName} for season {nomads.CurrentSeason}"); } else { Log.Debug($"No transformation rule found for tile {tileDef.ID} at {tileRef.GridIndices}"); } // Get biome from tile definition if (!Enum.TryParse(tileDef.Biome, true, out var biome)) { biome = Biome.Temperate; // Fallback to Temperate if biome string is invalid Log.Warning($"Invalid biome '{tileDef.Biome}' for tile at {tileRef.GridIndices}, defaulting to Temperate"); } if (_weatherTransitionMap.TryGetValue((biome, nomads.CurrentSeason, nomads.CurrentPrecipitation), out var weatherType)) { ApplyWeatherToTile(uid, nomads, gridUid.Value, tileRef, weatherType, grid, gridAtmosphere, roofComp); } else { Log.Warning($"No weather mapping found for Biome: {biome}, Season: {nomads.CurrentSeason}, Precipitation: {nomads.CurrentPrecipitation}"); } } } else { Log.Debug($"No tile transformations defined for season {nomads.CurrentSeason}, processing {gridAtmosphere.Tiles.Count} tiles for weather effects only"); // If no tile transformations, just apply weather effects foreach (var tile in gridAtmosphere.Tiles.Values) { var tileRef = grid.GetTileRef(tile.GridIndices); if (tileRef.Tile.IsEmpty) { Log.Debug($"Skipping empty tile at {tileRef.GridIndices}"); continue; // Skip empty tiles } // Get biome from tile definition var tileDef = (ContentTileDefinition)_tileDefManager[tileRef.Tile.TypeId]; if (!Enum.TryParse(tileDef.Biome, true, out var biome)) { biome = Biome.Temperate; // Fallback to Temperate if biome string is invalid Log.Warning($"Invalid biome '{tileDef.Biome}' for tile at {tileRef.GridIndices}, defaulting to Temperate"); } if (_weatherTransitionMap.TryGetValue((biome, nomads.CurrentSeason, nomads.CurrentPrecipitation), out var weatherType)) { ApplyWeatherToTile(uid, nomads, gridUid.Value, tileRef, weatherType, grid, gridAtmosphere, roofComp); } else { Log.Warning($"No weather mapping found for Biome: {biome}, Season: {nomads.CurrentSeason}, Precipitation: {nomads.CurrentPrecipitation}"); } } } // Apply entity transformations only if there are rules defined if (entityTransformationDictionary.Count > 0) { Log.Debug($"Calling TransformEntitiesOnGrid for {entityTransformationDictionary.Count} entity transformation rules in season {nomads.CurrentSeason}"); TransformEntitiesOnGrid(gridUid.Value, nomads, entityTransformationDictionary); } else { Log.Debug($"No entity transformations defined for season {nomads.CurrentSeason}, skipping TransformEntitiesOnGrid"); } } /// /// Applies weather effects and temperature to a specific tile. /// private void ApplyWeatherToTile(EntityUid weatherUid, WeatherNomadsComponent nomads, EntityUid gridUid, TileRef tileRef, string weatherType, MapGridComponent grid, GridAtmosphereComponent gridAtmosphere, RoofComponent? roofComp) { if (!CanWeatherAffect(gridUid, grid, tileRef, roofComp)) return; var tile = gridAtmosphere.Tiles[tileRef.GridIndices]; if (tile.Air == null) return; if (!_weatherTypes.TryGetValue(weatherType, out var weatherData)) { Log.Warning($"Weather type {weatherType} not found in _weatherTypes"); return; } // Update CurrentWeather if it has changed if (nomads.CurrentWeather != weatherType) { nomads.CurrentWeather = weatherType; Dirty(weatherUid, nomads); } // Apply weather visuals globally var mapId = Transform(gridUid).MapID; if (!string.IsNullOrEmpty(weatherData.PrototypeId) && _prototypeManager.TryIndex(weatherData.PrototypeId, out var proto)) { _weatherSystem.SetWeather(mapId, proto, null); } else { _weatherSystem.SetWeather(mapId, null, null); } // Adjust temperature var temperature = (float)(weatherData.MinTemperature + (weatherData.MaxTemperature - weatherData.MinTemperature) * Random.Shared.NextDouble()); var air = tile.Air; if (air.Immutable) { var newAir = new GasMixture(); newAir.CopyFrom(air); air = newAir; } air.Temperature = temperature; } /// /// Transforms entities on the grid based on the transformation rules defined for the current season. /// private void TransformEntitiesOnGrid(EntityUid gridUid, WeatherNomadsComponent nomads, Dictionary entityTransformationDictionary) { if (TryComp(gridUid, out var gridComp)) { Log.Debug($"TransformEntitiesOnGrid: MapGridComponent found for grid {gridUid}"); var anchoredEntities = EntityQuery() .Where(t => t.GridUid == gridUid && t.Anchored) .Select(t => t.Owner) .ToList(); Log.Debug($"Found {anchoredEntities.Count} anchored entities on grid {gridUid}"); foreach (var entity in anchoredEntities) { if (!TryComp(entity, out var metaData)) { Log.Debug($"Entity {entity} has no MetaDataComponent"); continue; } if (metaData.EntityPrototype == null) { Log.Debug($"Entity {entity} has no EntityPrototype"); continue; } if (!entityTransformationDictionary.TryGetValue(metaData.EntityPrototype.ID, out var transformedEntityPrototypeId)) { Log.Debug($"No transformation rule for entity {entity} with prototype ID {metaData.EntityPrototype.ID}"); continue; } if (metaData.EntityPrototype.ID == transformedEntityPrototypeId) { continue; } var transformComponent = Transform(entity); var entityCoordinates = transformComponent.Coordinates; QueueDel(entity); var newEntity = Spawn(transformedEntityPrototypeId, entityCoordinates); Log.Debug($"Transformed entity {entity} to {newEntity} at {entityCoordinates} for season {nomads.CurrentSeason}"); } } else { Log.Warning($"Could not get MapGridComponent for grid {gridUid}"); } } /// /// Gets the next precipitation state in the cycle. /// private Precipitation GetNextPrecipitation(Precipitation current) { return current switch { Precipitation.Dry => Precipitation.LightWet, Precipitation.LightWet => Precipitation.HeavyWet, Precipitation.HeavyWet => Precipitation.Storm, Precipitation.Storm => Precipitation.Dry, _ => Precipitation.Dry // Default to Dry if something goes wrong }; } /// /// Generates a random duration for a season based on component settings. /// private double GetRandomSeasonDuration(WeatherNomadsComponent component) { var duration = Random.Shared.Next(component.MinSeasonMinutes, component.MaxSeasonMinutes + 1); return duration; } /// /// Generates a random duration for a precipitation change based on component settings. /// private double GetRandomPrecipitationDuration(WeatherNomadsComponent component) { var duration = Random.Shared.Next(component.MinPrecipitationDurationMinutes, component.MaxPrecipitationDurationMinutes + 1); return duration; } /// /// Determines if weather can affect a specific tile, based on roof coverage, tile type, and blocking entities. /// private bool CanWeatherAffect(EntityUid gridUid, MapGridComponent grid, TileRef tileRef, RoofComponent? roofComp) { if (tileRef.Tile.IsEmpty) return true; if (roofComp != null && _roofSystem.IsRooved((gridUid, grid, roofComp), tileRef.GridIndices)) return false; var tileDef = (ContentTileDefinition)_tileDefManager[tileRef.Tile.TypeId]; if (!tileDef.Weather) return false; var anchoredEntities = _mapSystem.GetAnchoredEntitiesEnumerator(gridUid, grid, tileRef.GridIndices); while (anchoredEntities.MoveNext(out var ent)) { if (HasComp(ent.Value)) return false; } return true; } /// /// Retrieves the EntityUid of the grid associated with a given map ID. /// Assumes one grid per map for simplicity. /// private EntityUid? GetGridUidForMap(MapId mapId) { var grids = _mapManager.GetAllMapGrids(mapId); return grids.Any() ? grids.First().Owner : null; } /// /// Gets the next season in the cycle. /// private string GetNextSeason(string current) { return current switch { "Spring" => "Summer", "Summer" => "Autumn", "Autumn" => "Winter", "Winter" => "Spring", _ => "Spring" // Default to Spring if something goes wrong }; } }