diff --git a/OpenRA.Mods.Cnc/Traits/Buildings/ProductionAirdrop.cs b/OpenRA.Mods.Cnc/Traits/Buildings/ProductionAirdrop.cs index 8740932aee..abdd00a0e9 100644 --- a/OpenRA.Mods.Cnc/Traits/Buildings/ProductionAirdrop.cs +++ b/OpenRA.Mods.Cnc/Traits/Buildings/ProductionAirdrop.cs @@ -27,13 +27,13 @@ namespace OpenRA.Mods.Cnc.Traits [Desc("Cargo aircraft used.")] [ActorReference] public readonly string ActorType = "c17"; - public override object Create(ActorInitializer init) { return new ProductionAirdrop(this, init.Self); } + public override object Create(ActorInitializer init) { return new ProductionAirdrop(init, this); } } class ProductionAirdrop : Production { - public ProductionAirdrop(ProductionAirdropInfo info, Actor self) - : base(info, self) { } + public ProductionAirdrop(ActorInitializer init, ProductionAirdropInfo info) + : base(init, info) { } public override bool Produce(Actor self, ActorInfo producee, string raceVariant) { diff --git a/OpenRA.Mods.Common/Orders/PlaceBuildingOrderGenerator.cs b/OpenRA.Mods.Common/Orders/PlaceBuildingOrderGenerator.cs index b4571b498e..a41619041d 100644 --- a/OpenRA.Mods.Common/Orders/PlaceBuildingOrderGenerator.cs +++ b/OpenRA.Mods.Common/Orders/PlaceBuildingOrderGenerator.cs @@ -15,7 +15,6 @@ using OpenRA.Graphics; using OpenRA.Mods.Common.Graphics; using OpenRA.Mods.Common.Traits; using OpenRA.Primitives; -using OpenRA.Traits; namespace OpenRA.Mods.Common.Orders { @@ -24,15 +23,18 @@ namespace OpenRA.Mods.Common.Orders readonly Actor producer; readonly string building; readonly BuildingInfo buildingInfo; + readonly string race; + readonly Sprite buildOk; + readonly Sprite buildBlocked; IActorPreview[] preview; - Sprite buildOk, buildBlocked; - bool initialized = false; + bool initialized; public PlaceBuildingOrderGenerator(ProductionQueue queue, string name) { producer = queue.Actor; building = name; + race = queue.MostLikelyProducer().Trait.Race; // Clear selection if using Left-Click Orders if (Game.Settings.Game.UseClassicMouseStyle) @@ -122,7 +124,12 @@ namespace OpenRA.Mods.Common.Orders { if (!initialized) { - var init = new ActorPreviewInitializer(rules.Actors[building], producer.Owner, wr, new TypeDictionary()); + var td = new TypeDictionary() + { + new RaceInit(race) + }; + + var init = new ActorPreviewInitializer(rules.Actors[building], producer.Owner, wr, td); preview = rules.Actors[building].Traits.WithInterface() .SelectMany(rpi => rpi.RenderPreview(init)) .ToArray(); diff --git a/OpenRA.Mods.Common/Traits/Player/ClassicProductionQueue.cs b/OpenRA.Mods.Common/Traits/Player/ClassicProductionQueue.cs index 4bb1068451..40907694ad 100644 --- a/OpenRA.Mods.Common/Traits/Player/ClassicProductionQueue.cs +++ b/OpenRA.Mods.Common/Traits/Player/ClassicProductionQueue.cs @@ -74,6 +74,16 @@ namespace OpenRA.Mods.Common.Traits return isActive ? base.BuildableItems() : NoItems; } + public override TraitPair MostLikelyProducer() + { + return self.World.ActorsWithTrait() + .Where(x => x.Actor.Owner == self.Owner + && x.Trait.Info.Produces.Contains(Info.Type)) + .OrderByDescending(x => x.Actor.IsPrimaryBuilding()) + .ThenByDescending(x => x.Actor.ActorID) + .FirstOrDefault(); + } + protected override bool BuildUnit(string name) { // Find a production structure to build this actor @@ -97,7 +107,7 @@ namespace OpenRA.Mods.Common.Traits foreach (var p in producers.Where(p => !p.Actor.IsDisabled())) { - if (p.Trait.Produce(p.Actor, ai, Race)) + if (p.Trait.Produce(p.Actor, ai, p.Trait.Race)) { FinishProduction(); return true; diff --git a/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs b/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs index eb3b359729..eab585dba8 100644 --- a/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs +++ b/OpenRA.Mods.Common/Traits/Player/PlaceBuilding.cs @@ -38,6 +38,8 @@ namespace OpenRA.Mods.Common.Traits if (queue == null) return; + var producer = queue.MostLikelyProducer(); + var race = producer.Trait != null ? producer.Trait.Race : self.Owner.Country.Race; var buildingInfo = unit.Traits.Get(); if (order.OrderString == "LineBuild") @@ -49,7 +51,7 @@ namespace OpenRA.Mods.Common.Traits { new LocationInit(t), new OwnerInit(order.Player), - new RaceInit(queue.Race) + new RaceInit(race) }); if (playSounds) @@ -69,14 +71,16 @@ namespace OpenRA.Mods.Common.Traits { new LocationInit(order.TargetLocation), new OwnerInit(order.Player), - new RaceInit(queue.Race), + new RaceInit(race), }); foreach (var s in buildingInfo.BuildSounds) Sound.PlayToPlayer(order.Player, s, building.CenterPosition); } - PlayBuildAnim(self, unit); + if (producer.Actor != null) + foreach (var nbp in producer.Actor.TraitsImplementing()) + nbp.BuildingPlaced(producer.Actor); queue.FinishProduction(); @@ -84,9 +88,9 @@ namespace OpenRA.Mods.Common.Traits { // May be null if the build anywhere cheat is active // BuildingInfo.IsCloseEnoughToBase has already verified that this is a valid build location - var producer = buildingInfo.FindBaseProvider(w, self.Owner, order.TargetLocation); - if (producer != null) - producer.Trait().BeginCooldown(); + var provider = buildingInfo.FindBaseProvider(w, self.Owner, order.TargetLocation); + if (provider != null) + provider.Trait().BeginCooldown(); } if (GetNumBuildables(self.Owner) > prevItems) @@ -96,30 +100,11 @@ namespace OpenRA.Mods.Common.Traits } } - // finds a construction yard (or equivalent) and runs its "build" animation. - static void PlayBuildAnim(Actor self, ActorInfo unit) - { - var bi = unit.Traits.GetOrDefault(); - if (bi == null) - return; - - var producers = self.World.ActorsWithTrait() - .Where(x => x.Actor.Owner == self.Owner - && x.Actor.Info.Traits.Get().Produces.Intersect(bi.Queue).Any()) - .ToList(); - var producer = producers.Where(x => x.Actor.IsPrimaryBuilding()).Concat(producers) - .FirstOrDefault(); - - if (producer.Actor == null) - return; - - foreach (var nbp in producer.Actor.TraitsImplementing()) - nbp.BuildingPlaced(producer.Actor); - } - static int GetNumBuildables(Player p) { - if (p != p.World.LocalPlayer) return 0; // this only matters for local players. + // This only matters for local players. + if (p != p.World.LocalPlayer) + return 0; return p.World.ActorsWithTrait() .Where(a => a.Actor.Owner == p) diff --git a/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs b/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs index 4c335cfc97..56802e74df 100644 --- a/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs +++ b/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs @@ -79,7 +79,6 @@ namespace OpenRA.Mods.Common.Traits Dictionary produceable; List queue = new List(); - // A list of things we are currently building public Actor Actor { get { return self; } } [Sync] public int QueueLength { get { return queue.Count; } } @@ -361,6 +360,13 @@ namespace OpenRA.Mods.Common.Traits queue.Add(item); } + // Returns the actor/trait that is most likely (but not necessarily guaranteed) to produce something in this queue + public virtual TraitPair MostLikelyProducer() + { + var trait = self.TraitsImplementing().FirstOrDefault(p => p.Info.Produces.Contains(Info.Type)); + return new TraitPair { Actor = self, Trait = trait }; + } + // Builds a unit from the actor that holds this queue (1 queue per building) // Returns false if the unit can't be built protected virtual bool BuildUnit(string name) diff --git a/OpenRA.Mods.Common/Traits/Production.cs b/OpenRA.Mods.Common/Traits/Production.cs index 2526322f7d..0d5b30db9b 100644 --- a/OpenRA.Mods.Common/Traits/Production.cs +++ b/OpenRA.Mods.Common/Traits/Production.cs @@ -24,18 +24,21 @@ namespace OpenRA.Mods.Common.Traits [Desc("e.g. Infantry, Vehicles, Aircraft, Buildings")] public readonly string[] Produces = { }; - public virtual object Create(ActorInitializer init) { return new Production(this, init.Self); } + public virtual object Create(ActorInitializer init) { return new Production(init, this); } } public class Production { - Lazy rp; + readonly Lazy rp; public ProductionInfo Info; - public Production(ProductionInfo info, Actor self) + public string Race { get; private set; } + + public Production(ActorInitializer init, ProductionInfo info) { Info = info; - rp = Exts.Lazy(() => self.IsDead ? null : self.TraitOrDefault()); + rp = Exts.Lazy(() => init.Self.IsDead ? null : init.Self.TraitOrDefault()); + Race = init.Contains() ? init.Get() : init.Self.Owner.Country.Race; } public void DoProduction(Actor self, ActorInfo producee, ExitInfo exitinfo, string raceVariant) diff --git a/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs b/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs index 43bccc5603..f9ccf48e7b 100644 --- a/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ProductionPaletteWidget.cs @@ -252,7 +252,8 @@ namespace OpenRA.Mods.Common.Widgets public void RefreshIcons() { icons = new Dictionary(); - if (CurrentQueue == null) + var producer = CurrentQueue != null ? CurrentQueue.MostLikelyProducer() : default(TraitPair); + if (CurrentQueue == null || producer.Trait == null) { if (DisplayedIconCount != 0) { @@ -268,7 +269,7 @@ namespace OpenRA.Mods.Common.Widgets var ks = Game.Settings.Keys; var rb = RenderBounds; - var race = CurrentQueue.Actor.Owner.Country.Race; + var race = producer.Trait.Race; foreach (var item in AllBuildables.Skip(IconRowOffset * Columns).Take(MaxIconRowOffset * Columns)) {