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;