dogma-EVE的装备系统

翻译自esi-docs/docs/dogma.md at master · esi/esi-docs · GitHub

简介

在此文档中,我将会(尝试)解释 Dogma 系统是如何在 EVE Online 的装配计算中工作的。我会使用装配计算引擎 EOS 来引用 CCP 没有发布的东西,也就是 operandID 和它的含义以及运算符。这些引用可以在 EOS 项目中作为常量找到: EVEEOS 常量。这些常量可能会也可能不会包含在以后的文档中。

另外,为简单起见,我可能会以 attrID:attrName 的形式来引用属性。

最后,我制作了一个简单的GUI,在了解 Dogma 表达式时可能会有所帮助。 屏幕快照将在本文档中使用,以在视觉上帮助理解表达式树。要找到此程序的发布,请参阅 exp-gui

什么是 Dogma ?

Dogma 是管理 EVE 中各种项目的所有属性和修改效果的核心系统。在解释 Dogma 的工作原理之前,我们需要首先了解项目和效果。项目是 EVE 的基础——游戏中的任何东西都是一个项目。你装在船上的装备,载入到你武器里的弹药,还有你的舰船本身。项目也可以不是一个实际的实体,比如星系范围的虫洞天象效果或者是战术驱逐舰的模式。EVE 中的任何东西都是有某种类型的属性附加于其上的项目,而它被定义在 invTypes 表中。

但是拥有一堆具有属性的项目是没有用的,它们无法相互影响。这时,效果出现了。效果是项目的基础,它使得项目能够实际上做某些事情。假设你给你的舰船安装了一个停滞缠绕光束。当它激活的时候,它会将它的 decreaseTargetSpeed 效果附加到当前锁定的目标。当它被超载的时候,它会将它的 overloadSelfRangeBonus 效果附加给自身。效果与项目之间有多对多的关系,每个效果可能附着在许多不同的项目上,每个项目可能有许多不同的效果。 效果都定义在 dgmEffects 表中,它和项目的关系被定义在 dgmTypeEffects 表中。

每个效果(通常)都有一个或多个修改器,最终用于计算。

那么我们现在知道了什么是项目和效果,我们如何将一个项目的效果应用到另一个项目上呢?我们必须创造一个修改器,一般每个效果都会有一个或多个。要做到这一点,我们需要知道一些东西(为了简单起见,有几点被省略了):

  1. 源属性
  2. 目标(或者位置)
  3. 目标属性
  4. 操作 / 修改器
  5. 过滤器

举个例子,我们有一个停滞缠绕光束 II,现在将它应用到我们的目标上。这个网子装备它自己有一个属性决定了它的惩罚方式,而这个属性是 20:speedFactor ,它的数值是 -60.0。这就是我们的源属性(源属性总是附加在调用相关效果的项目上,在本例中是停滞缠绕光束 II)。目标属性是目标 CurrentTarget (这是CCP对当前锁定和选择的目标的定义) 的 37:maxVelocity 。这里最好注意的是,如果我们修改的属性不是在舰船本身,而是在另一个项目上,这将指的是该项目可能所在的位置(例如:当前舰船、当前目标、帮派成员等)。修改器是 PostPercent,它基本上告诉我们将源属性按百分比应用于目标属性。

最后一点,过滤器,并不适用于我们的具体例子,但在很多其他效果中都会用到。用于根据项目的组别或项目所需的技能,将效果限制在特定的项目组别。举个例子, durationBonusForGroupAfterburner 效果被用于降低加力燃烧器和微型跃迁推进器的持续时间(被引擎热能防护装置改装件使用)。它并不会被应用于我们所有的装备(这不合常理),我们需要过滤出我们的装备中属于“加力燃烧器”组的装备,也就是所有的加力燃烧器和微型跃迁推进器。

我们目前锁定并选择了一个敌方 Retribution,它的最大速度为348米/秒。我们转换我们的源属性 -60.0到一个百分比修改器(定义于 PostPercent = val / 100 + 1)[1](javascript:void(0)) 然后我们得到了 0.4。这时我们将它乘到目标属性,得到的最终数值是 348 * 0.4 = 139.2m/s。这就是效果的工作机制。

所以现在你知道你应该找什么了。你怎么得到它?

简单的办法:modifierInfo

注意:需要编写更多关于各种运算符和功能的文档。

CCP 最近开始向新的方向修改他们的效果,把修改器数据存储在效果的 modifierInfo 字段中,而不是放在表达式树中.这是一个 YAML 格式的字段,一个典型的看起来就像这样(来自 shipModeMaxTargetRangePostDiv 效果):

- domain: shipID
  func: ItemModifier
  modifiedAttributeID: 76
  modifyingAttributeID: 1991
  operator: 5

从中你可以很容易的看出涉及到的属性。operator 的值是 5,这被定义为是 PostDiv 运算符。 func 决定了我们进行的修改的类型——这是一个简单的从属性到属性的直接修改(就像 ItemModifier),或者这有一个过滤器需要被应用(就像 LocationGroupModifier)。在这种情况下,没有过滤器被应用。 domain 告诉了我们什么是我们的目标,在这个情况下它是我们自己的舰船。请注意, modifierInfo 相对来说是比较新的,而且种类还不多。举个例子, 据我所知,没有任何投射效果(那些投射到其他舰船和角色上的效果)使用 modifierInfo 格式,因此将来可能会改变。

传统的办法:dgmExpressions

CCP最初定义效果的方式是使用表达式。每个效果包含对表达式的两个引用: preExpressionpostExpressionpreExpression 被用于一个效果被应用的时候(例如:一个装备被激活时),而 postExpression 一般是它的镜像,被用于那个效果被移除的时候。表达式存储在 dgmExpressions 表中,而且有一个树状数据结构,每个表达式都可以由其他表达式组成;最终目标是遍历此树并提取产生可操作的修改器所需的信息。

这是一个典型的 dgmExpressions 条目。

"expressionGroupID": null,
"expressionAttributeID": null,
"description": null,
"expressionValue": null,
"arg1": 3487,
"arg2": 105,
"expressionName": "((CurrentTarget->maxVelocity).(PostPercent)).AddItemModifier (speedFactor)",
"operandID": 6,
"expressionID": 3489,
"expressionTypeID": null

这里需要注意的重要内容是 arg1arg2,和 operandIDoperandID 是精确定义 arg1arg2 实际上做什么的值。通常,它们是对其他表达式的引用(如本例所示),但它们也可以用于比较结构(例如: operandID = 38 表示它在以下比较中使用参数值: arg1 > arg2)或将多个表达式拼接在一起以形成多个修改器 (operandID = 17)。 operandID在确定您是否有可用数据时也很有用。举个例子,operandID = 22 将该表达式定义为指向属性,并且将会在 expressionAttributeID 字段中找到属性的ID。另一个例子,operandID = 24 告诉我们当前表达式定义了在 expressionValue 字段中找到的目标。

不幸的是,CCP不会发布有关什么操作数做什么的信息,但是您可以从客户端缓存中获取此数据。您还可以参考玩家制作的文档,例如我在本文开头引用的EOS EVE常量,可能会更有用/更有意义。

因此,既然您知道三个主要字段在做什么,其他字段又如何呢? 所有的 expression*ID 字段都具有可在最终修改器中使用的信息,无论它是一个属性,目标,还是过滤器(例如,某些效果被定义为仅应用于属于特定组的项目)。

expressionName 基本上是遍历表达式树时可能会发现的内容的概述。它是附加到当前表达式的所有其他表达式的汇编。

遍历表达式树:停滞缠绕光束 II

让我们回到我们的 decreaseTargetSpeed 效果,其 preExpression3489postExpression3491。因为它们实际上是相同的,现在我们将注意力集中到 preExpression 。我们将其称为我们的根树。在大多数表达式的根树下,参数将始终引用其他表达式——同样,您将需要检查 operandID 来确定它。如果这是一个可操作的操作数(一个开始定义修改器而不是逻辑构造的操作数),则 arg2 始终将是一个定义源属性的表达式,而 arg1 最终将定义其他所有内容。

我们首先加载两个参数,然后查看获得的数据:
image

我们可以看到 root.arg2.operandID = 22,它表示此表达式定义了一个属性。你可以看到属性的ID是 20 而且它的名字是 speedFactor。重复,一个可操作的根树中的 arg2 总是定义了源属性,因此我们已经有了一条关键属性。

剩下的 arg1 定义了其他所有内容。 root.arg1.operandID = 31 将该表达式指定为连接运算符和目标定义的表达式。作为联接表达式,我们可以将它们的值用作expressionID ,以将更多表达式安全地加载到 arg1arg2 中。加载这些数据显示:
image

在上图中,我折叠了 root.arg2 并且同时展开了 root.arg1 的两个参数。要非常小心——很容易迷失在树上。注意 root.arg1.arg1.operandID = 21,它在 expressionValue 字段中定义效果的运算符;在这个例子中是 PostPercent 。因此,现在我们既有源属性又有效果的运算符。

root.arg1.arg2.operandID = 12,它将表达式定义为连接目标和目标项目——我们需要的最后两个数据点。同样,使用参数中的值作为 expressionID 来加载接下来的两个表达式。

同样,我折叠了已经使用的参数,并展开了两个新参数。 随即您会看到树到此结束,因为没有更多参数了。

root.arg1.arg2.arg1.operandID = 24 ,它将表达式定义为在 expressionValue 中承载目标信息,在本例中为 Target。这是 CCP 对当前锁定和选定目标的定义。现在剩下的唯一的就是我们在修改的目标属性了…

root.arg1.arg2.arg2.operandID = 22 将表达式定义为一个属性,它的ID在 expressionAttributeID 中定义,在这个例子中是 37:maxVelocity 。您可能想知道我们如何知道这是目标属性,而不是表达式中可能使用的另一个属性。 答案是父表达式,其操作数将其定义为连接目标和目标属性。

就是这样! 我们遍历了 preExpression 树并编译了所需信息的列表:

  1. 源属性: 20:speedFactor
  2. 目标: Target
  3. 目标属性: 37:maxVelocity
  4. 操作 / 修改器: PostPercent

遍历表达式树:护盾回充增量放大器

这个示例将利用 shieldBoostAmplifier 效果来演示过滤器,并希望阐明“可操作的”表达式的含义,因为前面的示例有点模糊。
image

从这里我们可以看到 root.operandID = 17 ,它被定义为一个拼接。一个拼接将多个修改器简单的链接在一起;一种使用一个效果表达不同的修改器的方式。当您想为一个效果附加多个修改器时,请考虑一种情况。损伤控制装备进入了我的思绪。它有11个拼接表达式,总共12个修改器(每个伤害类型每个血量类型都有一个修改器)。这也不是一个“可操作的”表达式,因为我们尚未收集创建修改器所需的数据。因此, arg2 并不指向源属性,而是指向一个全新的表达式树。arg1 也是如此。尽管参数的使用取决于所使用的操作数,但是在这种特定情况下, arg1arg2 都应当视为根表达式,并以此进行评估。如果我们展开 arg1 ,我们会看到 root.arg1.operandIS = 9 ,它被定义为使用技能过滤器将某些属性应用于某个项目。
image

从这里开始,我们与前面的示例相同。 因此,我仅作总结: root.arg1.arg2 将定义源属性。 root.arg1.arg1 展开以定义操作(root.arg1.arg1.arg1)和目标(root.arg1.arg1.arg2)。

在目标表达式中,root.arg1.arg1.arg2.arg2 定义目标属性,而 root.arg1.arg1.arg2.arg1 定义目标本身。 这是我们获得一些新信息的地方。

您可以看到我们有一个新的操作: root.arg1.arg1.arg2.arg1.operandID = 49 将此表达式定义为加入目标(arg1)和技能要求定义(arg2)。这两个定义告诉我们要查看我们自己船上的装备(目标: CurrentShip),并将效果应用到需要 Capital Shield Operation技能的装备。

但是,我们都知道 护盾回充增量放大器 II 不仅适用于旗舰级护盾回充增量器。这就是为什么有一个拼接的原因: root.arg2 使用不同的技能定义了相同的修饰符:护盾操作,它将效果应用于游戏中的所有其他护盾回充增量器。

  • 修改器1
    1. 源属性: 548:shieldBoostMultiplier
    2. 目标: Ship
    3. 目标属性: 68:shieldBonus
    4. 操作 / 修改器: PostPercent
    5. 过滤器: 技能 – 21802:Capital Shield Operation
  • 修改器2.
    1. 源属性: 548:shieldBoostMultiplier
    2. 目标: Ship
    3. 目标属性: 68:shieldBonus
    4. 操作 / 修改器: PostPercent
    5. 过滤器: 技能 – 3416:Shield Operation

使用修改器数据

您可以从表达式树中提取修饰符信息,然后将其用于 dogma 计算中。 例如,EOS仅在计算中使用编译的修改器信息。 建立修改器数据库后,就再也不会触及表达式树。 最终,由应用程序的开发人员来决定他们希望如何处理信息。

这里只是简单的例子。还有很多比这里讨论的要复杂的效果。重要的是要记住, operandID 是确定表达式要描述的东西的关键。

警告

在构建表达式树时有一些“陷阱”。有一些组过滤器似乎被歪曲了。还有, preExpressionspostExpressions 并不总是互为镜像,特别是对于有状态装备(修改游戏中舰船状态而不是属性的装备,例如修改你的装甲损坏量而不是你拥有的装甲量的装甲维修器)。

但是,这些示例应该足以启动您的开发,始终欢迎您扩展本文。

资源

  1. EOS – EVE 常量 很多信息关于 operandID和效果类别。
  2. EOS – EOS 常量 EOS 特定的常量,但是,它们为域(效果的目标)之类的事物提供了更多的上下文。
  3. EOS – 缓存生成器逻辑 这是用于弄清楚如何以编程方式应用此信息的重要资源。 它很复杂,但是很容易理解。
  4. dgmOperands: 从 EVE 客户端缓存获取的 JSON 文件,定义了 dgmOperands 表。