diff --git a/OpenRa.DataStructures/PriorityQueue.cs b/OpenRa.DataStructures/PriorityQueue.cs index fcb20425d8..83b4af4f68 100644 --- a/OpenRa.DataStructures/PriorityQueue.cs +++ b/OpenRa.DataStructures/PriorityQueue.cs @@ -5,7 +5,7 @@ using System.Diagnostics; namespace OpenRa.DataStructures { - class PriorityQueue + public class PriorityQueue where T : IComparable { List items = new List(); @@ -36,10 +36,16 @@ namespace OpenRa.DataStructures { index = 0; ++level; - items.Add( new T[ 1 << level ] ); + if( items.Count <= level ) + items.Add( new T[ 1 << level ] ); } } + public bool Empty + { + get { return ( level == 0 ); } + } + T At( int level, int index ) { return items[ level ][ index ]; @@ -123,7 +129,7 @@ namespace OpenRa.DataStructures // System.Diagnostics.Debug.Assert( At( i, j ).CompareTo( Above( i, j ) ) < 0, "At( i, j ) > Above( i, j )" ); //} - private int RowLength( int i ) + int RowLength( int i ) { if( i == level ) return index; diff --git a/OpenRa.Game/MainWindow.cs b/OpenRa.Game/MainWindow.cs index bfb9fca6e2..e31ca2ef3d 100644 --- a/OpenRa.Game/MainWindow.cs +++ b/OpenRa.Game/MainWindow.cs @@ -41,7 +41,7 @@ namespace OpenRa.Game Location = Point.Empty; Visible = true; - renderer = new Renderer(this, GetResolution(settings), false); + renderer = new Renderer(this, GetResolution(settings), true); map = new Map(new IniFile(File.OpenRead("../../../" + settings.GetValue("map", "scm12ea.ini")))); @@ -71,6 +71,7 @@ namespace OpenRa.Game sidebar = new Sidebar(Race.Soviet, renderer, viewport); + PathFinder.Instance = new PathFinder( map, terrain.tileSet ); } void PrecacheStructure(string name) diff --git a/OpenRa.Game/Mcv.cs b/OpenRa.Game/Mcv.cs index b1eecc1da5..59a1756d5a 100644 --- a/OpenRa.Game/Mcv.cs +++ b/OpenRa.Game/Mcv.cs @@ -95,9 +95,16 @@ namespace OpenRa.Game currentOrder = null; else { - int2 dir = destination - fromCell; - toCell = fromCell + dir.Sign(); - moveFractionTotal = ( dir.X != 0 && dir.Y != 0 ) ? 250 : 200; + List res = PathFinder.Instance.FindUnitPath( world, this, destination ); + if( res.Count != 0 ) + { + toCell = res[ res.Count - 1 ]; + + int2 dir = toCell - fromCell; + moveFractionTotal = ( dir.X != 0 && dir.Y != 0 ) ? 250 : 200; + } + else + destination = toCell; } } } @@ -148,5 +155,10 @@ namespace OpenRa.Game else return new MoveOrder( this, xy ); } + + public int2 Location + { + get { return toCell; } + } } } diff --git a/OpenRa.Game/OpenRa.Game.csproj b/OpenRa.Game/OpenRa.Game.csproj index bc346eb2c8..6ce0ec31a1 100644 --- a/OpenRa.Game/OpenRa.Game.csproj +++ b/OpenRa.Game/OpenRa.Game.csproj @@ -79,6 +79,10 @@ + + {2F9E7A23-56C0-4286-9C8E-1060A9B2F073} + OpenRa.DataStructures + {BDAEAB25-991E-46A7-AF1E-4F0E03358DAA} OpenRa.FileFormats @@ -96,4 +100,4 @@ --> - \ No newline at end of file + diff --git a/OpenRa.Game/PathFinder.cs b/OpenRa.Game/PathFinder.cs new file mode 100644 index 0000000000..1c566445c1 --- /dev/null +++ b/OpenRa.Game/PathFinder.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Text; +using OpenRa.FileFormats; +using OpenRa.DataStructures; +using System.Windows.Forms; + +namespace OpenRa.Game +{ + class PathFinder + { + public static PathFinder Instance; + + bool[ , ] passable = new bool[ 128, 128 ]; + Map map; + + public PathFinder( Map map, TileSet tileSet ) + { + this.map = map; + + for( int x = 0 ; x < 128 ; x++ ) + { + for( int y = 0 ; y < 128 ; y++ ) + { + if( x < map.XOffset || y < map.YOffset || x >= map.XOffset + map.Width || y >= map.YOffset + map.Height ) + passable[ x, y ] = false; + else + { + // HACK: water( tiles 1 and 2) are impassable + passable[ x, y ] = ( map.MapTiles[ x, y ].tile != 1 && map.MapTiles[ x, y ].tile != 2 ); + // TODO: implement all the different terrain classes, including bonuses for roads etc + } + } + } + } + + static bool first = true; + + public List FindUnitPath( World world, Mcv unit, int2 destination ) + { + int2 offset = new int2( map.XOffset, map.YOffset ); + + destination += offset; + int2 startLocation = unit.Location + offset; + + bool[ , ] seen = new bool[ 128, 128 ]; + int2[ , ] path = new int2[ 128, 128 ]; + double[ , ] minCost = new double[ 128, 128 ]; + + for( int x = 0 ; x < 128 ; x++ ) + { + for( int y = 0 ; y < 128 ; y++ ) + { + path[ x, y ] = new int2( x, y ); + minCost[ x, y ] = double.PositiveInfinity; + } + } + + PriorityQueue queue = new PriorityQueue(); + + queue.Add( new PathDistance( Estimate( startLocation, destination ), startLocation ) ); + minCost[ startLocation.X, startLocation.Y ] = Estimate( startLocation, destination ); + + int hax = 0; + int seenCount = 0; + int impassableCount = 0; + + while( !queue.Empty ) + { + ++hax; + PathDistance p = queue.Pop(); + int2 here = p.Location; + seen[ here.X, here.Y ] = true; + + if( hax < 128 ) + world.AddFrameEndTask( delegate { world.Add( new Mcv( here - offset, 2 ) ); } ); + + if( p.Location == destination ) + { + Log.Write( "{0}, {1}, {2}", hax, seenCount, impassableCount ); + first = false; + return MakePath( path, destination, offset ); + } + + foreach( int2 d in directions ) + { + int2 newHere = here + d; + + if( seen[ newHere.X, newHere.Y ] ) + { + ++seenCount; + continue; + } + if( !passable[ newHere.X, newHere.Y ] ) + { + ++impassableCount; + continue; + } + + double newCost = minCost[ here.X, here.Y ] + ( ( d.X * d.Y != 0 ) ? 1.414213563 : 1.0 ); + + if( newCost >= minCost[ newHere.X, newHere.Y ] ) + continue; + + path[ newHere.X, newHere.Y ] = here; + minCost[ newHere.X, newHere.Y ] = newCost; + + queue.Add( new PathDistance( newCost + Estimate( newHere, destination ), newHere ) ); + } + } + + // no path exists + return new List(); + } + + List MakePath( int2[ , ] path, int2 destination, int2 offset ) + { + List ret = new List(); + int2 pathNode = destination; + + while( path[ pathNode.X, pathNode.Y ] != pathNode ) + { + ret.Add( pathNode - offset ); + pathNode = path[ pathNode.X, pathNode.Y ]; + } + + Log.Write( "Path Length: {0}", ret.Count ); + return ret; + } + + static readonly int2[] directions = + new int2[] { + new int2( -1, -1 ), + new int2( -1, 0 ), + new int2( -1, 1 ), + new int2( 0, -1 ), + new int2( 0, 1 ), + new int2( 1, -1 ), + new int2( 1, 0 ), + new int2( 1, 1 ), + }; + + double Estimate( int2 here, int2 destination ) + { + int2 d = ( here - destination ).Abs(); + int diag = Math.Min( d.X, d.Y ); + int straight = Math.Abs( d.X - d.Y ); + return 1.5 * diag + straight; + } + + struct PathDistance : IComparable + { + public double EstTotal; + public int2 Location; + + public PathDistance( double estTotal, int2 location ) + { + EstTotal = estTotal; + Location = location; + } + + public int CompareTo( PathDistance other ) + { + return Math.Sign( EstTotal - other.EstTotal ); + } + } + } +} diff --git a/OpenRa.Game/Program.cs b/OpenRa.Game/Program.cs index e9eb5d6a8a..4d0171800b 100644 --- a/OpenRa.Game/Program.cs +++ b/OpenRa.Game/Program.cs @@ -10,14 +10,15 @@ namespace OpenRa.Game [STAThread] static void Main( string[] args ) { + if( System.Diagnostics.Debugger.IsAttached ) + { + Run( args ); + return; + } + try { - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault( false ); - - Settings settings = new Settings(args); - - new MainWindow( settings ).Run(); + Run( args ); } catch( Exception e ) { @@ -26,5 +27,15 @@ namespace OpenRa.Game throw; } } + + private static void Run( string[] args ) + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault( false ); + + Settings settings = new Settings( args ); + + new MainWindow( settings ).Run(); + } } } \ No newline at end of file diff --git a/OpenRa.Game/TerrainRenderer.cs b/OpenRa.Game/TerrainRenderer.cs index 373d39191b..7eb57e16a7 100644 --- a/OpenRa.Game/TerrainRenderer.cs +++ b/OpenRa.Game/TerrainRenderer.cs @@ -14,7 +14,7 @@ namespace OpenRa.Game FvfVertexBuffer vertexBuffer; IndexBuffer indexBuffer; Sheet terrainSheet; - TileSet tileSet; + public TileSet tileSet; Viewport viewport; Renderer renderer; @@ -32,30 +32,30 @@ namespace OpenRa.Game Dictionary tileMapping = new Dictionary(); - Size tileSize = new Size(24, 24); + Size tileSize = new Size( 24, 24 ); List vertices = new List(); List indices = new List(); - for (int j = 0; j < map.Height; j++) - for (int i = 0; i < map.Width; i++) + for( int j = 0 ; j < map.Height ; j++ ) + for( int i = 0 ; i < map.Width ; i++ ) { - TileReference tileRef = map.MapTiles[i + map.XOffset, j + map.YOffset]; + TileReference tileRef = map.MapTiles[ i + map.XOffset, j + map.YOffset ]; Sprite tile; - if (!tileMapping.TryGetValue(tileRef, out tile)) - tileMapping.Add(tileRef, tile = SheetBuilder.Add(tileSet.GetBytes(tileRef), tileSize)); + if( !tileMapping.TryGetValue( tileRef, out tile ) ) + tileMapping.Add( tileRef, tile = SheetBuilder.Add( tileSet.GetBytes( tileRef ), tileSize ) ); terrainSheet = tile.sheet; - Util.CreateQuad(vertices, indices, 24 * new float2(i,j), tile, 0); + Util.CreateQuad( vertices, indices, 24 * new float2( i, j ), tile, 0 ); } - vertexBuffer = new FvfVertexBuffer(renderer.Device, vertices.Count, Vertex.Format); - vertexBuffer.SetData(vertices.ToArray()); + vertexBuffer = new FvfVertexBuffer( renderer.Device, vertices.Count, Vertex.Format ); + vertexBuffer.SetData( vertices.ToArray() ); - indexBuffer = new IndexBuffer(renderer.Device, indices.Count); - indexBuffer.SetData(indices.ToArray()); + indexBuffer = new IndexBuffer( renderer.Device, indices.Count ); + indexBuffer.SetData( indices.ToArray() ); } void Draw() diff --git a/OpenRa.Game/int2.cs b/OpenRa.Game/int2.cs index a87c2479bb..b4aa703623 100644 --- a/OpenRa.Game/int2.cs +++ b/OpenRa.Game/int2.cs @@ -24,6 +24,7 @@ namespace OpenRa.Game public static bool operator !=(int2 me, int2 other) { return !(me == other); } public int2 Sign() { return new int2(Math.Sign(X), Math.Sign(Y)); } + public int2 Abs() { return new int2( Math.Abs( X ), Math.Abs( Y ) ); } public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode(); } public override bool Equals(object obj)