diff --git a/Content.Server/Weather/WeatherCycleComponent.cs b/Content.Server/Weather/WeatherCycleComponent.cs new file mode 100644 index 00000000000000..24da0e1fae8f00 --- /dev/null +++ b/Content.Server/Weather/WeatherCycleComponent.cs @@ -0,0 +1,28 @@ +using Content.Shared.Weather; +using System.ComponentModel.DataAnnotations; +using Robust.Shared.Prototypes; + +namespace Content.Server.Weather +{ + [RegisterComponent] + public sealed partial class WeatherCycleComponent : Component + { + [ViewVariables(VVAccess.ReadWrite), DataField("isEnabled")] + public bool IsEnabled = true; + [ViewVariables(VVAccess.ReadWrite), DataField("weatherCicleDuration")] + public int WeatherCicleDuration = 9; + [ViewVariables(VVAccess.ReadWrite), DataField("weatherCicleStart")] + public int WeatherCicleStart = 19; + [ViewVariables(VVAccess.ReadWrite), DataField("minDuration")] + public int MinDuration = 3; + [ViewVariables(VVAccess.ReadWrite), DataField("maxDuration")] + public int MaxDuration = 8; + [ViewVariables(VVAccess.ReadWrite), DataField("minStartHour")] + public int MinStartHour = 1; + [ViewVariables(VVAccess.ReadWrite), DataField("maxStartHour")] + public int MaxStartHour = 23; + [ViewVariables(VVAccess.ReadWrite), DataField("weatherIds")] + public List WeatherIds { get; set; } = new(); + public bool IsCycleActive { get; set; } = false; + } +} diff --git a/Content.Server/Weather/WeatherCycleSystem.cs b/Content.Server/Weather/WeatherCycleSystem.cs new file mode 100644 index 00000000000000..7b1c463e1f502e --- /dev/null +++ b/Content.Server/Weather/WeatherCycleSystem.cs @@ -0,0 +1,151 @@ +using System.Linq; +using Content.Server.Chat.Systems; +using Content.Server.Weather; +using Content.Shared.CCVar; +using Content.Shared.Weather; +using Content.Shared.Coordinates; +using Robust.Shared.Map; +using Robust.Shared.Configuration; +using Robust.Shared.Random; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; + +namespace Content.Server.Time +{ + public sealed partial class WeatherCycleSystem : EntitySystem + { + [Dependency] private readonly IConfigurationManager _configuration = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IEntityManager _entityManager = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IEntitySystemManager _entitySystem = default!; + [Dependency] private readonly SharedWeatherSystem _weatherSystem = default!; + [Dependency] private readonly IGameTiming _timing = default!; + private TimeSystem? _timeSystem; + private int _currentHour; + private double _deltaTime; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnMapInit); + _timeSystem = _entitySystem.GetEntitySystem(); + } + + private void OnMapInit(EntityUid uid, WeatherCycleComponent comp, ComponentInit args) + { + comp.IsEnabled = _configuration.GetCVar(CCVars.CycleEnabled); + SetNewWeather(comp); + SetRandomCycleValues(comp); + comp.IsEnabled = true; + } + + private void SetRandomCycleValues(WeatherCycleComponent comp) + { + int previousDuration = comp.WeatherCicleDuration; + int previousStart = comp.WeatherCicleStart; + + int newDuration = _random.Next(comp.MinDuration, comp.MaxDuration); + int newStartHour = _random.Next(comp.MinStartHour, comp.MaxStartHour); + + if (Math.Abs(newDuration - previousDuration) < 2) + { + comp.WeatherCicleDuration = newDuration >= comp.MaxDuration ? newDuration - 1 : newDuration + 1; + } + else + { + comp.WeatherCicleDuration = newDuration; + } + + if (Math.Abs(newStartHour - previousStart) < 2) + { + comp.WeatherCicleStart = newStartHour >= comp.MaxStartHour ? newStartHour - 1 : newStartHour + 1; + } + else + { + comp.WeatherCicleStart = newStartHour; + } + } + + public override void Update(float frameTime) + { + if (_deltaTime >= 1.0) + { + _deltaTime = 0; + _currentHour = _timeSystem!.GetStationTime().Hours; + foreach (var comp in EntityQuery()) + { + if (comp.IsEnabled) + { + int endHour = (comp.WeatherCicleStart + comp.WeatherCicleDuration) % 24; + bool isWithinCycle; + + if (comp.WeatherCicleDuration >= 24) + { + isWithinCycle = true; + } + else if (comp.WeatherCicleStart + comp.WeatherCicleDuration < 24) + { + isWithinCycle = _currentHour >= comp.WeatherCicleStart && _currentHour < endHour; + } + else + { + isWithinCycle = _currentHour >= comp.WeatherCicleStart || _currentHour < endHour; + } + + if (!comp.IsCycleActive && isWithinCycle) + { + SetNewWeather(comp); + comp.IsCycleActive = true; + } + else if (comp.IsCycleActive && !isWithinCycle) + { + SetNewWeather(comp, true); + SetRandomCycleValues(comp); + comp.IsCycleActive = false; + } + } + } + } + else + { + _deltaTime += frameTime; + } + } + + private void SetNewWeather(WeatherCycleComponent comp, bool? clean = null) + { + if (comp.WeatherIds.Count == 0) + return; + + var curTime = _timing.CurTime; + var maxTime = TimeSpan.MaxValue; + + var mapId = comp.Owner.ToCoordinates().GetMapId(_entityManager); + + if (!_mapManager.MapExists(mapId)) + return; + + if (clean == true) + { + _weatherSystem.SetWeather(mapId, null, null); + return; + } + + var climaId = _random.Pick(comp.WeatherIds); + + if (TryComp(_mapManager.GetMapEntityId(mapId), out var weatherComp) && + weatherComp.Weather.TryGetValue(climaId, out var existing)) + { + maxTime = curTime - existing.StartTime; + } + + if (!_prototypeManager.TryIndex(climaId, out var clima)) + return; + + _weatherSystem.SetWeather(mapId, clima, null); + } + + } +} diff --git a/Resources/Maps/glacier.yml b/Resources/Maps/glacier.yml index d690941e05e287..4a3e107593a263 100644 --- a/Resources/Maps/glacier.yml +++ b/Resources/Maps/glacier.yml @@ -387,6 +387,13 @@ entities: fixtures: {} - type: OccluderTree - type: SpreaderGrid + - type: WeatherCycle + IsEnabled: true + weatherIds: + - SnowfallLight + - SnowfallMedium + - SnowfallHeavy + - Hail - type: Shuttle - type: GridPathfinding - type: Gravity diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/clothesmate.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/clothesmate.yml index 6179b8b0c7128c..6480a4d30e5741 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/clothesmate.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/clothesmate.yml @@ -111,6 +111,7 @@ ClothingUniformJumpskirtColegialUniformJPNYellow: 2 ClothingSimpleUnderwear: 4 ClothingEyesGlassesCheapSunglasses: 3 + ClothingNeckRaincoat: 2 # DO NOT ADD MORE, USE UNIFORM DYING contrabandInventory: ClothingMaskNeckGaiter: 2 diff --git a/Resources/Prototypes/Catalog/VendingMachines/Inventories/winterdrobe.yml b/Resources/Prototypes/Catalog/VendingMachines/Inventories/winterdrobe.yml index 0f8c73dac307e5..191d84eacdd762 100644 --- a/Resources/Prototypes/Catalog/VendingMachines/Inventories/winterdrobe.yml +++ b/Resources/Prototypes/Catalog/VendingMachines/Inventories/winterdrobe.yml @@ -18,6 +18,7 @@ ClothingOuterCoatBomber: 3 ClothingHeadHatSantahat: 2 ClothingHeadHatXmasCrown: 2 + ClothingNeckRaincoat: 5 emaggedInventory: ClothingNeckScarfStripedSyndieGreen: 3 diff --git a/Resources/Prototypes/Entities/Clothing/Neck/misc.yml b/Resources/Prototypes/Entities/Clothing/Neck/misc.yml index 51325c0bbbd056..3c2951e4e9d80a 100644 --- a/Resources/Prototypes/Entities/Clothing/Neck/misc.yml +++ b/Resources/Prototypes/Entities/Clothing/Neck/misc.yml @@ -79,3 +79,18 @@ event: !type:StethoscopeActionEvent checkCanInteract: false priority: -1 + +- type: entity + parent: ClothingNeckBase + id: ClothingNeckRaincoat + name: Raincoat + description: for rainy days! + components: + - type: Armor + modifiers: + coefficients: + Heat: 0.10 + - type: Sprite + sprite: Clothing/Neck/Misc/raincoat.rsi + - type: Clothing + sprite: Clothing/Neck/Misc/raincoat.rsi diff --git a/Resources/Textures/Clothing/Neck/Misc/raincoat.rsi/equipped-NECK.png b/Resources/Textures/Clothing/Neck/Misc/raincoat.rsi/equipped-NECK.png new file mode 100644 index 00000000000000..ec40a07645f1c4 Binary files /dev/null and b/Resources/Textures/Clothing/Neck/Misc/raincoat.rsi/equipped-NECK.png differ diff --git a/Resources/Textures/Clothing/Neck/Misc/raincoat.rsi/icon.png b/Resources/Textures/Clothing/Neck/Misc/raincoat.rsi/icon.png new file mode 100644 index 00000000000000..c28d40c767abb6 Binary files /dev/null and b/Resources/Textures/Clothing/Neck/Misc/raincoat.rsi/icon.png differ diff --git a/Resources/Textures/Clothing/Neck/Misc/raincoat.rsi/meta.json b/Resources/Textures/Clothing/Neck/Misc/raincoat.rsi/meta.json new file mode 100644 index 00000000000000..b7d935e7f5c58e --- /dev/null +++ b/Resources/Textures/Clothing/Neck/Misc/raincoat.rsi/meta.json @@ -0,0 +1,18 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "taken from https://github.com/vgstation-coders/vgstation13/blob/HEAD/icons/mob/suit.dmi", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "equipped-NECK", + "directions": 4 + } + ] +}