Files
Delta-v/Content.Client/Weather/WeatherSystem.cs
T
4186c8caa2 Weather entities (#41427)
* weather status effect entities

* proto port + fixes

* Update weather.yml

* fix visuals

* Update SharedWeatherSystem.cs

* Update SharedWeatherSystem.cs

* Update WeatherSystem.cs

* Thanks Slarti, thats a much better

* Update WeatherSystem.cs

* Update StencilOverlay.Weather.cs

* Update Content.Client/Overlays/StencilOverlay.Weather.cs

Co-authored-by: Pok <113675512+Pok27@users.noreply.github.com>

* Update Content.Shared/StatusEffectNew/Components/StatusEffectComponent.cs

Co-authored-by: Pok <113675512+Pok27@users.noreply.github.com>

* Update Content.Client/Overlays/StencilOverlay.Weather.cs

Co-authored-by: Pok <113675512+Pok27@users.noreply.github.com>

* Revise weather command help descriptions

Updated weather command help texts for clarity.

* Merge branch 'master' into ed-14-11-2025-weather-entities

* Tayrtahn review apply

* fixes and cleanup

---------

Co-authored-by: Pok <113675512+Pok27@users.noreply.github.com>
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
2026-06-29 12:44:02 +02:00

141 lines
5.3 KiB
C#

using System.Numerics;
using Content.Shared.Light.Components;
using Content.Shared.StatusEffectNew.Components;
using Content.Shared.Weather;
using Robust.Client.Audio;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Shared.Audio.Components;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Player;
namespace Content.Client.Weather;
public sealed class WeatherSystem : SharedWeatherSystem
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly AudioSystem _audio = default!;
[Dependency] private readonly MapSystem _mapSystem = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
private EntityQuery<AudioComponent> _audioQuery;
private EntityQuery<MapGridComponent> _gridQuery;
private EntityQuery<RoofComponent> _roofQuery;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<WeatherStatusEffectComponent, ComponentShutdown>(OnComponentShutdown);
_audioQuery = GetEntityQuery<AudioComponent>();
_gridQuery = GetEntityQuery<MapGridComponent>();
_roofQuery = GetEntityQuery<RoofComponent>();
}
private void OnComponentShutdown(Entity<WeatherStatusEffectComponent> ent, ref ComponentShutdown args)
{
ent.Comp.Stream = _audio.Stop(ent.Comp.Stream);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
if (!Timing.IsFirstTimePredicted)
return;
var player = _playerManager.LocalEntity;
if (player == null)
return;
var playerXform = Transform(player.Value);
var query = EntityQueryEnumerator<WeatherStatusEffectComponent, StatusEffectComponent>();
while (query.MoveNext(out var uid, out var weather, out var status))
{
if (weather.Sound == null || status.AppliedTo != playerXform.MapUid)
{
weather.Stream = _audio.Stop(weather.Stream);
return;
}
weather.Stream ??= _audio.PlayGlobal(weather.Sound, Filter.Local(), true)?.Entity;
if (!_audioQuery.TryComp(weather.Stream, out var audio))
return;
var occlusion = 0f;
// Work out tiles nearby to determine volume.
if (_gridQuery.TryComp(playerXform.GridUid, out var grid))
{
_roofQuery.TryComp(playerXform.GridUid, out var roofComp);
var gridId = playerXform.GridUid.Value;
// FloodFill to the nearest tile and use that for audio.
var seed = _mapSystem.GetTileRef(gridId, grid, playerXform.Coordinates);
var frontier = new Queue<TileRef>();
frontier.Enqueue(seed);
// If we don't have a nearest node don't play any sound.
EntityCoordinates? nearestNode = null;
var visited = new HashSet<Vector2i>();
while (frontier.TryDequeue(out var node))
{
if (!visited.Add(node.GridIndices))
continue;
if (!CanWeatherAffect((playerXform.GridUid.Value, grid, roofComp), node))
{
// Add neighbors
// TODO: Ideally we pick some deterministically random direction and use that
// We can't just do that naively here because it will flicker between nearby tiles.
for (var x = -1; x <= 1; x++)
{
for (var y = -1; y <= 1; y++)
{
if (Math.Abs(x) == 1 && Math.Abs(y) == 1 ||
x == 0 && y == 0 ||
(new Vector2(x, y) + node.GridIndices - seed.GridIndices).Length() > 3)
{
continue;
}
frontier.Enqueue(_mapSystem.GetTileRef(gridId, grid, new Vector2i(x, y) + node.GridIndices));
}
}
continue;
}
nearestNode = new EntityCoordinates(playerXform.GridUid.Value,
node.GridIndices + grid.TileSizeHalfVector);
break;
}
// Get occlusion to the targeted node if it exists, otherwise set a default occlusion.
if (nearestNode != null)
{
var entPos = _transform.GetMapCoordinates(playerXform);
var nodePosition = _transform.ToMapCoordinates(nearestNode.Value).Position;
var delta = nodePosition - entPos.Position;
var distance = delta.Length();
occlusion = _audio.GetOcclusion(entPos, delta, distance);
}
else
{
occlusion = 3f;
}
}
var alpha = GetWeatherPercent((uid, status));
alpha *= SharedAudioSystem.VolumeToGain(weather.Sound.Params.Volume);
_audio.SetGain(weather.Stream, alpha, audio);
audio.Occlusion = occlusion;
}
}
}