Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gas tile overlay rejig #9619

Merged
merged 9 commits into from
Jul 25, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
244 changes: 25 additions & 219 deletions Content.Client/Atmos/EntitySystems/GasTileOverlaySystem.cs
Original file line number Diff line number Diff line change
@@ -1,264 +1,70 @@
using Content.Client.Atmos.Overlays;
using Content.Shared.Atmos;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.GameTicking;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Client.Utility;
using Robust.Shared.Utility;

namespace Content.Client.Atmos.EntitySystems
{
[UsedImplicitly]
internal sealed class GasTileOverlaySystem : SharedGasTileOverlaySystem
public sealed class GasTileOverlaySystem : SharedGasTileOverlaySystem
{
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly IOverlayManager _overlayMan = default!;
[Dependency] private readonly SpriteSystem _spriteSys = default!;

// Gas overlays
public readonly float[] Timer = new float[Atmospherics.TotalNumberOfGases];
public readonly float[][] FrameDelays = new float[Atmospherics.TotalNumberOfGases][];
public readonly int[] FrameCounter = new int[Atmospherics.TotalNumberOfGases];
public readonly Texture[][] Frames = new Texture[Atmospherics.TotalNumberOfGases][];

// Fire overlays
public const int FireStates = 3;
public const string FireRsiPath = "/Textures/Effects/fire.rsi";

public readonly float[] FireTimer = new float[FireStates];
public readonly float[][] FireFrameDelays = new float[FireStates][];
public readonly int[] FireFrameCounter = new int[FireStates];
public readonly Texture[][] FireFrames = new Texture[FireStates][];

private readonly Dictionary<EntityUid, Dictionary<Vector2i, GasOverlayChunk>> _tileData =
new();

public const int GasOverlayZIndex = 1;
private GasTileOverlay _overlay = default!;

public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent<GasOverlayMessage>(HandleGasOverlayMessage);
SubscribeNetworkEvent<GasOverlayUpdateEvent>(HandleGasOverlayUpdate);
SubscribeLocalEvent<GridRemovalEvent>(OnGridRemoved);

for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
{
var overlay = _atmosphereSystem.GetOverlay(i);
switch (overlay)
{
case SpriteSpecifier.Rsi animated:
var rsi = _resourceCache.GetResource<RSIResource>(animated.RsiPath).RSI;
var stateId = animated.RsiState;

if(!rsi.TryGetState(stateId, out var state)) continue;

Frames[i] = state.GetFrames(RSI.State.Direction.South);
FrameDelays[i] = state.GetDelays();
FrameCounter[i] = 0;
break;
case SpriteSpecifier.Texture texture:
Frames[i] = new[] {texture.Frame0()};
FrameDelays[i] = Array.Empty<float>();
break;
case null:
Frames[i] = Array.Empty<Texture>();
FrameDelays[i] = Array.Empty<float>();
break;
}
}

var fire = _resourceCache.GetResource<RSIResource>(FireRsiPath).RSI;

for (var i = 0; i < FireStates; i++)
{
if (!fire.TryGetState((i+1).ToString(), out var state))
throw new ArgumentOutOfRangeException($"Fire RSI doesn't have state \"{i}\"!");

FireFrames[i] = state.GetFrames(RSI.State.Direction.South);
FireFrameDelays[i] = state.GetDelays();
FireFrameCounter[i] = 0;
}

var overlayManager = IoCManager.Resolve<IOverlayManager>();
overlayManager.AddOverlay(new GasTileOverlay());
overlayManager.AddOverlay(new FireTileOverlay());
_overlay = new GasTileOverlay(this, _resourceCache, ProtoMan, _spriteSys);
_overlayMan.AddOverlay(_overlay);
}

private void HandleGasOverlayMessage(GasOverlayMessage message)
public override void Reset(RoundRestartCleanupEvent ev)
{
foreach (var (indices, data) in message.OverlayData)
{
var chunk = GetOrCreateChunk(message.GridId, indices);
chunk.Update(data, indices);
}
}

// Slightly different to the server-side system version
private GasOverlayChunk GetOrCreateChunk(EntityUid gridId, Vector2i indices)
{
if (!_tileData.TryGetValue(gridId, out var chunks))
{
chunks = new Dictionary<Vector2i, GasOverlayChunk>();
_tileData[gridId] = chunks;
}

var chunkIndices = GetGasChunkIndices(indices);

if (!chunks.TryGetValue(chunkIndices, out var chunk))
{
chunk = new GasOverlayChunk(gridId, chunkIndices);
chunks[chunkIndices] = chunk;
}

return chunk;
_overlay.TileData.Clear();
}

public override void Shutdown()
{
base.Shutdown();
var overlayManager = IoCManager.Resolve<IOverlayManager>();
overlayManager.RemoveOverlay<GasTileOverlay>();
overlayManager.RemoveOverlay<FireTileOverlay>();
}

private void OnGridRemoved(GridRemovalEvent ev)
{
_tileData.Remove(ev.EntityUid);
}

public bool HasData(EntityUid gridId)
{
return _tileData.ContainsKey(gridId);
}

public GasOverlayEnumerator GetOverlays(EntityUid gridIndex, Vector2i indices)
{
if (!_tileData.TryGetValue(gridIndex, out var chunks))
return default;

var chunkIndex = GetGasChunkIndices(indices);
if (!chunks.TryGetValue(chunkIndex, out var chunk))
return default;

var overlays = chunk.GetData(indices);

return new GasOverlayEnumerator(overlays, this);
}

public FireOverlayEnumerator GetFireOverlays(EntityUid gridIndex, Vector2i indices)
{
if (!_tileData.TryGetValue(gridIndex, out var chunks))
return default;

var chunkIndex = GetGasChunkIndices(indices);
if (!chunks.TryGetValue(chunkIndex, out var chunk))
return default;

var overlays = chunk.GetData(indices);

return new FireOverlayEnumerator(overlays, this);
_overlayMan.RemoveOverlay(_overlay);
}

public override void FrameUpdate(float frameTime)
private void HandleGasOverlayUpdate(GasOverlayUpdateEvent ev)
{
base.FrameUpdate(frameTime);

for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
foreach (var (grid, removedIndicies) in ev.RemovedChunks)
{
var delays = FrameDelays[i];
if (delays.Length == 0) continue;

var frameCount = FrameCounter[i];
Timer[i] += frameTime;
var time = delays[frameCount];

if (Timer[i] < time)
if (!_overlay.TileData.TryGetValue(grid, out var chunks))
continue;

Timer[i] -= time;
FrameCounter[i] = (frameCount + 1) % Frames[i].Length;
}

for (var i = 0; i < FireStates; i++)
{
var delays = FireFrameDelays[i];
if (delays.Length == 0) continue;

var frameCount = FireFrameCounter[i];
FireTimer[i] += frameTime;
var time = delays[frameCount];

if (FireTimer[i] < time) continue;
FireTimer[i] -= time;
FireFrameCounter[i] = (frameCount + 1) % FireFrames[i].Length;
}
}

public struct GasOverlayEnumerator
{
private readonly GasTileOverlaySystem _system;
private readonly GasData[]? _data;
// TODO: Take Fire Temperature into account, when we code fire color

private readonly int _length; // We cache the length so we can avoid a pointer dereference, for speed. Brrr.
private int _current;

public GasOverlayEnumerator(in GasOverlayData data, GasTileOverlaySystem system)
{
// Gas can't be null, as the caller to this constructor already ensured it wasn't.
_data = data.Gas;

_system = system;

_length = _data?.Length ?? 0;
_current = 0;
foreach (var index in removedIndicies)
{
chunks.Remove(index);
}
}

public bool MoveNext(out (Texture Texture, Color Color) overlay)
foreach (var (grid, gridData) in ev.UpdatedChunks)
{
if (_current < _length)
var chunks = _overlay.TileData.GetOrNew(grid);
foreach (var chunkData in gridData)
{
// Data can't be null here unless length/current are incorrect
var gas = _data![_current++];
var frames = _system.Frames[gas.Index];
overlay = (frames[_system.FrameCounter[gas.Index]], Color.White.WithAlpha(gas.Opacity));
return true;
chunks[chunkData.Index] = chunkData;
}

overlay = default;
return false;
}
}

public struct FireOverlayEnumerator
private void OnGridRemoved(GridRemovalEvent ev)
{
private readonly GasTileOverlaySystem _system;
private byte _fireState;
// TODO: Take Fire Temperature into account, when we code fire color

public FireOverlayEnumerator(in GasOverlayData data, GasTileOverlaySystem system)
{
_fireState = data.FireState;
_system = system;
}
public bool MoveNext(out (Texture Texture, Color Color) overlay)
{

if (_fireState != 0)
{
var state = _fireState - 1;
var frames = _system.FireFrames[state];
// TODO ATMOS Set color depending on temperature
overlay = (frames[_system.FireFrameCounter[state]], Color.White);

// Setting this to zero so we don't get stuck in an infinite loop.
_fireState = 0;
return true;
}

overlay = default;
return false;
}
_overlay.TileData.Remove(ev.EntityUid);
}
}
}
57 changes: 0 additions & 57 deletions Content.Client/Atmos/Overlays/FireTileOverlay.cs

This file was deleted.

Loading