Merge pull request #5685 from pchote/race-restriction

Add capturable technology support. Fixes #2129
This commit is contained in:
Matthias Mailänder
2014-06-27 08:23:59 +02:00
50 changed files with 675 additions and 431 deletions

View File

@@ -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; } }

View File

@@ -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);

View File

@@ -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;

View File

@@ -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<INotifyDelivery>())
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);
}));

View File

@@ -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<ProductionQueue>());
&& a.TraitsImplementing<ProductionQueue>().Any(q => q.Enabled));
if (producer != null)
tabsWidget.Value.CurrentQueue = producer.TraitsImplementing<ProductionQueue>().First();
tabsWidget.Value.CurrentQueue = producer.TraitsImplementing<ProductionQueue>().First(q => q.Enabled);
}
}
}

View File

@@ -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;

View File

@@ -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<BuildingInfo>())
{
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

View File

@@ -196,7 +196,7 @@ namespace OpenRA.Mods.Cnc.Widgets
if (a.HasTrait<ProductionQueue>())
{
var allQueues = a.World.ActorsWithTrait<ProductionQueue>()
.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)

View File

@@ -68,12 +68,12 @@ namespace OpenRA.Mods.RA.Render
var production = self.TraitOrDefault<Production>();
var perBuildingQueues = self.TraitsImplementing<ProductionQueue>();
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<ProductionQueue>();
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)

View File

@@ -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
});
}
}

View File

@@ -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<Player, Enemy> aggro = new Cache<Player, Enemy>(_ => new Enemy());
BaseBuilder[] builders;
List<BaseBuilder> builders = new List<BaseBuilder>();
List<Squad> squads = new List<Squad>();
List<Actor> unitsHangingAroundTheBase = new List<Actor>();
@@ -144,10 +147,11 @@ namespace OpenRA.Mods.RA.AI
playerPower = p.PlayerActor.Trait<PowerManager>();
supportPowerMngr = p.PlayerActor.Trait<SupportPowerManager>();
playerResource = p.PlayerActor.Trait<PlayerResources>();
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<BuildableInfo>().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<ProductionQueue> FindQueues(string category)
{
return world.ActorsWithTrait<ProductionQueue>()
.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)

View File

@@ -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<Health>();
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<Cargo>();
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<INotifyTransformed>())
nt.OnTransformed(a);

View File

@@ -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)

View File

@@ -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

View File

@@ -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<RaceInit>() ? init.Get<RaceInit, string>() : 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()
};

View File

@@ -42,8 +42,9 @@ namespace OpenRA.Mods.RA
var bi = i.Value.Traits.GetOrDefault<BuildableInfo>();
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));
}
}
}

View File

@@ -517,6 +517,7 @@
<Compile Include="Render\WithRepairOverlay.cs" />
<Compile Include="Activities\MoveWithinRange.cs" />
<Compile Include="Lint\CheckPlayers.cs" />
<Compile Include="Player\ProvidesCustomPrerequisite.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.Game\OpenRA.Game.csproj">

View File

@@ -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 };
}
}

View File

@@ -22,19 +22,22 @@ namespace OpenRA.Mods.RA.Orders
readonly Actor Producer;
readonly string Building;
readonly BuildingInfo BuildingInfo;
IEnumerable<IRenderable> 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<BuildingInfo>();
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<BuildingInfo>();
buildOk = map.SequenceProvider.GetSequence("overlay", "build-valid-{0}".F(tileset)).GetSprite(0);
buildBlocked = map.SequenceProvider.GetSequence("overlay", "build-invalid").GetSprite(0);
}
public IEnumerable<Order> 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<LineBuildInfo>();
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
};
}
}

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.")]
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<Production>())
{
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<ActorInfo> AllItems()
{
return isActive ? base.AllItems() : None;
return isActive ? base.AllItems() : NoItems;
}
public override IEnumerable<ActorInfo> 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<BuildableInfo>())
@@ -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<BuildableInfo>().Queue;
var selfsameBuildings = self.World.ActorsWithTrait<Production>()
.Where(p => p.Trait.Info.Produces.Contains(unit.Traits.Get<BuildableInfo>().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;

View File

@@ -28,19 +28,17 @@ namespace OpenRA.Mods.RA
{
var prevItems = GetNumBuildables(self.Owner);
// Find the queue with the target actor
var queue = w.ActorsWithTrait<ProductionQueue>()
.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<ProductionQueue>()
.FirstOrDefault(q => q.CanBuild(unit));
if (queue == null)
return;
var unit = self.World.Map.Rules.Actors[order.TargetString];
var buildingInfo = unit.Traits.Get<BuildingInfo>();
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<Production>()
.Where(x => x.Actor.Owner == self.Owner
&& x.Actor.Info.Traits.Get<ProductionInfo>().Produces.Contains(bi.Queue))
&& x.Actor.Info.Traits.Get<ProductionInfo>().Produces.Intersect(bi.Queue).Any())
.ToList();
var producer = producers.Where(x => x.Actor.IsPrimaryBuilding()).Concat(producers)
.FirstOrDefault();

View File

@@ -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<ActorInfo, ProductionState> produceable;
List<ProductionItem> queue = new List<ProductionItem>();
// 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 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<ActorInfo, ProductionState> 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<PlayerResources>();
PlayerPower = playerActor.Trait<PowerManager>();
playerPower = playerActor.Trait<PowerManager>();
developerMode = playerActor.Trait<DeveloperMode>();
Race = self.Owner.Country;
Produceable = InitTech(playerActor);
Race = init.Contains<RaceInit>() ? init.Get<RaceInit, string>() : 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<PowerManager>();
playerResources = newOwner.PlayerActor.Trait<PlayerResources>();
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<PowerManager>();
playerResources = newOwner.PlayerActor.Trait<PlayerResources>();
developerMode = newOwner.PlayerActor.Trait<DeveloperMode>();
// 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();
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<TechTree>().Remove(this);
CacheProduceables(newOwner.PlayerActor);
newOwner.PlayerActor.Trait<TechTree>().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<ActorInfo, ProductionState> InitTech(Actor playerActor)
void CacheProduceables(Actor playerActor)
{
var tech = new Dictionary<ActorInfo, ProductionState>();
produceable = new Dictionary<ActorInfo, ProductionState>();
if (!Enabled)
return;
var ttc = playerActor.Trait<TechTree>();
foreach (var a in AllBuildables(Info.Type))
{
var bi = a.Traits.Get<BuildableInfo>();
// 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<ActorInfo> AllBuildables(string category)
{
return self.World.Map.Rules.Actors.Values
.Where( x => x.Name[ 0 ] != '^' )
.Where( x => x.Traits.Contains<BuildableInfo>() )
.Where( x => x.Traits.Get<BuildableInfo>().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<BuildableInfo>() &&
x.Traits.Get<BuildableInfo>().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<ProductionItem> AllQueued()
{
return Queue;
return queue;
}
public virtual IEnumerable<ActorInfo> AllItems()
{
if (self.World.AllowDevCommands && self.Owner.PlayerActor.Trait<DeveloperMode>().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<ActorInfo> BuildableItems()
{
if (self.World.AllowDevCommands && self.Owner.PlayerActor.Trait<DeveloperMode>().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<BuildableInfo>();
if (bi.Queue != Info.Type)
if (!bi.Queue.Contains(Info.Type))
return; /* Not built by this queue */
var cost = unit.Traits.Contains<ValuedInfo>() ? unit.Traits.Get<ValuedInfo>().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<Buildable>().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<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>();
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<BuildableInfo>())
if (unit == null || !unit.Traits.Contains<BuildableInfo>())
return 0;
if (self.World.AllowDevCommands && self.Owner.PlayerActor.Trait<DeveloperMode>().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<Production>().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; }
}
}

View File

@@ -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<RaceInit>())
race = init.Get<RaceInit, string>();
enabled = info.Race.Contains(race);
}
}
public IEnumerable<string> 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<string>
{
[FieldFromYamlKey] public readonly string Race;
public RaceInit() { }
public RaceInit(string race) { Race = race; }
public string Value(World world) { return Race; }
}
}

View File

@@ -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<string, List<Actor>> GatherOwnedPrerequisites(Player player)
{
var ret = new Cache<string, List<Actor>>(x => new List<Actor>());
@@ -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<string> ProvidesPrerequisites { get { yield return info.Prerequisite; } }
public ProvidesCustomPrerequisite(ProvidesCustomPrerequisiteInfo info)
{
this.info = info;
}
}
}

View File

@@ -53,7 +53,7 @@ namespace OpenRA.Mods.RA
rp = Exts.Lazy(() => self.IsDead() ? null : self.TraitOrDefault<RallyPoint>());
}
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<IMove>();
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;
}

View File

@@ -44,6 +44,7 @@ namespace OpenRA.Mods.RA
var type = info.ProductionType ?? self.Trait<Production>().Info.Produces.First();
// Per-actor queue
// Note: this includes disabled queues, as each bar must bind to exactly one queue.
queue = self.TraitsImplementing<ProductionQueue>()
.FirstOrDefault(q => type == null || type == q.Info.Type);

View File

@@ -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;
}

View File

@@ -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<ProductionQueue>();
var queue = factory.TraitsImplementing<ProductionQueue>()
.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<ProductionQueue>();
var queue = factory.TraitsImplementing<ProductionQueue>()
.FirstOrDefault(q => q.Enabled);
if (queue == null)
return true;

View File

@@ -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)));
}
}
}

View File

@@ -191,7 +191,8 @@ namespace OpenRA.Mods.RA
yield return new Order(order, manager.self, false)
{
TargetLocation = xy,
ExtraLocation = sourceLocation
ExtraLocation = sourceLocation,
SuppressVisualFeedback = true
};
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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<BuildingInfo>();
race = init.Contains<RaceInit>() ? init.Get<RaceInit, string>() : 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<IOrderTargeter> 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<IFacing>())
self.QueueActivity(new Turn(Info.Facing));
self.QueueActivity(new Turn(info.Facing));
var rb = self.TraitOrDefault<RenderBuilding>();
if (rb != null && self.Info.Traits.Get<RenderBuildingInfo>().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);

View File

@@ -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<BuildingInfo>())
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);
}

View File

@@ -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();
}

View File

@@ -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));

View File

@@ -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<ProductionQueue>());
var perqueue = world.Selection.Actors.FirstOrDefault(a => a.IsInWorld && a.World.LocalPlayer == a.Owner
&& a.TraitsImplementing<ProductionQueue>().Any(q => q.Enabled));
if (perqueue != null)
{
palette.SetCurrentTab(perqueue.TraitsImplementing<ProductionQueue>().First());
palette.SetCurrentTab(perqueue.TraitsImplementing<ProductionQueue>().First(q => q.Enabled));
return;
}
@@ -55,7 +55,7 @@ namespace OpenRA.Mods.RA
return;
palette.SetCurrentTab(world.LocalPlayer.PlayerActor.TraitsImplementing<ProductionQueue>()
.FirstOrDefault(t => types.Contains(t.Info.Type)));
.FirstOrDefault(q => q.Enabled && types.Contains(q.Info.Type)));
}
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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:

View File

@@ -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:

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View File

@@ -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:

View File

@@ -39,7 +39,7 @@ LST:
Buildable:
Queue: Vehicle
BuildPaletteOrder: 1000
Owner: None
Prerequisites: ~disabled
Mobile:
Crushes: crate
TerrainSpeeds:

View File

@@ -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:

View File

@@ -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:

View File

@@ -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