clipped voronoi regions for ground control; buildings cannot be placed outside of ground control anymore
This commit is contained in:
@@ -27,6 +27,9 @@ namespace OpenRa
|
||||
public int Length { get { return (int)Math.Sqrt(LengthSquared); } }
|
||||
public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode(); }
|
||||
|
||||
public static int2 Max(int2 a, int2 b) { return new int2(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y)); }
|
||||
public static int2 Min(int2 a, int2 b) { return new int2(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y)); }
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
|
||||
@@ -3,33 +3,94 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using OpenRa.Game.GameRules;
|
||||
using IjwFramework.Types;
|
||||
using IjwFramework.Collections;
|
||||
|
||||
namespace OpenRa.Game
|
||||
{
|
||||
class BuildingInfluenceMap
|
||||
{
|
||||
Actor[,] influence = new Actor[128, 128];
|
||||
Pair<Actor, float>[,] influence = new Pair<Actor, float>[128, 128];
|
||||
readonly int maxDistance; /* clip limit for voronoi cells */
|
||||
static readonly Pair<Actor, float> NoClaim = Pair.New((Actor)null, float.MaxValue);
|
||||
|
||||
public BuildingInfluenceMap(World world, Player player)
|
||||
public BuildingInfluenceMap(World world, int maxDistance)
|
||||
{
|
||||
this.maxDistance = maxDistance;
|
||||
|
||||
for (int j = 0; j < 128; j++)
|
||||
for (int i = 0; i < 128; i++)
|
||||
influence[i, j] = NoClaim;
|
||||
|
||||
world.ActorAdded +=
|
||||
a => { if (a.traits.Contains<Traits.Building>() && a.Owner == player) AddInfluence(a); };
|
||||
a => { if (a.traits.Contains<Traits.Building>()) AddInfluence(a); };
|
||||
world.ActorRemoved +=
|
||||
a => { if (a.traits.Contains<Traits.Building>() && a.Owner == player) RemoveInfluence(a); };
|
||||
a => { if (a.traits.Contains<Traits.Building>()) RemoveInfluence(a); };
|
||||
}
|
||||
|
||||
void AddInfluence(Actor a)
|
||||
{
|
||||
foreach (var t in Footprint.UnpathableTiles(a.unitInfo, a.Location))
|
||||
var tiles = Footprint.UnpathableTiles(a.unitInfo, a.Location).ToArray();
|
||||
var min = int2.Max(new int2(0, 0),
|
||||
tiles.Aggregate(int2.Min) - new int2(maxDistance, maxDistance));
|
||||
var max = int2.Min(new int2(128, 128),
|
||||
tiles.Aggregate(int2.Max) + new int2(maxDistance, maxDistance));
|
||||
|
||||
var pq = new PriorityQueue<Cell>();
|
||||
|
||||
var initialTileCount = 0;
|
||||
|
||||
foreach (var t in tiles)
|
||||
if (IsValid(t))
|
||||
influence[t.X, t.Y] = a;
|
||||
{
|
||||
pq.Add(new Cell { location = t, distance = 0, actor=a });
|
||||
++initialTileCount;
|
||||
}
|
||||
|
||||
Log.Write("Recalculating voronoi region for {{ {0} ({1},{2}) }}: {3} initial tiles",
|
||||
a.unitInfo.Name, a.Location.X, a.Location.Y, initialTileCount);
|
||||
|
||||
var updatedCells = 0;
|
||||
|
||||
while (!pq.Empty)
|
||||
{
|
||||
var c = pq.Pop();
|
||||
|
||||
if (influence[c.location.X, c.location.Y].Second <= c.distance)
|
||||
continue;
|
||||
|
||||
influence[c.location.X, c.location.Y].First = c.actor;
|
||||
influence[c.location.X, c.location.Y].Second = c.distance;
|
||||
|
||||
++updatedCells;
|
||||
|
||||
if (c.distance + 1 > maxDistance) continue;
|
||||
|
||||
foreach (var d in PathFinder.directions)
|
||||
{
|
||||
var e = c.location + d;
|
||||
if (e.X < min.X || e.Y < min.Y || e.X > max.X || e.Y > max.Y)
|
||||
continue;
|
||||
|
||||
pq.Add(new Cell
|
||||
{
|
||||
location = e,
|
||||
distance = c.distance + ((d.X * d.Y != 0) ? 1.414f : 1f),
|
||||
actor = c.actor
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Log.Write("Finished recalculating region. {0} cells updated.", updatedCells);
|
||||
}
|
||||
|
||||
void RemoveInfluence(Actor a)
|
||||
{
|
||||
foreach (var t in Footprint.UnpathableTiles(a.unitInfo, a.Location))
|
||||
if (IsValid(t))
|
||||
influence[t.X, t.Y] = null;
|
||||
influence[t.X, t.Y] = NoClaim;
|
||||
|
||||
/* todo: fix everything that was in this region! doesnt matter yet, since we cant destroy buildings */
|
||||
}
|
||||
|
||||
bool IsValid(int2 t)
|
||||
@@ -37,6 +98,35 @@ namespace OpenRa.Game
|
||||
return !(t.X < 0 || t.Y < 0 || t.X >= 128 || t.Y >= 128);
|
||||
}
|
||||
|
||||
public Actor this[int2 cell] { get { return IsValid(cell) ? influence[cell.X, cell.Y] : null; } }
|
||||
public Actor GetBuildingAt(int2 cell)
|
||||
{
|
||||
if (!IsValid(cell) || influence[cell.X, cell.Y].Second != 0)
|
||||
return null;
|
||||
return influence[cell.X, cell.Y].First;
|
||||
}
|
||||
|
||||
public Actor GetNearestBuilding(int2 cell)
|
||||
{
|
||||
if (!IsValid(cell)) return null;
|
||||
return influence[cell.X, cell.Y].First;
|
||||
}
|
||||
|
||||
public int GetDistanceToBuilding(int2 cell)
|
||||
{
|
||||
if (!IsValid(cell)) return int.MaxValue;
|
||||
return (int)influence[cell.X, cell.Y].Second;
|
||||
}
|
||||
|
||||
struct Cell : IComparable<Cell>
|
||||
{
|
||||
public int2 location;
|
||||
public float distance;
|
||||
public Actor actor;
|
||||
|
||||
public int CompareTo(Cell other)
|
||||
{
|
||||
return distance.CompareTo(other.distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace OpenRa.Game
|
||||
public static Dictionary<int, Player> players = new Dictionary<int, Player>();
|
||||
|
||||
public static Player LocalPlayer { get { return players[localPlayerIndex]; } }
|
||||
public static BuildingInfluenceMap LocalPlayerBuildings;
|
||||
public static BuildingInfluenceMap BuildingInfluence;
|
||||
|
||||
static ISoundEngine soundEngine;
|
||||
|
||||
@@ -55,9 +55,9 @@ namespace OpenRa.Game
|
||||
LoadMapBuildings( mapFile );
|
||||
LoadMapUnits( mapFile );
|
||||
|
||||
LocalPlayerBuildings = new BuildingInfluenceMap(world, LocalPlayer);
|
||||
BuildingInfluence = new BuildingInfluenceMap(world, 8);
|
||||
|
||||
pathFinder = new PathFinder(map, terrain.tileSet, LocalPlayerBuildings);
|
||||
pathFinder = new PathFinder(map, terrain.tileSet, BuildingInfluence);
|
||||
|
||||
network = new Network();
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace OpenRa.Game
|
||||
|
||||
public static bool IsCellBuildable(int2 a, UnitMovementType umt)
|
||||
{
|
||||
if (LocalPlayerBuildings[a] != null) return false;
|
||||
if (BuildingInfluence.GetBuildingAt(a) != null) return false;
|
||||
|
||||
a += map.Offset;
|
||||
|
||||
@@ -160,5 +160,14 @@ namespace OpenRa.Game
|
||||
var q = FindUnits(a, a);
|
||||
return q.Where(x => x.traits.Contains<Traits.Mobile>()).Concat(q).Take(1);
|
||||
}
|
||||
|
||||
public static int GetDistanceToBase(int2 b, Player p)
|
||||
{
|
||||
var building = BuildingInfluence.GetNearestBuilding(b);
|
||||
if (building == null || building.Owner != p)
|
||||
return int.MaxValue;
|
||||
|
||||
return BuildingInfluence.GetDistanceToBuilding(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace OpenRa.Game
|
||||
|
||||
SequenceProvider.ForcePrecache();
|
||||
|
||||
Game.world.Add( new Actor( "mcv", new int2( 5, 5 ), Game.players[ 3 ]) );
|
||||
Game.world.Add( new Actor( "mcv", new int2( 5, 5 ), Game.players[ 1 ]) );
|
||||
Game.world.Add( new Actor( "mcv", new int2( 7, 5 ), Game.players[ 2 ] ) );
|
||||
Game.world.Add( new Actor( "mcv", new int2( 9, 5 ), Game.players[ 0 ] ) );
|
||||
var jeep = new Actor( "jeep", new int2( 9, 15 ), Game.players[ 1 ] );
|
||||
|
||||
@@ -64,7 +64,7 @@ namespace OpenRa.Game
|
||||
continue;
|
||||
if( passableCost[ newHere.X, newHere.Y ] == double.PositiveInfinity )
|
||||
continue;
|
||||
if (bim[newHere - offset] != null)
|
||||
if (bim.GetBuildingAt(newHere - offset) != null)
|
||||
continue;
|
||||
|
||||
double cellCost = ( ( d.X * d.Y != 0 ) ? 1.414213563 : 1.0 ) * passableCost[ newHere.X, newHere.Y ];
|
||||
@@ -98,7 +98,7 @@ namespace OpenRa.Game
|
||||
return ret;
|
||||
}
|
||||
|
||||
static readonly int2[] directions =
|
||||
public static readonly int2[] directions =
|
||||
new int2[] {
|
||||
new int2( -1, -1 ),
|
||||
new int2( -1, 0 ),
|
||||
|
||||
@@ -26,6 +26,11 @@ namespace OpenRa.Game
|
||||
bi.WaterBound ? UnitMovementType.Float : UnitMovementType.Wheel)))
|
||||
yield break;
|
||||
|
||||
var maxDistance = bi.Adjacent + 2; /* real-ra is weird. this is 1 GAP. */
|
||||
if (!Footprint.Tiles(bi, xy).Any(
|
||||
t => Game.GetDistanceToBase(t, Owner) < maxDistance))
|
||||
yield break;
|
||||
|
||||
yield return new PlaceBuildingOrder(this, xy);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Drawing;
|
||||
using OpenRa.Game.Graphics;
|
||||
using System;
|
||||
using OpenRa.Game.GameRules;
|
||||
using System.Linq;
|
||||
|
||||
namespace OpenRa.Game
|
||||
{
|
||||
@@ -32,12 +33,16 @@ namespace OpenRa.Game
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
if (!hasOverlay)
|
||||
return;
|
||||
if (!hasOverlay) return;
|
||||
|
||||
var bi = (UnitInfo.BuildingInfo)Rules.UnitInfo[name];
|
||||
|
||||
var maxDistance = bi.Adjacent + 2; /* real-ra is weird. this is 1 GAP. */
|
||||
var tooFarFromBase = !Footprint.Tiles(bi, position).Any(
|
||||
t => Game.GetDistanceToBase(t, Game.LocalPlayer) < maxDistance);
|
||||
|
||||
foreach( var t in Footprint.Tiles( bi, position ) )
|
||||
spriteRenderer.DrawSprite( Game.IsCellBuildable( t, bi.WaterBound
|
||||
spriteRenderer.DrawSprite( !tooFarFromBase && Game.IsCellBuildable( t, bi.WaterBound
|
||||
? UnitMovementType.Float : UnitMovementType.Wheel )
|
||||
? buildOk : buildBlocked, Game.CellSize * t, 0 );
|
||||
|
||||
|
||||
Reference in New Issue
Block a user