Jump to content

Investigating an I_C_T Failure Report, or "Passing an Afternoon"


cmorgan

Recommended Posts

User report: The scene with Kalah2 is not ending correctly with Aran in the party. Similar problem when Kivan is in the party.

 

Eventually, every modder that sticks around long enough will have bug reports like this. And I have one. So let's blog away at it, and see what happens. We might find out I *gasp* made a MISTAKE! Or not. More importantly, let's see what it takes to drag out these kinds of reports, beat them into shape, and send them packing.

So let's look at what is going one.

We start with the code, by installing the mod, then decompiling the related dialog files to .d with WeiDU or DLTCEP.

OK, we have the following interjections in SoA dealing with Kalah and what he is promised. Now, it is probable that Unfinished Business or another quest mod has changed stuff here, but in vanilla, what should happen is the conversation should finish, and Aerie should react if she can. So let's make sure it all works as intended first, then deal with mod additions.

Code to try to add and Aran response, using I_C_T with passback, so things will flow. We use INTERJECT_COPY_TRANSITIVE (I_C_T) here because there are actions that Kalah's dialog state does that we really don't want to accidental;ly be applied to Aran, or left out of the link - I_C_T with a comment from Kalah at the end of it plays Hot Potato with the state actions, tossing them back to the original speaker (Kalah).

/* SoA Interjections : Kalah And What He Was Promised Comments - remember to add cross-mod for the UB restoration of Kalah's Quest */
I_C_T KALAH2 7 c-arankalah27 /* ~Indifference killed those fools from the circus.  I treat them in death as they were to me in life.  A gnome gets no respect...I just wanted people to look up...to me.  All that I have done...~ */
== C-ARANJ IF ~InParty("c-aran") InMyArea("c-aran") !StateCheck("c-aran",CD_STATE_NOTVALID) Global("c-silencearan","GLOBAL",0)~ THEN @202
== KALAH2 IF ~InParty("c-aran") InMyArea("c-aran") !StateCheck("c-aran",CD_STATE_NOTVALID) Global("c-silencearan","GLOBAL",0)~ THEN @203
END

I_C_T KALAH2 8 c-arankalah28 /* ~You don't understand, do you?  You take my life, but worse, you take my dignity.  I just wanted...I wanted to be the king instead of...instead of...~ */
== C-ARANJ IF ~InParty("c-aran") InMyArea("c-aran") !StateCheck("c-aran",CD_STATE_NOTVALID) Global("c-silencearan","GLOBAL",0)~ THEN @202
== KALAH2 IF ~InParty("c-aran") InMyArea("c-aran") !StateCheck("c-aran",CD_STATE_NOTVALID) Global("c-silencearan","GLOBAL",0)~ THEN @203
END

I_C_T KALAH2 9 c-arankalah29 /* ~You don't know...denied respect my entire life.  A clown, a fool...it does not matter...I die as I...as I...~ */
== C-ARANJ IF ~InParty("c-aran") InMyArea("c-aran") !StateCheck("c-aran",CD_STATE_NOTVALID) Global("c-silencearan","GLOBAL",0)~ THEN @202
== KALAH2 IF ~InParty("c-aran") InMyArea("c-aran") !StateCheck("c-aran",CD_STATE_NOTVALID) Global("c-silencearan","GLOBAL",0)~ THEN @203
END

patched states as installed after running that code, cut down to the evaluation of what happens if Aran is present:

 

IF ~~ THEN BEGIN 7 // from: 10.0 6.0
SAY #18865 /* ~Indifference killed those fools from the circus. I treat them in death as they were to me in life. A gnome gets no respect... I just wanted people to look up... to me. All that I have done...~ */
<- snip
IF ~Global("c-arankalah28","GLOBAL",0)
InParty("c-aran")
InMyArea("c-aran")
!StateCheck("c-aran",CD_STATE_NOTVALID)
Global("c-silencearan","GLOBAL",0)
~ THEN DO ~SetGlobal("c-arankalah28","GLOBAL",1)~ EXTERN ~C-ARANJ~ 10
END

IF ~~ THEN BEGIN 8 // from: 6.1 5.2
SAY #18867 /* ~You don't understand, do you? You take my life, but worse, you take my dignity. I just wanted... I wanted to be the king instead of... instead of...~ */
<- snip
IF ~Global("c-arankalah28","GLOBAL",0)
InParty("c-aran")
InMyArea("c-aran")
!StateCheck("c-aran",CD_STATE_NOTVALID)
Global("c-silencearan","GLOBAL",0)
~ THEN DO ~SetGlobal("c-arankalah28","GLOBAL",1)~ EXTERN ~C-ARANJ~ 10
END


IF ~~ THEN BEGIN 9 // from: 6.2
SAY #18868 /* ~You don't know... denied respect my entire life. A clown, a fool... it does not matter... I die as I... as I...~ */
<- snip
IF ~Global("c-arankalah29","GLOBAL",0)
InParty("c-aran")
InMyArea("c-aran")
!StateCheck("c-aran",CD_STATE_NOTVALID)
Global("c-silencearan","GLOBAL",0)
~ THEN DO ~SetGlobal("c-arankalah29","GLOBAL",1)~ EXTERN ~C-ARANJ~ 11
END


and the related states in Aran's file

IF ~~ THEN BEGIN 9 // from:
  SAY #103057 /* ~[ARAN] Blighted hells. All this because you don't know th' difference between bein' small on th' outside, versus bein' small in th' inside.~ */
<-- snip
  IF ~  InParty("c-aran")
InMyArea("c-aran")
!StateCheck("c-aran",CD_STATE_NOTVALID)
Global("c-silencearan","GLOBAL",0)
~ THEN EXTERN ~KALAH2~ 14
END

IF ~~ THEN BEGIN 10 // from:
  SAY #103057 /* ~[ARAN] Blighted hells. All this because you don't know th' difference between bein' small on th' outside, versus bein' small in th' inside.~ */
<-- snip
  IF ~  InParty("c-aran")
InMyArea("c-aran")
!StateCheck("c-aran",CD_STATE_NOTVALID)
Global("c-silencearan","GLOBAL",0)
~ THEN EXTERN ~KALAH2~ 15
END

IF ~~ THEN BEGIN 11 // from:
  SAY #103057 /* ~[ARAN] Blighted hells. All this because you don't know th' difference between bein' small on th' outside, versus bein' small in th' inside.~ */
<-- snip
  IF ~  InParty("c-aran")
InMyArea("c-aran")
!StateCheck("c-aran",CD_STATE_NOTVALID)
Global("c-silencearan","GLOBAL",0)
~ THEN EXTERN ~KALAH2~ 16
END

which leads us back to :

IF ~~ THEN BEGIN 14 // from:
  SAY #103058 /* ~[KALAH] You have no right to judge me.~ */
  IF ~Dead("Aerie")
!Dead("Quaylem")
~ THEN DO ~StartCutSceneMode()
EraseJournalEntry(34110)
EraseJournalEntry(34111)
SetGlobalTimer("CircusEnded","GLOBAL",ONE_DAY)
AddexperienceParty(25000)
ActionOverride("Quayle",Wait(3))
ActionOverride("Quayle",StartDialogueNoSet([PC]))
Kill(Myself)~ EXIT
  IF ~Dead("Aerie")
Dead("Quaylem")
~ THEN DO ~EraseJournalEntry(34110)
EraseJournalEntry(34111)
SetGlobalTimer("CircusEnded","GLOBAL",ONE_DAY)
AddexperienceParty(25000)
Kill(Myself)
~ EXIT
  IF ~!Dead("Aerie")
!InParty("Aerie")~ THEN DO ~StartCutSceneMode()
EraseJournalEntry(34110)
EraseJournalEntry(34111)
SetGlobalTimer("CircusEnded","GLOBAL",ONE_DAY)
AddexperienceParty(25000)
ActionOverride("Aerie",Wait(3))
ActionOverride("Aerie",StartDialogueNoSet([PC]))
Kill(Myself)~ SOLVED_JOURNAL #47528 /* ~Investigate the circus tent

The gnome illusionist who converted the circus into his own personal domain has been killed, and the tent has reverted back to normal.~ */ EXIT
  IF ~IsValidForPartyDialog("Aerie")~ THEN SOLVED_JOURNAL #47528 /* ~Investigate the circus tent

The gnome illusionist who converted the circus into his own personal domain has been killed, and the tent has reverted back to normal.~ */ EXTERN ~AERIEJ~ 36
  IF ~!IsValidForPartyDialog("Aerie")
IsValidForPartyDialog("Jaheira")~ THEN SOLVED_JOURNAL #47528 /* ~Investigate the circus tent

The gnome illusionist who converted the circus into his own personal domain has been killed, and the tent has reverted back to normal.~ */ EXTERN ~JAHEIRAJ~ 315
END

IF ~~ THEN BEGIN 15 // from:
  SAY #103058 /* ~[KALAH] You have no right to judge me.~ */
  IF ~Dead("Aerie")
!Dead("Quaylem")
~ THEN DO ~StartCutSceneMode()
EraseJournalEntry(34110)
EraseJournalEntry(34111)
SetGlobalTimer("CircusEnded","GLOBAL",ONE_DAY)
AddexperienceParty(25000)
ActionOverride("Quayle",Wait(3))
ActionOverride("Quayle",StartDialogueNoSet([PC]))
Kill(Myself)~ EXIT
  IF ~Dead("Aerie")
Dead("Quaylem")
~ THEN DO ~EraseJournalEntry(34110)
EraseJournalEntry(34111)
SetGlobalTimer("CircusEnded","GLOBAL",ONE_DAY)
AddexperienceParty(25000)
Kill(Myself)
~ EXIT
  IF ~!Dead("Aerie")
!InParty("Aerie")~ THEN DO ~StartCutSceneMode()
EraseJournalEntry(34110)
EraseJournalEntry(34111)
SetGlobalTimer("CircusEnded","GLOBAL",ONE_DAY)
AddexperienceParty(25000)
ActionOverride("Aerie",Wait(3))
ActionOverride("Aerie",StartDialogueNoSet([PC]))
Kill(Myself)~ EXIT
  IF ~IsValidForPartyDialog("Aerie")~ THEN EXTERN ~AERIEJ~ 36
  IF ~!IsValidForPartyDialog("Aerie")
IsValidForPartyDialog("Jaheira")~ THEN DO ~SetGlobalTimer("CircusEnded","GLOBAL",ONE_DAY)
AddexperienceParty(25000)
ActionOverride("Aerie",Wait(3))
ActionOverride("Aerie",StartDialogueNoSet([PC]))
Kill(Myself)~ EXTERN ~JAHEIRAJ~ 315
END

IF ~~ THEN BEGIN 16 // from:
  SAY #103058 /* ~[KALAH] You have no right to judge me.~ */
  IF ~Dead("Aerie")
!Dead("Quaylem")
~ THEN DO ~StartCutSceneMode()
EraseJournalEntry(34110)
EraseJournalEntry(34111)
SetGlobalTimer("CircusEnded","GLOBAL",ONE_DAY)
AddexperienceParty(25000)
ActionOverride("Quayle",Wait(3))
ActionOverride("Quayle",StartDialogueNoSet([PC]))
Kill(Myself)~ EXIT
  IF ~Dead("Aerie")
Dead("Quaylem")
~ THEN DO ~EraseJournalEntry(34110)
EraseJournalEntry(34111)
SetGlobalTimer("CircusEnded","GLOBAL",ONE_DAY)
AddexperienceParty(25000)
Kill(Myself)
~ EXIT
  IF ~!Dead("Aerie")
!InParty("Aerie")
~ THEN DO ~StartCutSceneMode()
EraseJournalEntry(34110)
EraseJournalEntry(34111)
SetGlobalTimer("CircusEnded","GLOBAL",ONE_DAY)
AddexperienceParty(25000)
ActionOverride("Aerie",Wait(3))
ActionOverride("Aerie",StartDialogueNoSet([PC]))
Kill(Myself)~ EXIT
  IF ~IsValidForPartyDialog("Aerie")~ THEN EXTERN ~AERIEJ~ 36
  IF ~!IsValidForPartyDialog("Aerie")
IsValidForPartyDialog("Jaheira")~ THEN EXTERN ~JAHEIRAJ~ 315
END

So, what did we learn.... well... err... we learned that the I_C_T with passback is working as intended. The actions for the state are transferred back to Kalah in this sequence.

In evaluation order (bottom to top for dialog, so it evaluates to this order):

IF aerie_can't_talk AND jaheira_can_talk THEN GOTO Jaheira to make a comment.
ELSE
IF aerie_can_talk THEN GOTO Aerie's comments
ELSE
IF aerie_not_dead AND aerie_not_in_party THEN make aerie wait and talk to PC and then clobber self
ELSE
IF aerie_is_dead AND quayle_is_dead THEN set a timer and clobber self
ELSE
IF aerie_is_dead AND quayle_is_not_dead THEN set a timer and make Quayle talk to PC and then clobber myself.

So, on a plain install, Aran's code should work just fine. We could simply say "Nope - the user is crazy. Or she has a messed up install." But when an experienced tester and user reports, it is far better to weigh in the benefit of the doubt and follow through farther - who knows what you will find.

So now, to the testing install.

First, we load up the game at a save point in C-AR01, The Broken Sword, with everything in a simple (unplayed) state. Then we have Aran join, and head over to the circus, to start the quest. This makes sure the least number of variables are set for quick troubleshooting.
Then, we play the quest. Ctr-Y is our friend - we want the opponents to die quickly, so we can stop testing and enjoy ourselves with the content.
Pass 1, Aerie Alive - works as intended; Aran does his interjection. It sends us to the correct evaluative state, too - Aerie is in the party, so she tosses her state 36 on as well:

 

IF ~~ THEN BEGIN 36 // from:
SAY #38875 /* ~What... what a pitiful little man. Everyone did laugh at him... but they didn't deserve to die. I... I just can't hate him, despite all he's done.~ */
IF ~ !IfValidForPartyDialogue("Jaheira")
~ THEN DO ~StartCutSceneMode()
EraseJournalEntry(34110)
EraseJournalEntry(34111)
SetGlobalTimer("CircusEnded","GLOBAL",ONE_DAY)
AddexperienceParty(25000)
ActionOverride("Kalah",Kill(Myself))
Wait(3)
StartDialogNoSet([PC])
~ EXIT
IF ~ IfValidForPartyDialogue("Jaheira")
~ THEN EXTERN ~JAHEIRAJ~ 315
END

and since we don't have Jaheira here, we expect the first valid dialog in AERIEJ to fire, and yep, it does...

 

IF ~ !Dead("Quaylem")
See("Quayle")
Global("AerieQ","GLOBAL",0)
Global("QuayleRaelis","GLOBAL",0)
!Global("AerieMove","GLOBAL",1)
~ THEN BEGIN 15 // from:
SAY #20308 /* ~Uncle Quayle, you're okay!~ [AERIEB6] */
IF ~~ THEN EXTERN ~QUAYLE~ 10
END


Well we know our stuff works on a clean install, now, but let's be sure and see what happens if quayle is dead, or aerie is dead, just to make sure.

Pass 2 - aerie left behind - works.
Pass 3 - killing aerie... works.
Pass 4 - Ctr-Y for you, Ctr-Y for you.... heck, Kill everyone except Aran and PC!! KILL MAIM DEEESTROOY - LEEERRROOOOOOYUYYY JEEENKIIIIIINS!

 

*ahem*.

 

Nope. It all works. Checks against the dialogue files.

OK, so we have not gotten the "hanging" behavior described. So now we REALLY are done with this we know the code as installed works.

EXCEPT - we don't know why that was happening on her game. So let's take a stick and poke in some dark corners, shall we?

Other Mod Research
First step, look for other mods tthat deal with Kalah and see what they do. Lots of utilities to do this, but since I was a weidu tester back in the day, I prefer the oldest trick in the book - text search of a whole ton of mods. Time to fire up NotePad++ and look in the Archive Of Unpacked Mods... not all the mods out there but a bunch.

We are looking for things that are messing with KALAH2.

We come up with 97 hits.

Of those some are screened out because they are patching the .cre or manipulating items or just checking the .cre for validity -

OK: D:\iecheckfiles\arath_v2.2\arath\dialogue\lk#aratj.d (1 hit) Line 4665: I_C_T KALAH2 11 LK#Arath_kalah
(uses no passback)
OK : D:\iecheckfiles\BDash168\Setup-BDToBv168.tp2 (1 hit) Line 5198: COPY_EXISTING ~KALAH2.DLG~ ~override~
(does REPLACE_TEXTUALLY ~ActionOverride("Aerie",StartDialogueNoSet())~ ~ActionOverride("Aerie",StartDialogueNoSet([PC]))~ )
OK: D:\iecheckfiles\bg2_fixpack-v9\bg2fixpack\compile\soa-dlg.d (1 hit)
(does REPLACE_ACTION_TEXT_REGEXP ~\(^aerie$\)\|\(^kalah2$\)~ ~StartDialogueNoSet()~ ~StartDialogueNoSet([PC])~ )
9and does WRITE_BYTE 0x27b 51 // alignment: chaotic evil )
OK: D:\iecheckfiles\Breagar-5.13\ACBre\D\ACBREB.D (6 hits)
Breagar uses the same basic code we do:

INTERJECT_COPY_TRANS KALAH 1 ACKALAH1
==ACBREB IF ~InParty("ACBRE")InMyArea("ACBRE")!StateCheck("ACBRE",CD_STATE_NOTVALID)~ THEN
@294
==KALAH IF ~InParty("ACBRE")InMyArea("ACBRE")!StateCheck("ACBRE",CD_STATE_NOTVALID)~ THEN
@295
END

INTERJECT_COPY_TRANS KALAH2 1 ACKALAH21
==ACBREB IF ~InParty("ACBRE")InMyArea("ACBRE")!StateCheck("ACBRE",CD_STATE_NOTVALID)~ THEN
@294
==KALAH2 IF ~InParty("ACBRE")InMyArea("ACBRE")!StateCheck("ACBRE",CD_STATE_NOTVALID)~ THEN
@295
END

INTERJECT_COPY_TRANS KALAH2 13 ACKALAH213
==ACBREB IF ~InParty("ACBRE")InMyArea("ACBRE")!StateCheck("ACBRE",CD_STATE_NOTVALID)~ THEN
@296
==KALAH2 IF ~InParty("ACBRE")InMyArea("ACBRE")!StateCheck("ACBRE",CD_STATE_NOTVALID)~ THEN
@297
END

D:\iecheckfiles\BTL\aD&L\Interj.d (7 hits) -- well, if this is the newest version, there may be troubles here. Let's look at

Line 4837: INTERJECT_COPY_TRANS2 Kalah2 4 Kalah2-4
Line 4837: INTERJECT_COPY_TRANS2 Kalah2 4 Kalah2-4
Line 4842: EXTEND_BOTTOM ~Kalah2~ 7
Line 4843: IF ~InParty("Kova") InMyArea("Kova")~ THEN EXTERN Z#KOVAJ Kalah2
Line 4847: IF ~~ THEN Z#KOVAJ Kalah2
Line 4850: == KALAH2
Line 4852: EXTERN KALAH2 9

Nope. While this uses some darned old code, the end result of all of it is to drop us back into state 9 in KALAH2. Aran will happily play his dialog from state 9. So no worries here.

OK: D:\iecheckfiles\Chloev1.5\Chloe\D\BanterCR.d (1 hit) Line 1436: I_C_T KALAH2 5 azucarCR29

OK: D:\iecheckfiles\gavin_bg2-v20\gavin_bg2\dialogue\b!gavin_interjections.d (1 hit)
Line 919: I_C_T KALAH2 6 BGavKALAH /* ~In Amn... a mage is a criminal and a gnome is a spectacle. In this tent... in my world... Kalah was the master, where none would dare to laugh...~ */

OK: D:\iecheckfiles\ImprovedAnvil_v5\Setup-ImprovedAnvil.tp2 (2 hits) just patches .cres

Cliffettes Iron modder 9 steals Kalah for a new encounter, but desosn't mess with dialog in the game, and skip a ton of mods that poke about re-fixing what Fixpack and Baldurdash do, and some more off-topic stufff... and...

OK: D:\iecheckfiles\KimNPC\KimInterject.d (1 hit)
Line 838: INTERJECT_COPY_TRANS KALAH2 13 Kimkalah
OK: D:\iecheckfiles\Kindrek\Dialogues\KINDREKJ.d (2 hits)
Line 785: INTERJECT_COPY_TRANS KALAH2 13 KindrekKalah
Line 788: == KALAH2

NOT OK - may need to go back and check latest versions, but I am sure this is fixed now as the player creating the reoprt on Aran specifically mentioned reporting it on Kivan:
D:\iecheckfiles\Kivan\d\P#KIVJ.d (2 hits)
Line 1560: I_C_T2 KALAH 4 P#KivanKalah2
Line 1564: I_C_T2 KALAH2 6 P#KivanKalah3

OK: D:\iecheckfiles\Severian\Dialogues\#SeverJ.d (1 hit) Line 812: INTERJECT_COPY_TRANS KALAH2 13 SeverKalah
OK: D:\iecheckfiles\SeverianV.01REM\Severian\Dialogues\#SeverJ.d (1 hit) Line 2941: INTERJECT_COPY_TRANS KALAH2 13 SeverKalah
OK: D:\iecheckfiles\tb_tweaks_windows_2.20\tb#tweaks\lib\romances.tpa (1 hit) Line 2: REPLACE_ACTION_TEXT_REGEXP ~\(^aerie$\)\|\(^kalah2$\)~ ~StartDialogueNoSet()~ ~StartDialogueNoSet([PC])~

heh. No worries on the Valen front after all. A quick check shows it is an accidental packaging of a /backup/ in the distro. The code is fine;
D:\iecheckfiles\Valen5Min\ValenNPC\KEVALENJ.d (3 hits)
Line 39: INTERJECT_COPY_TRANS KALAH2 9 KEValenKalahDead1
Line 44: INTERJECT_COPY_TRANS KALAH2 7 KEValenKalahDead2
Line 49: INTERJECT_COPY_TRANS KALAH2 8 KEValenKalahDead3

ALERT. Ok, we have a potential "old code might hijack this dialog" ...
D:\iecheckfiles\zymisc2\Z#Misc\Dialogue\Interj.d (7 hits)
Line 4825: INTERJECT_COPY_TRANS2 Kalah2 4 Kalah2-4
Line 4825: INTERJECT_COPY_TRANS2 Kalah2 4 Kalah2-4
Line 4830: EXTEND_BOTTOM ~Kalah2~ 7
Line 4831: IF ~InParty("Kova") InMyArea("Kova")~ THEN EXTERN Z#KOVAJ Kalah2
Line 4835: IF ~~ THEN Z#KOVAJ Kalah2
Line 4838: == KALAH2
Line 4840: EXTERN KALAH2 9

But nope. It eventually winds itself back to KALAH2 9, and a check of the code shows that while it will re-path from 4 to 9 dropping any I_C_T[234] on the chopping block, Aran hits 9 so we are all good. Plus, the actions are maintained.

SOOOOOO... several hours later, a bunch of messing about, and what do we know?

We can say "Doesn't look like us or a mod interaction on our end." We have some specific answers as to whom else thought this was a good place to interject. The only things left are glitches in evaluating SDNS(Player1) or Wait(3) or smallwait; all those things are engine things and script things that we can't control, as it is not our dialog that is processing that, it is Aerie's/Quayle's/Kalah's. And we can go back to modding.

To be safe, because these things do pop up, I am marking this "Unable To Replicate" and we'll see what comes up. In the meantime, I will pop over and make sure Kivan has his code done up right. Wouldn't want the lad to accidentally leave anything dangling in the wind, y'know.

Link to comment

Archived

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

×
×
  • Create New...