AI的艺♂术 - 2019年1月版AI教程
by Madman_M

前言 关于AI的一些常识
在折腾AI之前，首先要明确一点——Ra2这个游戏的AI非常残障，因此某种意义上来讲AI必须通过一定程度的作弊来才能与玩家抗衡……而写AI的目的，一方面是尽量让AI显得不那么残障，另一方面是限制AI的作弊程度，让AI的作弊变得不那么明显。

目录
第一章	AI的设计思路
第二章	Rules(MD)
第三章	单位代码
第四章	AI(MD)
第五章	地图相关
PLUS	Ares平台下的AI骚操作



第一章 AI的设计思路
1.什么样的AI才是好的AI？
无脑暴兵摁死玩家的AI是屑，换着花样儿慢慢“折磨”玩家的AI才是AI之鉴。
而折磨玩家的目的也不在于摁死玩家，而是让玩家在反折磨和反推AI之后获得成就感。
当然，一堆AI三下五除二灭掉一个玩家还是合情合理的……
2.AI应该怎样折磨玩家？
(1)战术的多样化：AI可以玩骚扰，而且玩起来还挺6。只要给AI加上抢中立科技、工程师偷家、间谍断电偷钱之类的骚扰战术，玩家在与AI正面对抗的同时，就需要随时留意后方。这样一来处理难度会有很大提升。
(2)攻击形式的多样化：在调整了全局的条件下，AI的最大优势就是不差钱，不差钱带来的优势就是可以随时改变战术。在一波地面攻势之后，AI可以毫无压力地再出动一波空中攻势，而玩家此时很可能并没有做好防空的准备，应付起来就会手忙脚乱。
(3)兵种搭配的多样化：与(2)不同，兵种搭配是在一个部队内进行的，如反装甲单位和反步兵单位搭配、远程单位和近战单位搭配、地面单位和空中单位搭配，都可以起到很好的效果。如果有不同阵营的AI，还有可能出现几支需要玩家使用不同手段分别处理的部队同时杀向玩家的奇景，会给玩家造成很大压力。
(4)适当暴兵：就像玩家一样，暴兵也是AI不可或缺的战术。但是暴兵时需要控制数量。一方面是要让玩家能够处理，另一方面也要考虑AI的寻路能力。
3.怎样才能做出优秀的AI？
首先合理的全局设定是AI执行的前提。其次优秀的AI通常【应该】掌握一些基本操作：
(1)工程师占中立或偷家。
(2)Rush和守家。
(3)间谍和反间谍。
(4)运输。
(5)扰矿。
(6)反特定建筑(如防御建筑和超武)。
(7)巷战和反巷战。
(8)海军。
(9)空袭。
总而言之，就是把自己能想到战术赋予AI，给AI制定一个具有多种可能性的剧本，并且通过代码让AI按照剧本发展，从而让AI表现得更像是有策略地进攻而不只是机械地出兵，是AI设计应该考虑的主要内容。



第二章 Rules(MD)
一套完整的AI，从来都不是只靠ai(md).ini一个文件来实现的。而是Rules设定、ai(md).ini以及单位代码共同作用的结果。
Rules对AI表现的影响非常大，会直接影响AI的出兵效率和发展水平。

以下是Rules中部分对AI产生影响的内容解释:
(注释: num-数字、X,Y,Z-三个数字、bool-yes/no、list-列表)

[General]
; computer and movement controls
BaseBias=num ;当敌人靠近AI时，AI对敌人威胁的判定倍数，会影响AI对单位威胁值的敏锐度。可以调高。
BaseDefenseDelay=num ;AI派出防御部队处理基地威胁的延迟时间。(单位:分钟)
CloseEnough=num ;AI执行"移动到"指令时，距离目的地多近就视为完成接近。
Stray=num ;一个小队在执行任务时，偏离这个距离之内时不会导致小队重组。可以适当增加以便AI可以使用大部队。
RelaxedStray=num ;执行"集结"指令(应该是指53,54号指令)时，以这个数值替代Stray。可以适当增加以便AI可以使用大部队。

TeamDelays=X,Y,Z ;(困难、中等、简单)的AI创造部队时间间隔。(单位:帧)

AIHateDelays=X,Y,Z ;(困难、中等、简单)的AI选择敌人的时间间隔。(单位:帧)
AIAlternateProductionCreditCutoff=num ;AI资金低于这个数值就不再花钱。

MultiplayerAICM=X,Y,Z ;(困难、中等、简单)的AI开局时的金钱【百分比】。公式为：AI开局金钱=玩家开局金钱*(X+100)/100。
AIVirtualPurifiers=X,Y,Z ;(困难、中等、简单)的AI获取资金的倍数。
AISlaveMinerNumber=X,Y,Z ;(困难、中等、简单)的AI的奴隶矿车数。
HarvestersPerRefinery=X,Y,Z ;三个阵营每个矿场的矿车数。
AIExtraRefineries=X,Y,Z ;(困难、中等、简单)的AI追加的矿场数。(Ares里的建议不给矿场加AIBuildCounts，如果需要增加矿场数量的话用AIExtraCounts)
FillEarliestTeamProbability=X,Y,Z ;(困难、中等、简单)的AI制造第一个部队的时间。(单位:帧)

MinimumAIDefensiveTeams=X,Y,Z ;(困难、中等、简单)的AI最少守家部队(IsBaseDefense=yes)数量。
MaximumAIDefensiveTeams=X,Y,Z ;(困难、中等、简单)的AI最多守家部队数量。(Min和Max即使都写0,0,0也会至少造1队守家部队)
UseMinDefenseRule=bool ;AI在制造了满足MinimumAIDefensiveTeams数量的守家部队之后才开始制造其他小队。
;UseMinDefenseRule=无论写啥AI都会至少先造1个守家部队，但是写no的话AI不会因为没有可造的守家部队而傻掉。所以如果UseMinDefenseRule=yes，要保证每个阵营【至少有一个】(不是只有一个!)可用的IsBaseDefense=yes的小队，否则AI不会出兵。

TotalAITeamCap=X,Y,Z ;(困难、中等、简单)的AI可以同时使用的部队总数。
DissolveUnfilledTeamDelay=num ;AI在多久后会解散一个不完整的部队。理论上不应降低，否则AI制造成员较多的部队时会在制造完毕之前就解散该队伍。(单位:帧)

;*** New AI type snippets ***
AISafeDistance=num ;AI执行53号指令时的集结点与敌方基地的距离。
(Ares)AIFriendlyDistance=num ;AI执行54号指令时的集结点与己方基地的距离。默认=AISafeDistance

AIMinorSuperReadyPercent=num ;AI使用次级超武的几率

AlliedBaseDefenseCounts=X,Y,Z
SovietBaseDefenseCounts=X,Y,Z
ThirdBaseDefenseCounts=X,Y,Z
三个阵营在(困难、中等、简单)时的防御建筑数量系数。计算公式:((已有建筑的价格-2000)/1500 * GDIBaseDefenseCoefficient) + 3*(该系数-1)

AIPickWallDefensePercent=X,Y,Z ;(困难、中等、简单)的AI选择围墙来替代防御建筑的概率。
AIRestrictReplaceTime=num ;AI建筑被摧毁后，用电厂/防御建筑/围墙替代被摧毁建筑的时间。(单位:帧)
ThreatPerOccupant=num ;每个驻军对可进驻建筑威胁值的提升。

GuardAreaTargetingDelay=num ;区域警戒(Area Guard)状态下AI搜索目标的延迟。
NormalTargetingDelay=num ;闲置状态下AI搜索目标的延迟。
DisabledDisguiseDetectionPercent=X,Y,Z ;(困难、中等、简单)的AI识破幻影的概率。

AISuperDefenseProbability=X,Y,Z ;(困难、中等、简单)的AI用护盾防御超武的概率。
AISuperDefenseFrames=num ;AI用护盾防御超武的延迟。(单位:帧)
AISuperDefenseDistance=num ;基地的多少距离内是AI考虑使用护盾的范围。

; AI trigger weighting parameters
AITriggerSuccessWeightDelta=num ;触发成功权重变化值(可以写负值)。
AITriggerFailureWeightDelta=num ;触发失败权重变化值(通常是负值)。
AITriggerTrackRecordCoefficient=num ;触发权重变化系数(建议写1)。
;负责[AITriggerTypes]中权重变化的三个数值。计算方法是每当AI的部队执行一次49号指令，就会在该AITrigger中增加AITriggerSuccessWeightDelta的权重。如果被消灭之前没能执行49号指令，则减少AITriggerFailureWeightDelta*AITriggerTrackRecordCoefficient的权重。

威胁值相关:
; default threat evaluation controls
MyEffectivenessCoefficientDefault=num ;当AI的单位与威胁等级高于自身的目标交战时，会在计算威胁时乘以这个系数。帮助AI恃强凌弱。
TargetEffectivenessCoefficientDefault=num ;当AI的单位与威胁等级高于自身的目标交战时，会在计算威胁时乘以这个系数。防止AI以卵击石。
TargetSpecialThreatCoefficientDefault=num ;当AI目标具有SpecialThreatValue时，会在计算威胁时考虑这个系数。
TargetStrengthCoefficientDefault=num ;当AI的单位将目标指向生命比自己高的单位时，会根据敌方生命百分比考虑这个系数。
TargetDistanceCoefficientDefault=num ;当AI的单位选择目标时，会根据距离考虑这个系数。
; defaults for dumb threat evaluation ;与default threat evaluation controls基本相同，但是适用于HasStupidGuardMode=yes的单位。
DumbMyEffectivenessCoefficient=num
DumbTargetEffectivenessCoefficient=num
DumbTargetSpecialThreatCoefficient=num
DumbTargetStrengthCoefficient=num
DumbTargetDistanceCoefficient=num
EnemyHouseThreatBonus=num
以上数值都不是百分数，而官方给的数值却是按百分数填的。都除以100可能会获得较为明显的效果。

附:威胁值计算公式
float Threat = Attacker's best weapon's Verses against Defender * Target Effectiveness;

if(Attacker is currently targeting Defender) { Threat = Threat * (-1);}

float tempValue = Special Threat Value * Special Threat Coefficient;
Threat = Threat + tempValue;

if(Defender is currently considering Attacker's owner house as the Enemy House) {Threat = Threat + [General]→EnemyHouseThreatBonus=;}
tempValue = Defender's best weapon's Verses against Attacker * Self Effectiveness;
Threat = Threat + tempValue;

tempValue = Target Strength * Attacker's current health percentage;
Threat = Threat + tempValue;

tempValue = Target Distance * Distance between Defender and Attacker;
Threat = Threat + tempValue;

Threat = Threat + 100000.0;

粗(JI)翻：
Threat = 攻击单位武器对目标的Versus * TargetEffectiveness;
如果进攻者已经在攻击目标，Threat=Threat * (-1)

tempValue = 单位的SpecialThreatValue * TargetSpecialThreatCoefficient;
Threat = Threat + tempValue;

如果AI作为防御方把进攻者视为敌对方，Threat = Threat + EnemyHouseThreatBonus;

tempValue = 防御方武器对进攻者的Versus * MyEffectiveness;
Threat = Threat + tempValue;

tempValue = TargetStrengthCoefficient * 攻击者当前的生命百分比;
Threat = Threat + tempValue;

tempValue = TargetDistanceCoefficient * 防御者和攻击者之间的距离;
Threat = Threat + tempValue;

Threat = Threat + 100000.0;

[CombatDamage]
Crush=num ;距离这么近时AI会尝试用碾压代替开火。写高点的话AI的车辆更容易直接碾压玩家步兵。

[AI]
BuildConst=list ;基地列表(可以不是基地)。在Ares里，决定StartInMultiplayer.WithConst=true时开局给的【建筑】。
BuildPower=list ;电力建筑列表，每个阵营只能写一个。开局必然会先造一个，因此如果有给某个阵营使用昂贵电力插件作为电力供给的情况，请把兵营或其他开局就能造的建筑注册到电力列表里。
BuildRefinery=list ;矿场列表。
BuildBarracks=list ;兵营列表。
BuildTech=list ;高科列表。
BuildWeapons=list ;工厂列表。
BuildRadar=list ;雷达列表。
ConcreteWalls=list ;围墙列表。
BuildNavalYard=list ;船坞列表。
;不弹框的最低要求是有基地(废话)、电厂，并且兵营和工厂没同时被禁(当然，这并不意味着AI真的能造，只要让AI以为自己能造就可以)。

AlliedBaseDefenses=list
SovietBaseDefenses=list
ThirdBaseDefenses=list
AIForcePredictionFudge=X,Y,Z
;(困难、中等、简单)的AI根据敌方拥有单位的造价总和，按照此处数值的百分比选择防空，反装甲或反步兵的防御建筑。(数值越小越敏感)

MaximumBaseDefenseValue=num ;AI防御建筑的威胁值总和(不包括防空建筑)。
ComputerBaseDefenseResponse=num ; 影响AI对基地和ToProtect单位的响应程度，具体效果未知。

PatrolScan=num ;AI执行巡逻任务时搜索敌人的时间。(单位:分钟)
CreditReserve=num ;低于这个资金量时AI不再进行维修。
PathDelay=num ;堵车时间隔这么长时间再次尝试通过。(单位:分钟)
BlockagePathDelay=num ;再次尝试隔这么长时间仍无法通过则绕路。(单位:帧)
AutocreateTime=num ;平均间隔多长时间建造一队具有"Autocreate=yes"标签的小队。(单位:分钟)
InfantryReserve=num ;AI拥有高于这个数值的金钱时将会一直建造步兵。
InfantryBaseMult=num ;如果AI当前建筑数量乘以此数值比步兵数量多，则一直制造步兵。
PowerSurplus=num ;AI电力超出临界不足这个数值时将会制造电厂。
BaseSizeAdd=num ;AI的基地规模不会大于玩家基地规模的这个倍数。

RefineryRatio=num
RefineryLimit=num
BarracksRatio=num
BarracksLimit=num
WarRatio=num
WarLimit=num
DefenseRatio=num
DefenseLimit=num
AARatio=num
AALimit=num
TeslaRatio=num
TeslaLimit=num
HelipadRatio=num
HelipadLimit=num
AirstripRatio=num ;由于Ra2里没有Airstrips，这个数值最好写0。
AirstripLimit=num ;同上。
;各类建筑的建造比重和数量限制，各类Ratio总和不能低于1。

Paranoid=bool ;AI私下进行PY交易共同对抗玩家。
PowerEmergency=num ;砸锅卖铁保电量的电量临界值。(%)
AIBaseSpacing=num ;AI建筑间隔距离，写0会无法正常摆建筑，稍微增大可以缓解AI堵车。

[IQ] ;一系列数字，通常结合地图中所属方的IQ设置生效。遭遇战中AI的IQ均为最大值。
MaxIQLevels=num ;IQ最大值。
SuperWeapons=num ;AI自动使用超武。
Production=num ;自动建造建筑物。
GuardArea=num ;新造出来的小队自动进入[Area Guard]状态。
RepairSell=num ;可以选择维修或变卖受损建筑。
AutoCrush=num ;自动碾压。
Scatter=num ;规避有弹道的抛射体。
ContentScan=num ;使[Easy]、[Normal]、[Difficult]项下的ContentScan生效。
Aircraft=num ;自动补充飞机。
Harvester=num ;自动补充矿车。
SellBack=num ;允许变卖建筑。
(ARES)TogglePower=num ;没电时让可断电建筑停止工作。

[Easy]、[Normal]、[Difficult] ;这仨的注册名称写！倒！了！Easy代表的是困难，Difficult代表的是简单。(然而里面的数值是正常的，WWSB!)
ContentScan=bool ;AI使用运输单位时，会将运输工具及乘客的Points和ThreatPosed都考虑进去。写no或[IQ]此项不满足的话则只考虑运输工具自身的。

; ******* Mission Control *******
;一些状态(Mission)相关参数，决定特定情况下单位的行动模式。箱子单位状态可能与踩箱子的单位一致(待验证)。
[Harvest] ;FreeUnit的默认状态。
[Hunt] ;很重要的一个状态，伞兵和空投单位通常都是这个状态。
[Enter] ;会自动找寻可进驻建筑物，可进驻的伞兵有时会是这个状态。
Recruitable=bool ;是否允许重组，写yes的话，AI的小队在执行一些特殊指令后(主要为11号指令相关)就可以接受重组并加入到其他小队中去。

Rule部分其实比AI更复杂，对AI强度的影响也更明显。赋予AI合理的数值是AI能否正常执行AI(MD)中指令的前提。

Ares新增阵营和新增国家相关：
新增阵营和新增国家，AI最低要求是至少不弹框。而让AI不弹框需要注意以下几项：
1.[AI]中指定了新阵营的基地、电厂。
2.[新国家]中用AI.PowerPlants指明了且该电厂的AIBasePlanningSide正常。



第三章 单位代码
单位代码虽然也包含在Rules里，但由于不同种类单位情况各不相同，因此单独列为一章。
本章内容较少，仅列举一些特殊语句和单位。

通用:
TechLevel=num ;AI可以建造TechLevel=-1的建筑，但是不能建造TechLevel=-1或大于最大TechLevel的单位。
的给各个可造单位安排合理的TechLevel可以让玩家的建造界面比较有层次感。(TechLevel=0可以用来调整建造界面单位顺序。)

拓展姿势:AI开局的建筑建造顺序：
经常玩无初始部队虐电1V7的小伙伴们肯定很熟悉AI的奇葩建造顺序——先憋矿场再造兵营，开局很容易被玩家工程师偷掉一两家。
电厂开局必然会先造一个。而决定兵营和矿场(目前测试结果来看也仅限兵营和矿场)建造顺序的，是兵营和矿场哪个TechLevel更小。
只要保证兵营的TechLevel小于矿场，AI就会先出兵营并且开始造守家部队，玩家速偷的战术成功率就会大大下降。

AI建造前提:
建筑:
无视:【TechLevel=-1】,PrerequisiteOverride,RequiresStolenAlliedTech,RequiresStolenSovietTech,RequiresStolenThirdTech,SpyEffect.StolenTechIndex(Ares)
有效:TechLevel,Prerequisite,Owner,AIBasePlanningSide,RequiredHouses,ForbiddenHouses
;如果建筑A的Prerequisite为建筑B且Owner/AIBasePlanningSide均正常，但是建筑B因为RequiredHouses/ForbiddenHouses无法建造，AI会认为建筑A可造，但是实际上无法建造建筑A，且不影响其他符合条件的建筑建造。
单位:
无视:Prerequisite,PrerequisiteOverride,Owner
有效:TechLevel,RequiredHouses,ForbiddenHouses,RequiresStolenAlliedTech,RequiresStolenSovietTech,RequiresStolenThirdTech,SpyEffect.StolenTechIndex(Ares)
;如果[TaskForces]里有无法建造的单位，则会跳过该项继续建造之后的单位。除非通过重组凑齐无法建造的单位，否则无法执行脚本。

ThreatPosed=num
SpecialThreatValue=num
详见Rules中的威胁值部分，给单位安排合理的威胁值会影响到AI选择目标的优先顺序，关系到AI能否选择合适的目标进行攻击。

建筑:
AIBasePlanningSide=num ;写阵营序号(0=GDI,1=Nod,2=ThirdSide，Ares新阵营依次往后排)，标明第几个阵营会建造该建筑(写-1时所有阵营都可造)。
AIBuildThis=bool ;AI是否会建造该建筑。
(Ares)AIBuildCounts=X,Y,Z ;AI至少建造几个该建筑。
(Ares)AIExtraCounts=X,Y,Z ;AI可能额外补充几个该建筑。
AI在补足AIBuildCounts和AIExtraCounts数量时不是一股脑补足的，而是随机穿插建造(虽然也有可能一连造好几个)。

另:AI所有建筑的数量都会根据其拥有的基地数量而翻倍！

单位:
ToProtect=bool ;写yes的单位受到攻击时，AI会出动守家部队去保护。原版海军都有这句，但海军加这个意义不大，除非给海军加守家部队……

专属单位:
RequiredHouses/ForbiddenHouses导致某个国家无法使用该阵营通用单位时，涉及该通用单位的[TaskForces]就需要每个国家单独设置。否则被禁用的国家在制造带有该单位时，会产生一支无法执行脚本的半成品小队。

武装直升机相关:
原版AI使用武直执行一切移动指令时，由于指令判定点为地面，而武直不会主动降落，因此会无限卡在移动指令的目标地点上空。想让AI的武直部署基本上只能在攻击指令之后衔接部署指令。
如果想让AI的武直执行移动指令，就需要给武直添加BalloonHover=yes语句。
给武直添加BalloonHover目前未发现任何负面效果，不但不影响部署落地，甚至BalloonHover自带的一定要飞目标头上拉翔的特技，在武直身上都不存在。
当然，做到这一步只能算初步解决了AI武直的移动问题和部署问题(这时的部署仅是个视觉效果)。除此之外还要面临一系列问题:
1.移动到路径点之后AI可能不会部署:经测试，AI可以正常完成移动至路径点的指令，只是无法在移动后部署。只要在移动和部署之间随便增加个容易完成的指令即可，比如5,0(非最优)。
2.部署之后立即起飞（甚至在部署状态就起飞，AI作弊实锤！）:部署之后需要让AI执行防守指令，以免AI瞎起飞。(注：不能用11,11)
3.部署之后不开炮或开炮距离过近:再给武直加个与副武器射程等同的GuardRange即可。
综上，在给武直添加BalloonHover和GuardRange之后，再结合合理的指令，即可做到让AI批量部署武直。
还想让AI部署之后重新起飞？其实也是可行的，不过意义不大。AI的武直杵在地上当炮台才是坠吼的！至于方法，在AI(MD)部分里找吧。



第四章 AI(MD)
ai(md).ini里包括4个部分：[TaskForces]、[ScriptTypes]、[TeamTypes]、[AITriggerTypes]

[TaskForces] ;决定AI的小队中包含哪些单位，建造顺序从上到下。
首先是注册表，格式跟正常注册表一样:索引=注册ID。可以跳数。
0=注册项0
1=注册项1
……
然后是每个注册项：
[注册项0]
Name=名称 ;随便写反正不读，仅供编写者记忆和查找用。
0=数量,单位注册名
1=数量,单位注册名
……
5=数量,单位注册名
Group=-1 ;无特殊需要建议保留原值-1
;索引只读取0~5(相当于两个G胖)，且无视顺序(也就是说你可以跳数写或者倒序写或者多写，反正不会数6)。

[ScriptTypes] ;决定AI的小队执行哪些指令，执行顺序从上到下。
首先也是注册表，格式跟正常注册表一样:索引=注册ID。也可以跳数。
0=注册项2
1=注册项3
……
然后是每个注册项：
[注册项2]
Name=名称 ;随便写反正不读，仅供编写者记忆和查找用。
0=X,Y ;X:指令,Y:参数
1=X,Y
2=X,Y
……
49=X,Y
;跟TaskForces索引类似，但是读取0~49。
以下是部分指令解释(标★为常用，☆为特定情况下有用):
(X,Y)
★0,n - 攻击最近的目标，目标类型由n指定。(海军不可用！用了这条语句的海军小队AI根本不会建造。)
	n =	0 - 任何目标([VehicleTypes]/[AircraftTypes]/[InfantryTypes]/[BuildingTypes])(飞机类会跳过这句。)
		1 - 同0,0
		2 - 建筑物([BuildingTypes])
		3 - 资源类(Storage=)
		4 - 步兵([InfantryTypes])
		5 - 载具([VehicleTypes])
		6 - 工厂([BuildingTypes],Factory=)
		7 - 防御建筑([BuildingTypes],IsBaseDefense=yes)
		8 - 同0,0
		9 - 电厂([BuildingTypes],BuildCat=Power)
		10 - 可驻军建筑([BuildingTypes],CanOccupyFire=yes)
		11 - 中立科技建筑([BuildingTypes],NeedsEngineer=yes)
★1,n - 攻击n号路径点，工程师/间谍/突击队都可以用这句完成占领/渗透/清理。
2,0 - Cyborg狂暴化(无效)。
☆3,n - 移动到n号路径点(Waypoint)。
☆4,n - 移动到n号单元标记(CellTag)。
☆5,n - 警戒(Guard)状态保持n*6秒。
★6,n - 跳转到当前Script的第n(n=索引+1)行。
7,0 - 钦点该Team所有者获胜。
★8,n - 释放乘客
	n =	0 - 保留运输工具和乘客执行接下来的指令。
		1 - 只保留运输工具。
		2 - 只保留乘客。
		3 - 全解散。
★9,0 - 部署。(步兵、基地车、武装直升机皆有效)
10,0 - 跟随最近的友好单位。(如果写在Script的第一行，将会无条件跟随产生的第一个小队)
★11,n - 进入第n种状态(通常能在Rules里找到并修改相应状态)，无法执行该Script内接下来的任何指令。(可以通过重组至其他小队来终止任务)
	n =	0	Sleep - 休眠。
		1	Attack - 根据威胁等级进行TeamType中的攻击任务。
		2	Move - 移动。
		3	QMove - 在其他部队移动结束后移动到目的地。(遭遇战无效)
		4	Retreat - 撤退(可能会逃出地图)。
		☆5	Guard - 防御，只攻击进入射程内的敌人。
		6	Sticky - 与防御类似，会与敌人交战但不会移动和追击。(默认无法重组，但是可以改)
		☆7	Enter - 进入建筑或运输工具。AI会自动找寻附近的建筑物进驻。
		8	Capture - 用于MultiEngineer模式的工程师抢建筑。
		9	Eaten - 修理时卖掉(疑似Ra95残留)。
		10	Harvest - 采矿。
		★11	Area Guard - 区域防御，会主动迎击附近的敌人。
		12	Return - 子机回归。
		13	Stop - 停止当前动作。
		14	Ambush - 无效。
		★15	Hunt - 猎杀敌人。(伞兵默认执行此状态。【海军、空军均有效】。默认无法重组，但是可以改)
		16	Unload - 运输单位卸货。
		17	Sabotage - 放置C4或炸药。
		18	Construction - 在建筑建立的位置造建筑。(车辆部署建筑同样有效)
		19	Selling - 变卖建筑。
		20	Repair - 修理。
		21	Rescue - 救援(意味不明)。
		22	Missile - 种蘑菇。
		23	Harmless - 进入人畜无害状态。
		24	Open - 开门开门开门呐！
		25	Patrol - 巡逻。(遭遇战无效)
		26	Paradrop Approach - 空投靠近。
		27	Paradrop Overfly - 空投。
		28	Wait - 等待。
		29	Move - (特殊)Chrono类单位移动到目的地。
		30	Attack - (特殊)使用AreaFire武器开火。
		31	Spyplane Approach - 侦察机靠近。
		32	Spyplane Overfly - 侦察机侦查。
★14,0 - 队伍中存在有Passengers位置的运输工具，并且其余单位Size和PhysicalSize满足运输条件时，乘客们进入运输工具。(实际使用时，一个队伍里只能存在1个运输工具)(运输步兵时可以在14,0之后加43,0保证单位装载完毕，运输车辆时不可接43,0)
16,n - 沿着n号路径点巡逻。巡逻途中会按照PatrolScan搜寻附近敌人并与敌人交战。通常用于任务，多了会卡……
17,n - 变为执行第n号脚本(n为该脚本在注册列表中的【顺序号】而非索引号，从0开始)。理论上可以用来实现超过50行的脚本……
18,n - 改变小队，让当前小队加入第n号小队(n为该小队在注册列表中的【顺序号】而非索引号，从0开始)。理论上可以用来实现超过6行的大队……
19,0 - 恐慌，小队中有Fraidycat标签的成员会乱跑,并且播放Panic序列，没有该标签的会卧倒。
20,n - 二五仔，任务限定。
☆21,0 - 分散部队，某些情况下有奇效。
22,0 - 躲猫猫……
23,0 - 决定该Team所有者失败。
29,0 - 任务中用于让AI自动建造。
30,0 - 卖家一波流。
31,0 - 自戕以谢天下。
41,0 - 不怕不怕，与19,0相反。
42,n - 改变朝向。
	n =	0-北
		1-东北
		2-东
		3-东南
		4-南
		5-西南
		6-西
		7-西北
43,0 - 等待运输单位完全装载完毕。运载步兵时可以用在14,0之后，运载车辆时不要使用！
★46,N - 攻击N指定建筑。(工程师为占领、间谍为渗透、突击者为清驻兵、海军可用此指令攻击陆地建筑)
	N=该建筑在注册列表中的【顺序号】+判定系数，从【0】开始(然而原版建筑注册是从1开始)。判定系数分别为:0-最小威胁、65536-最大威胁、131072-最近、196608-最远。
	推荐计算方法：把建筑列表复制进excel，【查找重复项并去掉重复项】，从1开始重新生成序号。
	按照上述方法，指定建筑时就可以用以下公式：
	-1 + 序号 = 选择指定建筑中威胁最小的。
	65535 + 序号 = 选择指定建筑中威胁最大的。 
	131071 + 序号 = 选择指定建筑中距离最近的。 
	196607 + 序号 = 选择指定建筑中距离最远的。 
	以YR为例：
	萌军电厂1=GAPOWR，4种计算结果分别为0、65536、131072、196608
	光头电厂306=YAPOWR，由于中间185和241都注册成了NAPSYA，253和273都被注释掉了，304根本不存在，所以光头电厂的顺序其实是302，4种计算结果分别是301、65837、131373、196909。
★47,N - 移动到N指定敌方或中立建筑附近。N的计算方法同上，"附近"的范围由CloseEnough指定。(海军可用)
48,0 - 侦查。(遭遇战无效)
★49,0 - 任务执行成功。小队每执行一次49号指令，都会在该AITrigger中增加AITriggerSuccessWeightDelta的权重。如果被消灭之前没能执行49号指令，则减少AITriggerFailureWeightDelta*AITriggerTrackRecordCoefficient的权重。
★53,0 - 在敌人基地附近集合，距离由AISafeDistance决定。(海军有效)
★54,0 - 在友方基地附近集合。距离由AISafeDistance决定，Ares中由AIFriendlyDistance决定。(海军有效)
☆55,0 - 如果铁幕就绪，则为该小队罩铁幕。
☆56,N - 如果超时空传送就绪，则传送该小队至N指定建筑物，N算法见46号指令。
☆57,n - 如果超时空传送就绪，则传送该小队至n指定类别，n类别同0号指令。
★58,N - 移动到N指定友方建筑附近，N算法见46号指令。(海军可用)
59,n - 攻击n号的路径点。
60,0 - 进入回收站。
☆61,0 - 进入坦克碉堡。
☆62,0 - 进入生化发电厂。
63,0 - 进驻最近的战斗碉堡。(经常不灵)
★64,0 - 进驻最近的可驻军建筑。(即使是用来占己方碉堡，也比63更稳定……)

与其他项目不同，Script通用性很强，尤其是进攻型小队，可能会有很多不同的小队执行相同或类似的任务——比如各种反载具小队都可以通过同样的攻击车辆的指令来达到反载具的目的。因此在制作AI时，可以先制作一个“Script池”，把泛用性较强的脚本(如反装甲、反步兵、拆塔、抄家等)放进池子里，建立TeamTypes的时候就可以直接从池子里调用。可以省去大量重复性的工作。


[TeamTypes]
首先还是注册表，格式跟正常注册表一样:索引=注册ID。也可以跳数。
0=注册项4
1=注册项5
……
然后是每个注册项 ;如无特殊标记，均可直接复制。：
★[注册项4]
☆Name=名称 ;随便写反正不读，仅供编写者记忆和查找用。
VeteranLevel=1 ;无视。
MindControlDecision=0 ;被心灵控制后的动作，0=随缘、1=加入控制者作战小队、2=部队回收厂卖钱、3=生化反应炉填电、4=搜索敌人、5=摸鱼
☆Loadable=yes/no ;运载单位写yes会计算ContentScan。
Full=no ;无视。
Annoyance=no ;无视。
GuardSlower=no ;无视。
House=<none> ;无视。
★Recruiter=yes/no ;可否从低优先级的队伍和闲置单位中直接调用成员。
☆Autocreate=yes/no ;结合地图预置单位生效。遭遇战中，在条件满足的情况下，平均每隔AutocreateTime规定的时间，制造一队带有Autocreate的小队。
☆Prebuild=yes/no ;可能会在不需要时提前造好该队伍。(待确认)
Reinforce=no ;无视。
Droppod=no ;无视。
UseTransportOrigin=no ;无视。
Whiner=no ;无视。
☆LooseRecruit=yes/no ;完成任务后解散。
★Aggressive=yes/no ;和Suicide共同起作用。
★Suicide=yes/no ;和Aggressive共同起作用。
★Priority=num ;优先级。
★Max=num ;该小队最大生成数量。
TechLevel=0 ;无视。
Group=-1 ;除非特殊需要。另：-2时会选取任何TaskForce。
OnTransOnly=no ;无视。
★AvoidThreats=yes/no ;是否会按威胁计算方法寻找最小威胁路径。
IonImmune=no ;无视。
☆TransportsReturnOnUnload=yes/no ;运输工具完成任务后是否回家。
★AreTeamMembersRecruitable=yes/no ;队伍成员可否被调去更高优先级的队伍中。
★IsBaseDefense=yes/no ;守家部队。
★OnlyTargetHouseEnemy=yes/no ;是否只针对敌对方。
★Script=ScriptTypes ;注册在[ScriptTypes]里的Script。
★TaskForce=TaskForce ;注册在[TaskForces]里的TaskForce。

Aggressive和Suicide:
这两句共同决定AI执行脚本过程中是否主动攻击敌人，以及受到攻击时是否还击。
(1)执行0号，1号，46号，59号指令时：Aggressive和Suicide无论写啥，都不会对射程内的敌人进行攻击，而是继续执行当前脚本。如果受到攻击，Suicide=no会进行还击。
(2)执行3号，47号，53号指令时：如果Aggressive=yes和Suicide=no，则会对进入射程的敌人进行攻击，在攻击结束后继续执行脚本。如果受到攻击，则会进行还击，并在还击结束后才继续执行脚本。其余情况均不会对射程内的敌人进行攻击，也不会还击。
(3)执行16号指令时：只要Suicide=yes，就会攻击进入射程的敌人，并且在攻击结束后继续执行脚本，但是受到攻击不会还击。如果Aggressive=yes和Suicide=no，与(2)相同。如果都是no，那么会变向路径点移动边进行攻击，如果受到攻击，则在射程内进行还击，其余情况继续移动执行脚本。

Recruiter、AreTeamMembersRecruitable、Priority:
可能是最神奇的几个语句，能让AI完成相对复杂的队伍重构工作。目前测试结果表明作用方式如下：
如果TeamA和TeamB有重合的单位并且同时存在于场上，且TeamB的成员所在状态是Recruitable=yes的，TeamA具有Recruiter=yes，TeamB具有AreTeamMembersRecruitable=yes，且TeamA的Priority大于(等于)TeamB，那么AI在招募TeamA时就会优先从TeamB中调取单位，而不是直接从工厂制造该单位。
另，一个Recruiter=yes但是Priority=0的队伍，也可以从无队伍的闲置单位中调取单位。

Autocreate:
生效前提是Recruiter=yes。
地图预置单位都有AUTOCREATE_NO_RECRUITABLE和AUTOCREATE_YES_RECRUITABLE两个标签，Autocreate会结合地图预置单位的这两个标签生效，会优先招募地图预置单位。
预置单位的标签位置根据其类型决定:
[Infantry]
INDEX=OWNER,ID,HEALTH,X,Y,SUB_CELL,MISSION,FACING,TAG,VETERANCY,GROUP,HIGH,AUTOCREATE_NO_RECRUITABLE,AUTOCREATE_YES_RECRUITABLE
[Units]
INDEX=OWNER,ID,HEALTH,X,Y,FACING,MISSION,TAG,VETERANCY,GROUP,HIGH,FOLLOWS_INDEX,AUTOCREATE_NO_RECRUITABLE,AUTOCREATE_YES_RECRUITABLE
[Aircraft]
INDEX=OWNER,ID,HEALTH,X,Y,FACING,MISSION,TAG,VETERANCY,GROUP,AUTOCREATE_NO_RECRUITABLE,AUTOCREATE_YES_RECRUITABLE
作用方式:
Autocreate=no时，AUTOCREATE_NO_RECRUITABLE=0的预置单位无法招募，AUTOCREATE_NO_RECRUITABLE=1可以招募，预置单位不够时会通过工厂生产补足队伍，预置单位无法响应招募时无法通过工厂生产替代该单位。
Autocreate=yes时，AUTOCREATE_YES_RECRUITABLE=0的预置单位无法招募，AUTOCREATE_YES_RECRUITABLE=1可以招募。预置单位无法响应招募时会从工厂生产来补足队伍，但是AUTOCREATE_NO_RECRUITABLE=1的单位无法通过工厂生产替代。

[AITriggerTypes]
首先这个没注册表……其次格式如下(最好直接复制已有的然后再改)。
1234567-G=Name瞎编一个就行(不能有,),7654321-G,<all>,3,1,GAREFN,0A00000002000000000000000000000000000000000000000000000000000000,500.000000,0.000000,500.000000,1,0,1,0,<none>,1,1,1
注册项6=Name,TeamTypesA,国家,TechLevel,条件,前提,前提条件参数,基础权重,权重下限,权重上限,是否用于遭遇战(0-否,1-是,写1即可),0,阵营,0,TeamTypesB,简单AI能否使用(0-否,1-是),中等AI能否使用(0-否,1-是),困难AI能否使用(0-否,1-是)
以下是各项解释:
Name - 名称 ;随便写反正不读，仅供编写者记忆和查找用。
TeamTypesA & TeamTypesB - 第一小队 & 第二小队：分别判定，而且第二小队先造。(不建议用第二小队，写<none>最好)
国家 - 如果不涉及专属单位就写<all>，需要设置专属单位就写该国家注册名。
TechLevel - 无特殊需要可以写1，如果有特殊模式可以按模式需要自行拟定。
条件,前提,前提条件参数:
条件,前提:
	-1,<none>	任何条件
	0,前提		敌方拥有(前提)指定的单位
	1,前提		己方拥有(前提)指定的单位
	2,<none>		敌方电力黄
	3,<none>		敌方电力红(间谍偷电成功时有效）
	4,<none>		敌方金钱大于或等于
	5,<none>		本方铁幕装置就绪
	6,<none>		本方超时空传送就绪
	7,前提		中立方拥有(前提)指定的单位
前提:单位注册名，AircraftTypes，BuildingTypes，InfantryTypes，VehicleTypes皆可。
前提条件参数:格式写作XXXXXXXXYY000000000000000000000000000000000000000000000000000000，64位长度，16进制。前10位有效。前8位代表数值，第9、10位代表算法值。
(前8位是程序猿格式，要两位两位写，比如5000的16进制是1388，这里就要写成8813，10W的16进制是186A0，这里就要写成A08601，不过除了金钱也没其他数字会写这么长)。算法2位：00 - 小于，01 - 小于等于，02 - 等于，03 - 大于等于，04 - 大于，05 - 不等于。
例:如果触发条件是"敌方拥有的BILLY小于等于144个"，那么就写成"0,BILLY,9000000001000000000000000000000000000000000000000000000000000000"
三个权重:控制AI在满足条件下激活该触发的可能性。数字写多少都行，整数小数都没问题，当然最好控制合理些以便AI可以抽到不同的触发。(【只有】两个特殊值：0 - 完全不触发。5000 - 满足条件必触发)
阵营:1-萌，2-联，3-尤，4-第4阵营，5-第5阵营……(0-全)
三个难度(这次WW没写倒):用来区分不同难度的AI。
运用：AITrigger的重点在于为AI不同发展时期的Team安排不同阶段的触发条件。比如初期低级兵种Rush就可以将前提条件设定为"己方不存在机场/雷达/其他高科技建筑"，这样在AI拥有高级科技建筑以后就不会再爆初期队伍了。此外还可以利用AI权重变化，把AI权重降为0，AI就彻底不会出这支小队了。
以上只是简单的举例，具体到如何编排AI的队伍，就要各位modder自己开动脑筋了。

关于机场飞机：
Ra2对机场飞机的判定极为坑爹。飞机一旦离地，AI就会倾向于认为飞机"没了"……而一旦AI造的机场飞机和正好回归的飞机撞了停机坪，就有大概率弹框。
目前较为稳妥的方式是，用飞机自身的数量作为触发前提，尽可能单个制造。需要群体出击的话通过重组组成大队出击，但是补充飞机时优先重组现有飞机。
大体流程如下：飞机数量小于4(视机场数量和停机坪数量而定)时，制造包含1架飞机的低优先小队，该小队Max=4。飞机数量等于4时，编成4架飞机组成的高优先级队伍出击。高优先级小队可以调用低优先级小队成员。
利用重组的方法，减少AI大批量造飞机的可能性，这样即使偶尔撞停机位也不至于崩溃。

以上就是AI(MD).ini的四个主要板块，其实还是很好理解的。

关于命名：
原版AI条目都是毫无规律的数字。但是在自己编写AI的时候，完全可以采用有规律的命名方式，这样在4个部分切换的时候会非常方便记忆和查找。采用何种规律取决于自己的记忆习惯，只要不超过32个字节即可。
另：-G完全没用，唯一的用处是可以确保AI项目的注册名不会跟其他项目的注册名重复。

以下举几个命名规律的例子，四个[]分别对应[TaskForces]、[ScriptTypes]、[TeamTypes]、[AITriggerTypes](实际写的时候AITriggerTypes没有[]):
[3HTNKTask-G]、[3HTNKScript-G]、[3HTNKTeam-G]、[3HTNKTrigger-G]
[3HTNK001-G]、[3HTNK101-G]、[3HTNK201-G]、[3HTNK301-G]
[AL000001-G]、[AL100001-G]、[AL200001-G]、[AL300001-G]
[3ENG-G]、[CapOil-G]、[3ENGCapOilTeam-G]、[3ENGCapOilTrig-G]
[Ass♂We♂Can]、[Deep♂Dark♂Fantasy]、[Thank♂You♂Sir]、[DA☆ZE]
[金坷垃]、[是我哒]、[真不傻]、[不给他]（用中文和!会出bug，不建议）

其他：吱吱酱的AI检查脚本是极好的辅助工具，能够查出不少容易被忽略的细节疏漏。



第五章 地图相关
地图中可以单独添加AI，为了区分地图中的AI和aimd.ini中的AI，通常把写在aimd.ini中的叫做全局AI，写在map里的叫做和本地AI。
[General]、[TaskForces]、[ScriptTypes]、[TeamTypes]、[AITriggerTypes]都可以在地图里单独设定，且优先级高于ini文件。
在制作玩法较为特别的地图时，为地图单独设定一套专有的AI会大大提高地图可玩性。
且地图里可用路径点来完成一些全局AI无法通用的操作。

另外有两句十分重要的语句：
[Basic]
IgnoreGlobalAITriggers=bool ;是否屏蔽全局AI。AI测试图的关键语句。另外一些特殊玩法地图也可以屏蔽掉全局AI，用地图内置AI取代全局。
AI的编写和测试是一个繁琐的工作。自制一张简易的AI测试图会非常有帮助。AI测试图需要的大概就只有两个发展空间足够大的岛和一条通道，再摆上一些中立建筑可进驻建筑之类的，用以测试一条完整的AITrigger能不能顺利触发，用处巨大。

[AITriggerTypesEnable]
对【遭遇战地图】，控制本地AI是否生效，对全局AI完全无效。格式"通常"是：AITriggerTypes注册名=bool。
目前可以确定遭遇战地图中必须把本地AITriggerTypes=写进去才能生效，但是"="后面写任何东西都算激活该条AITriggerTypes。不写或只写到=则视为该AITriggerTypes无效。
举个栗子:
某遭遇战地图的[AITriggerTypes]项下有AI555、AI998、AI2333、AI6666、AI114514一共5个本地AITriggerTypes，[AITriggerTypesEnable]写成如下鸟样：
[AITriggerTypesEnable]
AI555==
AI2333=倪达耶德
AI6666=no
AI114514=
那么AI555、AI2333、AI6666生效，AI998和AI114514都不生效。
对【战役地图】生效方式很(待)奇(确)葩(认)，据说战役地图全局AI默认yes，本地AI默认no。[Basic]IgnoreGlobalAITriggers=yes会覆盖AITriggerTypesEnable中列出的全局AI。
如果要禁用部分全局AI，则应将要禁用的全局AITriggerTypes在此列出并写no，同时IgnoreGlobalAITriggers写为no。
如果要在开局时启用部分本地AI，则要将本地AIAITriggerTypes在此列出并写yes。
此外还可以通过触发无视以上步骤激活本地AI。

Ares地图相关：除了预置单位以及开局就刷出来的单位，地图触发无法刷出带载荷的运输单位，因此需要通过其他方法实现刷载荷单位。



PLUS Ares平台下的AI骚操作
虽然Ares在AI方面并没有多少针对性改动，不过却在无意之间为AI提供了更多可能性。

套皮单位：
Ares最有价值的一环，即玩家无法识别的套皮单位。然而AI却可以识别，如何只能让AI制造套皮单位就是应用的关键。
如果是直接制造，我们知道AI制造非建筑时会受到TechLevel限制，却无视Prerequisite，因此只要让套皮单位的TechLevel保持正常，把Prerequisite设定成玩家无法获得的建筑就可以了，比如无法占领的中立建筑(篝火、邮筒、阳伞、毛巾、垃圾箱、热狗摊等)，也可以是只有注册名却不实际存在的"注册建筑"。
非直接制造的话，其实被玩家获得也没啥影响，反正看不出来。
套皮单位结合合理全局设置和AI脚本，甚至可以实现AI精确操控伞兵。(其实不套皮也能，只不过有时不能精确控制)

无节操运输：
套皮单位的一个分支，可以避免AI装载货物时各种卡死，以及运输脚本的诸多限制。
借助Ares甚至可以做到多个运输单位同时存在于同一个AI编队，并且流畅地完成运输任务。虽然穿帮较为明显，但"AI必须通过一定程度的作弊来才能与玩家抗衡"，综合来看，还是能让AI做出比正常情况下更为多样的表现，是利大于弊的。

Delivery超武：
这个神奇的超武可以帮助AI完成许多特别的操作。比如通过触发在地图上刷出具有初始载荷的单位，一定程度上解决了地图触发刷的单位没有载荷的问题。
还有让AI更加智能地运用防御塔防守的策略(特别鸣谢该战术的研发者星星)：在敌人进攻到家门口时贴脸造起一座塔，打敌人个猝不及防，让AI表现得更像玩家。
此外，由于地图触发无法刷出带载荷的运输单位，可以考虑使用Delivery超武刷载荷单位，再结合AI脚本让AI控制该单位。

模块化：
拆分ini的功能使得AI可以更容易地分块进行添加和测试，活用#include功能可以加快AI制作的效率，对AI制作流程优化有着很明显的帮助。


以上列举的有趣玩法仅供参考，还有更多的可能性需要大家自行发掘。


教学部分至此结束。


AI是个制作水平很难快速提升，一方面需要足够开阔的脑洞才能在诸多引擎限制之中找到赋予AI骚操作的套路，另一方面也需要枯燥乏味的海量测试来观测AI的执行效果。即使是这篇教程，也会有不少不严谨或测试不到的地方，细节之处还是需要自行测试。
最后，重复一遍，一套完整的AI是Rules设定、ai(md).ini以及单位代码共同作用的结果，AI的许多骚操作都要通过不同领域的功能组合才能实现。希望这篇教程能拓宽你的思路，并且创造出新的AI套路。



										Madman_M

										2019.04.22


