Merge pull request #3861 from pchote/spatialbins-cleanup

Overhaul spatial caching
This commit is contained in:
Chris Forbes
2013-09-28 19:53:31 -07:00
59 changed files with 634 additions and 423 deletions

View File

@@ -24,13 +24,11 @@ namespace OpenRA
public readonly World World;
public readonly uint ActorID;
public Lazy<Rectangle> Bounds;
Lazy<IOccupySpace> occupySpace;
Lazy<IFacing> facing;
public Cached<Rectangle> Bounds;
public Cached<Rectangle> ExtendedBounds;
public IOccupySpace OccupiesSpace { get { return occupySpace.Value; } }
public CPos Location { get { return occupySpace.Value.TopLeft; } }
@@ -76,27 +74,25 @@ namespace OpenRA
facing = Lazy.New(() => TraitOrDefault<IFacing>());
size = Lazy.New(() =>
{
var si = Info.Traits.GetOrDefault<SelectableInfo>();
if (si != null && si.Bounds != null)
return new int2(si.Bounds[0], si.Bounds[1]);
return TraitsImplementing<IAutoSelectionSize>().Select(x => x.SelectionSize(this)).FirstOrDefault();
});
applyIRender = (x, wr) => x.Render(this, wr);
applyRenderModifier = (m, p, wr) => p.ModifyRender(this, wr, m);
Bounds = Cached.New(() => CalculateBounds(false));
ExtendedBounds = Cached.New(() => CalculateBounds(true));
Bounds = Lazy.New(() =>
{
var si = Info.Traits.GetOrDefault<SelectableInfo>();
var size = (si != null && si.Bounds != null) ? new int2(si.Bounds[0], si.Bounds[1]) :
TraitsImplementing<IAutoSelectionSize>().Select(x => x.SelectionSize(this)).FirstOrDefault();
var offset = -size / 2;
if (si != null && si.Bounds != null && si.Bounds.Length > 2)
offset += new int2(si.Bounds[2], si.Bounds[3]);
return new Rectangle(offset.X, offset.Y, size.X, size.Y);
});
}
public void Tick()
{
Bounds.Invalidate();
ExtendedBounds.Invalidate();
currentActivity = Traits.Util.RunActivity(this, currentActivity);
}
@@ -105,8 +101,6 @@ namespace OpenRA
get { return currentActivity == null; }
}
OpenRA.FileFormats.Lazy<int2> size;
// note: these delegates are cached to avoid massive allocation.
Func<IRender, WorldRenderer, IEnumerable<IRenderable>> applyIRender;
Func<IEnumerable<IRenderable>, IRenderModifier, WorldRenderer, IEnumerable<IRenderable>> applyRenderModifier;
@@ -117,34 +111,6 @@ namespace OpenRA
return mods.Aggregate(sprites, (m, p) => applyRenderModifier(m, p, wr));
}
// When useAltitude = true, the bounding box is extended
// vertically to altitude = 0 to support FindUnitsInCircle queries
// When false, the bounding box is given for the actor
// at its current altitude
Rectangle CalculateBounds(bool useAltitude)
{
var sizeVector = (PVecInt)size.Value;
var loc = CenterLocation - sizeVector / 2;
var si = Info.Traits.GetOrDefault<SelectableInfo>();
if (si != null && si.Bounds != null && si.Bounds.Length > 2)
{
loc += new PVecInt(si.Bounds[2], si.Bounds[3]);
}
var ios = occupySpace.Value;
if (ios != null)
{
var altitude = ios.CenterPosition.Z * Game.CellSize / 1024;
loc -= new PVecInt(0, altitude);
if (useAltitude)
sizeVector = new PVecInt(sizeVector.X, sizeVector.Y + altitude);
}
return new Rectangle(loc.X, loc.Y, sizeVector.X, sizeVector.Y);
}
public bool IsInWorld { get; internal set; }
public void QueueActivity(bool queued, Activity nextActivity)

View File

@@ -10,6 +10,7 @@
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Traits;
namespace OpenRA
{

View File

@@ -1,122 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA
{
public enum SubCell { FullCell, TopLeft, TopRight, Center, BottomLeft, BottomRight }
public class ActorMap
{
class InfluenceNode
{
public InfluenceNode next;
public SubCell subCell;
public Actor actor;
}
InfluenceNode[,] influence;
Map map;
public ActorMap( World world )
{
map = world.Map;
influence = new InfluenceNode[world.Map.MapSize.X, world.Map.MapSize.Y];
world.ActorAdded += a => Add( a, a.OccupiesSpace );
world.ActorRemoved += a => Remove( a, a.OccupiesSpace );
}
public IEnumerable<Actor> GetUnitsAt(CPos a)
{
if (!map.IsInMap(a)) yield break;
for( var i = influence[ a.X, a.Y ] ; i != null ; i = i.next )
if (!i.actor.Destroyed)
yield return i.actor;
}
public IEnumerable<Actor> GetUnitsAt(CPos a, SubCell sub)
{
if (!map.IsInMap(a)) yield break;
for( var i = influence[ a.X, a.Y ] ; i != null ; i = i.next )
if (!i.actor.Destroyed && (i.subCell == sub || i.subCell == SubCell.FullCell))
yield return i.actor;
}
public bool HasFreeSubCell(CPos a)
{
if (!AnyUnitsAt(a))
return true;
return new[]{ SubCell.TopLeft, SubCell.TopRight, SubCell.Center,
SubCell.BottomLeft, SubCell.BottomRight }.Any(b => !AnyUnitsAt(a,b));
}
public SubCell? FreeSubCell(CPos a)
{
if (!HasFreeSubCell(a))
return null;
return new[]{ SubCell.TopLeft, SubCell.TopRight, SubCell.Center,
SubCell.BottomLeft, SubCell.BottomRight }.First(b => !AnyUnitsAt(a,b));
}
public bool AnyUnitsAt(CPos a)
{
return influence[ a.X, a.Y ] != null;
}
public bool AnyUnitsAt(CPos a, SubCell sub)
{
for( var i = influence[ a.X, a.Y ] ; i != null ; i = i.next )
if (i.subCell == sub || i.subCell == SubCell.FullCell)
return true;
return false;
}
public void Add( Actor self, IOccupySpace unit )
{
if (unit != null)
foreach( var c in unit.OccupiedCells() )
influence[ c.First.X, c.First.Y ] = new InfluenceNode { next = influence[ c.First.X, c.First.Y ], subCell = c.Second, actor = self };
}
public void Remove( Actor self, IOccupySpace unit )
{
if (unit != null)
foreach (var c in unit.OccupiedCells())
RemoveInner( ref influence[ c.First.X, c.First.Y ], self );
}
void RemoveInner( ref InfluenceNode influenceNode, Actor toRemove )
{
if( influenceNode == null )
return;
else if( influenceNode.actor == toRemove )
influenceNode = influenceNode.next;
if (influenceNode != null)
RemoveInner( ref influenceNode.next, toRemove );
}
public void Update(Actor self, IOccupySpace unit)
{
Remove(self, unit);
if (!self.IsDead()) Add(self, unit);
}
}
}

View File

@@ -213,8 +213,11 @@ namespace OpenRA
viewport = new Viewport(new int2(Renderer.Resolution), map.Bounds, Renderer);
orderManager.world = new World(modData.Manifest, map, orderManager, isShellmap);
worldRenderer = new WorldRenderer(orderManager.world);
orderManager.world.LoadComplete(worldRenderer);
if (orderManager.GameStarted)
return;
if (orderManager.GameStarted) return;
Ui.MouseFocusWidget = null;
Ui.KeyboardFocusWidget = null;

View File

@@ -73,12 +73,11 @@ namespace OpenRA.Graphics
List<IRenderable> GenerateRenderables()
{
var bounds = Game.viewport.WorldBounds(world);
var comparer = new RenderableComparer(this);
var tl = bounds.TopLeftAsCPos();
var br = bounds.BottomRightAsCPos();
var actors = world.FindActorsInBox(tl, br)
var vb = Game.viewport.ViewBounds(world);
var tl = Game.viewport.ViewToWorldPx(new int2(vb.Left, vb.Top)).ToInt2();
var br = Game.viewport.ViewToWorldPx(new int2(vb.Right, vb.Bottom)).ToInt2();
var actors = world.ScreenMap.ActorsInBox(tl, br)
.Append(world.WorldActor)
.ToList();
@@ -151,14 +150,15 @@ namespace OpenRA.Graphics
Game.Renderer.Flush();
}
public void DrawSelectionBox(Actor selectedUnit, Color c)
public void DrawSelectionBox(Actor a, Color c)
{
var bounds = selectedUnit.Bounds.Value;
var pos = ScreenPxPosition(a.CenterPosition);
var bounds = a.Bounds.Value;
var xy = new float2(bounds.Left, bounds.Top);
var Xy = new float2(bounds.Right, bounds.Top);
var xY = new float2(bounds.Left, bounds.Bottom);
var XY = new float2(bounds.Right, bounds.Bottom);
var xy = pos + new float2(bounds.Left, bounds.Top);
var Xy = pos + new float2(bounds.Right, bounds.Top);
var xY = pos + new float2(bounds.Left, bounds.Bottom);
var XY = pos + new float2(bounds.Right, bounds.Bottom);
var wlr = Game.Renderer.WorldLineRenderer;
wlr.DrawLine(xy, xy + new float2(4, 0), c, c);

View File

@@ -83,7 +83,6 @@
<ItemGroup>
<Compile Include="Actor.cs" />
<Compile Include="ActorInitializer.cs" />
<Compile Include="ActorMap.cs" />
<Compile Include="ActorReference.cs" />
<Compile Include="Graphics\QuadRenderer.cs" />
<Compile Include="PVecInt.cs" />
@@ -185,7 +184,6 @@
<Compile Include="Traits\World\ResourceType.cs" />
<Compile Include="Traits\World\ScreenShaker.cs" />
<Compile Include="Traits\World\Shroud.cs" />
<Compile Include="Traits\World\SpatialBins.cs" />
<Compile Include="Widgets\BackgroundWidget.cs" />
<Compile Include="Widgets\ButtonWidget.cs" />
<Compile Include="Widgets\ChatDisplayWidget.cs" />
@@ -238,6 +236,8 @@
<Compile Include="Graphics\Theater.cs" />
<Compile Include="Traits\Player\PlayerColorPalette.cs" />
<Compile Include="Traits\Player\PlayerHighlightPalette.cs" />
<Compile Include="Traits\World\ScreenMap.cs" />
<Compile Include="Traits\World\ActorMap.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">

View File

@@ -19,8 +19,8 @@ namespace OpenRA.Orders
{
public IEnumerable<Order> Order(World world, CPos xy, MouseInput mi)
{
var underCursor = world.FindUnitsAtMouse(mi.Location)
.Where(a => a.HasTrait<ITargetable>())
var underCursor = world.ScreenMap.ActorsAt(Game.viewport.ViewToWorldPx(mi.Location))
.Where(a => !world.FogObscures(a) && a.HasTrait<ITargetable>())
.OrderByDescending(a => a.Info.SelectionPriority())
.FirstOrDefault();
@@ -59,10 +59,9 @@ namespace OpenRA.Orders
public string GetCursor(World world, CPos xy, MouseInput mi)
{
bool useSelect = false;
var underCursor = world.FindUnitsAtMouse(mi.Location)
.Where(a => a.HasTrait<ITargetable>())
var useSelect = false;
var underCursor = world.ScreenMap.ActorsAt(Game.viewport.ViewToWorldPx(mi.Location))
.Where(a => !world.FogObscures(a) && a.HasTrait<ITargetable>())
.OrderByDescending(a => a.Info.SelectionPriority())
.FirstOrDefault();

View File

@@ -20,10 +20,7 @@ namespace OpenRA.Traits
{
public class FrozenActorLayerInfo : ITraitInfo
{
[Desc("Size of partition bins (screen pixels)")]
public readonly int BinSize = 250;
public object Create(ActorInitializer init) { return new FrozenActorLayer(init.world, this); }
public object Create(ActorInitializer init) { return new FrozenActorLayer(init.self); }
}
public class FrozenActor
@@ -98,34 +95,21 @@ namespace OpenRA.Traits
[Sync] public int VisibilityHash;
[Sync] public int FrozenHash;
readonly FrozenActorLayerInfo info;
readonly World world;
readonly Player owner;
Dictionary<uint, FrozenActor> frozen;
List<FrozenActor>[,] bins;
public FrozenActorLayer(World world, FrozenActorLayerInfo info)
public FrozenActorLayer(Actor self)
{
this.info = info;
world = self.World;
owner = self.Owner;
frozen = new Dictionary<uint, FrozenActor>();
bins = new List<FrozenActor>[
world.Map.MapSize.X * Game.CellSize / info.BinSize,
world.Map.MapSize.Y * Game.CellSize / info.BinSize];
for (var j = 0; j <= bins.GetUpperBound(1); j++)
for (var i = 0; i <= bins.GetUpperBound(0); i++)
bins[i, j] = new List<FrozenActor>();
}
public void Add(FrozenActor fa)
{
frozen.Add(fa.ID, fa);
var top = (int)Math.Max(0, fa.Bounds.Top / info.BinSize);
var left = (int)Math.Max(0, fa.Bounds.Left / info.BinSize);
var bottom = (int)Math.Min(bins.GetUpperBound(1), fa.Bounds.Bottom / info.BinSize);
var right = (int)Math.Min(bins.GetUpperBound(0), fa.Bounds.Right / info.BinSize);
for (var j = top; j <= bottom; j++)
for (var i = left; i <= right; i++)
bins[i, j].Add(fa);
world.ScreenMap.Add(owner, fa);
}
public void Tick(Actor self)
@@ -148,8 +132,7 @@ namespace OpenRA.Traits
foreach (var r in remove)
{
foreach (var bin in bins)
bin.Remove(frozen[r]);
world.ScreenMap.Remove(owner, frozen[r]);
frozen.Remove(r);
}
}
@@ -161,13 +144,6 @@ namespace OpenRA.Traits
.SelectMany(ff => ff.Render(wr));
}
public IEnumerable<FrozenActor> FrozenActorsAt(int2 pxPos)
{
var x = (pxPos.X / info.BinSize).Clamp(0, bins.GetUpperBound(0));
var y = (pxPos.Y / info.BinSize).Clamp(0, bins.GetUpperBound(1));
return bins[x, y].Where(p => p.Bounds.Contains(pxPos) && p.IsValid);
}
public FrozenActor FromID(uint id)
{
FrozenActor ret;

View File

@@ -40,7 +40,9 @@ namespace OpenRA.Traits
if (!Info.Selectable)
return;
var pos = wr.ScreenPxPosition(self.CenterPosition);
var bounds = self.Bounds.Value;
bounds.Offset(pos.X, pos.Y);
var xy = new float2(bounds.Left, bounds.Top);
var Xy = new float2(bounds.Right, bounds.Top);
@@ -56,7 +58,9 @@ namespace OpenRA.Traits
if (!Info.Selectable)
return;
var pos = wr.ScreenPxPosition(self.CenterPosition);
var bounds = self.Bounds.Value;
bounds.Offset(pos.X, pos.Y);
var xy = new float2(bounds.Left, bounds.Top);
var Xy = new float2(bounds.Right, bounds.Top);

View File

@@ -34,7 +34,10 @@ namespace OpenRA.Traits
if (self.World.FogObscures(self))
return;
var pos = wr.ScreenPxPosition(self.CenterPosition);
var bounds = self.Bounds.Value;
bounds.Offset(pos.X, pos.Y);
var xy = new float2(bounds.Left, bounds.Top);
var xY = new float2(bounds.Left, bounds.Bottom);

View File

@@ -174,7 +174,7 @@ namespace OpenRA.Traits
public interface UsesInit<T> where T : IActorInit { }
public interface INotifySelection { void SelectionChanged(); }
public interface IWorldLoaded { void WorldLoaded(World w); }
public interface IWorldLoaded { void WorldLoaded(World w, WorldRenderer wr); }
public interface ICreatePlayers { void CreatePlayers(World w); }
public interface IBotInfo { string Name { get; } }

View File

@@ -18,9 +18,9 @@ namespace OpenRA.Traits
public object Create( ActorInitializer init ) { return new Waypoint( init ); }
}
class Waypoint : IOccupySpace, ISync
class Waypoint : IOccupySpace, ISync, INotifyAddedToWorld, INotifyRemovedFromWorld
{
[Sync] CPos location;
[Sync] readonly CPos location;
public Waypoint(ActorInitializer init)
{
@@ -30,5 +30,19 @@ namespace OpenRA.Traits
public CPos TopLeft { get { return location; } }
public IEnumerable<Pair<CPos, SubCell>> OccupiedCells() { yield break; }
public WPos CenterPosition { get { return location.CenterPosition; } }
public void AddedToWorld(Actor self)
{
self.World.ActorMap.AddInfluence(self, this);
self.World.ActorMap.AddPosition(self, this);
self.World.ScreenMap.Add(self);
}
public void RemovedFromWorld(Actor self)
{
self.World.ActorMap.RemoveInfluence(self, this);
self.World.ActorMap.RemovePosition(self, this);
self.World.ScreenMap.Remove(self);
}
}
}

View File

@@ -0,0 +1,180 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Traits;
namespace OpenRA.Traits
{
public enum SubCell { FullCell, TopLeft, TopRight, Center, BottomLeft, BottomRight }
public class ActorMapInfo : ITraitInfo
{
[Desc("Size of partition bins (cells)")]
public readonly int BinSize = 10;
public object Create(ActorInitializer init) { return new ActorMap(init.world, this); }
}
public class ActorMap
{
class InfluenceNode
{
public InfluenceNode next;
public SubCell subCell;
public Actor actor;
}
readonly ActorMapInfo info;
readonly Map map;
InfluenceNode[,] influence;
List<Actor>[] actors;
int rows, cols;
public ActorMap(World world, ActorMapInfo info)
{
this.info = info;
map = world.Map;
influence = new InfluenceNode[world.Map.MapSize.X, world.Map.MapSize.Y];
cols = world.Map.MapSize.X / info.BinSize + 1;
rows = world.Map.MapSize.Y / info.BinSize + 1;
actors = new List<Actor>[rows * cols];
for (var j = 0; j < rows; j++)
for (var i = 0; i < cols; i++)
actors[j * cols + i] = new List<Actor>();
}
public IEnumerable<Actor> GetUnitsAt(CPos a)
{
if (!map.IsInMap(a))
yield break;
for (var i = influence[a.X, a.Y]; i != null; i = i.next)
if (!i.actor.Destroyed)
yield return i.actor;
}
public IEnumerable<Actor> GetUnitsAt(CPos a, SubCell sub)
{
if (!map.IsInMap(a))
yield break;
for (var i = influence[a.X, a.Y]; i != null; i = i.next)
if (!i.actor.Destroyed && (i.subCell == sub || i.subCell == SubCell.FullCell))
yield return i.actor;
}
public bool HasFreeSubCell(CPos a)
{
if (!AnyUnitsAt(a))
return true;
return new[] { SubCell.TopLeft, SubCell.TopRight, SubCell.Center,
SubCell.BottomLeft, SubCell.BottomRight }.Any(b => !AnyUnitsAt(a,b));
}
public SubCell? FreeSubCell(CPos a)
{
if (!HasFreeSubCell(a))
return null;
return new[] { SubCell.TopLeft, SubCell.TopRight, SubCell.Center,
SubCell.BottomLeft, SubCell.BottomRight }.First(b => !AnyUnitsAt(a,b));
}
public bool AnyUnitsAt(CPos a)
{
return influence[a.X, a.Y] != null;
}
public bool AnyUnitsAt(CPos a, SubCell sub)
{
for (var i = influence[a.X, a.Y]; i != null; i = i.next)
if (i.subCell == sub || i.subCell == SubCell.FullCell)
return true;
return false;
}
public void AddInfluence(Actor self, IOccupySpace ios)
{
foreach (var c in ios.OccupiedCells())
influence[c.First.X, c.First.Y] = new InfluenceNode { next = influence[c.First.X, c.First.Y], subCell = c.Second, actor = self };
}
public void RemoveInfluence(Actor self, IOccupySpace ios)
{
foreach (var c in ios.OccupiedCells())
RemoveInfluenceInner(ref influence[c.First.X, c.First.Y], self);
}
void RemoveInfluenceInner(ref InfluenceNode influenceNode, Actor toRemove)
{
if (influenceNode == null)
return;
else if (influenceNode.actor == toRemove)
influenceNode = influenceNode.next;
if (influenceNode != null)
RemoveInfluenceInner(ref influenceNode.next, toRemove);
}
public void AddPosition(Actor a, IOccupySpace ios)
{
var pos = ios.CenterPosition;
var i = (pos.X / info.BinSize).Clamp(0, cols - 1);
var j = (pos.Y / info.BinSize).Clamp(0, rows - 1);
actors[j*cols + i].Add(a);
}
public void RemovePosition(Actor a, IOccupySpace ios)
{
foreach (var bin in actors)
bin.Remove(a);
}
public void UpdatePosition(Actor a, IOccupySpace ios)
{
RemovePosition(a, ios);
AddPosition(a, ios);
}
public IEnumerable<Actor> ActorsInBox(WPos a, WPos b)
{
var left = Math.Min(a.X, b.X);
var top = Math.Min(a.Y, b.Y);
var right = Math.Max(a.X, b.X);
var bottom = Math.Max(a.Y, b.Y);
var i1 = (left / info.BinSize).Clamp(0, cols - 1);
var i2 = (right / info.BinSize).Clamp(0, cols - 1);
var j1 = (top / info.BinSize).Clamp(0, rows - 1);
var j2 = (bottom / info.BinSize).Clamp(0, rows - 1);
for (var j = j1; j <= j2; j++)
{
for (var i = i1; i <= i2; i++)
{
foreach (var actor in actors[j*cols + i])
{
var c = actor.CenterPosition;
if (left <= c.X && c.X <= right && top <= c.Y && c.Y <= bottom)
yield return actor;
}
}
}
}
}
}

View File

@@ -57,7 +57,7 @@ namespace OpenRA.Traits
}
}
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
this.world = w;
content = new CellContents[w.Map.MapSize.X, w.Map.MapSize.Y];

View File

@@ -0,0 +1,159 @@
#region Copyright & License Information
/*
* Copyright 2007-2013 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Traits
{
public class ScreenMapInfo : ITraitInfo
{
[Desc("Size of partition bins (world pixels)")]
public readonly int BinSize = 250;
public object Create(ActorInitializer init) { return new ScreenMap(init.world, this); }
}
public class ScreenMap : IWorldLoaded
{
ScreenMapInfo info;
WorldRenderer worldRenderer;
Cache<Player, Dictionary<FrozenActor, Rectangle>[]> frozen;
Dictionary<Actor, Rectangle>[] actors;
int rows, cols;
public ScreenMap(World world, ScreenMapInfo info)
{
this.info = info;
cols = world.Map.MapSize.X * Game.CellSize / info.BinSize + 1;
rows = world.Map.MapSize.Y * Game.CellSize / info.BinSize + 1;
frozen = new Cache<Player, Dictionary<FrozenActor, Rectangle>[]>(InitializeFrozenActors);
actors = new Dictionary<Actor, Rectangle>[rows * cols];
for (var j = 0; j < rows; j++)
for (var i = 0; i < cols; i++)
actors[j * cols + i] = new Dictionary<Actor, Rectangle>();
}
public void WorldLoaded(World w, WorldRenderer wr) { worldRenderer = wr; }
Dictionary<FrozenActor, Rectangle>[] InitializeFrozenActors(Player p)
{
var f = new Dictionary<FrozenActor, Rectangle>[rows * cols];
for (var j = 0; j < rows; j++)
for (var i = 0; i < cols; i++)
f[j * cols + i] = new Dictionary<FrozenActor, Rectangle>();
return f;
}
public void Add(Player viewer, FrozenActor fa)
{
var pos = worldRenderer.ScreenPxPosition(fa.CenterPosition);
var bounds = fa.Bounds;
bounds.Offset(pos.X, pos.Y);
var top = Math.Max(0, bounds.Top / info.BinSize);
var left = Math.Max(0, bounds.Left / info.BinSize);
var bottom = Math.Min(rows - 1, bounds.Bottom / info.BinSize);
var right = Math.Min(cols - 1, bounds.Right / info.BinSize);
for (var j = top; j <= bottom; j++)
for (var i = left; i <= right; i++)
frozen[viewer][j*cols + i].Add(fa, bounds);
}
public void Remove(Player viewer, FrozenActor fa)
{
foreach (var bin in frozen[viewer])
bin.Remove(fa);
}
public void Add(Actor a)
{
var pos = worldRenderer.ScreenPxPosition(a.CenterPosition);
var bounds = a.Bounds.Value;
bounds.Offset(pos.X, pos.Y);
var top = Math.Max(0, bounds.Top / info.BinSize);
var left = Math.Max(0, bounds.Left / info.BinSize);
var bottom = Math.Min(rows - 1, bounds.Bottom / info.BinSize);
var right = Math.Min(cols - 1, bounds.Right / info.BinSize);
for (var j = top; j <= bottom; j++)
for (var i = left; i <= right; i++)
actors[j * cols + i].Add(a, bounds);
}
public void Remove(Actor a)
{
foreach (var bin in actors)
bin.Remove(a);
}
public void Update(Actor a)
{
Remove(a);
Add(a);
}
public IEnumerable<FrozenActor> FrozenActorsAt(Player viewer, int2 pxPos)
{
var i = (pxPos.X / info.BinSize).Clamp(0, cols - 1);
var j = (pxPos.Y / info.BinSize).Clamp(0, rows - 1);
return frozen[viewer][j*cols + i]
.Where(kv => kv.Key.IsValid && kv.Value.Contains(pxPos))
.Select(kv => kv.Key);
}
public IEnumerable<Actor> ActorsAt(int2 pxPos)
{
var i = (pxPos.X / info.BinSize).Clamp(0, cols - 1);
var j = (pxPos.Y / info.BinSize).Clamp(0, rows - 1);
return actors[j * cols + i]
.Where(kv => kv.Key.IsInWorld && kv.Value.Contains(pxPos))
.Select(kv => kv.Key);
}
// Legacy fallback
public IEnumerable<Actor> ActorsAt(PPos pxPos) { return ActorsAt(pxPos.ToInt2()); }
public IEnumerable<Actor> ActorsInBox(int2 a, int2 b)
{
return ActorsInBox(Rectangle.FromLTRB(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y), Math.Max(a.X, b.X), Math.Max(a.Y, b.Y)));
}
public IEnumerable<Actor> ActorsInBox(Rectangle r)
{
var left = (r.Left / info.BinSize).Clamp(0, cols - 1);
var right = (r.Right / info.BinSize).Clamp(0, cols - 1);
var top = (r.Top / info.BinSize).Clamp(0, rows - 1);
var bottom = (r.Bottom / info.BinSize).Clamp(0, rows - 1);
for (var j = top; j <= bottom; j++)
{
for (var i = left; i <= right; i++)
{
var ret = actors[j * cols + i]
.Where(kv => kv.Key.IsInWorld && kv.Value.IntersectsWith(r))
.Select(kv => kv.Key);
foreach (var a in ret)
yield return a;
}
}
}
}
}

View File

@@ -1,87 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2011 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace OpenRA.Traits
{
class SpatialBinsInfo : ITraitInfo
{
public readonly int BinSize = 8;
public object Create(ActorInitializer init) { return new SpatialBins( init.self, this ); }
}
class SpatialBins : ITick
{
List<Actor>[,] bins;
int scale;
public SpatialBins(Actor self, SpatialBinsInfo info)
{
bins = new List<Actor>[
self.World.Map.MapSize.X / info.BinSize,
self.World.Map.MapSize.Y / info.BinSize];
scale = Game.CellSize * info.BinSize;
for (var j = 0; j <= bins.GetUpperBound(1); j++)
for (var i = 0; i <= bins.GetUpperBound(0); i++)
bins[i, j] = new List<Actor>();
}
public void Tick(Actor self)
{
for (var j = 0; j <= bins.GetUpperBound(1); j++)
for (var i = 0; i <= bins.GetUpperBound(0); i++)
bins[i, j].Clear();
foreach (var a in self.World.ActorsWithTrait<IOccupySpace>())
{
var bounds = a.Actor.ExtendedBounds.Value;
if (bounds.Right <= Game.CellSize * self.World.Map.Bounds.Left) continue;
if (bounds.Bottom <= Game.CellSize * self.World.Map.Bounds.Top) continue;
if (bounds.Left >= Game.CellSize * self.World.Map.Bounds.Right) continue;
if (bounds.Top >= Game.CellSize * self.World.Map.Bounds.Bottom) continue;
var i1 = Math.Max(0, bounds.Left / scale);
var i2 = Math.Min(bins.GetUpperBound(0), bounds.Right / scale);
var j1 = Math.Max(0, bounds.Top / scale);
var j2 = Math.Min(bins.GetUpperBound(1), bounds.Bottom / scale);
for (var j = j1; j <= j2; j++)
for (var i = i1; i <= i2; i++)
bins[i, j].Add(a.Actor);
}
}
IEnumerable<Actor> ActorsInBins(int i1, int i2, int j1, int j2)
{
j1 = Math.Max(0, j1); j2 = Math.Min(j2, bins.GetUpperBound(1));
i1 = Math.Max(0, i1); i2 = Math.Min(i2, bins.GetUpperBound(0));
for (var j = j1; j <= j2; j++)
for (var i = i1; i <= i2; i++)
foreach (var a in bins[i, j])
yield return a;
}
public IEnumerable<Actor> ActorsInBox(PPos a, PPos b)
{
var r = Rectangle.FromLTRB(a.X, a.Y, b.X, b.Y);
return ActorsInBins(a.X / scale, b.X / scale, a.Y / scale, b.Y / scale)
.Distinct()
.Where(u => u.IsInWorld && u.ExtendedBounds.Value.IntersectsWith(r));
}
}
}

View File

@@ -103,8 +103,8 @@ namespace OpenRA.Widgets
return;
}
var underCursor = world.FindUnitsAtMouse(Viewport.LastMousePos)
.Where(a => a.HasTrait<IToolTip>())
var underCursor = world.ScreenMap.ActorsAt(Game.viewport.ViewToWorldPx(Viewport.LastMousePos))
.Where(a => !world.FogObscures(a) && a.HasTrait<IToolTip>())
.OrderByDescending(a => a.Info.SelectionPriority())
.FirstOrDefault();

View File

@@ -90,7 +90,7 @@ namespace OpenRA.Widgets
{
if (MultiClick)
{
var unit = SelectActorsInBox(world, xy, xy, _ => true).FirstOrDefault();
var unit = world.ScreenMap.ActorsAt(xy).FirstOrDefault();
var visibleWorld = Game.viewport.ViewBounds(world);
var topLeft = Game.viewport.ViewToWorldPx(new int2(visibleWorld.Left, visibleWorld.Top));
@@ -185,7 +185,7 @@ namespace OpenRA.Widgets
static readonly Actor[] NoActors = {};
IEnumerable<Actor> SelectActorsInBox(World world, PPos a, PPos b, Func<Actor, bool> cond)
{
return world.FindActorsInBox(a.ToWPos(0), b.ToWPos(0))
return world.ScreenMap.ActorsInBox(a.ToInt2(), b.ToInt2())
.Where(x => x.HasTrait<Selectable>() && x.Trait<Selectable>().Info.Selectable && !world.FogObscures(x) && cond(x))
.GroupBy(x => x.GetSelectionPriority())
.OrderByDescending(g => g.Key)

View File

@@ -79,6 +79,7 @@ namespace OpenRA
public readonly Map Map;
public readonly TileSet TileSet;
public readonly ActorMap ActorMap;
public readonly ScreenMap ScreenMap;
public void IssueOrder( Order o ) { orderManager.IssueOrder( o ); } /* avoid exposing the OM to mod code */
@@ -121,8 +122,9 @@ namespace OpenRA
TileSet = Rules.TileSets[Map.Tileset];
SharedRandom = new XRandom(orderManager.LobbyInfo.GlobalSettings.RandomSeed);
WorldActor = CreateActor( "World", new TypeDictionary() );
ActorMap = new ActorMap(this);
WorldActor = CreateActor("World", new TypeDictionary());
ActorMap = WorldActor.Trait<ActorMap>();
ScreenMap = WorldActor.Trait<ScreenMap>();
// Add players
foreach (var cmp in WorldActor.TraitsImplementing<ICreatePlayers>())
@@ -135,8 +137,12 @@ namespace OpenRA
p.Stances[q] = Stance.Neutral;
Sound.SoundVolumeModifier = 1.0f;
}
public void LoadComplete(WorldRenderer wr)
{
foreach (var wlh in WorldActor.TraitsImplementing<IWorldLoaded>())
wlh.WorldLoaded(this);
wlh.WorldLoaded(this, wr);
}
public Actor CreateActor( string name, TypeDictionary initDict )

View File

@@ -21,38 +21,24 @@ namespace OpenRA
{
public static class WorldUtils
{
public static IEnumerable<Actor> FindUnitsAtMouse(this World world, int2 mouseLocation)
{
var loc = Game.viewport.ViewToWorldPx(mouseLocation).ToWPos(0);
return FindActorsInBox(world, loc, loc).Where(a => !world.FogObscures(a));
}
public static readonly IEnumerable<FrozenActor> NoFrozenActors = new FrozenActor[0].AsEnumerable();
public static IEnumerable<FrozenActor> FindFrozenActorsAtMouse(this World world, int2 mouseLocation)
{
if (world.RenderPlayer == null)
return NoFrozenActors;
var frozenLayer = world.RenderPlayer.PlayerActor.TraitOrDefault<FrozenActorLayer>();
if (frozenLayer == null)
return NoFrozenActors;
var loc = Game.viewport.ViewToWorldPx(mouseLocation).ToInt2();
return frozenLayer.FrozenActorsAt(loc);
return world.ScreenMap.FrozenActorsAt(world.RenderPlayer, Game.viewport.ViewToWorldPx(mouseLocation).ToInt2());
}
public static IEnumerable<Actor> FindActorsInBox(this World world, CPos tl, CPos br)
{
// TODO: Support diamond boxes for isometric maps?
return world.FindActorsInBox(tl.TopLeft, br.BottomRight);
}
public static IEnumerable<Actor> FindActorsInBox(this World world, WPos tl, WPos br)
{
var a = PPos.FromWPos(tl);
var b = PPos.FromWPos(br);
var u = PPos.Min(a, b);
var v = PPos.Max(a, b);
return world.WorldActor.Trait<SpatialBins>().ActorsInBox(u,v);
return world.ActorMap.ActorsInBox(tl, br);
}
public static Actor ClosestTo(this IEnumerable<Actor> actors, Actor a)
@@ -73,13 +59,8 @@ namespace OpenRA
// Target ranges are calculated in 2D, so ignore height differences
var vec = new WVec(r, r, WRange.Zero);
var rSq = r.Range*r.Range;
return world.FindActorsInBox(origin - vec, origin + vec).Where(a =>
{
var pos = a.CenterPosition;
var dx = (long)(pos.X - origin.X);
var dy = (long)(pos.Y - origin.Y);
return dx*dx + dy*dy <= rSq;
});
return world.FindActorsInBox(origin - vec, origin + vec).Where(
a => (a.CenterPosition - origin).HorizontalLengthSquared <= rSq);
}
}

View File

@@ -11,6 +11,7 @@
using System;
using System.Collections.Generic;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Move;
using OpenRA.Traits;
@@ -24,7 +25,7 @@ namespace OpenRA.Mods.RA
static CPos viewportOrigin;
Dictionary<string, Actor> actors;
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
var b = w.Map.Bounds;
viewportOrigin = new CPos(b.Left + b.Width / 2, b.Top + b.Height / 2);

View File

@@ -12,6 +12,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Move;
@@ -28,7 +29,7 @@ namespace OpenRA.Mods.Cnc.Missions
Dictionary<string, Actor> actors;
Dictionary<string, Player> players;
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
players = w.Players.ToDictionary(p => p.InternalName);
actors = w.WorldActor.Trait<SpawnMapActors>().Actors;

View File

@@ -12,6 +12,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.Cnc;
using OpenRA.Mods.RA;
using OpenRA.Mods.RA.Activities;
@@ -162,7 +163,7 @@ namespace OpenRA.Mods.Cnc.Missions
nr3.QueueActivity(nr3.Trait<Mobile>().ScriptedMove(nr3.Location - new CVec(0, -5)));
}
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
world = w;
nod = w.Players.Single(p => p.InternalName == "NOD");

View File

@@ -36,7 +36,7 @@ namespace OpenRA.Mods.RA.Air
public int GetInitialFacing() { return InitialFacing; }
}
public class Aircraft : IFacing, IPositionable, ISync, INotifyKilled, IIssueOrder, IOrderVoice
public class Aircraft : IFacing, IPositionable, ISync, INotifyKilled, IIssueOrder, IOrderVoice, INotifyAddedToWorld, INotifyRemovedFromWorld
{
static readonly Pair<CPos, SubCell>[] NoCells = new Pair<CPos, SubCell>[] { };
@@ -101,15 +101,35 @@ namespace OpenRA.Mods.RA.Air
UnReserve();
}
public void SetPosition(Actor self, CPos cell)
public void SetPosition(Actor self, WPos pos)
{
// Changes position, but not altitude
CenterPosition = cell.CenterPosition + new WVec(0, 0, CenterPosition.Z);
CenterPosition = pos;
if (self.IsInWorld)
{
self.World.ScreenMap.Update(self);
self.World.ActorMap.UpdatePosition(self, this);
}
}
public void SetPosition(Actor self, WPos pos) { CenterPosition = pos; }
// Changes position, but not altitude
public void SetPosition(Actor self, CPos cell) { SetPosition(self, cell.CenterPosition + new WVec(0, 0, CenterPosition.Z)); }
public void SetVisualPosition(Actor self, WPos pos) { SetPosition(self, pos); }
public void AddedToWorld(Actor self)
{
self.World.ActorMap.AddInfluence(self, this);
self.World.ActorMap.AddPosition(self, this);
self.World.ScreenMap.Add(self);
}
public void RemovedFromWorld(Actor self)
{
self.World.ActorMap.RemoveInfluence(self, this);
self.World.ActorMap.RemovePosition(self, this);
self.World.ScreenMap.Remove(self);
}
public bool AircraftCanEnter(Actor a)
{
if (self.AppearsHostileTo(a))

View File

@@ -11,6 +11,7 @@
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
@@ -36,7 +37,7 @@ namespace OpenRA.Mods.RA
this.world = self.World;
}
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
Bridges = new Bridge[w.Map.MapSize.X, w.Map.MapSize.Y];

View File

@@ -97,7 +97,7 @@ namespace OpenRA.Mods.RA.Buildings
}
}
public class Building : INotifyDamage, IOccupySpace, INotifyCapture, ISync, ITechTreePrerequisite
public class Building : INotifyDamage, IOccupySpace, INotifyCapture, ISync, ITechTreePrerequisite, INotifyAddedToWorld, INotifyRemovedFromWorld
{
readonly Actor self;
public readonly BuildingInfo Info;
@@ -157,5 +157,19 @@ namespace OpenRA.Mods.RA.Buildings
{
PlayerPower = newOwner.PlayerActor.Trait<PowerManager>();
}
public void AddedToWorld(Actor self)
{
self.World.ActorMap.AddInfluence(self, this);
self.World.ActorMap.AddPosition(self, this);
self.World.ScreenMap.Add(self);
}
public void RemovedFromWorld(Actor self)
{
self.World.ActorMap.RemoveInfluence(self, this);
self.World.ActorMap.RemovePosition(self, this);
self.World.ScreenMap.Remove(self);
}
}
}

View File

@@ -24,7 +24,7 @@ namespace OpenRA.Mods.RA
}
// ITeleportable is required for paradrop
class Crate : ITick, IPositionable, ICrushable, ISync, INotifyParachuteLanded
class Crate : ITick, IPositionable, ICrushable, ISync, INotifyParachuteLanded, INotifyAddedToWorld, INotifyRemovedFromWorld
{
readonly Actor self;
readonly CrateInfo info;
@@ -103,9 +103,7 @@ namespace OpenRA.Mods.RA
public void SetPosition(Actor self, CPos cell)
{
if (self.IsInWorld)
self.World.ActorMap.Remove(self, this);
self.World.ActorMap.RemoveInfluence(self, this);
Location = cell;
CenterPosition = cell.CenterPosition;
@@ -115,12 +113,30 @@ namespace OpenRA.Mods.RA
rs.anim.PlayRepeating(seq);
if (self.IsInWorld)
self.World.ActorMap.Add(self, this);
{
self.World.ActorMap.AddInfluence(self, this);
self.World.ActorMap.UpdatePosition(self, this);
self.World.ScreenMap.Update(self);
}
}
public bool CrushableBy(string[] crushClasses, Player owner)
{
return crushClasses.Contains("crate");
}
public void AddedToWorld(Actor self)
{
self.World.ActorMap.AddInfluence(self, this);
self.World.ActorMap.AddPosition(self, this);
self.World.ScreenMap.Add(self);
}
public void RemovedFromWorld(Actor self)
{
self.World.ActorMap.RemoveInfluence(self, this);
self.World.ActorMap.RemovePosition(self, this);
self.World.ScreenMap.Remove(self);
}
}
}

View File

@@ -117,9 +117,12 @@ namespace OpenRA.Mods.RA
if (self.World.FogObscures(self))
yield break;
var pos = wr.ScreenPxPosition(self.CenterPosition);
var bounds = self.Bounds.Value;
var pos = new PPos(bounds.Right, bounds.Bottom - 2).ToWPos(0);
yield return new SpriteRenderable(RankAnim.Image, pos, WVec.Zero, 0, wr.Palette("effect"), 1f, true);
bounds.Offset(pos.X, pos.Y);
var effectPos = new PPos(bounds.Right, bounds.Bottom - 2).ToWPos(0);
yield return new SpriteRenderable(RankAnim.Image, effectPos, WVec.Zero, 0, wr.Palette("effect"), 1f, true);
}
}

View File

@@ -58,7 +58,7 @@ namespace OpenRA.Mods.RA
yield break;
}
var target = FriendlyGuardableUnitsAtMouse(world, mi).FirstOrDefault();
var target = FriendlyGuardableUnits(world, mi).FirstOrDefault();
if (target == null || subjects.All(s => s.IsDead()))
yield break;
@@ -79,19 +79,19 @@ namespace OpenRA.Mods.RA
public string GetCursor(World world, CPos xy, MouseInput mi)
{
if (world.Map.IsInMap(xy))
{
var targets = FriendlyGuardableUnitsAtMouse(world, mi);
if (targets.Any() && (subjects.Count() > 1 || (subjects.Count() == 1 && subjects.First() != targets.First())))
return "guard";
}
return "move-blocked";
var multiple = subjects.Count() > 1;
var canGuard = FriendlyGuardableUnits(world, mi)
.Any(a => multiple || a != subjects.First());
return canGuard ? "guard" : "move-blocked";
}
static IEnumerable<Actor> FriendlyGuardableUnitsAtMouse(World world, MouseInput mi)
static IEnumerable<Actor> FriendlyGuardableUnits(World world, MouseInput mi)
{
return world.FindUnitsAtMouse(mi.Location)
.Where(a => !a.IsDead() && a.AppearsFriendlyTo(world.LocalPlayer.PlayerActor) && a.HasTrait<Guardable>());
return world.ScreenMap.ActorsAt(Game.viewport.ViewToWorldPx(mi.Location))
.Where(a => !world.FogObscures(a) && !a.IsDead() &&
a.AppearsFriendlyTo(world.LocalPlayer.PlayerActor) &&
a.HasTrait<Guardable>());
}
}

View File

@@ -25,7 +25,7 @@ namespace OpenRA.Mods.RA
public int GetInitialFacing() { return 128; }
}
class Husk : IPositionable, IFacing, ISync
class Husk : IPositionable, IFacing, ISync, INotifyAddedToWorld, INotifyRemovedFromWorld
{
readonly HuskInfo info;
readonly Actor self;
@@ -64,14 +64,34 @@ namespace OpenRA.Mods.RA
}
public void SetPosition(Actor self, CPos cell) { SetPosition(self, cell.CenterPosition); }
public void SetVisualPosition(Actor self, WPos pos) { CenterPosition = pos; }
public void SetVisualPosition(Actor self, WPos pos)
{
CenterPosition = pos;
self.World.ScreenMap.Update(self);
}
public void SetPosition(Actor self, WPos pos)
{
self.World.ActorMap.Remove(self, this);
self.World.ActorMap.RemoveInfluence(self, this);
CenterPosition = pos;
TopLeft = pos.ToCPos();
self.World.ActorMap.Add(self, this);
self.World.ActorMap.AddInfluence(self, this);
self.World.ActorMap.UpdatePosition(self, this);
self.World.ScreenMap.Update(self);
}
public void AddedToWorld(Actor self)
{
self.World.ActorMap.AddInfluence(self, this);
self.World.ActorMap.AddPosition(self, this);
self.World.ScreenMap.Add(self);
}
public void RemovedFromWorld(Actor self)
{
self.World.ActorMap.RemoveInfluence(self, this);
self.World.ActorMap.RemovePosition(self, this);
self.World.ScreenMap.Remove(self);
}
}

View File

@@ -8,6 +8,7 @@
*/
#endregion
using OpenRA.Graphics;
using OpenRA.Traits;
using OpenRA.Widgets;
@@ -28,7 +29,7 @@ namespace OpenRA.Mods.RA
this.Info = Info;
}
public void WorldLoaded(World world)
public void WorldLoaded(World world, WorldRenderer wr)
{
// Clear any existing widget state
if (Info.ClearRoot)

View File

@@ -12,6 +12,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Network;
using OpenRA.Traits;
@@ -26,7 +27,7 @@ namespace OpenRA.Mods.RA
{
public Dictionary<Player, CPos> Start = new Dictionary<Player, CPos>();
public void WorldLoaded(World world)
public void WorldLoaded(World world, WorldRenderer wr)
{
var taken = world.LobbyInfo.Clients.Where(c => c.SpawnPoint != 0 && c.Slot != null)
.Select(c => (CPos) world.Map.GetSpawnPoints()[c.SpawnPoint-1]).ToList();

View File

@@ -26,7 +26,7 @@ namespace OpenRA.Mods.RA
public object Create(ActorInitializer init) { return new Mine(init, this); }
}
class Mine : ICrushable, IOccupySpace, ISync
class Mine : ICrushable, IOccupySpace, ISync, INotifyAddedToWorld, INotifyRemovedFromWorld
{
readonly Actor self;
readonly MineInfo info;
@@ -62,6 +62,20 @@ namespace OpenRA.Mods.RA
public IEnumerable<Pair<CPos, SubCell>> OccupiedCells() { yield return Pair.New(TopLeft, SubCell.FullCell); }
public WPos CenterPosition { get { return location.CenterPosition; } }
public void AddedToWorld(Actor self)
{
self.World.ActorMap.AddInfluence(self, this);
self.World.ActorMap.AddPosition(self, this);
self.World.ScreenMap.Add(self);
}
public void RemovedFromWorld(Actor self)
{
self.World.ActorMap.RemoveInfluence(self, this);
self.World.ActorMap.RemovePosition(self, this);
self.World.ScreenMap.Remove(self);
}
}
/* tag trait for stuff that shouldnt trigger mines */

View File

@@ -106,7 +106,8 @@ namespace OpenRA.Mods.RA
yield break;
}
var underCursor = world.FindUnitsAtMouse(mi.Location)
var underCursor = world.ScreenMap.ActorsAt(Game.viewport.ViewToWorldPx(mi.Location))
.Where(a => !world.FogObscures(a))
.OrderByDescending(a => a.Info.Traits.Contains<SelectableInfo>()
? a.Info.Traits.Get<SelectableInfo>().Priority : int.MinValue)
.FirstOrDefault();

View File

@@ -12,6 +12,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Air;
using OpenRA.Mods.RA.Move;
@@ -274,7 +275,7 @@ namespace OpenRA.Mods.RA.Missions
actor.Trait<AutoTarget>().stance = UnitStance.Defend;
}
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
world = w;

View File

@@ -12,6 +12,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Air;
using OpenRA.Mods.RA.Buildings;
@@ -270,7 +271,7 @@ namespace OpenRA.Mods.RA.Missions
{
new LocationInit(yakEntryPoint.Location),
new OwnerInit(soviets),
new FacingInit(Util.GetFacing(yakAttackPoint.Location - yakEntryPoint.Location, 0)),
new FacingInit(Traits.Util.GetFacing(yakAttackPoint.Location - yakEntryPoint.Location, 0)),
new AltitudeInit(Rules.Info[YakName].Traits.Get<PlaneInfo>().CruiseAltitude)
});
}
@@ -405,7 +406,7 @@ namespace OpenRA.Mods.RA.Missions
unit.QueueActivity(new AttackMove.AttackMoveActivity(unit, new Move.Move(townPoint.Location, SovietTownMoveNearEnough)));
}
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
world = w;

View File

@@ -13,6 +13,7 @@ using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Air;
using OpenRA.Mods.RA.Buildings;
@@ -335,7 +336,7 @@ namespace OpenRA.Mods.RA.Missions
{
new OwnerInit(owner),
new LocationInit(entry),
new FacingInit(Util.GetFacing(to - entry, 0))
new FacingInit(Traits.Util.GetFacing(to - entry, 0))
});
unit.QueueActivity(new Move.Move(to));
return unit;
@@ -380,7 +381,7 @@ namespace OpenRA.Mods.RA.Missions
}
}
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
world = w;

View File

@@ -13,6 +13,7 @@ using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Buildings;
using OpenRA.Mods.RA.Move;
@@ -350,7 +351,7 @@ namespace OpenRA.Mods.RA.Missions
actor.Trait<AutoTarget>().stance = UnitStance.Defend;
}
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
world = w;

View File

@@ -11,6 +11,7 @@
using System;
using System.Collections.Generic;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA.Air;
using OpenRA.Traits;
@@ -23,7 +24,7 @@ namespace OpenRA.Mods.RA
Dictionary<string, Actor> Actors;
static CPos ViewportOrigin;
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
var b = w.Map.Bounds;
ViewportOrigin = new CPos(b.Left + b.Width/2, b.Top + b.Height/2);

View File

@@ -11,6 +11,7 @@
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Air;
using OpenRA.Mods.RA.Buildings;
@@ -90,7 +91,7 @@ namespace OpenRA.Mods.RA.Missions
{
var actor = OffmapAttackers.Random(world.SharedRandom);
var spawn = offmapAttackerSpawns.Random(world.SharedRandom);
var u = world.CreateActor(actor, soviets, spawn.Location, Util.GetFacing(attackLocation.Location - spawn.Location, 0));
var u = world.CreateActor(actor, soviets, spawn.Location, Traits.Util.GetFacing(attackLocation.Location - spawn.Location, 0));
var cargo = u.TraitOrDefault<Cargo>();
if (cargo != null)
{
@@ -176,7 +177,7 @@ namespace OpenRA.Mods.RA.Missions
{
foreach (var tank in HeavyTanks)
{
var u = world.CreateActor(tank, soviets, heavyTankSpawn.Location, Util.GetFacing(heavyTankWP.Location - heavyTankSpawn.Location, 0));
var u = world.CreateActor(tank, soviets, heavyTankSpawn.Location, Traits.Util.GetFacing(heavyTankWP.Location - heavyTankSpawn.Location, 0));
u.QueueActivity(new AttackMove.AttackMoveActivity(u, new Move.Move(heavyTankWP.Location, 0)));
}
ironCurtain.Trait<IronCurtainPower>().Activate(ironCurtain, new Order { TargetLocation = heavyTankSpawn.Location });
@@ -187,7 +188,7 @@ namespace OpenRA.Mods.RA.Missions
var chronoInfo = new List<Pair<Actor, CPos>>();
foreach (var tank in MediumTanks.Select((x, i) => new { x, i }))
{
var u = world.CreateActor(tank.x, allies, mediumTankChronoSpawn.Location, Util.GetFacing(heavyTankWP.Location - mediumTankChronoSpawn.Location, 0));
var u = world.CreateActor(tank.x, allies, mediumTankChronoSpawn.Location, Traits.Util.GetFacing(heavyTankWP.Location - mediumTankChronoSpawn.Location, 0));
chronoInfo.Add(Pair.New(u, new CPos(mediumTankChronoSpawn.Location.X + tank.i, mediumTankChronoSpawn.Location.Y)));
}
RASpecialPowers.Chronoshift(world, chronoInfo, chronosphere, -1, false);
@@ -201,7 +202,7 @@ namespace OpenRA.Mods.RA.Missions
{
new OwnerInit(soviets),
new LocationInit(waypoints[0]),
new FacingInit(Util.GetFacing(waypoints[1] - waypoints[0], 0))
new FacingInit(Traits.Util.GetFacing(waypoints[1] - waypoints[0], 0))
});
foreach (var waypoint in waypoints)
m.QueueActivity(Fly.ToCell(waypoint));
@@ -210,7 +211,7 @@ namespace OpenRA.Mods.RA.Missions
void SendChinookReinforcements(CPos entry, Actor lz)
{
var chinook = world.CreateActor("tran", allies, entry, Util.GetFacing(lz.Location - entry, 0));
var chinook = world.CreateActor("tran", allies, entry, Traits.Util.GetFacing(lz.Location - entry, 0));
var cargo = chinook.Trait<Cargo>();
while (cargo.HasSpace(1))
@@ -234,7 +235,7 @@ namespace OpenRA.Mods.RA.Missions
alliedWarFactory.Trait<PrimaryBuilding>().SetPrimaryProducer(alliedWarFactory, true);
}
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
world = w;

View File

@@ -12,6 +12,7 @@ using System;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Buildings;
using OpenRA.Mods.RA.Move;
@@ -342,7 +343,7 @@ namespace OpenRA.Mods.RA.Missions
bool SpawnVehicles = true;
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
world = w;
soviets = w.Players.Single(p => p.InternalName == "Soviets");

View File

@@ -11,6 +11,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Buildings;
using OpenRA.Mods.RA.Move;
@@ -258,7 +259,7 @@ namespace OpenRA.Mods.RA.Missions
{
Sound.Play("reinfor1.aud");
foreach (var unit in units)
world.CreateActor(unit, greece, startEntryPoint.Location, Util.GetFacing(startBridgeEndPoint.CenterPosition - startEntryPoint.CenterPosition, 0))
world.CreateActor(unit, greece, startEntryPoint.Location, Traits.Util.GetFacing(startBridgeEndPoint.CenterPosition - startEntryPoint.CenterPosition, 0))
.QueueActivity(new Move.Move(startMovePoint.Location, 0));
}
@@ -266,7 +267,7 @@ namespace OpenRA.Mods.RA.Missions
{
Sound.Play("reinfor1.aud");
foreach (var unit in units)
world.CreateActor(unit, greece, alliedBaseEntryPoint.Location, Util.GetFacing(alliedBaseMovePoint.CenterPosition - alliedBaseEntryPoint.CenterPosition, 0))
world.CreateActor(unit, greece, alliedBaseEntryPoint.Location, Traits.Util.GetFacing(alliedBaseMovePoint.CenterPosition - alliedBaseEntryPoint.CenterPosition, 0))
.QueueActivity(new Move.Move(alliedBaseMovePoint.Location, 0));
}
@@ -294,7 +295,7 @@ namespace OpenRA.Mods.RA.Missions
OnObjectivesUpdated(true);
}
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
world = w;

View File

@@ -12,6 +12,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Air;
using OpenRA.Mods.RA.Buildings;
@@ -107,7 +108,7 @@ namespace OpenRA.Mods.RA.Missions
{
new OwnerInit(ussr),
new LocationInit(entry),
new FacingInit(Util.GetFacing(airfield.Location - entry, 0)),
new FacingInit(Traits.Util.GetFacing(airfield.Location - entry, 0)),
new AltitudeInit(Rules.Info["yak"].Traits.Get<PlaneInfo>().CruiseAltitude)
});
@@ -133,7 +134,7 @@ namespace OpenRA.Mods.RA.Missions
}));
}
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
world = w;

View File

@@ -12,6 +12,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Buildings;
using OpenRA.Mods.RA.Move;
@@ -271,7 +272,7 @@ namespace OpenRA.Mods.RA.Missions
OnObjectivesUpdated(true);
}
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
world = w;

View File

@@ -13,6 +13,7 @@ using System.Linq;
using System;
using System.Drawing;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA.Activities;
using OpenRA.Mods.RA.Move;
using OpenRA.Traits;
@@ -351,7 +352,7 @@ namespace OpenRA.Mods.RA.Missions
bool producing = true;
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
world = w;
allies = w.Players.SingleOrDefault(p => p.InternalName == "Allies");

View File

@@ -144,7 +144,7 @@ namespace OpenRA.Mods.RA.Move
public int GetInitialFacing() { return InitialFacing; }
}
public class Mobile : IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove, IFacing, ISync
public class Mobile : IIssueOrder, IResolveOrder, IOrderVoice, IPositionable, IMove, IFacing, ISync, INotifyAddedToWorld, INotifyRemovedFromWorld
{
public readonly Actor self;
public readonly MobileInfo Info;
@@ -231,6 +231,25 @@ namespace OpenRA.Mods.RA.Move
public void SetVisualPosition(Actor self, WPos pos)
{
CenterPosition = pos;
if (self.IsInWorld)
{
self.World.ScreenMap.Update(self);
self.World.ActorMap.UpdatePosition(self, this);
}
}
public void AddedToWorld(Actor self)
{
self.World.ActorMap.AddInfluence(self, this);
self.World.ActorMap.AddPosition(self, this);
self.World.ScreenMap.Add(self);
}
public void RemovedFromWorld(Actor self)
{
self.World.ActorMap.RemoveInfluence(self, this);
self.World.ActorMap.RemovePosition(self, this);
self.World.ScreenMap.Remove(self);
}
public IEnumerable<IOrderTargeter> Orders { get { yield return new MoveOrderTargeter(Info); } }
@@ -430,13 +449,13 @@ namespace OpenRA.Mods.RA.Move
public void AddInfluence()
{
if (self.IsInWorld)
self.World.ActorMap.Add(self, this);
self.World.ActorMap.AddInfluence(self, this);
}
public void RemoveInfluence()
{
if (self.IsInWorld)
self.World.ActorMap.Remove(self, this);
self.World.ActorMap.RemoveInfluence(self, this);
}
public void Nudge(Actor self, Actor nudger, bool force)

View File

@@ -39,9 +39,8 @@ namespace OpenRA.Mods.RA.Orders
{
if (mi.Button == MouseButton.Left)
{
var underCursor = world.FindUnitsAtMouse(mi.Location)
.Where(a => a.Owner == world.LocalPlayer
&& a.HasTrait<T>()).FirstOrDefault();
var underCursor = world.ScreenMap.ActorsAt(Game.viewport.ViewToWorldPx(mi.Location))
.Where(a => a.Owner == world.LocalPlayer && a.HasTrait<T>()).FirstOrDefault();
if (underCursor != null)
yield return new Order(order, underCursor, false);

View File

@@ -30,8 +30,8 @@ namespace OpenRA.Mods.RA.Orders
{
if (mi.Button == MouseButton.Left)
{
var underCursor = world.FindUnitsAtMouse(mi.Location)
.Where(a => a.AppearsFriendlyTo(world.LocalPlayer.PlayerActor) && a.HasTrait<RepairableBuilding>()).FirstOrDefault();
var underCursor = world.ScreenMap.ActorsAt(Game.viewport.ViewToWorldPx(mi.Location))
.Where(a => !world.FogObscures(a) && a.AppearsFriendlyTo(world.LocalPlayer.PlayerActor) && a.HasTrait<RepairableBuilding>()).FirstOrDefault();
if (underCursor == null)
yield break;

View File

@@ -12,6 +12,7 @@ using System;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA.Move;
using OpenRA.Traits;
@@ -21,7 +22,7 @@ namespace OpenRA.Mods.RA
public class SpawnMPUnits : IWorldLoaded
{
public void WorldLoaded(World world)
public void WorldLoaded(World world, WorldRenderer wr)
{
foreach (var s in world.WorldActor.Trait<MPStartLocations>().Start)
SpawnUnitsForPlayer(world, s.Key, s.Value);

View File

@@ -10,6 +10,7 @@
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
@@ -20,7 +21,7 @@ namespace OpenRA.Mods.RA
{
public Dictionary<string, Actor> Actors = new Dictionary<string, Actor>();
public void WorldLoaded(World world)
public void WorldLoaded(World world, WorldRenderer wr)
{
foreach (var actorReference in world.Map.Actors.Value)
{

View File

@@ -20,7 +20,7 @@ namespace OpenRA.Mods.RA
World world;
public bool Visible;
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
this.world = w;
this.refreshTick = 0;

View File

@@ -14,6 +14,7 @@ using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA.Move;
using OpenRA.Support;
using OpenRA.Traits;
@@ -27,7 +28,7 @@ namespace OpenRA.Mods.RA
{
Dictionary<uint, MovementClassDomainIndex> domainIndexes;
public void WorldLoaded(World world)
public void WorldLoaded(World world, WorldRenderer wr)
{
domainIndexes = new Dictionary<uint, MovementClassDomainIndex>();
var movementClasses = new HashSet<uint>(

View File

@@ -11,6 +11,7 @@
using System;
using System.Collections.Generic;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Traits;
using OpenRA.Widgets;
@@ -30,7 +31,7 @@ namespace OpenRA.Mods.RA
public PlayMusicOnMapLoad(PlayMusicOnMapLoadInfo info) { Info = info; }
public void WorldLoaded(World w) { PlayMusic(); }
public void WorldLoaded(World w, WorldRenderer wr) { PlayMusic(); }
void PlayMusic()
{

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
@@ -34,7 +35,7 @@ namespace OpenRA.Mods.RA
}
}
public void WorldLoaded(OpenRA.World w)
public void WorldLoaded(OpenRA.World w, WorldRenderer wr)
{
// NOTE(jsd): 32 seems a sane default initial capacity for the total # of harvesters in a game. Purely a guesstimate.
claimByCell = new Dictionary<CPos, ResourceClaim>(32);

View File

@@ -43,7 +43,7 @@ namespace OpenRA.Mods.RA
smudgeSprites = Info.Types.Select(x => Game.modData.SpriteLoader.LoadAllSprites(x)).ToArray();
}
public void WorldLoaded(World w)
public void WorldLoaded(World w, WorldRenderer wr)
{
world = w;
tiles = new Dictionary<CPos, TileReference<byte, byte>>();

View File

@@ -206,6 +206,8 @@ Player:
FrozenActorLayer:
World:
ScreenMap:
ActorMap:
LoadWidgetAtGameStart:
Widget: INGAME_ROOT
CncMenuPaletteEffect:
@@ -363,8 +365,6 @@ World:
BaseActor: mcv
SupportActors: e1,e1,e1,e1,e1,e2,e2,e2,e3,e3,apc,mtnk
SpawnMPUnits:
SpatialBins:
BinSize: 4
CrateSpawner:
Minimum: 1
Maximum: 6

View File

@@ -357,6 +357,8 @@ Player:
PlayerStatistics:
World:
ScreenMap:
ActorMap:
LoadWidgetAtGameStart:
Widget: INGAME_ROOT
ScreenShaker:
@@ -474,8 +476,6 @@ World:
Races: ordos
BaseActor: mcvo
SpawnMPUnits:
SpatialBins:
BinSize: 4
PathFinder:
ValidateOrder:
DebugPauseState:

View File

@@ -529,6 +529,8 @@ Player:
PlayerStatistics:
World:
ScreenMap:
ActorMap:
LoadWidgetAtGameStart:
Widget: INGAME_ROOT
ScreenShaker:
@@ -680,8 +682,6 @@ World:
OuterSupportRadius: 5
MPStartLocations:
SpawnMPUnits:
SpatialBins:
BinSize: 4
PathFinder:
ValidateOrder:
DebugPauseState:

View File

@@ -42,6 +42,8 @@ Player:
PlayerStatistics:
World:
ScreenMap:
ActorMap:
LoadWidgetAtGameStart:
Widget: INGAME_ROOT
BuildingInfluence:
@@ -122,8 +124,6 @@ World:
BaseActor: mcv
MPStartLocations:
SpawnMPUnits:
SpatialBins:
BinSize: 4
PathFinder:
ValidateOrder:
DebugPauseState: