diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 590a19a100..97c112b5e1 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -332,6 +332,8 @@ + + diff --git a/OpenRA.Mods.Common/Traits/Multipliers/ProductionCostMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/ProductionCostMultiplier.cs new file mode 100644 index 0000000000..25b20e58e6 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Multipliers/ProductionCostMultiplier.cs @@ -0,0 +1,41 @@ +#region Copyright & License Information +/* + * Copyright 2007-2019 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, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System.Collections.Generic; +using System.Linq; +using OpenRA.Primitives; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("Modifies the production cost of this actor for a specific queue or when a prerequisite is granted.")] + public class ProductionCostMultiplierInfo : TraitInfo, IProductionCostModifierInfo + { + [Desc("Percentage modifier to apply.")] + public readonly int Multiplier = 100; + + [Desc("Only apply this cost change if owner has these prerequisites.")] + public readonly string[] Prerequisites = { }; + + [Desc("Queues that this cost will apply.")] + public readonly HashSet Queue = new HashSet(); + + int IProductionCostModifierInfo.GetProductionCostModifier(TechTree techTree, string queue) + { + if ((!Queue.Any() || Queue.Contains(queue)) && (!Prerequisites.Any() || techTree.HasPrerequisites(Prerequisites))) + return Multiplier; + + return 100; + } + } + + public class ProductionCostMultiplier { } +} diff --git a/OpenRA.Mods.Common/Traits/Multipliers/ProductionTimeMultiplier.cs b/OpenRA.Mods.Common/Traits/Multipliers/ProductionTimeMultiplier.cs new file mode 100644 index 0000000000..aa356a9460 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/Multipliers/ProductionTimeMultiplier.cs @@ -0,0 +1,41 @@ +#region Copyright & License Information +/* + * Copyright 2007-2019 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, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System.Collections.Generic; +using System.Linq; +using OpenRA.Primitives; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("Modifies the production time of this actor for a specific queue or when a prerequisite is granted.")] + public class ProductionTimeMultiplierInfo : TraitInfo, IProductionTimeModifierInfo + { + [Desc("Percentage modifier to apply.")] + public readonly int Multiplier = 100; + + [Desc("Only apply this time change if owner has these prerequisites.")] + public readonly string[] Prerequisites = { }; + + [Desc("Queues that this time will apply.")] + public readonly HashSet Queue = new HashSet(); + + int IProductionTimeModifierInfo.GetProductionTimeModifier(TechTree techTree, string queue) + { + if ((!Queue.Any() || Queue.Contains(queue)) && (!Prerequisites.Any() || techTree.HasPrerequisites(Prerequisites))) + return Multiplier; + + return 100; + } + } + + public class ProductionTimeMultiplier { } +} diff --git a/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs b/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs index d571b74dc6..6fd87d36b8 100644 --- a/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs +++ b/OpenRA.Mods.Common/Traits/Player/ProductionQueue.cs @@ -111,6 +111,7 @@ namespace OpenRA.Mods.Common.Traits PowerManager playerPower; protected PlayerResources playerResources; protected DeveloperMode developerMode; + protected TechTree techTree; public Actor Actor { get { return self; } } @@ -123,14 +124,11 @@ namespace OpenRA.Mods.Common.Traits { self = init.Self; Info = info; - playerResources = playerActor.Trait(); - developerMode = playerActor.Trait(); Faction = init.Contains() ? init.Get() : self.Owner.Faction.InternalName; IsValidFaction = !info.Factions.Any() || info.Factions.Contains(Faction); Enabled = IsValidFaction; - CacheProducibles(playerActor); allProducibles = Producible.Where(a => a.Value.Buildable || a.Value.Visible).Select(a => a.Key); buildableProducibles = Producible.Where(a => a.Value.Buildable).Select(a => a.Key); } @@ -141,8 +139,15 @@ namespace OpenRA.Mods.Common.Traits // Created is called before Player.PlayerActor is assigned, // so we must query other player traits from self, knowing that // it refers to the same actor as self.Owner.PlayerActor - playerPower = (self.Info.Name == "player" ? self : self.Owner.PlayerActor).TraitOrDefault(); + var playerActor = self.Info.Name == "player" ? self : self.Owner.PlayerActor; + + playerPower = playerActor.TraitOrDefault(); + playerResources = playerActor.Trait(); + developerMode = playerActor.Trait(); + techTree = playerActor.Trait(); + productionTraits = self.TraitsImplementing().Where(p => p.Info.Produces.Contains(Info.Type)).ToArray(); + CacheProducibles(playerActor); } protected void ClearQueue() @@ -160,6 +165,7 @@ namespace OpenRA.Mods.Common.Traits playerPower = newOwner.PlayerActor.TraitOrDefault(); playerResources = newOwner.PlayerActor.Trait(); developerMode = newOwner.PlayerActor.Trait(); + techTree = newOwner.PlayerActor.Trait(); if (!Info.Sticky) { @@ -170,7 +176,7 @@ namespace OpenRA.Mods.Common.Traits // Regenerate the producibles and tech tree state oldOwner.PlayerActor.Trait().Remove(this); CacheProducibles(newOwner.PlayerActor); - newOwner.PlayerActor.Trait().Update(); + techTree.Update(); } void INotifyKilled.Killed(Actor killed, AttackInfo e) { if (killed == self) { ClearQueue(); Enabled = false; } } @@ -187,14 +193,12 @@ namespace OpenRA.Mods.Common.Traits if (!Enabled) return; - var ttc = playerActor.Trait(); - foreach (var a in AllBuildables(Info.Type)) { var bi = a.TraitInfo(); Producible.Add(a, new ProductionState()); - ttc.Add(a.Name, bi.Prerequisites, bi.BuildLimit, this); + techTree.Add(a.Name, bi.Prerequisites, bi.BuildLimit, this); } } @@ -393,8 +397,7 @@ namespace OpenRA.Mods.Common.Traits return; } - var valued = unit.TraitInfoOrDefault(); - var cost = valued != null ? valued.Cost : 0; + var cost = GetProductionCost(unit); var time = GetBuildTime(unit, bi); var amountToBuild = Math.Min(fromLimit, order.ExtraData); for (var n = 0; n < amountToBuild; n++) @@ -434,13 +437,26 @@ namespace OpenRA.Mods.Common.Traits var time = bi.BuildDuration; if (time == -1) - { - var valued = unit.TraitInfoOrDefault(); - time = valued != null ? valued.Cost : 0; - } + time = GetProductionCost(unit); - time = time * bi.BuildDurationModifier * Info.BuildDurationModifier / 10000; - return time; + var modifiers = unit.TraitInfos() + .Select(t => t.GetProductionTimeModifier(techTree, Info.Type)) + .Append(bi.BuildDurationModifier) + .Append(Info.BuildDurationModifier); + + return Util.ApplyPercentageModifiers(time, modifiers); + } + + public virtual int GetProductionCost(ActorInfo unit) + { + var valued = unit.TraitInfoOrDefault(); + if (valued == null) + return 0; + + var modifiers = unit.TraitInfos() + .Select(t => t.GetProductionCostModifier(techTree, Info.Type)); + + return Util.ApplyPercentageModifiers(valued.Cost, modifiers); } protected void PauseProduction(string itemName, bool paused) diff --git a/OpenRA.Mods.Common/TraitsInterfaces.cs b/OpenRA.Mods.Common/TraitsInterfaces.cs index ef6ef5811f..b14f3919aa 100644 --- a/OpenRA.Mods.Common/TraitsInterfaces.cs +++ b/OpenRA.Mods.Common/TraitsInterfaces.cs @@ -323,6 +323,12 @@ namespace OpenRA.Mods.Common.Traits string SequencePrefix { get; } } + [RequireExplicitImplementation] + public interface IProductionCostModifierInfo : ITraitInfo { int GetProductionCostModifier(TechTree techTree, string queue); } + + [RequireExplicitImplementation] + public interface IProductionTimeModifierInfo : ITraitInfo { int GetProductionTimeModifier(TechTree techTree, string queue); } + [RequireExplicitImplementation] public interface ICashTricklerModifier { int GetCashTricklerModifier(); } diff --git a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ProductionTooltipLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ProductionTooltipLogic.cs index e7841c1077..93c97baff8 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Ingame/ProductionTooltipLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Ingame/ProductionTooltipLogic.cs @@ -70,8 +70,16 @@ namespace OpenRA.Mods.Common.Widgets.Logic var tooltip = actor.TraitInfos().FirstOrDefault(info => info.EnabledByDefault); var name = tooltip != null ? tooltip.Name : actor.Name; var buildable = actor.TraitInfo(); - var valued = actor.TraitInfoOrDefault(); - var cost = valued != null ? valued.Cost : 0; + + var cost = 0; + if (tooltipIcon.ProductionQueue != null) + cost = tooltipIcon.ProductionQueue.GetProductionCost(actor); + else + { + var valued = actor.TraitInfoOrDefault(); + if (valued != null) + cost = valued.Cost; + } nameLabel.Text = name;