Merge pull request #12307 from pchote/passive-visibility

Add plumbing for improved RA gap generator behaviour.
This commit is contained in:
reaperrr
2016-11-18 14:34:35 +01:00
committed by GitHub
3 changed files with 109 additions and 57 deletions

View File

@@ -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;
visibleCount[uv]++; switch (type)
explored[uv] = true; {
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)) 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]--; {
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); sources.Remove(key);
Invalidate(visible); Invalidate(state.ProjectedCells);
}
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);
} }
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)

View File

@@ -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(); }
} }

View File

@@ -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); }
} }
} }