AI Support Power Meta Data implemented.

Changes included:
Decision can be defined for the AI support powers, governing their targeting.
Each decision can be made up of multiple considerations.
This commit is contained in:
UberWaffe
2014-07-09 18:48:52 +02:00
parent 60a3196bb2
commit a1e51b71d6
10 changed files with 778 additions and 38 deletions

View File

@@ -23,14 +23,25 @@ namespace OpenRA.Mods.RA.AI
{
public sealed class HackyAIInfo : IBotInfo, ITraitInfo
{
[Desc("Ingame name this bot uses.")]
public readonly string Name = "Unnamed Bot";
[Desc("Minimum number of units AI must have before attacking.")]
public readonly int SquadSize = 8;
[Desc("Production queues AI uses for buildings.")]
public readonly string[] BuildingQueues = { "Building" };
[Desc("Production queues AI uses for defenses.")]
public readonly string[] DefenseQueues = { "Defense" };
[Desc("Delay (in ticks) between giving out orders to units.")]
public readonly int AssignRolesInterval = 20;
[Desc("Delay (in ticks) between attempting rush attacks.")]
public readonly int RushInterval = 600;
[Desc("Delay (in ticks) between updating squads.")]
public readonly int AttackForceInterval = 30;
[Desc("How long to wait (in ticks) between structure production checks when there is no active production.")]
@@ -63,26 +74,39 @@ namespace OpenRA.Mods.RA.AI
[Desc("Radius in cells around the center of the base to expand.")]
public readonly int MaxBaseRadius = 20;
[Desc("Production queues AI uses for producing units.")]
public readonly string[] UnitQueues = { "Vehicle", "Infantry", "Plane", "Ship", "Aircraft" };
[Desc("Should the AI repair its buildings if damaged?")]
public readonly bool ShouldRepairBuildings = true;
string IBotInfo.Name { get { return this.Name; } }
[Desc("What units to the AI should build.", "What % of the total army must be this type of unit.")]
[FieldLoader.LoadUsing("LoadUnits")]
public readonly Dictionary<string, float> UnitsToBuild = null;
[Desc("What buildings to the AI should build.", "What % of the total base must be this type of building.")]
[FieldLoader.LoadUsing("LoadBuildings")]
public readonly Dictionary<string, float> BuildingFractions = null;
[Desc("Tells the AI what unit types fall under the same common name.")]
[FieldLoader.LoadUsing("LoadUnitsCommonNames")]
public readonly Dictionary<string, string[]> UnitsCommonNames = null;
[Desc("Tells the AI what building types fall under the same common name.")]
[FieldLoader.LoadUsing("LoadBuildingsCommonNames")]
public readonly Dictionary<string, string[]> BuildingCommonNames = null;
[Desc("What buildings should the AI have max limits n.", "What is the limit of the building.")]
[FieldLoader.LoadUsing("LoadBuildingLimits")]
public readonly Dictionary<string, int> BuildingLimits = null;
// TODO Update OpenRA.Utility/Command.cs#L300 to first handle lists and also read nested ones
[Desc("Tells the AI how to use its support powers.")]
[FieldLoader.LoadUsing("LoadDecisions")]
public readonly List<SupportPowerDecision> PowerDecisions = new List<SupportPowerDecision>();
static object LoadList<T>(MiniYaml y, string field)
{
var nd = y.ToDictionary();
@@ -99,6 +123,16 @@ namespace OpenRA.Mods.RA.AI
static object LoadBuildingLimits(MiniYaml y) { return LoadList<int>(y, "BuildingLimits"); }
static object LoadDecisions(MiniYaml yaml)
{
var ret = new List<SupportPowerDecision>();
foreach (var d in yaml.Nodes)
if (d.Key.Split('@')[0] == "SupportPowerDecision")
ret.Add(new SupportPowerDecision(d.Value));
return ret;
}
public object Create(ActorInitializer init) { return new HackyAI(this, init); }
}
@@ -113,6 +147,9 @@ namespace OpenRA.Mods.RA.AI
public CPos baseCenter { get; private set; }
public Player p { get; private set; }
Dictionary<SupportPowerInstance, int> waitingPowers = new Dictionary<SupportPowerInstance, int>();
Dictionary<string, SupportPowerDecision> powerDecisions = new Dictionary<string, SupportPowerDecision>();
PowerManager playerPower;
SupportPowerManager supportPowerMngr;
PlayerResources playerResource;
@@ -142,6 +179,9 @@ namespace OpenRA.Mods.RA.AI
{
Info = info;
world = init.world;
foreach (var decision in info.PowerDecisions)
powerDecisions.Add(decision.OrderName, decision);
}
public static void BotDebug(string s, params object[] args)
@@ -695,46 +735,120 @@ namespace OpenRA.Mods.RA.AI
foreach (var kv in powers)
{
var sp = kv.Value;
if (sp.Ready)
{
var attackLocation = FindAttackLocationToSupportPower(5);
if (attackLocation == null)
return;
// Add power to dictionary if not in delay dictionary yet
if (!waitingPowers.ContainsKey(sp))
waitingPowers.Add(sp, 0);
if (waitingPowers[sp] > 0)
waitingPowers[sp]--;
// If we have recently tried and failed to find a use location for a power, then do not try again until later
var isDelayed = (waitingPowers[sp] > 0);
if (sp.Ready && !isDelayed && powerDecisions.ContainsKey(sp.Info.OrderName))
{
var powerDecision = powerDecisions[sp.Info.OrderName];
if (powerDecision == null)
{
BotDebug("Bot Bug: FindAttackLocationToSupportPower, couldn't find powerDecision for {0}", sp.Info.OrderName);
continue;
}
var attackLocation = FindCoarseAttackLocationToSupportPower(sp);
if (attackLocation == null)
{
BotDebug("AI: {1} can't find suitable coarse attack location for support power {0}. Delaying rescan.", sp.Info.OrderName, p.PlayerName);
waitingPowers[sp] += powerDecision.GetNextScanTime(this);
continue;
}
// Found a target location, check for precise target
attackLocation = FindFineAttackLocationToSupportPower(sp, (CPos)attackLocation);
if (attackLocation == null)
{
BotDebug("AI: {1} can't find suitable final attack location for support power {0}. Delaying rescan.", sp.Info.OrderName, p.PlayerName);
waitingPowers[sp] += powerDecision.GetNextScanTime(this);
continue;
}
// Valid target found, delay by a few ticks to avoid rescanning before power fires via order
BotDebug("AI: {2} found new target location {0} for support power {1}.", attackLocation, sp.Info.OrderName, p.PlayerName);
waitingPowers[sp] += 10;
world.IssueOrder(new Order(sp.Info.OrderName, supportPowerMngr.self, false) { TargetLocation = attackLocation.Value, SuppressVisualFeedback = true });
}
}
}
CPos? FindAttackLocationToSupportPower(int radiusOfPower)
///<summary>Scans the map in chunks, evaluating all actors in each.</summary>
CPos? FindCoarseAttackLocationToSupportPower(SupportPowerInstance readyPower)
{
CPos? resLoc = null;
var countUnits = 0;
var x = (world.Map.MapSize.X % radiusOfPower) == 0 ? world.Map.MapSize.X : world.Map.MapSize.X + radiusOfPower;
var y = (world.Map.MapSize.Y % radiusOfPower) == 0 ? world.Map.MapSize.Y : world.Map.MapSize.Y + radiusOfPower;
for (var i = 0; i < x; i += radiusOfPower * 2)
CPos? bestLocation = null;
var bestAttractiveness = 0;
var powerDecision = powerDecisions[readyPower.Info.OrderName];
if (powerDecision == null)
{
for (var j = 0; j < y; j += radiusOfPower * 2)
{
var pos = world.Map.CenterOfCell(new CPos(i, j));
var targets = world.FindActorsInCircle(pos, WRange.FromCells(radiusOfPower)).ToList();
var enemies = targets.Where(unit => p.Stances[unit.Owner] == Stance.Enemy).ToList();
var ally = targets.Where(unit => p.Stances[unit.Owner] == Stance.Ally || unit.Owner == p).ToList();
BotDebug("Bot Bug: FindAttackLocationToSupportPower, couldn't find powerDecision for {0}", readyPower.Info.OrderName);
return null;
}
if (enemies.Count < ally.Count || !enemies.Any())
var checkRadius = powerDecision.CoarseScanRadius;
for (var i = 0; i < world.Map.MapSize.X; i += checkRadius)
{
for (var j = 0; j < world.Map.MapSize.Y; j += checkRadius)
{
var consideredAttractiveness = 0;
var tl = world.Map.CenterOfCell(new CPos(i, j));
var br = world.Map.CenterOfCell(new CPos(i + checkRadius, j + checkRadius));
var targets = world.ActorMap.ActorsInBox(tl, br);
consideredAttractiveness = powerDecision.GetAttractiveness(targets, p);
if (consideredAttractiveness <= bestAttractiveness || consideredAttractiveness < powerDecision.MinimumAttractiveness)
continue;
if (enemies.Count > countUnits)
{
countUnits = enemies.Count;
resLoc = enemies.Random(random).Location;
}
bestAttractiveness = consideredAttractiveness;
bestLocation = new CPos(i, j);
}
}
return resLoc;
return bestLocation;
}
///<summary>Detail scans an area, evaluating positions.</summary>
CPos? FindFineAttackLocationToSupportPower(SupportPowerInstance readyPower, CPos checkPos, int extendedRange = 1)
{
CPos? bestLocation = null;
var bestAttractiveness = 0;
var powerDecision = powerDecisions[readyPower.Info.OrderName];
if (powerDecision == null)
{
BotDebug("Bot Bug: FindAttackLocationToSupportPower, couldn't find powerDecision for {0}", readyPower.Info.OrderName);
return null;
}
var checkRadius = powerDecision.CoarseScanRadius;
var fineCheck = powerDecision.FineScanRadius;
for (var i = (0 - extendedRange); i <= (checkRadius + extendedRange); i += fineCheck)
{
var x = checkPos.X + i;
for (var j = (0 - extendedRange); j <= (checkRadius + extendedRange); j += fineCheck)
{
var y = checkPos.Y + j;
var pos = world.Map.CenterOfCell(new CPos(x, y));
var consideredAttractiveness = 0;
consideredAttractiveness += powerDecision.GetAttractiveness(pos, p);
if (consideredAttractiveness <= bestAttractiveness || consideredAttractiveness < powerDecision.MinimumAttractiveness)
continue;
bestAttractiveness = consideredAttractiveness;
bestLocation = new CPos(x, y);
}
}
return bestLocation;
}
internal IEnumerable<ProductionQueue> FindQueues(string category)

View File

@@ -0,0 +1,155 @@
#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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenRA;
using OpenRA.Mods.RA.AI;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
[Desc("Adds metadata for the AI bots.")]
public class SupportPowerDecision
{
[Desc("What is the minimum attractiveness we will use this power for?")]
public readonly int MinimumAttractiveness = 1;
[Desc("What support power does this decision apply to?")]
public readonly string OrderName = "AirstrikePowerInfoOrder";
[Desc("What is the coarse scan radius of this power?","For finding the general target area, before doing a detail scan","Should be 10 or more to avoid lag")]
public readonly int CoarseScanRadius = 20;
[Desc("What is the fine scan radius of this power?", "For doing a detailed scan in the general target area.", "Minimum is 1")]
public readonly int FineScanRadius = 2;
[FieldLoader.LoadUsing("LoadConsiderations")]
[Desc("The decisions associated with this power")]
readonly List<Consideration> Considerations = new List<Consideration>();
[Desc("Minimum ticks to wait until next Decision scan attempt.")]
public readonly int MinimumScanTimeInterval = 250;
[Desc("Maximum ticks to wait until next Decision scan attempt.")]
public readonly int MaximumScanTimeInterval = 262;
public SupportPowerDecision(MiniYaml yaml)
{
FieldLoader.Load(this, yaml);
}
static object LoadConsiderations(MiniYaml yaml)
{
var ret = new List<Consideration>();
foreach (var d in yaml.Nodes)
if (d.Key.Split('@')[0] == "Consideration")
ret.Add(new Consideration(d.Value));
return ret;
}
///<summary>Evaluates the attractiveness of a position according to all considerations</summary>
public int GetAttractiveness(WPos pos, Player firedBy)
{
var answer = 0;
var world = firedBy.World;
var targetTile = world.Map.CellContaining(pos);
if (!world.Map.Contains(targetTile))
return 0;
foreach (var consideration in Considerations)
{
var radiusToUse = new WRange(consideration.CheckRadius.Range);
var checkActors = world.FindActorsInCircle(pos, radiusToUse);
foreach (var scrutinized in checkActors)
answer += consideration.GetAttractiveness(scrutinized, firedBy.Stances[scrutinized.Owner]);
}
return answer;
}
///<summary>Evaluates the attractiveness of a group of actors according to all considerations</summary>
public int GetAttractiveness(IEnumerable<Actor> actors, Player firedBy)
{
var answer = 0;
foreach (var consideration in Considerations)
foreach (var scrutinized in actors)
answer += consideration.GetAttractiveness(scrutinized, firedBy.Stances[scrutinized.Owner]);
return answer;
}
public int GetNextScanTime(HackyAI ai) { return ai.random.Next(MinimumScanTimeInterval, MaximumScanTimeInterval); }
///<summary>Makes up part of a decision, describing how to evaluate a target.</summary>
class Consideration
{
public enum DecisionMetric { Health, Value, None };
[Desc("Against whom should this power be used?", "Allowed keywords: Ally, Neutral, Enemy")]
public readonly Stance Against = Stance.Enemy;
[Desc("What types should the desired targets of this power be?")]
public readonly string[] Types = { "Air", "Ground", "Water" };
[Desc("How attractive are these types of targets?")]
public readonly int Attractiveness = 100;
[Desc("Weight the target attractiveness by this property", "Allowed keywords: Health, Value, None")]
public readonly DecisionMetric TargetMetric = DecisionMetric.None;
[Desc("What is the check radius of this decision?")]
public readonly WRange CheckRadius = WRange.FromCells(5);
public Consideration(MiniYaml yaml)
{
FieldLoader.Load(this, yaml);
}
///<summary>Evaluates a single actor according to the rules defined in this consideration</summary>
public int GetAttractiveness(Actor a, Stance stance)
{
if (stance != Against)
return 0;
if (a == null)
return 0;
var targetable = a.TraitOrDefault<ITargetable>();
if (targetable == null)
return 0;
if (Types.Intersect(targetable.TargetTypes).Any())
{
switch (TargetMetric)
{
case DecisionMetric.Value:
var valueInfo = a.Info.Traits.GetOrDefault<ValuedInfo>();
return (valueInfo != null) ? valueInfo.Cost * Attractiveness : 0;
case DecisionMetric.Health:
var health = a.TraitOrDefault<Health>();
return (health != null) ? (health.HP / health.MaxHP) * Attractiveness : 0;
default:
return Attractiveness;
}
}
return 0;
}
}
}
}

View File

@@ -117,6 +117,7 @@
<Compile Include="Air\Aircraft.cs" />
<Compile Include="Air\AttackHeli.cs" />
<Compile Include="Air\AttackPlane.cs" />
<Compile Include="AI\SupportPowerDecision.cs" />
<Compile Include="Crates\DuplicateUnitCrateAction.cs" />
<Compile Include="Power.cs" />
<Compile Include="Effects\Beacon.cs" />

View File

@@ -65,6 +65,58 @@ Player:
heli: 5%
orca: 5%
SquadSize: 15
SupportPowerDecision@Airstrike:
OrderName: AirstrikePowerInfoOrder
MinimumAttractiveness: 2000
Consideration@1:
Against: Enemy
Types: Vehicle, Infantry
Attractiveness: 1
TargetMetric: Value
CheckRadius: 2c0
Consideration@2:
Against: Ally
Types: Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 2c0
SupportPowerDecision@IonCannonPower:
OrderName: IonCannonPowerInfoOrder
MinimumAttractiveness: 1000
FineScanRadius: 2
Consideration@1:
Against: Enemy
Types: Air, Tank, Vehicle, Infantry, Water
Attractiveness: 2
TargetMetric: Value
CheckRadius: 2c0
Consideration@2:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: Value
CheckRadius: 2c0
Consideration@3:
Against: Ally
Types: Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 3c0
SupportPowerDecision@NukePower:
OrderName: NukePowerInfoOrder
MinimumAttractiveness: 3000
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: Value
CheckRadius: 5c0
Consideration@2:
Against: Ally
Types: Air, Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 7c0
HackyAI@Watson:
Name: Watson
BuildingCommonNames:
@@ -131,6 +183,58 @@ Player:
jeep: 20%
mtnk: 50%
SquadSize: 15
SupportPowerDecision@Airstrike:
OrderName: AirstrikePowerInfoOrder
MinimumAttractiveness: 2000
Consideration@1:
Against: Enemy
Types: Vehicle, Infantry
Attractiveness: 1
TargetMetric: Value
CheckRadius: 2c0
Consideration@2:
Against: Ally
Types: Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 2c0
SupportPowerDecision@IonCannonPower:
OrderName: IonCannonPowerInfoOrder
MinimumAttractiveness: 1000
FineScanRadius: 2
Consideration@1:
Against: Enemy
Types: Air, Tank, Vehicle, Infantry, Water
Attractiveness: 2
TargetMetric: Value
CheckRadius: 2c0
Consideration@2:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: Value
CheckRadius: 2c0
Consideration@3:
Against: Ally
Types: Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 3c0
SupportPowerDecision@NukePower:
OrderName: NukePowerInfoOrder
MinimumAttractiveness: 3000
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: Value
CheckRadius: 5c0
Consideration@2:
Against: Ally
Types: Air, Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 7c0
HackyAI@HAL9001:
Name: HAL 9001
BuildingCommonNames:
@@ -199,3 +303,55 @@ Player:
htnk: 50%
orca: 10%
SquadSize: 8
SupportPowerDecision@Airstrike:
OrderName: AirstrikePowerInfoOrder
MinimumAttractiveness: 2000
Consideration@1:
Against: Enemy
Types: Vehicle, Infantry
Attractiveness: 1
TargetMetric: Value
CheckRadius: 2c0
Consideration@2:
Against: Ally
Types: Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 2c0
SupportPowerDecision@IonCannonPower:
OrderName: IonCannonPowerInfoOrder
MinimumAttractiveness: 1000
FineScanRadius: 2
Consideration@1:
Against: Enemy
Types: Air, Tank, Vehicle, Infantry, Water
Attractiveness: 2
TargetMetric: Value
CheckRadius: 2c0
Consideration@2:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: Value
CheckRadius: 2c0
Consideration@3:
Against: Ally
Types: Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 3c0
SupportPowerDecision@NukePower:
OrderName: NukePowerInfoOrder
MinimumAttractiveness: 3000
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: Value
CheckRadius: 5c0
Consideration@2:
Against: Ally
Types: Air, Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 7c0

View File

@@ -324,7 +324,7 @@
Selectable:
Priority: 3
TargetableBuilding:
TargetTypes: Ground, C4
TargetTypes: Ground, C4, Structure
Armor:
Type: Wood
Building:

View File

@@ -107,6 +107,57 @@ Player:
combato: 100%
SquadSize: 8
MaxBaseRadius: 40
SupportPowerDecision@Airstrike:
OrderName: AirstrikePowerInfoOrder
MinimumAttractiveness: 2000
Consideration@1:
Against: Enemy
Types: Vehicle, Tank, Infantry
Attractiveness: 2
TargetMetric: Value
CheckRadius: 3c0
Consideration@2:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: Value
CheckRadius: 2c0
Consideration@3:
Against: Ally
Types: Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 4c0
SupportPowerDecision@Paratroopers:
OrderName: ParatroopersPowerInfoOrder
MinimumAttractiveness: 5
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: None
CheckRadius: 10c0
Consideration@2:
Against: Enemy
Types: Vehicle, Tank, Infantry
Attractiveness: -5
TargetMetric: None
CheckRadius: 4c0
SupportPowerDecision@NukePower:
OrderName: NukePowerInfoOrder
MinimumAttractiveness: 3000
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: Value
CheckRadius: 5c0
Consideration@2:
Against: Ally
Types: Air, Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 7c0
HackyAI@Vidius:
Name: Vidious
UnitQueues: Infantry, Vehicle, Armor, Starport
@@ -215,6 +266,57 @@ Player:
combato: 100%
SquadSize: 6
MaxBaseRadius: 40
SupportPowerDecision@Airstrike:
OrderName: AirstrikePowerInfoOrder
MinimumAttractiveness: 2000
Consideration@1:
Against: Enemy
Types: Vehicle, Tank, Infantry
Attractiveness: 2
TargetMetric: Value
CheckRadius: 3c0
Consideration@2:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: Value
CheckRadius: 2c0
Consideration@3:
Against: Ally
Types: Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 4c0
SupportPowerDecision@Paratroopers:
OrderName: ParatroopersPowerInfoOrder
MinimumAttractiveness: 5
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: None
CheckRadius: 10c0
Consideration@2:
Against: Enemy
Types: Vehicle, Tank, Infantry
Attractiveness: -5
TargetMetric: None
CheckRadius: 4c0
SupportPowerDecision@NukePower:
OrderName: NukePowerInfoOrder
MinimumAttractiveness: 3000
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: Value
CheckRadius: 5c0
Consideration@2:
Against: Ally
Types: Air, Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 7c0
HackyAI@Gladius:
Name: Gladius
UnitQueues: Infantry, Vehicle, Armor, Starport
@@ -323,3 +425,54 @@ Player:
combato: 100%
SquadSize: 10
MaxBaseRadius: 40
SupportPowerDecision@Airstrike:
OrderName: AirstrikePowerInfoOrder
MinimumAttractiveness: 2000
Consideration@1:
Against: Enemy
Types: Vehicle, Tank, Infantry
Attractiveness: 2
TargetMetric: Value
CheckRadius: 3c0
Consideration@2:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: Value
CheckRadius: 2c0
Consideration@3:
Against: Ally
Types: Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 4c0
SupportPowerDecision@Paratroopers:
OrderName: ParatroopersPowerInfoOrder
MinimumAttractiveness: 5
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: None
CheckRadius: 10c0
Consideration@2:
Against: Enemy
Types: Vehicle, Tank, Infantry
Attractiveness: -5
TargetMetric: None
CheckRadius: 4c0
SupportPowerDecision@NukePower:
OrderName: NukePowerInfoOrder
MinimumAttractiveness: 3000
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: Value
CheckRadius: 5c0
Consideration@2:
Against: Ally
Types: Air, Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 7c0

View File

@@ -237,7 +237,7 @@
Selectable:
Priority: 2
TargetableBuilding:
TargetTypes: Ground, C4
TargetTypes: Ground, C4, Structure
Building:
Dimensions: 1,1
Footprint: x

View File

@@ -52,6 +52,45 @@ Player:
2tnk: 25%
3tnk: 50%
SquadSize: 20
SupportPowerDecision@Airstrike:
OrderName: AirstrikePowerInfoOrder
MinimumAttractiveness: 1
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: None
CheckRadius: 5c0
SupportPowerDecision@Paratroopers:
OrderName: ParatroopersPowerInfoOrder
MinimumAttractiveness: 5
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: None
CheckRadius: 10c0
Consideration@2:
Against: Enemy
Types: Vehicle, Tank, Infantry, Defense
Attractiveness: -10
TargetMetric: None
CheckRadius: 10c0
SupportPowerDecision@NukePower:
OrderName: NukePowerInfoOrder
MinimumAttractiveness: 3000
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: Value
CheckRadius: 5c0
Consideration@2:
Against: Ally
Types: Air, Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 7c0
HackyAI@NormalAI:
Name: Normal AI
BuildingCommonNames:
@@ -119,6 +158,45 @@ Player:
ca: 10%
pt: 10%
SquadSize: 40
SupportPowerDecision@Airstrike:
OrderName: AirstrikePowerInfoOrder
MinimumAttractiveness: 1
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: None
CheckRadius: 5c0
SupportPowerDecision@Paratroopers:
OrderName: ParatroopersPowerInfoOrder
MinimumAttractiveness: 5
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: None
CheckRadius: 10c0
Consideration@2:
Against: Enemy
Types: Vehicle, Tank, Infantry, Defense
Attractiveness: -10
TargetMetric: None
CheckRadius: 10c0
SupportPowerDecision@NukePower:
OrderName: NukePowerInfoOrder
MinimumAttractiveness: 3000
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: Value
CheckRadius: 5c0
Consideration@2:
Against: Ally
Types: Air, Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 7c0
HackyAI@TurtleAI:
Name: Turtle AI
BuildingCommonNames:
@@ -185,6 +263,45 @@ Player:
ca: 10%
pt: 10%
SquadSize: 10
SupportPowerDecision@Airstrike:
OrderName: AirstrikePowerInfoOrder
MinimumAttractiveness: 1
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: None
CheckRadius: 5c0
SupportPowerDecision@Paratroopers:
OrderName: ParatroopersPowerInfoOrder
MinimumAttractiveness: 5
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: None
CheckRadius: 10c0
Consideration@2:
Against: Enemy
Types: Vehicle, Tank, Infantry, Defense
Attractiveness: -10
TargetMetric: None
CheckRadius: 10c0
SupportPowerDecision@NukePower:
OrderName: NukePowerInfoOrder
MinimumAttractiveness: 3000
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: Value
CheckRadius: 5c0
Consideration@2:
Against: Ally
Types: Air, Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 7c0
HackyAI@NavalAI:
Name: Naval AI
BuildingCommonNames:
@@ -238,4 +355,43 @@ Player:
ca: 20%
pt: 10%
SquadSize: 1
SupportPowerDecision@Airstrike:
OrderName: AirstrikePowerInfoOrder
MinimumAttractiveness: 1
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: None
CheckRadius: 5c0
SupportPowerDecision@Paratroopers:
OrderName: ParatroopersPowerInfoOrder
MinimumAttractiveness: 5
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: None
CheckRadius: 10c0
Consideration@2:
Against: Enemy
Types: Vehicle, Tank, Infantry, Defense
Attractiveness: -10
TargetMetric: None
CheckRadius: 10c0
SupportPowerDecision@NukePower:
OrderName: NukePowerInfoOrder
MinimumAttractiveness: 3000
Consideration@1:
Against: Enemy
Types: Structure
Attractiveness: 1
TargetMetric: Value
CheckRadius: 5c0
Consideration@2:
Against: Ally
Types: Air, Ground, Water
Attractiveness: -10
TargetMetric: Value
CheckRadius: 7c0

View File

@@ -15,7 +15,7 @@
Selectable:
Voice: VehicleVoice
TargetableUnit:
TargetTypes: Ground, C4, Repair
TargetTypes: Ground, C4, Repair, Vehicle
Repairable:
Chronoshiftable:
Passenger:
@@ -73,7 +73,7 @@
Selectable:
Voice: VehicleVoice
TargetableUnit:
TargetTypes: Ground, C4, Repair
TargetTypes: Ground, C4, Repair, Tank
Repairable:
Chronoshiftable:
Passenger:
@@ -268,7 +268,7 @@
Selectable:
Priority: 3
TargetableBuilding:
TargetTypes: Ground, C4, DetonateAttack
TargetTypes: Ground, C4, DetonateAttack, Structure
Building:
Dimensions: 1,1
Footprint: x
@@ -310,6 +310,11 @@
LuaScriptEvents:
Demolishable:
ScriptTriggers:
^Defense:
Inherits: ^Building
TargetableBuilding:
TargetTypes: Ground, C4, DetonateAttack, Structure, Defense
^Wall:
AppearsOnRadar:

View File

@@ -276,7 +276,7 @@ PDOX:
Amount: -200
TSLA:
Inherits: ^Building
Inherits: ^Defense
Buildable:
Queue: Defense
BuildPaletteOrder: 80
@@ -319,7 +319,7 @@ TSLA:
Amount: -150
AGUN:
Inherits: ^Building
Inherits: ^Defense
Buildable:
Queue: Defense
BuildPaletteOrder: 90
@@ -400,7 +400,7 @@ DOME:
Amount: -40
PBOX:
Inherits: ^Building
Inherits: ^Defense
Tooltip:
Name: Pillbox
Description: Static defense with a fireport for a\ngarrisoned soldier.
@@ -445,7 +445,7 @@ PBOX:
Amount: -15
HBOX:
Inherits: ^Building
Inherits: ^Defense
Tooltip:
Name: Camo Pillbox
Description: Camouflaged static defense with a fireport\nfor a garrisoned soldier.
@@ -493,7 +493,7 @@ HBOX:
Amount: -15
GUN:
Inherits: ^Building
Inherits: ^Defense
Buildable:
Queue: Defense
BuildPaletteOrder: 70
@@ -533,7 +533,7 @@ GUN:
Amount: -40
FTUR:
Inherits: ^Building
Inherits: ^Defense
Buildable:
Queue: Defense
BuildPaletteOrder: 60
@@ -571,7 +571,7 @@ FTUR:
Amount: -20
SAM:
Inherits: ^Building
Inherits: ^Defense
Buildable:
Queue: Defense
BuildPaletteOrder: 100