diff --git a/Content.Client/Light/EntitySystems/PlanetLightSystem.cs b/Content.Client/Light/EntitySystems/PlanetLightSystem.cs index b72bfc569dc..7ac683385e7 100644 --- a/Content.Client/Light/EntitySystems/PlanetLightSystem.cs +++ b/Content.Client/Light/EntitySystems/PlanetLightSystem.cs @@ -6,11 +6,12 @@ public sealed class PlanetLightSystem : EntitySystem { [Dependency] private readonly IOverlayManager _overlayMan = default!; - private RoofOverlay _overlay = new(); + private RoofOverlay _overlay = default!; public override void Initialize() { base.Initialize(); + _overlay = new(EntityManager); _overlayMan.AddOverlay(_overlay); } diff --git a/Content.Client/Light/EntitySystems/TileEmissionSystem.cs b/Content.Client/Light/EntitySystems/TileEmissionSystem.cs new file mode 100644 index 00000000000..4e90ecd78d8 --- /dev/null +++ b/Content.Client/Light/EntitySystems/TileEmissionSystem.cs @@ -0,0 +1,23 @@ +using Robust.Client.Graphics; + +namespace Content.Client.Light.EntitySystems; + +public sealed class TileEmissionSystem : EntitySystem +{ + [Dependency] private readonly IOverlayManager _overlayMan = default!; + + private TileEmissionOverlay _overlay = default!; + + public override void Initialize() + { + base.Initialize(); + _overlay = new(EntityManager); + _overlayMan.AddOverlay(_overlay); + } + + public override void Shutdown() + { + base.Shutdown(); + _overlayMan.RemoveOverlay(_overlay); + } +} diff --git a/Content.Client/Light/RoofOverlay.cs b/Content.Client/Light/RoofOverlay.cs index c65f0de5dbb..66c1b11a4a5 100644 --- a/Content.Client/Light/RoofOverlay.cs +++ b/Content.Client/Light/RoofOverlay.cs @@ -1,18 +1,27 @@ using System.Numerics; using Content.Shared.Light.Components; +using Content.Shared.Maps; using Robust.Client.Graphics; using Robust.Shared.Enums; +using Robust.Shared.Map; using Robust.Shared.Map.Components; namespace Content.Client.Light; public sealed class RoofOverlay : Overlay { + [Dependency] private readonly ITileDefinitionManager _tileDefMan = default!; + private readonly IEntityManager _entManager; + + private readonly HashSet> _occluders = new(); + public override OverlaySpace Space => OverlaySpace.BeforeLighting; - public RoofOverlay() + public RoofOverlay(IEntityManager entManager) { + _entManager = entManager; IoCManager.InjectDependencies(this); + ZIndex = -1; } protected override void Draw(in OverlayDrawArgs args) @@ -22,16 +31,15 @@ public sealed class RoofOverlay : Overlay var viewport = args.Viewport; var eye = args.Viewport.Eye; - var entManager = IoCManager.Resolve(); - var mapSystem = entManager.System(); + var mapSystem = _entManager.System(); var worldHandle = args.WorldHandle; var bounds = args.WorldBounds; var mapId = args.MapId; - var lookup = entManager.System(); - var xformSystem = entManager.System(); + var lookup = _entManager.System(); + var xformSystem = _entManager.System(); - var query = entManager.AllEntityQueryEnumerator(); + var query = _entManager.AllEntityQueryEnumerator(); worldHandle.RenderInRenderTarget(viewport.LightRenderTarget, () => @@ -52,13 +60,36 @@ public sealed class RoofOverlay : Overlay while (tileEnumerator.MoveNext(out var tileRef)) { - if (tileRef.Tile.TypeId != 126) - continue; + var tileDef = (ContentTileDefinition) _tileDefMan[tileRef.Tile.TypeId]; + + if (!tileDef.Roof) + { + // 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(uid, tileRef.GridIndices, _occluders); + var found = false; + + foreach (var occluder in _occluders) + { + if (!occluder.Comp.Enabled) + continue; + + found = true; + break; + } + + if (!found) + continue; + } var local = lookup.GetLocalBounds(tileRef, grid.TileSize); worldHandle.DrawRect(local, comp.Color); } } }, null); + + // Around half-a-tile in length because too lazy to do shadows properly and this avoids it going through walls. + // IoCManager.Resolve().BlurLights(viewport, viewport.LightRenderTarget, viewport.Eye, 14f * 4f); } } diff --git a/Content.Client/Light/TileEmissionOverlay.cs b/Content.Client/Light/TileEmissionOverlay.cs new file mode 100644 index 00000000000..a3842529bf5 --- /dev/null +++ b/Content.Client/Light/TileEmissionOverlay.cs @@ -0,0 +1,76 @@ +using System.Numerics; +using Content.Shared.Light.Components; +using Robust.Client.Graphics; +using Robust.Shared.Enums; +using Robust.Shared.Map.Components; + +namespace Content.Client.Light; + +public sealed class TileEmissionOverlay : Overlay +{ + public override OverlaySpace Space => OverlaySpace.BeforeLighting; + + [Dependency] private readonly IClyde _clyde = default!; + + private readonly IEntityManager _entManager; + private readonly EntityLookupSystem _lookup; + + private EntityQuery _gridQuery; + private EntityQuery _xformQuery; + private readonly HashSet> _entities = new(); + + public TileEmissionOverlay(IEntityManager entManager) + { + _entManager = entManager; + IoCManager.InjectDependencies(this); + + _lookup = _entManager.System(); + _gridQuery = _entManager.GetEntityQuery(); + _xformQuery = _entManager.GetEntityQuery(); + } + + protected override void Draw(in OverlayDrawArgs args) + { + if (args.Viewport.Eye == null) + return; + + var mapId = args.MapId; + var worldHandle = args.WorldHandle; + var bounds = args.WorldBounds; + var viewport = args.Viewport; + + args.WorldHandle.RenderInRenderTarget(viewport.LightRenderTarget, + () => + { + var invMatrix = viewport.LightRenderTarget.GetWorldToLocalMatrix(viewport.Eye, viewport.RenderScale / 2f); + _entities.Clear(); + _lookup.GetEntitiesIntersecting(mapId, bounds, _entities); + + foreach (var ent in _entities) + { + var xform = _xformQuery.Comp(ent); + + if (!_gridQuery.TryComp(xform.GridUid, out var grid)) + continue; + + // TODO: Optimise, allocate ents to each grid for transforms + var tile = _entManager.System().LocalToTile(xform.GridUid.Value, grid, xform.Coordinates); + + var gridMatrix = _entManager.System().GetWorldMatrix(xform.GridUid.Value); + var matty = Matrix3x2.Multiply(gridMatrix, invMatrix); + + worldHandle.SetTransform(matty); + + // Yes I am fully aware this leads to overlap. If you really want to have alpha then you'll need + // to turn the squares into polys. + // Additionally no shadows so if you make it too big it's going to go through a 1x wall. + var local = _lookup.GetLocalBounds(tile, grid.TileSize).Enlarged(ent.Comp.Range); + worldHandle.DrawRect(local, ent.Comp.Color); + } + }, null); + + // This also handles blurring for roofoverlay; if these ever become decoupled then you will need to draw at least + // one of these to a separate texture. + _clyde.BlurLights(viewport, viewport.LightRenderTarget, viewport.Eye, 14f * 4f); + } +} diff --git a/Content.Server/Light/EntitySystems/PlanetLightSystem.cs b/Content.Server/Light/EntitySystems/RoofSystem.cs similarity index 59% rename from Content.Server/Light/EntitySystems/PlanetLightSystem.cs rename to Content.Server/Light/EntitySystems/RoofSystem.cs index 82fd75dd49f..3a69a62e68f 100644 --- a/Content.Server/Light/EntitySystems/PlanetLightSystem.cs +++ b/Content.Server/Light/EntitySystems/RoofSystem.cs @@ -2,7 +2,7 @@ using Content.Shared.Light.EntitySystems; namespace Content.Server.Light.EntitySystems; -public sealed class PlanetLightSystem : SharedPlanetLightSystem +public sealed class RoofSystem : SharedRoofSystem { } diff --git a/Content.Server/Parallax/BiomeSystem.cs b/Content.Server/Parallax/BiomeSystem.cs index 8f260a532ae..ece1b54e0d5 100644 --- a/Content.Server/Parallax/BiomeSystem.cs +++ b/Content.Server/Parallax/BiomeSystem.cs @@ -323,6 +323,9 @@ public sealed partial class BiomeSystem : SharedBiomeSystem while (biomes.MoveNext(out var biome)) { + if (biome.LifeStage < ComponentLifeStage.Running) + continue; + _activeChunks.Add(biome, _tilePool.Get()); _markerChunks.GetOrNew(biome); } @@ -370,6 +373,10 @@ public sealed partial class BiomeSystem : SharedBiomeSystem while (loadBiomes.MoveNext(out var gridUid, out var biome, out var grid)) { + // If not MapInit don't run it. + if (biome.LifeStage < ComponentLifeStage.Running) + continue; + if (!biome.Enabled) continue; diff --git a/Content.Shared/Light/Components/TileEmissionComponent.cs b/Content.Shared/Light/Components/TileEmissionComponent.cs new file mode 100644 index 00000000000..3942ca12ea4 --- /dev/null +++ b/Content.Shared/Light/Components/TileEmissionComponent.cs @@ -0,0 +1,13 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Light.Components; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class TileEmissionComponent : Component +{ + [DataField, AutoNetworkedField] + public float Range = 0.25f; + + [DataField(required: true), AutoNetworkedField] + public Color Color = Color.Transparent; +} diff --git a/Content.Shared/Light/EntitySystems/SharedPlanetLightSystem.cs b/Content.Shared/Light/EntitySystems/SharedPlanetLightSystem.cs deleted file mode 100644 index d6b8e265748..00000000000 --- a/Content.Shared/Light/EntitySystems/SharedPlanetLightSystem.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Content.Shared.Light.EntitySystems; - -public abstract class SharedPlanetLightSystem : EntitySystem -{ - -} diff --git a/Content.Shared/Light/EntitySystems/SharedRoofSystem.cs b/Content.Shared/Light/EntitySystems/SharedRoofSystem.cs new file mode 100644 index 00000000000..898feb898ef --- /dev/null +++ b/Content.Shared/Light/EntitySystems/SharedRoofSystem.cs @@ -0,0 +1,6 @@ +namespace Content.Shared.Light.EntitySystems; + +public abstract class SharedRoofSystem : EntitySystem +{ + +} diff --git a/Content.Shared/Maps/ContentTileDefinition.cs b/Content.Shared/Maps/ContentTileDefinition.cs index 839d920df94..f62d241be7f 100644 --- a/Content.Shared/Maps/ContentTileDefinition.cs +++ b/Content.Shared/Maps/ContentTileDefinition.cs @@ -1,4 +1,5 @@ using Content.Shared.Atmos; +using Content.Shared.Light.Components; using Content.Shared.Movement.Systems; using Content.Shared.Tools; using Robust.Shared.Audio; @@ -42,6 +43,12 @@ namespace Content.Shared.Maps [DataField("baseTurf")] public string BaseTurf { get; private set; } = string.Empty; + /// + /// Whether this tile should draw roof shadows on maps. + /// + [DataField] + public bool Roof = true; + [DataField] public PrototypeFlags DeconstructTools { get; set; } = new(); diff --git a/Resources/Prototypes/Entities/Tiles/lava.yml b/Resources/Prototypes/Entities/Tiles/lava.yml index 36c7b80b81b..908902051b0 100644 --- a/Resources/Prototypes/Entities/Tiles/lava.yml +++ b/Resources/Prototypes/Entities/Tiles/lava.yml @@ -7,6 +7,8 @@ snap: - Wall components: + - type: TileEmission + color: "#FFFF45" - type: StepTrigger requiredTriggeredSpeed: 0 intersectRatio: 0.1 diff --git a/Resources/Prototypes/Entities/Tiles/liquid_plasma.yml b/Resources/Prototypes/Entities/Tiles/liquid_plasma.yml index 869db085970..ade23b6f711 100644 --- a/Resources/Prototypes/Entities/Tiles/liquid_plasma.yml +++ b/Resources/Prototypes/Entities/Tiles/liquid_plasma.yml @@ -7,6 +7,8 @@ snap: - Wall components: + - type: TileEmission + color: "#974988" - type: StepTrigger requiredTriggeredSpeed: 0 intersectRatio: 0.1 diff --git a/Resources/Prototypes/Tiles/floors.yml b/Resources/Prototypes/Tiles/floors.yml index 2d552cc33e0..8fc4c0d7569 100644 --- a/Resources/Prototypes/Tiles/floors.yml +++ b/Resources/Prototypes/Tiles/floors.yml @@ -1450,6 +1450,7 @@ id: FloorGrass name: tiles-planet-grass-floor sprite: /Textures/Tiles/grass.png + roof: false baseTurf: FloorDirt isSubfloor: true footstepSounds: @@ -1462,6 +1463,7 @@ id: FloorGrassJungle name: tiles-jungle-grass-floor sprite: /Textures/Tiles/grassjungle.png + roof: false baseTurf: FloorDirt isSubfloor: true footstepSounds: @@ -1474,6 +1476,7 @@ id: FloorGrassDark name: tiles-dark-grass-floor sprite: /Textures/Tiles/grassdark.png + roof: false variants: 4 placementVariants: - 1.0 @@ -1491,6 +1494,7 @@ id: FloorGrassLight name: tiles-light-grass-floor sprite: /Textures/Tiles/grasslight.png + roof: false variants: 4 placementVariants: - 1.0 @@ -1501,6 +1505,7 @@ isSubfloor: true footstepSounds: collection: FootstepGrass + roof: false heatCapacity: 10000 weather: true @@ -1508,6 +1513,7 @@ id: FloorDirt name: tiles-dirt-floor sprite: /Textures/Tiles/dirt.png + roof: false variants: 4 placementVariants: - 1.0 diff --git a/Resources/Prototypes/Tiles/planet.yml b/Resources/Prototypes/Tiles/planet.yml index ccb3fb61dac..42017b5c142 100644 --- a/Resources/Prototypes/Tiles/planet.yml +++ b/Resources/Prototypes/Tiles/planet.yml @@ -2,6 +2,7 @@ id: FloorPlanetDirt name: tiles-dirt-floor sprite: /Textures/Tiles/Planet/dirt.rsi/dirt.png + roof: false variants: 4 placementVariants: - 1.0 @@ -20,6 +21,7 @@ id: FloorDesert name: tiles-desert-floor sprite: /Textures/Tiles/Planet/Desert/desert.png + roof: false variants: 6 placementVariants: - 1.0 @@ -39,6 +41,7 @@ id: FloorLowDesert name: tiles-low-desert-floor sprite: /Textures/Tiles/Planet/Desert/low_desert.png + roof: false variants: 6 placementVariants: - 1.0 @@ -59,6 +62,7 @@ id: FloorPlanetGrass name: tiles-grass-planet-floor sprite: /Textures/Tiles/Planet/Grass/grass.png + roof: false variants: 4 placementVariants: - 1.0 @@ -89,6 +93,7 @@ id: FloorBasalt name: tiles-basalt-floor sprite: /Textures/Tiles/Planet/basalt.png + roof: false isSubfloor: true footstepSounds: collection: FootstepAsteroid @@ -101,6 +106,7 @@ id: FloorSnow name: tiles-snow sprite: /Textures/Tiles/Planet/Snow/snow.png + roof: false variants: 13 placementVariants: - 0.8 @@ -134,6 +140,7 @@ id: FloorIce name: tiles-ice sprite: /Textures/Tiles/Planet/Snow/ice.png + roof: false isSubfloor: true friction: 0.05 heatCapacity: 10000 @@ -148,6 +155,7 @@ id: FloorSnowDug name: tiles-snow-dug sprite: /Textures/Tiles/Planet/Snow/snow_dug.png + roof: false edgeSpritePriority: 1 edgeSprites: South: /Textures/Tiles/Planet/Snow/snow_dug_double_edge_south.png