diff --git a/OpenRA.Game/Traits/World/Shroud.cs b/OpenRA.Game/Traits/World/Shroud.cs index 5a26727b46..9e65396379 100644 --- a/OpenRA.Game/Traits/World/Shroud.cs +++ b/OpenRA.Game/Traits/World/Shroud.cs @@ -11,8 +11,6 @@ using System; using System.Collections.Generic; -using System.Linq; -using OpenRA.Network; namespace OpenRA.Traits { @@ -42,20 +40,37 @@ namespace OpenRA.Traits public class Shroud : ISync, INotifyCreated { + public enum SourceType : byte { PassiveVisibility, Shroud, Visibility } public event Action> CellsChanged; + enum ShroudCellType : byte { Shroud, Fog, Visible } + class ShroudSource + { + public readonly SourceType Type; + public readonly PPos[] ProjectedCells; + + public ShroudSource(SourceType type, PPos[] projectedCells) + { + Type = type; + ProjectedCells = projectedCells; + } + } + readonly Actor self; readonly ShroudInfo info; readonly Map map; + // Individual shroud modifier sources (type and area) + readonly Dictionary sources = new Dictionary(); + + // Per-cell count of each source type, used to resolve the final cell type + readonly CellLayer passiveVisibleCount; readonly CellLayer visibleCount; readonly CellLayer generatedShroudCount; readonly CellLayer explored; - // Cache of visibility that was added, so no matter what crazy trait code does, it - // can't make us invalid. - readonly Dictionary visibility = new Dictionary(); - readonly Dictionary generation = new Dictionary(); + // Per-cell cache of the resolved cell type (shroud/fog/visible) + readonly CellLayer resolvedType; [Sync] bool disabled; public bool Disabled @@ -81,15 +96,23 @@ namespace OpenRA.Traits public int Hash { get; private set; } + // Enabled at runtime on first use + bool shroudGenerationEnabled; + bool passiveVisibilityEnabled; + public Shroud(Actor self, ShroudInfo info) { this.self = self; this.info = info; map = self.World.Map; + passiveVisibleCount = new CellLayer(map); visibleCount = new CellLayer(map); generatedShroudCount = new CellLayer(map); explored = new CellLayer(map); + + // Defaults to 0 = Shroud + resolvedType = new CellLayer(map); } void INotifyCreated.Created(Actor self) @@ -104,6 +127,23 @@ namespace OpenRA.Traits void Invalidate(IEnumerable changed) { + foreach (var puv in changed) + { + var uv = (MPos)puv; + var type = ShroudCellType.Shroud; + + if (explored[uv] && (!shroudGenerationEnabled || generatedShroudCount[uv] == 0 || visibleCount[uv] > 0)) + { + var count = visibleCount[uv]; + if (passiveVisibilityEnabled) + count += passiveVisibleCount[uv]; + + type = count > 0 ? ShroudCellType.Visible : ShroudCellType.Fog; + } + + resolvedType[uv] = type; + } + if (CellsChanged != null) CellsChanged(changed); @@ -142,66 +182,70 @@ namespace OpenRA.Traits return ProjectedCellsInRange(map, map.CenterOfCell(cell), range, maxHeightDelta); } - public void AddProjectedVisibility(object key, PPos[] visible) + public void AddSource(object key, SourceType type, PPos[] projectedCells) { - foreach (var puv in visible) + if (sources.ContainsKey(key)) + throw new InvalidOperationException("Attempting to add duplicate shroud source"); + + sources[key] = new ShroudSource(type, projectedCells); + + foreach (var puv in projectedCells) { // Force cells outside the visible bounds invisible if (!map.Contains(puv)) continue; var uv = (MPos)puv; - visibleCount[uv]++; - explored[uv] = true; + switch (type) + { + case SourceType.PassiveVisibility: + passiveVisibilityEnabled = true; + passiveVisibleCount[uv]++; + explored[uv] = true; + break; + case SourceType.Visibility: + visibleCount[uv]++; + explored[uv] = true; + break; + case SourceType.Shroud: + shroudGenerationEnabled = true; + generatedShroudCount[uv]++; + break; + } } - if (visibility.ContainsKey(key)) - throw new InvalidOperationException("Attempting to add duplicate actor visibility"); - - visibility[key] = visible; - Invalidate(visible); + Invalidate(projectedCells); } - public void RemoveVisibility(object key) + public void RemoveSource(object key) { - PPos[] visible; - if (!visibility.TryGetValue(key, out visible)) + ShroudSource state; + if (!sources.TryGetValue(key, out state)) return; - foreach (var puv in visible) + foreach (var puv in state.ProjectedCells) { // Cells outside the visible bounds don't increment visibleCount if (map.Contains(puv)) - visibleCount[(MPos)puv]--; + { + var uv = (MPos)puv; + switch (state.Type) + { + case SourceType.PassiveVisibility: + passiveVisibleCount[uv]--; + break; + case SourceType.Visibility: + visibleCount[uv]--; + break; + case SourceType.Shroud: + generatedShroudCount[uv]--; + break; + } + } } - visibility.Remove(key); - Invalidate(visible); - } - - public void AddProjectedShroudGeneration(object key, PPos[] shrouded) - { - foreach (var uv in shrouded) - generatedShroudCount[(MPos)uv]++; - - if (generation.ContainsKey(key)) - throw new InvalidOperationException("Attempting to add duplicate shroud generation"); - - generation[key] = shrouded; - Invalidate(shrouded); - } - - public void RemoveShroudGeneration(object key) - { - PPos[] shrouded; - if (!generation.TryGetValue(key, out shrouded)) - return; - - foreach (var uv in shrouded) - generatedShroudCount[(MPos)uv]--; - - generation.Remove(key); - Invalidate(shrouded); + sources.Remove(key); + Invalidate(state.ProjectedCells); } public void ExploreProjectedCells(World world, IEnumerable cells) @@ -261,7 +305,7 @@ namespace OpenRA.Traits foreach (var puv in map.ProjectedCellBounds) { var uv = (MPos)puv; - var visible = visibleCount[uv] > 0; + var visible = visibleCount[uv] + passiveVisibleCount[uv] > 0; if (explored[uv] != visible) { explored[uv] = visible; @@ -299,8 +343,7 @@ namespace OpenRA.Traits if (Disabled) return map.Contains(puv); - var uv = (MPos)puv; - return explored.Contains(uv) && explored[uv] && (generatedShroudCount[uv] == 0 || visibleCount[uv] > 0); + return resolvedType[(MPos)puv] > ShroudCellType.Shroud; } public bool IsVisible(WPos pos) @@ -315,7 +358,7 @@ namespace OpenRA.Traits public bool IsVisible(MPos uv) { - if (!visibleCount.Contains(uv)) + if (!resolvedType.Contains(uv)) return false; foreach (var puv in map.ProjectedCellsCovering(uv)) @@ -332,7 +375,7 @@ namespace OpenRA.Traits return map.Contains(puv); var uv = (MPos)puv; - return visibleCount.Contains(uv) && visibleCount[uv] > 0; + return resolvedType.Contains(uv) && resolvedType[uv] == ShroudCellType.Visible; } public bool Contains(PPos uv) diff --git a/OpenRA.Mods.Common/Traits/CreatesShroud.cs b/OpenRA.Mods.Common/Traits/CreatesShroud.cs index 4b6bb0af60..ec1166d0b0 100644 --- a/OpenRA.Mods.Common/Traits/CreatesShroud.cs +++ b/OpenRA.Mods.Common/Traits/CreatesShroud.cs @@ -33,10 +33,10 @@ namespace OpenRA.Mods.Common.Traits if (!info.ValidStances.HasStance(p.Stances[self.Owner])) return; - p.Shroud.AddProjectedShroudGeneration(this, uv); + p.Shroud.AddSource(this, Shroud.SourceType.Shroud, uv); } - protected override void RemoveCellsFromPlayerShroud(Actor self, Player p) { p.Shroud.RemoveShroudGeneration(this); } + protected override void RemoveCellsFromPlayerShroud(Actor self, Player p) { p.Shroud.RemoveSource(this); } protected override bool IsDisabled(Actor self) { return self.IsDisabled(); } } diff --git a/OpenRA.Mods.Common/Traits/RevealsShroud.cs b/OpenRA.Mods.Common/Traits/RevealsShroud.cs index 059e2d390c..1c66e6a8c5 100644 --- a/OpenRA.Mods.Common/Traits/RevealsShroud.cs +++ b/OpenRA.Mods.Common/Traits/RevealsShroud.cs @@ -18,24 +18,33 @@ namespace OpenRA.Mods.Common.Traits [Desc("Stance the watching player needs to see the shroud removed.")] public readonly Stance ValidStances = Stance.Ally; + [Desc("Can this actor reveal shroud generated by the GeneratesShroud trait?")] + public readonly bool RevealGeneratedShroud = true; + public override object Create(ActorInitializer init) { return new RevealsShroud(init.Self, this); } } public class RevealsShroud : AffectsShroud { readonly RevealsShroudInfo info; + readonly Shroud.SourceType type; public RevealsShroud(Actor self, RevealsShroudInfo info) - : base(self, info) { this.info = info; } + : base(self, info) + { + this.info = info; + type = info.RevealGeneratedShroud ? Shroud.SourceType.Visibility + : Shroud.SourceType.PassiveVisibility; + } protected override void AddCellsToPlayerShroud(Actor self, Player p, PPos[] uv) { if (!info.ValidStances.HasStance(p.Stances[self.Owner])) return; - p.Shroud.AddProjectedVisibility(this, uv); + p.Shroud.AddSource(this, type, uv); } - protected override void RemoveCellsFromPlayerShroud(Actor self, Player p) { p.Shroud.RemoveVisibility(this); } + protected override void RemoveCellsFromPlayerShroud(Actor self, Player p) { p.Shroud.RemoveSource(this); } } }