Merge pull request #5833 from pchote/cnc-techlevels

Closes #3221
This commit is contained in:
Matthias Mailänder
2014-07-09 08:51:29 +02:00
11 changed files with 342 additions and 249 deletions

View File

@@ -9,95 +9,215 @@
#endregion #endregion
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenRA.Mods.RA.Buildings;
using OpenRA.Traits;
namespace OpenRA.Mods.RA.AI namespace OpenRA.Mods.RA.AI
{ {
class BaseBuilder class BaseBuilder
{ {
enum BuildState { ChooseItem, WaitForProduction, WaitForFeedback } readonly string category;
BuildState state = BuildState.WaitForFeedback; readonly HackyAI ai;
string category; readonly World world;
HackyAI ai; readonly Player player;
int lastThinkTick; readonly PowerManager playerPower;
Func<ProductionQueue, ActorInfo> chooseItem; readonly PlayerResources playerResources;
public BaseBuilder(HackyAI ai, string category, Func<ProductionQueue, ActorInfo> chooseItem) int waitTicks;
Actor[] playerBuildings;
public BaseBuilder(HackyAI ai, string category, Player p, PowerManager pm, PlayerResources pr)
{ {
this.ai = ai; this.ai = ai;
world = p.World;
player = p;
playerPower = pm;
playerResources = pr;
this.category = category; this.category = category;
this.chooseItem = chooseItem;
} }
public void Tick() public void Tick()
{ {
// Pick a free queue // Only update once per second or so
var queue = ai.FindQueues(category).FirstOrDefault(); if (--waitTicks > 0)
if (queue == null)
return; return;
playerBuildings = world.ActorsWithTrait<Building>()
.Where(a => a.Actor.Owner == player)
.Select(a => a.Actor)
.ToArray();
var active = false;
foreach (var queue in ai.FindQueues(category))
if (TickQueue(queue))
active = true;
waitTicks = active ? ai.Info.StructureProductionActiveDelay : ai.Info.StructureProductionInactiveDelay;
}
bool TickQueue(ProductionQueue queue)
{
var currentBuilding = queue.CurrentItem(); var currentBuilding = queue.CurrentItem();
switch (state)
// Waiting to build something
if (currentBuilding == null)
{ {
case BuildState.ChooseItem: var item = ChooseBuildingToBuild(queue);
var item = chooseItem(queue);
if (item == null) if (item == null)
return false;
HackyAI.BotDebug("AI: {0} is starting production of {1}".F(player, item.Name));
world.IssueOrder(Order.StartProduction(queue.Actor, item.Name, 1));
}
// Production is complete
else if (currentBuilding.Done)
{
// Choose the placement logic
// HACK: HACK HACK HACK
var type = BuildingType.Building;
if (world.Map.Rules.Actors[currentBuilding.Item].Traits.Contains<AttackBaseInfo>())
type = BuildingType.Defense;
else if (world.Map.Rules.Actors[currentBuilding.Item].Traits.Contains<OreRefineryInfo>())
type = BuildingType.Refinery;
var location = ai.ChooseBuildLocation(currentBuilding.Item, true, type);
if (location == null)
{ {
state = BuildState.WaitForFeedback; HackyAI.BotDebug("AI: {0} has nowhere to place {1}".F(player, currentBuilding.Item));
lastThinkTick = ai.ticks; world.IssueOrder(Order.CancelProduction(queue.Actor, currentBuilding.Item, 1));
} }
else else
{ {
HackyAI.BotDebug("AI: Starting production of {0}".F(item.Name)); world.IssueOrder(new Order("PlaceBuilding", player.PlayerActor, false)
state = BuildState.WaitForProduction;
ai.world.IssueOrder(Order.StartProduction(queue.Actor, item.Name, 1));
}
break;
case BuildState.WaitForProduction:
if (currentBuilding == null)
return;
if (currentBuilding.Paused)
ai.world.IssueOrder(Order.PauseProduction(queue.Actor, currentBuilding.Item, false));
else if (currentBuilding.Done)
{
state = BuildState.WaitForFeedback;
lastThinkTick = ai.ticks;
// Place the building
var type = BuildingType.Building;
if (ai.Map.Rules.Actors[currentBuilding.Item].Traits.Contains<AttackBaseInfo>())
type = BuildingType.Defense;
else if (ai.Map.Rules.Actors[currentBuilding.Item].Traits.Contains<OreRefineryInfo>())
type = BuildingType.Refinery;
var location = ai.ChooseBuildLocation(currentBuilding.Item, type);
if (location == null)
{ {
HackyAI.BotDebug("AI: Nowhere to place {0}".F(currentBuilding.Item)); TargetLocation = location.Value,
ai.world.IssueOrder(Order.CancelProduction(queue.Actor, currentBuilding.Item, 1)); TargetString = currentBuilding.Item,
} TargetActor = queue.Actor,
else SuppressVisualFeedback = true
{ });
ai.world.IssueOrder(new Order("PlaceBuilding", ai.p.PlayerActor, false)
{ return true;
TargetLocation = location.Value,
TargetString = currentBuilding.Item,
TargetActor = queue.Actor,
SuppressVisualFeedback = true
});
}
} }
break;
case BuildState.WaitForFeedback:
if (ai.ticks - lastThinkTick > HackyAI.feedbackTime)
state = BuildState.ChooseItem;
break;
} }
return true;
}
ActorInfo GetProducibleBuilding(string commonName, IEnumerable<ActorInfo> buildables, Func<ActorInfo, int> orderBy = null)
{
string[] actors;
if (!ai.Info.BuildingCommonNames.TryGetValue(commonName, out actors))
throw new InvalidOperationException("Can't find {0} in the HackyAI BuildingCommonNames definition.".F(commonName));
var available = buildables.Where(actor =>
{
// Are we able to build this?
if (!actors.Contains(actor.Name))
return false;
if (!ai.Info.BuildingLimits.ContainsKey(actor.Name))
return true;
return playerBuildings.Count(a => a.Info.Name == actor.Name) <= ai.Info.BuildingLimits[actor.Name];
});
if (orderBy != null)
return available.MaxByOrDefault(orderBy);
return available.RandomOrDefault(ai.random);
}
ActorInfo ChooseBuildingToBuild(ProductionQueue queue)
{
var buildableThings = queue.BuildableItems();
// First priority is to get out of a low power situation
if (playerPower.ExcessPower < 0)
{
var power = GetProducibleBuilding("Power", buildableThings, a => a.Traits.Get<BuildingInfo>().Power);
if (power != null && power.Traits.Get<BuildingInfo>().Power > 0)
{
HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (low power)", queue.Actor.Owner, power.Name);
return power;
}
}
// Next is to build up a strong economy
if (!ai.HasAdequateProc() || !ai.HasMinimumProc())
{
var refinery = GetProducibleBuilding("Refinery", buildableThings);
if (refinery != null)
{
HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (refinery)", queue.Actor.Owner, refinery.Name);
return refinery;
}
}
// Make sure that we can can spend as fast as we are earning
if (ai.Info.NewProductionCashThreshold > 0 && playerResources.Resources > ai.Info.NewProductionCashThreshold)
{
var production = GetProducibleBuilding("Production", buildableThings);
if (production != null)
{
HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (production)", queue.Actor.Owner, production.Name);
return production;
}
}
// Create some head room for resource storage if we really need it
if (playerResources.AlertSilo)
{
var silo = GetProducibleBuilding("Silo", buildableThings);
if (silo != null)
{
HackyAI.BotDebug("AI: {0} decided to build {1}: Priority override (silo)", queue.Actor.Owner, silo.Name);
return silo;
}
}
// Build everything else
foreach (var frac in ai.Info.BuildingFractions.Shuffle(ai.random))
{
var name = frac.Key;
// Can we build this structure?
if (!buildableThings.Any(b => b.Name == name))
continue;
// Do we want to build this structure?
var count = playerBuildings.Count(a => a.Info.Name == name);
if (count > frac.Value * playerBuildings.Length)
continue;
if (ai.Info.BuildingLimits.ContainsKey(name) && ai.Info.BuildingLimits[name] <= count)
continue;
// Will this put us into low power?
var actor = world.Map.Rules.Actors[frac.Key];
var bi = actor.Traits.Get<BuildingInfo>();
if (playerPower.ExcessPower < 0 || playerPower.ExcessPower < bi.Power)
{
// Try building a power plant instead
var power = GetProducibleBuilding("Power", buildableThings, a => a.Traits.Get<BuildingInfo>().Power);
if (power != null && power.Traits.Get<BuildingInfo>().Power > 0)
{
HackyAI.BotDebug("{0} decided to build {1}: Priority override (would be low power)", queue.Actor.Owner, power.Name);
return power;
}
}
// Lets build this
HackyAI.BotDebug("{0} decided to build {1}: Desired is {2} ({3} / {4}); current is {5} / {4}", queue.Actor.Owner, name, frac.Value, frac.Value * playerBuildings.Length, playerBuildings.Length, count);
return actor;
}
// Too spammy to keep enabled all the time, but very useful when debugging specific issues.
// HackyAI.BotDebug("{0} couldn't decide what to build for queue {1}.", queue.Actor.Owner, queue.Info.Group);
return null;
} }
} }
} }

View File

@@ -33,19 +33,36 @@ namespace OpenRA.Mods.RA.AI
public readonly int RushInterval = 600; public readonly int RushInterval = 600;
public readonly int AttackForceInterval = 30; public readonly int AttackForceInterval = 30;
[Desc("By what factor should power output exceed power consumption.")] [Desc("How long to wait (in ticks) between structure production checks when there is no active production.")]
public readonly float ExcessPowerFactor = 1.2f; public readonly int StructureProductionInactiveDelay = 125;
[Desc("By what minimum amount should power output exceed power consumption.")]
public readonly int MinimumExcessPower = 50; [Desc("How long to wait (in ticks) between structure production checks ticks when actively building things.")]
public readonly int StructureProductionActiveDelay = 10;
[Desc("Minimum range at which to build defensive structures near a combat hotspot.")]
public readonly int MinimumDefenseRadius = 5;
[Desc("Maximum range at which to build defensive structures near a combat hotspot.")]
public readonly int MaximumDefenseRadius = 20;
[Desc("Try to build another production building if there is too much cash.")]
public readonly int NewProductionCashThreshold = 5000;
[Desc("Only produce units as long as there are less than this amount of units idling inside the base.")] [Desc("Only produce units as long as there are less than this amount of units idling inside the base.")]
public readonly int IdleBaseUnitsMaximum = 12; public readonly int IdleBaseUnitsMaximum = 12;
[Desc("Radius in cells around enemy BaseBuilder (Construction Yard) where AI scans for targets to rush.")] [Desc("Radius in cells around enemy BaseBuilder (Construction Yard) where AI scans for targets to rush.")]
public readonly int RushAttackScanRadius = 15; public readonly int RushAttackScanRadius = 15;
[Desc("Radius in cells around the base that should be scanned for units to be protected.")] [Desc("Radius in cells around the base that should be scanned for units to be protected.")]
public readonly int ProtectUnitScanRadius = 15; public readonly int ProtectUnitScanRadius = 15;
[Desc("Radius in cells around a factory scanned for rally points by the AI.")] [Desc("Radius in cells around a factory scanned for rally points by the AI.")]
public readonly int RallyPointScanRadius = 8; public readonly int RallyPointScanRadius = 8;
[Desc("Radius in cells around the center of the base to expand.")]
public readonly int MaxBaseRadius = 20;
public readonly string[] UnitQueues = { "Vehicle", "Infantry", "Plane", "Ship", "Aircraft" }; public readonly string[] UnitQueues = { "Vehicle", "Infantry", "Plane", "Ship", "Aircraft" };
public readonly bool ShouldRepairBuildings = true; public readonly bool ShouldRepairBuildings = true;
@@ -91,15 +108,16 @@ namespace OpenRA.Mods.RA.AI
public sealed class HackyAI : ITick, IBot, INotifyDamage public sealed class HackyAI : ITick, IBot, INotifyDamage
{ {
bool enabled; public MersenneTwister random { get; private set; }
public int ticks; public readonly HackyAIInfo Info;
public Player p; public CPos baseCenter { get; private set; }
public MersenneTwister random; public Player p { get; private set; }
public CPos baseCenter;
PowerManager playerPower; PowerManager playerPower;
SupportPowerManager supportPowerMngr; SupportPowerManager supportPowerMngr;
PlayerResources playerResource; PlayerResources playerResource;
internal readonly HackyAIInfo Info; bool enabled;
int ticks;
HashSet<int> resourceTypeIndices; HashSet<int> resourceTypeIndices;
@@ -114,7 +132,6 @@ namespace OpenRA.Mods.RA.AI
// Units that the ai already knows about. Any unit not on this list needs to be given a role. // Units that the ai already knows about. Any unit not on this list needs to be given a role.
List<Actor> activeUnits = new List<Actor>(); List<Actor> activeUnits = new List<Actor>();
const int MaxBaseDistance = 40;
public const int feedbackTime = 30; // ticks; = a bit over 1s. must be >= netlag. public const int feedbackTime = 30; // ticks; = a bit over 1s. must be >= netlag.
public readonly World world; public readonly World world;
@@ -143,9 +160,9 @@ namespace OpenRA.Mods.RA.AI
playerResource = p.PlayerActor.Trait<PlayerResources>(); playerResource = p.PlayerActor.Trait<PlayerResources>();
foreach (var building in Info.BuildingQueues) foreach (var building in Info.BuildingQueues)
builders.Add(new BaseBuilder(this, building, q => ChooseBuildingToBuild(q, false))); builders.Add(new BaseBuilder(this, building, p, playerPower, playerResource));
foreach (var defense in Info.DefenseQueues) foreach (var defense in Info.DefenseQueues)
builders.Add(new BaseBuilder(this, defense, q => ChooseBuildingToBuild(q, true))); builders.Add(new BaseBuilder(this, defense, p, playerPower, playerResource));
random = new MersenneTwister((int)p.PlayerActor.ActorID); random = new MersenneTwister((int)p.PlayerActor.ActorID);
@@ -155,12 +172,6 @@ namespace OpenRA.Mods.RA.AI
.Select(t => world.TileSet.GetTerrainIndex(t.TerrainType))); .Select(t => world.TileSet.GetTerrainIndex(t.TerrainType)));
} }
static int GetPowerProvidedBy(ActorInfo building)
{
var bi = building.Traits.GetOrDefault<BuildingInfo>();
return bi != null ? bi.Power : 0;
}
ActorInfo ChooseRandomUnitToBuild(ProductionQueue queue) ActorInfo ChooseRandomUnitToBuild(ProductionQueue queue)
{ {
var buildableThings = queue.BuildableItems(); var buildableThings = queue.BuildableItems();
@@ -182,7 +193,7 @@ namespace OpenRA.Mods.RA.AI
.Where(a => a.Actor.Owner == p) .Where(a => a.Actor.Owner == p)
.Select(a => a.Actor.Info.Name).ToArray(); .Select(a => a.Actor.Info.Name).ToArray();
foreach (var unit in Info.UnitsToBuild) foreach (var unit in Info.UnitsToBuild.Shuffle(random))
if (buildableThings.Any(b => b.Name == unit.Key)) if (buildableThings.Any(b => b.Name == unit.Key))
if (myUnits.Count(a => a == unit.Key) < unit.Value * myUnits.Length) if (myUnits.Count(a => a == unit.Key) < unit.Value * myUnits.Length)
if (HasAdequateAirUnits(Map.Rules.Actors[unit.Key])) if (HasAdequateAirUnits(Map.Rules.Actors[unit.Key]))
@@ -212,7 +223,7 @@ namespace OpenRA.Mods.RA.AI
.Count(a => a.Actor.Owner == owner && Info.BuildingCommonNames[commonName].Contains(a.Actor.Info.Name)); .Count(a => a.Actor.Owner == owner && Info.BuildingCommonNames[commonName].Contains(a.Actor.Info.Name));
} }
ActorInfo GetBuildingInfoByCommonName(string commonName, Player owner) public ActorInfo GetBuildingInfoByCommonName(string commonName, Player owner)
{ {
if (commonName == "ConstructionYard") if (commonName == "ConstructionYard")
return Map.Rules.Actors.Where(k => Info.BuildingCommonNames[commonName].Contains(k.Key)).Random(random).Value; return Map.Rules.Actors.Where(k => Info.BuildingCommonNames[commonName].Contains(k.Key)).Random(random).Value;
@@ -220,12 +231,12 @@ namespace OpenRA.Mods.RA.AI
return GetInfoByCommonName(Info.BuildingCommonNames, commonName, owner); return GetInfoByCommonName(Info.BuildingCommonNames, commonName, owner);
} }
ActorInfo GetUnitInfoByCommonName(string commonName, Player owner) public ActorInfo GetUnitInfoByCommonName(string commonName, Player owner)
{ {
return GetInfoByCommonName(Info.UnitsCommonNames, commonName, owner); return GetInfoByCommonName(Info.UnitsCommonNames, commonName, owner);
} }
ActorInfo GetInfoByCommonName(Dictionary<string, string[]> names, string commonName, Player owner) public ActorInfo GetInfoByCommonName(Dictionary<string, string[]> names, string commonName, Player owner)
{ {
if (!names.Any() || !names.ContainsKey(commonName)) if (!names.Any() || !names.ContainsKey(commonName))
throw new InvalidOperationException("Can't find {0} in the HackyAI UnitsCommonNames definition.".F(commonName)); throw new InvalidOperationException("Can't find {0} in the HackyAI UnitsCommonNames definition.".F(commonName));
@@ -233,28 +244,21 @@ namespace OpenRA.Mods.RA.AI
return Map.Rules.Actors.Where(k => names[commonName].Contains(k.Key)).Random(random).Value; return Map.Rules.Actors.Where(k => names[commonName].Contains(k.Key)).Random(random).Value;
} }
bool HasAdequatePower() public bool HasAdequateFact()
{
// note: CNC `fact` provides a small amount of power. don't get jammed because of that.
return playerPower.PowerProvided > Info.MinimumExcessPower &&
playerPower.PowerProvided > playerPower.PowerDrained * Info.ExcessPowerFactor;
}
bool HasAdequateFact()
{ {
// Require at least one construction yard, unless we have no vehicles factory (can't build it). // Require at least one construction yard, unless we have no vehicles factory (can't build it).
return CountBuildingByCommonName("ConstructionYard", p) > 0 || return CountBuildingByCommonName("ConstructionYard", p) > 0 ||
CountBuildingByCommonName("VehiclesFactory", p) == 0; CountBuildingByCommonName("VehiclesFactory", p) == 0;
} }
bool HasAdequateProc() public bool HasAdequateProc()
{ {
// Require at least one refinery, unless we have no power (can't build it). // Require at least one refinery, unless we have no power (can't build it).
return CountBuildingByCommonName("Refinery", p) > 0 || return CountBuildingByCommonName("Refinery", p) > 0 ||
CountBuildingByCommonName("Power", p) == 0; CountBuildingByCommonName("Power", p) == 0;
} }
bool HasMinimumProc() public bool HasMinimumProc()
{ {
// Require at least two refineries, unless we have no power (can't build it) // Require at least two refineries, unless we have no power (can't build it)
// or barracks (higher priority?) // or barracks (higher priority?)
@@ -263,14 +267,6 @@ namespace OpenRA.Mods.RA.AI
CountBuildingByCommonName("Barracks", p) == 0; CountBuildingByCommonName("Barracks", p) == 0;
} }
bool HasAdequateNumber(string frac, Player owner)
{
if (Info.BuildingLimits.ContainsKey(frac))
return CountBuilding(frac, owner) < Info.BuildingLimits[frac];
return true;
}
// For mods like RA (number of building must match the number of aircraft) // For mods like RA (number of building must match the number of aircraft)
bool HasAdequateAirUnits(ActorInfo actorInfo) bool HasAdequateAirUnits(ActorInfo actorInfo)
{ {
@@ -286,108 +282,68 @@ namespace OpenRA.Mods.RA.AI
return true; return true;
} }
ActorInfo ChooseBuildingToBuild(ProductionQueue queue, bool isDefense)
{
var buildableThings = queue.BuildableItems();
if (!isDefense)
{
// Try to maintain 20% excess power
if (!HasAdequatePower())
return buildableThings.Where(a => GetPowerProvidedBy(a) > 0)
.MaxByOrDefault(a => GetPowerProvidedBy(a));
if (playerResource.AlertSilo)
return GetBuildingInfoByCommonName("Silo", p);
if (!HasAdequateProc() || !HasMinimumProc())
return GetBuildingInfoByCommonName("Refinery", p);
}
var myBuildings = p.World
.ActorsWithTrait<Building>()
.Where(a => a.Actor.Owner == p)
.Select(a => a.Actor.Info.Name)
.ToArray();
foreach (var frac in Info.BuildingFractions)
if (buildableThings.Any(b => b.Name == frac.Key))
if (myBuildings.Count(a => a == frac.Key) < frac.Value * myBuildings.Length && HasAdequateNumber(frac.Key, p) &&
playerPower.ExcessPower >= Map.Rules.Actors[frac.Key].Traits.Get<BuildingInfo>().Power)
return Map.Rules.Actors[frac.Key];
return null;
}
bool NoBuildingsUnder(IEnumerable<CPos> cells)
{
var bi = world.WorldActor.Trait<BuildingInfluence>();
return cells.All(c => bi.GetBuildingAt(c) == null);
}
CPos defenseCenter; CPos defenseCenter;
public CPos? ChooseBuildLocation(string actorType, BuildingType type) public CPos? ChooseBuildLocation(string actorType, bool distanceToBaseIsImportant, BuildingType type)
{
return ChooseBuildLocation(actorType, true, MaxBaseDistance, type);
}
public CPos? ChooseBuildLocation(string actorType, bool distanceToBaseIsImportant, int maxBaseDistance, BuildingType type)
{ {
var bi = Map.Rules.Actors[actorType].Traits.GetOrDefault<BuildingInfo>(); var bi = Map.Rules.Actors[actorType].Traits.GetOrDefault<BuildingInfo>();
if (bi == null) if (bi == null)
return null; return null;
Func<WPos, CPos, CPos?> findPos = (pos, center) => // Find the buildable cell that is closest to pos and centered around center
Func<CPos, CPos, int, int, CPos?> findPos = (center, target, minRange, maxRange) =>
{ {
for (var k = MaxBaseDistance; k >= 0; k--) var cells = Map.FindTilesInAnnulus(center, minRange, maxRange);
{
var tlist = Map.FindTilesInCircle(center, k);
foreach (var t in tlist) // Sort by distance to target if we have one
if (world.CanPlaceBuilding(actorType, bi, t, null)) if (center != target)
if (bi.IsCloseEnoughToBase(world, p, actorType, t)) cells = cells.OrderBy(c => (center - target).LengthSquared);
if (NoBuildingsUnder(Util.ExpandFootprint(FootprintUtils.Tiles(Map.Rules, actorType, bi, t), false))) else
return t; cells = cells.Shuffle(random);
foreach (var cell in cells)
{
if (!world.CanPlaceBuilding(actorType, bi, cell, null))
continue;
if (distanceToBaseIsImportant && !bi.IsCloseEnoughToBase(world, p, actorType, cell))
continue;
return cell;
} }
return null; return null;
}; };
var baseCenterPos = world.Map.CenterOfCell(baseCenter);
switch (type) switch (type)
{ {
case BuildingType.Defense: case BuildingType.Defense:
var enemyBase = FindEnemyBuildingClosestToPos(baseCenterPos);
return enemyBase != null ? findPos(enemyBase.CenterPosition, defenseCenter) : null; // Build near the closest enemy structure
var closestEnemy = world.Actors.Where(a => !a.Destroyed && a.HasTrait<Building>() && p.Stances[a.Owner] == Stance.Enemy)
.ClosestTo(world.Map.CenterOfCell(defenseCenter));
var targetCell = closestEnemy != null ? closestEnemy.Location : baseCenter;
return findPos(defenseCenter, targetCell, Info.MinimumDefenseRadius, Info.MaximumDefenseRadius);
case BuildingType.Refinery: case BuildingType.Refinery:
var tilesPos = Map.FindTilesInCircle(baseCenter, MaxBaseDistance)
.Where(a => resourceTypeIndices.Contains(Map.GetTerrainIndex(new CPos(a.X, a.Y))));
if (tilesPos.Any())
{
var pos = tilesPos.MinBy(a => (world.Map.CenterOfCell(a) - baseCenterPos).LengthSquared);
return findPos(world.Map.CenterOfCell(pos), baseCenter);
}
return null;
// Try and place the refinery near a resource field
var nearbyResources = Map.FindTilesInCircle(baseCenter, Info.MaxBaseRadius)
.Where(a => resourceTypeIndices.Contains(Map.GetTerrainIndex(a)))
.Shuffle(random);
foreach (var c in nearbyResources)
{
var found = findPos(c, baseCenter, 0, Info.MaxBaseRadius);
if (found != null)
return found;
}
// Try and find a free spot somewhere else in the base
return findPos(baseCenter, baseCenter, 0, Info.MaxBaseRadius);
case BuildingType.Building: case BuildingType.Building:
for (var k = 0; k < maxBaseDistance; k++) return findPos(baseCenter, baseCenter, 0, distanceToBaseIsImportant ? Info.MaxBaseRadius : Map.MaxTilesInCircleRange);
{
foreach (var t in Map.FindTilesInCircle(baseCenter, k))
{
if (world.CanPlaceBuilding(actorType, bi, t, null))
{
if (distanceToBaseIsImportant && !bi.IsCloseEnoughToBase(world, p, actorType, t))
continue;
if (NoBuildingsUnder(Util.ExpandFootprint(FootprintUtils.Tiles(Map.Rules, actorType, bi, t), false)))
return t;
}
}
}
break;
} }
// Can't find a build location // Can't find a build location
@@ -481,14 +437,6 @@ namespace OpenRA.Mods.RA.AI
&& a.HasTrait<BaseBuilding>() && !a.HasTrait<Mobile>()).ToList(); && a.HasTrait<BaseBuilding>() && !a.HasTrait<Mobile>()).ToList();
} }
Actor FindEnemyBuildingClosestToPos(WPos pos)
{
var closestBuilding = world.Actors.Where(a => p.Stances[a.Owner] == Stance.Enemy
&& !a.Destroyed && a.HasTrait<Building>()).ClosestTo(pos);
return closestBuilding;
}
void CleanSquads() void CleanSquads()
{ {
squads.RemoveAll(s => !s.IsValid); squads.RemoveAll(s => !s.IsValid);
@@ -579,7 +527,6 @@ namespace OpenRA.Mods.RA.AI
foreach (var a in newUnits) foreach (var a in newUnits)
{ {
BotDebug("AI: Found a newly built unit");
if (a.HasTrait<Harvester>()) if (a.HasTrait<Harvester>())
world.IssueOrder(new Order("Harvest", a, false)); world.IssueOrder(new Order("Harvest", a, false));
else else
@@ -676,10 +623,6 @@ namespace OpenRA.Mods.RA.AI
.Where(rp => rp.Actor.Owner == p && .Where(rp => rp.Actor.Owner == p &&
!IsRallyPointValid(rp.Trait.Location, rp.Actor.Info.Traits.GetOrDefault<BuildingInfo>())).ToArray(); !IsRallyPointValid(rp.Trait.Location, rp.Actor.Info.Traits.GetOrDefault<BuildingInfo>())).ToArray();
if (buildings.Length > 0)
BotDebug("Bot {0} needs to find rallypoints for {1} buildings.",
p.PlayerName, buildings.Length);
foreach (var a in buildings) foreach (var a in buildings)
world.IssueOrder(new Order("SetRallyPoint", a.Actor, false) { TargetLocation = ChooseRallyLocationNear(a.Actor), SuppressVisualFeedback = true }); world.IssueOrder(new Order("SetRallyPoint", a.Actor, false) { TargetLocation = ChooseRallyLocationNear(a.Actor), SuppressVisualFeedback = true });
} }
@@ -723,8 +666,6 @@ namespace OpenRA.Mods.RA.AI
// backup location within the main base. // backup location within the main base.
void FindAndDeployBackupMcv(Actor self) void FindAndDeployBackupMcv(Actor self)
{ {
var maxBaseDistance = Math.Max(world.Map.MapSize.X, world.Map.MapSize.Y);
// HACK: This needs to query against MCVs directly // HACK: This needs to query against MCVs directly
var mcvs = self.World.Actors.Where(a => a.Owner == p && a.HasTrait<BaseBuilding>() && a.HasTrait<Mobile>()); var mcvs = self.World.Actors.Where(a => a.Owner == p && a.HasTrait<BaseBuilding>() && a.HasTrait<Mobile>());
if (!mcvs.Any()) if (!mcvs.Any())
@@ -736,7 +677,7 @@ namespace OpenRA.Mods.RA.AI
continue; continue;
var factType = mcv.Info.Traits.Get<TransformsInfo>().IntoActor; var factType = mcv.Info.Traits.Get<TransformsInfo>().IntoActor;
var desiredLocation = ChooseBuildLocation(factType, false, maxBaseDistance, BuildingType.Building); var desiredLocation = ChooseBuildLocation(factType, false, BuildingType.Building);
if (desiredLocation == null) if (desiredLocation == null)
continue; continue;

View File

@@ -156,6 +156,7 @@ LobbyDefaults:
FragileAlliances: false FragileAlliances: false
Shroud: true Shroud: true
Fog: true Fog: true
TechLevel: Unrestricted
ChromeMetrics: ChromeMetrics:
mods/cnc/metrics.yaml mods/cnc/metrics.yaml

View File

@@ -7,6 +7,7 @@ Player:
Power: nuke,nuk2 Power: nuke,nuk2
Barracks: pyle,hand Barracks: pyle,hand
VehiclesFactory: weap,afld VehiclesFactory: weap,afld
Production: pyle,hand,weap,afld,hpad
Silo: silo Silo: silo
UnitsCommonNames: UnitsCommonNames:
Mcv: mcv Mcv: mcv
@@ -15,11 +16,11 @@ Player:
UnitQueues: Vehicle.Nod, Vehicle.GDI, Infantry.Nod, Infantry.GDI, Aircraft.Nod, Aircraft.GDI UnitQueues: Vehicle.Nod, Vehicle.GDI, Infantry.Nod, Infantry.GDI, Aircraft.Nod, Aircraft.GDI
BuildingLimits: BuildingLimits:
proc: 4 proc: 4
pyle: 2 pyle: 3
hand: 2 hand: 3
hq: 1 hq: 1
weap: 2 weap: 3
afld: 2 afld: 3
hpad: 0 hpad: 0
eye: 1 eye: 1
tmpl: 1 tmpl: 1
@@ -72,6 +73,7 @@ Player:
Power: nuke,nuk2 Power: nuke,nuk2
Barracks: pyle,hand Barracks: pyle,hand
VehiclesFactory: weap,afld VehiclesFactory: weap,afld
Production: pyle,hand,weap,afld,hpad
Silo: silo Silo: silo
UnitsCommonNames: UnitsCommonNames:
Mcv: mcv Mcv: mcv
@@ -80,12 +82,12 @@ Player:
UnitQueues: Vehicle.Nod, Vehicle.GDI, Infantry.Nod, Infantry.GDI, Aircraft.Nod, Aircraft.GDI UnitQueues: Vehicle.Nod, Vehicle.GDI, Infantry.Nod, Infantry.GDI, Aircraft.Nod, Aircraft.GDI
BuildingLimits: BuildingLimits:
proc: 4 proc: 4
pyle: 2 pyle: 3
hand: 2 hand: 3
hq: 1 hq: 1
weap: 2 weap: 3
afld: 2 afld: 3
hpad: 1 hpad: 2
eye: 1 eye: 1
tmpl: 1 tmpl: 1
fix: 0 fix: 0
@@ -137,6 +139,7 @@ Player:
Power: nuke,nuk2 Power: nuke,nuk2
Barracks: pyle,hand Barracks: pyle,hand
VehiclesFactory: weap,afld VehiclesFactory: weap,afld
Production: pyle,hand,weap,afld,hpad
Silo: silo Silo: silo
UnitsCommonNames: UnitsCommonNames:
Mcv: mcv Mcv: mcv
@@ -145,12 +148,12 @@ Player:
UnitQueues: Vehicle.Nod, Vehicle.GDI, Infantry.Nod, Infantry.GDI, Aircraft.Nod, Aircraft.GDI UnitQueues: Vehicle.Nod, Vehicle.GDI, Infantry.Nod, Infantry.GDI, Aircraft.Nod, Aircraft.GDI
BuildingLimits: BuildingLimits:
proc: 4 proc: 4
pyle: 2 pyle: 4
hand: 4 hand: 4
hq: 1 hq: 1
weap: 3 weap: 4
afld: 2 afld: 4
hpad: 1 hpad: 2
eye: 1 eye: 1
tmpl: 1 tmpl: 1
fix: 0 fix: 0

View File

@@ -52,7 +52,7 @@ HELI:
Description: Helicopter Gunship with Chainguns.\n Strong vs Infantry, Light Vehicles\n Weak vs Tanks Description: Helicopter Gunship with Chainguns.\n Strong vs Infantry, Light Vehicles\n Weak vs Tanks
Buildable: Buildable:
BuildPaletteOrder: 20 BuildPaletteOrder: 20
Prerequisites: hpad, anyhq Prerequisites: hpad, anyhq, ~techlevel.medium
Queue: Aircraft.Nod Queue: Aircraft.Nod
Selectable: Selectable:
Bounds: 30,24 Bounds: 30,24
@@ -104,7 +104,7 @@ ORCA:
Description: Helicopter Gunship with AG Missiles.\n Strong vs Buildings, Tanks\n Weak vs Infantry Description: Helicopter Gunship with AG Missiles.\n Strong vs Buildings, Tanks\n Weak vs Infantry
Buildable: Buildable:
BuildPaletteOrder: 20 BuildPaletteOrder: 20
Prerequisites: hpad, anyhq Prerequisites: hpad, anyhq, ~techlevel.medium
Queue: Aircraft.GDI Queue: Aircraft.GDI
Selectable: Selectable:
Bounds: 30,24 Bounds: 30,24

View File

@@ -30,7 +30,7 @@ E2:
Description: Fast infantry armed with grenades. \n Strong vs Buildings, slow-moving targets Description: Fast infantry armed with grenades. \n Strong vs Buildings, slow-moving targets
Buildable: Buildable:
BuildPaletteOrder: 40 BuildPaletteOrder: 40
Prerequisites: anyhq Prerequisites: anyhq, ~techlevel.medium
Queue: Infantry.GDI Queue: Infantry.GDI
Selectable: Selectable:
Bounds: 12,17,0,-6 Bounds: 12,17,0,-6
@@ -87,7 +87,7 @@ E4:
Description: Advanced Anti-infantry unit.\n Strong vs Infantry, Buildings\n Weak vs Tanks Description: Advanced Anti-infantry unit.\n Strong vs Infantry, Buildings\n Weak vs Tanks
Buildable: Buildable:
BuildPaletteOrder: 40 BuildPaletteOrder: 40
Prerequisites: anyhq Prerequisites: anyhq, ~techlevel.medium
Queue: Infantry.Nod Queue: Infantry.Nod
Selectable: Selectable:
Bounds: 12,17,0,-6 Bounds: 12,17,0,-6
@@ -117,7 +117,7 @@ E5:
Description: Advanced general-purpose infantry.\n Strong vs all Ground units Description: Advanced general-purpose infantry.\n Strong vs all Ground units
Buildable: Buildable:
BuildPaletteOrder: 50 BuildPaletteOrder: 50
Prerequisites: tmpl Prerequisites: tmpl, ~techlevel.high
Queue: Infantry.Nod Queue: Infantry.Nod
Selectable: Selectable:
Bounds: 12,17,0,-6 Bounds: 12,17,0,-6
@@ -181,7 +181,7 @@ RMBO:
Description: Elite sniper infantry unit.\n Strong vs Infantry, Buildings\n Weak vs Vehicles Description: Elite sniper infantry unit.\n Strong vs Infantry, Buildings\n Weak vs Vehicles
Buildable: Buildable:
BuildPaletteOrder: 50 BuildPaletteOrder: 50
Prerequisites: eye Prerequisites: eye, ~techlevel.high
Queue: Infantry.GDI Queue: Infantry.GDI
Selectable: Selectable:
Bounds: 12,17,0,-6 Bounds: 12,17,0,-6

View File

@@ -17,4 +17,15 @@ Player:
PlayerStatistics: PlayerStatistics:
FrozenActorLayer: FrozenActorLayer:
PlaceBeacon: PlaceBeacon:
ProvidesTechPrerequisite@low:
Name: Low
Prerequisites: techlevel.low
ProvidesTechPrerequisite@medium:
Name: Medium
Prerequisites: techlevel.low, techlevel.medium
ProvidesTechPrerequisite@nosuper:
Name: No Powers
Prerequisites: techlevel.low, techlevel.medium, techlevel.high
ProvidesTechPrerequisite@all:
Name: Unrestricted
Prerequisites: techlevel.low, techlevel.medium, techlevel.high, techlevel.superweapons

View File

@@ -110,7 +110,7 @@ NUK2:
Prerequisite: anypower Prerequisite: anypower
Buildable: Buildable:
BuildPaletteOrder: 30 BuildPaletteOrder: 30
Prerequisites: anyhq Prerequisites: anyhq, ~techlevel.medium
Queue: Building.GDI, Building.Nod Queue: Building.GDI, Building.Nod
Building: Building:
Power: 200 Power: 200
@@ -402,7 +402,7 @@ HQ:
Prerequisite: anyhq Prerequisite: anyhq
Buildable: Buildable:
BuildPaletteOrder: 70 BuildPaletteOrder: 70
Prerequisites: proc Prerequisites: proc, ~techlevel.medium
Queue: Building.GDI, Building.Nod Queue: Building.GDI, Building.Nod
Building: Building:
Power: -40 Power: -40
@@ -423,6 +423,7 @@ HQ:
DetectCloaked: DetectCloaked:
Range: 5 Range: 5
AirstrikePower: AirstrikePower:
Prerequisites: ~techlevel.superweapons
Icon: airstrike Icon: airstrike
ChargeTime: 180 ChargeTime: 180
SquadSize: 3 SquadSize: 3
@@ -477,7 +478,7 @@ EYE:
Prerequisite: anyhq Prerequisite: anyhq
Buildable: Buildable:
BuildPaletteOrder: 100 BuildPaletteOrder: 100
Prerequisites: anyhq Prerequisites: anyhq, ~techlevel.high
Queue: Building.GDI Queue: Building.GDI
Building: Building:
Power: -200 Power: -200
@@ -498,6 +499,7 @@ EYE:
DetectCloaked: DetectCloaked:
Range: 5 Range: 5
IonCannonPower: IonCannonPower:
Prerequisites: ~techlevel.superweapons
Icon: ioncannon Icon: ioncannon
ChargeTime: 180 ChargeTime: 180
Description: Ion Cannon Description: Ion Cannon
@@ -522,7 +524,7 @@ TMPL:
Prerequisite: anyhq Prerequisite: anyhq
Buildable: Buildable:
BuildPaletteOrder: 100 BuildPaletteOrder: 100
Prerequisites: anyhq Prerequisites: anyhq, ~techlevel.high
Queue: Building.Nod Queue: Building.Nod
Building: Building:
Power: -150 Power: -150
@@ -537,6 +539,7 @@ TMPL:
Range: 6c0 Range: 6c0
Bib: Bib:
NukePower: NukePower:
Prerequisites: ~techlevel.superweapons
Icon: abomb Icon: abomb
ChargeTime: 300 ChargeTime: 300
Description: Nuclear Strike Description: Nuclear Strike
@@ -646,7 +649,7 @@ OBLI:
Description: Advanced base defense. \nRequires power to operate.\n Strong vs all Ground units\n Cannot target Aircraft Description: Advanced base defense. \nRequires power to operate.\n Strong vs all Ground units\n Cannot target Aircraft
Buildable: Buildable:
BuildPaletteOrder: 60 BuildPaletteOrder: 60
Prerequisites: tmpl Prerequisites: tmpl, ~techlevel.high
Queue: Defence.Nod Queue: Defence.Nod
Building: Building:
Power: -150 Power: -150
@@ -729,7 +732,7 @@ ATWR:
Description: All-purpose defensive structure.\n Strong vs Aircraft, Tanks\n Weak vs Infantry Description: All-purpose defensive structure.\n Strong vs Aircraft, Tanks\n Weak vs Infantry
Buildable: Buildable:
BuildPaletteOrder: 60 BuildPaletteOrder: 60
Prerequisites: anyhq Prerequisites: anyhq, ~techlevel.medium
Queue: Defence.GDI Queue: Defence.GDI
Building: Building:
Power: -40 Power: -40

View File

@@ -7,7 +7,7 @@ MCV:
Description: Deploys into another Construction Yard.\n Unarmed Description: Deploys into another Construction Yard.\n Unarmed
Buildable: Buildable:
BuildPaletteOrder: 100 BuildPaletteOrder: 100
Prerequisites: anyhq Prerequisites: anyhq, ~techlevel.medium
Queue: Vehicle.GDI, Vehicle.Nod Queue: Vehicle.GDI, Vehicle.Nod
Selectable: Selectable:
Priority: 3 Priority: 3
@@ -130,7 +130,7 @@ ARTY:
Description: Long-range artillery.\n Strong vs Infantry, Vehicles & Buildings Description: Long-range artillery.\n Strong vs Infantry, Vehicles & Buildings
Buildable: Buildable:
BuildPaletteOrder: 60 BuildPaletteOrder: 60
Prerequisites: anyhq Prerequisites: anyhq, ~techlevel.medium
Queue: Vehicle.Nod Queue: Vehicle.Nod
Mobile: Mobile:
ROT: 2 ROT: 2
@@ -166,7 +166,7 @@ FTNK:
Description: Heavily armored flame-throwing vehicle.\n Strong vs Infantry, Buildings & Vehicles\n Weak vs Tanks Description: Heavily armored flame-throwing vehicle.\n Strong vs Infantry, Buildings & Vehicles\n Weak vs Tanks
Buildable: Buildable:
BuildPaletteOrder: 50 BuildPaletteOrder: 50
Prerequisites: anyhq Prerequisites: anyhq, ~techlevel.medium
Queue: Vehicle.Nod Queue: Vehicle.Nod
Mobile: Mobile:
ROT: 7 ROT: 7
@@ -309,7 +309,7 @@ LTNK:
Description: Fast, light tank.\n Strong vs Vehicles, Tanks\n Weak vs Infantry Description: Fast, light tank.\n Strong vs Vehicles, Tanks\n Weak vs Infantry
Buildable: Buildable:
BuildPaletteOrder: 40 BuildPaletteOrder: 40
Prerequisites: anyhq Prerequisites: anyhq, ~techlevel.medium
Queue: Vehicle.Nod Queue: Vehicle.Nod
Mobile: Mobile:
ROT: 7 ROT: 7
@@ -348,7 +348,7 @@ MTNK:
Description: General-Purpose GDI Tank.\n Strong vs Tanks, Vehicles\n Weak vs Infantry Description: General-Purpose GDI Tank.\n Strong vs Tanks, Vehicles\n Weak vs Infantry
Buildable: Buildable:
BuildPaletteOrder: 40 BuildPaletteOrder: 40
Prerequisites: anyhq Prerequisites: anyhq, ~techlevel.medium
Queue: Vehicle.GDI Queue: Vehicle.GDI
Mobile: Mobile:
Speed: 85 Speed: 85
@@ -388,7 +388,7 @@ HTNK:
Description: Heavily armored GDI Tank. \nCan attack Aircraft.\n Strong vs Everything Description: Heavily armored GDI Tank. \nCan attack Aircraft.\n Strong vs Everything
Buildable: Buildable:
BuildPaletteOrder: 60 BuildPaletteOrder: 60
Prerequisites: eye Prerequisites: eye, ~techlevel.high
Queue: Vehicle.GDI Queue: Vehicle.GDI
Mobile: Mobile:
Crushes: wall, heavywall, crate, infantry Crushes: wall, heavywall, crate, infantry
@@ -441,7 +441,7 @@ MSAM:
Description: Long range rocket artillery.\n Strong vs all Ground units. Description: Long range rocket artillery.\n Strong vs all Ground units.
Buildable: Buildable:
BuildPaletteOrder: 50 BuildPaletteOrder: 50
Prerequisites: anyhq Prerequisites: anyhq, ~techlevel.medium
Queue: Vehicle.GDI Queue: Vehicle.GDI
Mobile: Mobile:
Speed: 85 Speed: 85
@@ -478,7 +478,7 @@ MLRS:
Description: Powerful anti-air unit. \nCannot attack Ground units. Description: Powerful anti-air unit. \nCannot attack Ground units.
Buildable: Buildable:
BuildPaletteOrder: 70 BuildPaletteOrder: 70
Prerequisites: anyhq Prerequisites: anyhq, ~techlevel.medium
Queue: Vehicle.Nod Queue: Vehicle.Nod
Mobile: Mobile:
Speed: 99 Speed: 99
@@ -518,7 +518,7 @@ STNK:
Description: Long-range missile tank that can cloak. \nCan attack Aircraft. \nHas weak armor. Can be spotted by infantry.\n Strong vs Vehicles, Tanks\n Weak vs Infantry. Description: Long-range missile tank that can cloak. \nCan attack Aircraft. \nHas weak armor. Can be spotted by infantry.\n Strong vs Vehicles, Tanks\n Weak vs Infantry.
Buildable: Buildable:
BuildPaletteOrder: 90 BuildPaletteOrder: 90
Prerequisites: tmpl Prerequisites: tmpl, ~techlevel.high
Queue: Vehicle.Nod Queue: Vehicle.Nod
Mobile: Mobile:
ROT: 10 ROT: 10

View File

@@ -6,7 +6,9 @@ Player:
ConstructionYard: conyarda,conyardh,conyardo ConstructionYard: conyarda,conyardh,conyardo
Refinery: refa,refh,refo Refinery: refa,refh,refo
Power: pwra,pwrh,pwro Power: pwra,pwrh,pwro
Barracks: barra,barrh,barro
VehiclesFactory: lighta,lighth,lighto,heavya,heavyh,heavyo VehiclesFactory: lighta,lighth,lighto,heavya,heavyh,heavyo
Production: lighta,lighth,lighto,heavya,heavyh,heavyo,barra,barrh,barro
Silo: siloa, siloh, siloo Silo: siloa, siloh, siloo
UnitsCommonNames: UnitsCommonNames:
Mcv: mcva,mcvh,mcvo Mcv: mcva,mcvh,mcvo
@@ -72,9 +74,9 @@ Player:
rockettowera: 7% rockettowera: 7%
rockettowerh: 7% rockettowerh: 7%
rockettowero: 7% rockettowero: 7%
powra: 10% pwra: 10%
powrh: 10% pwrh: 10%
powro: 10% pwro: 10%
UnitsToBuild: UnitsToBuild:
rifle: 6% rifle: 6%
bazooka: 5% bazooka: 5%
@@ -100,6 +102,7 @@ Player:
combath: 100% combath: 100%
combato: 100% combato: 100%
SquadSize: 8 SquadSize: 8
MaxBaseRadius: 40
HackyAI@Vidius: HackyAI@Vidius:
Name: Vidious Name: Vidious
UnitQueues: Infantry, Vehicle, Armor, Starport UnitQueues: Infantry, Vehicle, Armor, Starport
@@ -107,7 +110,9 @@ Player:
ConstructionYard: conyarda,conyardh,conyardo ConstructionYard: conyarda,conyardh,conyardo
Refinery: refa,refh,refo Refinery: refa,refh,refo
Power: pwra,pwrh,pwro Power: pwra,pwrh,pwro
Barracks: barra,barrh,barro
VehiclesFactory: lighta,lighth,lighto,heavya,heavyh,heavyo VehiclesFactory: lighta,lighth,lighto,heavya,heavyh,heavyo
Production: lighta,lighth,lighto,heavya,heavyh,heavyo,barra,barrh,barro
Silo: siloa, siloh, siloo Silo: siloa, siloh, siloo
UnitsCommonNames: UnitsCommonNames:
Mcv: mcva,mcvh,mcvo Mcv: mcva,mcvh,mcvo
@@ -173,9 +178,9 @@ Player:
rockettowera: 10% rockettowera: 10%
rockettowerh: 10% rockettowerh: 10%
rockettowero: 10% rockettowero: 10%
powra: 12% pwra: 12%
powrh: 12% pwrh: 12%
powro: 12% pwro: 12%
UnitsToBuild: UnitsToBuild:
rifle: 2% rifle: 2%
bazooka: 2% bazooka: 2%
@@ -202,6 +207,7 @@ Player:
combath: 100% combath: 100%
combato: 100% combato: 100%
SquadSize: 6 SquadSize: 6
MaxBaseRadius: 40
HackyAI@Gladius: HackyAI@Gladius:
Name: Gladius Name: Gladius
UnitQueues: Infantry, Vehicle, Armor, Starport UnitQueues: Infantry, Vehicle, Armor, Starport
@@ -209,7 +215,9 @@ Player:
ConstructionYard: conyarda,conyardh,conyardo ConstructionYard: conyarda,conyardh,conyardo
Refinery: refa,refh,refo Refinery: refa,refh,refo
Power: pwra,pwrh,pwro Power: pwra,pwrh,pwro
Barracks: barra,barrh,barro
VehiclesFactory: lighta,lighth,lighto,heavya,heavyh,heavyo VehiclesFactory: lighta,lighth,lighto,heavya,heavyh,heavyo
Production: lighta,lighth,lighto,heavya,heavyh,heavyo,barra,barrh,barro
Silo: siloa, siloh, siloo Silo: siloa, siloh, siloo
UnitsCommonNames: UnitsCommonNames:
Mcv: mcva,mcvh,mcvo Mcv: mcva,mcvh,mcvo
@@ -271,9 +279,9 @@ Player:
palaceo: 0.1% palaceo: 0.1%
guntower: 10% guntower: 10%
rockettower: 5% rockettower: 5%
powra: 10% pwra: 10%
powrh: 10% pwrh: 10%
powro: 10% pwro: 10%
UnitsToBuild: UnitsToBuild:
rifle: 15% rifle: 15%
bazooka: 13% bazooka: 13%
@@ -300,4 +308,4 @@ Player:
combath: 100% combath: 100%
combato: 100% combato: 100%
SquadSize: 10 SquadSize: 10
MaxBaseRadius: 40

View File

@@ -7,6 +7,7 @@ Player:
Power: powr,apwr Power: powr,apwr
Barracks: barr,tent Barracks: barr,tent
VehiclesFactory: weap VehiclesFactory: weap
Production: barr,tent,weap,afld,hpad,spen,syrd
Silo: silo Silo: silo
UnitsCommonNames: UnitsCommonNames:
Mcv: mcv Mcv: mcv
@@ -29,7 +30,7 @@ Player:
barr: 1% barr: 1%
tent: 1% tent: 1%
weap: 1% weap: 1%
pbox.e1: 7% pbox: 7%
gun: 7% gun: 7%
tsla: 5% tsla: 5%
ftur: 10% ftur: 10%
@@ -59,6 +60,7 @@ Player:
Power: powr,apwr Power: powr,apwr
Barracks: barr,tent Barracks: barr,tent
VehiclesFactory: weap VehiclesFactory: weap
Production: barr,tent,weap,afld,hpad,spen,syrd
Silo: silo Silo: silo
UnitsCommonNames: UnitsCommonNames:
Mcv: mcv Mcv: mcv
@@ -87,7 +89,7 @@ Player:
spen: 1% spen: 1%
syrd: 1% syrd: 1%
afld: 4% afld: 4%
pbox.e1: 7% pbox: 7%
gun: 7% gun: 7%
ftur: 10% ftur: 10%
tsla: 5% tsla: 5%
@@ -125,6 +127,7 @@ Player:
Power: powr,apwr Power: powr,apwr
Barracks: barr,tent Barracks: barr,tent
VehiclesFactory: weap VehiclesFactory: weap
Production: barr,tent,weap,afld,hpad,spen,syrd
Silo: silo Silo: silo
UnitsCommonNames: UnitsCommonNames:
Mcv: mcv Mcv: mcv
@@ -151,7 +154,7 @@ Player:
hpad: 2% hpad: 2%
spen: 1% spen: 1%
syrd: 1% syrd: 1%
pbox.e1: 7% pbox: 7%
gun: 7% gun: 7%
ftur: 10% ftur: 10%
tsla: 5% tsla: 5%
@@ -188,6 +191,9 @@ Player:
ConstructionYard: fact ConstructionYard: fact
Refinery: proc Refinery: proc
Power: powr,apwr Power: powr,apwr
Barracks: barr,tent
VehiclesFactory: weap
Production: barr,tent,weap,afld,hpad,spen,syrd
Silo: silo Silo: silo
UnitsCommonNames: UnitsCommonNames:
Mcv: mcv Mcv: mcv
@@ -213,7 +219,7 @@ Player:
stek: 1% stek: 1%
spen: 1% spen: 1%
syrd: 1% syrd: 1%
pbox.e1: 12% pbox: 12%
gun: 12% gun: 12%
ftur: 12% ftur: 12%
tsla: 12% tsla: 12%