废话
rimworld在A17版本后提供了一个新的编写mod的特性,叫做XML PatchOperations。直译就是补丁手术。
正如字面意思,它使用xpath的语法让modder可以以补丁的方式修改原版以及其他mod中变量的值。
本篇做一个简单的入门介绍,因为原文写的已经非常详细了。
原文链接
核心内容
1 使用Xpath制作patch(补丁)
1.1 基本格式
大部分格式都是 手术类型–变量位置–值,一些高级语法稍微复杂一些,可以做到if()else{}一样的逻辑判断
<?xml version="1.0" encoding="utf-8" ?>
<Patch>
<!-- 要动用的手术类型 -->
<Operation Class="PatchOperationReplace">
<!-- 要修改的变量是某个标签为<TerrainDef>中defName为WaterDeep的物品,修改这个物品的纹理值texturePath -->
<xpath>/Defs/TerrainDef[defName="WaterDeep"]/texturePath</xpath>
<value>
<!-- 值改为你新创造的纹理目录 -->
<texturePath>Your/Texture/Path/Here</texturePath>
</value>
</Operation>
</Patch>
关于Xpath的路径需要注意的是,它代表xml文件中的节点路径,而不是你的xml文件在硬盘中的路径,虽然文件夹可能也叫一样的名字
举例分析:
Human这个种族的def文件中,xpath的路径应该为/Defs/ThingDef,如果修改label则应该写为/Defs/ThingDef[defName=“Human”]/label,如果要添加comps节点,则要在value中填写comps,不可以在路径中填写comps,因为源文件里没有这个属性。反之如果源文件里有comps则要追踪到comps路径下。
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<ThingDef ParentName="BasePawn">
<defName>Human</defName>
<label>human</label>
<description>A baseline human, mostly unmodified by gene engineering and mostly unchanged by evolutionary pressures on non-Earth planets.</description>
<statBases>
<MarketValue>1750</MarketValue>
<MoveSpeed>4.6</MoveSpeed>
<Flammability>1.0</Flammability>
<ComfyTemperatureMin>16</ComfyTemperatureMin>
<ComfyTemperatureMax>26</ComfyTemperatureMax>
<LeatherAmount>50</LeatherAmount>
<RoyalFavorValue>3</RoyalFavorValue>
</statBases>
<tools>
<li>
<label>left fist</label>
<capacities>
<li>Blunt</li>
</capacities>
<power>8.2</power>
<cooldownTime>2</cooldownTime>
<linkedBodyPartsGroup>LeftHand</linkedBodyPartsGroup>
<surpriseAttack>
<extraMeleeDamages>
<li>
<def>Stun</def>
<amount>14</amount>
</li>
</extraMeleeDamages>
</surpriseAttack>
</li>
<li>
<label>right fist</label>
<capacities>
<li>Blunt</li>
</capacities>
<power>8.2</power>
<cooldownTime>2</cooldownTime>
<linkedBodyPartsGroup>RightHand</linkedBodyPartsGroup>
<surpriseAttack>
<extraMeleeDamages>
<li>
<def>Stun</def>
<amount>14</amount>
</li>
</extraMeleeDamages>
</surpriseAttack>
</li>
<li>
<label>teeth</label>
<capacities>
<li>Bite</li>
</capacities>
<power>8.2</power>
<cooldownTime>2</cooldownTime>
<linkedBodyPartsGroup>Teeth</linkedBodyPartsGroup>
<chanceFactor>0.07</chanceFactor>
</li>
<li>
<label>head</label>
<capacities>
<li>Blunt</li>
</capacities>
<power>5</power>
<cooldownTime>2</cooldownTime>
<linkedBodyPartsGroup>HeadAttackTool</linkedBodyPartsGroup>
<ensureLinkedBodyPartsGroupAlwaysUsable>true</ensureLinkedBodyPartsGroupAlwaysUsable>
<chanceFactor>0.2</chanceFactor>
</li>
</tools>
<race>
<thinkTreeMain>Humanlike</thinkTreeMain>
<thinkTreeConstant>HumanlikeConstant</thinkTreeConstant>
<intelligence>Humanlike</intelligence>
<makesFootprints>true</makesFootprints>
<lifeExpectancy>80</lifeExpectancy>
<leatherDef>Leather_Human</leatherDef>
<nameCategory>HumanStandard</nameCategory>
<body>Human</body>
<baseBodySize>1</baseBodySize>
<baseHealthScale>1</baseHealthScale>
<foodType>OmnivoreHuman</foodType>
<gestationPeriodDays>45</gestationPeriodDays>
<meatMarketValue>0.8</meatMarketValue>
<manhunterOnDamageChance>0.20</manhunterOnDamageChance>
<manhunterOnTameFailChance>0.02</manhunterOnTameFailChance>
<litterSizeCurve>
<points>
<li>(0.5, 0)</li>
<li>(1, 1)</li>
<li>(1.01, 0.02)</li>
<li>(3.5, 0)</li>
</points>
</litterSizeCurve>
<lifeStageAges>
<li>
<def>HumanlikeBaby</def>
<minAge>0</minAge>
</li>
<li>
<def>HumanlikeToddler</def>
<minAge>1.2</minAge>
</li>
<li>
<def>HumanlikeChild</def>
<minAge>4</minAge>
</li>
<li>
<def>HumanlikeTeenager</def>
<minAge>13</minAge>
</li>
<li>
<def>HumanlikeAdult</def>
<minAge>18</minAge>
</li>
</lifeStageAges>
<soundMeleeHitPawn>Pawn_Melee_Punch_HitPawn</soundMeleeHitPawn>
<soundMeleeHitBuilding>Pawn_Melee_Punch_HitBuilding</soundMeleeHitBuilding>
<soundMeleeMiss>Pawn_Melee_Punch_Miss</soundMeleeMiss>
<specialShadowData>
<volume>(0.3, 0.8, 0.4)</volume>
<offset>(0,0,-0.3)</offset>
</specialShadowData>
<ageGenerationCurve>
<points>
<li>(14,0)</li>
<li>(16,100)</li>
<li>(50,100)</li>
<li>(60,30)</li>
<li>(70,18)</li>
<li>(80,10)</li>
<li>(90,3)</li>
<li>(100,0)</li>
</points>
</ageGenerationCurve>
<hediffGiverSets>
<li>OrganicStandard</li>
<li>Human</li>
</hediffGiverSets>
</race>
<recipes>
<li>ExciseCarcinoma</li>
<li>AdministerMechSerumHealer</li>
<li>RemoveBodyPart</li>
<li>Euthanize</li>
<li>Anesthetize</li>
</recipes>
</ThingDef>
</Defs>
1.2 手术类型
支持的手术类型如下
四种基础手术
PatchOperationAdd 为所选的节点添加一个子节点,以及子节点的默认值,其实就是新增变量
PatchOperationInsert 为所选的节点添加一个同级别的节点和默认值
PatchOperationRemove 删除所选的节点
PatchOperationReplace 替换所选的节点(的值)
三个针对xml Attribute(特性)的手术
PatchOperationAttributeAdd 添加特性,只有在特性不存在的时候才会生效
PatchOperationAttributeSet 为已经存在的特性提供修改
PatchOperationAttributeRemove 撤销某个特性
三种更复杂的类型
PatchOperationAddModExtension 添加ModExtension.
ModExtension是一种以C#的方式给xml打补丁的方式,简单轻便,可以做更复杂的逻辑,但因为没有自己独立的def容易出一些问题。
ModExtension介绍
PatchOperationSetName 改变节点的命名
PatchOperationSequence 一个PatchOperration格式的序列,一组手术,有语法错误的时候会停下来
三种条件手术
PatchOperationTest 对节点进行测试,很适合用在手术序列上
PatchOperationConditional 条件判断的手术,有了它你甚至可以在xml里写逻辑代码0.0
PatchOperationFindMod 可以用来测试某个mod是否存在,然后配合Conditional类型的手术可以让你的mod针对其他mod做一些特别的修改,从而达到高兼容性
我就主要说一下条件判断手术,因为会比较常用
<?xml version="1.0" encoding="utf-8" ?>
<Patch>
<!-- 对上次做的手办(鸡)的描述/Defs/ThingDefs_Buildings[defName = "GarageKid_Chicken"]/description 做条件判断 -->
<Operation Class = "PatchOperationConditional">
<xpath>/Defs/ThingDefs_Buildings[defName = "GarageKid_Chicken"]/description</xpath>
<!-- 存在这个变量名 做替换-->
<match Class = "PatchOperationReplace">
<xpath>/Defs/ThingDefs_Buildings[defName = "GarageKid_Chicken"]/description</xpath>
<value>
<description>修改后的描述</description>
</value>
</match>
<!-- 不存在这个变量名 做添加操作 -->
<nomatch Class = "PatchOperationAdd">
<xpath>/Defs/ThingDefs_Buildings[defName = "GarageKid_Chicken"]/description</xpath>
<value>
<description>修改后的描述</description>
</value>
</nomatch>
</Operation>
</Patch>
1.3 偷窥 参考他人代码
这周有一个流行度很高的mod刚好采用xpath实现功能,名字叫做Priority treatment,它的功能是修改小人的AI。原版的小人有时候特别智障,一群伤员躺在病房,结果医生去吃饭,去睡觉,甚至去看电视。。。这个mod改善了这种情况,让医生职业的角色优先从事医护工作。
修改AI这操作,萌新一定觉得非常高大上,然而这个mod一共只使用了4个非常简单的xpath手术操作。
原版小人路过燃烧的地方时,不论他在做什么工作,一定会优先灭火,这个行为是由于灭火工作里有一个<emergency>true</emergency>
突发事件=true的变量。普通的工作是没有的。通过给原版其他工作添加这个变量,从而达到让医生救人如救火的效果,各种意义上。
<?xml version="1.0" encoding="utf-8" ?>
<Patch>
<Operation Class="PatchOperationAdd">
<xpath>*/WorkGiverDef[defName = "DoctorTendToHumanlikes"]</xpath>
<value>
<emergency>true</emergency>
</value>
</Operation>
<Operation Class="PatchOperationAdd">
<xpath>*/WorkGiverDef[defName = "DoctorTendToSelf"]</xpath>
<value>
<emergency>true</emergency>
</value>
</Operation>
<Operation Class="PatchOperationAdd">
<xpath>*/WorkGiverDef[defName = "DoctorTendToAnimals"]</xpath>
<value>
<emergency>true</emergency>
</value>
</Operation>
<Operation Class="PatchOperationAdd">
<xpath>*/WorkGiverDef[defName = "DoctorRescue"]</xpath>
<value>
<emergency>true</emergency>
</value>
</Operation>
</Patch>
*表示xpath语法中匹配任何元素节点,是从根目录匹配到/Defs存在WorkGiverDef这个目录的,感兴趣的话可以看一眼xpath的语法教程
xpath语法教程
如果这篇文章对你有帮助,点赞收藏支持一下呗!