Skip to content
Ryan Holmes edited this page Aug 18, 2015 · 4 revisions

In this document, I will try my best to explain how effects are handled at the database level for EVE Online. I will be using EOS constants to reference things that are not published by CCP, namely operandIDs and their meaning, as well as the operators. These references can be found as constants in the EOS project: EVE and EOS constants.

Additionally, I may refer to attributes in the form of attrID:attrName for simplicity.

Introduction

Before we explain how expressions work, we need to first understand items and effects. Items are the building blocks of EVE - everything in the game is an item. The modules that you put on your ship, the charges you load into your guns, and your ship itself. Items can also be non-physical entities such as system-wide wormhole effects or the tactical destroyer mode. Everything in EVE that has some sort of attribute is an item, and these are defined in the invTypes table.

But having a bunch of items with attributes is useless without a way for them to affect one another. Thats where effects come in at. Effects are the building blocks of items and are what actually make an item do something. Suppose you fit a Webifier to your ship. When it's active, it applies its decreaseTargetSpeed effect to the currently locked target. When it's overloaded, it applies its overloadSelfRangeBonus effect to itself. Effects have a many-to-many relationship with items, where each effect may have be attached to many different items, and each item may have many different effects. Effects are defined in the dgmEffects table, whereas the item:effects relationship is defined with the dgmTypeEffects table.

But how do we know what an effect actually does? If you look at an effect entry in the database, it doesn't tell you much about the modifying information 1. To define what an effect does, CCP uses expressions. Each effect contains two references to expressions: preExpression and postExpression. The preExpression is used when an effect is applied (ie: a module is activated), whereas the postExpression is its mirror and used when the effect is removed. Expressions are stored in the dgmExpressions table and have a tree data structure, in which each expression can be made out of other expressions. Traversing this tree and extracting the information needed to produce an actionable effect is the goal of this document.

The Result: Modifier Information

The expression tree is simply a means to an end, to extract the data necessary to make a modifier. It helps to know what data you are looking for before actually looking for it, and this section should help with that before we dive into extracting the data from the expression tree.

Modifiers gives us the information necessary to apply an effect correctly. To apply an effect correctly, we need to know a few things (for simplicity, there are a couple other points I'm omitting):

  1. The source attribute
  2. The target
  3. The target attribute
  4. The operation / modifier

For example, we have a Stasis Web II and are applying it to our target. The web module itself has an attribute that determines the penalty it deals, and this attribute is 20:speedFactor with a value of -60.0. This is our source attribute (the source attribute is always attached to the item that is calling the effect in question, in this case the Web II). The target attribute is 37:maxVelocity with the target being CurrentTarget (which is CCP's definition of the currently locked and selected target). The modifier is PostPercent, which basically tells us apply our source attribute as a percentage to the target attribute.

We have an enemy Retribution currently locked and selected and it has with a max velocity of 348m/s. We convert our source attribute value, -60.0, to a percentage modifier (defined with PostPercent = val / 100 + 1)2 and we get 0.4. This is then multiplied with the target attribute to get the final value of 348 * 0.4 = 139.2m/s. And that's how effects work.

We will be revisiting the Stasis Webifier II in our traversal examples section.

dgmExpressions Entries Explained

Here is a typical entry in 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>

The important things to note here are arg1, arg2, and operandID. The operandID is the value that defines exactly what arg1 and arg2 is supposed to be or do. Usually, they are references to other expressions (like in this example), but they could also be used in comparison constructs (eg: operandID = 38 means that it uses the argument values in the following comparison: arg1 > arg2). The operandID is also useful in determining if you have workable data. For example, operandID = 22 defines this expression as pointing to an attribute, and to find the attribute ID in the expressionAttributeID field. As another example, operandID = 24 tells us that current expression defines the target which is found in the expressionValue field.

Unfortunately, CCP does not publish the information on what operand does what. This is when you would reference player-made documentation, such as the EOS EVE constants that I referred at the beginning of the document.

So now that you know what the three main fields do, what about the other ones? All the expression*ID fields have information that can be used in the final modifier, whether it be an attribute, target, or even a filter (for example, some effects are defined as being applied to only items under a certain group).

expressionName is basically an overview of what you could expect to find when you traverse the expression tree. It's a compilation of all the other expressions that are attached to the current one.

Traversing the Expression Tree

Example: Stasis Webifier II

Let's start back at our decreaseTargetSpeed effect, which has a preExpression of 3489 and a postExpression of 3491. As they are both practically the same, we'll focus on the preExpression for now. We will call this our root tree. Under the root tree of any expression, the arguments will always reference other expressions. Additionally, arg2 is always going be an expression that defines the source attribute, while arg1 will eventually define everything else.

We start by loading both arguments and look at the data we obtain:

root tree

We can see that root.arg2.operandID = 22, which designates that this expression is defining an attribute. You can see the attribute ID is 20 and the name is speedFactor. Again, arg2 of the root tree always defines the source attribute, so already we have 1 piece of critical information.

arg1 is left to define everything else. root.arg1.operandID = 31 designates this expression as one that joins the operator and target definitions. Being a joiner expression, we can safely load more expressions into arg1 and arg2, using their values as the expressionID. Loading that data up shows:

fs

In the above image, I collapsed root.arg2 and have expanded both arguments in root.arg1. Be very careful - it's easy to get lost in the tree. Looking at root.arg1.arg1.operandID = 21, which defines the operator of the effect in the expressionValue field; PostPercent in this case. So now we have both the source attribute and the operator of the effect.

root.arg1.arg2.operandID = 12, which defines the expression as joining the target and the target item - our last two data points needed for this effect. Again, load the next two expressions using the values in the arguments as the expressionID

faf

Again, I've collapsed the arguments that have already been used, and expanded the two new ones. Immediately you can see that the tree ends here, as there are no more arguments.

root.arg1.arg2.arg1.operandID = 24 which defines the expression as carrying the target information in expressionValue, in this case Target. This is CCP's definition of the currently locked and selected target. The only thing that is left is the target attribute that we are modifying...

root.arg1.arg2.arg2.operandID = 22 defines the expression as an attribute, the ID of which is in expressionAttributeID, in this case 37:maxVelocity. You may be wondering how we know this is the target attribute and not another attribute that might be used in the expression. The answer to this would be the parent expression, whose operand defines it as joining the target and target attribute.

And that's it! We have traversed the preExpression tree and compiled a list of the needed information:

  1. The source attribute: 20:speedFactor
  2. The target: Target
  3. The target attribute: 37:maxVelocity
  4. The operation / modifier: PostPercent

Using Modifier Data

One you extract the modifier information from the expression tree, you can use it in your dogma calculations. The difference in preExpression and postExpression does not seem to affect any calculations once you have the modifier information, and is probably only used by CCP. From what I've seen, the only difference is the root.operandID of the postExpression is defines as the reverse of the root.operandID of the preExpression.

EOS, for example, only used the compiled modifier information in its calculations. Once it's made its modifier database, it never touches the expression trees again. Ultimately it's up to the developer of the application to decide what they wish to do with the informaton.

This was a simple example. There are more complicated effects than this; ones that are filtered based on skills, one that are applied to certain groups of items, effects that have different scope (eg: ganglinks). It's important to remember that the operandID is the key to determining what exactly an expression is trying to portray.

Resources

  1. EOS - EVE constants Has a lot of information on operandIDs as well as effect categories
  2. EOS - EOS constants EOS-specific constants, however they provide a little more context for things like domains (the target of the effect).
  3. EOS - Cache Generator logic This is a great resource for figuring out how this information may be applied programatically. It's complex, but fairly easy to follow.

Footnotes

1 This is not completely the case. With the release of Tactical Destroyers, CCP has begun to implement a new system in which modifier information is stored in the modifierInfo column of the effect row. This makes it much easier to figure out what an effect does. However, at the time of this writing, this is only used for a very limited selection of effects, and the vast majority of effects still use the legacy Expression method.

2 Arithmetic for the various operations can be found here