Weather tweaks (#14271)
This commit is contained in:
@@ -46,7 +46,7 @@ public sealed class WeatherOverlay : Overlay
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!_entManager.TryGetComponent<WeatherComponent>(_mapManager.GetMapEntityId(args.MapId), out var weather) ||
|
if (!_entManager.TryGetComponent<WeatherComponent>(_mapManager.GetMapEntityId(args.MapId), out var weather) ||
|
||||||
weather.Weather == null)
|
weather.Weather.Count == 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -58,15 +58,19 @@ public sealed class WeatherOverlay : Overlay
|
|||||||
{
|
{
|
||||||
var mapUid = _mapManager.GetMapEntityId(args.MapId);
|
var mapUid = _mapManager.GetMapEntityId(args.MapId);
|
||||||
|
|
||||||
if (!_entManager.TryGetComponent<WeatherComponent>(mapUid, out var weather) ||
|
if (!_entManager.TryGetComponent<WeatherComponent>(mapUid, out var comp))
|
||||||
weather.Weather == null ||
|
|
||||||
!_protoManager.TryIndex<WeatherPrototype>(weather.Weather, out var weatherProto))
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var alpha = _weather.GetPercent(weather, mapUid, weatherProto);
|
foreach (var (proto, weather) in comp.Weather)
|
||||||
DrawWorld(args, weatherProto, alpha);
|
{
|
||||||
|
if (!_protoManager.TryIndex<WeatherPrototype>(proto, out var weatherProto))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var alpha = _weather.GetPercent(weather, mapUid);
|
||||||
|
DrawWorld(args, weatherProto, alpha);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawWorld(in OverlayDrawArgs args, WeatherPrototype weatherProto, float alpha)
|
private void DrawWorld(in OverlayDrawArgs args, WeatherPrototype weatherProto, float alpha)
|
||||||
|
|||||||
@@ -17,14 +17,9 @@ public sealed class WeatherSystem : SharedWeatherSystem
|
|||||||
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
||||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||||
[Dependency] private readonly AudioSystem _audio = default!;
|
[Dependency] private readonly AudioSystem _audio = default!;
|
||||||
[Dependency] private readonly MetaDataSystem _metadata = default!;
|
|
||||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
|
|
||||||
// Consistency isn't really important, just want to avoid sharp changes and there's no way to lerp on engine nicely atm.
|
|
||||||
private float _lastAlpha;
|
|
||||||
private float _lastOcclusion;
|
|
||||||
|
|
||||||
private const float OcclusionLerpRate = 4f;
|
private const float OcclusionLerpRate = 4f;
|
||||||
private const float AlphaLerpRate = 4f;
|
private const float AlphaLerpRate = 4f;
|
||||||
|
|
||||||
@@ -41,9 +36,9 @@ public sealed class WeatherSystem : SharedWeatherSystem
|
|||||||
_overlayManager.RemoveOverlay<WeatherOverlay>();
|
_overlayManager.RemoveOverlay<WeatherOverlay>();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Run(EntityUid uid, WeatherComponent component, WeatherPrototype weather, WeatherState state, float frameTime)
|
protected override void Run(EntityUid uid, WeatherData weather, WeatherPrototype weatherProto, float frameTime)
|
||||||
{
|
{
|
||||||
base.Run(uid, component, weather, state, frameTime);
|
base.Run(uid, weather, weatherProto, frameTime);
|
||||||
|
|
||||||
var ent = _playerManager.LocalPlayer?.ControlledEntity;
|
var ent = _playerManager.LocalPlayer?.ControlledEntity;
|
||||||
|
|
||||||
@@ -56,21 +51,21 @@ public sealed class WeatherSystem : SharedWeatherSystem
|
|||||||
// Maybe have the viewports manage this?
|
// Maybe have the viewports manage this?
|
||||||
if (mapUid == null || entXform.MapUid != mapUid)
|
if (mapUid == null || entXform.MapUid != mapUid)
|
||||||
{
|
{
|
||||||
_lastOcclusion = 0f;
|
weather.LastOcclusion = 0f;
|
||||||
_lastAlpha = 0f;
|
weather.LastAlpha = 0f;
|
||||||
component.Stream?.Stop();
|
weather.Stream?.Stop();
|
||||||
component.Stream = null;
|
weather.Stream = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Timing.IsFirstTimePredicted || weather.Sound == null)
|
if (!Timing.IsFirstTimePredicted || weatherProto.Sound == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
component.Stream ??= _audio.PlayGlobal(weather.Sound, Filter.Local(), true);
|
weather.Stream ??= _audio.PlayGlobal(weatherProto.Sound, Filter.Local(), true);
|
||||||
var volumeMod = MathF.Pow(10, weather.Sound.Params.Volume / 10f);
|
var volumeMod = MathF.Pow(10, weatherProto.Sound.Params.Volume / 10f);
|
||||||
|
|
||||||
var stream = (AudioSystem.PlayingStream) component.Stream!;
|
var stream = (AudioSystem.PlayingStream) weather.Stream!;
|
||||||
var alpha = GetPercent(component, mapUid.Value, weather);
|
var alpha = weather.LastAlpha;
|
||||||
alpha = MathF.Pow(alpha, 2f) * volumeMod;
|
alpha = MathF.Pow(alpha, 2f) * volumeMod;
|
||||||
// TODO: Lerp this occlusion.
|
// TODO: Lerp this occlusion.
|
||||||
var occlusion = 0f;
|
var occlusion = 0f;
|
||||||
@@ -138,81 +133,75 @@ public sealed class WeatherSystem : SharedWeatherSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MathHelper.CloseTo(_lastOcclusion, occlusion, 0.01f))
|
if (MathHelper.CloseTo(weather.LastOcclusion, occlusion, 0.01f))
|
||||||
_lastOcclusion = occlusion;
|
weather.LastOcclusion = occlusion;
|
||||||
else
|
else
|
||||||
_lastOcclusion += (occlusion - _lastOcclusion) * OcclusionLerpRate * frameTime;
|
weather.LastOcclusion += (occlusion - weather.LastOcclusion) * OcclusionLerpRate * frameTime;
|
||||||
|
|
||||||
if (MathHelper.CloseTo(_lastAlpha, alpha, 0.01f))
|
if (MathHelper.CloseTo(weather.LastAlpha, alpha, 0.01f))
|
||||||
_lastAlpha = alpha;
|
weather.LastAlpha = alpha;
|
||||||
else
|
else
|
||||||
_lastAlpha += (alpha - _lastAlpha) * AlphaLerpRate * frameTime;
|
weather.LastAlpha += (alpha - weather.LastAlpha) * AlphaLerpRate * frameTime;
|
||||||
|
|
||||||
// Full volume if not on grid
|
// Full volume if not on grid
|
||||||
stream.Source.SetVolumeDirect(_lastAlpha);
|
stream.Source.SetVolumeDirect(weather.LastAlpha);
|
||||||
stream.Source.SetOcclusion(_lastOcclusion);
|
stream.Source.SetOcclusion(weather.LastOcclusion);
|
||||||
}
|
}
|
||||||
|
|
||||||
public float GetPercent(WeatherComponent component, EntityUid mapUid, WeatherPrototype weatherProto)
|
protected override void EndWeather(EntityUid uid, WeatherComponent component, string proto)
|
||||||
{
|
{
|
||||||
var pauseTime = _metadata.GetPauseTime(mapUid);
|
base.EndWeather(uid, component, proto);
|
||||||
var elapsed = Timing.CurTime - (component.StartTime + pauseTime);
|
|
||||||
var duration = component.Duration;
|
|
||||||
var remaining = duration - elapsed;
|
|
||||||
float alpha;
|
|
||||||
|
|
||||||
if (elapsed < weatherProto.StartupTime)
|
if (!component.Weather.TryGetValue(proto, out var weather))
|
||||||
{
|
return;
|
||||||
alpha = (float) (elapsed / weatherProto.StartupTime);
|
|
||||||
}
|
|
||||||
else if (remaining < weatherProto.ShutdownTime)
|
|
||||||
{
|
|
||||||
alpha = (float) (remaining / weatherProto.ShutdownTime);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
alpha = 1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
return alpha;
|
weather.LastAlpha = 0f;
|
||||||
|
weather.LastOcclusion = 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool SetState(EntityUid uid, WeatherComponent component, WeatherState state, WeatherPrototype prototype)
|
protected override bool SetState(WeatherState state, WeatherComponent comp, WeatherData weather, WeatherPrototype weatherProto)
|
||||||
{
|
{
|
||||||
if (!base.SetState(uid, component, state, prototype))
|
if (!base.SetState(state, comp, weather, weatherProto))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!Timing.IsFirstTimePredicted)
|
if (!Timing.IsFirstTimePredicted)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// TODO: Fades
|
// TODO: Fades (properly)
|
||||||
component.Stream?.Stop();
|
weather.Stream?.Stop();
|
||||||
component.Stream = null;
|
weather.Stream = null;
|
||||||
component.Stream = _audio.PlayGlobal(prototype.Sound, Filter.Local(), true);
|
weather.Stream = _audio.PlayGlobal(weatherProto.Sound, Filter.Local(), true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void EndWeather(WeatherComponent component)
|
|
||||||
{
|
|
||||||
_lastOcclusion = 0f;
|
|
||||||
_lastAlpha = 0f;
|
|
||||||
base.EndWeather(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnWeatherHandleState(EntityUid uid, WeatherComponent component, ref ComponentHandleState args)
|
private void OnWeatherHandleState(EntityUid uid, WeatherComponent component, ref ComponentHandleState args)
|
||||||
{
|
{
|
||||||
if (args.Current is not WeatherComponentState state)
|
if (args.Current is not WeatherComponentState state)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (component.Weather != state.Weather || !component.EndTime.Equals(state.EndTime) || !component.StartTime.Equals(state.StartTime))
|
foreach (var (proto, weather) in component.Weather)
|
||||||
{
|
{
|
||||||
EndWeather(component);
|
// End existing one
|
||||||
|
if (!state.Weather.TryGetValue(proto, out var stateData))
|
||||||
|
{
|
||||||
|
EndWeather(uid, component, proto);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (state.Weather != null)
|
// Data update?
|
||||||
StartWeather(component, ProtoMan.Index<WeatherPrototype>(state.Weather));
|
weather.StartTime = stateData.StartTime;
|
||||||
|
weather.EndTime = stateData.EndTime;
|
||||||
|
weather.State = stateData.State;
|
||||||
}
|
}
|
||||||
|
|
||||||
component.EndTime = state.EndTime;
|
foreach (var (proto, weather) in state.Weather)
|
||||||
component.StartTime = state.StartTime;
|
{
|
||||||
|
if (component.Weather.ContainsKey(proto))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// New weather
|
||||||
|
StartWeather(component, ProtoMan.Index<WeatherPrototype>(proto), weather.EndTime);
|
||||||
|
weather.LastAlpha = 0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,19 +26,15 @@ public sealed class WeatherSystem : SharedWeatherSystem
|
|||||||
|
|
||||||
private void OnWeatherGetState(EntityUid uid, WeatherComponent component, ref ComponentGetState args)
|
private void OnWeatherGetState(EntityUid uid, WeatherComponent component, ref ComponentGetState args)
|
||||||
{
|
{
|
||||||
args.State = new WeatherComponentState()
|
args.State = new WeatherComponentState(component.Weather);
|
||||||
{
|
|
||||||
Weather = component.Weather,
|
|
||||||
EndTime = component.EndTime,
|
|
||||||
StartTime = component.StartTime,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[AdminCommand(AdminFlags.Fun)]
|
[AdminCommand(AdminFlags.Fun)]
|
||||||
private void WeatherTwo(IConsoleShell shell, string argstr, string[] args)
|
private void WeatherTwo(IConsoleShell shell, string argstr, string[] args)
|
||||||
{
|
{
|
||||||
if (args.Length != 2)
|
if (args.Length < 2)
|
||||||
{
|
{
|
||||||
|
shell.WriteError($"A");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,13 +50,36 @@ public sealed class WeatherSystem : SharedWeatherSystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimeSpan? endTime = null;
|
||||||
|
|
||||||
|
if (args.Length == 3)
|
||||||
|
{
|
||||||
|
if (int.TryParse(args[2], out var durationInt))
|
||||||
|
{
|
||||||
|
var curTime = Timing.CurTime;
|
||||||
|
var maxTime = TimeSpan.MaxValue;
|
||||||
|
|
||||||
|
// If it's already running then just fade out with how much time we're into the weather.
|
||||||
|
if (TryComp<WeatherComponent>(MapManager.GetMapEntityId(mapId), out var weatherComp) &&
|
||||||
|
weatherComp.Weather.TryGetValue(args[1], out var existing))
|
||||||
|
{
|
||||||
|
maxTime = curTime - existing.StartTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
endTime = curTime + TimeSpan.FromSeconds(durationInt);
|
||||||
|
|
||||||
|
if (endTime > maxTime)
|
||||||
|
endTime = maxTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (args[1].Equals("null"))
|
if (args[1].Equals("null"))
|
||||||
{
|
{
|
||||||
SetWeather(mapId, null);
|
SetWeather(mapId, null, endTime);
|
||||||
}
|
}
|
||||||
else if (ProtoMan.TryIndex<WeatherPrototype>(args[1], out var weatherProto))
|
else if (ProtoMan.TryIndex<WeatherPrototype>(args[1], out var weatherProto))
|
||||||
{
|
{
|
||||||
SetWeather(mapId, weatherProto);
|
SetWeather(mapId, weatherProto, endTime);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,10 +3,8 @@ using Robust.Shared.Map;
|
|||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Random;
|
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
using Robust.Shared.Utility;
|
|
||||||
|
|
||||||
namespace Content.Shared.Weather;
|
namespace Content.Shared.Weather;
|
||||||
|
|
||||||
@@ -15,8 +13,8 @@ public abstract class SharedWeatherSystem : EntitySystem
|
|||||||
[Dependency] protected readonly IGameTiming Timing = default!;
|
[Dependency] protected readonly IGameTiming Timing = default!;
|
||||||
[Dependency] protected readonly IMapManager MapManager = default!;
|
[Dependency] protected readonly IMapManager MapManager = default!;
|
||||||
[Dependency] protected readonly IPrototypeManager ProtoMan = default!;
|
[Dependency] protected readonly IPrototypeManager ProtoMan = default!;
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
|
||||||
[Dependency] private readonly ITileDefinitionManager _tileDefManager = default!;
|
[Dependency] private readonly ITileDefinitionManager _tileDefManager = default!;
|
||||||
|
[Dependency] private readonly MetaDataSystem _metadata = default!;
|
||||||
|
|
||||||
protected ISawmill Sawmill = default!;
|
protected ISawmill Sawmill = default!;
|
||||||
|
|
||||||
@@ -29,7 +27,13 @@ public abstract class SharedWeatherSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnWeatherUnpaused(EntityUid uid, WeatherComponent component, ref EntityUnpausedEvent args)
|
private void OnWeatherUnpaused(EntityUid uid, WeatherComponent component, ref EntityUnpausedEvent args)
|
||||||
{
|
{
|
||||||
component.EndTime += args.PausedTime;
|
foreach (var weather in component.Weather.Values)
|
||||||
|
{
|
||||||
|
weather.StartTime += args.PausedTime;
|
||||||
|
|
||||||
|
if (weather.EndTime != null)
|
||||||
|
weather.EndTime = weather.EndTime.Value + args.PausedTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool CanWeatherAffect(MapGridComponent grid, TileRef tileRef, EntityQuery<PhysicsComponent> bodyQuery)
|
public bool CanWeatherAffect(MapGridComponent grid, TileRef tileRef, EntityQuery<PhysicsComponent> bodyQuery)
|
||||||
@@ -56,6 +60,31 @@ public abstract class SharedWeatherSystem : EntitySystem
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float GetPercent(WeatherData component, EntityUid mapUid)
|
||||||
|
{
|
||||||
|
var pauseTime = _metadata.GetPauseTime(mapUid);
|
||||||
|
var elapsed = Timing.CurTime - (component.StartTime + pauseTime);
|
||||||
|
var duration = component.Duration;
|
||||||
|
var remaining = duration - elapsed;
|
||||||
|
float alpha;
|
||||||
|
|
||||||
|
if (remaining < WeatherComponent.ShutdownTime)
|
||||||
|
{
|
||||||
|
alpha = (float) (remaining / WeatherComponent.ShutdownTime);
|
||||||
|
}
|
||||||
|
else if (elapsed < WeatherComponent.StartupTime)
|
||||||
|
{
|
||||||
|
alpha = (float) (elapsed / WeatherComponent.StartupTime);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
alpha = 1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
base.Update(frameTime);
|
base.Update(frameTime);
|
||||||
@@ -65,103 +94,141 @@ public abstract class SharedWeatherSystem : EntitySystem
|
|||||||
|
|
||||||
var curTime = Timing.CurTime;
|
var curTime = Timing.CurTime;
|
||||||
|
|
||||||
foreach (var (comp, metadata) in EntityQuery<WeatherComponent, MetaDataComponent>())
|
foreach (var comp in EntityQuery<WeatherComponent>())
|
||||||
{
|
{
|
||||||
if (comp.Weather == null)
|
if (comp.Weather.Count == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var uid = comp.Owner;
|
var uid = comp.Owner;
|
||||||
var endTime = comp.EndTime;
|
|
||||||
|
|
||||||
// Ended
|
foreach (var (proto, weather) in comp.Weather)
|
||||||
if (endTime < curTime)
|
|
||||||
{
|
{
|
||||||
EndWeather(comp);
|
var endTime = weather.EndTime;
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Admin messed up or the likes.
|
// Ended
|
||||||
if (!ProtoMan.TryIndex<WeatherPrototype>(comp.Weather, out var weatherProto))
|
if (endTime != null && endTime < curTime)
|
||||||
{
|
|
||||||
Sawmill.Error($"Unable to find weather prototype for {comp.Weather}, ending!");
|
|
||||||
EndWeather(comp);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var remainingTime = endTime - curTime;
|
|
||||||
|
|
||||||
// Shutting down
|
|
||||||
if (remainingTime < weatherProto.ShutdownTime)
|
|
||||||
{
|
|
||||||
SetState(uid, comp, WeatherState.Ending, weatherProto);
|
|
||||||
}
|
|
||||||
// Starting up
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var startTime = comp.StartTime;
|
|
||||||
var elapsed = Timing.CurTime - startTime;
|
|
||||||
|
|
||||||
if (elapsed < weatherProto.StartupTime)
|
|
||||||
{
|
{
|
||||||
SetState(uid, comp, WeatherState.Starting, weatherProto);
|
EndWeather(uid, comp, proto);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Run whatever code we need.
|
var remainingTime = endTime - curTime;
|
||||||
Run(uid, comp, weatherProto, comp.State, frameTime);
|
|
||||||
|
// Admin messed up or the likes.
|
||||||
|
if (!ProtoMan.TryIndex<WeatherPrototype>(proto, out var weatherProto))
|
||||||
|
{
|
||||||
|
Sawmill.Error($"Unable to find weather prototype for {comp.Weather}, ending!");
|
||||||
|
EndWeather(uid, comp, proto);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutting down
|
||||||
|
if (endTime != null && remainingTime < WeatherComponent.ShutdownTime)
|
||||||
|
{
|
||||||
|
SetState(WeatherState.Ending, comp, weather, weatherProto);
|
||||||
|
}
|
||||||
|
// Starting up
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var startTime = weather.StartTime;
|
||||||
|
var elapsed = Timing.CurTime - startTime;
|
||||||
|
|
||||||
|
if (elapsed < WeatherComponent.StartupTime)
|
||||||
|
{
|
||||||
|
SetState(WeatherState.Starting, comp, weather, weatherProto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run whatever code we need.
|
||||||
|
Run(uid, weather, weatherProto, frameTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetWeather(MapId mapId, WeatherPrototype? weather)
|
/// <summary>
|
||||||
|
/// Shuts down all existing weather and starts the new one if applicable.
|
||||||
|
/// </summary>
|
||||||
|
public void SetWeather(MapId mapId, WeatherPrototype? proto, TimeSpan? endTime)
|
||||||
{
|
{
|
||||||
var weatherComp = EnsureComp<WeatherComponent>(MapManager.GetMapEntityId(mapId));
|
var weatherComp = EnsureComp<WeatherComponent>(MapManager.GetMapEntityId(mapId));
|
||||||
EndWeather(weatherComp);
|
|
||||||
|
|
||||||
if (weather != null)
|
foreach (var (eProto, weather) in weatherComp.Weather)
|
||||||
StartWeather(weatherComp, weather);
|
{
|
||||||
|
// Reset cooldown if it's an existing one.
|
||||||
|
if (eProto == proto?.ID)
|
||||||
|
{
|
||||||
|
weather.EndTime = endTime;
|
||||||
|
|
||||||
|
if (weather.State == WeatherState.Ending)
|
||||||
|
weather.State = WeatherState.Running;
|
||||||
|
|
||||||
|
Dirty(weatherComp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Speedrun
|
||||||
|
var end = Timing.CurTime + WeatherComponent.ShutdownTime;
|
||||||
|
|
||||||
|
if (weather.EndTime == null || weather.EndTime > end)
|
||||||
|
{
|
||||||
|
weather.EndTime = end;
|
||||||
|
Dirty(weatherComp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (proto != null)
|
||||||
|
StartWeather(weatherComp, proto, endTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Run every tick when the weather is running.
|
/// Run every tick when the weather is running.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual void Run(EntityUid uid, WeatherComponent component, WeatherPrototype weather, WeatherState state, float frameTime) {}
|
protected virtual void Run(EntityUid uid, WeatherData weather, WeatherPrototype weatherProto, float frameTime) {}
|
||||||
|
|
||||||
protected void StartWeather(WeatherComponent component, WeatherPrototype weather)
|
protected void StartWeather(WeatherComponent component, WeatherPrototype weather, TimeSpan? endTime)
|
||||||
{
|
{
|
||||||
component.Weather = weather.ID;
|
if (component.Weather.ContainsKey(weather.ID))
|
||||||
// TODO: ENGINE PR
|
return;
|
||||||
var duration = _random.NextDouble(weather.DurationMinimum.TotalSeconds, weather.DurationMaximum.TotalSeconds);
|
|
||||||
component.EndTime = Timing.CurTime + TimeSpan.FromSeconds(duration);
|
var data = new WeatherData()
|
||||||
component.StartTime = Timing.CurTime;
|
{
|
||||||
DebugTools.Assert(component.State == WeatherState.Invalid);
|
StartTime = Timing.CurTime,
|
||||||
|
EndTime = endTime,
|
||||||
|
};
|
||||||
|
|
||||||
|
component.Weather.Add(weather.ID, data);
|
||||||
Dirty(component);
|
Dirty(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void EndWeather(WeatherComponent component)
|
protected virtual void EndWeather(EntityUid uid, WeatherComponent component, string proto)
|
||||||
{
|
{
|
||||||
component.Stream?.Stop();
|
if (!component.Weather.TryGetValue(proto, out var data))
|
||||||
component.Stream = null;
|
return;
|
||||||
component.Weather = null;
|
|
||||||
component.StartTime = TimeSpan.Zero;
|
data.Stream?.Stop();
|
||||||
component.EndTime = TimeSpan.Zero;
|
data.Stream = null;
|
||||||
component.State = WeatherState.Invalid;
|
component.Weather.Remove(proto);
|
||||||
Dirty(component);
|
Dirty(component);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual bool SetState(EntityUid uid, WeatherComponent component, WeatherState state, WeatherPrototype prototype)
|
protected virtual bool SetState(WeatherState state, WeatherComponent component, WeatherData weather, WeatherPrototype weatherProto)
|
||||||
{
|
{
|
||||||
if (component.State.Equals(state))
|
if (weather.State.Equals(state))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
component.State = state;
|
weather.State = state;
|
||||||
|
Dirty(component);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
protected sealed class WeatherComponentState : ComponentState
|
protected sealed class WeatherComponentState : ComponentState
|
||||||
{
|
{
|
||||||
public string? Weather;
|
public Dictionary<string, WeatherData> Weather;
|
||||||
public TimeSpan StartTime;
|
|
||||||
public TimeSpan EndTime;
|
public WeatherComponentState(Dictionary<string, WeatherData> weather)
|
||||||
|
{
|
||||||
|
Weather = weather;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
|
||||||
|
|
||||||
namespace Content.Shared.Weather;
|
namespace Content.Shared.Weather;
|
||||||
|
|
||||||
@@ -7,31 +10,45 @@ namespace Content.Shared.Weather;
|
|||||||
public sealed class WeatherComponent : Component
|
public sealed class WeatherComponent : Component
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Currently running weather.
|
/// Currently running weathers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables, DataField("weather")]
|
[ViewVariables, DataField("weather", customTypeSerializer:typeof(PrototypeIdDictionarySerializer<WeatherData, WeatherPrototype>))]
|
||||||
public string? Weather;
|
public Dictionary<string, WeatherData> Weather = new();
|
||||||
|
|
||||||
// now
|
public static readonly TimeSpan StartupTime = TimeSpan.FromSeconds(15);
|
||||||
|
public static readonly TimeSpan ShutdownTime = TimeSpan.FromSeconds(15);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataDefinition, Serializable, NetSerializable]
|
||||||
|
public sealed class WeatherData
|
||||||
|
{
|
||||||
|
// Client audio stream.
|
||||||
|
[NonSerialized]
|
||||||
public IPlayingAudioStream? Stream;
|
public IPlayingAudioStream? Stream;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When the weather started.
|
/// When the weather started if relevant.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables, DataField("startTime")]
|
[ViewVariables, DataField("startTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
public TimeSpan StartTime = TimeSpan.Zero;
|
public TimeSpan StartTime = TimeSpan.Zero;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When the applied weather will end.
|
/// When the applied weather will end.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("endTime")]
|
[ViewVariables(VVAccess.ReadWrite), DataField("endTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||||
public TimeSpan EndTime = TimeSpan.Zero;
|
public TimeSpan? EndTime;
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public TimeSpan Duration => EndTime - StartTime;
|
public TimeSpan Duration => EndTime == null ? TimeSpan.MaxValue : EndTime.Value - StartTime;
|
||||||
|
|
||||||
[ViewVariables]
|
[DataField("state")]
|
||||||
public WeatherState State = WeatherState.Invalid;
|
public WeatherState State = WeatherState.Invalid;
|
||||||
|
|
||||||
|
[ViewVariables, NonSerialized]
|
||||||
|
public float LastAlpha;
|
||||||
|
|
||||||
|
[ViewVariables, NonSerialized]
|
||||||
|
public float LastOcclusion;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum WeatherState : byte
|
public enum WeatherState : byte
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
using Content.Shared.Maps;
|
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Prototypes;
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
|
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Shared.Weather;
|
namespace Content.Shared.Weather;
|
||||||
@@ -11,24 +9,6 @@ public sealed class WeatherPrototype : IPrototype
|
|||||||
{
|
{
|
||||||
[IdDataField] public string ID { get; } = default!;
|
[IdDataField] public string ID { get; } = default!;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Minimum duration for the weather.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public TimeSpan DurationMinimum = TimeSpan.FromSeconds(120);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Maximum duration for the weather.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public TimeSpan DurationMaximum = TimeSpan.FromSeconds(300);
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("startupTime")]
|
|
||||||
public TimeSpan StartupTime = TimeSpan.FromSeconds(30);
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("endTime")]
|
|
||||||
public TimeSpan ShutdownTime = TimeSpan.FromSeconds(30);
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("sprite", required: true)]
|
[ViewVariables(VVAccess.ReadWrite), DataField("sprite", required: true)]
|
||||||
public SpriteSpecifier Sprite = default!;
|
public SpriteSpecifier Sprite = default!;
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
- Space
|
- Space
|
||||||
isSubfloor: true
|
isSubfloor: true
|
||||||
canWirecutter: true
|
canWirecutter: true
|
||||||
|
weather: true
|
||||||
footstepSounds:
|
footstepSounds:
|
||||||
collection: FootstepPlating
|
collection: FootstepPlating
|
||||||
friction: 0.5
|
friction: 0.5
|
||||||
|
|||||||
Reference in New Issue
Block a user