[BUG/FIX] Why Shards don't increase magic damage (as coded)... and how to fix it

DISCLAIMER: I am not a developer for SD. I don't really know exactly how they're processing the calculation elements. I'm inferring this behavior from numerous experiments with settings, variable names, and spell definitions. It's entirely possible that my "why" is a little off on a few technical details. But the fix works. 

There are two critical things you need to know to see how the bug happened:

1. <Calculate/> elements in the xml (Better tutorial on that HERE)
These are used all over the place to figure out (for example) how much damage something does as a percentage of a stat.

I'm being loose with my terminology below. I know that the xml isn't actually creating/storing variables, obviously. All that talk is referring to whatever is "processing" the various calculation tags.
Calculate.InternalName = A "local" variable name you're creating to refer to the value we're storing. It's a little unclear where the value actually lives. I'm thinking of this more like a pointer or reference.
Calculate.ValueOwner = One of a few set strings that tell the processor where to go to get the values for variables in your expression. If a variable name doesn't belong to the specified ValueOwner, it appears to use "local" variables. This is usually left blank (for local) or set to CastingUnit or TargetUnit. It seems like there is a default setting for this property that isn't NULL. I'm guessing it's set differently in the Tactical combat vs. AutoResolve logic, and that's why we're seeing the different results.
Calculate.Expression = Defines an atomic or binary algebraic expression that appears to accept the operators *, /, +, and -. Either or both of the arguments can be variables (that both belong to the ValueOwner specified) or constants (double/float/int).

2. UnitStat_NumFireShards (Water/Air/Earth, also)
This value apparently has a default value of 1. This is important. When you build a new shrine, it's increased by 1. This means when you have 2 shrines, you have "3" NumFireShards. I'm guessing they did this so they could multiply by NumXXXShards without having to add one first.

So here's how it goes down in the xml:
(This is from the core Fireball spell definition)

Code: xml
  1.   &lt;GameModifier InternalName="FireballModifier"&gt;
  2.    &lt;ModType&gt;Unit&lt;/ModType&gt;
  3.    &lt;Attribute&gt;DefendableDamage&lt;/Attribute&gt;
  4.    &lt;Calculate InternalName="AttackerIntelligence" ValueOwner="CastingUnit"&gt;
  5.     &lt;Expression&gt;&lt;![CDATA[[UnitStat_Intelligence]]]&gt;&lt;/Expression&gt;
  6.    &lt;/Calculate&gt;
  7.    &lt;Calculate InternalName="Value" ValueOwner="CastingUnit"&gt;
  8.     &lt;Expression&gt;&lt;![CDATA[[AttackerIntelligence] * -1.0]]&gt;&lt;/Expression&gt;
  9.    &lt;/Calculate&gt;
  10.       &lt;Calculate InternalName="Value"&gt;
  11.       &lt;Expression&gt;&lt;![CDATA[[Value] * [UnitStat_NumFireShards]]]&gt;&lt;/Expression&gt;
  12.       &lt;/Calculate&gt;
  13.   &lt;/GameModifier&gt;


So what happens here?
Calculate #1: We assign the UnitStat_Intelligence value from "CastingUnit" to a new local variable named AttackerIntelligence.
Calculate #2
: We assign negative the calculated AttackerIntelligence value (from step 1) to "Value." This is the first place where things get a little iffy. Where is Value being set? "Locally" as the value of the Expression that's being parsed, or to the property of the same name on "CastingUnit?" Honestly, it's just a tad sloppy.
Calculate #3: This is where it's definitely off course. In this expression, they reference UnitStat_NumFireShards. But... the ValueOwner is blank, meaning that it's unclear where this value is actually coming from. It doesn't completely bomb, though, because the default value for NumXXXShards is 1, remember? So even when it grabs the wrong source (which is seems to do in tactical combat), it doesn't cause an obvious "Oh look it's broken!" effect.

And how do we fix it?
Pretty easily, actually. Just clean up the unclear references:

Code: xml
  1.   &lt;GameModifier InternalName="FireballModifier"&gt;
  2.    &lt;ModType&gt;Unit&lt;/ModType&gt;
  3.    &lt;Attribute&gt;DefendableDamage&lt;/Attribute&gt;
  4.    &lt;Calculate InternalName="EffectStrength" ValueOwner="CastingUnit"&gt;
  5.     &lt;Expression&gt;&lt;![CDATA[[UnitStat_Intelligence]*[UnitStat_NumFireShards]]]&gt;&lt;/Expression&gt;
  6.    &lt;/Calculate&gt;
  7.    &lt;Calculate InternalName="CalculatedDamage"&gt;
  8.     &lt;Expression&gt;&lt;![CDATA[[EffectStrength] * -1.0]]&gt;&lt;/Expression&gt;
  9.    &lt;/Calculate&gt;
  10.    &lt;Calculate InternalName="Value"&gt;
  11.    &lt;Expression&gt;&lt;![CDATA[[CalculatedDamage]]]&gt;&lt;/Expression&gt;
  12.    &lt;/Calculate&gt;
  13.   &lt;/GameModifier&gt;


Calculate #1: We assign the EffectStrength as Int*NumXShards. Both of these values belong to CastingUnit, so it works great.
Calculate #2: We multiply the damage by -1 to make sure it hurts the target. This could have been rolled into step 3, but it's better to keep it alone where we can tweak the damage multiplier by itself.
Calculate #3: Just assigns the calculated damage to the "Value" element of the modifier.

Gnilbert

30,422 views 30 replies
Reply #1 Top

I really hope a dev sees this and puts it in the next update.

k2  

Reply #2 Top

Pretty sure Boggiebac already was looking into this, and also looking into changing the damage variables for spells.

Reply #3 Top

Great that you've figured this out Gnilbert :-). I'll be fixing it tomorrow if a patch isn't out by playtime.

Gnilbert can has karma!

Reply #4 Top

Quoting DRavisher, reply 3
Great that you've figured this out Gnilbert . I'll be fixing it tomorrow if a patch isn't out by playtime.

Gnilbert can has karma!

Thanks! If you want more examples, I've included the fixes (for my abilities) in a simple mod I've posted about HERE.

Gnilbert

Reply #5 Top

Nice job! I couldn't figure this out. Damn, I wish gamemodifiers didn't stack at the moment. Otherwise I could fix this for all the basic spells.

Reply #6 Top

I got all the damage spells working (and heal!). The morale spells seem to require a bit more. I'll dig into that tomorrow. Here's the combat spells for now (mostly untested).

http://thedyinggrounds.com/Elemental/HF106_spell_Shardproof.xml

Combat: Mind Blast, Arcane Strike
Water: Stab of ice, Blizzard
Earth: Hurl Boulder
Restoration: Heal
Fire: Fireball, MorrigansSpite, Flamedart, Melting Touch, Infernal
Air: Lightning Strike, Chain Lightning, Gale Winds

Strangely, spells don't seem to suffer from doubling issues. Maybe because the gamemodifiers have internalnames?

Reply #7 Top

Quoting Heavenfall, reply 6
Strangely, spells don't seem to suffer from doubling issues. Maybe because the gamemodifiers have internalnames?

I imagine that would be the case. Fixing the stacking GameModifiers is likely going to require them all to be given a unique ID so they can be overwritten properly. I'd hate to be that person... :'(

Also, nice work on fixing the spells. Oddly, Auto Resolve battles seemed to calculate the damage just fine before... or at least I always seemed to get a helluva lot more damage out of spells in Auto Resolve than in manual battle. Who knows...

Reply #9 Top

Quoting Lyonsfl, reply 8
Just what happens when you have 0 shards because 0 times * anything equals 0

thanks

Good point - when I said the "default" value was 1, I really meant, "By default, when you have 0 shards, the value of NumFireShards is actually 1." When you have 1 shard, it's 2, etc. That way, they can safely multiply by "NumXShards" even when you don't have any without zeroing everything out. It does make it a bit counterintuitive though.

Gnilbert

Reply #10 Top

Sadly, shards are so rare I had not even noticed.   I swear I discover virtually every type of shard OTHER than the type I am seeking.  

(in my opinion: there should be a rich vs. not rich magic option on world generation like in MoM how I could choose how strong the nodes were)

Reply #11 Top

There is one thing I don't undestand.

Why with original code, maximum damage for spell is calculated correctly in spell info window during tactical combat. For example, Lightning Strike with 1 Air Shard and cast with Intelligence of 15, will show 30 as maximum damage. While actual spell will do up 15 (to enemies with 0 defense).

Reply #12 Top

Anyway, I found that much simpler edit is necessary to make spells work in combat.

Just add ValueOwner="CastingUnit" attribute to 3rd caluculation that includes [UnitStat_NumIceShards], and all is fixed. No need to "reformat" everything (which just makes it easier to add some typo while doing edits).

 

As for why calculation works without ValueOwner for spell info screen, I have no idea.

 

P.S.

Are Burning Blade and Shield of Fire affected by this too?

They don't deal damage, but add buffs based on number of shards. They are both tactical spells, and use exactly same type of calculation as those damaging spells.

 

P.P.S.

For Mind Blast, modification may need to be a bit more complex, since I'm not sure if value that gets calculated gets owned by ValueOwner.

Which is important here, since in 2nd calculation calculates Value that has "TargetUnit" as owner, and in 3rd calculation needs "CastingUnit" as owner to get shards working.

Reply #13 Top

Just tested Mind Blast, with same modification (ValueOwner="CastingUnit" for 3rd calculation). And it worked properly. So it seems that ValueOwner is only used to define owner for unit attributes inside calculation (is it caster stat, is it target stat, or similar), it doesn't define ownership of currently calculated value, or other values calculated elsewhere.

So calculated value, regardless of owner used in one calculation, can be used in next calculation without problem irregardless who is owner of that next calculation.

 

P.S.

Heavenfall's mod calculated Mind Blast wrongly. It is supposed to use target Intelligence, not casters.

Reply #14 Top

I did something wrong?! To the fix-mobile!

Reply #15 Top

I'll wait for the OFFICAL FIX since yours could be flawed and or screw up the game later on down the line causing things like slowing down of game or CTD's or graphics tears....things like that. ;)

Reply #16 Top

Btw, I started experimenting with Flame Blade, and noticed that calculation doesn't match spell description.

It says it increases attack by 2, but instead it multiplies unit attack rating by two, and then that results mutiplies result by number of fire shards.

If shards worked correctly, spell would be broooken. Attack 10 units with no shards = 20, attack 10 unit with 3 shards = 80.

 

On the other hand, in testing with stock version of spell (just level changed to 1, so I could test it in new game early), units do not get increase to attack at all. So something else is wrong there, not just shard bonus.

Reply #17 Top

I'm working on the rest of the spells now.

The reason those spells (fire shield, burning blade) aren't working is because I think they were intentionally turned off. They WANTED the spells to add defense or attack for the rest of the tactical combat, but there's no way of achieving that (as far as I can see). If you don't add a <duration> to the gamemodifier, it will last forever (including outside tactical combat).

My shield of fire adds caster intelligence * fire shards to target defense for 2 turns. It's a level 9 spell, so I don't think people will be using it anyway (lol).

Edit: Doh... I could just add a very large duration number, and it expires after tactical combat.

Edit2: Gah... doubling errors... not gonna add those.

Edit3: No wait, I can cancel them out! mohaha

Reply #18 Top

Quoting rossanderson48, reply 15
I'll wait for the OFFICAL FIX since yours could be flawed and or screw up the game later on down the line causing things like slowing down of game or CTD's or graphics tears....things like that.

 

 

Plus if it doesn't come from mount Zion it can't be good.

Reply #19 Top

About flame blade/flame shield:

What tag does define what stat they increase, since I only see reference to attack or defense in those variables defined in inbetween calculations?

They both calculate AdjustUnitStat, but what stat exactly? It's pretty ambiguous.

Reply #20 Top

It's not in the core file, but it's <StrVal>Unit_Attack</StrVal> and Unit_Defense.

However, another major problem with these two spells is that they can be cast on the same target several times.

Edit: Sorry, that's UnitStat_Attack obviously. And UnitStat_Defense

Reply #21 Top

Yes, I just realized that without <StrVal>UnitStat_Attack</StrVal> or similar defense tag, these spells don't do anything (by looking at Berserker spell, that has those tags set up properly).

But when that is fixed, effect stays after battle, and stacks as you say. And it actually triples attack/defense not doubles (with no shards), at least for Str10 unit, since it actually adds current attack value to the base attack of the target (so strong units will get even bigger effect).

Reply #22 Top

You know what's funny.

That Bravery spell uses code very similar to one suggested in first post. And I bet it's the only spell for which shards bonuses work correctly:

 

Code: xml
  1.     &lt;!-- ************* --&gt;
  2.     &lt;!-- ** Bravery ** --&gt;
  3.     &lt;!-- ************* --&gt;
  4.     &lt;SpellDef InternalName="Bravery"&gt;
  5.         &lt;DisplayName&gt;Bravery&lt;/DisplayName&gt;
  6.         &lt;Description&gt;Target unit's morale is equal to caster's intelligence.  The effect is increased for every shard controlled.&lt;/Description&gt;
  7.         &lt;Image&gt;EarthCrystal_Medallion.png&lt;/Image&gt;
  8.         &lt;IconFG&gt;Bravery.png&lt;/IconFG&gt;
  9.         &lt;IconColor&gt;245,245,245&lt;/IconColor&gt;
  10.         &lt;SpellType&gt;Tactical&lt;/SpellType&gt;
  11.         &lt;SpellClass&gt;Defensive&lt;/SpellClass&gt;
  12.         &lt;SpellTargetType&gt;FriendlyUnit&lt;/SpellTargetType&gt;
  13.         &lt;SoundFX&gt;Spell_Heal&lt;/SoundFX&gt;
  14.         &lt;Range&gt;-1&lt;/Range&gt;
  15.         &lt;ManaCost&gt;4&lt;/ManaCost&gt;
  16.         &lt;SpellLevel&gt;4&lt;/SpellLevel&gt;
  17.         &lt;SpellDefEffect&gt;
  18.             &lt;EffectName&gt;Bravery&lt;/EffectName&gt;
  19.             &lt;LocalPosition&gt;0,50,0&lt;/LocalPosition&gt;
  20.             &lt;EffectScale&gt;1.5&lt;/EffectScale&gt;
  21.             &lt;EffectDelay&gt;0.0&lt;/EffectDelay&gt;
  22.             &lt;SnapToTerrain&gt;1&lt;/SnapToTerrain&gt;
  23.         &lt;/SpellDefEffect&gt;
  24.         &lt;GameModifier InternalName="AdjustUnitStat"&gt;
  25.             &lt;ModType&gt;Unit&lt;/ModType&gt;
  26.             &lt;Attribute&gt;AdjustUnitStat&lt;/Attribute&gt;
  27.             &lt;StrVal&gt;UnitStat_Morale&lt;/StrVal&gt;
  28.             &lt;Calculate InternalName="CasterIntelligence" ValueOwner="CastingUnit"&gt;
  29.                 &lt;Expression&gt;&lt;![CDATA[[UnitStat_Intelligence] * 1.0]]&gt;&lt;/Expression&gt;
  30.             &lt;/Calculate&gt;
  31.       &lt;Calculate InternalName="CasterIntelligence" ValueOwner="CastingUnit"&gt;
  32.         &lt;Expression&gt;&lt;![CDATA[[UnitStat_Intelligence] * [UnitStat_NumAllShards]]]&gt;&lt;/Expression&gt;
  33.       &lt;/Calculate&gt;
  34.             &lt;Calculate InternalName="TargetMorale" ValueOwner="TargetUnit"&gt;
  35.                 &lt;Expression&gt;&lt;![CDATA[[UnitStat_Morale]]]&gt;&lt;/Expression&gt;
  36.             &lt;/Calculate&gt;
  37.             &lt;Calculate InternalName="Value"&gt;
  38.                 &lt;Expression&gt;&lt;![CDATA[[TargetMorale] * [CasterIntelligence]]]&gt;&lt;/Expression&gt;
  39.             &lt;/Calculate&gt;
  40.             &lt;Duration&gt;3&lt;/Duration&gt;
  41.         &lt;/GameModifier&gt;
  42.     &lt;/SpellDef&gt;

 

^How to remove black lines from code section in this forum?

Reply #23 Top

I noticed the same thing, however Bravery still didn't work (its attribute isn't accurate) :D If you want to look at a core spell that actually can change morale, look at Rallying Cry or Fear

I fixed everything except Shield of Fire and Burning Blade (I just can't get them to work right without being able to overwrite core gamemodifiers)

http://thedyinggrounds.com/Elemental/HF106_spell_Shardproof.xml

This is a non-module mod, I just didn't want to put all spells in their own files. I used Gnillbert's shard fix and applied it to all spells, so all spells are now properly affected by the number of shards you control in tactical combat.
Drain Life - now does damage equal to caster's intelligence (no drain effect). The unique quality of this spell is that it stacks with all shards, instead of just one type like other damage spells.
Crush Spirit - fixed
Rallying Cry - now works with shards
Fear - now works with shards
Bravery - fixed, removed duration
Removed strings mentioning Ice shards, renamed Water shards after Beta

Also added it to the bugfix mod.

 

Reply #24 Top

Yes, I just looked into two Ruin school spells (Crush Spirit and Drain Life), and both are non-functonal.

Crush Spirit uses AdjustUnitStat attribute to adjust morale (which doesn't work). It should use IncreaseMorale attribute like fear.

And Drain Life doesn't do a thing. Plus, both visual spell effects (drain and heal) are applied to target.

 

 

P.S.

Isn't your version of Drain life a bit overpowered. Full Int damage over old 1/8 Int damage. I know it doesn't heal now, but still... it uses all shards for bonus damage, that's got count for something.

 

P.P.S.

For some reason, Heal uses shard bonus, even without any modification.

Reply #25 Top

I upped the mana cost to 3. It also requires level 3 spell-level unlike other basic single-target spells.

It's the only spell empire gets that is even half-okay. Kingdom gets heal (spell-level 1) that heals a target for 1/3 int * all shards, and strategical restoration that heals every unit in an army for 20 health.

Meh. Maybe I'll reduce the range on drain life to 2.