Jump to content

Some questions regarding the scripting states


critto

Recommended Posts

I've been trying to get a better understanding of scripting states for some time now and I think that I got the general gist of it, but some stuff remains confusing to me. I've probably read all threads in IE forums by now that discuss the issue. I've been studying the code of detectable spells lately, but it's tough to deal with.

 

Hope that someone can clarify this.

 

First, here's the description from documentation:

 

 

 

Modifies Scripting state to the given value. Scripting State range from 0 to 35. The scripting state can be checked via scripts (see stats.ids), subtract 156 from the stat value to get the scripting state.

 

This makes no sense to me. Stats.ids lists PICKPOCKET as 29. What exactly do I subtract from what?
Now, another thing. From what I gathered, EXTRAPROFICIENCY2-20 and SCRIPTINGSTATE1-10 are left unused by the devs of the vanilla game. There are also some empty slots between SCRIPTINGSTATE10 and 199 STONESKINSGOLEM that can be used for whatever.
For instance, if I do a CheckStat(LastSeenBy(Myself),1,SCRIPTINGSTATE1) trigger call, I check that the creature in question has a state numbered 156 set, according to stats.ids. What happens, however, if I have the following function call: CheckStat(LastSeenBy(Myself),2,SCRIPTINGSTATE1)? What does the value of two imply? That it is not 156, but actually 157 now? Does it mean that it overlaps with SCRIPTINGSTATE2 that is assigned to 157 in stats.ids and weird stuff can happen inside the scripts? For instance, when the state value was found and one thing was expected, but it was something else entirely that caused the creature to acquire said state?
In, SCS, DavidW checks values up to 7 for WIZARD_SPELL_TRAP, for example. The whole stats.ids table is filled out, though, how can it go up to 7?
Cheers,
critto
Link to comment
This makes no sense to me. Stats.ids lists PICKPOCKET as 29. What exactly do I subtract from what?

 

STATS.IDS contains proficiency points (values 89<>155, set by opcode 233), scripting states (value 156+, set by opcode 282), and other things like HP, saves, AC etc. Quite collective, it is, yes.

Note that "scripting states" are actually stats, not states, as they can have values other than 0 and 1. The only reason they are being called states, I think, is because Bioware designers used them only in true/false capacity in AI scripting. Still, some opcodes are hardcoded to set various scripting states to 1, and other values either have no effect or possibly crash the game (?).

 

While you can set proficiency by setting effect's parameter2 directly to the corresponding value in stats.ids (use par2=134 to set EXTRAPROFICIENCY20), for scripting states you have to subtract 156 from stat's value to obtain valid par2.

Let's say you need to set SCRIPTINGSTATE3 via spell/item/creature effect. It is listed under the value of 158 in stats.ids, which makes it a scripting state rather than a proficiency. 158-156=2, which is what should be used for par2 in your effect.

SCRIPTINGSTATE1 is 156, which means you need par2=0 for it.

 

Now, another thing. From what I gathered, EXTRAPROFICIENCY2-20 and SCRIPTINGSTATE1-10 are left unused by the devs of the vanilla game.

1-3 are used for wing buffet, death ward and level drain immunity, respectively. Don't remember about 4-6, but I believe those had some use in vanilla as well, at least in scripts.

 

 

There are also some empty slots between SCRIPTINGSTATE10 and 199 STONESKINSGOLEM that can be used for whatever.

 

Everything above SCRIPTINGSTATE10 is being used for something. Values 176-182 are thief skills' modifiers, so they're fairly harmless to utilize with +/-1 adjustments, but messing with others is a bad idea.

 

166 MELEE_THACO_BONUS
167 MELEE_DAMAGE_BONUS
168 MISSILE_DAMAGE_BONUS
169 DISABLE_CIRCLE
170 FIST_THACO_BONUS
171 FIST_DAMAGE_BONUS
172 CLASS_STRING_OVERRIDE_MIXED
173 CLASS_STRING_OVERRIDE_LOWER
174 PREVENT_SPELL_PROTECTION_EFFECTS
175 IMMUNITY_TO_BACKSTAB
176 LOCKPICKINGMTPBONUS
177 MOVESILENTLYMTPBONUS
178 TRAPSMTPBONUS
179 PICKPOCKETMTPBONUS
180 HIDEINSHADOWSMTPBONUS
181 DETECTILLUSIONSMTPBONUS
182 SETTRAPSMTPBONUS
183 PREVENT_AI_SLOWDOWN
184 EXISTANCE_DELAY_OVERRIDE
185 ANIMATION_ONLY_HASTE
186 NO_PERMANENT_DEATH
187 IMMUNE_TO_TURN_UNDEAD

 

 

For instance, if I do a CheckStat(LastSeenBy(Myself),1,SCRIPTINGSTATE1) trigger call, I check that the creature in question has a state numbered 156 set, according to stats.ids.

 

You check that the stat 156 (SCRIPTINGSTATE1 in stats.ids) has its value set to 1.

 

What happens, however, if I have the following function call: CheckStat(LastSeenBy(Myself),2,SCRIPTINGSTATE1)? What does the value of two imply? That it is not 156, but actually 157 now?

 

You check that SCRIPTINGSTATE1 has its value set to 2.

 

Does it mean that it overlaps with SCRIPTINGSTATE2 that is assigned to 157 in stats.ids and weird stuff can happen inside the scripts? For instance, when the state value was found and one thing was expected, but it was something else entirely that caused the creature to acquire said state?

 

Stuff like that happens, but in a slightly different form.

Some spells are being assigned same stats but with different values. E.g., both SPWI522.SPL and SPWI701.SPL use the same stat 129 (WIZARD_SPELL_TURNING in DS), with values 1 and 2 respectively. There's a reason for this, as the effect is very similar and usually there's no point to claim a different stat for such minor difference (remember, free stats are in very short supply). However, if you were to cast simultaneously both minor and regular Spell Turning, you'll end up with WIZARD_SPELL_TURNING=1 and WIZARD_SPELL_TURNING=2 active at the same time. Since it is treated by engine as a numeric stat rather than boolean state, checking the value by scripts will only yield 2 (ST), even though you have an effect with 1 active (MST) as well. If your script checks for CheckStat(LastSeenBy(Myself),1,WIZARD_SPELL_TURNING) to cast Spell Thrust at the target (e.g. dispel spell protections up to level 5, which is what MST is), then you'll be unable to do so until regular Spell Turning is dispelled first. Of course, if your target is protected by ST, then MST is probably the least of your worries at the moment, so not being able to detect is rarely a big deal.

It does get worse with Spell Immunity, though, because all of those also share a stat and can stack, and, unlike Spell Turning/Deflection, have rather different effects.

Link to comment

That it's called "scriptingstateX" is completely arbitrary. As long as whatever it's called in STATS.IDS is what's used in the script, they can be freely renamed (because it's just going to reference back to the #, which you could use instead of the text anyway). Bioware could (should) have renamed SS2 "leveldrain", and then these things would have been more obvious.

Link to comment
@ Ardanis:
I couldn't imagine a better person to receive an explanation from than the current mantainer of DS :) Thanks, Ardanis!
The subtraction is now clear to me. I definitely could've figured it out on my own if I were to take a closer look at how stats are actually set by the 282 opcode. It would be a good idea to make a more descriptive explanation for the opcode in the respective section of IEDSP. Right now it's very confusing and has some typos in it (WTF is 1139 in the example? Is it 13 or 19? Doesn't make any sense)

Note that "scripting states" are actually stats, not states, as they can have values other than 0 and 1. The only reason they are being called states, I think, is because Bioware designers used them only in true/false capacity in AI scripting. Still, some opcodes are hardcoded to set various scripting states to 1, and other values either have no effect or possibly crash the game (?).

Yes. This much I already understood from my interaction with scripting states. I am calling them "states" simply by convenience, I guess. I figured that they can take more than a pair of values and are not states per se.
The thing with a stat being capable to take on different values now makes sense as well and explains the way they are used in a setup I'm dealing with. Scripting state 1 is set to distinctive values for various kits (both custom and vanilla ones) to be later used in AI scripts. Not the best idea, I think, since the Kit() trigger appears to function properly both in ToB and EE as long as scripts are compiled after the kits are added to the game.
EDIT: one more thing. As I understand, TobEx expands the vanilla engine and provides opportunity to employ more stats in the game, as long as they are set and checked explicitly. Does EE duplicate that functionality?
@ Fiann:
Indeed. I figured that the names are not set in stone and could be subject to change, I just never bothered to do so before I made sure I understood their function completely.
Link to comment

Not the best idea, I think, since the Kit() trigger appears to function properly both in ToB and EE as long as scripts are compiled after the kits are added to the game.

Yeah, I'm pretty sure Kit() works just fine.

 

EDIT: one more thing. As I understand, TobEx expands the vanilla engine and provides opportunity to employ more stats in the game, as long as they are set and checked explicitly. Does EE duplicate that functionality?

EE v2.0 and higher support what is called a spell state. It is a state rather than stat (i.e. boolean) and requires a different trigger to check via script:

0x40E2 CheckSpellState(O:Object*,I:State*splstate)

It does have a downside of not being able to assign any value, but since it was originally implemented for IWD:EE and I wasn't part of that project, I can't tell for sure the rationalization for such decision.

Link to comment

 

Not the best idea, I think, since the Kit() trigger appears to function properly both in ToB and EE as long as scripts are compiled after the kits are added to the game.

Yeah, I'm pretty sure Kit() works just fine.

Appear and actually functioning correctly is a big difference. In vanilla BG2, it doesn't work at all.

And the last 12 years, until very recently, if you had installed a badly named kit and then some other mods, even the BG2Fixpack, do to it's coding, could break the kit.ids when it tries to fix it. And then we have 15 other mods that copied the code from there or from each others and we have a whole lot of mess, that's far worse due to the fact that they might not be needed to be installed after the very first mod that is ToBEx.

Yeah, I still remember one of the SHS member that had a 20 different Barbarian kits in his kit.ids file ... due to a regression.

Link to comment

Appear and actually functioning correctly is a big difference. In vanilla BG2, it doesn't work at all.

 

This is actually an interesting subject, Jarno. Could you clarify on the "not working" part, please? The trigger call itself does not work or kit.ids is bugged due to years of bad patching?

 

When I was implementing interaction with party members based on their kit, I had a strange outdated kit.ids. I got rid of it, did some research, found your rant about incorrect fixes of kit.ids and applied your most recent patch instead.

 

I also did a lot of testing on kit detection. I've checked both vanilla and custom kits, there was never any problem. During play-testing in ToB, the feature that was based on kit and class detection worked correctly. Maybe I have missed something, of course. If Kit() indeed doesn't work, I'll have to re-check everything again.

Link to comment

Could you clarify on the "not working" part, please? The trigger call itself does not work or kit.ids is bugged due to years of bad patching?

It should be a little bit of both.

Either the script can't be complied or they behave as if compiled totally out of whack.

And in the vanilla BG2, the call won't work at all because the ids file doesn't match to the in game files actual reality. See the kit.ids with and without the fix, as documented in the IESDP.

 

The original (incorrect) kit.ids file is available here .

To test it;

Install a kit that has a badly named kit id, you can use the IJ#blade kit in the above posts links post and make it the example, then on top of that install the current v10 BG2Fixpack, from here, as it doesn't have the patch and then try to compile a script using the actual name of the kit in the .baf files that you had installed before the BG2Fixpack. It shouldn't be able to install at all. And then play the game until you find if it works or not.

If you switch the install order, then the NPC should identify the normal Blade (kitted bard) as if it were the mod added IJ#blade, aka a fighter... so there in theory will be no difference between the two.

Link to comment

So, to summarise. If I take corrected kit.ids for vanilla from IESDP (or from BG2:EE, it appears to match the correct one) and apply your patch on top, then I get a version that will work, right? Assuming that I have no badly named kits or BG2 Fixpack to mess things up. I just did that and got a successful test of custom kit detection in ToB.

 

I'm gonna do the test you propose as well to see how it misbehaves.

 

BTW, by your patch I mean the following:

COPY_EXISTING ~kit.ids~ ~override~
  REPLACE_TEXTUALLY ~0x0000 BARBARIAN~   ~0x40000000 BARBARIAN~
  REPLACE_TEXTUALLY ~0x0000 WILDMAGE~    ~0x80000000 WILDMAGE~
BUT_ONLY
 
APPEND ~kit.ids~ ~0x4000 TRUECLASS~ UNLESS ~\bTRUECLASS\b~
APPEND ~kit.ids~ ~0x40000000 BARBARIAN~ UNLESS ~\bBARBARIAN\b~
APPEND ~kit.ids~ ~0x80000000 WILDMAGE~ UNLESS ~\bWILDMAGE\b~
Link to comment

I propose that you use this instead:

COPY_EXISTING ~kit.ids~ ~override~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(TRUECLASS[ %TAB%%LNL%%MNL%%WNL%]+\)~              ~0x4000 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(BERSERKER[ %TAB%%LNL%%MNL%%WNL%]+\)~              ~0x4001 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(WIZARDSLAYER[ %TAB%%LNL%%MNL%%WNL%]+\)~           ~0x4002 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(KENSAI[ %TAB%%LNL%%MNL%%WNL%]+\)~                 ~0x4003 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(CAVALIER[ %TAB%%LNL%%MNL%%WNL%]+\)~               ~0x4004 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(INQUISITOR[ %TAB%%LNL%%MNL%%WNL%]+\)~             ~0x4005 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(UNDEADHUNTER[ %TAB%%LNL%%MNL%%WNL%]+\)~           ~0x4006 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(MAGESCHOOL_ABJURER[ %TAB%%LNL%%MNL%%WNL%]+\)~     ~0x0040 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(MAGESCHOOL_CONJURER[ %TAB%%LNL%%MNL%%WNL%]+\)~    ~0x0080 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(MAGESCHOOL_DIVINER[ %TAB%%LNL%%MNL%%WNL%]+\)~     ~0x0100 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(MAGESCHOOL_ENCHANTER[ %TAB%%LNL%%MNL%%WNL%]+\)~   ~0x0200 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(MAGESCHOOL_ILLUSIONIST[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x0400 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(MAGESCHOOL_INVOKER[ %TAB%%LNL%%MNL%%WNL%]+\)~     ~0x0800 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(MAGESCHOOL_NECROMANCER[ %TAB%%LNL%%MNL%%WNL%]+\)~ ~0x1000 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(MAGESCHOOL_TRANSMUTER[ %TAB%%LNL%%MNL%%WNL%]+\)~  ~0x2000 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(MAGESCHOOL_GENERALIST[ %TAB%%LNL%%MNL%%WNL%]+\)~  ~0x4000 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(FERALAN[ %TAB%%LNL%%MNL%%WNL%]+\)~                ~0x4007 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(STALKER[ %TAB%%LNL%%MNL%%WNL%]+\)~                ~0x4008 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(BEASTMASTER[ %TAB%%LNL%%MNL%%WNL%]+\)~            ~0x4009 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(ASSASIN[ %TAB%%LNL%%MNL%%WNL%]+\)~                ~0x400A \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(BOUNTYHUNTER[ %TAB%%LNL%%MNL%%WNL%]+\)~           ~0x400B \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(SWASHBUCKLER[ %TAB%%LNL%%MNL%%WNL%]+\)~           ~0x400C \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(BLADE[ %TAB%%LNL%%MNL%%WNL%]+\)~                  ~0x400D \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(JESTER[ %TAB%%LNL%%MNL%%WNL%]+\)~                 ~0x400E \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(SKALD[ %TAB%%LNL%%MNL%%WNL%]+\)~                  ~0x400F \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(GODTALOS[ %TAB%%LNL%%MNL%%WNL%]+\)~               ~0x4013 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(GODHELM[ %TAB%%LNL%%MNL%%WNL%]+\)~                ~0x4014 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(GODLATHANDER[ %TAB%%LNL%%MNL%%WNL%]+\)~           ~0x4015 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(TOTEMIC[ %TAB%%LNL%%MNL%%WNL%]+\)~                ~0x4010 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(SHAPESHIFTER[ %TAB%%LNL%%MNL%%WNL%]+\)~           ~0x4011 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(BEASTFRIEND[ %TAB%%LNL%%MNL%%WNL%]+\)~            ~0x4012 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(BARBARIAN[ %TAB%%LNL%%MNL%%WNL%]+\)~              ~0x40000000 \1~
  REPLACE_TEXTUALLY ~^.+[ %TAB%]\(WILDMAGE[ %TAB%%LNL%%MNL%%WNL%]+\)~               ~0x80000000 \1~
  BUT_ONLY

APPEND ~kit.ids~ ~0x4000 TRUECLASS~ UNLESS ~^.+[ %TAB%]TRUECLASS[ %TAB%%LNL%%MNL%%WNL%]+~
APPEND ~kit.ids~ ~0x40000000 BARBARIAN~ UNLESS ~^.+[ %TAB%]BARBARIAN[ %TAB%%LNL%%MNL%%WNL%]+~
APPEND ~kit.ids~ ~0x80000000 WILDMAGE~ UNLESS ~^.+[ %TAB%]WILDMAGE[ %TAB%%LNL%%MNL%%WNL%]+~

It's in the same thread, a page forward, and by CamDawn, and it's like the most up to date I have seen in a while. The Fixpack v11 should be using that already... and it can be applied to already fixed file without resulting into bad regressions.

Link to comment

Opcode 282:

Modifies Scripting state to the given value. Scripting State range from 0 to 35. The scripting state can be checked via scripts (see stats.ids), subtract 156 from the stat value to get the scripting state.

 

This effect does not have proper bounds checking, and can therefore be used in unintended ways. For example scripting state 13 equals with the NO_CIRCLE attribute (stat=169), and scripting state 1139 ties to PICKPOCKET (stat=29).

 

 

 

Does anybody have a single idea to find the number that could set scripting state 63 SANCTUARY ?

Link to comment

 

Opcode 282:

Modifies Scripting state to the given value. Scripting State range from 0 to 35. The scripting state can be checked via scripts (see stats.ids), subtract 156 from the stat value to get the scripting state.

 

This effect does not have proper bounds checking, and can therefore be used in unintended ways. For example scripting state 13 equals with the NO_CIRCLE attribute (stat=169), and scripting state 1139 ties to PICKPOCKET (stat=29).

 

 

 

Does anybody have a single idea to find the number that could set scripting state 63 SANCTUARY ?

 

 

Semantics: scripting states are all stats, but not all stats are scripting states.

The sanctuary stat (63) is set by the sanctuary opcode (153).

 

What you refer to in red is an actual code exploit (feature available only in buggy code), which is completely unsafe and not available for all stats. It would also zero out or overwrite multiple stats, or some values could cause internal memory problems and crash the game. The missing boundary check is added to EE, so the illegal values wouldn't be usable in EE. Again, these numbers are absolutely unsafe in the original game they could target only a few fields correctly (outside of the real scripting states).

Link to comment

Thanks for your answer.

 

I know understand clearly the opcode #282 definition. It might be useful to update it in IESDP, even removing the last sentence which is very confusing.

 

In fact, I want to create a kind of sanctuary spell with a more elegant animation file than the hardcoded ugly one provided by #153. That's why I tried to find a way to set variable 63 to one, a trick that would work on classical ad enhanced games..

Link to comment

Archived

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

×
×
  • Create New...