|
Using a simple Galaxy function CatalogFieldValueGet() to read data from XML template
must reserve! 转载请保留此行
虽然此前我做过强化AI,但是其实基本都主要在玩XML的部分,脚本部分是交给叔叔的。今天算是比较正式地第一次接触脚本。这里来介绍一个不错的函数CatalogFieldValueGet().
该函数可以自由读取GameData下51个数据模板文件中所有模板的内容。我们可用直接用这一个函数来得到所有单位、技能、演算实体、动体、行为、效果、武器、光、升级、验证器等等模板的属性。
在目前开放的AI文件中并没有关于该函数的声明,但是实际上已经存在于SC2.exe里,
其参数列表可以参考这个帖子。
http://bbs.islga.org/read-htm-tid-38282.html
但是因为没有声明,无法通过参数名猜测用途,因此这些参数的作用都是我测试得到的。现在通过我目前得知的参数作用来给它的参数标上参数名。
[codes=galaxy]
native string CatalogFieldValueGet(int catalog, string templateId, string prop, int player);
[/codes]
参数介绍:
int catalog:
第一个参数是整数,代表想要读取的是GameData的哪个xml。
Core中的TriggerLibs\GameData下面的GameData.galaxy定义了所有的51个内部XML对应的整数值(话说我总算知道TriggerLibs\GameData下这些个.galaxy件是干嘛的了)。其中0代表AbilData.xml,1代表AchievementData.xml,按照常量名以此类推。以下是51个对应数据文件的常量。
[codes=galaxy]
// EGameCatalog
const int c_gameCatalogAbil = 0;
const int c_gameCatalogAchievement = 1;
const int c_gameCatalogAchievementTerm = 2;
const int c_gameCatalogActor = 3;
const int c_gameCatalogAlert = 4;
const int c_gameCatalogAttachMethod = 5;
const int c_gameCatalogBeam = 6;
const int c_gameCatalogBehavior = 7;
const int c_gameCatalogButton = 8;
const int c_gameCatalogCamera = 9;
const int c_gameCatalogCliff = 10;
const int c_gameCatalogCliffMesh = 11;
const int c_gameCatalogConversation = 12;
const int c_gameCatalogConversationState = 13;
const int c_gameCatalogCursor = 14;
const int c_gameCatalogDSP = 15;
const int c_gameCatalogEffect = 16;
const int c_gameCatalogError = 17;
const int c_gameCatalogFootprint = 18;
const int c_gameCatalogFoW = 19;
const int c_gameCatalogGame = 20;
const int c_gameCatalogGameUI = 21;
const int c_gameCatalogItem = 22;
const int c_gameCatalogItemClass = 23;
const int c_gameCatalogItemContainer = 24;
const int c_gameCatalogLight = 25;
const int c_gameCatalogLoot = 26;
const int c_gameCatalogModel = 27;
const int c_gameCatalogMover = 28;
const int c_gameCatalogRace = 29;
const int c_gameCatalogRequirement = 30;
const int c_gameCatalogRequirementNode = 31;
const int c_gameCatalogReverb = 32;
const int c_gameCatalogScoreResult = 33;
const int c_gameCatalogScoreValue = 34;
const int c_gameCatalogSound = 35;
const int c_gameCatalogSoundtrack = 36;
const int c_gameCatalogTactical = 37;
const int c_gameCatalogTargetFind = 38;
const int c_gameCatalogTargetSort = 39;
const int c_gameCatalogTerrain = 40;
const int c_gameCatalogTerrainObject = 41;
const int c_gameCatalogTerrainTex = 42;
const int c_gameCatalogTexture = 43;
const int c_gameCatalogTile = 44;
const int c_gameCatalogTurret = 45;
const int c_gameCatalogUnit = 46;
const int c_gameCatalogUpgrade = 47;
const int c_gameCatalogValidator = 48;
const int c_gameCatalogWater = 49;
const int c_gameCatalogWeapon = 50;
[/codes]
string templateID
第二个参数为字符串类型,代表想要读取的模板的id,也就是那个模板的id属性(Core中的一些默认模板并没有id,是通用模板,可能就无法读取,但这个其实无所谓,手动加上id属性即可)。
再拿这个用烂的例子来举例好了,要读取下面这个模板的属性数据,就是用TerranAddOns来做第二个参数,注意,大小写敏感。
[codes=xml]
<CAbilBuild default="1" id="TerranAddOns">
<EditorCategories value="Race:Terran,AbilityorEffectType:Units"/>
<Alert value="AddOnComplete"/>
<Type value="AddOn"/>
<FlagArray index="InstantPlacement" value="1"/>
<FlagArray index="PeonDisableAbils" value="1"/>
<FlagArray index="ShowProgress" value="1"/>
<InfoArray index="Build1" Unit="TechLab" Time="30">
<Resource index="Minerals" value="50"/>
<Resource index="Vespene" value="50"/>
<Button DefaultButtonFace="TechLabBarracks" State="Suppressed"/>
</InfoArray>
<InfoArray index="Build2" Unit="NuclearReactor" Time="40">
<Resource index="Minerals" value="50"/>
<Resource index="Vespene" value="50"/>
<Button DefaultButtonFace="NuclearReactor" State="Restricted"/>
</InfoArray>
</CAbilBuild>
[/codes]
使用不存在的id来做参数会导致错误,具体参照下面的错误一节。
注意该参数并不限于游戏内部定义的那些模板,你完全可以在任意一个xml的数据文件中的<Catalog>中定义一段自己的XML元素,然后只要格式符合该xml文件的通用格式(基础类和及其属性),那么加上id这个属性就能读取,就算它实际上根本就不是一个单位、技能之类东西的模板也行。
string prop
该参数即指定模板中需要读取的属性。
注意,使用该参数可以读取模板下任意一级的属性,而不仅仅限于模板的直属属性。
指定子属性的方式是使用点(.)。
对于属性数组,需要指定读取数组的哪个元素,用index属性来表示索引。如"InfoArray[Build1]"。
关于子属性和属性数组中在该参数中的表示方式可以参考下面的实际例子。
尝试获取不存在的属性会出错,具体参照下面的错误部分。
int player
勘误:
http://bbs.islga.org/read-htm-tid-39787.html
几个实际例子:
现在来看几个实际例子。
第一个例子:
还是拿出TerranAddOns,该模板位于AbilData.xml
[codes=xml]
<CAbilBuild default="1" id="TerranAddOns">
<EditorCategories value="Race:Terran,AbilityorEffectType:Units"/>
<Alert value="AddOnComplete"/>
<Type value="AddOn"/>
<FlagArray index="InstantPlacement" value="1"/>
<FlagArray index="PeonDisableAbils" value="1"/>
<FlagArray index="ShowProgress" value="1"/>
<InfoArray index="Build1" Unit="TechLab" Time="30">
<Resource index="Minerals" value="50"/>
<Resource index="Vespene" value="50"/>
<Button DefaultButtonFace="TechLabBarracks" State="Suppressed"/>
</InfoArray>
<InfoArray index="Build2" Unit="NuclearReactor" Time="40">
<Resource index="Minerals" value="50"/>
<Resource index="Vespene" value="50"/>
<Button DefaultButtonFace="NuclearReactor" State="Restricted"/>
</InfoArray>
</CAbilBuild>
[/codes]
我们现在来尝试获得该模板的
<EditorCategories value="Race:Terran,AbilityorEffectType:Units"/>中的value
<InfoArray index="Build1" Unit="TechLab" Time="30">中的Unit
<InfoArray index="Build1">之下的<Resource index="Minerals" value="50"/>中的value
以下开始分析
要获得的第一个值其实是该模板的EditorCategories属性的值,因此第三个参数写"EditorCategories"即可
第二个值其实是该模板的InfoArray属性数组中索引为Build1的属性的子属性Unit的值,因此第三个参数写"InfoArray[Build1].Unit"
第三个值其实是该模板的InfoArray属性数组中索引为Build1的属性的子属性数组Resource中索引为的Minerals的属性的值,因此第三个参数写"InfoArray[Build1].Resource[Minerals]"
注:
如果仔细斟酌我上面的话,你会发现,对SC2来说,XML数据文件中,XML元素的XML属性,与该XML元素的子XML元素其实是等价的,因此我在那篇规范文中将他们统称为“属性”。
事实上
<InfoArray index="Build1" Unit="TechLab" Time="30">的写法
与
<InfoArray index="Build1">
<Unit value="TechLab"/>
<Time value="30"/>
</InfoArry>
这样的写法完全等价。将XML属性改写成子XML元素只需要加个value即可。无论是哪种写法,获得Unit的值的方式都是"InfoArray[Build1].Unit",写成"InfoArray[Build1].Unit.value"反而会出错。
于是以下是实际代码和显示结果。第一个参数c_gameCatalogAbil代表读取的是AbilData.xml,第二个参数代表读取TerranAddOns模板,最后一个参数代表读取的是玩家0(我自己)电脑上的数据。
[codes=galaxy]
string str;
str=CatalogFieldValueGet(c_gameCatalogAbil, "TerranAddOns", "EditorCategories", 0);
UIDisplayMessage(PlayerGroupAll(), 4, StringToText(str));
str=CatalogFieldValueGet(c_gameCatalogAbil, "TerranAddOns", "InfoArray[Build1].Unit", 0);
UIDisplayMessage(PlayerGroupAll(), 4, StringToText(str));
str=CatalogFieldValueGet(c_gameCatalogAbil, "TerranAddOns", "InfoArray[Build1].Resource[Minerals]", 0);
UIDisplayMessage(PlayerGroupAll(), 4, StringToText(str));
[/codes]
结果正常地获得了3个属性的值。
第二个例子:
可能大家有些眼尖的同学会发现,并不是所有同名属性都有各自的index,比如在UnitData.xml里很多单位有多个 名为AbilArray的属性,它们是不是也是属性数组呢?它们之间是怎么区分的呢?
实际上它们依然是属性数组,仅仅是省略了index而已,而且,该属性数组中每个元素的默认index由它们出现的顺序决定:从0开始,1,2,3,4,5...这样的顺序增长。
于是我们这里有个例子,这个单位模板拥有属性数组AbilArray。
[codes=xml]
<CUnit id="PointDefenseDrone">
<Race value="Terr"/>
<Mob value="Multiplayer"/>
<PlaneArray index="Air" value="1"/>
<Collide index="Flying" value="1"/>
<Attributes index="Light" value="1"/>
<Attributes index="Mechanical" value="1"/>
<FlagArray index="NoPortraitTalk" value="1"/>
<FlagArray index="ArmorDisabledWhileConstructing" value="1"/>
<LifeStart value="50"/>
<LifeMax value="50"/>
<LifeArmorName value="Unit/LifeArmorName/TerranShipPlating"/>
<EnergyStart value="200"/>
<EnergyMax value="200"/>
<EnergyRegenRate value="1"/>
<Mover value="Fly"/>
<Sight value="7"/>
<Height value="3"/>
<VisionHeight value="4"/>
<CostResource index="Minerals" value="100"/>
<RepairTime value="33.3332"/>
<AttackTargetPriority value="20"/>
<AbilArray Link="attack"/>
<AbilArray Link="stop"/>
<CardLayouts>
<LayoutButtons Face="PointDefense" Type="Passive" AbilCmd="255,255" Row="1" Column="0"/>
</CardLayouts>
<Radius value="0.625"/>
<SeparationRadius value="0.6"/>
<ScoreMake value="50"/>
<ScoreKill value="100"/>
<SubgroupPriority value="6"/>
<MinimapRadius value="0.6"/>
<EditorCategories value="ObjectType:Unit,ObjectFamily:Melee"/>
<GlossaryPriority value="250"/>
<GlossaryCategory value="Unit/Category/TerranUnits"/>
<WeaponArray Link="PointDefenseLaser" Turret="PointDefenseDrone"/>
<KillDisplay value="Never"/>
</CUnit>
[/codes]
这玩意儿是个定点防御机枪塔,位于UnitData.xml中,因此我们使用c_gameCatalogUnit来做第一个参数。
[codes=galaxy]
string str;
str=CatalogFieldValueGet(c_gameCatalogUnit, "PointDefenseDrone", "Name", 0);
UIDisplayMessage(PlayerGroupAll(), 4, StringToText(str));
str=CatalogFieldValueGet(c_gameCatalogUnit, "PointDefenseDrone", "AbilArray[1].Link", 0);
UIDisplayMessage(PlayerGroupAll(), 4, StringToText(str));
[/codes]
得到了结果,注意这里的AbilArray[1]代表的是AbilArray属性数组的第二个元素,所以是stop,别搞错。
有些眼尖的同学可能已经注意到,这个模板里并没有Name这个属性阿,这个Name是怎么来的呢?其实它是继承自Core中的CUnit默认模板(default=1)的。因此这个函数可以读取虽然没有显式地出现在当前模板内,但是由当前模板从其他模板继承而来的属性
关于该函数可能引发的错误(或者叫异常?):
Galaxy脚本比Jass进步多了,不是很容易崩溃。通常Native函数出错,还会直接在屏幕上显示错误,比较方便调试。而且一般来说就算函数出错游戏和当前触发还会继续运行,不会造成太大影响。因此叫异常可能更合适一些。
第一个参数可能引发的错误:
第一个参数必须是0-50间的整数,具体可以参考上面GameData.xml中定义的51个常数。如果使用此外的整数,函数会在游戏中提示错误信息,并返回null。
以下是使用了-1作为第一个参数导致的结果,提示GetCatalogEntry()错误,CatalogFieldValueGet()错误。
第二个参数可能引发的错误:
第二个参数必须为指定XML文件中实际存在的模板,否则就会提示错误,并返回null。注意该参数是大小写敏感的,大小写有差异同样会导致错误。
以下就是尝试在UnitData.xml中查询一个不存在的模板PointDefenseDrone22222时出现的错误提示,提示not found,CatalogFieldValueGet()错误。
第三个参数可能引发的错误:
第三个参数必须为指定模板实际拥有的属性,否则就会提示错误,并返回null。另外该参数可以指定虽然没有显式地出现在当前模板内,但是由当前模板从其他模板继承而来的属性(无论是隐式的继承还是显式的继承)。注意该参数是大小写敏感的,大小写有差异同样会导致错误。
以下是尝试查询一个不存在的属性EditorCategories.race时提示的错误,提示无法读取字段“EditorCategories.race”,需要的对象未找到。
注意,查询一个存在子属性,本身却没有被指定值的节点性属性并不会提示错误。如果我们直接这样写
str=CatalogFieldValueGet(c_gameCatalogAbil, "TerranAddOns", "InfoArray[Build1]", 0);
那么只会返回"",不会提示任何错误。因为InfoArray[Build1]既然存在子属性,那么本身自然也是存在的,只是没有被指定值而已。
第四个参数可能引发的错误:
第四个参数必须为-1到15之间的整数,否则就会提示错误并返回特殊的null。
以下是试图读取玩家1000的xml数据的结果,提示超出范围,最小0,最大15
特别提一下,虽然上头提示范围是0到15,但是我用-1也能通过,其它的负数就不行了,可能-1特指默认玩家或者本地玩家之类的吧。 |
|