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 System.Numerics;
using Content.Shared.Light.Components; using Content.Shared.Light.Components;
using Content.Shared.Light.EntitySystems;
using Content.Shared.Maps; using Content.Shared.Maps;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using Robust.Shared.Map.Enumerators;
using Robust.Shared.Physics; using Robust.Shared.Physics;
namespace Content.Client.Light; namespace Content.Client.Light;
@@ -17,9 +19,9 @@ public sealed class RoofOverlay : Overlay
private readonly EntityLookupSystem _lookup; private readonly EntityLookupSystem _lookup;
private readonly SharedMapSystem _mapSystem; private readonly SharedMapSystem _mapSystem;
private readonly SharedRoofSystem _roof = default!;
private readonly SharedTransformSystem _xformSystem; private readonly SharedTransformSystem _xformSystem;
private readonly HashSet<Entity<OccluderComponent>> _occluders = new();
private List<Entity<MapGridComponent>> _grids = new(); private List<Entity<MapGridComponent>> _grids = new();
public override OverlaySpace Space => OverlaySpace.BeforeLighting; public override OverlaySpace Space => OverlaySpace.BeforeLighting;
@@ -33,6 +35,7 @@ public sealed class RoofOverlay : Overlay
_lookup = _entManager.System<EntityLookupSystem>(); _lookup = _entManager.System<EntityLookupSystem>();
_mapSystem = _entManager.System<SharedMapSystem>(); _mapSystem = _entManager.System<SharedMapSystem>();
_roof = _entManager.System<SharedRoofSystem>();
_xformSystem = _entManager.System<SharedTransformSystem>(); _xformSystem = _entManager.System<SharedTransformSystem>();
ZIndex = ContentZIndex; ZIndex = ContentZIndex;
@@ -86,28 +89,13 @@ public sealed class RoofOverlay : Overlay
worldHandle.SetTransform(matty); worldHandle.SetTransform(matty);
var tileEnumerator = _mapSystem.GetTilesEnumerator(grid.Owner, grid, bounds); var tileEnumerator = _mapSystem.GetTilesEnumerator(grid.Owner, grid, bounds);
var roofEnt = (grid.Owner, grid.Comp, roof);
// Due to stencilling we essentially draw on unrooved tiles // Due to stencilling we essentially draw on unrooved tiles
while (tileEnumerator.MoveNext(out var tileRef)) 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; continue;
} }

View File

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

View File

@@ -1,4 +1,5 @@
using System.Numerics; using System.Numerics;
using Content.Shared.Light.Components;
using Content.Shared.Weather; using Content.Shared.Weather;
using Robust.Client.Audio; using Robust.Client.Audio;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
@@ -57,6 +58,7 @@ public sealed class WeatherSystem : SharedWeatherSystem
// Work out tiles nearby to determine volume. // Work out tiles nearby to determine volume.
if (TryComp<MapGridComponent>(entXform.GridUid, out var grid)) if (TryComp<MapGridComponent>(entXform.GridUid, out var grid))
{ {
TryComp(entXform.GridUid, out RoofComponent? roofComp);
var gridId = entXform.GridUid.Value; var gridId = entXform.GridUid.Value;
// FloodFill to the nearest tile and use that for audio. // FloodFill to the nearest tile and use that for audio.
var seed = _mapSystem.GetTileRef(gridId, grid, entXform.Coordinates); var seed = _mapSystem.GetTileRef(gridId, grid, entXform.Coordinates);
@@ -71,7 +73,7 @@ public sealed class WeatherSystem : SharedWeatherSystem
if (!visited.Add(node.GridIndices)) if (!visited.Add(node.GridIndices))
continue; continue;
if (!CanWeatherAffect(entXform.GridUid.Value, grid, node)) if (!CanWeatherAffect(entXform.GridUid.Value, grid, node, roofComp))
{ {
// Add neighbors // Add neighbors
// TODO: Ideally we pick some deterministically random direction and use that // 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; namespace Content.Shared.Light.Components;
/// <summary> /// <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> /// </summary>
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] [RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
public sealed partial class RoofComponent : Component public sealed partial class RoofComponent : Component
{ {
public const int ChunkSize = 8;
[DataField, AutoNetworkedField] [DataField, AutoNetworkedField]
public Color Color = Color.Black; 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> /// </summary>
public abstract class SharedRoofSystem : EntitySystem 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) public void SetRoof(Entity<MapGridComponent?, RoofComponent?> grid, Vector2i index, bool value)
{ {
if (!Resolve(grid, ref grid.Comp1, ref grid.Comp2, false)) if (!Resolve(grid, ref grid.Comp1, ref grid.Comp2, false))
return; 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; return;
}
var mask = (tile.Flags & (byte)TileFlag.Roof); chunkData = 0;
var rooved = mask != 0x0; }
if (rooved == value) var chunkRelative = SharedMapSystem.GetChunkRelative(index, RoofComponent.ChunkSize);
return; var bitFlag = (ulong) 1 << (chunkRelative.X + chunkRelative.Y * RoofComponent.ChunkSize);
Tile newTile;
if (value) if (value)
{ {
newTile = tile.WithFlag((byte)(tile.Flags | (ushort)TileFlag.Roof)); // Already set
if ((chunkData & bitFlag) == bitFlag)
return;
chunkData |= bitFlag;
} }
else 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; 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)] [DataField(required: true)]
public ProtoId<ContentTileDefinition> Tile = string.Empty; 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) if (layer is not BiomeTileLayer tileLayer)
continue; 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; return true;
} }
@@ -142,7 +142,7 @@ public abstract class SharedBiomeSystem : EntitySystem
/// <summary> /// <summary>
/// Gets the underlying biome tile, ignoring any existing tile that may be there. /// Gets the underlying biome tile, ignoring any existing tile that may be there.
/// </summary> /// </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); var found = noise.GetNoise(indices.X, indices.Y);
found = invert ? found * -1 : found; found = invert ? found * -1 : found;
@@ -163,7 +163,7 @@ public abstract class SharedBiomeSystem : EntitySystem
variant = _tile.PickVariant(tileDef, (int) variantValue); variant = _tile.PickVariant(tileDef, (int) variantValue);
} }
tile = new Tile(tileDef.TileId, flags: tileFlags, variant); tile = new Tile(tileDef.TileId, variant);
return true; return true;
} }

View File

@@ -1,3 +1,5 @@
using Content.Shared.Light.Components;
using Content.Shared.Light.EntitySystems;
using Content.Shared.Maps; using Content.Shared.Maps;
using Robust.Shared.Audio.Systems; using Robust.Shared.Audio.Systems;
using Robust.Shared.Map; using Robust.Shared.Map;
@@ -17,6 +19,7 @@ public abstract class SharedWeatherSystem : EntitySystem
[Dependency] private readonly MetaDataSystem _metadata = default!; [Dependency] private readonly MetaDataSystem _metadata = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedMapSystem _mapSystem = default!; [Dependency] private readonly SharedMapSystem _mapSystem = default!;
[Dependency] private readonly SharedRoofSystem _roof = default!;
private EntityQuery<BlockWeatherComponent> _blockQuery; 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) if (tileRef.Tile.IsEmpty)
return true; 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; return false;
var tileDef = (ContentTileDefinition) _tileDefManager[tileRef.Tile.TypeId]; var tileDef = (ContentTileDefinition) _tileDefManager[tileRef.Tile.TypeId];

View File

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

View File

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