diff --git a/OpenRa.Game/Actor.cs b/OpenRa.Game/Actor.cs index 397c02f422..5e001e4e91 100755 --- a/OpenRa.Game/Actor.cs +++ b/OpenRa.Game/Actor.cs @@ -27,20 +27,24 @@ namespace OpenRa.Game Location = location; CenterLocation = Traits.Util.CenterOfCell(Location); Owner = owner; - Health = Info.Strength; /* todo: handle cases where this is not true! */ - if( Info.Traits != null ) + if( Info != null ) { - foreach( var traitName in Info.Traits ) + Health = Info.Strength; /* todo: handle cases where this is not true! */ + + if( Info.Traits != null ) { - var type = typeof( Traits.Mobile ).Assembly.GetType( typeof( Traits.Mobile ).Namespace + "." + traitName, true, false ); - var ctor = type.GetConstructor( new[] { typeof( Actor ) } ); - traits.Add( type, ctor.Invoke( new object[] { this } ) ); + foreach( var traitName in Info.Traits ) + { + var type = typeof( Traits.Mobile ).Assembly.GetType( typeof( Traits.Mobile ).Namespace + "." + traitName, true, false ); + var ctor = type.GetConstructor( new[] { typeof( Actor ) } ); + traits.Add( type, ctor.Invoke( new object[] { this } ) ); + } } + else + throw new InvalidOperationException( "No Actor traits for " + Info.Name + + "; add Traits= to units.ini for appropriate unit" ); } - else - throw new InvalidOperationException( "No Actor traits for " + Info.Name - + "; add Traits= to units.ini for appropriate unit" ); } public void Tick() @@ -57,7 +61,16 @@ namespace OpenRa.Game } public float2 CenterLocation; - public float2 SelectedSize { get { return Render().First().a.size; } } + public float2 SelectedSize + { + get + { + var firstSprite = Render().FirstOrDefault(); + if( firstSprite == null ) + return new float2( 0, 0 ); + return firstSprite.a.size; + } + } public IEnumerable> Render() { diff --git a/OpenRa.Game/Game.cs b/OpenRa.Game/Game.cs index 474d89a03a..36d842604c 100644 --- a/OpenRa.Game/Game.cs +++ b/OpenRa.Game/Game.cs @@ -50,9 +50,16 @@ namespace OpenRa.Game public static void Initialize(string mapName, Renderer renderer, int2 clientSize, int localPlayer, bool useAftermath) { Rules.LoadRules(mapName, useAftermath); + world = new World(); - for (int i = 0; i < 8; i++) - players[i] = new Player(i, i, "Multi{0}".F(i), Race.Soviet); + for( int i = 0 ; i < 8 ; i++ ) + { + var a = new Actor( null, new int2( int.MaxValue, int.MaxValue ), null ); + players[ i ] = new Player( a, i, i, "Multi{0}".F( i ), Race.Soviet ); + a.Owner = players[ i ]; + a.traits.Add( new Traits.ProductionQueue( a ) ); + Game.world.Add( a ); + } localPlayerIndex = localPlayer; @@ -64,7 +71,6 @@ namespace OpenRa.Game SequenceProvider.Initialize(useAftermath); viewport = new Viewport( clientSize, Rules.Map.Offset, Rules.Map.Offset + Rules.Map.Size, renderer ); - world = new World(); Sound.Initialize(); BuildingInfluence = new BuildingInfluenceMap(); diff --git a/OpenRa.Game/MainWindow.cs b/OpenRa.Game/MainWindow.cs index 5ff62cc5dc..5d8214b7a0 100755 --- a/OpenRa.Game/MainWindow.cs +++ b/OpenRa.Game/MainWindow.cs @@ -135,8 +135,7 @@ namespace OpenRa.Game { Game.LocalPlayer.IsReady ^= true; Game.controller.AddOrder( - new Order(Game.LocalPlayer, - "ToggleReady", null, null, int2.Zero, + new Order( "ToggleReady", Game.LocalPlayer.PlayerActor, null, int2.Zero, Game.LocalPlayer.IsReady ? "ready" : "not ready") { IsImmediate = true }); } diff --git a/OpenRa.Game/OpenRa.Game.csproj b/OpenRa.Game/OpenRa.Game.csproj index f1b5bec396..c15f4223cd 100644 --- a/OpenRa.Game/OpenRa.Game.csproj +++ b/OpenRa.Game/OpenRa.Game.csproj @@ -161,6 +161,7 @@ + diff --git a/OpenRa.Game/Order.cs b/OpenRa.Game/Order.cs index a41cd65c4b..978cdfd39a 100644 --- a/OpenRa.Game/Order.cs +++ b/OpenRa.Game/Order.cs @@ -6,7 +6,6 @@ namespace OpenRa.Game { sealed class Order { - public readonly Player Player; public readonly string OrderString; readonly uint SubjectId; readonly uint TargetActorId; @@ -16,16 +15,16 @@ namespace OpenRa.Game public Actor Subject { get { return ActorFromUInt(SubjectId); } } public Actor TargetActor { get { return ActorFromUInt(TargetActorId); } } + public Player Player { get { return Subject.Owner; } } - public Order(Player player, string orderString, Actor subject, + public Order(string orderString, Actor subject, Actor targetActor, int2 targetLocation, string targetString) - : this( player, orderString, UIntFromActor( subject ), + : this( orderString, UIntFromActor( subject ), UIntFromActor( targetActor ), targetLocation, targetString ) {} - Order(Player player, string orderString, uint subjectId, + Order(string orderString, uint subjectId, uint targetActorId, int2 targetLocation, string targetString) { - this.Player = player; this.OrderString = orderString; this.SubjectId = subjectId; this.TargetActorId = targetActorId; @@ -68,7 +67,6 @@ namespace OpenRa.Game var ret = new MemoryStream(); var w = new BinaryWriter(ret); w.Write( (byte)0xFF ); - w.Write( (uint)Player.Index ); w.Write(OrderString); w.Write(SubjectId); w.Write(TargetActorId); @@ -95,7 +93,6 @@ namespace OpenRa.Game { case 0xFF: { - var playerID = r.ReadUInt32(); var order = r.ReadString(); var subjectId = r.ReadUInt32(); var targetActorId = r.ReadUInt32(); @@ -105,9 +102,7 @@ namespace OpenRa.Game if (r.ReadBoolean()) targetString = r.ReadString(); - return new Order( LookupPlayer(playerID), - order, subjectId, targetActorId, targetLocation, - targetString); + return new Order( order, subjectId, targetActorId, targetLocation, targetString); } case 0xfe: @@ -116,8 +111,7 @@ namespace OpenRa.Game var name = r.ReadString(); var data = r.ReadString(); - return new Order(LookupPlayer(playerID), - name, null, null, int2.Zero, data) { IsImmediate = true }; + return new Order( name, LookupPlayer( playerID ).PlayerActor, null, int2.Zero, data ) { IsImmediate = true }; } default: @@ -141,58 +135,58 @@ namespace OpenRa.Game // Now that Orders are resolved by individual Actors, these are weird; you unpack orders manually, but not pack them. public static Order Chat(Player subject, string text) { - return new Order(subject, "Chat", null, null, int2.Zero, text) + return new Order("Chat", subject.PlayerActor, null, int2.Zero, text) { IsImmediate = true }; } public static Order Attack(Actor subject, Actor target) { - return new Order(subject.Owner, "Attack", subject, target, int2.Zero, null); + return new Order("Attack", subject, target, int2.Zero, null); } public static Order Move(Actor subject, int2 target) { - return new Order(subject.Owner, "Move", subject, null, target, null); + return new Order("Move", subject, null, target, null); } public static Order DeployMcv(Actor subject) { - return new Order(subject.Owner, "DeployMcv", subject, null, int2.Zero, null); + return new Order("DeployMcv", subject, null, int2.Zero, null); } public static Order PlaceBuilding(Player subject, int2 target, string buildingName) { - return new Order(subject, "PlaceBuilding", null, null, target, buildingName); + return new Order("PlaceBuilding", subject.PlayerActor, null, target, buildingName); } public static Order DeliverOre(Actor subject, Actor target) { - return new Order(subject.Owner, "DeliverOre", subject, target, int2.Zero, null); + return new Order("DeliverOre", subject, target, int2.Zero, null); } public static Order Harvest(Actor subject, int2 target) { - return new Order(subject.Owner, "Harvest", subject, null, target, null); + return new Order("Harvest", subject, null, target, null); } public static Order StartProduction(Player subject, string item) { - return new Order(subject, "StartProduction", null, null, int2.Zero, item ); + return new Order("StartProduction", subject.PlayerActor, null, int2.Zero, item ); } public static Order PauseProduction(Player subject, string item, bool pause) { - return new Order( subject, "PauseProduction", null, null, new int2(pause ?1:0,0), item ); + return new Order("PauseProduction", subject.PlayerActor, null, new int2( pause ? 1 : 0, 0 ), item); } public static Order CancelProduction(Player subject, string item) { - return new Order( subject, "CancelProduction", null, null, int2.Zero, item ); + return new Order("CancelProduction", subject.PlayerActor, null, int2.Zero, item); } public static Order SetRallyPoint(Actor subject, int2 target) { - return new Order(subject.Owner, "SetRallyPoint", subject, null, target, null ); + return new Order("SetRallyPoint", subject, null, target, null ); } } } diff --git a/OpenRa.Game/Player.cs b/OpenRa.Game/Player.cs index 66abfb021b..f3c1bab792 100644 --- a/OpenRa.Game/Player.cs +++ b/OpenRa.Game/Player.cs @@ -10,6 +10,7 @@ namespace OpenRa.Game class Player { + public Actor PlayerActor; public int Palette; public int Kills; public string PlayerName; @@ -23,8 +24,9 @@ namespace OpenRa.Game public bool IsReady; - public Player( int index, int palette, string playerName, Race race ) + public Player( Actor playerActor, int index, int palette, string playerName, Race race ) { + this.PlayerActor = playerActor; this.Index = index; this.Palette = palette; this.PlayerName = playerName; diff --git a/OpenRa.Game/Traits/ProductionQueue.cs b/OpenRa.Game/Traits/ProductionQueue.cs new file mode 100755 index 0000000000..f7a6354238 --- /dev/null +++ b/OpenRa.Game/Traits/ProductionQueue.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenRa.Game.Traits +{ + class ProductionQueue : IOrder + { + public ProductionQueue( Actor self ) + { + + } + + public Order IssueOrder( Actor self, int2 xy, bool lmb, Actor underCursor ) + { + // production isn't done by clicks in the world; the chrome handles it. + return null; + } + + public void ResolveOrder( Actor self, Order order ) + { + switch( order.OrderString ) + { + case "StartProduction": + { + string group = Rules.UnitCategory[ order.TargetString ]; + var ui = Rules.UnitInfo[ order.TargetString ]; + var time = ui.Cost + * Rules.General.BuildSpeed /* todo: country-specific build speed bonus */ + * ( 25 * 60 ) /* frames per min */ /* todo: build acceleration, if we do that */ + / 1000; + + time = .08f * time; /* temporary hax so we can build stuff fast for test */ + + if( !Rules.TechTree.BuildableItems( order.Player, group ).Contains( order.TargetString ) ) + return; /* you can't build that!! */ + + bool hasPlayedSound = false; + + order.Player.BeginProduction( group, + new ProductionItem( order.TargetString, (int)time, ui.Cost, + () => Game.world.AddFrameEndTask( + _ => + { + var isBuilding = group == "Building" || group == "Defense"; + if( !hasPlayedSound && order.Player == Game.LocalPlayer ) + { + Sound.Play( isBuilding ? "conscmp1.aud" : "unitrdy1.aud" ); + hasPlayedSound = true; + } + if( !isBuilding ) + Game.BuildUnit( order.Player, order.TargetString ); + } ) ) ); + break; + } + case "PauseProduction": + { + var producing = order.Player.Producing( Rules.UnitCategory[ order.TargetString ] ); + if( producing != null && producing.Item == order.TargetString ) + producing.Paused = ( order.TargetLocation.X != 0 ); + break; + } + case "CancelProduction": + { + var producing = order.Player.Producing( Rules.UnitCategory[ order.TargetString ] ); + if( producing != null && producing.Item == order.TargetString ) + order.Player.CancelProduction( Rules.UnitCategory[ order.TargetString ] ); + break; + } + } + } + } +} diff --git a/OpenRa.Game/UnitOrders.cs b/OpenRa.Game/UnitOrders.cs index cf82d039c8..fd6c86b4a6 100755 --- a/OpenRa.Game/UnitOrders.cs +++ b/OpenRa.Game/UnitOrders.cs @@ -24,6 +24,9 @@ namespace OpenRa.Game case "DeliverOre": case "Harvest": case "SetRallyPoint": + case "StartProduction": + case "PauseProduction": + case "CancelProduction": { foreach( var t in order.Subject.traits.WithInterface() ) t.ResolveOrder( order.Subject, order ); @@ -51,52 +54,6 @@ namespace OpenRa.Game } ); break; } - case "StartProduction": - { - string group = Rules.UnitCategory[ order.TargetString ]; - var ui = Rules.UnitInfo[ order.TargetString ]; - var time = ui.Cost - * Rules.General.BuildSpeed /* todo: country-specific build speed bonus */ - * (25 * 60) /* frames per min */ /* todo: build acceleration, if we do that */ - / 1000; - - time = .08f * time; /* temporary hax so we can build stuff fast for test */ - - if (!Rules.TechTree.BuildableItems(order.Player, group).Contains(order.TargetString)) - return; /* you can't build that!! */ - - bool hasPlayedSound = false; - - order.Player.BeginProduction(group, - new ProductionItem(order.TargetString, (int)time, ui.Cost, - () => Game.world.AddFrameEndTask( - _ => - { - var isBuilding = group == "Building" || group == "Defense"; - if (!hasPlayedSound && order.Player == Game.LocalPlayer) - { - Sound.Play(isBuilding ? "conscmp1.aud" : "unitrdy1.aud"); - hasPlayedSound = true; - } - if (!isBuilding) - Game.BuildUnit(order.Player, order.TargetString); - }))); - break; - } - case "PauseProduction": - { - var producing = order.Player.Producing( Rules.UnitCategory[ order.TargetString ] ); - if( producing != null && producing.Item == order.TargetString ) - producing.Paused = ( order.TargetLocation.X != 0 ); - break; - } - case "CancelProduction": - { - var producing = order.Player.Producing( Rules.UnitCategory[ order.TargetString ] ); - if( producing != null && producing.Item == order.TargetString ) - order.Player.CancelProduction( Rules.UnitCategory[ order.TargetString ] ); - break; - } case "Chat": { Game.chat.AddLine(order.Player, order.TargetString);