Tidy production traits.

This commit is contained in:
Paul Chote
2014-06-21 15:56:50 +12:00
parent f46f71217e
commit 7b3a0ebeb5
7 changed files with 160 additions and 133 deletions

View File

@@ -81,7 +81,7 @@ namespace OpenRA.Mods.Cnc.Widgets
public override void Tick() public override void Tick()
{ {
if (CurrentQueue != null && !CurrentQueue.self.IsInWorld) if (CurrentQueue != null && !CurrentQueue.Actor.IsInWorld)
CurrentQueue = null; CurrentQueue = null;
if (CurrentQueue != null) if (CurrentQueue != null)
@@ -125,20 +125,20 @@ namespace OpenRA.Mods.Cnc.Widgets
if (first != null && first.Done && actor.Traits.Contains<BuildingInfo>()) if (first != null && first.Done && actor.Traits.Contains<BuildingInfo>())
{ {
Sound.Play(TabClick); Sound.Play(TabClick);
World.OrderGenerator = new PlaceBuildingOrderGenerator(CurrentQueue.self, icon.Name); World.OrderGenerator = new PlaceBuildingOrderGenerator(CurrentQueue.Actor, icon.Name);
} }
else if (first != null && first.Paused) else if (first != null && first.Paused)
{ {
// Resume a paused item // Resume a paused item
Sound.Play(TabClick); Sound.Play(TabClick);
World.IssueOrder(Order.PauseProduction(CurrentQueue.self, icon.Name, false)); World.IssueOrder(Order.PauseProduction(CurrentQueue.Actor, icon.Name, false));
} }
else if (CurrentQueue.BuildableItems().Any(a => a.Name == icon.Name)) else if (CurrentQueue.BuildableItems().Any(a => a.Name == icon.Name))
{ {
// Queue a new item // Queue a new item
Sound.Play(TabClick); Sound.Play(TabClick);
Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Speech", CurrentQueue.Info.QueuedAudio, World.LocalPlayer.Country.Race); Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Speech", CurrentQueue.Info.QueuedAudio, World.LocalPlayer.Country.Race);
World.IssueOrder(Order.StartProduction(CurrentQueue.self, icon.Name, World.IssueOrder(Order.StartProduction(CurrentQueue.Actor, icon.Name,
Game.GetModifierKeys().HasModifier(Modifiers.Shift) ? 5 : 1)); Game.GetModifierKeys().HasModifier(Modifiers.Shift) ? 5 : 1));
} }
else else
@@ -155,13 +155,13 @@ namespace OpenRA.Mods.Cnc.Widgets
if (first.Paused || first.Done || first.TotalCost == first.RemainingCost) if (first.Paused || first.Done || first.TotalCost == first.RemainingCost)
{ {
Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Speech", CurrentQueue.Info.CancelledAudio, World.LocalPlayer.Country.Race); Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Speech", CurrentQueue.Info.CancelledAudio, World.LocalPlayer.Country.Race);
World.IssueOrder(Order.CancelProduction(CurrentQueue.self, icon.Name, World.IssueOrder(Order.CancelProduction(CurrentQueue.Actor, icon.Name,
Game.GetModifierKeys().HasModifier(Modifiers.Shift) ? 5 : 1)); Game.GetModifierKeys().HasModifier(Modifiers.Shift) ? 5 : 1));
} }
else else
{ {
Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Speech", CurrentQueue.Info.OnHoldAudio, World.LocalPlayer.Country.Race); Sound.PlayNotification(World.Map.Rules, World.LocalPlayer, "Speech", CurrentQueue.Info.OnHoldAudio, World.LocalPlayer.Country.Race);
World.IssueOrder(Order.PauseProduction(CurrentQueue.self, icon.Name, true)); World.IssueOrder(Order.PauseProduction(CurrentQueue.Actor, icon.Name, true));
} }
} }
else else

View File

@@ -51,7 +51,7 @@ namespace OpenRA.Mods.RA.AI
{ {
HackyAI.BotDebug("AI: Starting production of {0}".F(item.Name)); HackyAI.BotDebug("AI: Starting production of {0}".F(item.Name));
state = BuildState.WaitForProduction; state = BuildState.WaitForProduction;
ai.world.IssueOrder(Order.StartProduction(queue.self, item.Name, 1)); ai.world.IssueOrder(Order.StartProduction(queue.Actor, item.Name, 1));
} }
break; break;
@@ -60,7 +60,7 @@ namespace OpenRA.Mods.RA.AI
return; return;
if (currentBuilding.Paused) if (currentBuilding.Paused)
ai.world.IssueOrder(Order.PauseProduction(queue.self, currentBuilding.Item, false)); ai.world.IssueOrder(Order.PauseProduction(queue.Actor, currentBuilding.Item, false));
else if (currentBuilding.Done) else if (currentBuilding.Done)
{ {
state = BuildState.WaitForFeedback; state = BuildState.WaitForFeedback;
@@ -77,7 +77,7 @@ namespace OpenRA.Mods.RA.AI
if (location == null) if (location == null)
{ {
HackyAI.BotDebug("AI: Nowhere to place {0}".F(currentBuilding.Item)); HackyAI.BotDebug("AI: Nowhere to place {0}".F(currentBuilding.Item));
ai.world.IssueOrder(Order.CancelProduction(queue.self, currentBuilding.Item, 1)); ai.world.IssueOrder(Order.CancelProduction(queue.Actor, currentBuilding.Item, 1));
} }
else else
{ {

View File

@@ -839,7 +839,7 @@ namespace OpenRA.Mods.RA.AI
ChooseUnitToBuild(queue); ChooseUnitToBuild(queue);
if (unit != null && Info.UnitsToBuild.Any(u => u.Key == unit.Name)) if (unit != null && Info.UnitsToBuild.Any(u => u.Key == unit.Name))
world.IssueOrder(Order.StartProduction(queue.self, unit.Name, 1)); world.IssueOrder(Order.StartProduction(queue.Actor, unit.Name, 1));
} }
void BuildUnit(string category, string name) void BuildUnit(string category, string name)
@@ -849,7 +849,7 @@ namespace OpenRA.Mods.RA.AI
return; return;
if (Map.Rules.Actors[name] != null) if (Map.Rules.Actors[name] != null)
world.IssueOrder(Order.StartProduction(queue.self, name, 1)); world.IssueOrder(Order.StartProduction(queue.Actor, name, 1));
} }
public void Damaged(Actor self, AttackInfo e) public void Damaged(Actor self, AttackInfo e)

View File

@@ -23,22 +23,27 @@ namespace OpenRA.Mods.RA
{ {
[Desc("If you build more actors of the same type,", "the same queue will get its build time lowered for every actor produced there.")] [Desc("If you build more actors of the same type,", "the same queue will get its build time lowered for every actor produced there.")]
public readonly bool SpeedUp = false; public readonly bool SpeedUp = false;
[Desc("Every time another production building of the same queue is", [Desc("Every time another production building of the same queue is",
"contructed, the build times of all actors in the queue", "contructed, the build times of all actors in the queue",
"decreased by a percentage of the original time.")] "decreased by a percentage of the original time.")]
public readonly int[] BuildTimeSpeedReduction = { 100, 85, 75, 65, 60, 55, 50 }; public readonly int[] BuildTimeSpeedReduction = { 100, 85, 75, 65, 60, 55, 50 };
public override object Create(ActorInitializer init) { return new ClassicProductionQueue(init.self, this); } public override object Create(ActorInitializer init) { return new ClassicProductionQueue(init, this); }
} }
public class ClassicProductionQueue : ProductionQueue, ISync public class ClassicProductionQueue : ProductionQueue, ISync
{ {
public new ClassicProductionQueueInfo Info; static readonly ActorInfo[] NoItems = { };
public ClassicProductionQueue(Actor self, ClassicProductionQueueInfo info) readonly Actor self;
: base(self, self, info) readonly ClassicProductionQueueInfo info;
public ClassicProductionQueue(ActorInitializer init, ClassicProductionQueueInfo info)
: base(init, init.self, info)
{ {
this.Info = info; this.self = init.self;
this.info = info;
} }
[Sync] bool isActive = false; [Sync] bool isActive = false;
@@ -47,23 +52,25 @@ namespace OpenRA.Mods.RA
{ {
isActive = false; isActive = false;
foreach (var x in self.World.ActorsWithTrait<Production>()) foreach (var x in self.World.ActorsWithTrait<Production>())
{
if (x.Actor.Owner == self.Owner && x.Trait.Info.Produces.Contains(Info.Type)) if (x.Actor.Owner == self.Owner && x.Trait.Info.Produces.Contains(Info.Type))
{ {
isActive = true; isActive = true;
break; break;
} }
}
base.Tick(self); base.Tick(self);
} }
static ActorInfo[] None = { };
public override IEnumerable<ActorInfo> AllItems() public override IEnumerable<ActorInfo> AllItems()
{ {
return isActive ? base.AllItems() : None; return isActive ? base.AllItems() : NoItems;
} }
public override IEnumerable<ActorInfo> BuildableItems() public override IEnumerable<ActorInfo> BuildableItems()
{ {
return isActive ? base.BuildableItems() : None; return isActive ? base.BuildableItems() : NoItems;
} }
protected override bool BuildUnit(string name) protected override bool BuildUnit(string name)
@@ -89,10 +96,11 @@ namespace OpenRA.Mods.RA
return true; return true;
} }
} }
return false; return false;
} }
public override int GetBuildTime(String unitString) public override int GetBuildTime(string unitString)
{ {
var unit = self.World.Map.Rules.Actors[unitString]; var unit = self.World.Map.Rules.Actors[unitString];
if (unit == null || !unit.Traits.Contains<BuildableInfo>()) if (unit == null || !unit.Traits.Contains<BuildableInfo>())
@@ -103,15 +111,15 @@ namespace OpenRA.Mods.RA
var time = (int)(unit.GetBuildTime() * Info.BuildSpeed); var time = (int)(unit.GetBuildTime() * Info.BuildSpeed);
if (Info.SpeedUp) if (info.SpeedUp)
{ {
var queues = unit.Traits.Get<BuildableInfo>().Queue; var queues = unit.Traits.Get<BuildableInfo>().Queue;
var selfsameBuildings = self.World.ActorsWithTrait<Production>() var selfsameBuildings = self.World.ActorsWithTrait<Production>()
.Where(p => p.Actor.Owner == self.Owner && p.Trait.Info.Produces.Intersect(queues).Any()) .Where(p => p.Actor.Owner == self.Owner && p.Trait.Info.Produces.Intersect(queues).Any())
.ToArray(); .ToArray();
var speedModifier = selfsameBuildings.Count().Clamp(1, Info.BuildTimeSpeedReduction.Length) - 1; var speedModifier = selfsameBuildings.Count().Clamp(1, info.BuildTimeSpeedReduction.Length) - 1;
time = (time * Info.BuildTimeSpeedReduction[speedModifier]) / 100; time = (time * info.BuildTimeSpeedReduction[speedModifier]) / 100;
} }
return time; return time;

View File

@@ -23,6 +23,7 @@ namespace OpenRA.Mods.RA
{ {
[Desc("What kind of production will be added (e.g. Building, Infantry, Vehicle, ...)")] [Desc("What kind of production will be added (e.g. Building, Infantry, Vehicle, ...)")]
public readonly string Type = null; public readonly string Type = null;
[Desc("Group queues from separate buildings together into the same tab.")] [Desc("Group queues from separate buildings together into the same tab.")]
public readonly string Group = null; public readonly string Group = null;
@@ -30,96 +31,99 @@ namespace OpenRA.Mods.RA
public readonly bool RequireOwner = true; public readonly bool RequireOwner = true;
[Desc("This value is used to translate the unit cost into build time.")] [Desc("This value is used to translate the unit cost into build time.")]
public float BuildSpeed = 0.4f; public readonly float BuildSpeed = 0.4f;
[Desc("The build time is multiplied with this value on low power.")] [Desc("The build time is multiplied with this value on low power.")]
public readonly int LowPowerSlowdown = 3; public readonly int LowPowerSlowdown = 3;
[Desc("Notification played when production is complete.", [Desc("Notification played when production is complete.",
"The filename of the audio is defined per faction in notifications.yaml.")] "The filename of the audio is defined per faction in notifications.yaml.")]
public readonly string ReadyAudio = "UnitReady"; public readonly string ReadyAudio = "UnitReady";
[Desc("Notification played when you can't train another unit", [Desc("Notification played when you can't train another unit",
"when the build limit exceeded or the exit is jammed.", "when the build limit exceeded or the exit is jammed.",
"The filename of the audio is defined per faction in notifications.yaml.")] "The filename of the audio is defined per faction in notifications.yaml.")]
public readonly string BlockedAudio = "NoBuild"; public readonly string BlockedAudio = "NoBuild";
[Desc("Notification played when user clicks on the build palette icon.", [Desc("Notification played when user clicks on the build palette icon.",
"The filename of the audio is defined per faction in notifications.yaml.")] "The filename of the audio is defined per faction in notifications.yaml.")]
public readonly string QueuedAudio = "Training"; public readonly string QueuedAudio = "Training";
[Desc("Notification played when player right-clicks on the build palette icon.", [Desc("Notification played when player right-clicks on the build palette icon.",
"The filename of the audio is defined per faction in notifications.yaml.")] "The filename of the audio is defined per faction in notifications.yaml.")]
public readonly string OnHoldAudio = "OnHold"; public readonly string OnHoldAudio = "OnHold";
[Desc("Notification played when player right-clicks on a build palette icon that is already on hold.", [Desc("Notification played when player right-clicks on a build palette icon that is already on hold.",
"The filename of the audio is defined per faction in notifications.yaml.")] "The filename of the audio is defined per faction in notifications.yaml.")]
public readonly string CancelledAudio = "Cancelled"; public readonly string CancelledAudio = "Cancelled";
public virtual object Create(ActorInitializer init) { return new ProductionQueue(init.self, init.self.Owner.PlayerActor, this); } public virtual object Create(ActorInitializer init) { return new ProductionQueue(init, init.self.Owner.PlayerActor, this); }
} }
public class ProductionQueue : IResolveOrder, ITick, ITechTreeElement, INotifyCapture, INotifyKilled, INotifySold, ISync, INotifyTransform public class ProductionQueue : IResolveOrder, ITick, ITechTreeElement, INotifyCapture, INotifyKilled, INotifySold, ISync, INotifyTransform
{ {
public readonly Actor self; public readonly ProductionQueueInfo Info;
public ProductionQueueInfo Info; readonly Actor self;
PowerManager PlayerPower; readonly string race;
// Can change if the actor is captured
PowerManager playerPower;
PlayerResources playerResources; PlayerResources playerResources;
readonly CountryInfo Race;
// A list of things we could possibly build
Dictionary<ActorInfo, ProductionState> produceable;
List<ProductionItem> queue = new List<ProductionItem>();
// A list of things we are currently building // A list of things we are currently building
public List<ProductionItem> Queue = new List<ProductionItem>(); public Actor Actor { get { return self; } }
[Sync] public int QueueLength { get { return Queue.Count; } } [Sync] public int QueueLength { get { return queue.Count; } }
[Sync] public int CurrentRemainingCost { get { return QueueLength == 0 ? 0 : Queue[0].RemainingCost; } } [Sync] public int CurrentRemainingCost { get { return QueueLength == 0 ? 0 : queue[0].RemainingCost; } }
[Sync] public int CurrentRemainingTime { get { return QueueLength == 0 ? 0 : Queue[0].RemainingTime; } } [Sync] public int CurrentRemainingTime { get { return QueueLength == 0 ? 0 : queue[0].RemainingTime; } }
[Sync] public int CurrentSlowdown { get { return QueueLength == 0 ? 0 : Queue[0].slowdown; } } [Sync] public int CurrentSlowdown { get { return QueueLength == 0 ? 0 : queue[0].Slowdown; } }
[Sync] public bool CurrentPaused { get { return QueueLength != 0 && Queue[0].Paused; } } [Sync] public bool CurrentPaused { get { return QueueLength != 0 && queue[0].Paused; } }
[Sync] public bool CurrentDone { get { return QueueLength != 0 && Queue[0].Done; } } [Sync] public bool CurrentDone { get { return QueueLength != 0 && queue[0].Done; } }
// A list of things we could possibly build, even if our race doesn't normally get it public ProductionQueue(ActorInitializer init, Actor playerActor, ProductionQueueInfo info)
public Dictionary<ActorInfo, ProductionState> Produceable;
public ProductionQueue( Actor self, Actor playerActor, ProductionQueueInfo info )
{ {
this.self = self; self = init.self;
this.Info = info; Info = info;
playerResources = playerActor.Trait<PlayerResources>(); playerResources = playerActor.Trait<PlayerResources>();
PlayerPower = playerActor.Trait<PowerManager>(); playerPower = playerActor.Trait<PowerManager>();
Race = self.Owner.Country; race = self.Owner.Country.Race;
Produceable = InitTech(playerActor); CacheProduceables(playerActor);
} }
void ClearQueue() void ClearQueue()
{ {
if (Queue.Count == 0) if (queue.Count == 0)
return; return;
// Refund the current item // Refund the current item
playerResources.GiveCash(Queue[0].TotalCost - Queue[0].RemainingCost); playerResources.GiveCash(queue[0].TotalCost - queue[0].RemainingCost);
Queue.Clear(); queue.Clear();
} }
public void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner) public void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner)
{ {
PlayerPower = newOwner.PlayerActor.Trait<PowerManager>(); playerPower = newOwner.PlayerActor.Trait<PowerManager>();
playerResources = newOwner.PlayerActor.Trait<PlayerResources>(); playerResources = newOwner.PlayerActor.Trait<PlayerResources>();
ClearQueue(); ClearQueue();
// Produceable contains the tech from the original owner - this is desired so we don't clear it. // Regenerate the produceables and tech tree state
Produceable = InitTech(self.Owner.PlayerActor); CacheProduceables(newOwner.PlayerActor);
// Force a third(!) tech tree update to ensure that prerequisites are correct.
// The first two updates are triggered by adding/removing the actor when
// changing ownership, *before* the new techtree watchers have been set up.
// This is crap.
self.Owner.PlayerActor.Trait<TechTree>().Update(); self.Owner.PlayerActor.Trait<TechTree>().Update();
} }
public void Killed(Actor killed, AttackInfo e) { if (killed == self) ClearQueue(); } public void Killed(Actor killed, AttackInfo e) { if (killed == self) ClearQueue(); }
public void Selling(Actor self) {} public void Selling(Actor self) { }
public void Sold(Actor self) { ClearQueue(); } public void Sold(Actor self) { ClearQueue(); }
public void OnTransform(Actor self) { ClearQueue(); } public void OnTransform(Actor self) { ClearQueue(); }
Dictionary<ActorInfo, ProductionState> InitTech(Actor playerActor) void CacheProduceables(Actor playerActor)
{ {
var tech = new Dictionary<ActorInfo, ProductionState>(); produceable = new Dictionary<ActorInfo, ProductionState>();
var ttc = playerActor.Trait<TechTree>(); var ttc = playerActor.Trait<TechTree>();
foreach (var a in AllBuildables(Info.Type)) foreach (var a in AllBuildables(Info.Type))
@@ -127,102 +131,101 @@ namespace OpenRA.Mods.RA
var bi = a.Traits.Get<BuildableInfo>(); var bi = a.Traits.Get<BuildableInfo>();
// Can our race build this by satisfying normal prerequisites? // Can our race build this by satisfying normal prerequisites?
var buildable = !Info.RequireOwner || bi.Owner.Contains(Race.Race); var buildable = !Info.RequireOwner || bi.Owner.Contains(race);
// Checks if Prerequisites want to hide the Actor from buildQueue if they are false // Checks if Prerequisites want to hide the Actor from buildQueue if they are false
tech.Add(a, new ProductionState { Visible = buildable }); produceable.Add(a, new ProductionState { Visible = buildable });
if (buildable) if (buildable)
ttc.Add(a.Name, bi.Prerequisites, bi.BuildLimit, this); ttc.Add(a.Name, bi.Prerequisites, bi.BuildLimit, this);
} }
return tech;
} }
IEnumerable<ActorInfo> AllBuildables(string category) IEnumerable<ActorInfo> AllBuildables(string category)
{ {
return self.World.Map.Rules.Actors.Values return self.World.Map.Rules.Actors.Values
.Where(x => .Where(x =>
x.Name[ 0 ] != '^' && x.Name[0] != '^' &&
x.Traits.Contains<BuildableInfo>() && x.Traits.Contains<BuildableInfo>() &&
x.Traits.Get<BuildableInfo>().Queue.Contains(category)); x.Traits.Get<BuildableInfo>().Queue.Contains(category));
} }
public void OverrideProduction(ActorInfo type, bool buildable) public void OverrideProduction(ActorInfo type, bool buildable)
{ {
Produceable[type].Buildable = buildable; produceable[type].Buildable = buildable;
Produceable[type].Sticky = true; produceable[type].Sticky = true;
} }
public void PrerequisitesAvailable(string key) public void PrerequisitesAvailable(string key)
{ {
var ps = Produceable[ self.World.Map.Rules.Actors[key] ]; var ps = produceable[self.World.Map.Rules.Actors[key]];
if (!ps.Sticky) if (!ps.Sticky)
ps.Buildable = true; ps.Buildable = true;
} }
public void PrerequisitesUnavailable(string key) public void PrerequisitesUnavailable(string key)
{ {
var ps = Produceable[ self.World.Map.Rules.Actors[key] ]; var ps = produceable[self.World.Map.Rules.Actors[key]];
if (!ps.Sticky) if (!ps.Sticky)
ps.Buildable = false; ps.Buildable = false;
} }
public void PrerequisitesItemHidden(string key) public void PrerequisitesItemHidden(string key)
{ {
Produceable[self.World.Map.Rules.Actors[key]].Visible = false; produceable[self.World.Map.Rules.Actors[key]].Visible = false;
} }
public void PrerequisitesItemVisible(string key) public void PrerequisitesItemVisible(string key)
{ {
Produceable[self.World.Map.Rules.Actors[key]].Visible = true; produceable[self.World.Map.Rules.Actors[key]].Visible = true;
} }
public ProductionItem CurrentItem() public ProductionItem CurrentItem()
{ {
return Queue.ElementAtOrDefault(0); return queue.ElementAtOrDefault(0);
} }
public IEnumerable<ProductionItem> AllQueued() public IEnumerable<ProductionItem> AllQueued()
{ {
return Queue; return queue;
} }
public virtual IEnumerable<ActorInfo> AllItems() public virtual IEnumerable<ActorInfo> AllItems()
{ {
if (self.World.AllowDevCommands && self.Owner.PlayerActor.Trait<DeveloperMode>().AllTech) if (self.World.AllowDevCommands && self.Owner.PlayerActor.Trait<DeveloperMode>().AllTech)
return Produceable.Select(a => a.Key); return produceable.Select(a => a.Key);
return Produceable.Where(a => a.Value.Buildable || a.Value.Visible).Select(a => a.Key); return produceable.Where(a => a.Value.Buildable || a.Value.Visible).Select(a => a.Key);
} }
public virtual IEnumerable<ActorInfo> BuildableItems() public virtual IEnumerable<ActorInfo> BuildableItems()
{ {
if (self.World.AllowDevCommands && self.Owner.PlayerActor.Trait<DeveloperMode>().AllTech) if (self.World.AllowDevCommands && self.Owner.PlayerActor.Trait<DeveloperMode>().AllTech)
return Produceable.Select(a => a.Key); return produceable.Select(a => a.Key);
return Produceable.Where(a => a.Value.Buildable).Select(a => a.Key); return produceable.Where(a => a.Value.Buildable).Select(a => a.Key);
} }
public bool CanBuild(ActorInfo actor) public bool CanBuild(ActorInfo actor)
{ {
return Produceable.ContainsKey(actor) && Produceable[actor].Buildable; return produceable.ContainsKey(actor) && produceable[actor].Buildable;
} }
public virtual void Tick(Actor self) public virtual void Tick(Actor self)
{ {
while (Queue.Count > 0 && BuildableItems().All(b => b.Name != Queue[ 0 ].Item)) while (queue.Count > 0 && BuildableItems().All(b => b.Name != queue[0].Item))
{ {
playerResources.GiveCash(Queue[0].TotalCost - Queue[0].RemainingCost); // refund what's been paid so far. playerResources.GiveCash(queue[0].TotalCost - queue[0].RemainingCost); // refund what's been paid so far.
FinishProduction(); FinishProduction();
} }
if (Queue.Count > 0)
Queue[ 0 ].Tick(playerResources); if (queue.Count > 0)
queue[0].Tick(playerResources);
} }
public void ResolveOrder(Actor self, Order order) public void ResolveOrder(Actor self, Order order)
{ {
switch(order.OrderString) switch (order.OrderString)
{ {
case "StartProduction": case "StartProduction":
{ {
@@ -241,7 +244,7 @@ namespace OpenRA.Mods.RA
var fromLimit = int.MaxValue; var fromLimit = int.MaxValue;
if (bi.BuildLimit > 0) if (bi.BuildLimit > 0)
{ {
var inQueue = Queue.Count(pi => pi.Item == order.TargetString); var inQueue = queue.Count(pi => pi.Item == order.TargetString);
var owned = self.Owner.World.ActorsWithTrait<Buildable>().Count(a => a.Actor.Info.Name == order.TargetString && a.Actor.Owner == self.Owner); var owned = self.Owner.World.ActorsWithTrait<Buildable>().Count(a => a.Actor.Info.Name == order.TargetString && a.Actor.Owner == self.Owner);
fromLimit = bi.BuildLimit - (inQueue + owned); fromLimit = bi.BuildLimit - (inQueue + owned);
@@ -250,37 +253,39 @@ namespace OpenRA.Mods.RA
} }
var amountToBuild = Math.Min(fromLimit, order.ExtraData); var amountToBuild = Math.Min(fromLimit, order.ExtraData);
for (var n = 0; n < amountToBuild; n++)
for (var n = 0; n < amountToBuild; n++) // repeat count
{ {
var hasPlayedSound = false; var hasPlayedSound = false;
BeginProduction(new ProductionItem(this, order.TargetString, cost, PlayerPower, BeginProduction(new ProductionItem(this, order.TargetString, cost, playerPower, () => self.World.AddFrameEndTask(_ =>
() => self.World.AddFrameEndTask(_ => {
var isBuilding = unit.Traits.Contains<BuildingInfo>();
if (isBuilding && !hasPlayedSound)
{
hasPlayedSound = Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.ReadyAudio, self.Owner.Country.Race);
}
else if (!isBuilding)
{
if (BuildUnit(order.TargetString))
Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.ReadyAudio, self.Owner.Country.Race);
else if (!hasPlayedSound && time > 0)
{ {
var isBuilding = unit.Traits.Contains<BuildingInfo>(); hasPlayedSound = Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.BlockedAudio, self.Owner.Country.Race);
if (isBuilding && !hasPlayedSound) }
{ }
hasPlayedSound = Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.ReadyAudio, self.Owner.Country.Race); })));
}
else if (!isBuilding)
{
if (BuildUnit(order.TargetString))
Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.ReadyAudio, self.Owner.Country.Race);
else if (!hasPlayedSound && time > 0)
{
hasPlayedSound = Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.BlockedAudio, self.Owner.Country.Race);
}
}
})));
} }
break; break;
} }
case "PauseProduction": case "PauseProduction":
{ {
if (Queue.Count > 0 && Queue[0].Item == order.TargetString) if (queue.Count > 0 && queue[0].Item == order.TargetString)
Queue[0].Paused = ( order.ExtraData != 0 ); queue[0].Pause(order.ExtraData != 0);
break; break;
} }
case "CancelProduction": case "CancelProduction":
{ {
CancelProduction(order.TargetString, order.ExtraData); CancelProduction(order.TargetString, order.ExtraData);
@@ -289,10 +294,10 @@ namespace OpenRA.Mods.RA
} }
} }
virtual public int GetBuildTime(String unitString) public virtual int GetBuildTime(string unitString)
{ {
var unit = self.World.Map.Rules.Actors[unitString]; var unit = self.World.Map.Rules.Actors[unitString];
if (unit == null || ! unit.Traits.Contains<BuildableInfo>()) if (unit == null || !unit.Traits.Contains<BuildableInfo>())
return 0; return 0;
if (self.World.AllowDevCommands && self.Owner.PlayerActor.Trait<DeveloperMode>().FastBuild) if (self.World.AllowDevCommands && self.Owner.PlayerActor.Trait<DeveloperMode>().FastBuild)
@@ -300,7 +305,7 @@ namespace OpenRA.Mods.RA
var time = unit.GetBuildTime() * Info.BuildSpeed; var time = unit.GetBuildTime() * Info.BuildSpeed;
return (int) time; return (int)time;
} }
protected void CancelProduction(string itemName, uint numberToCancel) protected void CancelProduction(string itemName, uint numberToCancel)
@@ -311,13 +316,13 @@ namespace OpenRA.Mods.RA
void CancelProductionInner(string itemName) void CancelProductionInner(string itemName)
{ {
var lastIndex = Queue.FindLastIndex(a => a.Item == itemName); var lastIndex = queue.FindLastIndex(a => a.Item == itemName);
if (lastIndex > 0) if (lastIndex > 0)
Queue.RemoveAt(lastIndex); queue.RemoveAt(lastIndex);
else if (lastIndex == 0) else if (lastIndex == 0)
{ {
var item = Queue[0]; var item = queue[0];
playerResources.GiveCash(item.TotalCost - item.RemainingCost); // refund what has been paid playerResources.GiveCash(item.TotalCost - item.RemainingCost); // refund what has been paid
FinishProduction(); FinishProduction();
} }
@@ -325,13 +330,13 @@ namespace OpenRA.Mods.RA
public void FinishProduction() public void FinishProduction()
{ {
if (Queue.Count == 0) return; if (queue.Count != 0)
Queue.RemoveAt(0); queue.RemoveAt(0);
} }
protected void BeginProduction(ProductionItem item) protected void BeginProduction(ProductionItem item)
{ {
Queue.Add(item); queue.Add(item);
} }
// Builds a unit from the actor that holds this queue (1 queue per building) // Builds a unit from the actor that holds this queue (1 queue per building)
@@ -351,6 +356,7 @@ namespace OpenRA.Mods.RA
FinishProduction(); FinishProduction();
return true; return true;
} }
return false; return false;
} }
} }
@@ -366,9 +372,10 @@ namespace OpenRA.Mods.RA
{ {
public readonly string Item; public readonly string Item;
public readonly ProductionQueue Queue; public readonly ProductionQueue Queue;
readonly PowerManager pm;
public int TotalTime;
public readonly int TotalCost; public readonly int TotalCost;
public readonly Action OnComplete;
public int TotalTime { get; private set; }
public int RemainingTime { get; private set; } public int RemainingTime { get; private set; }
public int RemainingCost { get; private set; } public int RemainingCost { get; private set; }
public int RemainingTimeActual public int RemainingTimeActual
@@ -380,9 +387,12 @@ namespace OpenRA.Mods.RA
} }
} }
public bool Paused = false, Done = false, Started = false; public bool Paused { get; private set; }
public Action OnComplete; public bool Done { get; private set; }
public int slowdown = 0; public bool Started { get; private set; }
public int Slowdown { get; private set; }
readonly PowerManager pm;
public ProductionItem(ProductionQueue queue, string item, int cost, PowerManager pm, Action onComplete) public ProductionItem(ProductionQueue queue, string item, int cost, PowerManager pm, Action onComplete)
{ {
@@ -392,7 +402,6 @@ namespace OpenRA.Mods.RA
OnComplete = onComplete; OnComplete = onComplete;
Queue = queue; Queue = queue;
this.pm = pm; this.pm = pm;
//Log.Write("debug", "new ProductionItem: {0} time={1} cost={2}", item, time, cost);
} }
public void Tick(PlayerResources pr) public void Tick(PlayerResources pr)
@@ -400,33 +409,43 @@ namespace OpenRA.Mods.RA
if (!Started) if (!Started)
{ {
var time = Queue.GetBuildTime(Item); var time = Queue.GetBuildTime(Item);
if (time > 0) RemainingTime = TotalTime = time; if (time > 0)
RemainingTime = TotalTime = time;
Started = true; Started = true;
} }
if (Done) if (Done)
{ {
if (OnComplete != null) OnComplete(); if (OnComplete != null)
OnComplete();
return; return;
} }
if (Paused) return; if (Paused)
return;
if (pm.PowerState != PowerState.Normal) if (pm.PowerState != PowerState.Normal)
{ {
if (--slowdown <= 0) if (--Slowdown <= 0)
slowdown = Queue.Info.LowPowerSlowdown; Slowdown = Queue.Info.LowPowerSlowdown;
else else
return; return;
} }
var costThisFrame = RemainingCost / RemainingTime; var costThisFrame = RemainingCost / RemainingTime;
if (costThisFrame != 0 && !pr.TakeCash(costThisFrame)) return; if (costThisFrame != 0 && !pr.TakeCash(costThisFrame))
return;
RemainingCost -= costThisFrame; RemainingCost -= costThisFrame;
RemainingTime -= 1; RemainingTime -= 1;
if (RemainingTime > 0) return; if (RemainingTime > 0)
return;
Done = true; Done = true;
} }
public void Pause(bool paused) { Paused = paused; }
} }
} }

View File

@@ -402,7 +402,7 @@ namespace OpenRA.Mods.RA.Scripting
var queue = GetSharedQueueForUnit(player, unit); var queue = GetSharedQueueForUnit(player, unit);
if (queue != null) if (queue != null)
queue.ResolveOrder(queue.self, Order.StartProduction(queue.self, unit, (int)amount)); queue.ResolveOrder(queue.Actor, Order.StartProduction(queue.Actor, unit, (int)amount));
} }
[LuaGlobal] [LuaGlobal]

View File

@@ -94,7 +94,7 @@ namespace OpenRA.Mods.RA.Widgets
.Where(p => p.Actor.Owner == world.LocalPlayer) .Where(p => p.Actor.Owner == world.LocalPlayer)
.Select(p => p.Trait); .Select(p => p.Trait);
if (CurrentQueue != null && CurrentQueue.self.Destroyed) if (CurrentQueue != null && CurrentQueue.Actor.Destroyed)
CurrentQueue = null; CurrentQueue = null;
foreach (var queue in queues) foreach (var queue in queues)
@@ -366,7 +366,7 @@ namespace OpenRA.Mods.RA.Widgets
if (producing.Done) if (producing.Done)
{ {
if (unit.Traits.Contains<BuildingInfo>()) if (unit.Traits.Contains<BuildingInfo>())
world.OrderGenerator = new PlaceBuildingOrderGenerator(CurrentQueue.self, item); world.OrderGenerator = new PlaceBuildingOrderGenerator(CurrentQueue.Actor, item);
else else
StartProduction(world, item); StartProduction(world, item);
return; return;
@@ -374,7 +374,7 @@ namespace OpenRA.Mods.RA.Widgets
if (producing.Paused) if (producing.Paused)
{ {
world.IssueOrder(Order.PauseProduction(CurrentQueue.self, item, false)); world.IssueOrder(Order.PauseProduction(CurrentQueue.Actor, item, false));
return; return;
} }
} }
@@ -403,12 +403,12 @@ namespace OpenRA.Mods.RA.Widgets
Sound.PlayNotification(world.Map.Rules, world.LocalPlayer, "Speech", CurrentQueue.Info.CancelledAudio, world.LocalPlayer.Country.Race); Sound.PlayNotification(world.Map.Rules, world.LocalPlayer, "Speech", CurrentQueue.Info.CancelledAudio, world.LocalPlayer.Country.Race);
var numberToCancel = Game.GetModifierKeys().HasModifier(Modifiers.Shift) ? 5 : 1; var numberToCancel = Game.GetModifierKeys().HasModifier(Modifiers.Shift) ? 5 : 1;
world.IssueOrder(Order.CancelProduction(CurrentQueue.self, item, numberToCancel)); world.IssueOrder(Order.CancelProduction(CurrentQueue.Actor, item, numberToCancel));
} }
else else
{ {
Sound.PlayNotification(world.Map.Rules, world.LocalPlayer, "Speech", CurrentQueue.Info.OnHoldAudio, world.LocalPlayer.Country.Race); Sound.PlayNotification(world.Map.Rules, world.LocalPlayer, "Speech", CurrentQueue.Info.OnHoldAudio, world.LocalPlayer.Country.Race);
world.IssueOrder(Order.PauseProduction(CurrentQueue.self, item, true)); world.IssueOrder(Order.PauseProduction(CurrentQueue.Actor, item, true));
} }
} }
} }
@@ -416,7 +416,7 @@ namespace OpenRA.Mods.RA.Widgets
void StartProduction(World world, string item) void StartProduction(World world, string item)
{ {
world.IssueOrder(Order.StartProduction(CurrentQueue.self, item, world.IssueOrder(Order.StartProduction(CurrentQueue.Actor, item,
Game.GetModifierKeys().HasModifier(Modifiers.Shift) ? 5 : 1)); Game.GetModifierKeys().HasModifier(Modifiers.Shift) ? 5 : 1));
} }