diff --git a/OpenRa.Game/Game.cs b/OpenRa.Game/Game.cs index c3bb31ebab..6d7180ea2f 100644 --- a/OpenRa.Game/Game.cs +++ b/OpenRa.Game/Game.cs @@ -5,9 +5,9 @@ using OpenRa.TechTree; using System.Drawing; using System.Linq; using IrrKlang; -using IjwFramework.Collections; -using System; -using IjwFramework.Types; +using IjwFramework.Collections; +using System; +using IjwFramework.Types; using OpenRa.Game.Traits; namespace OpenRa.Game @@ -16,7 +16,7 @@ namespace OpenRa.Game { public static readonly int CellSize = 24; - public static World world; + public static World world; public static Map map; static TreeCache treeCache; public static TerrainRenderer terrain; @@ -40,8 +40,8 @@ namespace OpenRa.Game Rules.LoadRules( mapName ); for( int i = 0 ; i < 8 ; i++ ) - players.Add(i, new Player(i, string.Format("Multi{0}", i), Race.Soviet)); - + players.Add(i, new Player(i, string.Format("Multi{0}", i), Race.Soviet)); + localPlayerIndex = localPlayer; var mapFile = new IniFile( FileSystem.Open( mapName ) ); @@ -55,8 +55,8 @@ namespace OpenRa.Game treeCache = new TreeCache(map); foreach( TreeReference treeReference in map.Trees ) - world.Add( new Actor( treeReference, treeCache, map.Offset ) ); - + world.Add( new Actor( treeReference, treeCache, map.Offset ) ); + BuildingInfluence = new BuildingInfluenceMap(world, 8); LoadMapBuildings( mapFile ); @@ -136,67 +136,69 @@ namespace OpenRa.Game return map.IsInMap(a.X, a.Y) && TerrainCosts.Cost(umt, terrain.tileSet.GetWalkability(map.MapTiles[a.X, a.Y])) < double.PositiveInfinity; - } - - static IEnumerable FindUnits(float2 a, float2 b) - { - var min = float2.Min(a, b); - var max = float2.Max(a, b); - - var rect = new RectangleF(min.X, min.Y, max.X - min.X, max.Y - min.Y); - - return world.Actors - .Where(x => x.Bounds.IntersectsWith(rect)); - } - - public static IEnumerable FindUnitsInCircle(float2 a, float r) - { - return FindUnits(a - new float2(r, r), a + new float2(r, r)) - .Where(x => (x.CenterLocation - a).LengthSquared < r * r); + } + + static IEnumerable FindUnits(float2 a, float2 b) + { + var min = float2.Min(a, b); + var max = float2.Max(a, b); + + var rect = new RectangleF(min.X, min.Y, max.X - min.X, max.Y - min.Y); + + return world.Actors + .Where(x => x.Bounds.IntersectsWith(rect)); + } + + public static IEnumerable FindUnitsInCircle(float2 a, float r) + { + return FindUnits(a - new float2(r, r), a + new float2(r, r)) + .Where(x => (x.CenterLocation - a).LengthSquared < r * r); } public static IEnumerable SelectUnitsInBox(float2 a, float2 b) - { + { return FindUnits(a, b).Where(x => x.Owner == LocalPlayer && x.traits.Contains()); - } - - public static IEnumerable SelectUnitOrBuilding(float2 a) - { - var q = FindUnits(a, a); - return q.Where(x => x.traits.Contains()).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); - } - - public static Random SharedRandom = new Random(); /* for things that require sync */ - public static Random CosmeticRandom = new Random(); /* for things that are just fluff */ - - public static readonly Pair SovietVoices = - Pair.New( - new VoicePool("ackno", "affirm1", "noprob", "overout", "ritaway", "roger", "ugotit"), - new VoicePool("await1", "ready", "report1", "yessir1")); - - public static void BuildUnit(Player player, string name) - { - var producer = world.Actors - .FirstOrDefault(a => a.unitInfo != null && a.unitInfo.Name == "weap" && a.Owner == player); - - if (producer == null) - throw new InvalidOperationException("BuildUnit without suitable production structure!"); - + } + + public static IEnumerable SelectUnitOrBuilding(float2 a) + { + var q = FindUnits(a, a); + return q.Where(x => x.traits.Contains()).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); + } + + public static Random SharedRandom = new Random(); /* for things that require sync */ + public static Random CosmeticRandom = new Random(); /* for things that are just fluff */ + + public static readonly Pair SovietVoices = + Pair.New( + new VoicePool("ackno", "affirm1", "noprob", "overout", "ritaway", "roger", "ugotit"), + new VoicePool("await1", "ready", "report1", "yessir1")); + + public static void BuildUnit(Player player, string name) + { + var producer = world.Actors + .FirstOrDefault(a => a.unitInfo != null && a.unitInfo.Name == "weap" && a.Owner == player); + + if (producer == null) + throw new InvalidOperationException("BuildUnit without suitable production structure!"); + var unit = new Actor(name, (1/24f * producer.CenterLocation).ToInt2(), player); - unit.Order(unit.Location + new int2(0, 3)).Apply(false); - - world.AddFrameEndTask(_ => world.Add(unit)); - - // todo: make the producing building play `build` - } + var mobile = unit.traits.Get(); + mobile.facing = 128; + mobile.SetNextAction( new Traits.Mobile.MoveTo( unit.Location + new int2( 0, 3 ) ) ); + + world.AddFrameEndTask(_ => world.Add(unit)); + + // todo: make the producing building play `build` + } } } diff --git a/OpenRa.Game/MoveOrder.cs b/OpenRa.Game/MoveOrder.cs index 0ffbaa1656..96eef6db20 100644 --- a/OpenRa.Game/MoveOrder.cs +++ b/OpenRa.Game/MoveOrder.cs @@ -33,7 +33,7 @@ namespace OpenRa.Game if (Game.LocalPlayer == Unit.Owner) Game.PlaySound("ackno.r00", false); var mobile = Unit.traits.Get(); - mobile.SetNextAction( new Traits.Mobile.MoveTo( Destination ) ); + mobile.QueueAction( new Traits.Mobile.MoveTo( Destination ) ); } } } diff --git a/OpenRa.Game/Traits/McvDeploy.cs b/OpenRa.Game/Traits/McvDeploy.cs index 1000e84bfa..1a78ce9db1 100644 --- a/OpenRa.Game/Traits/McvDeploy.cs +++ b/OpenRa.Game/Traits/McvDeploy.cs @@ -35,7 +35,9 @@ namespace OpenRa.Game.Traits public override void Apply( bool leftMouseButton ) { if( leftMouseButton ) return; - Unit.traits.Get().SetNextAction( new Mobile.Turn( 96 ) { NextAction = new DeployAction() } ); + var mobile = Unit.traits.Get(); + mobile.QueueAction( new Mobile.Turn( 96 ) ); + mobile.QueueAction( new DeployAction() ); } class DeployAction : Mobile.CurrentAction @@ -50,6 +52,12 @@ namespace OpenRa.Game.Traits Game.world.Add( new Actor( "fact", self.Location - new int2( 1, 1 ), self.Owner ) ); } ); } + + public void Cancel( Actor self, Mobile mobile ) + { + // Cancel can't happen between this being moved to the head of the list, and it being Ticked. + throw new InvalidOperationException( "DeployMcvAction: Cancel() should never occur." ); + } } } } diff --git a/OpenRa.Game/Traits/Mobile.cs b/OpenRa.Game/Traits/Mobile.cs index 67dc19ece7..4a848250ac 100644 --- a/OpenRa.Game/Traits/Mobile.cs +++ b/OpenRa.Game/Traits/Mobile.cs @@ -30,6 +30,21 @@ namespace OpenRa.Game.Traits currentAction = nextAction; else currentAction.NextAction = nextAction; + } + + public void QueueAction( CurrentAction nextAction ) + { + if( currentAction == null ) + { + currentAction = nextAction; + return; + } + var act = currentAction; + while( act.NextAction != null ) + { + act = act.NextAction; + } + act.NextAction = nextAction; } public void Tick(Actor self) @@ -50,16 +65,17 @@ namespace OpenRa.Game.Traits public interface CurrentAction - { - CurrentAction NextAction { set; } - void Tick( Actor self, Mobile mobile ); + { + CurrentAction NextAction { get; set; } + void Tick( Actor self, Mobile mobile ); + void Cancel( Actor self, Mobile mobile ); } public class Turn : CurrentAction { public CurrentAction NextAction { get; set; } - public readonly int desiredFacing; + public int desiredFacing; public Turn( int desiredFacing ) { @@ -76,6 +92,12 @@ namespace OpenRa.Game.Traits return; } Util.TickFacing( ref mobile.facing, desiredFacing, self.unitInfo.ROT ); + } + + public void Cancel( Actor self, Mobile mobile ) + { + desiredFacing = mobile.facing; + NextAction = null; } } @@ -87,7 +109,7 @@ namespace OpenRa.Game.Traits List path; int moveFraction, moveFractionTotal; - float2 from, to; + float2 from, to; int fromFacing, toFacing; Action OnComplete; @@ -130,14 +152,14 @@ namespace OpenRa.Game.Traits path.RemoveAt( path.Count - 1 ); moveFractionTotal = ( dir.X != 0 && dir.Y != 0 ) ? 35 : 25; from = CenterOfCell( mobile.fromCell ); - to = BetweenCells( mobile.fromCell, mobile.toCell ); - CalculateMoveFraction(); - fromFacing = mobile.facing; + to = BetweenCells( mobile.fromCell, mobile.toCell ); + CalculateMoveFraction(); + fromFacing = mobile.facing; toFacing = mobile.facing; OnComplete = OnCompleteFirstHalf; } mobile.currentAction.Tick( self, mobile ); - } + } void TickMove( Actor self, Mobile mobile ) { @@ -150,21 +172,21 @@ namespace OpenRa.Game.Traits //mobile.fromCell = mobile.toCell; } return; - } - - void UpdateCenterLocation( Actor self, Mobile mobile, float frac ) - { - self.CenterLocation = float2.Lerp( from, to, frac ); - if( moveFraction >= moveFractionTotal ) - mobile.facing = toFacing; - else - mobile.facing = ( fromFacing + ( toFacing - fromFacing ) * moveFraction / moveFractionTotal ) & 0xFF; - } - - void CalculateMoveFraction() - { - var d = to - from; - moveFractionTotal = (int)Math.Sqrt( d.X * d.X + d.Y * d.Y ) * (25 / 6); + } + + void UpdateCenterLocation( Actor self, Mobile mobile, float frac ) + { + self.CenterLocation = float2.Lerp( from, to, frac ); + if( moveFraction >= moveFractionTotal ) + mobile.facing = toFacing; + else + mobile.facing = ( fromFacing + ( toFacing - fromFacing ) * moveFraction / moveFractionTotal ) & 0xFF; + } + + void CalculateMoveFraction() + { + var d = to - from; + moveFractionTotal = (int)Math.Sqrt( d.X * d.X + d.Y * d.Y ) * (25 / 6); } static float2 CenterOfCell( int2 loc ) @@ -178,38 +200,44 @@ namespace OpenRa.Game.Traits } void OnCompleteFirstHalf( Actor self, Mobile mobile ) - { - if( path.Count > 0 ) - { - var nextCell = path[ path.Count - 1 ]; - if( ( nextCell - mobile.toCell ) != ( mobile.toCell - mobile.fromCell ) ) - { - path.RemoveAt( path.Count - 1 ); - from = BetweenCells( mobile.fromCell, mobile.toCell ); - to = BetweenCells( mobile.toCell, nextCell ); - CalculateMoveFraction(); - mobile.fromCell = mobile.toCell; - mobile.toCell = nextCell; - fromFacing = mobile.facing; - toFacing = Util.GetNearestFacing( fromFacing, Util.GetFacing( mobile.toCell-mobile.fromCell, fromFacing ) ); - OnComplete = OnCompleteFirstHalf; - return; - } - } - from = BetweenCells( mobile.fromCell, mobile.toCell ); - to = CenterOfCell( mobile.toCell ); - CalculateMoveFraction(); - fromFacing = toFacing = mobile.facing; - OnComplete = OnCompleteSecondHalf; - mobile.fromCell = mobile.toCell; + { + if( path.Count > 0 ) + { + var nextCell = path[ path.Count - 1 ]; + if( ( nextCell - mobile.toCell ) != ( mobile.toCell - mobile.fromCell ) ) + { + path.RemoveAt( path.Count - 1 ); + from = BetweenCells( mobile.fromCell, mobile.toCell ); + to = BetweenCells( mobile.toCell, nextCell ); + CalculateMoveFraction(); + mobile.fromCell = mobile.toCell; + mobile.toCell = nextCell; + fromFacing = mobile.facing; + toFacing = Util.GetNearestFacing( fromFacing, Util.GetFacing( mobile.toCell-mobile.fromCell, fromFacing ) ); + OnComplete = OnCompleteFirstHalf; + return; + } + } + from = BetweenCells( mobile.fromCell, mobile.toCell ); + to = CenterOfCell( mobile.toCell ); + CalculateMoveFraction(); + fromFacing = toFacing = mobile.facing; + OnComplete = OnCompleteSecondHalf; + mobile.fromCell = mobile.toCell; } void OnCompleteSecondHalf( Actor self, Mobile mobile ) - { + { moveFractionTotal = 0; self.CenterLocation = CenterOfCell( mobile.toCell ); - OnComplete = null; - mobile.fromCell = mobile.toCell; + OnComplete = null; + mobile.fromCell = mobile.toCell; + } + + public void Cancel( Actor self, Mobile mobile ) + { + path.Clear(); + NextAction = null; } } }