DavidW Posted July 14, 2019 Share Posted July 14, 2019 A large part of NPC modding is the interjections into existing conversations, and WEIDU has a lot of technology to help with it. I recently wrote some functions to supplement that technology a bit; this series of posts is an explanation. In this first post I'll explain current best practice for these interjections. (Much of this material is also covered in a very informative series of posts by Kaeloree at Spellhold Studios.) In the second post I'll say what I think is wrong with it; in the third post I'll suggest a way to improve on it. Basic interjections work like this. An existing dialog block might look like: IF ~~ THEN BEGIN 14 SAY ~Hello, O great adventurer.~ IF ~~ THEN REPLY ~Finally, someone recognises my greatness!~ GOTO 15 IF ~~ THEN REPLY ~Aw, shucks, I'm not so great as all that.~ GOTO 16 END A simple interjection, most straightforwardly coded with INTERJECT_COPY_TRANS, changes it to something like IF ~~ THEN BEGIN 14 SAY ~Hello, O great adventurer.~ IF ~~ THEN REPLY ~Finally, someone recognises my greatness!~ GOTO 15 IF ~~ THEN REPLY ~Aw, shucks, I'm not so great as all that.~ GOTO 16 IF ~InParty("imoen2")~ THEN EXTERN imoen2 17 END <imoen block> IF ~~ THEN BEGIN 17 SAY ~Hey, you'll give <CHARNAME> delusions of grandeur!~ IF ~~ THEN REPLY ~Finally, someone recognises my greatness!~ EXTERN original_npc 15 IF ~~ THEN REPLY ~Aw, shucks, I'm not so great as all that.~ EXTERN original_npc 16 END In other words, if Imoen is present then the dialog skips to her interjection; the player then gets offered the original reply options from her dialog. That works fine *unless* there are DO blocks in the original. Suppose, say, that the original block was: IF ~~ THEN BEGIN 14 SAY ~Hello, O great adventurer. Have some gold as a token of your magnificence.~ IF ~~ THEN REPLY ~Finally, someone recognises my greatness!~ DO ~GivePartyGold(500)~ GOTO 15 IF ~~ THEN REPLY ~Aw, shucks, I'm not so great as all that.~ DO ~GivePartyGold(500)~ GOTO 16 END If those blocks move to Imoen, then it will be her, not the NPC, who tries to give the gold to the party, and that doesn't work - she doesn't have the gold in her inventory. (It would be even more dramatic if there was an Enemy() command!) One solution - this is what INTERJECT_COPY_TRANS2 does - is to move the GivePartyGold command to the original Imoen reply, as in: IF ~~ THEN BEGIN 14 SAY ~Hello, O great adventurer. Have some gold as a token of your magnificence.~ IF ~~ THEN REPLY ~Finally, someone recognises my greatness!~ DO ~GivePartyGold(500)~ GOTO 15 IF ~~ THEN REPLY ~Aw, shucks, I'm not so great as all that.~ DO ~GivePartyGold(500)~ GOTO 16 IF ~InParty("imoen2")~ THEN DO ~GivePartyGold(500)~ EXTERN imoen2 17 END But modders are generally advised against this, as it only works if all the DO actions are the same. Suppose instead that the original block looks like IF ~~ THEN BEGIN 14 SAY ~Hello, O great adventurer. Have some gold as a token of your magnificence.~ IF ~~ THEN REPLY ~Finally, someone recognises my greatness!~ DO ~GivePartyGold(500)~ GOTO 15 IF ~~ THEN REPLY ~Aw, shucks, I'm not so great as all that. Your need is greater than mine - keep the gold.~ GOTO 16 END Now there's no way to move 'the' DO command to Imoen's interjection, because there's no single DO command. Because of this problem, current best practice is to include a 'passback' - a block in the original NPC's dialog that the original replies move to. With a passback, the original dialog gets modified to: IF ~~ THEN BEGIN 14 SAY ~Hello, O great adventurer. Have some gold as a token of your magnificence.~ IF ~~ THEN REPLY ~Finally, someone recognises my greatness!~ DO ~GivePartyGold(500)~ GOTO 15 IF ~~ THEN REPLY ~Aw, shucks, I'm not so great as all that. Your need is greater than mine - keep the gold.~ GOTO 16 IF ~~ THEN GOTO 99 END IF ~~ THEN BEGIN 99 SAY ~Yes, you really are great.~ IF ~~ THEN REPLY ~Finally, someone recognises my greatness!~ DO ~GivePartyGold(500)~ GOTO 15 IF ~~ THEN REPLY ~Aw, shucks, I'm not so great as all that. Your need is greater than mine - keep the gold.~ GOTO 16 END In the original block (block 14) the original replies never have a chance to fire - the dialog automatically jumps to block 99. After an extra line of dialog, the original replies are there. Now when Imoen's interjection is added, we get IF ~~ THEN BEGIN 14 SAY ~Hello, O great adventurer. Have some gold as a token of your magnificence.~ IF ~~ THEN REPLY ~Finally, someone recognises my greatness!~ DO ~GivePartyGold(500)~ GOTO 15 IF ~~ THEN REPLY ~Aw, shucks, I'm not so great as all that. Your need is greater than mine - keep the gold.~ GOTO 16 IF ~~ THEN GOTO 99 IF ~InParty("imoen2")~ THEN EXTERN imoen2 17 END IF ~~ THEN BEGIN 99 SAY ~Yes, you really are great.~ IF ~~ THEN REPLY ~Finally, someone recognises my greatness!~ DO ~GivePartyGold(500)~ GOTO 15 IF ~~ THEN REPLY ~Aw, shucks, I'm not so great as all that. Your need is greater than mine - keep the gold.~ GOTO 16 END <imoen block> IF ~~ THEN BEGIN 17 SAY ~Hey, you'll give <CHARNAME> delusions of grandeur!~ IF ~~ THEN REPLY ~Finally, someone recognises my greatness!~ DO ~GivePartyGold(500)~ EXTERN original_npc 15 IF ~~ THEN REPLY ~Aw, shucks, I'm not so great as all that. Your need is greater than mine - keep the gold.~ EXTERN original_npc 16 IF ~~ THEN GOTO 99 END The transitions are copied to Imoen's block, and again the original replies never have a chance to fire. Instead we're routed back to the passback. The original replies are then offered to the player, and because they're being offered by the original NPC, there's no problem with actions being associated to the wrong creature. All this normally gets implemented via the INTERJECT_COPY_TRANS3 command in WEIDU. Here's an example from BG1NPC (bg1npc/phase2/dlg/x#ict3.d): /* Davaeorn */ /* passback supplied */ I_C_T3 ~tutu_varDAVAEO~ 0 X#DAVAEO0 == ~YESLIJ~ IF ~InParty("yeslick") InMyArea("yeslick") !StateCheck("yeslick",CD_STATE_NOTVALID)~ THEN @206 == ~DAVAEO~ IF ~InParty("yeslick") InMyArea("yeslick") !StateCheck("yeslick",CD_STATE_NOTVALID)~ THEN @207 == ~DYNAHJ~ IF ~InParty("dynaheir") InMyArea("dynaheir") !StateCheck("dynaheir",CD_STATE_NOTVALID)~ THEN @208 == ~IMOENJ~ IF ~InParty("imoen") InMyArea("imoen") !StateCheck("imoen",CD_STATE_NOTVALID) !Class("imoen",MAGE_ALL)~ THEN @209 == ~CORANJ~ IF ~InParty("coran") InMyArea("coran") !StateCheck("coran",CD_STATE_NOTVALID)~ THEN @210 == ~SHARTJ~ IF ~InParty("sharteel") InMyArea("sharteel") !StateCheck("sharteel",CD_STATE_NOTVALID)~ THEN @211 == ~FALDOJ~ IF ~InParty("faldorn") InMyArea("faldorn") !StateCheck("faldorn",CD_STATE_NOTVALID)~ THEN @212 == ~DAVAEO~ IF ~InParty("faldorn") InMyArea("faldorn") !StateCheck("faldorn",CD_STATE_NOTVALID)~ THEN @213 == ~KIVANJ~ IF ~InParty("kivan") InMyArea("kivan") !StateCheck("kivan",CD_STATE_NOTVALID)~ THEN @214 == ~VICONJ~ IF ~InParty("viconia") InMyArea("viconia") !StateCheck("viconia",CD_STATE_NOTVALID)~ THEN @215 == ~DAVAEO~ @216 END The last line is the passback. All the other lines are the various interjections by party members. (For readability I've removed the various bits of BG1NPC code that localise this to BGT/BG1TUTU/BGEE.) Quote Link to comment
DavidW Posted July 14, 2019 Author Share Posted July 14, 2019 The passback solution is elegant and reliable: so what's wrong with it? One problem is that the code it generates is very cluttered. You can see this in the Imoen example in the last post: there are extra REPLYs in Imoen's block and in block 14 that are guaranteed never to fire. In realistic code it gets much, much worse than that - if you actually implement that Davaeorn interject (e.g. by installing BG1NPC) and then do 'weidu davaeo.dlg --transitive' you'll see what I mean. There are many copies of each block, and long strings of reply options that are just along for the ride, and if you try to make sense of the dialog block, it's almost impossible to understand the logic. But this doesn't matter that much: you can't see that tangle in-game, it doesn't slow things down, and trying to modify existing interjections in another mod is an edge case at best. The real problem is that the passback blocks upset the flow of the dialog. Look at Davaeorn again: in the unmodded game, his line to you is the marvellously dismissive Why have you come? Is it to steal my riches or perhaps you seek to righteously punish me for my affront to your morality? It matters little, for you will do neither. Before I dispose of you in some horribly gruesome manner, perhaps I should introduce myself. I am known as Davaeorn. I would ask you for your names, but I care little to become acquainted with the dead. after which he goes hostile and attacks without any further comment. In BG1NPC, if you encounter Davaeorn without any NPCs present, this becomes Why have you come? Is it to steal my riches or perhaps you seek to righteously punish me for my affront to your morality? It matters little, for you will do neither. Before I dispose of you in some horribly gruesome manner, perhaps I should introduce myself. I am known as Davaeorn. I would ask you for your names, but I care little to become acquainted with the dead. Now that we have been introduced, it is time for you to die! The second line is the passback, which you also get following any interjections from party NPCs. To my ear, that reads much less well. And in general, I think it's hard to write passbacks that don't just clutter and slow the flow of dialog (especially since you're having to add them in to dialogs which weren't originally written to facilitate them.) What's the alternative? Well, as it happens Davaeorn doesn't need a passback at all: there's only one DO command in his original replies - ~Enemy()~ and that can be moved, INTERJECT_COPY_TRANS2 style, to the NPC interjections. Even if that wasn't the case, there's no need for a passback if there are no NPCs present, and there's no need for a passback if the last interjection comes from Yeslick or Faldorn, since - as you can see from the block I pasted above - in those cases Davaeorn has a reply to that NPC already. But of course coding that gets very cumbersome and quite vulnerable to possible changes made by other mods (say, if someone else added a 'talk Davaeorn down' mod). The solution is to automate it: I'll explain how that works in the next post. Quote Link to comment
DavidW Posted July 14, 2019 Author Share Posted July 14, 2019 The function 'compile_with_ict_handling' is intended to address these issues. It has one argument, 'dialog', which should be the full name and path of a dialog file containing I_C_T3 (and I_C_T4) blocks. When it runs, it goes through each ICT block and modifies the dialog to implement the interjections. The passback is only included if it's needed, which is to say: if there are multiple REPLYs to the original block with different code in (or if any block uses StartStore(), which causes trouble if it's moved). And the passback is only used in interjection chains where it's needed (i.e., when the original NPC doesn't get the last word in any case). If a passback is needed but there isn't one already, a blank one is added, i.e., the NPC replies but doesn't say anything. (That's a bit untidy, but the alternative would be breaking the logic of the dialog.) As a bonus, the code created is much tidier, without extra block copies and unused replies. Once all the I_C_Tx blocks are implemented, any remaining dialog is COMPILEd normally. (There are limitations here: if there are cross-references between I_C_Tx and non-I_C_Tx blocks, the code will fail.) I've implemented this in a fork of BG1NPC, available here. The function is contained in "lib/lib_interject.tpa"; it also requires some functions in "lib/alter_dlg.tpa". The only change in BG1NPC's code itself (other than turning on the AUTO_EVAL_STRINGS setting) is a replacement, in bg1npc.tp2, of COMPILE EVALUATE_BUFFER ~bg1npc/Phase2/dlg/X#ICT3.D~ with WITH_TRA "bg1npc\tra\english\x#ict3.tra" "bg1npc\tra\%LANGUAGE%\x#ict3.tra" BEGIN LAF compile_with_ict_handling STR_VAR dialog="bg1npc/Phase2/dlg/x#ict3.d" END END (I have to give it the dialog file explicitly, since my code doesn't interact with AUTO_TRA.) Thoughts and feedback very welcome. Quote Link to comment
jastey Posted July 15, 2019 Share Posted July 15, 2019 This means the passback line would be spared if no other mod added differently triggered transactions, but would play out like it is now if the line would be needed because of the transactions? That sounds very cool. The main reason I am using I_C_T(3) with passback as a standard is because I can't be sure another mod didn't add more transactions to the dialogue state I am interjecting to. With this function, the added passback would just be a savety measure in case that's the case. For passback line the function just takes the last line with the original NPC talking? What exactly does this do, what is changed in the dialogue, do you mean the dlg? 17 hours ago, DavidW said: goes through each ICT block and modifies the dialog to implement the interjections I do not agree that an empty dialogue box wouldn't disturb the flow of the dialogue, though. For me, an empty box would more look like a bug or "mod added". All in all this sounds like a really good thing to get rid of at least some of the passback lines. Quote Link to comment
DavidW Posted July 15, 2019 Author Share Posted July 15, 2019 Yes, I mean the dlg. And passback line is the last line of the ICT3, provided it’s spoken by the original NPC with no conditions (I’m following bg1npc conventions here). As for an empty passback: yes, I agree, it’s ugly and breaks the flow. But in the situations where I use it, the alternative is a logic error that can break the game, so I think it’s the least worst option. (It’s only going to come up if the function gets handed an ICT3 that should have had a passback but doesn’t.) Quote Link to comment
jastey Posted July 15, 2019 Share Posted July 15, 2019 Ah, I didn't catch you are explicitely talking about unconditioned passback lines (because I obviously didn't look at the example thoroughly enough). BG1NPC had the problem of combining lines for several NPCs where there is no logixal trigger to account for one of them present. Usually, in a mod it is one NPC and then the passback line bears the trigger of this NPC beging present. Since some of my NPCs like to babble a comment into CHARNAME's ear which the original game character isn't even supposed to hear, lest alone comment on it, would it be possible to include the last line spoken by the original DLG as a passback line in your function, no matter the conditions? I can provide examples if you want later when I am back at my computer. Quote Link to comment
DavidW Posted July 15, 2019 Author Share Posted July 15, 2019 Sure, give me an example. (The code runs for everything in x#ict3, but of course it's possible that the block it's producing isn't working ideally.) Quote Link to comment
jastey Posted April 7, 2020 Share Posted April 7, 2020 Hi @DavidW, for BG1NPC I used the function in the following way: include the function definition in the ALWAYS block (inside "bg1npc_always.tpa"): INCLUDE "bg1npc/lib/lib_interject.tpa" INCLUDE "bg1npc/lib/alter_dlg.tpa" Then calling the function to compile the files with the multiple interjections "x#ict3.d" (function call in "bg1npc_phase2.tpa"): WITH_TRA "bg1npc\tra\english\x#ict3.tra" "bg1npc\tra\%LANGUAGE%\x#ict3.tra" BEGIN LAF compile_with_ict_handling STR_VAR dialog="bg1npc/Phase2/dlg/X#ICT3.D" END END When looking at the game files after installing the mod (component "Quest, Banters and Interjections"), there are some interjections missing in the game. For example, Branwen and Quayle's interjection into GAZIB.dlg (tested in BGT). This is what the I_C_T3 inside the "x#ict3.d" looks like: /* Great Gazib, the */ /* passback not required - trans action is CreateCreature() */ I_C_T3 ~%tutu_var%GAZIB~ 1 X#GAZIB1 == ~%BRANWEN_JOINED%~ IF ~InParty("branwen") InMyArea("branwen") !StateCheck("branwen",CD_STATE_NOTVALID)~ THEN @699 == ~%QUAYLE_JOINED%~ IF ~InParty("quayle") InMyArea("quayle") !StateCheck("quayle",CD_STATE_NOTVALID)~ THEN @700 END Compiled using the function as described above, the GAZIB.dlg looks like this - no interjections are added to state 1: Spoiler // creator : D:\NearInfinity-20180615\NearInfinity.jar (v2.1-20180615) // game : D:\BGT // resource : GAZIB.DLG // source : Override\GAZIB.DLG // dialog : dialog.tlk // dialogF : (none) BEGIN ~GAZIB~ IF ~ NumberOfTimesTalkedTo(0) ~ THEN BEGIN 0 // from: SAY #79830 /* ~Hi, come well and welcome! You have stumbled upon The Great Gazib Show, starring yours truly, the Great Gazib!!! Allow me to introduce the Amazing Oopah, the world's only exploding ogre!~ */ IF ~~ THEN DO ~CreateCreature("OOPAH",[1282.3222],1) ~ EXIT IF ~ Global("X#GarGaz","GLOBAL",0) InParty("garrick") InMyArea("garrick") !StateCheck("garrick",CD_STATE_NOTVALID) ~ THEN DO ~SetGlobal("X#GarGaz","GLOBAL",1) ~ EXTERN ~GARRIJ~ 71 END IF ~ NumberOfTimesTalkedTo(1) Dead("Oopah") ~ THEN BEGIN 1 // from: SAY #79831 /* ~Fun for the whole family! Now, let's try that crowd-pleaser one more time.~ */ IF ~~ THEN DO ~CreateCreature("OOPAH",[1282.3222],1) ~ EXIT END IF ~ GlobalGT("SPRITE_IS_DEADOopah","GLOBAL",1) ~ THEN BEGIN 2 // from: SAY #79832 /* ~You're either a die-hard fan or a sadist, friend... (No, Oopah, just one more, one last one, then you can go back to the tent... Oopah, put the weapon down... Oopah?) AAaeee!~ */ IF ~~ THEN DO ~CreateCreature("OOPAH2",[1282.3222],1) EscapeAreaDestroy(90) ~ EXIT IF ~ Global("X#VicGaz","GLOBAL",0) InParty("viconia") InMyArea("viconia") !StateCheck("viconia",CD_STATE_NOTVALID) ~ THEN DO ~SetGlobal("X#VicGaz","GLOBAL",1) ~ EXTERN ~VICONIJ~ 292 END IF ~~ THEN BEGIN 3 // from: SAY #119010 /* ~Step right up!~ */ IF ~~ THEN DO ~CreateCreature("OOPAH",[1282.3222],1) ~ EXIT END IF ~~ THEN BEGIN 4 // from: SAY #89489 /* ~Noooo!~ */ IF ~~ THEN DO ~CreateCreature("OOPAH2",[1282.3222],1) EscapeAreaDestroy(90) ~ EXIT END If I disable the function and compile the file directly, the GAZIB.dlg looks like this, now the interjections are added to state 1: Spoiler // creator : D:\NearInfinity-20180615\NearInfinity.jar (v2.1-20180615) // game : D:\BGT // resource : GAZIB.DLG // source : Override\GAZIB.DLG // dialog : dialog.tlk // dialogF : (none) BEGIN ~GAZIB~ IF ~ NumberOfTimesTalkedTo(0) ~ THEN BEGIN 0 // from: SAY #79830 /* ~Hi, come well and welcome! You have stumbled upon The Great Gazib Show, starring yours truly, the Great Gazib!!! Allow me to introduce the Amazing Oopah, the world's only exploding ogre!~ */ IF ~~ THEN DO ~CreateCreature("OOPAH",[1282.3222],1) ~ EXIT IF ~Global("X#GarGaz","GLOBAL",0) InParty("garrick") InMyArea("garrick") !StateCheck("garrick",CD_STATE_NOTVALID) ~ THEN DO ~SetGlobal("X#GarGaz","GLOBAL",1)~ EXTERN ~GARRIJ~ 71 END IF ~ NumberOfTimesTalkedTo(1) Dead("Oopah") ~ THEN BEGIN 1 // from: SAY #79831 /* ~Fun for the whole family! Now, let's try that crowd-pleaser one more time.~ */ IF ~~ THEN DO ~CreateCreature("OOPAH",[1282.3222],1) ~ EXIT IF ~Global("X#GAZIB1","GLOBAL",0) InParty("quayle") InMyArea("quayle") !StateCheck("quayle",CD_STATE_NOTVALID) ~ THEN DO ~SetGlobal("X#GAZIB1","GLOBAL",1)~ EXTERN ~QUAYLJ~ 52 IF ~Global("X#GAZIB1","GLOBAL",0) InParty("branwen") InMyArea("branwen") !StateCheck("branwen",CD_STATE_NOTVALID) ~ THEN DO ~SetGlobal("X#GAZIB1","GLOBAL",1)~ EXTERN ~BRANWJ~ 102 END IF ~ GlobalGT("SPRITE_IS_DEADOopah","GLOBAL",1) ~ THEN BEGIN 2 // from: SAY #79832 /* ~You're either a die-hard fan or a sadist, friend... (No, Oopah, just one more, one last one, then you can go back to the tent... Oopah, put the weapon down... Oopah?) AAaeee!~ */ IF ~~ THEN DO ~CreateCreature("OOPAH2",[1282.3222],1) EscapeAreaDestroy(90) ~ EXIT IF ~Global("X#VicGaz","GLOBAL",0) InParty("viconia") InMyArea("viconia") !StateCheck("viconia",CD_STATE_NOTVALID) ~ THEN DO ~SetGlobal("X#VicGaz","GLOBAL",1)~ EXTERN ~VICONIJ~ 292 END IF ~~ THEN BEGIN 3 // from: SAY #119010 /* ~Step right up!~ */ IF ~~ THEN DO ~CreateCreature("OOPAH",[1282.3222],1) ~ EXIT END IF ~~ THEN BEGIN 4 // from: SAY #89489 /* ~Noooo!~ */ IF ~~ THEN DO ~CreateCreature("OOPAH2",[1282.3222],1) EscapeAreaDestroy(90) ~ EXIT END Maybe I am using it wrong, but from what I see currently it seems the function removes interjections completely. Quote Link to comment
jastey Posted April 15, 2020 Share Posted April 15, 2020 Another finding in the usage of this function for BG1NPC: It appears that I_C_T3 is turned into a I_C_T4 behavior. What I mean is that actions at the end of the dialogue state are put into the transactions to the interjecting NPC, not into the transactions *after* the NPC spoke the interjection. - If this is intended then it might be of interest that this lead to problems in this specific example: After the group is arrested in BG city and put to prison, the help of Neb is needed to leave the prison. A cutscene is started which lets the group exit the prison: Spoiler IF ~~ THEN BEGIN 8 // from: 7.4 SAY #76466 /* ~Let the solars come and I shall kill them too! In a world without justice, the gods are little more than a divine puppet show. Come, the tunnel's through the wall, here, and it is time we cut ourselves free from this confining womb.~ */ IF ~~ THEN DO ~StartCutScene("Capcut02") ~ EXIT END BG1NPC adds interjections of several NPCs using I_C_T3 which give this if compiled conventionally. The transitions point to the NPCs' interjections, the start of the cutscene is moved after the interjections: Spoiler IF ~~ THEN BEGIN 8 // from: 7.4 20.4 SAY #76466 /* ~Let the solars come and I shall kill them too! In a world without justice, the gods are little more than a divine puppet show. Come, the tunnel's through the wall, here, and it is time we cut ourselves free from this confining womb.~ */ IF ~~ THEN DO ~StartCutScene("Capcut02") ~ EXIT IF ~Global("X#NEB8","GLOBAL",0) InParty("jaheira") InMyArea("jaheira") !StateCheck("jaheira",CD_STATE_NOTVALID) ~ THEN DO ~SetGlobal("X#NEB8","GLOBAL",1)~ GOTO 27 IF ~Global("X#NEB8","GLOBAL",0) InParty("jaheira") InMyArea("jaheira") !StateCheck("jaheira",CD_STATE_NOTVALID) ~ THEN DO ~SetGlobal("X#NEB8","GLOBAL",1)~ EXTERN ~JAHEIRAJ~ 916 IF ~Global("X#NEB8","GLOBAL",0) InParty("dynaheir") InMyArea("dynaheir") !StateCheck("dynaheir",CD_STATE_NOTVALID) ~ THEN DO ~SetGlobal("X#NEB8","GLOBAL",1)~ GOTO 29 IF ~Global("X#NEB8","GLOBAL",0) InParty("dynaheir") InMyArea("dynaheir") !StateCheck("dynaheir",CD_STATE_NOTVALID) ~ THEN DO ~SetGlobal("X#NEB8","GLOBAL",1)~ EXTERN ~DYNAJ~ 215 IF ~Global("X#NEB8","GLOBAL",0) InParty("kivan") InMyArea("kivan") !StateCheck("kivan",CD_STATE_NOTVALID) ~ THEN DO ~SetGlobal("X#NEB8","GLOBAL",1)~ GOTO 33 IF ~Global("X#NEB8","GLOBAL",0) InParty("kivan") InMyArea("kivan") !StateCheck("kivan",CD_STATE_NOTVALID) ~ THEN DO ~SetGlobal("X#NEB8","GLOBAL",1)~ EXTERN ~KIVANJ~ 369 IF ~Global("X#NEB8","GLOBAL",0) InParty("ajantis") InMyArea("ajantis") !StateCheck("ajantis",CD_STATE_NOTVALID) ~ THEN DO ~SetGlobal("X#NEB8","GLOBAL",1)~ GOTO 39 IF ~Global("X#NEB8","GLOBAL",0) InParty("ajantis") InMyArea("ajantis") !StateCheck("ajantis",CD_STATE_NOTVALID) ~ THEN DO ~SetGlobal("X#NEB8","GLOBAL",1)~ EXTERN ~AJANTJ~ 256 END Using the Smart I_C_T Function, the starting of the cutscene is moved into the transaction to the NPCs' interjections. This lead to the situation that the cutscene started while the dialogue box was still open, making the game hang: Spoiler IF ~~ THEN BEGIN 8 // from: 7.4 20.4 SAY #76466 /* ~Let the solars come and I shall kill them too! In a world without justice, the gods are little more than a divine puppet show. Come, the tunnel's through the wall, here, and it is time we cut ourselves free from this confining womb.~ */ IF ~~ THEN DO ~StartCutScene("Capcut02") ~ EXIT IF ~ Global("X#NEB8","GLOBAL",0) InParty("jaheira") InMyArea("jaheira") !StateCheck("jaheira",CD_STATE_NOTVALID) ~ THEN DO ~SetGlobal("X#NEB8","GLOBAL",1) StartCutScene("Capcut02") ~ EXTERN ~JAHEIRAJ~ 802 IF ~ Global("X#NEB8","GLOBAL",0) InParty("dynaheir") InMyArea("dynaheir") !StateCheck("dynaheir",CD_STATE_NOTVALID) ~ THEN DO ~SetGlobal("X#NEB8","GLOBAL",1) StartCutScene("Capcut02") ~ EXTERN ~DYNAJ~ 155 IF ~ Global("X#NEB8","GLOBAL",0) InParty("kivan") InMyArea("kivan") !StateCheck("kivan",CD_STATE_NOTVALID) ~ THEN DO ~SetGlobal("X#NEB8","GLOBAL",1) StartCutScene("Capcut02") ~ EXTERN ~KIVANJ~ 308 IF ~ Global("X#NEB8","GLOBAL",0) InParty("ajantis") InMyArea("ajantis") !StateCheck("ajantis",CD_STATE_NOTVALID) ~ THEN DO ~SetGlobal("X#NEB8","GLOBAL",1) StartCutScene("Capcut02") ~ EXTERN ~AJANTJ~ 216 END Quote Link to comment
DavidW Posted May 14, 2020 Author Share Posted May 14, 2020 Belatedly seeing this - I'll have a look. Clearly there are cases I wasn't considering. Quote Link to comment
Recommended Posts
Join the conversation
You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.