devSin Posted February 21, 2006 Share Posted February 21, 2006 I finally got a moment to check why Odren's scroll fails if the spell level is set to 1, and made some boring observations. We append triggers to the IDS files and include scriptable spells, so I'm posting this here for none to see. It turns out that SpellCastInnate() will *only* return true if the spell level is correct (i.e., the spell number / 100). If the spell is any other level, the SpellCastInnate() trigger will return false. Additionally, SpellCastInnate() is the only trigger that will return true when an innate spell is cast, so it has to be used. APPEND ~TRIGGER.IDS~ ~0x00A1 SpellCastOnMeRES(S:Spell*,O:Caster*)~ APPEND ~TRIGGER.IDS~ ~0x0091 SpellCastRES(S:Spell*,O:Object*)~ APPEND ~TRIGGER.IDS~ ~0x00A6 SpellCastPriestRES(S:Spell*,O:Object*)~ APPEND ~TRIGGER.IDS~ ~0x00A7 SpellCastInnateRES(S:Spell*,O:Object*)~ APPEND ~TRIGGER.IDS~ ~0x4031 HaveSpellRES(S:Spell*)~ I am unable to verify that most of these actually work. HaveSpellRES() does work, as from the following script: IF  HaveSpellRES("CAMDAWG") THEN  RESPONSE #100   DisplayStringHead(Myself,67787) // I'd say it's time to die, demon.   Continue() END IF  HaveSpellRES("SPWI406") // Minor Globe of Invulneribility  Global("SexBomb","LOCALS",0) THEN  RESPONSE #100   CreateVisualEffectObject("SPFIREPI",Myself)   SetGlobal("SexBomb","LOCALS",1) END When loaded, Imoen will never run the string, and will always play the effect (any spell she has can be substituted for 2406). SpellCastInnateRES() doesn't seem to work at all. It looks like the resource reference is ignored entirely, and the game is just treating it at SpellCastInnate(O:O,I:0), from the following code: IF  SpellCastInnateRES("DING0",[GOODCUTOFF]) THEN  RESPONSE #100   DisplayStringHead(Player1,21792) // What if I'm not interested in having my eyes removed?   Continue() END Any innate spell of any level can be cast, and the string will always run over Player1 (regardless of the reference specified). The behavior is the same whether a generic object ([30], [0], etc.), or an explicit object (e.g., "IMOEN", Player1) is used (although, that aspect does work as expected). The same continues to be true if SpellCastPriestRES() or SpellCastRES() are used in place of SpellCastInnateRES() (again, the matching to spell type and the caster still works, it just picks up any spell). I didn't check HaveSpellPartyRES() or SpellCastOnMeRES(). I don't care. Most of this doesn't really affect the fixpack, but if somebody can verify, I'd chuck the worthless appends. Additionally, I'd remove the following files from the scriptable spells patch, since they are of the correct level (by default) and are checked with SpellCastInnate() triggers in the default scripts (actually, only ever for Saladrex's script): SPCL412, SPCL414, SPCL910, SPCL911, and SPCL912. I did a very brief check of the actions; they all work except for the two RangeRES() actions (I don't even know if the normal ones work, though). I didn't check RemoveSpellRES(). I don't care. IF  HotKey(B)  Global("HotKeyB","LOCALS",0) THEN  RESPONSE #100   ApplySpellRES("SPCL907",Myself) // Hardiness   SetGlobal("HotKeyB","LOCALS",1) END IF  HotKey(D)  Global("HotKeyD","LOCALS",0) THEN  RESPONSE #100   ForceSpellPointRangeRES("SPWI205",[3622.2851]) // Horror   SetGlobal("HotKeyD","LOCALS",1) END IF  HotKey(E)  Global("HotKeyE","LOCALS",0) THEN  RESPONSE #100   ForceSpellPointRES("SPPR208",[3720.2923]) // Hold Person   SetGlobal("HotKeyE","LOCALS",1) END IF  HotKey(F)  Global("HotKeyF","LOCALS",0) THEN  RESPONSE #100   ForceSpellRangeRES("SPWI206",Player1) // Invisibility   SetGlobal("HotKeyF","LOCALS",1) END IF  HotKey(S)  Global("HotKeyS","LOCALS",0) THEN  RESPONSE #100   ForceSpellRES("SPWI201",Myself) // Blur   SetGlobal("HotKeyS","LOCALS",1) END IF  HotKey(B)  Global("HotKeyB","LOCALS",1) THEN  RESPONSE #100   ReallyForceSpellDeadRES("SPIN101",Player1) // Cure Light Wounds   SetGlobal("HotKeyB","LOCALS",2) END IF  HotKey(D)  Global("HotKeyD","LOCALS",1) THEN  RESPONSE #100   ReallyForceSpellPointRES("SPPR203",[3622.2851]) // Chant   SetGlobal("HotKeyD","LOCALS",2) END IF  HotKey(E)  Global("HotKeyE","LOCALS",1) THEN  RESPONSE #100   ReallyForceSpellRES("SPIN795",Player1) // No such index   SetGlobal("HotKeyE","LOCALS",0) END IF  HotKey(F)  Global("HotKeyF","LOCALS",1) THEN  RESPONSE #100   SpellNoDecRES("SPWI408",Myself) // Stoneskin   SetGlobal("HotKeyF","LOCALS",0) END IF  HotKey(S)  Global("HotKeyS","LOCALS",1) THEN  RESPONSE #100   SpellPointNoDecRES("SPWI305",[3670.2882]) // Haste   SetGlobal("HotKeyS","LOCALS",0) END IF  HotKey(B)  Global("HotKeyB","LOCALS",2) THEN  RESPONSE #100   SpellPointRES("SPWI309",[3483.2871]) // Monster Summoning I   SetGlobal("HotKeyB","LOCALS",0) END IF  HotKey(D)  Global("HotKeyD","LOCALS",2) THEN  RESPONSE #100   SpellRES("SPWI102",Myself) // Armor   SetGlobal("HotKeyD","LOCALS",0) END Link to comment
devSin Posted February 21, 2006 Author Share Posted February 21, 2006 As odd as it may seem, I very much want somebody to come and prove me wrong. I'm looking at you, cirerrek. Link to comment
cirerrek Posted February 21, 2006 Share Posted February 21, 2006 As odd as it may seem, I very much want somebody to come and prove me wrong. I'm looking at you, cirerrek. <{POST_SNAPBACK}> I'm listening, but this stream of consciousness thing is throwing me off. Could you please summarize? It'll be later tonight before I can get back to this. Thanks, Cirerrek Link to comment
devSin Posted February 21, 2006 Author Share Posted February 21, 2006 SpellCastInnate() will only return true if the innate spell has the right level (e.g., SPCL412 needs to be level 4). SpellCast*RES() triggers simply do not work. The engine is treating this like SpellCast*(O:O,0), ignoring the resref completely. Link to comment
SimDing0 Posted February 21, 2006 Share Posted February 21, 2006 SpellCast*RES() triggers simply do not work. The engine is treating this like SpellCast*(O:O,0), ignoring the resref completely. Correct. On reflection, I knew this as the cause of a QP bug. Sorry. Link to comment
cirerrek Posted February 22, 2006 Share Posted February 22, 2006 IF  SpellCastInnate(Myself,4414) THEN  RESPONSE #100   DisplayStringHead(Myself,22724) // say "Gong Mallet" END  devSin is correct. The string was only displayed when the spell level was set to 4. Checking the biffs, this is what I found. Set Snare - SPCL412 - is set to lvl 0 by default (default meaning straight from the biff) Set Special Snare - SPCL414 - is set to lvl 0 by default Set Spike Trap - SPCL910 - is set to lvl 9 by default SPCL910B - is set to lvl 0 by default Set Exploding Trap - SPCL911 - is set to lvl 9 by default SPCL911B - is set to lvl 0 by default Set Time Trap - SPCL912 - is set to lvl 9 by default SPCL912B - set to lvl 0 by default Setting the spell level to 0 String is not displayed No crash when cast from a script, the spell is simply not cast (it was in series with a couple of other spells to be cast, the Set Snares with its level set to 1 was cast, Set Special Snares with its level set to 0 was not cast. Setting the spell level to 1 String is not displayed No crash when cast from a script, both Set Snares and Set Special Snares were cast as expected. Setting the spell to level 4 String was displayed Crash to desktop when cast from a script. Conclusion We are at a bit of an impasse. Saldrex needs SpellCastInnate for his/its scripts to work. If we set the level to 4, then it causes AI Scripts that cast the spell via HaveSpell or HaveSpellRES() to CtD. For Further Research Anyone have clean install of BG2:SoA + ToB? I'm curious to see whether or not Set Snares and Set Special Snares are set to the proper level (4) and are in the override courtesy of the ToB install, otherwise, Saldrex's script never worked properly from the start. Thanks, Link to comment
devSin Posted February 22, 2006 Author Share Posted February 22, 2006 They exist in the override and have a level of 4. Does casting the spell crash, or is it just when HaveSpell() goes crawling through the memorized spells? After-the-fact spells (like the actual trap part of set traps), and AP_ special abilities are usually set to level 0 (but these are never spells the player would cast in-game). This would correspond to the SPCL*Bs you posted, and also to SPCL411 and SPCL415. Link to comment
cirerrek Posted February 22, 2006 Share Posted February 22, 2006 They exist in the override and have a level of 4. Does casting the spell crash, or is it just when HaveSpell() goes crawling through the memorized spells? After-the-fact spells (like the actual trap part of set traps), and AP_ special abilities are usually set to level 0 (but these are never spells the player would cast in-game). This would correspond to the SPCL*Bs you posted, and also to SPCL411 and SPCL415. <{POST_SNAPBACK}> It is hard to tell because no information is dumped to the Text Box before the crash. I think when HaveSpell() or HaveSpellRES() go looking for a spell and find an innate reference with the incorrect level, the game first freezes as it tries to digest this inconsistency, and then the games CtDs when it can't resolve the issue internally. Link to comment
NiGHTMARE Posted February 22, 2006 Share Posted February 22, 2006 What about creating non-joinable only duplicates of each of the relevant innates? EDIT: of course, that would require quite a bit of script re-writing. Link to comment
devSin Posted February 22, 2006 Author Share Posted February 22, 2006 It is hard to tell because no information is dumped to the Text Box before the crash. I think when HaveSpell() or HaveSpellRES() go looking for a spell and find an innate reference with the incorrect level, the game first freezes as it tries to digest this inconsistency, and then the games CtDs when it can't resolve the issue internally.If you ever look at a stack trace, you'll notice it running through trigger evaluation in the AI thread. Since the hard crash is so inconsistent here, my guess was that the crash occurs when the engine goes to walk through the memorized spells in the creature definition (I guess it would end up walking out in the weeds if it tried to read innate spell levels > 1 from the memorization table). It may be interesting to someday have somebody try patching in a table that has all 9 levels (0-8) for innate spells, but this might cause general crashes with normal spellcasting (if it works at all). EDIT: I though I had one laying around, but I don't. I've got a crash dump from a HaveSpell() crash, though. You can roughly see how the game is handling running AI scripts, and the steps it's going through to process the HaveSpell() call. Examining the registers around the time of the crash would likely yield more insight, but I'm not that concerned about it. 0 <Unknown disk fragment> 0x02a2d7c0 _eq__7CResRefCFPc + 16 1 <Unknown disk fragment> 0x029fcda0 HaveSpell__11CGameSpriteFRC7CString + 832 2 <Unknown disk fragment> 0x029fbf80 EvaluateStatusTrigger__11CGameSpriteFRC10CAITrigger + 432 3 <Unknown disk fragment> 0x025fe458 TriggerHolds__12CAIConditionFP10CAITriggerR38CTypedPtrList<8CPtrList,P10CAITrigger>P11CGameAIBase + 72 4 <Unknown disk fragment> 0x025fe5e0 Hold__12CAIConditionFR38CTypedPtrList<8CPtrList,P10CAITrigger>P11CGameAIBase + 128 5 <Unknown disk fragment> 0x0260eab4 Find__9CAIScriptFR38CTypedPtrList<8CPtrList,P10CAITrigger>P11CGameAIBase + 100 6 <Unknown disk fragment> 0x0265a390 ProcessPendingTriggers__11CGameAIBaseFi + 1040 7 <Unknown disk fragment> 0x02a09570 ProcessPendingTriggers__11CGameSpriteFi + 416 8 <Unknown disk fragment> 0x02a214e0 MainActionPicking__11CGameSpriteFv + 448 9 <Unknown disk fragment> 0x02a05ad0 ProcessAI__11CGameSpriteFv + 6464 10 <Unknown disk fragment> 0x029b957c AIUpdate__11CGameSpriteFv + 3020 11 <Unknown disk fragment> 0x0269144c AIUpdate__9CGameAreaFv + 5340 12 <Unknown disk fragment> 0x0291d340 AsynchronousUpdate__12CScreenWorldFi + 3440 Link to comment
CamDawg Posted February 22, 2006 Share Posted February 22, 2006 If you ever look at a stack trace, you'll notice it running through trigger evaluation in the AI thread. Since the hard crash is so inconsistent here, my guess was that the crash occurs when the engine goes to walk through the memorized spells in the creature definition (I guess it would end up walking out in the weeds if it tried to read innate spell levels > 1 from the memorization table). It may be interesting to someday have somebody try patching in a table that has all 9 levels (0-8) for innate spells, but this might cause general crashes with normal spellcasting (if it works at all). Now that sounds like a fun experiment. Link to comment
CamDawg Posted March 13, 2006 Share Posted March 13, 2006 It occurs to me that there may be a simpler solution--shell spells, like we do for Larloch's Minor Drain and Vampiric Touch. For example, we copy spcl910 to spcl910a, and alter spcl910 to only have one effect, Cast Spell: spcl910a, and make it level 1. I'm thinking that this would allow us to script spcl910 (since it would be the spell memorized and level 1) while allowing us to detect its casting via spcl910a. If someone wants to test the theory, here's code to do just that: COPY_EXISTING ~spcl910.spl~ ~override/spcl910a.spl~ // spike trap SAY NAME1 #-1 // suppresses batle text SAY NAME2 #-1 // suppresses batle text WRITE_LONG 0x34 1 // set to level 1 READ_LONG 0x64 "abil_off" READ_SHORT 0x68 "abil_num" FOR (index = 0; index < abil_num; index = index + 1) BEGIN WRITE_SHORT ("%abil_off%" + 0x26 + (0x28 * "%index%")) 1 // removes projectile END COPY_EXISTING ~spcl910.spl~ ~override~ // spike trap shell WRITE_LONG 0x34 9 // set to level 9 READ_LONG 0x64 "abil_off" READ_SHORT 0x68 "abil_num" READ_LONG 0x6a "fx_off" READ_SHORT 0x70 "fx_num" FOR (index = 0; index < abil_num; index = index + 1) BEGIN READ_SHORT ("%abil_off%" + 0x10 + (0x28 * "%index%")) "level" READ_SHORT ("%abil_off%" + 0x1e + (0x28 * "%index%")) "abil_fx_num" WRITE_SHORT ("%abil_off%" + 0x1e + (0x28 * "%index%")) 1 READ_SHORT ("%abil_off%" + 0x20 + (0x28 * "%index%")) "abil_fx_idx" WRITE_SHORT ("%abil_off%" + 0x20 + (0x28 * "%index%")) "%index%" DELETE_BYTES ("%fx_off%" + (0x30 * "%abil_fx_idx%")) (0x30 * "%abil_fx_num%") // deletes all effects INSERT_BYTES ("%fx_off%" + (0x30 * "%abil_fx_idx%")) 0x30 // cast actual spell WRITE_SHORT ("%fx_off%" + (0x30 * "%abil_fx_idx%")) 146 // cast spell WRITE_BYTE ("%fx_off%" + 0x02 + (0x30 * "%abil_fx_idx%")) 2 // target: preset target WRITE_BYTE ("%fx_off%" + 0x03 + (0x30 * "%abil_fx_idx%")) 9 // power WRITE_LONG ("%fx_off%" + 0x04 + (0x30 * "%abil_fx_idx%")) "%level%" // cast at level WRITE_LONG ("%fx_off%" + 0x08 + (0x30 * "%abil_fx_idx%")) 1 // cast instantly WRITE_BYTE ("%fx_off%" + 0x0c + (0x30 * "%abil_fx_idx%")) 1 // instant/permanent WRITE_BYTE ("%fx_off%" + 0x0d + (0x30 * "%abil_fx_idx%")) 1 // dispel/not bypass WRITE_BYTE ("%fx_off%" + 0x12 + (0x30 * "%abil_fx_idx%")) 100 // probability WRITE_EVALUATED_ASCII ("%fx_off%" + 0x14 + (0x30 * "%abil_fx_idx%")) ~%SOURCE_RES%a~ // spell END PATCH_IF ("%fx_num%" > 0) BEGIN // eliminates global fx DELETE_BYTES "%fx_off%" (0x30 * "%fx_num%") WRITE_SHORT 0x70 0 END BUT_ONLY_IF_IT_CHANGES Link to comment
devSin Posted March 13, 2006 Author Share Posted March 13, 2006 I'm thinking that this would allow us to script spcl910 (since it would be the spell memorized and level 1) while allowing us to detect its casting via spcl910a.How, oh, how? Link to comment
CamDawg Posted March 13, 2006 Share Posted March 13, 2006 I sooo love our games of 20 questions. Is it an animal, vegetable or mineral? Link to comment
devSin Posted March 14, 2006 Author Share Posted March 14, 2006 I sooo love our games of 20 questions. Is it an animal, vegetable or mineral? I really don't know how to respond to that, sorry. Since SpellCastRES() triggers don't work, the SPCL90n spell needs to level 9. Since the kit applies SPCL90n, HaveSpell() will crash if not level 1. I don't see an obvious solution outside of changing the kit bonus and telling everybody to now check HaveSpellRES("SPCL90nA"). My suggestion is still to not patch SPCL411, SPCL412, SPCL910, SPCL911, and SPCL912 in the fixpack, since these spells are checked with SpellCastInnate() in a default script. Link to comment
Recommended Posts
Archived
This topic is now archived and is closed to further replies.