diff --git a/Content.Server/_CP14/MapEvents/CPMapEventsSchedulerComponent.cs b/Content.Server/_CP14/MapEvents/CPMapEventsSchedulerComponent.cs
new file mode 100644
index 0000000000..c9f6791990
--- /dev/null
+++ b/Content.Server/_CP14/MapEvents/CPMapEventsSchedulerComponent.cs
@@ -0,0 +1,33 @@
+using Robust.Shared.Map;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.CrystallPunk.MapEvents;
+
+///
+///
+///
+
+[RegisterComponent, Access(typeof(CPMapEventsSchedulerSystem))]
+public sealed partial class CPMapEventsSchedulerComponent : Component
+{
+ public MapId Map;
+
+ [DataField]
+ public float MinimumTimeUntilFirstEvent = 300f;
+
+ [DataField]
+ public float MinimumTimeBetweenEvents = 30f;
+
+ [DataField]
+ public float MaximumTimeBetweenEvents = 300f;
+
+ [DataField(required: true)]
+ public List WhitelistedEvents = new();
+
+
+ ///
+ /// How long until the next check for an event runs
+ ///
+ [DataField]
+ public TimeSpan NextEventTime;
+}
diff --git a/Content.Server/_CP14/MapEvents/CPMapEventsSchedulerSystem.cs b/Content.Server/_CP14/MapEvents/CPMapEventsSchedulerSystem.cs
new file mode 100644
index 0000000000..1e0b49a756
--- /dev/null
+++ b/Content.Server/_CP14/MapEvents/CPMapEventsSchedulerSystem.cs
@@ -0,0 +1,47 @@
+using Content.Server.GameTicking;
+using Content.Server.GameTicking.Rules;
+using Content.Server.GameTicking.Rules.Components;
+using Content.Server.StationEvents;
+using Robust.Shared.Random;
+using Robust.Shared.Timing;
+
+namespace Content.Server.CrystallPunk.MapEvents;
+
+public sealed class CPMapEventsSchedulerSystem : EntitySystem
+{
+ [Dependency] private readonly IRobustRandom _random = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] public readonly GameTicker GameTicker = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnSchedulerInit);
+ }
+
+ private void OnSchedulerInit(Entity scheduler, ref MapInitEvent args)
+ {
+ scheduler.Comp.NextEventTime = _timing.CurTime + TimeSpan.FromSeconds(scheduler.Comp.MinimumTimeUntilFirstEvent);
+ scheduler.Comp.Map = Transform(scheduler).MapID;
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var scheduler))
+ {
+ if (_timing.CurTime < scheduler.NextEventTime)
+ continue;
+
+ var randomEvent = _random.Pick(scheduler.WhitelistedEvents);
+
+ //var ent = GameTicker.AddGameRule(randomEvent);
+ GameTicker.StartGameRule(randomEvent);
+
+ scheduler.NextEventTime = _timing.CurTime + TimeSpan.FromSeconds(_random.NextFloat(scheduler.MinimumTimeBetweenEvents, scheduler.MaximumTimeBetweenEvents));
+ }
+ }
+}
diff --git a/Content.Server/_CP14/MapEvents/Components/CPWeatherRuleComponent.cs b/Content.Server/_CP14/MapEvents/Components/CPWeatherRuleComponent.cs
new file mode 100644
index 0000000000..215e1b83c4
--- /dev/null
+++ b/Content.Server/_CP14/MapEvents/Components/CPWeatherRuleComponent.cs
@@ -0,0 +1,21 @@
+using Content.Shared.Weather;
+using Robust.Shared.Prototypes;
+
+namespace Content.Server.CrystallPunk.MapEvents;
+
+///
+///
+///
+
+[RegisterComponent, Access(typeof(CPWeatherRule))]
+public sealed partial class CPWeatherRuleComponent : Component
+{
+ [DataField(required: true)]
+ public ProtoId Weather = "Storm";
+
+ [DataField]
+ public float MinimumLength = 60f;
+
+ [DataField]
+ public float MaximumLength = 300f;
+}
diff --git a/Content.Server/_CP14/MapEvents/Events/CPWeatherRule.cs b/Content.Server/_CP14/MapEvents/Events/CPWeatherRule.cs
new file mode 100644
index 0000000000..ba8de3add6
--- /dev/null
+++ b/Content.Server/_CP14/MapEvents/Events/CPWeatherRule.cs
@@ -0,0 +1,29 @@
+using Content.Server.GameTicking.Rules.Components;
+using Content.Server.Station.Systems;
+using Content.Server.StationEvents.Events;
+using Content.Server.Weather;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Random;
+using Robust.Shared.Timing;
+
+namespace Content.Server.CrystallPunk.MapEvents;
+
+
+public sealed class CPWeatherRule : StationEventSystem
+{
+
+ [Dependency] private readonly WeatherSystem _weather = default!;
+ [Dependency] private readonly IRobustRandom _random = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly IPrototypeManager _proto = default!;
+ protected override void Started(EntityUid uid, CPWeatherRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args)
+ {
+ base.Started(uid, component, gameRule, args);
+
+
+ var mapId = GameTicker.DefaultMap;
+ var duration = _random.NextFloat(component.MinimumLength, component.MaximumLength);
+
+ _weather.SetWeather(mapId, _proto.Index(component.Weather), _timing.CurTime + TimeSpan.FromSeconds(duration));
+ }
+}
diff --git a/Resources/Maps/CrystallPunk/dev_map.yml b/Resources/Maps/CrystallPunk/dev_map.yml
index b88833ef8f..a41be683a6 100644
--- a/Resources/Maps/CrystallPunk/dev_map.yml
+++ b/Resources/Maps/CrystallPunk/dev_map.yml
@@ -391,6 +391,14 @@ entities:
- type: GridTree
- type: MovedGrids
- type: MapLight
+ - type: CPMapEventsScheduler
+ minimumTimeUntilFirstEvent: 5
+ minimumTimeBetweenEvents: 50
+ maximumTimeBetweenEvents: 120
+ whitelistedEvents:
+ - CPWeatherRain
+ - CPWeatherStorm
+ - CPWeatherAshfallHeavy
- type: DayCycle
timeEntries:
- startColor: "#754a4a" #Рассвет
diff --git a/Resources/Prototypes/_CP14/Entities/GameRules/weather.yml b/Resources/Prototypes/_CP14/Entities/GameRules/weather.yml
new file mode 100644
index 0000000000..b08f4221b3
--- /dev/null
+++ b/Resources/Prototypes/_CP14/Entities/GameRules/weather.yml
@@ -0,0 +1,29 @@
+- type: entity
+ id: CPWeatherStorm
+ parent: BaseGameRule
+ noSpawn: true
+ components:
+ - type: CPWeatherRule
+ weather: Storm
+ minimumLength: 30
+ maximumLength: 120
+
+- type: entity
+ id: CPWeatherRain
+ parent: BaseGameRule
+ noSpawn: true
+ components:
+ - type: CPWeatherRule
+ weather: Rain
+ minimumLength: 30
+ maximumLength: 120
+
+- type: entity
+ id: CPWeatherAshfallHeavy
+ parent: BaseGameRule
+ noSpawn: true
+ components:
+ - type: CPWeatherRule
+ weather: AshfallHeavy
+ minimumLength: 30
+ maximumLength: 120
\ No newline at end of file