diff --git a/OpenRA.Game/Network/Order.cs b/OpenRA.Game/Network/Order.cs index cdc5532035..390cfa65c4 100755 --- a/OpenRA.Game/Network/Order.cs +++ b/OpenRA.Game/Network/Order.cs @@ -45,6 +45,7 @@ namespace OpenRA public CPos ExtraLocation; public uint ExtraData; public bool IsImmediate; + public bool SuppressVisualFeedback; public Player Player { get { return Subject.Owner; } } diff --git a/OpenRA.Game/Network/UnitOrders.cs b/OpenRA.Game/Network/UnitOrders.cs index 843d153d5e..c32870654a 100644 --- a/OpenRA.Game/Network/UnitOrders.cs +++ b/OpenRA.Game/Network/UnitOrders.cs @@ -237,7 +237,7 @@ namespace OpenRA.Network return; var targetPlayer = order.Player.World.Players.FirstOrDefault(p => p.InternalName == order.TargetString); - var newStance = (Stance)order.TargetLocation.X; + var newStance = (Stance)order.ExtraData; SetPlayerStance(world, order.Player, targetPlayer, newStance); diff --git a/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs b/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs index 6c4c264262..3b1e4e50be 100644 --- a/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs +++ b/OpenRA.Game/Widgets/WorldInteractionControllerWidget.cs @@ -152,14 +152,14 @@ namespace OpenRA.Widgets if (o == null) continue; - if (!flashed) + if (!flashed && !o.SuppressVisualFeedback) { if (o.TargetActor != null) { world.AddFrameEndTask(w => w.Add(new FlashTarget(o.TargetActor))); flashed = true; } - else if (o.Subject != world.LocalPlayer.PlayerActor && o.TargetLocation != CPos.Zero) // TODO: this filters building placement, but also suppport powers :( + else if (o.TargetLocation != CPos.Zero) { world.AddFrameEndTask(w => w.Add(new MoveFlash(worldRenderer.Position(worldRenderer.Viewport.ViewToWorldPx(mi.Location)), world))); flashed = true; diff --git a/OpenRA.Mods.Cnc/ProductionAirdrop.cs b/OpenRA.Mods.Cnc/ProductionAirdrop.cs index 509672c3f6..176ea0d5e9 100644 --- a/OpenRA.Mods.Cnc/ProductionAirdrop.cs +++ b/OpenRA.Mods.Cnc/ProductionAirdrop.cs @@ -32,7 +32,7 @@ namespace OpenRA.Mods.Cnc public ProductionAirdrop(ProductionAirdropInfo info, Actor self) : base(info, self) { } - public override bool Produce(Actor self, ActorInfo producee) + public override bool Produce(Actor self, ActorInfo producee, string raceVariant) { var owner = self.Owner; @@ -69,7 +69,8 @@ namespace OpenRA.Mods.Cnc foreach (var cargo in self.TraitsImplementing()) cargo.Delivered(self); - self.World.AddFrameEndTask(ww => DoProduction(self, producee, exit)); + + self.World.AddFrameEndTask(ww => DoProduction(self, producee, exit, raceVariant)); Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.ReadyAudio, self.Owner.Country.Race); })); diff --git a/OpenRA.Mods.Cnc/ProductionQueueFromSelection.cs b/OpenRA.Mods.Cnc/ProductionQueueFromSelection.cs index 0ecd483def..345092e233 100644 --- a/OpenRA.Mods.Cnc/ProductionQueueFromSelection.cs +++ b/OpenRA.Mods.Cnc/ProductionQueueFromSelection.cs @@ -41,10 +41,10 @@ namespace OpenRA.Mods.Cnc.Widgets // Find an actor with a queue var producer = world.Selection.Actors.FirstOrDefault(a => a.IsInWorld && a.World.LocalPlayer == a.Owner - && a.HasTrait()); + && a.TraitsImplementing().Any(q => q.Enabled)); if (producer != null) - tabsWidget.Value.CurrentQueue = producer.TraitsImplementing().First(); + tabsWidget.Value.CurrentQueue = producer.TraitsImplementing().First(q => q.Enabled); } } } diff --git a/OpenRA.Mods.Cnc/Widgets/Logic/ProductionTooltipLogic.cs b/OpenRA.Mods.Cnc/Widgets/Logic/ProductionTooltipLogic.cs index 925c90bb76..5866e6a71b 100644 --- a/OpenRA.Mods.Cnc/Widgets/Logic/ProductionTooltipLogic.cs +++ b/OpenRA.Mods.Cnc/Widgets/Logic/ProductionTooltipLogic.cs @@ -54,7 +54,7 @@ namespace OpenRA.Mods.Cnc.Widgets.Logic nameLabel.GetText = () => tooltip.Name; - var prereqs = buildable.Prerequisites.Select(a => ActorName(mapRules, a)); + var prereqs = buildable.Prerequisites.Select(a => ActorName(mapRules, a)).Where(s => !s.StartsWith("~")); var requiresString = prereqs.Any() ? requiresLabel.Text.F(prereqs.JoinWith(", ")) : ""; requiresLabel.GetText = () => requiresString; diff --git a/OpenRA.Mods.Cnc/Widgets/ProductionPaletteWidget.cs b/OpenRA.Mods.Cnc/Widgets/ProductionPaletteWidget.cs index 4dbb1af4aa..dfcb3b8e8a 100644 --- a/OpenRA.Mods.Cnc/Widgets/ProductionPaletteWidget.cs +++ b/OpenRA.Mods.Cnc/Widgets/ProductionPaletteWidget.cs @@ -81,7 +81,7 @@ namespace OpenRA.Mods.Cnc.Widgets public override void Tick() { - if (CurrentQueue != null && !CurrentQueue.self.IsInWorld) + if (CurrentQueue != null && !CurrentQueue.Actor.IsInWorld) CurrentQueue = null; if (CurrentQueue != null) @@ -125,20 +125,20 @@ namespace OpenRA.Mods.Cnc.Widgets if (first != null && first.Done && actor.Traits.Contains()) { Sound.Play(TabClick); - World.OrderGenerator = new PlaceBuildingOrderGenerator(CurrentQueue.self, icon.Name); + World.OrderGenerator = new PlaceBuildingOrderGenerator(CurrentQueue, icon.Name); } else if (first != null && first.Paused) { // Resume a paused item 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)) { // Queue a new item Sound.Play(TabClick); 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)); } else @@ -155,13 +155,13 @@ namespace OpenRA.Mods.Cnc.Widgets if (first.Paused || first.Done || first.TotalCost == first.RemainingCost) { 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)); } else { 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 diff --git a/OpenRA.Mods.Cnc/Widgets/ProductionTabsWidget.cs b/OpenRA.Mods.Cnc/Widgets/ProductionTabsWidget.cs index 133db405ba..a7c7a19fd1 100644 --- a/OpenRA.Mods.Cnc/Widgets/ProductionTabsWidget.cs +++ b/OpenRA.Mods.Cnc/Widgets/ProductionTabsWidget.cs @@ -196,7 +196,7 @@ namespace OpenRA.Mods.Cnc.Widgets if (a.HasTrait()) { var allQueues = a.World.ActorsWithTrait() - .Where(p => p.Actor.Owner == p.Actor.World.LocalPlayer && p.Actor.IsInWorld) + .Where(p => p.Actor.Owner == p.Actor.World.LocalPlayer && p.Actor.IsInWorld && p.Trait.Enabled) .Select(p => p.Trait).ToArray(); foreach (var g in Groups.Values) diff --git a/OpenRA.Mods.D2k/Render/WithProductionOverlay.cs b/OpenRA.Mods.D2k/Render/WithProductionOverlay.cs index 65e943b97c..e27bd7233e 100644 --- a/OpenRA.Mods.D2k/Render/WithProductionOverlay.cs +++ b/OpenRA.Mods.D2k/Render/WithProductionOverlay.cs @@ -68,12 +68,12 @@ namespace OpenRA.Mods.RA.Render var production = self.TraitOrDefault(); var perBuildingQueues = self.TraitsImplementing(); - queue = perBuildingQueues.FirstOrDefault(q => production.Info.Produces.Contains(q.Info.Type)); + queue = perBuildingQueues.FirstOrDefault(q => q.Enabled && production.Info.Produces.Contains(q.Info.Type)); if (queue == null) { var perPlayerQueues = self.Owner.PlayerActor.TraitsImplementing(); - queue = perPlayerQueues.FirstOrDefault(q => production.Info.Produces.Contains(q.Info.Type)); + queue = perPlayerQueues.FirstOrDefault(q => q.Enabled && production.Info.Produces.Contains(q.Info.Type)); } if (queue == null) diff --git a/OpenRA.Mods.RA/AI/BaseBuilder.cs b/OpenRA.Mods.RA/AI/BaseBuilder.cs index 000baa5ff1..ab1c5ca8fa 100644 --- a/OpenRA.Mods.RA/AI/BaseBuilder.cs +++ b/OpenRA.Mods.RA/AI/BaseBuilder.cs @@ -51,7 +51,7 @@ namespace OpenRA.Mods.RA.AI { HackyAI.BotDebug("AI: Starting production of {0}".F(item.Name)); state = BuildState.WaitForProduction; - ai.world.IssueOrder(Order.StartProduction(queue.self, item.Name, 1)); + ai.world.IssueOrder(Order.StartProduction(queue.Actor, item.Name, 1)); } break; @@ -60,7 +60,7 @@ namespace OpenRA.Mods.RA.AI return; 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) { state = BuildState.WaitForFeedback; @@ -77,14 +77,16 @@ namespace OpenRA.Mods.RA.AI if (location == null) { 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 { ai.world.IssueOrder(new Order("PlaceBuilding", ai.p.PlayerActor, false) { TargetLocation = location.Value, - TargetString = currentBuilding.Item + TargetString = currentBuilding.Item, + TargetActor = queue.Actor, + SuppressVisualFeedback = true }); } } diff --git a/OpenRA.Mods.RA/AI/HackyAI.cs b/OpenRA.Mods.RA/AI/HackyAI.cs index 4f643ec78f..3d1bd3cf3c 100644 --- a/OpenRA.Mods.RA/AI/HackyAI.cs +++ b/OpenRA.Mods.RA/AI/HackyAI.cs @@ -26,6 +26,9 @@ namespace OpenRA.Mods.RA.AI public readonly string Name = "Unnamed Bot"; public readonly int SquadSize = 8; + public readonly string[] BuildingQueues = { "Building" }; + public readonly string[] DefenseQueues = { "Defense" }; + public readonly int AssignRolesInterval = 20; public readonly int RushInterval = 600; public readonly int AttackForceInterval = 30; @@ -106,7 +109,7 @@ namespace OpenRA.Mods.RA.AI RushFuzzy rushFuzzy = new RushFuzzy(); Cache aggro = new Cache(_ => new Enemy()); - BaseBuilder[] builders; + List builders = new List(); List squads = new List(); List unitsHangingAroundTheBase = new List(); @@ -144,10 +147,11 @@ namespace OpenRA.Mods.RA.AI playerPower = p.PlayerActor.Trait(); supportPowerMngr = p.PlayerActor.Trait(); playerResource = p.PlayerActor.Trait(); - builders = new BaseBuilder[] { - new BaseBuilder(this, "Building", q => ChooseBuildingToBuild(q, false)), - new BaseBuilder(this, "Defense", q => ChooseBuildingToBuild(q, true)) - }; + + foreach (var building in Info.BuildingQueues) + builders.Add(new BaseBuilder(this, building, q => ChooseBuildingToBuild(q, false))); + foreach (var defense in Info.DefenseQueues) + builders.Add(new BaseBuilder(this, defense, q => ChooseBuildingToBuild(q, true))); random = new MersenneTwister((int)p.PlayerActor.ActorID); @@ -232,8 +236,7 @@ namespace OpenRA.Mods.RA.AI if (!names.Any() || !names.ContainsKey(commonName)) return null; - return Map.Rules.Actors.Where(k => names[commonName].Contains(k.Key) && - k.Value.Traits.Get().Owner.Contains(owner.Country.Race)).Random(random).Value; + return Map.Rules.Actors.Where(k => names[commonName].Contains(k.Key)).Random(random).Value; } bool HasAdequatePower() @@ -685,7 +688,7 @@ namespace OpenRA.Mods.RA.AI p.PlayerName, buildings.Length); foreach (var a in buildings) - world.IssueOrder(new Order("SetRallyPoint", a.Actor, false) { TargetLocation = ChooseRallyLocationNear(a.Actor.Location) }); + world.IssueOrder(new Order("SetRallyPoint", a.Actor, false) { TargetLocation = ChooseRallyLocationNear(a.Actor.Location), SuppressVisualFeedback = true }); } // Won't work for shipyards... @@ -770,7 +773,7 @@ namespace OpenRA.Mods.RA.AI if (attackLocation == null) return; - world.IssueOrder(new Order(sp.Info.OrderName, supportPowerMngr.self, false) { TargetLocation = attackLocation.Value }); + world.IssueOrder(new Order(sp.Info.OrderName, supportPowerMngr.self, false) { TargetLocation = attackLocation.Value, SuppressVisualFeedback = true }); } } } @@ -809,7 +812,7 @@ namespace OpenRA.Mods.RA.AI internal IEnumerable FindQueues(string category) { return world.ActorsWithTrait() - .Where(a => a.Actor.Owner == p && a.Trait.Info.Type == category) + .Where(a => a.Actor.Owner == p && a.Trait.Info.Type == category && a.Trait.Enabled) .Select(a => a.Trait); } @@ -839,7 +842,7 @@ namespace OpenRA.Mods.RA.AI ChooseUnitToBuild(queue); 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) @@ -849,7 +852,7 @@ namespace OpenRA.Mods.RA.AI return; 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) diff --git a/OpenRA.Mods.RA/Activities/Transform.cs b/OpenRA.Mods.RA/Activities/Transform.cs index 86d77ad2ae..47012b5454 100644 --- a/OpenRA.Mods.RA/Activities/Transform.cs +++ b/OpenRA.Mods.RA/Activities/Transform.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2011 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2014 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. For more information, @@ -16,21 +16,23 @@ namespace OpenRA.Mods.RA.Activities { class Transform : Activity { - public readonly string ToActor = null; - public CVec Offset = new CVec(0, 0); + public readonly string ToActor; + public CVec Offset = CVec.Zero; public int Facing = 96; - public string[] Sounds = {}; + public string[] Sounds = { }; public int ForceHealthPercentage = 0; public bool SkipMakeAnims = false; + public string Race = null; public Transform(Actor self, string toActor) { - this.ToActor = toActor; + ToActor = toActor; } - public override Activity Tick( Actor self ) + public override Activity Tick(Actor self) { - if (IsCanceled) return NextActivity; + if (IsCanceled) + return NextActivity; self.World.AddFrameEndTask(w => { @@ -48,12 +50,16 @@ namespace OpenRA.Mods.RA.Activities var init = new TypeDictionary { - new LocationInit( self.Location + Offset ), - new OwnerInit( self.Owner ), - new FacingInit( Facing ), + new LocationInit(self.Location + Offset), + new OwnerInit(self.Owner), + new FacingInit(Facing), }; - if (SkipMakeAnims) init.Add(new SkipMakeAnimsInit()); + if (SkipMakeAnims) + init.Add(new SkipMakeAnimsInit()); + + if (Race != null) + init.Add(new RaceInit(Race)); var health = self.TraitOrDefault(); if (health != null) @@ -62,15 +68,14 @@ namespace OpenRA.Mods.RA.Activities ? ForceHealthPercentage / 100f : (float)health.HP / health.MaxHP; - init.Add( new HealthInit(newHP) ); + init.Add(new HealthInit(newHP)); } var cargo = self.TraitOrDefault(); if (cargo != null) - init.Add( new RuntimeCargoInit( cargo.Passengers.ToArray() ) ); - - var a = w.CreateActor( ToActor, init ); + init.Add(new RuntimeCargoInit(cargo.Passengers.ToArray())); + var a = w.CreateActor(ToActor, init); foreach (var nt in self.TraitsImplementing()) nt.OnTransformed(a); diff --git a/OpenRA.Mods.RA/AutoTarget.cs b/OpenRA.Mods.RA/AutoTarget.cs index 2b03ff1891..ef1ef7ea26 100644 --- a/OpenRA.Mods.RA/AutoTarget.cs +++ b/OpenRA.Mods.RA/AutoTarget.cs @@ -66,7 +66,7 @@ namespace OpenRA.Mods.RA public void ResolveOrder(Actor self, Order order) { if (order.OrderString == "SetUnitStance" && info.EnableStances) - Stance = (UnitStance)order.TargetLocation.X; + Stance = (UnitStance)order.ExtraData; } public void Damaged(Actor self, AttackInfo e) diff --git a/OpenRA.Mods.RA/Buildable.cs b/OpenRA.Mods.RA/Buildable.cs index b70b64038f..3d29c40313 100755 --- a/OpenRA.Mods.RA/Buildable.cs +++ b/OpenRA.Mods.RA/Buildable.cs @@ -17,7 +17,7 @@ namespace OpenRA.Mods.RA public readonly string[] Prerequisites = { }; public readonly string[] Owner = { }; - public readonly string Queue; + public readonly string[] Queue = { }; public readonly int BuildLimit = 0; // TODO: UI fluff; doesn't belong here diff --git a/OpenRA.Mods.RA/LeavesHusk.cs b/OpenRA.Mods.RA/LeavesHusk.cs index b69777969d..5cd4a728b2 100644 --- a/OpenRA.Mods.RA/LeavesHusk.cs +++ b/OpenRA.Mods.RA/LeavesHusk.cs @@ -20,14 +20,20 @@ namespace OpenRA.Mods.RA [ActorReference] public readonly string HuskActor = null; - public object Create(ActorInitializer init) { return new LeavesHusk(this); } + public object Create(ActorInitializer init) { return new LeavesHusk(init, this); } } public class LeavesHusk : INotifyKilled { - LeavesHuskInfo info; + readonly LeavesHuskInfo info; + readonly string race; - public LeavesHusk(LeavesHuskInfo info) { this.info = info; } + public LeavesHusk(ActorInitializer init, LeavesHuskInfo info) + { + this.info = info; + + race = init.Contains() ? init.Get() : init.self.Owner.Country.Race; + } public void Killed(Actor self, AttackInfo e) { @@ -42,6 +48,7 @@ namespace OpenRA.Mods.RA new LocationInit(self.Location), new CenterPositionInit(self.CenterPosition), new OwnerInit(self.Owner), + new RaceInit(race), new SkipMakeAnimsInit() }; diff --git a/OpenRA.Mods.RA/Lint/LintBuildablePrerequisites.cs b/OpenRA.Mods.RA/Lint/LintBuildablePrerequisites.cs index 39daeddd05..7f181be8ba 100644 --- a/OpenRA.Mods.RA/Lint/LintBuildablePrerequisites.cs +++ b/OpenRA.Mods.RA/Lint/LintBuildablePrerequisites.cs @@ -42,8 +42,9 @@ namespace OpenRA.Mods.RA var bi = i.Value.Traits.GetOrDefault(); if (bi != null) foreach (var prereq in bi.Prerequisites) - if (!providedPrereqs.Contains(prereq.Replace("!", "").Replace("~", ""))) - emitError("Buildable actor {0} has prereq {1} not provided by anything.".F(i.Key, prereq)); + if (!prereq.StartsWith("~disabled")) + if (!providedPrereqs.Contains(prereq.Replace("!", "").Replace("~", ""))) + emitError("Buildable actor {0} has prereq {1} not provided by anything.".F(i.Key, prereq)); } } } diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index ee29ef2a2c..aeb4fdda34 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -517,6 +517,7 @@ + diff --git a/OpenRA.Mods.RA/Orders/BeaconOrderGenerator.cs b/OpenRA.Mods.RA/Orders/BeaconOrderGenerator.cs index ba8ce8a995..b4d1f88fa2 100644 --- a/OpenRA.Mods.RA/Orders/BeaconOrderGenerator.cs +++ b/OpenRA.Mods.RA/Orders/BeaconOrderGenerator.cs @@ -23,7 +23,7 @@ namespace OpenRA.Mods.RA.Orders if (!world.ShroudObscures(xy)) { world.CancelInputMode(); - yield return new Order("PlaceBeacon", world.LocalPlayer.PlayerActor, false) { TargetLocation = xy }; + yield return new Order("PlaceBeacon", world.LocalPlayer.PlayerActor, false) { TargetLocation = xy, SuppressVisualFeedback = true }; } } diff --git a/OpenRA.Mods.RA/Orders/PlaceBuildingOrderGenerator.cs b/OpenRA.Mods.RA/Orders/PlaceBuildingOrderGenerator.cs index 53dd148698..46ee716d4d 100644 --- a/OpenRA.Mods.RA/Orders/PlaceBuildingOrderGenerator.cs +++ b/OpenRA.Mods.RA/Orders/PlaceBuildingOrderGenerator.cs @@ -22,19 +22,22 @@ namespace OpenRA.Mods.RA.Orders readonly Actor Producer; readonly string Building; readonly BuildingInfo BuildingInfo; + IEnumerable preview; Sprite buildOk, buildBlocked; bool initialized = false; - public PlaceBuildingOrderGenerator(Actor producer, string name) + public PlaceBuildingOrderGenerator(ProductionQueue queue, string name) { - Producer = producer; + Producer = queue.Actor; Building = name; - var tileset = producer.World.TileSet.Id.ToLowerInvariant(); - BuildingInfo = producer.World.Map.Rules.Actors[Building].Traits.Get(); - buildOk = producer.World.Map.SequenceProvider.GetSequence("overlay", "build-valid-{0}".F(tileset)).GetSprite(0); - buildBlocked = producer.World.Map.SequenceProvider.GetSequence("overlay", "build-invalid").GetSprite(0); + var map = Producer.World.Map; + var tileset = Producer.World.TileSet.Id.ToLowerInvariant(); + BuildingInfo = map.Rules.Actors[Building].Traits.Get(); + + buildOk = map.SequenceProvider.GetSequence("overlay", "build-valid-{0}".F(tileset)).GetSprite(0); + buildBlocked = map.SequenceProvider.GetSequence("overlay", "build-invalid").GetSprite(0); } public IEnumerable Order(World world, CPos xy, MouseInput mi) @@ -62,8 +65,13 @@ namespace OpenRA.Mods.RA.Orders } var isLineBuild = world.Map.Rules.Actors[Building].Traits.Contains(); - yield return new Order(isLineBuild ? "LineBuild" : "PlaceBuilding", - Producer.Owner.PlayerActor, false) { TargetLocation = topLeft, TargetString = Building }; + yield return new Order(isLineBuild ? "LineBuild" : "PlaceBuilding", Producer.Owner.PlayerActor, false) + { + TargetLocation = topLeft, + TargetActor = Producer, + TargetString = Building, + SuppressVisualFeedback = true + }; } } diff --git a/OpenRA.Mods.RA/Player/ClassicProductionQueue.cs b/OpenRA.Mods.RA/Player/ClassicProductionQueue.cs index 5c0a42ba43..b914006e6b 100644 --- a/OpenRA.Mods.RA/Player/ClassicProductionQueue.cs +++ b/OpenRA.Mods.RA/Player/ClassicProductionQueue.cs @@ -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.")] public readonly bool SpeedUp = false; + [Desc("Every time another production building of the same queue is", "contructed, the build times of all actors in the queue", "decreased by a percentage of the original time.")] 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 new ClassicProductionQueueInfo Info; + static readonly ActorInfo[] NoItems = { }; - public ClassicProductionQueue(Actor self, ClassicProductionQueueInfo info) - : base(self, self, info) + readonly Actor self; + 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; @@ -47,23 +52,25 @@ namespace OpenRA.Mods.RA { isActive = false; foreach (var x in self.World.ActorsWithTrait()) + { if (x.Actor.Owner == self.Owner && x.Trait.Info.Produces.Contains(Info.Type)) { isActive = true; break; } + } + base.Tick(self); } - static ActorInfo[] None = { }; public override IEnumerable AllItems() { - return isActive ? base.AllItems() : None; + return isActive ? base.AllItems() : NoItems; } public override IEnumerable BuildableItems() { - return isActive ? base.BuildableItems() : None; + return isActive ? base.BuildableItems() : NoItems; } protected override bool BuildUnit(string name) @@ -83,16 +90,17 @@ namespace OpenRA.Mods.RA foreach (var p in producers.Where(p => !p.Actor.IsDisabled())) { - if (p.Trait.Produce(p.Actor, self.World.Map.Rules.Actors[name])) + if (p.Trait.Produce(p.Actor, self.World.Map.Rules.Actors[name], Race)) { FinishProduction(); return true; } } + return false; } - public override int GetBuildTime(String unitString) + public override int GetBuildTime(string unitString) { var unit = self.World.Map.Rules.Actors[unitString]; if (unit == null || !unit.Traits.Contains()) @@ -103,14 +111,15 @@ namespace OpenRA.Mods.RA var time = (int)(unit.GetBuildTime() * Info.BuildSpeed); - if (Info.SpeedUp) + if (info.SpeedUp) { + var queues = unit.Traits.Get().Queue; var selfsameBuildings = self.World.ActorsWithTrait() - .Where(p => p.Trait.Info.Produces.Contains(unit.Traits.Get().Queue)) - .Where(p => p.Actor.Owner == self.Owner).ToArray(); + .Where(p => p.Actor.Owner == self.Owner && p.Trait.Info.Produces.Intersect(queues).Any()) + .ToArray(); - var speedModifier = selfsameBuildings.Count().Clamp(1, Info.BuildTimeSpeedReduction.Length) - 1; - time = (time * Info.BuildTimeSpeedReduction[speedModifier]) / 100; + var speedModifier = selfsameBuildings.Count().Clamp(1, info.BuildTimeSpeedReduction.Length) - 1; + time = (time * info.BuildTimeSpeedReduction[speedModifier]) / 100; } return time; diff --git a/OpenRA.Mods.RA/Player/PlaceBuilding.cs b/OpenRA.Mods.RA/Player/PlaceBuilding.cs index abd3b176fe..a2ad8c04fb 100644 --- a/OpenRA.Mods.RA/Player/PlaceBuilding.cs +++ b/OpenRA.Mods.RA/Player/PlaceBuilding.cs @@ -28,19 +28,17 @@ namespace OpenRA.Mods.RA { var prevItems = GetNumBuildables(self.Owner); - // Find the queue with the target actor - var queue = w.ActorsWithTrait() - .Where(p => p.Actor.Owner == self.Owner && - p.Trait.CurrentItem() != null && - p.Trait.CurrentItem().Item == order.TargetString && - p.Trait.CurrentItem().RemainingTime == 0) - .Select(p => p.Trait) - .FirstOrDefault(); + if (order.TargetActor.IsDead()) + return; + + var unit = self.World.Map.Rules.Actors[order.TargetString]; + var queue = order.TargetActor.TraitsImplementing() + .FirstOrDefault(q => q.CanBuild(unit)); if (queue == null) return; - var unit = self.World.Map.Rules.Actors[order.TargetString]; + var buildingInfo = unit.Traits.Get(); if (order.OrderString == "LineBuild") @@ -52,11 +50,13 @@ namespace OpenRA.Mods.RA { new LocationInit(t), new OwnerInit(order.Player), + new RaceInit(queue.Race) }); if (playSounds) foreach (var s in buildingInfo.BuildSounds) Sound.PlayToPlayer(order.Player, s, building.CenterPosition); + playSounds = false; } } @@ -72,7 +72,9 @@ namespace OpenRA.Mods.RA { new LocationInit(order.TargetLocation), new OwnerInit(order.Player), + new RaceInit(queue.Race), }); + foreach (var s in buildingInfo.BuildSounds) Sound.PlayToPlayer(order.Player, s, building.CenterPosition); } @@ -106,7 +108,7 @@ namespace OpenRA.Mods.RA var producers = self.World.ActorsWithTrait() .Where(x => x.Actor.Owner == self.Owner - && x.Actor.Info.Traits.Get().Produces.Contains(bi.Queue)) + && x.Actor.Info.Traits.Get().Produces.Intersect(bi.Queue).Any()) .ToList(); var producer = producers.Where(x => x.Actor.IsPrimaryBuilding()).Concat(producers) .FirstOrDefault(); diff --git a/OpenRA.Mods.RA/Player/ProductionQueue.cs b/OpenRA.Mods.RA/Player/ProductionQueue.cs index 8a095f96ee..cfce76529f 100644 --- a/OpenRA.Mods.RA/Player/ProductionQueue.cs +++ b/OpenRA.Mods.RA/Player/ProductionQueue.cs @@ -23,205 +23,236 @@ namespace OpenRA.Mods.RA { [Desc("What kind of production will be added (e.g. Building, Infantry, Vehicle, ...)")] public readonly string Type = null; + [Desc("Group queues from separate buildings together into the same tab.")] public readonly string Group = null; + [Desc("Filter buildable items based on their Owner.")] + public readonly bool RequireOwner = true; + + [Desc("Only enable this queue for certain factions")] + public readonly string[] Race = { }; + + [Desc("Should the prerequisite remain enabled if the owner changes?")] + public readonly bool Sticky = true; + [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.")] public readonly int LowPowerSlowdown = 3; [Desc("Notification played when production is complete.", "The filename of the audio is defined per faction in notifications.yaml.")] public readonly string ReadyAudio = "UnitReady"; + [Desc("Notification played when you can't train another unit", "when the build limit exceeded or the exit is jammed.", "The filename of the audio is defined per faction in notifications.yaml.")] public readonly string BlockedAudio = "NoBuild"; + [Desc("Notification played when user clicks on the build palette icon.", "The filename of the audio is defined per faction in notifications.yaml.")] public readonly string QueuedAudio = "Training"; + [Desc("Notification played when player right-clicks on the build palette icon.", "The filename of the audio is defined per faction in notifications.yaml.")] public readonly string OnHoldAudio = "OnHold"; + [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.")] 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, INotifyOwnerChanged, INotifyKilled, INotifySold, ISync, INotifyTransform { - public readonly Actor self; - public ProductionQueueInfo Info; - PowerManager PlayerPower; + public readonly ProductionQueueInfo Info; + readonly Actor self; + + // Will change if the owner changes + PowerManager playerPower; PlayerResources playerResources; - readonly CountryInfo Race; + DeveloperMode developerMode; + + // A list of things we could possibly build + Dictionary produceable; + List queue = new List(); // A list of things we are currently building - public List Queue = new List(); + public Actor Actor { get { return self; } } - [Sync] public int QueueLength { get { return Queue.Count; } } - [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 CurrentSlowdown { get { return QueueLength == 0 ? 0 : Queue[0].slowdown; } } - [Sync] public bool CurrentPaused { get { return QueueLength != 0 && Queue[0].Paused; } } - [Sync] public bool CurrentDone { get { return QueueLength != 0 && Queue[0].Done; } } + [Sync] public int QueueLength { get { return queue.Count; } } + [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 CurrentSlowdown { get { return QueueLength == 0 ? 0 : queue[0].Slowdown; } } + [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 Enabled { get; private set; } - // A list of things we could possibly build, even if our race doesn't normally get it - public Dictionary Produceable; + public string Race { get; private set; } - public ProductionQueue( Actor self, Actor playerActor, ProductionQueueInfo info ) + public ProductionQueue(ActorInitializer init, Actor playerActor, ProductionQueueInfo info) { - this.self = self; - this.Info = info; + self = init.self; + Info = info; playerResources = playerActor.Trait(); - PlayerPower = playerActor.Trait(); + playerPower = playerActor.Trait(); + developerMode = playerActor.Trait(); - Race = self.Owner.Country; - Produceable = InitTech(playerActor); + Race = init.Contains() ? init.Get() : self.Owner.Country.Race; + Enabled = !info.Race.Any() || info.Race.Contains(Race); + + CacheProduceables(playerActor); } void ClearQueue() { - if (Queue.Count == 0) + if (queue.Count == 0) return; // Refund the current item - playerResources.GiveCash(Queue[0].TotalCost - Queue[0].RemainingCost); - Queue.Clear(); + playerResources.GiveCash(queue[0].TotalCost - queue[0].RemainingCost); + queue.Clear(); } - public void OnCapture(Actor self, Actor captor, Player oldOwner, Player newOwner) + public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) { - PlayerPower = newOwner.PlayerActor.Trait(); - playerResources = newOwner.PlayerActor.Trait(); ClearQueue(); - // Produceable contains the tech from the original owner - this is desired so we don't clear it. - Produceable = InitTech(self.Owner.PlayerActor); + playerPower = newOwner.PlayerActor.Trait(); + playerResources = newOwner.PlayerActor.Trait(); + developerMode = newOwner.PlayerActor.Trait(); - // 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().Update(); + if (!Info.Sticky) + { + Race = self.Owner.Country.Race; + Enabled = !Info.Race.Any() || Info.Race.Contains(Race); + } + + // Regenerate the produceables and tech tree state + oldOwner.PlayerActor.Trait().Remove(this); + CacheProduceables(newOwner.PlayerActor); + newOwner.PlayerActor.Trait().Update(); } 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 OnTransform(Actor self) { ClearQueue(); } - Dictionary InitTech(Actor playerActor) + void CacheProduceables(Actor playerActor) { - var tech = new Dictionary(); + produceable = new Dictionary(); + if (!Enabled) + return; + var ttc = playerActor.Trait(); foreach (var a in AllBuildables(Info.Type)) { var bi = a.Traits.Get(); + // Can our race build this by satisfying normal prerequisites? - var buildable = 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 - tech.Add(a, new ProductionState { Visible = buildable }); + produceable.Add(a, new ProductionState { Visible = buildable }); + if (buildable) ttc.Add(a.Name, bi.Prerequisites, bi.BuildLimit, this); } - - return tech; } IEnumerable AllBuildables(string category) { return self.World.Map.Rules.Actors.Values - .Where( x => x.Name[ 0 ] != '^' ) - .Where( x => x.Traits.Contains() ) - .Where( x => x.Traits.Get().Queue == category ); - } - - public void OverrideProduction(ActorInfo type, bool buildable) - { - Produceable[type].Buildable = buildable; - Produceable[type].Sticky = true; + .Where(x => + x.Name[0] != '^' && + x.Traits.Contains() && + x.Traits.Get().Queue.Contains(category)); } public void PrerequisitesAvailable(string key) { - var ps = Produceable[ self.World.Map.Rules.Actors[key] ]; - if (!ps.Sticky) - ps.Buildable = true; + produceable[self.World.Map.Rules.Actors[key]].Buildable = true; } public void PrerequisitesUnavailable(string key) { - var ps = Produceable[ self.World.Map.Rules.Actors[key] ]; - if (!ps.Sticky) - ps.Buildable = false; + produceable[self.World.Map.Rules.Actors[key]].Buildable = false; } 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) { - Produceable[self.World.Map.Rules.Actors[key]].Visible = true; + produceable[self.World.Map.Rules.Actors[key]].Visible = true; } public ProductionItem CurrentItem() { - return Queue.ElementAtOrDefault(0); + return queue.ElementAtOrDefault(0); } public IEnumerable AllQueued() { - return Queue; + return queue; } public virtual IEnumerable AllItems() { - if (self.World.AllowDevCommands && self.Owner.PlayerActor.Trait().AllTech) - return Produceable.Select(a => a.Key); + if (self.World.AllowDevCommands && developerMode.AllTech) + 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 BuildableItems() { - if (self.World.AllowDevCommands && self.Owner.PlayerActor.Trait().AllTech) - return Produceable.Select(a => a.Key); + if (self.World.AllowDevCommands && developerMode.AllTech) + 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) { - return Produceable.ContainsKey(actor) && Produceable[actor].Buildable; + ProductionState ps; + if (!produceable.TryGetValue(actor, out ps)) + return false; + + return ps.Buildable || (self.World.AllowDevCommands && developerMode.AllTech); } 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(); } - if (Queue.Count > 0) - Queue[ 0 ].Tick(playerResources); + + if (queue.Count > 0) + queue[0].Tick(playerResources); } public void ResolveOrder(Actor self, Order order) { - switch(order.OrderString) + if (!Enabled) + return; + + switch (order.OrderString) { case "StartProduction": { var unit = self.World.Map.Rules.Actors[order.TargetString]; var bi = unit.Traits.Get(); - if (bi.Queue != Info.Type) + if (!bi.Queue.Contains(Info.Type)) return; /* Not built by this queue */ var cost = unit.Traits.Contains() ? unit.Traits.Get().Cost : 0; @@ -234,7 +265,7 @@ namespace OpenRA.Mods.RA var fromLimit = int.MaxValue; 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().Count(a => a.Actor.Info.Name == order.TargetString && a.Actor.Owner == self.Owner); fromLimit = bi.BuildLimit - (inQueue + owned); @@ -243,37 +274,39 @@ namespace OpenRA.Mods.RA } var amountToBuild = Math.Min(fromLimit, order.ExtraData); - - for (var n = 0; n < amountToBuild; n++) // repeat count + for (var n = 0; n < amountToBuild; n++) { var hasPlayedSound = false; - BeginProduction(new ProductionItem(this, order.TargetString, cost, PlayerPower, - () => self.World.AddFrameEndTask(_ => + BeginProduction(new ProductionItem(this, order.TargetString, cost, playerPower, () => self.World.AddFrameEndTask(_ => + { + var isBuilding = unit.Traits.Contains(); + 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(); - 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); - } - } - }))); + hasPlayedSound = Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", Info.BlockedAudio, self.Owner.Country.Race); + } + } + }))); } + break; } + case "PauseProduction": { - if (Queue.Count > 0 && Queue[0].Item == order.TargetString) - Queue[0].Paused = ( order.ExtraData != 0 ); + if (queue.Count > 0 && queue[0].Item == order.TargetString) + queue[0].Pause(order.ExtraData != 0); + break; } + case "CancelProduction": { CancelProduction(order.TargetString, order.ExtraData); @@ -282,10 +315,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]; - if (unit == null || ! unit.Traits.Contains()) + if (unit == null || !unit.Traits.Contains()) return 0; if (self.World.AllowDevCommands && self.Owner.PlayerActor.Trait().FastBuild) @@ -293,7 +326,7 @@ namespace OpenRA.Mods.RA var time = unit.GetBuildTime() * Info.BuildSpeed; - return (int) time; + return (int)time; } protected void CancelProduction(string itemName, uint numberToCancel) @@ -304,13 +337,13 @@ namespace OpenRA.Mods.RA void CancelProductionInner(string itemName) { - var lastIndex = Queue.FindLastIndex(a => a.Item == itemName); + var lastIndex = queue.FindLastIndex(a => a.Item == itemName); if (lastIndex > 0) - Queue.RemoveAt(lastIndex); + queue.RemoveAt(lastIndex); else if (lastIndex == 0) { - var item = Queue[0]; + var item = queue[0]; playerResources.GiveCash(item.TotalCost - item.RemainingCost); // refund what has been paid FinishProduction(); } @@ -318,13 +351,13 @@ namespace OpenRA.Mods.RA public void FinishProduction() { - if (Queue.Count == 0) return; - Queue.RemoveAt(0); + if (queue.Count != 0) + queue.RemoveAt(0); } 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) @@ -339,11 +372,12 @@ namespace OpenRA.Mods.RA } var sp = self.TraitsImplementing().FirstOrDefault(p => p.Info.Produces.Contains(Info.Type)); - if (sp != null && !self.IsDisabled() && sp.Produce(self, self.World.Map.Rules.Actors[name])) + if (sp != null && !self.IsDisabled() && sp.Produce(self, self.World.Map.Rules.Actors[name], Race)) { FinishProduction(); return true; } + return false; } } @@ -352,16 +386,16 @@ namespace OpenRA.Mods.RA { public bool Visible = false; public bool Buildable = false; - public bool Sticky = false; } public class ProductionItem { public readonly string Item; public readonly ProductionQueue Queue; - readonly PowerManager pm; - public int TotalTime; public readonly int TotalCost; + public readonly Action OnComplete; + + public int TotalTime { get; private set; } public int RemainingTime { get; private set; } public int RemainingCost { get; private set; } public int RemainingTimeActual @@ -373,9 +407,12 @@ namespace OpenRA.Mods.RA } } - public bool Paused = false, Done = false, Started = false; - public Action OnComplete; - public int slowdown = 0; + public bool Paused { get; private set; } + public bool Done { get; private set; } + 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) { @@ -385,7 +422,6 @@ namespace OpenRA.Mods.RA OnComplete = onComplete; Queue = queue; this.pm = pm; - //Log.Write("debug", "new ProductionItem: {0} time={1} cost={2}", item, time, cost); } public void Tick(PlayerResources pr) @@ -393,33 +429,43 @@ namespace OpenRA.Mods.RA if (!Started) { var time = Queue.GetBuildTime(Item); - if (time > 0) RemainingTime = TotalTime = time; + if (time > 0) + RemainingTime = TotalTime = time; + Started = true; } if (Done) { - if (OnComplete != null) OnComplete(); + if (OnComplete != null) + OnComplete(); + return; } - if (Paused) return; + if (Paused) + return; if (pm.PowerState != PowerState.Normal) { - if (--slowdown <= 0) - slowdown = Queue.Info.LowPowerSlowdown; + if (--Slowdown <= 0) + Slowdown = Queue.Info.LowPowerSlowdown; else return; } var costThisFrame = RemainingCost / RemainingTime; - if (costThisFrame != 0 && !pr.TakeCash(costThisFrame)) return; + if (costThisFrame != 0 && !pr.TakeCash(costThisFrame)) + return; + RemainingCost -= costThisFrame; RemainingTime -= 1; - if (RemainingTime > 0) return; + if (RemainingTime > 0) + return; Done = true; } + + public void Pause(bool paused) { Paused = paused; } } } diff --git a/OpenRA.Mods.RA/Player/ProvidesCustomPrerequisite.cs b/OpenRA.Mods.RA/Player/ProvidesCustomPrerequisite.cs new file mode 100755 index 0000000000..6f78597ffa --- /dev/null +++ b/OpenRA.Mods.RA/Player/ProvidesCustomPrerequisite.cs @@ -0,0 +1,77 @@ +#region Copyright & License Information +/* + * Copyright 2007-2014 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. For more information, + * see COPYING. + */ +#endregion + +using System.Collections.Generic; +using System.Linq; +using OpenRA.Primitives; +using OpenRA.Traits; + +namespace OpenRA.Mods.RA +{ + public class ProvidesCustomPrerequisiteInfo : ITraitInfo + { + [Desc("The prerequisite type that this provides")] + public readonly string Prerequisite = null; + + [Desc("Only grant this prerequisite for certain factions")] + public readonly string[] Race = { }; + + [Desc("Should the prerequisite remain enabled if the owner changes?")] + public readonly bool Sticky = true; + public object Create(ActorInitializer init) { return new ProvidesCustomPrerequisite(init, this); } + } + + public class ProvidesCustomPrerequisite : ITechTreePrerequisite, INotifyOwnerChanged + { + ProvidesCustomPrerequisiteInfo info; + bool enabled = true; + + public ProvidesCustomPrerequisite(ActorInitializer init, ProvidesCustomPrerequisiteInfo info) + { + this.info = info; + + if (info.Race.Any()) + { + var race = init.self.Owner.Country.Race; + if (init.Contains()) + race = init.Get(); + + enabled = info.Race.Contains(race); + } + } + + public IEnumerable ProvidesPrerequisites + { + get + { + if (!enabled) + yield break; + + yield return info.Prerequisite; + } + } + + public void OnOwnerChanged(Actor self, Player oldOwner, Player newOwner) + { + if (!info.Sticky && info.Race.Any()) + enabled = info.Race.Contains(self.Owner.Country.Race); + } + } + + // Allows maps / transformations to specify the race variant of an actor. + public class RaceInit : IActorInit + { + [FieldFromYamlKey] public readonly string Race; + + public RaceInit() { } + public RaceInit(string race) { Race = race; } + public string Value(World world) { return Race; } + } +} diff --git a/OpenRA.Mods.RA/Player/TechTree.cs b/OpenRA.Mods.RA/Player/TechTree.cs index 24e76c2a7a..6563fc8271 100755 --- a/OpenRA.Mods.RA/Player/TechTree.cs +++ b/OpenRA.Mods.RA/Player/TechTree.cs @@ -1,6 +1,6 @@ #region Copyright & License Information /* - * Copyright 2007-2013 The OpenRA Developers (see AUTHORS) + * Copyright 2007-2014 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. For more information, @@ -56,6 +56,11 @@ namespace OpenRA.Mods.RA watchers.RemoveAll(x => x.Key == key); } + public void Remove(ITechTreeElement tte) + { + watchers.RemoveAll(x => x.RegisteredBy == tte); + } + static Cache> GatherOwnedPrerequisites(Player player) { var ret = new Cache>(x => new List()); @@ -94,6 +99,7 @@ namespace OpenRA.Mods.RA class Watcher { public readonly string Key; + public ITechTreeElement RegisteredBy { get { return watcher; } } // Strings may be either actor type, or "alternate name" key readonly string[] prerequisites; @@ -155,23 +161,4 @@ namespace OpenRA.Mods.RA } } } - - public class ProvidesCustomPrerequisiteInfo : ITraitInfo - { - public readonly string Prerequisite; - - public object Create(ActorInitializer init) { return new ProvidesCustomPrerequisite(this); } - } - - public class ProvidesCustomPrerequisite : ITechTreePrerequisite - { - ProvidesCustomPrerequisiteInfo info; - - public IEnumerable ProvidesPrerequisites { get { yield return info.Prerequisite; } } - - public ProvidesCustomPrerequisite(ProvidesCustomPrerequisiteInfo info) - { - this.info = info; - } - } } diff --git a/OpenRA.Mods.RA/Production.cs b/OpenRA.Mods.RA/Production.cs index 01e4f26d06..15000c5751 100755 --- a/OpenRA.Mods.RA/Production.cs +++ b/OpenRA.Mods.RA/Production.cs @@ -53,7 +53,7 @@ namespace OpenRA.Mods.RA rp = Exts.Lazy(() => self.IsDead() ? null : self.TraitOrDefault()); } - public void DoProduction(Actor self, ActorInfo producee, ExitInfo exitinfo) + public void DoProduction(Actor self, ActorInfo producee, ExitInfo exitinfo, string raceVariant) { var exit = self.Location + exitinfo.ExitCell; var spawn = self.CenterPosition + exitinfo.SpawnOffset; @@ -68,13 +68,18 @@ namespace OpenRA.Mods.RA self.World.AddFrameEndTask(w => { - var newUnit = self.World.CreateActor(producee.Name, new TypeDictionary + var td = new TypeDictionary { new OwnerInit(self.Owner), new LocationInit(exit), new CenterPositionInit(spawn), new FacingInit(initialFacing) - }); + }; + + if (raceVariant != null) + td.Add(new RaceInit(raceVariant)); + + var newUnit = self.World.CreateActor(producee.Name, td); var move = newUnit.TraitOrDefault(); if (move != null) @@ -96,7 +101,7 @@ namespace OpenRA.Mods.RA }); } - public virtual bool Produce(Actor self, ActorInfo producee) + public virtual bool Produce(Actor self, ActorInfo producee, string raceVariant) { if (Reservable.IsReserved(self)) return false; @@ -107,7 +112,7 @@ namespace OpenRA.Mods.RA if (exit != null) { - DoProduction(self, producee, exit); + DoProduction(self, producee, exit, raceVariant); return true; } diff --git a/OpenRA.Mods.RA/ProductionBar.cs b/OpenRA.Mods.RA/ProductionBar.cs index d599b1a176..557b0a28f8 100644 --- a/OpenRA.Mods.RA/ProductionBar.cs +++ b/OpenRA.Mods.RA/ProductionBar.cs @@ -44,6 +44,7 @@ namespace OpenRA.Mods.RA var type = info.ProductionType ?? self.Trait().Info.Produces.First(); // Per-actor queue + // Note: this includes disabled queues, as each bar must bind to exactly one queue. queue = self.TraitsImplementing() .FirstOrDefault(q => type == null || type == q.Info.Type); diff --git a/OpenRA.Mods.RA/RallyPoint.cs b/OpenRA.Mods.RA/RallyPoint.cs index 1b65a58114..2fcb3f660d 100755 --- a/OpenRA.Mods.RA/RallyPoint.cs +++ b/OpenRA.Mods.RA/RallyPoint.cs @@ -40,8 +40,8 @@ namespace OpenRA.Mods.RA public Order IssueOrder( Actor self, IOrderTargeter order, Target target, bool queued ) { - if( order.OrderID == "SetRallyPoint" ) - return new Order(order.OrderID, self, false) { TargetLocation = target.CenterPosition.ToCPos() }; + if (order.OrderID == "SetRallyPoint") + return new Order(order.OrderID, self, false) { TargetLocation = target.CenterPosition.ToCPos(), SuppressVisualFeedback = true }; return null; } diff --git a/OpenRA.Mods.RA/Scripting/LuaScriptInterface.cs b/OpenRA.Mods.RA/Scripting/LuaScriptInterface.cs index 76bfc98db5..59342d0ed7 100644 --- a/OpenRA.Mods.RA/Scripting/LuaScriptInterface.cs +++ b/OpenRA.Mods.RA/Scripting/LuaScriptInterface.cs @@ -393,7 +393,7 @@ namespace OpenRA.Mods.RA.Scripting if (bi == null) return null; - return GetSharedQueueForCategory(player, bi.Queue); + return bi.Queue.Select(q => GetSharedQueueForCategory(player, q)).FirstOrDefault(); } [LuaGlobal] @@ -402,7 +402,7 @@ namespace OpenRA.Mods.RA.Scripting var queue = GetSharedQueueForUnit(player, unit); 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] @@ -414,7 +414,8 @@ namespace OpenRA.Mods.RA.Scripting if (bi == null) return; - var queue = factory.TraitOrDefault(); + var queue = factory.TraitsImplementing() + .FirstOrDefault(q => q.Enabled); if (queue != null) queue.ResolveOrder(factory, Order.StartProduction(factory, unit, (int)amount)); @@ -434,7 +435,8 @@ namespace OpenRA.Mods.RA.Scripting [LuaGlobal] public bool PerFactoryQueueIsBusy(Actor factory) { - var queue = factory.TraitOrDefault(); + var queue = factory.TraitsImplementing() + .FirstOrDefault(q => q.Enabled); if (queue == null) return true; diff --git a/OpenRA.Mods.RA/Scripting/Properties/ProductionProperties.cs b/OpenRA.Mods.RA/Scripting/Properties/ProductionProperties.cs index cf8431829c..d51c43bdd8 100644 --- a/OpenRA.Mods.RA/Scripting/Properties/ProductionProperties.cs +++ b/OpenRA.Mods.RA/Scripting/Properties/ProductionProperties.cs @@ -28,13 +28,13 @@ namespace OpenRA.Mods.RA.Scripting [ScriptActorPropertyActivity] [Desc("Build a unit, ignoring the production queue. The activity will wait if the exit is blocked")] - public void Produce(string actorType) + public void Produce(string actorType, string raceVariant = null) { ActorInfo actorInfo; if (!self.World.Map.Rules.Actors.TryGetValue(actorType, out actorInfo)) throw new LuaException("Unknown actor type '{0}'".F(actorType)); - self.QueueActivity(new WaitFor(() => p.Produce(self, actorInfo))); + self.QueueActivity(new WaitFor(() => p.Produce(self, actorInfo, raceVariant))); } } } \ No newline at end of file diff --git a/OpenRA.Mods.RA/SupportPowers/ChronoshiftPower.cs b/OpenRA.Mods.RA/SupportPowers/ChronoshiftPower.cs index f519513884..4f76a76a0b 100644 --- a/OpenRA.Mods.RA/SupportPowers/ChronoshiftPower.cs +++ b/OpenRA.Mods.RA/SupportPowers/ChronoshiftPower.cs @@ -191,7 +191,8 @@ namespace OpenRA.Mods.RA yield return new Order(order, manager.self, false) { TargetLocation = xy, - ExtraLocation = sourceLocation + ExtraLocation = sourceLocation, + SuppressVisualFeedback = true }; } diff --git a/OpenRA.Mods.RA/SupportPowers/IronCurtainPower.cs b/OpenRA.Mods.RA/SupportPowers/IronCurtainPower.cs index 4f7fbe5057..5836b9f10e 100644 --- a/OpenRA.Mods.RA/SupportPowers/IronCurtainPower.cs +++ b/OpenRA.Mods.RA/SupportPowers/IronCurtainPower.cs @@ -88,7 +88,7 @@ namespace OpenRA.Mods.RA { world.CancelInputMode(); if (mi.Button == MouseButton.Left && power.UnitsInRange(xy).Any()) - yield return new Order(order, manager.self, false) { TargetLocation = xy }; + yield return new Order(order, manager.self, false) { TargetLocation = xy, SuppressVisualFeedback = true }; } public void Tick(World world) diff --git a/OpenRA.Mods.RA/SupportPowers/SupportPowerManager.cs b/OpenRA.Mods.RA/SupportPowers/SupportPowerManager.cs index 5bceff7b41..2220f07b5a 100644 --- a/OpenRA.Mods.RA/SupportPowers/SupportPowerManager.cs +++ b/OpenRA.Mods.RA/SupportPowers/SupportPowerManager.cs @@ -246,7 +246,7 @@ namespace OpenRA.Mods.RA { world.CancelInputMode(); if (mi.Button == expectedButton && world.Map.IsInMap(xy)) - yield return new Order(order, manager.self, false) { TargetLocation = xy }; + yield return new Order(order, manager.self, false) { TargetLocation = xy, SuppressVisualFeedback = true }; } public virtual void Tick(World world) diff --git a/OpenRA.Mods.RA/Transforms.cs b/OpenRA.Mods.RA/Transforms.cs index 10b243d0b3..0b6b6d730b 100644 --- a/OpenRA.Mods.RA/Transforms.cs +++ b/OpenRA.Mods.RA/Transforms.cs @@ -22,23 +22,25 @@ namespace OpenRA.Mods.RA [ActorReference] public readonly string IntoActor = null; public readonly int2 Offset = int2.Zero; public readonly int Facing = 96; - public readonly string[] TransformSounds = {}; - public readonly string[] NoTransformSounds = {}; + public readonly string[] TransformSounds = { }; + public readonly string[] NoTransformSounds = { }; - public virtual object Create(ActorInitializer init) { return new Transforms(init.self, this); } + public virtual object Create(ActorInitializer init) { return new Transforms(init, this); } } class Transforms : IIssueOrder, IResolveOrder, IOrderVoice { - Actor self; - TransformsInfo Info; - BuildingInfo bi; + readonly Actor self; + readonly TransformsInfo info; + readonly BuildingInfo bi; + readonly string race; - public Transforms(Actor self, TransformsInfo info) + public Transforms(ActorInitializer init, TransformsInfo info) { - this.self = self; - Info = info; + self = init.self; + this.info = info; bi = self.World.Map.Rules.Actors[info.IntoActor].Traits.GetOrDefault(); + race = init.Contains() ? init.Get() : self.Owner.Country.Race; } public string VoicePhraseForOrder(Actor self, Order order) @@ -52,18 +54,18 @@ namespace OpenRA.Mods.RA if (b != null && b.Locked) return false; - return (bi == null || self.World.CanPlaceBuilding(Info.IntoActor, bi, self.Location + (CVec)Info.Offset, self)); + return bi == null || self.World.CanPlaceBuilding(info.IntoActor, bi, self.Location + (CVec)info.Offset, self); } public IEnumerable Orders { - get { yield return new DeployOrderTargeter( "DeployTransform", 5, () => CanDeploy() ); } + get { yield return new DeployOrderTargeter("DeployTransform", 5, () => CanDeploy()); } } - public Order IssueOrder( Actor self, IOrderTargeter order, Target target, bool queued ) + public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued) { - if( order.OrderID == "DeployTransform" ) - return new Order( order.OrderID, self, queued ); + if (order.OrderID == "DeployTransform") + return new Order(order.OrderID, self, queued); return null; } @@ -74,8 +76,9 @@ namespace OpenRA.Mods.RA if (!CanDeploy() || (b != null && !b.Lock())) { - foreach (var s in Info.NoTransformSounds) + foreach (var s in info.NoTransformSounds) Sound.PlayToPlayer(self.Owner, s); + return; } @@ -83,16 +86,16 @@ namespace OpenRA.Mods.RA self.CancelActivity(); if (self.HasTrait()) - self.QueueActivity(new Turn(Info.Facing)); + self.QueueActivity(new Turn(info.Facing)); var rb = self.TraitOrDefault(); if (rb != null && self.Info.Traits.Get().HasMakeAnimation) self.QueueActivity(new MakeAnimation(self, true, () => rb.PlayCustomAnim(self, "make"))); - self.QueueActivity(new Transform(self, Info.IntoActor) { Offset = (CVec)Info.Offset, Facing = Info.Facing, Sounds = Info.TransformSounds }); + self.QueueActivity(new Transform(self, info.IntoActor) { Offset = (CVec)info.Offset, Facing = info.Facing, Sounds = info.TransformSounds, Race = race }); } - public void ResolveOrder( Actor self, Order order ) + public void ResolveOrder(Actor self, Order order) { if (order.OrderString == "DeployTransform") DeployTransform(order.Queued); diff --git a/OpenRA.Mods.RA/Widgets/BuildPaletteWidget.cs b/OpenRA.Mods.RA/Widgets/BuildPaletteWidget.cs index 8e61264fee..eb967a22c1 100644 --- a/OpenRA.Mods.RA/Widgets/BuildPaletteWidget.cs +++ b/OpenRA.Mods.RA/Widgets/BuildPaletteWidget.cs @@ -94,7 +94,7 @@ namespace OpenRA.Mods.RA.Widgets .Where(p => p.Actor.Owner == world.LocalPlayer) .Select(p => p.Trait); - if (CurrentQueue != null && CurrentQueue.self.Destroyed) + if (CurrentQueue != null && CurrentQueue.Actor.Destroyed) CurrentQueue = null; foreach (var queue in queues) @@ -366,7 +366,7 @@ namespace OpenRA.Mods.RA.Widgets if (producing.Done) { if (unit.Traits.Contains()) - world.OrderGenerator = new PlaceBuildingOrderGenerator(CurrentQueue.self, item); + world.OrderGenerator = new PlaceBuildingOrderGenerator(CurrentQueue, item); else StartProduction(world, item); return; @@ -374,7 +374,7 @@ namespace OpenRA.Mods.RA.Widgets if (producing.Paused) { - world.IssueOrder(Order.PauseProduction(CurrentQueue.self, item, false)); + world.IssueOrder(Order.PauseProduction(CurrentQueue.Actor, item, false)); 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); 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 { 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) { - world.IssueOrder(Order.StartProduction(CurrentQueue.self, item, + world.IssueOrder(Order.StartProduction(CurrentQueue.Actor, item, Game.GetModifierKeys().HasModifier(Modifiers.Shift) ? 5 : 1)); } @@ -504,10 +504,10 @@ namespace OpenRA.Mods.RA.Widgets p += new int2(5, 35); if (!canBuildThis) { - var prereqs = buildable.Prerequisites.Select(s => Description(world.Map.Rules, s)); + var prereqs = buildable.Prerequisites.Select(s => Description(world.Map.Rules, s)).Where(s => !s.StartsWith("~")); if (prereqs.Any()) { - Game.Renderer.Fonts["Regular"].DrawText(RequiresText.F(prereqs.Where(s => !s.StartsWith("~")).JoinWith(", ")), p.ToInt2(), Color.White); + Game.Renderer.Fonts["Regular"].DrawText(RequiresText.F(prereqs.JoinWith(", ")), p.ToInt2(), Color.White); p += new int2(0, 8); } diff --git a/OpenRA.Mods.RA/Widgets/Logic/DiplomacyLogic.cs b/OpenRA.Mods.RA/Widgets/Logic/DiplomacyLogic.cs index 9c23a5b7a6..d4d02722df 100644 --- a/OpenRA.Mods.RA/Widgets/Logic/DiplomacyLogic.cs +++ b/OpenRA.Mods.RA/Widgets/Logic/DiplomacyLogic.cs @@ -87,9 +87,11 @@ namespace OpenRA.Mods.RA.Widgets.Logic if (!p.World.LobbyInfo.GlobalSettings.FragileAlliances) return; // stance changes are banned - // HACK: Abuse of the type system here with `CPos` world.IssueOrder(new Order("SetStance", world.LocalPlayer.PlayerActor, false) - { TargetLocation = new CPos((int)ss, 0), TargetString = p.InternalName }); + { + ExtraData = (uint)ss, + TargetString = p.InternalName, + }); bw.Text = ss.ToString(); } diff --git a/OpenRA.Mods.RA/Widgets/WorldCommandWidget.cs b/OpenRA.Mods.RA/Widgets/WorldCommandWidget.cs index 035092e0b4..19776dbb13 100644 --- a/OpenRA.Mods.RA/Widgets/WorldCommandWidget.cs +++ b/OpenRA.Mods.RA/Widgets/WorldCommandWidget.cs @@ -164,8 +164,7 @@ namespace OpenRA.Mods.RA.Widgets if (at != null) at.PredictedStance = nextStance; - // FIXME: Abuse of the type system here with `CPos` - return new Order("SetUnitStance", a, false) { TargetLocation = new CPos((int)nextStance, 0) }; + return new Order("SetUnitStance", a, false) { ExtraData = (uint)nextStance }; }); Game.Debug("Unit stance set to: {0}".F(nextStance)); diff --git a/OpenRA.Mods.RA/World/ChooseBuildTabOnSelect.cs b/OpenRA.Mods.RA/World/ChooseBuildTabOnSelect.cs index f9aaf00ffb..8c95e2e5e6 100644 --- a/OpenRA.Mods.RA/World/ChooseBuildTabOnSelect.cs +++ b/OpenRA.Mods.RA/World/ChooseBuildTabOnSelect.cs @@ -36,12 +36,12 @@ namespace OpenRA.Mods.RA return; // Queue-per-structure - var perqueue = world.Selection.Actors.FirstOrDefault( - a => a.IsInWorld && a.World.LocalPlayer == a.Owner && a.HasTrait()); + var perqueue = world.Selection.Actors.FirstOrDefault(a => a.IsInWorld && a.World.LocalPlayer == a.Owner + && a.TraitsImplementing().Any(q => q.Enabled)); if (perqueue != null) { - palette.SetCurrentTab(perqueue.TraitsImplementing().First()); + palette.SetCurrentTab(perqueue.TraitsImplementing().First(q => q.Enabled)); return; } @@ -55,7 +55,7 @@ namespace OpenRA.Mods.RA return; palette.SetCurrentTab(world.LocalPlayer.PlayerActor.TraitsImplementing() - .FirstOrDefault(t => types.Contains(t.Info.Type))); + .FirstOrDefault(q => q.Enabled && types.Contains(q.Info.Type))); } } } diff --git a/mods/cnc/chrome.yaml b/mods/cnc/chrome.yaml index 83549d7d47..bc880c150e 100644 --- a/mods/cnc/chrome.yaml +++ b/mods/cnc/chrome.yaml @@ -434,9 +434,9 @@ production-icons: chrome.png building: 384,0,16,16 building-disabled: 384,16,16,16 building-alert: 384,32,16,16 - defense: 400,0,16,16 - defense-disabled: 400,16,16,16 - defense-alert: 400,32,16,16 + defence: 400,0,16,16 + defence-disabled: 400,16,16,16 + defence-alert: 400,32,16,16 infantry: 416,0,16,16 infantry-disabled: 416,16,16,16 infantry-alert: 416,32,16,16 diff --git a/mods/cnc/chrome/ingame.yaml b/mods/cnc/chrome/ingame.yaml index 17d9a09f36..2b938e8feb 100644 --- a/mods/cnc/chrome/ingame.yaml +++ b/mods/cnc/chrome/ingame.yaml @@ -317,14 +317,14 @@ Container@PLAYER_WIDGETS: X: 7 Y: 7 ImageCollection: production-icons - ProductionTypeButton@DEFENSE: + ProductionTypeButton@DEFENCE: X: 35 Width: 30 Height: 30 Key: w - TooltipText: Defense + TooltipText: Defence TooltipContainer: TOOLTIP_CONTAINER - ProductionGroup: Defense + ProductionGroup: Defence Children: Image@ICON: X: 7 diff --git a/mods/cnc/maps/gdi01/map.yaml b/mods/cnc/maps/gdi01/map.yaml index fa60b3f7ae..bc8fffa2d7 100644 --- a/mods/cnc/maps/gdi01/map.yaml +++ b/mods/cnc/maps/gdi01/map.yaml @@ -464,36 +464,51 @@ Rules: ^Infantry: MustBeDestroyed: PROC: - -Buildable: + Buildable: + Prerequisites: ~disabled SILO: - -Buildable: + Buildable: + Prerequisites: ~disabled WEAP: - -Buildable: + Buildable: + Prerequisites: ~disabled HQ: - -Buildable: + Buildable: + Prerequisites: ~disabled NUK2: - -Buildable: + Buildable: + Prerequisites: ~disabled FIX: - -Buildable: + Buildable: + Prerequisites: ~disabled HPAD: - -Buildable: + Buildable: + Prerequisites: ~disabled EYE: - -Buildable: + Buildable: + Prerequisites: ~disabled GUN: - -Buildable: + Buildable: + Prerequisites: ~disabled MustBeDestroyed: GTWR: - -Buildable: + Buildable: + Prerequisites: ~disabled ATWR: - -Buildable: + Buildable: + Prerequisites: ~disabled E2: - -Buildable: + Buildable: + Prerequisites: ~disabled E3: - -Buildable: + Buildable: + Prerequisites: ~disabled E6: - -Buildable: + Buildable: + Prerequisites: ~disabled RMBO: - -Buildable: + Buildable: + Prerequisites: ~disabled BOAT: Health: HP: 1500 diff --git a/mods/cnc/maps/gdi02/map.yaml b/mods/cnc/maps/gdi02/map.yaml index a4e8489c03..08a82ce766 100644 --- a/mods/cnc/maps/gdi02/map.yaml +++ b/mods/cnc/maps/gdi02/map.yaml @@ -739,47 +739,68 @@ Rules: Player: -ConquestVictoryConditions: PROC: - -Buildable: + Buildable: + Prerequisites: ~disabled SILO: - -Buildable: + Buildable: + Prerequisites: ~disabled WEAP: - -Buildable: + Buildable: + Prerequisites: ~disabled HQ: - -Buildable: + Buildable: + Prerequisites: ~disabled NUK2: - -Buildable: + Buildable: + Prerequisites: ~disabled FIX: - -Buildable: + Buildable: + Prerequisites: ~disabled HPAD: - -Buildable: + Buildable: + Prerequisites: ~disabled EYE: - -Buildable: + Buildable: + Prerequisites: ~disabled GUN: - -Buildable: + Buildable: + Prerequisites: ~disabled GTWR: - -Buildable: + Buildable: + Prerequisites: ~disabled ATWR: - -Buildable: + Buildable: + Prerequisites: ~disabled E2: - -Buildable: + Buildable: + Prerequisites: ~disabled E3: - -Buildable: + Buildable: + Prerequisites: ~disabled E4: - -Buildable: + Buildable: + Prerequisites: ~disabled E5: - -Buildable: + Buildable: + Prerequisites: ~disabled E6: - -Buildable: + Buildable: + Prerequisites: ~disabled RMBO: - -Buildable: + Buildable: + Prerequisites: ~disabled AFLD: - -Buildable: + Buildable: + Prerequisites: ~disabled TMPL: - -Buildable: + Buildable: + Prerequisites: ~disabled OBLI: - -Buildable: + Buildable: + Prerequisites: ~disabled SAM: - -Buildable: + Buildable: + Prerequisites: ~disabled OLDLST: Inherits: LST -WithRoof: diff --git a/mods/cnc/maps/gdi03/map.yaml b/mods/cnc/maps/gdi03/map.yaml index 91790e2932..100683f0ef 100644 --- a/mods/cnc/maps/gdi03/map.yaml +++ b/mods/cnc/maps/gdi03/map.yaml @@ -902,39 +902,55 @@ Rules: ^Infantry: MustBeDestroyed: WEAP: - -Buildable: + Buildable: + Prerequisites: ~disabled NUK2: - -Buildable: + Buildable: + Prerequisites: ~disabled FIX: - -Buildable: + Buildable: + Prerequisites: ~disabled HPAD: - -Buildable: + Buildable: + Prerequisites: ~disabled EYE: - -Buildable: + Buildable: + Prerequisites: ~disabled GUN: - -Buildable: + Buildable: + Prerequisites: ~disabled ATWR: - -Buildable: + Buildable: + Prerequisites: ~disabled E3: - -Buildable: + Buildable: + Prerequisites: ~disabled E4: - -Buildable: + Buildable: + Prerequisites: ~disabled E5: - -Buildable: + Buildable: + Prerequisites: ~disabled RMBO: - -Buildable: + Buildable: + Prerequisites: ~disabled AFLD: - -Buildable: + Buildable: + Prerequisites: ~disabled TMPL: - -Buildable: + Buildable: + Prerequisites: ~disabled OBLI: - -Buildable: + Buildable: + Prerequisites: ~disabled SAM: - -Buildable: + Buildable: + Prerequisites: ~disabled Building: Power: -10 HQ: - -Buildable: + Buildable: + Prerequisites: ~disabled NOHQ: RequiresPower: CanPowerDown: diff --git a/mods/cnc/rules/ai.yaml b/mods/cnc/rules/ai.yaml index 2540269624..3f776d94ac 100644 --- a/mods/cnc/rules/ai.yaml +++ b/mods/cnc/rules/ai.yaml @@ -10,6 +10,9 @@ Player: Silo: silo UnitsCommonNames: Mcv: mcv + BuildingQueues: Building.Nod, Building.GDI + DefenseQueues: Defence.Nod, Defence.GDI + UnitQueues: Vehicle.Nod, Vehicle.GDI, Infantry.Nod, Infantry.GDI, Aircraft.Nod, Aircraft.GDI BuildingLimits: proc: 4 pyle: 2 @@ -72,6 +75,9 @@ Player: Silo: silo UnitsCommonNames: Mcv: mcv + BuildingQueues: Building.Nod, Building.GDI + DefenseQueues: Defence.Nod, Defence.GDI + UnitQueues: Vehicle.Nod, Vehicle.GDI, Infantry.Nod, Infantry.GDI, Aircraft.Nod, Aircraft.GDI BuildingLimits: proc: 4 pyle: 2 @@ -134,6 +140,9 @@ Player: Silo: silo UnitsCommonNames: Mcv: mcv + BuildingQueues: Building.Nod, Building.GDI + DefenseQueues: Defence.Nod, Defence.GDI + UnitQueues: Vehicle.Nod, Vehicle.GDI, Infantry.Nod, Infantry.GDI, Aircraft.Nod, Aircraft.GDI BuildingLimits: proc: 4 pyle: 2 @@ -187,4 +196,3 @@ Player: htnk: 50% orca: 10% SquadSize: 8 - diff --git a/mods/cnc/rules/aircraft.yaml b/mods/cnc/rules/aircraft.yaml index 0e47c0265b..6839c9bd6a 100644 --- a/mods/cnc/rules/aircraft.yaml +++ b/mods/cnc/rules/aircraft.yaml @@ -8,7 +8,7 @@ TRAN: Buildable: BuildPaletteOrder: 10 Prerequisites: hpad - Owner: gdi,nod + Queue: Aircraft.GDI, Aircraft.Nod Selectable: Bounds: 41,41 Helicopter: @@ -52,7 +52,7 @@ HELI: Buildable: BuildPaletteOrder: 20 Prerequisites: hpad, anyhq - Owner: nod + Queue: Aircraft.Nod Selectable: Bounds: 30,24 Helicopter: @@ -104,7 +104,7 @@ ORCA: Buildable: BuildPaletteOrder: 20 Prerequisites: hpad, anyhq - Owner: gdi + Queue: Aircraft.GDI Selectable: Bounds: 30,24 Helicopter: diff --git a/mods/cnc/rules/defaults.yaml b/mods/cnc/rules/defaults.yaml index a1c49cc874..cad8a0e7b1 100644 --- a/mods/cnc/rules/defaults.yaml +++ b/mods/cnc/rules/defaults.yaml @@ -15,8 +15,6 @@ Voice: VehicleVoice TargetableUnit: TargetTypes: Ground, Vehicle - Buildable: - Queue: Vehicle Repairable: Chronoshiftable: Passenger: @@ -61,8 +59,6 @@ Voice: VehicleVoice TargetableUnit: TargetTypes: Ground, Vehicle - Buildable: - Queue: Vehicle Repairable: Chronoshiftable: Passenger: @@ -106,8 +102,6 @@ RepairBuildings: hpad RearmBuildings: LandWhenIdle: false - Buildable: - Queue: Aircraft HiddenUnderFog: GainsExperience: GivesExperience: @@ -153,8 +147,6 @@ Voice: GenericVoice TargetableUnit: TargetTypes: Ground, Infantry - Buildable: - Queue: Infantry TakeCover: ProneSpeed: 0.6 RenderInfantryProne: @@ -198,7 +190,6 @@ ^CivInfantry: Inherits: ^Infantry - -Buildable: -AutoTarget: -TakeCover: -RenderInfantryProne: @@ -237,7 +228,6 @@ Buildable: Queue: Biolab BuildPaletteOrder: 50 - Owner: gdi, nod Valued: Cost: 1000 Tooltip: @@ -369,8 +359,6 @@ ^BaseBuilding: Inherits: ^Building MustBeDestroyed: - Buildable: - Queue: Building RepairableBuilding: RepairPercent: 40 RepairStep: 14 diff --git a/mods/cnc/rules/infantry.yaml b/mods/cnc/rules/infantry.yaml index 0c68dbd3b2..6bbfab34fd 100644 --- a/mods/cnc/rules/infantry.yaml +++ b/mods/cnc/rules/infantry.yaml @@ -7,7 +7,7 @@ E1: Description: General-purpose infantry.\n Strong vs Infantry\n Weak vs Vehicles Buildable: BuildPaletteOrder: 10 - Owner: gdi, nod + Queue: Infantry.GDI, Infantry.Nod Selectable: Bounds: 12,17,0,-6 Mobile: @@ -31,7 +31,7 @@ E2: Buildable: BuildPaletteOrder: 40 Prerequisites: anyhq - Owner: gdi + Queue: Infantry.GDI Selectable: Bounds: 12,17,0,-6 Mobile: @@ -60,7 +60,7 @@ E3: Description: Anti-tank/Anti-aircraft infantry. \n Strong vs Tanks, Aircraft\n Weak vs Infantry Buildable: BuildPaletteOrder: 20 - Owner: nod, gdi + Queue: Infantry.GDI, Infantry.Nod Selectable: Bounds: 12,17,0,-6 Mobile: @@ -87,8 +87,8 @@ E4: Description: Advanced Anti-infantry unit.\n Strong vs Infantry, Buildings\n Weak vs Tanks Buildable: BuildPaletteOrder: 40 - Owner: nod Prerequisites: anyhq + Queue: Infantry.Nod Selectable: Bounds: 12,17,0,-6 Mobile: @@ -117,8 +117,8 @@ E5: Description: Advanced general-purpose infantry.\n Strong vs all Ground units Buildable: BuildPaletteOrder: 50 - Owner: nod Prerequisites: tmpl + Queue: Infantry.Nod Selectable: Bounds: 12,17,0,-6 Mobile: @@ -153,7 +153,7 @@ E6: Description: Infiltrates and captures enemy structures.\n Strong vs Nothing\n Weak vs Everything Buildable: BuildPaletteOrder: 30 - Owner: gdi,nod + Queue: Infantry.GDI, Infantry.Nod Selectable: Bounds: 12,17,0,-6 Mobile: @@ -181,8 +181,8 @@ RMBO: Description: Elite sniper infantry unit.\n Strong vs Infantry, Buildings\n Weak vs Vehicles Buildable: BuildPaletteOrder: 50 - Owner: gdi Prerequisites: eye + Queue: Infantry.GDI Selectable: Bounds: 12,17,0,-6 Voice: CommandoVoice @@ -215,7 +215,6 @@ PVICE: Buildable: Queue: Biolab BuildPaletteOrder: 40 - Owner: gdi, nod Tooltip: Description: Mutated abomination that spits liquid tiberium.\n Strong vs Infantry, Buildings\n Weak vs Aircraft DrawLineToTarget: diff --git a/mods/cnc/rules/ships.yaml b/mods/cnc/rules/ships.yaml index 313d992b2d..b52958b617 100644 --- a/mods/cnc/rules/ships.yaml +++ b/mods/cnc/rules/ships.yaml @@ -39,7 +39,7 @@ LST: Buildable: Queue: Vehicle BuildPaletteOrder: 1000 - Owner: None + Prerequisites: ~disabled Mobile: Crushes: crate TerrainSpeeds: diff --git a/mods/cnc/rules/structures.yaml b/mods/cnc/rules/structures.yaml index d91e718042..251cf083e9 100644 --- a/mods/cnc/rules/structures.yaml +++ b/mods/cnc/rules/structures.yaml @@ -1,9 +1,9 @@ FACT: Inherits: ^BaseBuilding Buildable: - Queue: Building + Queue: Building.GDI, Building.Nod BuildPaletteOrder: 1000 - Owner: None + Prerequisites: ~disabled Valued: Cost: 2000 Tooltip: @@ -21,30 +21,56 @@ FACT: Range: 10c0 Bib: Production: - Produces: Building,Defense + Produces: Building.GDI, Buildings.Nod, Defence.GDI, Defence.Nod Transforms: IntoActor: mcv Offset: 1,1 Facing: 108 - ProductionQueue@Building: - Type: Building + ProductionQueue@GDIBuilding: + Type: Building.GDI + Race: gdi + RequireOwner: false Group: Building BuildSpeed: .4 LowPowerSlowdown: 2 QueuedAudio: Building ReadyAudio: ConstructionComplete - ProductionQueue@Defense: - Type: Defense - Group: Defense + ProductionQueue@NodBuilding: + Type: Building.Nod + Race: nod + RequireOwner: false + Group: Building + BuildSpeed: .4 + LowPowerSlowdown: 2 + QueuedAudio: Building + ReadyAudio: ConstructionComplete + ProductionQueue@GDIDefense: + Type: Defence.GDI + Race: gdi + RequireOwner: false + Group: Defence + BuildSpeed: .4 + LowPowerSlowdown: 3 + QueuedAudio: Building + ReadyAudio: ConstructionComplete + ProductionQueue@NodDefense: + Type: Defence.Nod + Race: nod + RequireOwner: false + Group: Defence BuildSpeed: .4 LowPowerSlowdown: 3 QueuedAudio: Building ReadyAudio: ConstructionComplete BaseBuilding: - ProductionBar@Building: - ProductionType: Building - ProductionBar@Defense: - ProductionType: Defense + ProductionBar@BuildingGDI: + ProductionType: Building.GDI + ProductionBar@BuildingNod: + ProductionType: Building.Nod + ProductionBar@DefenceGDI: + ProductionType: Defence.GDI + ProductionBar@DefenceNod: + ProductionType: Defence.Nod BaseProvider: Cooldown: 75 Range: 14 @@ -61,8 +87,8 @@ NUKE: Prerequisite: anypower Buildable: BuildPaletteOrder: 10 - Owner: gdi,nod Prerequisites: fact + Queue: Building.GDI, Building.Nod Building: Power: 100 Footprint: x_ xx @@ -85,7 +111,7 @@ NUK2: Buildable: BuildPaletteOrder: 30 Prerequisites: anyhq - Owner: gdi,nod + Queue: Building.GDI, Building.Nod Building: Power: 200 Footprint: xx xx @@ -106,7 +132,7 @@ PROC: Buildable: BuildPaletteOrder: 20 Prerequisites: anypower - Owner: gdi,nod + Queue: Building.GDI, Building.Nod Building: Power: -50 Footprint: _x_ xxx === @@ -142,10 +168,9 @@ SILO: Name: Tiberium Silo Description: Stores processed Tiberium Buildable: - Queue: Defense BuildPaletteOrder: 35 Prerequisites: proc - Owner: gdi,nod + Queue: Defence.GDI, Defence.Nod Building: Power: -10 Footprint: xx @@ -179,7 +204,7 @@ PYLE: Buildable: BuildPaletteOrder: 40 Prerequisites: anypower - Owner: gdi + Queue: Building.GDI Building: Power: -20 Footprint: xx xx @@ -197,10 +222,11 @@ PYLE: SpawnOffset: 298,298,0 ExitCell: 1,1 Production: - Produces: Infantry + Produces: Infantry.GDI ProductionQueue: - Type: Infantry + Type: Infantry.GDI Group: Infantry + RequireOwner: false BuildSpeed: .4 LowPowerSlowdown: 3 ProductionBar: @@ -217,7 +243,7 @@ HAND: Buildable: BuildPaletteOrder: 40 Prerequisites: anypower - Owner: nod + Queue: Building.Nod Building: Power: -20 Footprint: __ xx xx @@ -232,10 +258,11 @@ HAND: SpawnOffset: 512,1024,0 ExitCell: 1,2 Production: - Produces: Infantry + Produces: Infantry.Nod ProductionQueue: - Type: Infantry + Type: Infantry.Nod Group: Infantry + RequireOwner: false BuildSpeed: .4 LowPowerSlowdown: 3 ProductionBar: @@ -252,7 +279,7 @@ AFLD: Buildable: BuildPaletteOrder: 50 Prerequisites: proc - Owner: nod + Queue: Building.Nod Building: Power: -30 Footprint: xxxx xxxx @@ -268,11 +295,12 @@ AFLD: SpawnOffset: -1024,0,0 ExitCell: 3,1 ProductionAirdrop: - Produces: Vehicle + Produces: Vehicle.Nod WithDeliveryAnimation: ProductionQueue: - Type: Vehicle + Type: Vehicle.Nod Group: Vehicle + RequireOwner: false BuildSpeed: .4 LowPowerSlowdown: 3 ReadyAudio: @@ -290,7 +318,7 @@ WEAP: Buildable: BuildPaletteOrder: 50 Prerequisites: proc - Owner: gdi + Queue: Building.GDI Building: Power: -30 Footprint: ___ xxx === @@ -308,9 +336,10 @@ WEAP: SpawnOffset: -341,-341,0 ExitCell: 0,2 Production: - Produces: Vehicle + Produces: Vehicle.GDI ProductionQueue: - Type: Vehicle + Type: Vehicle.GDI + RequireOwner: false Group: Vehicle BuildSpeed: .4 LowPowerSlowdown: 3 @@ -326,7 +355,7 @@ HPAD: Buildable: BuildPaletteOrder: 60 Prerequisites: proc - Owner: gdi,nod + Queue: Building.GDI, Building.Nod Building: Power: -10 Footprint: xx xx @@ -338,17 +367,29 @@ HPAD: Exit@1: SpawnOffset: 0,-256,0 Production: - Produces: Aircraft + Produces: Aircraft.GDI, Aircraft.Nod Reservable: RepairsUnits: WithRepairAnimation: RallyPoint: - ProductionQueue: - Type: Aircraft + ProductionQueue@GDI: + Type: Aircraft.GDI + Race: gdi Group: Aircraft + RequireOwner: false BuildSpeed: .4 LowPowerSlowdown: 3 - ProductionBar: + ProductionQueue@Nod: + Type: Aircraft.Nod + Race: nod + Group: Aircraft + RequireOwner: false + BuildSpeed: .4 + LowPowerSlowdown: 3 + ProductionBar@GDI: + ProductionType: Aircraft.GDI + ProductionBar@Nod: + ProductionType: Aircraft.Nod HQ: Inherits: ^BaseBuilding @@ -362,7 +403,7 @@ HQ: Buildable: BuildPaletteOrder: 70 Prerequisites: proc - Owner: gdi,nod + Queue: Building.GDI, Building.Nod Building: Power: -40 Footprint: x_ xx @@ -410,7 +451,7 @@ FIX: Buildable: BuildPaletteOrder: 80 Prerequisites: vehicleproduction - Owner: gdi,nod + Queue: Building.GDI, Building.Nod Building: Power: -30 Footprint: _x_ xxx _x_ @@ -438,7 +479,7 @@ EYE: Buildable: BuildPaletteOrder: 100 Prerequisites: anyhq - Owner: gdi + Queue: Building.GDI Building: Power: -200 Footprint: x_ xx @@ -483,7 +524,7 @@ TMPL: Buildable: BuildPaletteOrder: 100 Prerequisites: anyhq - Owner: nod + Queue: Building.Nod Building: Power: -150 Footprint: ___ xxx xxx @@ -525,10 +566,9 @@ GUN: Name: Turret Description: Basic Anti-Tank base defense.\n Strong vs Tanks, vehicles\n Weak vs Infantry Buildable: - Queue: Defense BuildPaletteOrder: 45 Prerequisites: barracks - Owner: gdi,nod + Queue: Defence.GDI, Defence.Nod Building: Power: -20 -GivesBuildableArea: @@ -568,10 +608,9 @@ SAM: Name: SAM Site Description: Anti-Aircraft base defense.\n Strong vs Aircraft\n Cannot target Ground units. Buildable: - Queue: Defense BuildPaletteOrder: 50 Prerequisites: hand - Owner: nod + Queue: Defence.Nod Building: Power: -20 Footprint: xx @@ -608,10 +647,9 @@ OBLI: Name: Obelisk of Light Description: Advanced base defense. \nRequires power to operate.\n Strong vs all Ground units\n Cannot target Aircraft Buildable: - Queue: Defense BuildPaletteOrder: 60 Prerequisites: tmpl - Owner: nod + Queue: Defence.Nod Building: Power: -150 Footprint: _ x @@ -654,10 +692,9 @@ GTWR: Name: Guard Tower Description: Basic defensive structure.\n Strong vs Infantry\n Weak vs Tanks Buildable: - Queue: Defense BuildPaletteOrder: 40 Prerequisites: barracks - Owner: gdi,nod + Queue: Defence.GDI Building: Power: -10 -GivesBuildableArea: @@ -693,10 +730,9 @@ ATWR: Name: Advanced Guard Tower Description: All-purpose defensive structure.\n Strong vs Aircraft, Tanks\n Weak vs Infantry Buildable: - Queue: Defense BuildPaletteOrder: 60 Prerequisites: anyhq - Owner: gdi + Queue: Defence.GDI Building: Power: -40 Footprint: _ x @@ -742,10 +778,9 @@ SBAG: Name: Sandbag Barrier Description: Stops infantry & light vehicles. \nCan be crushed by tanks. Buildable: - Queue: Defense BuildPaletteOrder: 20 Prerequisites: fact - Owner: gdi + Queue: Defence.GDI Health: HP: 100 Armor: @@ -768,10 +803,9 @@ CYCL: Name: Chain Link Barrier Description: Stops infantry & light vehicles. \nCan be crushed by tanks. Buildable: - Queue: Defense BuildPaletteOrder: 20 Prerequisites: fact - Owner: nod + Queue: Defence.Nod Health: HP: 100 Armor: @@ -794,10 +828,9 @@ BRIK: Name: Concrete Barrier Description: Stop units. Buildable: - Queue: Defense BuildPaletteOrder: 30 Prerequisites: vehicleproduction - Owner: gdi,nod + Queue: Defence.GDI, Defence.Nod Health: HP: 250 Armor: diff --git a/mods/cnc/rules/tech.yaml b/mods/cnc/rules/tech.yaml index 0d9426096f..7d618558df 100644 --- a/mods/cnc/rules/tech.yaml +++ b/mods/cnc/rules/tech.yaml @@ -61,6 +61,7 @@ BIO: ProductionQueue: Type: Biolab Group: Infantry + RequireOwner: false BuildSpeed: .4 LowPowerSlowdown: 3 ProductionBar: @@ -88,7 +89,7 @@ MISS: Buildable: Queue: Building BuildPaletteOrder: 1000 - Owner: None + Prerequisites: ~disabled Valued: Cost: 2000 Bib: diff --git a/mods/cnc/rules/vehicles.yaml b/mods/cnc/rules/vehicles.yaml index 659e3442b2..f6cfeea34b 100644 --- a/mods/cnc/rules/vehicles.yaml +++ b/mods/cnc/rules/vehicles.yaml @@ -8,7 +8,7 @@ MCV: Buildable: BuildPaletteOrder: 100 Prerequisites: anyhq - Owner: gdi,nod + Queue: Vehicle.GDI, Vehicle.Nod Selectable: Priority: 3 Mobile: @@ -47,7 +47,7 @@ HARV: Buildable: BuildPaletteOrder: 10 Prerequisites: proc - Owner: gdi,nod + Queue: Vehicle.GDI, Vehicle.Nod Selectable: Priority: 7 Bounds: 36,36 @@ -84,7 +84,7 @@ APC: Buildable: BuildPaletteOrder: 30 Prerequisites: pyle - Owner: gdi + Queue: Vehicle.GDI Mobile: ROT: 8 Speed: 128 @@ -131,7 +131,7 @@ ARTY: Buildable: BuildPaletteOrder: 60 Prerequisites: anyhq - Owner: nod + Queue: Vehicle.Nod Mobile: ROT: 2 Speed: 85 @@ -167,7 +167,7 @@ FTNK: Buildable: BuildPaletteOrder: 50 Prerequisites: anyhq - Owner: nod + Queue: Vehicle.Nod Mobile: ROT: 7 Speed: 113 @@ -203,7 +203,7 @@ BGGY: Buildable: BuildPaletteOrder: 20 Prerequisites: afld - Owner: nod + Queue: Vehicle.Nod Mobile: ROT: 10 Speed: 170 @@ -238,7 +238,7 @@ BIKE: Buildable: BuildPaletteOrder: 30 Prerequisites: afld - Owner: nod + Queue: Vehicle.Nod Mobile: ROT: 10 Speed: 213 @@ -275,7 +275,7 @@ JEEP: Buildable: BuildPaletteOrder: 20 Prerequisites: weap - Owner: gdi + Queue: Vehicle.GDI Mobile: ROT: 10 Speed: 156 @@ -310,7 +310,7 @@ LTNK: Buildable: BuildPaletteOrder: 40 Prerequisites: anyhq - Owner: nod + Queue: Vehicle.Nod Mobile: ROT: 7 Speed: 113 @@ -349,7 +349,7 @@ MTNK: Buildable: BuildPaletteOrder: 40 Prerequisites: anyhq - Owner: gdi + Queue: Vehicle.GDI Mobile: Speed: 85 Health: @@ -389,7 +389,7 @@ HTNK: Buildable: BuildPaletteOrder: 60 Prerequisites: eye - Owner: gdi + Queue: Vehicle.GDI Mobile: Crushes: wall, heavywall, crate, infantry Speed: 56 @@ -442,7 +442,7 @@ MSAM: Buildable: BuildPaletteOrder: 50 Prerequisites: anyhq - Owner: gdi + Queue: Vehicle.GDI Mobile: Speed: 85 ROT: 4 @@ -479,7 +479,7 @@ MLRS: Buildable: BuildPaletteOrder: 70 Prerequisites: anyhq - Owner: nod + Queue: Vehicle.Nod Mobile: Speed: 99 ROT: 7 @@ -519,7 +519,7 @@ STNK: Buildable: BuildPaletteOrder: 90 Prerequisites: tmpl - Owner: nod + Queue: Vehicle.Nod Mobile: ROT: 10 Speed: 142