Merge pull request #12307 from pchote/passive-visibility
Add plumbing for improved RA gap generator behaviour.
This commit is contained in:
@@ -11,8 +11,6 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using OpenRA.Network;
|
|
||||||
|
|
||||||
namespace OpenRA.Traits
|
namespace OpenRA.Traits
|
||||||
{
|
{
|
||||||
@@ -42,20 +40,37 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
public class Shroud : ISync, INotifyCreated
|
public class Shroud : ISync, INotifyCreated
|
||||||
{
|
{
|
||||||
|
public enum SourceType : byte { PassiveVisibility, Shroud, Visibility }
|
||||||
public event Action<IEnumerable<PPos>> CellsChanged;
|
public event Action<IEnumerable<PPos>> 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 Actor self;
|
||||||
readonly ShroudInfo info;
|
readonly ShroudInfo info;
|
||||||
readonly Map map;
|
readonly Map map;
|
||||||
|
|
||||||
|
// Individual shroud modifier sources (type and area)
|
||||||
|
readonly Dictionary<object, ShroudSource> sources = new Dictionary<object, ShroudSource>();
|
||||||
|
|
||||||
|
// Per-cell count of each source type, used to resolve the final cell type
|
||||||
|
readonly CellLayer<short> passiveVisibleCount;
|
||||||
readonly CellLayer<short> visibleCount;
|
readonly CellLayer<short> visibleCount;
|
||||||
readonly CellLayer<short> generatedShroudCount;
|
readonly CellLayer<short> generatedShroudCount;
|
||||||
readonly CellLayer<bool> explored;
|
readonly CellLayer<bool> explored;
|
||||||
|
|
||||||
// Cache of visibility that was added, so no matter what crazy trait code does, it
|
// Per-cell cache of the resolved cell type (shroud/fog/visible)
|
||||||
// can't make us invalid.
|
readonly CellLayer<ShroudCellType> resolvedType;
|
||||||
readonly Dictionary<object, PPos[]> visibility = new Dictionary<object, PPos[]>();
|
|
||||||
readonly Dictionary<object, PPos[]> generation = new Dictionary<object, PPos[]>();
|
|
||||||
|
|
||||||
[Sync] bool disabled;
|
[Sync] bool disabled;
|
||||||
public bool Disabled
|
public bool Disabled
|
||||||
@@ -81,15 +96,23 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
public int Hash { get; private set; }
|
public int Hash { get; private set; }
|
||||||
|
|
||||||
|
// Enabled at runtime on first use
|
||||||
|
bool shroudGenerationEnabled;
|
||||||
|
bool passiveVisibilityEnabled;
|
||||||
|
|
||||||
public Shroud(Actor self, ShroudInfo info)
|
public Shroud(Actor self, ShroudInfo info)
|
||||||
{
|
{
|
||||||
this.self = self;
|
this.self = self;
|
||||||
this.info = info;
|
this.info = info;
|
||||||
map = self.World.Map;
|
map = self.World.Map;
|
||||||
|
|
||||||
|
passiveVisibleCount = new CellLayer<short>(map);
|
||||||
visibleCount = new CellLayer<short>(map);
|
visibleCount = new CellLayer<short>(map);
|
||||||
generatedShroudCount = new CellLayer<short>(map);
|
generatedShroudCount = new CellLayer<short>(map);
|
||||||
explored = new CellLayer<bool>(map);
|
explored = new CellLayer<bool>(map);
|
||||||
|
|
||||||
|
// Defaults to 0 = Shroud
|
||||||
|
resolvedType = new CellLayer<ShroudCellType>(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
void INotifyCreated.Created(Actor self)
|
void INotifyCreated.Created(Actor self)
|
||||||
@@ -104,6 +127,23 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
void Invalidate(IEnumerable<PPos> changed)
|
void Invalidate(IEnumerable<PPos> 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)
|
if (CellsChanged != null)
|
||||||
CellsChanged(changed);
|
CellsChanged(changed);
|
||||||
|
|
||||||
@@ -142,66 +182,70 @@ namespace OpenRA.Traits
|
|||||||
return ProjectedCellsInRange(map, map.CenterOfCell(cell), range, maxHeightDelta);
|
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
|
// Force cells outside the visible bounds invisible
|
||||||
if (!map.Contains(puv))
|
if (!map.Contains(puv))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var uv = (MPos)puv;
|
var uv = (MPos)puv;
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case SourceType.PassiveVisibility:
|
||||||
|
passiveVisibilityEnabled = true;
|
||||||
|
passiveVisibleCount[uv]++;
|
||||||
|
explored[uv] = true;
|
||||||
|
break;
|
||||||
|
case SourceType.Visibility:
|
||||||
visibleCount[uv]++;
|
visibleCount[uv]++;
|
||||||
explored[uv] = true;
|
explored[uv] = true;
|
||||||
|
break;
|
||||||
|
case SourceType.Shroud:
|
||||||
|
shroudGenerationEnabled = true;
|
||||||
|
generatedShroudCount[uv]++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (visibility.ContainsKey(key))
|
Invalidate(projectedCells);
|
||||||
throw new InvalidOperationException("Attempting to add duplicate actor visibility");
|
|
||||||
|
|
||||||
visibility[key] = visible;
|
|
||||||
Invalidate(visible);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveVisibility(object key)
|
public void RemoveSource(object key)
|
||||||
{
|
{
|
||||||
PPos[] visible;
|
ShroudSource state;
|
||||||
if (!visibility.TryGetValue(key, out visible))
|
if (!sources.TryGetValue(key, out state))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach (var puv in visible)
|
foreach (var puv in state.ProjectedCells)
|
||||||
{
|
{
|
||||||
// Cells outside the visible bounds don't increment visibleCount
|
// Cells outside the visible bounds don't increment visibleCount
|
||||||
if (map.Contains(puv))
|
if (map.Contains(puv))
|
||||||
visibleCount[(MPos)puv]--;
|
|
||||||
}
|
|
||||||
|
|
||||||
visibility.Remove(key);
|
|
||||||
Invalidate(visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddProjectedShroudGeneration(object key, PPos[] shrouded)
|
|
||||||
{
|
{
|
||||||
foreach (var uv in shrouded)
|
var uv = (MPos)puv;
|
||||||
generatedShroudCount[(MPos)uv]++;
|
switch (state.Type)
|
||||||
|
{
|
||||||
if (generation.ContainsKey(key))
|
case SourceType.PassiveVisibility:
|
||||||
throw new InvalidOperationException("Attempting to add duplicate shroud generation");
|
passiveVisibleCount[uv]--;
|
||||||
|
break;
|
||||||
generation[key] = shrouded;
|
case SourceType.Visibility:
|
||||||
Invalidate(shrouded);
|
visibleCount[uv]--;
|
||||||
|
break;
|
||||||
|
case SourceType.Shroud:
|
||||||
|
generatedShroudCount[uv]--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RemoveShroudGeneration(object key)
|
sources.Remove(key);
|
||||||
{
|
Invalidate(state.ProjectedCells);
|
||||||
PPos[] shrouded;
|
|
||||||
if (!generation.TryGetValue(key, out shrouded))
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var uv in shrouded)
|
|
||||||
generatedShroudCount[(MPos)uv]--;
|
|
||||||
|
|
||||||
generation.Remove(key);
|
|
||||||
Invalidate(shrouded);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ExploreProjectedCells(World world, IEnumerable<PPos> cells)
|
public void ExploreProjectedCells(World world, IEnumerable<PPos> cells)
|
||||||
@@ -261,7 +305,7 @@ namespace OpenRA.Traits
|
|||||||
foreach (var puv in map.ProjectedCellBounds)
|
foreach (var puv in map.ProjectedCellBounds)
|
||||||
{
|
{
|
||||||
var uv = (MPos)puv;
|
var uv = (MPos)puv;
|
||||||
var visible = visibleCount[uv] > 0;
|
var visible = visibleCount[uv] + passiveVisibleCount[uv] > 0;
|
||||||
if (explored[uv] != visible)
|
if (explored[uv] != visible)
|
||||||
{
|
{
|
||||||
explored[uv] = visible;
|
explored[uv] = visible;
|
||||||
@@ -299,8 +343,7 @@ namespace OpenRA.Traits
|
|||||||
if (Disabled)
|
if (Disabled)
|
||||||
return map.Contains(puv);
|
return map.Contains(puv);
|
||||||
|
|
||||||
var uv = (MPos)puv;
|
return resolvedType[(MPos)puv] > ShroudCellType.Shroud;
|
||||||
return explored.Contains(uv) && explored[uv] && (generatedShroudCount[uv] == 0 || visibleCount[uv] > 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsVisible(WPos pos)
|
public bool IsVisible(WPos pos)
|
||||||
@@ -315,7 +358,7 @@ namespace OpenRA.Traits
|
|||||||
|
|
||||||
public bool IsVisible(MPos uv)
|
public bool IsVisible(MPos uv)
|
||||||
{
|
{
|
||||||
if (!visibleCount.Contains(uv))
|
if (!resolvedType.Contains(uv))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
foreach (var puv in map.ProjectedCellsCovering(uv))
|
foreach (var puv in map.ProjectedCellsCovering(uv))
|
||||||
@@ -332,7 +375,7 @@ namespace OpenRA.Traits
|
|||||||
return map.Contains(puv);
|
return map.Contains(puv);
|
||||||
|
|
||||||
var uv = (MPos)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)
|
public bool Contains(PPos uv)
|
||||||
|
|||||||
@@ -33,10 +33,10 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
if (!info.ValidStances.HasStance(p.Stances[self.Owner]))
|
if (!info.ValidStances.HasStance(p.Stances[self.Owner]))
|
||||||
return;
|
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(); }
|
protected override bool IsDisabled(Actor self) { return self.IsDisabled(); }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,24 +18,33 @@ namespace OpenRA.Mods.Common.Traits
|
|||||||
[Desc("Stance the watching player needs to see the shroud removed.")]
|
[Desc("Stance the watching player needs to see the shroud removed.")]
|
||||||
public readonly Stance ValidStances = Stance.Ally;
|
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 override object Create(ActorInitializer init) { return new RevealsShroud(init.Self, this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class RevealsShroud : AffectsShroud
|
public class RevealsShroud : AffectsShroud
|
||||||
{
|
{
|
||||||
readonly RevealsShroudInfo info;
|
readonly RevealsShroudInfo info;
|
||||||
|
readonly Shroud.SourceType type;
|
||||||
|
|
||||||
public RevealsShroud(Actor self, RevealsShroudInfo info)
|
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)
|
protected override void AddCellsToPlayerShroud(Actor self, Player p, PPos[] uv)
|
||||||
{
|
{
|
||||||
if (!info.ValidStances.HasStance(p.Stances[self.Owner]))
|
if (!info.ValidStances.HasStance(p.Stances[self.Owner]))
|
||||||
return;
|
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); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user