Jump to content

IWD2: Modders guide to Spells


Recommended Posts

I was messing with creating the "missing" 3 spells (Bear's Endurance, Fox's Cunning and Owl's Wisdom) that should accompany Bull's Strength, Cat's Grace and Eagle's Splendor and learned a few things that could help any IWD2 modders out there...

 

Miscellaneous

 

For Arcane casters there are 24 slots for spells at character creation (6x4) and in the spellbook (3x8), yet 30 slots at levelling up (6x5). Here are the list of slots, and how many slots are used up:

1 - 19; 2 - 25; 3 - 21; 4 - 26; 5 - 22; 6 - 26; 7 - 17; 8 - 14; 9 - 9

 

Without creating some extreme items, an arcane caster is never going to to be able to cast more than 24 spells, so we can use 30 as a limit (except for level 1) for new spells.

 

LIST****.2DA files contain the references to spells used in the game. listmage and listdomn are in alphabetical order - adding a spell to the end causes the spell at character creation and levelling up to appear at the end.

 

RACESPAB.2DA contains a list of 2DA file references for each race. Each of these 2DA files contains a list of Innate Spells the race has.

 

SPELL.IDS and SPLSTATE.IDS are used for spell effects and scripts. If you want to set "Immunity From" type effects (for existing spells, or your own to prevent multiple casting), you will need to add lines here.

In spell.ids, each entry starts with 4 digits. The first digit is 1, 2 or 3, based upon its use:

1 = divine caster; 2 = arcane caster; 3 = Innate/trap

The last three digits are equivalent to the last digits of an spl file, e.g. SPWI101 (Wizard's Grease) entry is 2101 in spell.ids

 

Portrait Icons

 

For this, you need 4 files - states.bam, states2.bam, statdesc.2da, effects.src. Let's look at each one...

 

states.bam

This file contains the portrait icons displayed inside the small portrait of the main GUI. The bam file has 255 sequences, starting at 65 and ending at 210. There are 141 frames in total. All empty sequences use frame 37.

To add a new icon, create a 10x10 image (with the "shadow" (pinkish) background colour), insert a frame (142+) and replace frame 37 with your new one.

 

states2.bam

This file contains the "states" in the Character record. It is exactly the same as states.bam with two exceptions: images are 12x13 pixels and use the "blank" (blue/green) background colour.

 

statdesc.2da

This file contains the string references that appears next to the icon in the Character record. You will probably need dialog.tlk changed if you add new spells.

 

effects.src

This is not really needed, but you can add your descriptions to effects 142 and 169.

 

Casting Time

 

The values go from 1 to 10. There are no spell descriptions with a casting time of 10. Why? 10 equals a round, or 7 seconds. So a casting time of 1 equals 0.7 real time seconds.

 

Spell Duration

 

This is what screwed me up at first - and there are still a few things I don't know about...

 

The 3 spells I was playing with would last 10 minutes/round. Here are a few fields in an SPL file that you need to look at (0xnn describes the offset in the spl file):

 

Flags (0x18) = Flag 14: Simplified Duration

I have no idea exactly what this is, but it seems to be used for most "n rounds/minutes/hours per level" spells (oddly though, Bull's Strength does NOT). Any ideas what this really does would be welcome.

 

Spell Duration - rounds / level (0x72)

For a duration linked with the caster level.

 

Spell Duration - rounds (0x73)

For a constant duration.

 

EFFECT extended header - Duration (0x0e)

This seems to be set in seconds, where 7 equals a round.

 

I saw the whole duration calculation being pretty screwed at first, but the first thing you have to learn is that "minutes" and "hours" are NOT real time (check existing spell descriptions)!

Now, this is where it gets crazy...

 

I picked 4 spells that last 10 minutes/level. All have simplified duration and have 10 at 0x72. So how can 10 rounds/level equal 10 minutes/level? It got odder from there. Here are the four spells and the "Duration" values in their effects extended header:

Invisibility = 1680

Invisibility Sphere = 3000

Protection From Arrows = 350

Symbol of Pain = 770

 

I am at a total loss as to why 4 spells that last the same amount of time have vastly different extended header effect durations. Are they really 10 minutes/level? Does Simplified Duration negate the need for this extended header duration? Or do these durations only effect enemies?

 

Crazier still, let us look at 3 priest spells: Bless, Bane and Faerie Fire. All are supposed to last 1 minute/level.

However, Bless and Bane have 10 at 0x72 (same as 10 minutes/level!), while Faerie Fire has 3. All "Duration" values in the extended header are 0.

 

Remember minutes/hours is NOT real time? Well, I decided to test in-game some spells. The times are approximate, give or take 1 or 2 real time seconds. The following are all described as lasting 1 minute/round:

Bless (level 1, Battleguard) = 1:10

Bless (level 1, Battleguard's Domain spell) = 2:23

Blur (level 3, Illusionist) = 1:02

Blur (level 4, Illusionist) = 1:23

Blur (scroll - cast at level 6) = 2:05

Protection From Evil (level 1, Watcher of Helm) = 0:24

Protection From Evil (level 1, Watcher of Helm's Domain spell) = 0:41

 

However, Bless has 10 at 0x72 (already said) while Blur and Protection From Evil (priest) have 3 at 0x72 (like Faerie Fire).

 

Let us now look at Mage Armor. This was not fun to time, as it is supposed to last 1 hour/level. In fact it lasts about 5:48. What is interesting, is that at 0x72, the value is 50. Why is it interesting? Well, It is supposed to last 5 times longer than Bless - and it does! Ok, not so interesting, but here are the results (based on my timings being slightly inaccurate):

 

One "minute" lasts 3 rounds, or 21 seconds.

Ten "minutes" lasts 10 rounds, or 70 seconds.

One "hour" lasts 50 rounds, or 5 minutes 50 seconds (350 seconds).

Eight "hours" lasts 2400 (e.g. Iron Skins) or 3000 (e.g. Blindness) seconds.

One "day" lasts 9000 seconds (2.5 hours as opposed to 2 hours the manual specifies).

 

Unfortunately, it also shows that the spell description for Bless (and therefore Bane) is wrong. So how many other spells are described with incorrect durations?

Well, you can see in the above results that either Iron Skins or Blindness is wrong for a start...

 

Domain Spells

 

Looking at "Bless" from my timings above, I thought "domain spells last twice as long!". So I immediately went to test it on Executioner's Eyes (20 rounds instead of 10 - yes!). This spell is "constant", so I wasn't surprised that it lasted the "correct" amount of time - 10 rounds, or 70 seconds. So I wanted to know if this domain/timing anomoly affected ALL "variable" length Domain spells. As usual, I wasted precious seconds of my life timing the following:

Bless (level 1, Battleguard) = 1:10

Bless (level 1, Battleguard's Domain spell) = 2:23

Bless (level 1, Demarch) = 1:10

Bless (level 3, Battleguard) = 2:20

Bless (level 3, Battleguard's Domain spell) = 3:28

Death Armour (level 3, Necromancer) = 0:21

Death Armour (level 3, Stormlord's Domain spell) = 0:28

Minor Mirror Image (level 1, Illusionist) = 0:28

Minor Mirror Image (level 1, Demarch's Domain spell) = 0:35

Minor Mirror Image (level 2, Illusionist) = 0:35

Minor Mirror Image (level 2, Demarch's Domain spell) = 0:42

Sanctuary (level 1, Silverstar) = 0:07

Sanctuary (level 1, Silverstar's Domain spell) = 0:14

 

I did test a couple of other spells and it appears that Domain spells are cast as though they are 1 level higher. What screws this up is Recitation:

Recitation (level 7, Battleguard) = 0:48

Recitation (level 7, Battleguard's Domain spell) = 0:48

 

The only thing I can think of here is that this spell has no effects as such, but uses the spells EFFR1 and EFFR2 instead (these are what cause the Recitation effect).

Link to comment

Either/Or Spells

 

Now, I am no expert at modding IWD2, but I have been messing about with these types of spells. Simply put, "either/or" spells are ones that have one effect for a failed saving throw and a different effect for a successful saving throw. Most IWD2 spells like this are death/nd6 damage. This is easy to implement, because if the target does not die, it gets injured.

 

My experimentation was with a spell called "Spirit Worm". I have left out details that are not relevant for the requirements to this type of spell:

 

Spirit Worm

S/W 1

Duration: 1 round/level

Range: Touch

Save: Fortitude (special)

(automatic hit)

 

If the target fails its saving throw, it loses 1 temporary point of Constitution (maximum of 5) while the spell lasts. If the target passes its saving throw, it takes 1d2 magic damage per round (maximum 5d2).

 

There are a few problems here, for creating this type of spell:

1) There is no simple way of causing a different effect based upon the success of a saving throw.

2) Unlike BG2, the invisible creature trick is not valid - because there is no "LastSummonerOf" in OBJECT.IDS.

3) Enemy targetting in a script. This is more difficult than you think - there is no item in OBJECT.IDS that selects the correct object. SpellTarget only works if the spell is cast from within a script!

 

So here is my solution. Any other (better) ideas would be welcome...

 

There are a few things we need:

1) 2 "somethings" that say: a - we have cast the spell; b - has the saving throw been made?

2) Scripts - we can control the effects of the spell for "or" here (when we can't in an spl file itself)

 

1) Get the file resources

 

The following files are those that I changed:

SPLSTATE.IDS

TRIGGER.IDS

ACTION.IDS

INSTANT.IDS - not sure this is actually used, but better to be safe!

 

I also created the following:

INTVALS.IDS - contains "255 SPIRITWORM_DAM"

TIMERS.IDS - contains "251 ROUND" - for more readable timer ids

 

2) Create the spell

 

The Constitution damage effect is applied in here using the delay timing mode. Obviously it has a Fortitude save.

 

3) Update SPLSTATE.IDS

 

We include SPIRIT_WORMGEN and SPIRIT_WORMSAVE to the file and add these effects "Set State" to the spell. SPIRIT_WORMSAVE has the Fortitude saving throw and lasts 2 rounds.

This is the way of targetting the enemy.

 

4) Create a supplementary spell

 

This spell, called "pe_swd" causes 1d2 magic damage.

 

5) THE SCRIPT

 

PART 1 - SPELL SETUP SCRIPT

 

I found that making 7 script files kept each block of code reasonably managable. The first script file (SPIRITW.BCS) contained the following code:

 

// Find spell target - autoset LastMarkedObject

IF
See(TenthNearestEnemyOf(Myself),0)
CheckSpellState(TenthNearestEnemyOf(Myself),SPIRIT_WORMGEN)
False()
THEN
RESPONSE #100
END
.
. (9th-2nd Nearest enemies in this block, but not displayed here)
.
IF
See(NearestEnemyOf(Myself),0)
CheckSpellState(NearestEnemyOf(Myself),SPIRIT_WORMGEN)
False()
THEN
RESPONSE #100
END

// Set MyTarget from LastMarkedObject

IF
CheckSpellState(LastMarkedObject,SPIRIT_WORMSAVE)
InternalLT(LastMarkedObject,4,1)
THEN
RESPONSE #100
	ActionOverride(Myself,SetInternal(MyTarget,4,1)
	ChangeCurrentScript("")
END

IF
!CheckSpellState(LastMarkedObject,SPIRIT_WORMSAVE)
InternalLT(LastMarkedObject,4,1)
THEN
RESPONSE #100
	ActionOverride(Myself,SetMyTarget(LastMarkedObject))
	ActionOverride(Myself,SetInternal(MyTarget,4,SPIRITWORM_DMG))
END


// Go to new script, depending on caster level

IF
InternalGT(MyTarget,4,0)
Or(2)
	LevelInClass(Myself,1,SORCERER)
	LevelInClass(Myself,1,WIZARD)
THEN
RESPONSE #100
	ChangeCurrentScript("spiritw1")
END

IF
InternalGT(MyTarget,4,0)
Or(2)
	LevelInClass(Myself,2,SORCERER)
	LevelInClass(Myself,2,WIZARD)
THEN
RESPONSE #100
	ChangeCurrentScript("spiritw2")
END

IF
InternalGT(MyTarget,4,0)
Or(2)
	LevelInClass(Myself,3,SORCERER)
	LevelInClass(Myself,3,WIZARD)
THEN
RESPONSE #100
	ChangeCurrentScript("spiritw3")
END

IF
InternalGT(MyTarget,4,0)
Or(2)
	LevelInClass(Myself,4,SORCERER)
	LevelInClass(Myself,4,WIZARD)
THEN
RESPONSE #100
	ChangeCurrentScript("spiritw4")
END

IF
InternalGT(MyTarget,4,0)
Or(2)
	LevelInClass(Myself,5,SORCERER)
	LevelInClass(Myself,5,WIZARD)
THEN
RESPONSE #100
	ChangeCurrentScript("spiritw5")
END

IF
InternalGT(MyTarget,4,0)
Or(2)
	LevelInClassGT(Myself,5,SORCERER)
	LevelInClassGT(Myself,5,WIZARD)
THEN
RESPONSE #100
	ChangeCurrentScript("spiritw6")
END

 

There are 3 things to note:

1) The use of Internals - apparently they execute quicker than "LOCALS" global commands. This has something to do with the string parsing. Also note that there are no Continue() statements, because Internals do not get set until the end of a round.

2) I edited ACTION.IDS, INSTANT.IDS, TRIGGER.IDS for all Internal commands, e.g. 0x40A0 Internal(O:Object*,I:Index*INTVALS,I:Value*INTVALS)

3) MyTarget - I thought the best thing to do is to take away the target mapping problem (NearestEnemyOf(Myself) object) as soon as possible by setting up the MyTarget object. As already stated, I cannot find an object that 100% guarantees MyTarget being the correct creature.

 

As I said, I am not really a modding expert, so don't know whether ActionOverride should be add to the ChangeCurrentScript lines, or whether they should be removed from the "Set MyTarget from LastMarkedObject" block. I don't even know if ClearAllActions() should be used. Comments would be welcomed.

 

Like the Internal functions I altered with INTVALS, I also altered all Timer functions in ACTION.IDS and TRIGGERS.IDS to include my "ROUND" timer.

In addition (thanks to Icewind Gate II for this), I added ApplySpellRES to ACTION.IDS, so I could use "pe_swd" without having to name it in the format SPWInnn, and then add it to the "official" spell list. This, however, slows the script down, due to the string parsing required.

 

PART 2 - SPELL EFFECT SCRIPT

 

The scripts SPIRITW1.BCS to SPIRITW6.BCS all have a similar format. SPIRITW6.BCS is extended to fit the lasting Constitution effect for casters above 5th level (25 IF blocks, using the LevelInClass command!). The following is from SPIRITW2.BCS:

 

IF
Dead(MyTarget)
THEN
RESPONSE #100
	ChangeCurrentScript("")
END

IF
Internal(MyTarget,3,2) //*
THEN
RESPONSE #100
	SetInternal(MyTarget,3,0)
	SetInternal(MyTarget,4,0)
	ChangeCurrentScript("")
END

IF
Internal(MyTarget,4,SPIRITWORM_DMG)
InternalLT(MyTarget,3,2) //*
!TimerActive(ROUND) // does not appear in SPIRITW1
THEN
RESPONSE #100
	ApplySpellRES("pe_swd",MyTarget)
	IncrementInternal(MyTarget,3,1)
	StartTimer(ROUND,7) // does not appear in SPIRITW1
END

 

The only parts that differ between BCS files are changing Internal 3's value (commented with *). And the 25 IF blocks in SPIRITW6.

As with the previous script, I am unsure about ActionOverride...

 

Potential Problems

 

"Don't stop running"

 

On a few rare occasions during testing, I found my character continued moving after casting the spell. For some reason the script sometimes failed to kick in.

 

"The Internals"

 

This is set so the spell only works against that particular enemy and not another one, e.g.

Cast Spirit Worm on goblin1

Cast Spirit Worm on goblin2 - Internal 4 prevents the spell effects happening to goblin1.

Unfortunately, you cannot cast this spell on the same enemy, until the previous instance of the spell has finished. Only Constitution damage could occur. Otherwise Internals 3 and 4 would both be overwritten and the effects of the previous spell instance would be lost.

It is also assumed that nothing else uses these internals.

 

"Spell Delay"

 

It takes about a round for the script to kick in. It has been discussed before as to why IWD2 puts this delay in.

 

"General Spell Failure"

 

Two occurances of this may happen:

1) As there is a delay for the script, there may be extreme instances of the SPIRIT_WORMSAVE state disappearing before MyTarget is set. On my testing (in Targos Docks), it worked ok. The state could be made to last longer than 2 rounds if necessary.

2) If two casters cast this spell on two different enemies simultaneously, then the TenthNearestEnemy-NearestEnemy block may fail. This is unlikely, as it is a touch spell.

 

"Applying all this to another spell"

 

Ranged spells of "either/or" type impossible to implement. The targetting system simply won't allow it. There is no decent "object" that is flagged as the creature you have targetted.

You could limit the SPIRIT_WORMGEN spell state to, say, 1 round. However, you could end up finding the wrong "LastMarkedObject" on a 2nd casting of the spell.

Link to comment

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...