Roof data rework (#35388)

This commit is contained in:
metalgearsloth
2025-02-23 18:23:00 +11:00
committed by GitHub
parent cb0d2b3472
commit a457551108
12 changed files with 107 additions and 53 deletions

View File

@@ -1,10 +1,12 @@
using System.Numerics;
using Content.Shared.Light.Components;
using Content.Shared.Light.EntitySystems;
using Content.Shared.Maps;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Map.Enumerators;
using Robust.Shared.Physics;
namespace Content.Client.Light;
@@ -17,9 +19,9 @@ public sealed class RoofOverlay : Overlay
private readonly EntityLookupSystem _lookup;
private readonly SharedMapSystem _mapSystem;
private readonly SharedRoofSystem _roof = default!;
private readonly SharedTransformSystem _xformSystem;
private readonly HashSet<Entity<OccluderComponent>> _occluders = new();
private List<Entity<MapGridComponent>> _grids = new();
public override OverlaySpace Space => OverlaySpace.BeforeLighting;
@@ -33,6 +35,7 @@ public sealed class RoofOverlay : Overlay
_lookup = _entManager.System<EntityLookupSystem>();
_mapSystem = _entManager.System<SharedMapSystem>();
_roof = _entManager.System<SharedRoofSystem>();
_xformSystem = _entManager.System<SharedTransformSystem>();
ZIndex = ContentZIndex;
@@ -86,28 +89,13 @@ public sealed class RoofOverlay : Overlay
worldHandle.SetTransform(matty);
var tileEnumerator = _mapSystem.GetTilesEnumerator(grid.Owner, grid, bounds);
var roofEnt = (grid.Owner, grid.Comp, roof);
// Due to stencilling we essentially draw on unrooved tiles
while (tileEnumerator.MoveNext(out var tileRef))
{
if ((tileRef.Tile.Flags & (byte) TileFlag.Roof) == 0x0)
if (!_roof.IsRooved(roofEnt, tileRef.GridIndices))
{
// Check if the tile is occluded in which case hide it anyway.
// This is to avoid lit walls bleeding over to unlit tiles.
_occluders.Clear();
_lookup.GetLocalEntitiesIntersecting(grid.Owner, tileRef.GridIndices, _occluders);
var found = false;
foreach (var occluder in _occluders)
{
if (!occluder.Comp.Enabled)
continue;
found = true;
break;
}
if (!found)
continue;
}

View File

@@ -1,4 +1,5 @@
using System.Numerics;
using Content.Shared.Light.Components;
using Content.Shared.Weather;
using Robust.Client.Graphics;
using Robust.Shared.Map.Components;
@@ -34,11 +35,12 @@ public sealed partial class StencilOverlay
var matrix = _transform.GetWorldMatrix(grid, xformQuery);
var matty = Matrix3x2.Multiply(matrix, invMatrix);
worldHandle.SetTransform(matty);
_entManager.TryGetComponent(grid.Owner, out RoofComponent? roofComp);
foreach (var tile in _map.GetTilesIntersecting(grid.Owner, grid, worldAABB))
{
// Ignored tiles for stencil
if (_weather.CanWeatherAffect(grid.Owner, grid, tile))
if (_weather.CanWeatherAffect(grid.Owner, grid, tile, roofComp))
{
continue;
}

View File

@@ -1,4 +1,5 @@
using System.Numerics;
using Content.Shared.Light.Components;
using Content.Shared.Weather;
using Robust.Client.Audio;
using Robust.Client.GameObjects;
@@ -57,6 +58,7 @@ public sealed class WeatherSystem : SharedWeatherSystem
// Work out tiles nearby to determine volume.
if (TryComp<MapGridComponent>(entXform.GridUid, out var grid))
{
TryComp(entXform.GridUid, out RoofComponent? roofComp);
var gridId = entXform.GridUid.Value;
// FloodFill to the nearest tile and use that for audio.
var seed = _mapSystem.GetTileRef(gridId, grid, entXform.Coordinates);
@@ -71,7 +73,7 @@ public sealed class WeatherSystem : SharedWeatherSystem
if (!visited.Add(node.GridIndices))
continue;
if (!CanWeatherAffect(entXform.GridUid.Value, grid, node))
if (!CanWeatherAffect(entXform.GridUid.Value, grid, node, roofComp))
{
// Add neighbors
// TODO: Ideally we pick some deterministically random direction and use that

View File

@@ -0,0 +1,13 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Light.Components;
/// <summary>
/// Counts the tile this entity on as being rooved.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class IsRoofComponent : Component
{
[DataField, AutoNetworkedField]
public bool Enabled = true;
}

View File

@@ -3,11 +3,19 @@ using Robust.Shared.GameStates;
namespace Content.Shared.Light.Components;
/// <summary>
/// Will draw shadows over tiles flagged as roof tiles on the attached map.
/// Will draw shadows over tiles flagged as roof tiles on the attached grid.
/// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class RoofComponent : Component
{
public const int ChunkSize = 8;
[DataField, AutoNetworkedField]
public Color Color = Color.Black;
/// <summary>
/// Chunk origin and bitmask of value in chunk.
/// </summary>
[DataField, AutoNetworkedField]
public Dictionary<Vector2i, ulong> Data = new();
}

View File

@@ -10,33 +10,85 @@ namespace Content.Shared.Light.EntitySystems;
/// </summary>
public abstract class SharedRoofSystem : EntitySystem
{
[Dependency] private readonly SharedMapSystem _maps = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
private HashSet<Entity<IsRoofComponent>> _roofSet = new();
/// <summary>
/// Returns whether the specified tile is roof-occupied.
/// </summary>
/// <returns>Returns false if no data or not rooved.</returns>
public bool IsRooved(Entity<MapGridComponent, RoofComponent> grid, Vector2i index)
{
var roof = grid.Comp2;
var chunkOrigin = SharedMapSystem.GetChunkIndices(index, RoofComponent.ChunkSize);
if (roof.Data.TryGetValue(chunkOrigin, out var bitMask))
{
var chunkRelative = SharedMapSystem.GetChunkRelative(index, RoofComponent.ChunkSize);
var bitFlag = (ulong) 1 << (chunkRelative.X + chunkRelative.Y * RoofComponent.ChunkSize);
var isRoof = (bitMask & bitFlag) == bitFlag;
// Early out, otherwise check for components on tile.
if (isRoof)
return true;
}
_roofSet.Clear();
_lookup.GetLocalEntitiesIntersecting(grid.Owner, index, _roofSet);
foreach (var isRoofEnt in _roofSet)
{
if (!isRoofEnt.Comp.Enabled)
continue;
return true;
}
return false;
}
public void SetRoof(Entity<MapGridComponent?, RoofComponent?> grid, Vector2i index, bool value)
{
if (!Resolve(grid, ref grid.Comp1, ref grid.Comp2, false))
return;
if (!_maps.TryGetTile(grid.Comp1, index, out var tile))
var chunkOrigin = SharedMapSystem.GetChunkIndices(index, RoofComponent.ChunkSize);
var roof = grid.Comp2;
if (!roof.Data.TryGetValue(chunkOrigin, out var chunkData))
{
// No value to remove so leave it.
if (!value)
{
return;
}
var mask = (tile.Flags & (byte)TileFlag.Roof);
var rooved = mask != 0x0;
chunkData = 0;
}
if (rooved == value)
return;
Tile newTile;
var chunkRelative = SharedMapSystem.GetChunkRelative(index, RoofComponent.ChunkSize);
var bitFlag = (ulong) 1 << (chunkRelative.X + chunkRelative.Y * RoofComponent.ChunkSize);
if (value)
{
newTile = tile.WithFlag((byte)(tile.Flags | (ushort)TileFlag.Roof));
// Already set
if ((chunkData & bitFlag) == bitFlag)
return;
chunkData |= bitFlag;
}
else
{
newTile = tile.WithFlag((byte)(tile.Flags & ~(ushort)TileFlag.Roof));
// Not already set
if ((chunkData & bitFlag) == 0x0)
return;
chunkData &= ~bitFlag;
}
_maps.SetTile((grid.Owner, grid.Comp1), index, newTile);
roof.Data[chunkOrigin] = chunkData;
Dirty(grid.Owner, roof);
}
}

View File

@@ -120,11 +120,4 @@ namespace Content.Shared.Maps
TileId = id;
}
}
[Flags]
public enum TileFlag : byte
{
None = 0,
Roof = 1 << 0,
}
}

View File

@@ -26,11 +26,4 @@ public sealed partial class BiomeTileLayer : IBiomeLayer
[DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile = string.Empty;
// TODO: Need some good engine solution to this, see FlagSerializer for what needs changing.
/// <summary>
/// Flags to set on the tile when placed.
/// </summary>
[DataField]
public byte Flags = 0;
}

View File

@@ -129,7 +129,7 @@ public abstract class SharedBiomeSystem : EntitySystem
if (layer is not BiomeTileLayer tileLayer)
continue;
if (TryGetTile(indices, noiseCopy, tileLayer.Invert, tileLayer.Threshold, ProtoManager.Index(tileLayer.Tile), tileLayer.Flags, tileLayer.Variants, out tile))
if (TryGetTile(indices, noiseCopy, tileLayer.Invert, tileLayer.Threshold, ProtoManager.Index(tileLayer.Tile), tileLayer.Variants, out tile))
{
return true;
}
@@ -142,7 +142,7 @@ public abstract class SharedBiomeSystem : EntitySystem
/// <summary>
/// Gets the underlying biome tile, ignoring any existing tile that may be there.
/// </summary>
private bool TryGetTile(Vector2i indices, FastNoiseLite noise, bool invert, float threshold, ContentTileDefinition tileDef, byte tileFlags, List<byte>? variants, [NotNullWhen(true)] out Tile? tile)
private bool TryGetTile(Vector2i indices, FastNoiseLite noise, bool invert, float threshold, ContentTileDefinition tileDef, List<byte>? variants, [NotNullWhen(true)] out Tile? tile)
{
var found = noise.GetNoise(indices.X, indices.Y);
found = invert ? found * -1 : found;
@@ -163,7 +163,7 @@ public abstract class SharedBiomeSystem : EntitySystem
variant = _tile.PickVariant(tileDef, (int) variantValue);
}
tile = new Tile(tileDef.TileId, flags: tileFlags, variant);
tile = new Tile(tileDef.TileId, variant);
return true;
}

View File

@@ -1,3 +1,5 @@
using Content.Shared.Light.Components;
using Content.Shared.Light.EntitySystems;
using Content.Shared.Maps;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Map;
@@ -17,6 +19,7 @@ public abstract class SharedWeatherSystem : EntitySystem
[Dependency] private readonly MetaDataSystem _metadata = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedMapSystem _mapSystem = default!;
[Dependency] private readonly SharedRoofSystem _roof = default!;
private EntityQuery<BlockWeatherComponent> _blockQuery;
@@ -38,12 +41,12 @@ public abstract class SharedWeatherSystem : EntitySystem
}
}
public bool CanWeatherAffect(EntityUid uid, MapGridComponent grid, TileRef tileRef)
public bool CanWeatherAffect(EntityUid uid, MapGridComponent grid, TileRef tileRef, RoofComponent? roofComp = null)
{
if (tileRef.Tile.IsEmpty)
return true;
if ((tileRef.Tile.Flags & (byte) TileFlag.Roof) == (byte) TileFlag.Roof)
if (Resolve(uid, ref roofComp, false) && _roof.IsRooved((uid, grid, roofComp), tileRef.GridIndices))
return false;
var tileDef = (ContentTileDefinition) _tileDefManager[tileRef.Tile.TypeId];

View File

@@ -21,6 +21,7 @@
- type: Tag
tags:
- Wall
- type: IsRoof
- type: Sprite
drawdepth: Walls
- type: Icon

View File

@@ -544,7 +544,6 @@
- !type:BiomeTileLayer
threshold: -1.0
tile: FloorAsteroidSand
flags: 1
# Asteroid
- type: biomeTemplate