To really make you trust me, let me throw this out there:
I don't know exactly what the code that's processing the Calculate elements is doing. As in, I don't know where it's storing its variables, if they're on the heap or the stack, etc. I am pretty confident about the inputs and outputs of the black box, though, so I'm going to try and keep this post at that level. If it sounds like I'm hand-waving some of the technical "why does it do that," well - I am.
Calculate elements show up everywhere in the XML in GameModifier elements in spell definitions. They describe how the game should determine the Value attribute of the GameModifier as some combination of stats from the Caster and Target at the time the spell is cast. Usually, it's pretty straightforward - copy a value or multiply a value by .75. But if you dig into the files a bit, you'll find some more complicated scenarios.
Diagram of the Calculate tag:
Code: xml
- <Calculate InternalName="EffectStrength" ValueOwner="CastingUnit">
- <Expression><![CDATA[[UnitStat_Intelligence]*[UnitStat_NumFireShards]]]></Expression>
- </Calculate>
Calculate.InternalName. Like almost everywhere else this is set, it gives a name that you will use to refer to this value other places in your xml. It appears that the name you choose is only "in scope" in the GameModifier element the Calculate lives in. When you don't specify a "ValueOwner," and set this to "Value," it uses the result of this calculation as the Value for the containing GameModifier.
Calculate.Expression. This weirdo attribute includes a CDATA section that the game engine uses to determine how to carry out your calculation. It can take one of a couple of forms (notice that you can't add three numbers at once, for example):
1: [
VariableName] 2: [
VariableName1] operator [
VariableName2]
3: [
VariableName] operator
Constant
4:
Constant operator
[
VariableName]
* Oddly, when I've tried with no variables at all, it's actually crashed the game. Now, why
you would want to do that is actually a bit of a mystery.It appears to accept the operators: *, /, +, and -
The constants are Real values.
Calculate.ValueOwner. This one is crucial - be very careful to have this set correctly! Three common values are: Nothing at all,
CastingUnit and
TargetUnit. When the value is set, the engine tries to get the value for the variable names from the "owner" before looking for a value from an InternalName (like the result of a previous).
Let's say you have the variable [UnitStat_Attack] in an expression. If you set ValueOwner=CastingUnit, then when the expression is processed, [UnitStat_Attack] is replaced with the Attack strength of the unit that's casting the spell. If you'd used ValueOwner=TargetUnit, it would instead use the attack value of the unit the spell was being cast
on.
What happens when you don't set ValueOwner? That's a problem - it's not really clear. It
seems like the value isn't "none." Apparently, there is always an inferred owner that's checked for the values first. Because of this,
never leave out ValueOwner when you're referring to a variable on a Unit (or something else) in your expression. You know the whole "Shards aren't increasing spell damage correctly" bug? That's what happened (read more on that
HERE). Also, be careful when assigning InternalName. You don't want it to conflict with an actual property of something else.
So let's do a few examples to help it sink in.
Example 1Let's say you're creating a spell that does 5x the caster's Intelligence as damage.
Here's what you need to do: Grab the CastingUnit's intelligence score, multiply it by -5 (since you're taking health
away), and then assign that as the "Value" for the modifier. Here's the xml:
Code: xml
- <GameModifier InternalName="ApplyDamage">
- <ModType>Unit</ModType>
- <Attribute>DefendableDamage</Attribute>
- <Calculate InternalName="CalculatedDamage" ValueOwner="CastingUnit">
- <Expression><![CDATA[[UnitStat_Intelligence] * -5.0]]></Expression>
- </Calculate>
- <Calculate InternalName="Value">
- <Expression><![CDATA[[CalculatedDamage]]]></Expression>
- </Calculate>
- </GameModifier>
Here's how it works: The first calculate gets the value of UnitStat_Intelligence from CastingUnit (the one who's casting the spell or using the ability). It mutiplies this value by -5, then stores it in a variable named "CalculatedDamage" that lives... somewhere. Honestly, who cares where it lives - you've got it's name, and that's good enough.
Now, the CalculatedDamage variable is used as the value to assign to "Value." And that's the part that says, "use this value for my GameModifer please!"
Example 2
Let's try another example. In this case, I want the spell to decrease the target's Strength by 50%.
Code: xml
- <GameModifier InternalName="ApplyWeaken">
- <ModType>Unit</ModType>
- <Attribute>AdjustUnitStat</Attribute>
- <StrVal>UnitStat_Strength</StrVal>
- <Calculate InternalName="StrengthPenalty" ValueOwner="TargetUnit">
- <Expression><![CDATA[[UnitStat_Strength] * -0.50]]></Expression>
- </Calculate>
- <Calculate InternalName="Value">
- <Expression><![CDATA[[StrengthPenalty]]]></Expression>
- </Calculate>
- </GameModifier>
So here, I grab the target's current strength value, multiply by -0.5 to both half it and make it negative (so it's a penalty), and then I assign it as the value for the GameModifier.
Example 3For the final example, let's get a little fancier. This spell heals a unit like this: Heals 30% of the damage the unit has taken +2% per point of caster's Intelligence. Yikes. Who the heck would write such a thing?
Here's the code.
Code: xml
- <GameModifier InternalName="ApplyWeaken">
- <ModType>Unit</ModType>
- <Attribute>CurHealth</Attribute>
-
- <Calculate InternalName="ActualTargetDamage" ValueOwner="TargetUnit">
- <Expression><![CDATA[[UnitStat_HitPoints] - [CurHealth]]]></Expression>
- </Calculate>
- <Calculate InternalName="CasterIntBonus" ValueOwner="CastingUnit">
- <Expression><![CDATA[[UnitStat_Intelligence] * 0.02]]></Expression>
- </Calculate>
- <Calculate InternalName="HealingFactor">
- <Expression><![CDATA[[CasterIntBonus] + 0.30]]></Expression>
- </Calculate>
- <Calculate InternalName="Value">
- <Expression><![CDATA[[HealingFactor] * [ActualTargetDamage]]]></Expression>
- </Calculate>
- </GameModifier>
Happy modding,
Gnilbert