Author Topic: INTERJECT, COPY_TRANS, huh?  (Read 19743 times)

0 Members and 1 Guest are viewing this topic.

Offline berelinde

  • Global Moderator
  • Level 3
  • *****
  • Posts: 1011
  • Karma: +102/-31
    • View Profile
INTERJECT, COPY_TRANS, huh?
« on: July 11, 2012, 06:24:50 AM »
When you make an NPC, you may want him to interrupt a game NPC's dialogue with remarks of his own. Or maybe you're working on a quest and would like to use an existing game NPC as a quest giver. You're going to have to use some kind of interjection to make this happen.

INTERJECT_COPY_TRANS

If you're writing an NPC mod, INTERJECT_COPY_TRANS will be the workhorse of your interjection toolbox. Let's address that one first.

You want your NPC, Will Doit, to respond to Lady Yuth's greeting. She is the scroll merchant in the Adventure Mart. The first thing you will need to do is decompile Lady Yuth's dialogue using WeiDU. After you have identified her dialogue file, SCROLL01.dlg, you decompile it and find that state 0 is her greeting state. Will Doit's joined dialogue file is MPWillJ and his DV is MPWillDoit.

Code: [Select]

IF ~!Global("stolenfrom","LOCALS",1)~ THEN BEGIN 0 // from:
  SAY #46479 /* ~Hello there.  I've been watching you since you entered...I suspect you may have need of my wares.~ */
  IF ~~ THEN REPLY #46481 /* ~What is it that you sell?~ */ DO ~SetGlobal("YuthTalk","AR0702",1)~ GOTO 2
  IF ~~ THEN REPLY #46519 /* ~I don't think so, maybe another time.~ */ GOTO 1
  IF ~Global("YuthTalk","AR0702",1)~ THEN REPLY #61053 /* ~Let me see what you have.~ */ DO ~StartStore("scrolls",LastTalkedToBy())~ EXIT
END


This is the syntax for your interjection.

Code: [Select]

INTERJECT_COPY_TRANS SCROLL01 0 MPSCROLL01.0
== MPWILLJ IF ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID)~ THEN ~You've been watching us, eh?~
== SCROLL01 IF ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID) ReputationLT(Player1,14)~ THEN ~You do have a rather shifty look about you.~
== SCROLL01 IF ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID) !ReputationLT(Player1,14)~ THEN ~Force of habit.~
END


Easy enough. Let's take it apart.

INTERJECT_COPY_TRANS, also known as I_C_T, is the WeiDU command that makes it happen.

SCROLL01 is Lady Yuth's dialogue file.

0 is the state where you want the interjection to occur.

MPSCROLL01.0 this is the state label. For all interjections, this will get set as a global variable, so if you look at the global variables in the saved game, you will see this listed as MPSCROLL01.0 = 1. This is a great way to check to see if a particular interjection happened, area was visited, or what have you. If the NPC commented, he was there.

== : the first = opens a new line of dialogue, i.e. changes the text in the dialogue window, and the second = changes the speaker. We need a new dialogue window and we need to change the speaker from Lady Yuth to Will.

MPWILLJ is Will's joined dialogue file.

IF ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID) ReputationLT(Player1,14)~ is the trigger section.

InParty("MPWillDoit"), because you don't want this interjection to occur if Will is not in the party.
InMyArea("MPWillDoit"), because you don't want this interjection to occur if Will got left outside in Waukeen's Promenade.
!StateCheck("WillDoit",CD_STATE_NOTVALID), is a custom status that eliminates dialogue by dead, stunned, or silenced creatures. It exists because IsValidForPartyDialogue() doesn't always work as it should. You can use it in your mod by inserting this at the beginning of your tp2
Code: [Select]

  /* STATE.IDS patching to ToB - thanks, CamDawg, if you read it */
  /* adds custom IsValidForPartyDialogue state */
  APPEND ~STATE.IDS~ ~0x80101FEF CD_STATE_NOTVALID~ UNLESS ~CD_STATE_NOTVALID~

ReputationLT(Player1,14), because if the party is walking around with a 20 reputation, Lady Yuth had better not have a problem with the party.

So, does Lady Yuth have to say anything after Will? Can't he have the last word? Here, he can. I_C_T transfers the actions of the transitions (PC replies) to the last speaker, so if Lady Yuth did not say anything, Will would have to set the globals and start the store. Here, this isn't a problem becaue any creature may set globals or start a store, but if one of those transition actions was Kill(Myself), that would create a problem for Will. It's better to accept the fact that Will won't always have the last word.

When to use INTERJECT_COPY_TRANS: Anytime you want to interrupt a speaker. Sometimes, the original speaker will have to reply to the NPC.

Offline berelinde

  • Global Moderator
  • Level 3
  • *****
  • Posts: 1011
  • Karma: +102/-31
    • View Profile
INTERJECT, COPY_TRANS, huh?
« Reply #1 on: July 11, 2012, 06:27:40 AM »
INTERJECT_COPY_TRANS2

What happens if you really, really want your NPC to have the last word? You could use I_C_T2. It works like I_C_T except that the actions in the transition are not transferred to the interjector.

This one is dangerous because it has the potential to screw up your game if you aren't really careful, so let's pick it apart and use it successfully... and then try to use it unsuccessfully and find out what goes wrong.

For this one, we're going to have Will complain about opening the sarcophagus in the Unseeing Eye sewer complex. The Shade Lich's dialogue file is BHCRYPT.

Code: [Select]

IF ~True()~ THEN BEGIN 0 // from:
  SAY #35892 /* ~You too will die for disturbing my rest.~ */
  IF ~~ THEN DO ~Enemy()~ EXIT
END


I want Will to say "Wow, I guess you really aren't a morning person!" but there really isn't a suitable reply. What is the lich supposed to say? "Hssss!"? But I don't want Will going hostile, either. I_C_T2 to the rescue.

Code: [Select]

I_C_T2 BHCRYPT 0 BHalCrypt
== MPWILLJ IF ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID)~ THEN ~Wow, I guess you really aren't a morning person!~
END


It looks simple and straightforward, right? Why wouldn't you use this all the time? The reason is because it produces undesired results when there is more than one transition that follows. The first transition or PC reply will *always* be chosen. Let's go back to Lady Yuth's dialogue.

Code: [Select]

IF ~!Global("stolenfrom","LOCALS",1)~ THEN BEGIN 0 // from:
  SAY #46479 /* ~Hello there.  I've been watching you since you entered...I suspect you may have need of my wares.~ */
  IF ~~ THEN REPLY #46481 /* ~What is it that you sell?~ */ DO ~SetGlobal("YuthTalk","AR0702",1)~ GOTO 2
  IF ~~ THEN REPLY #46519 /* ~I don't think so, maybe another time.~ */ GOTO 1
  IF ~Global("YuthTalk","AR0702",1)~ THEN REPLY #61053 /* ~Let me see what you have.~ */ DO ~StartStore("scrolls",LastTalkedToBy())~ EXIT
END


If I tried an I_C_T2 here, the store wouldn't open. You would just set the variable that Yuth was talked to. That's because the actions of the first transition are always performed, regardless of how many transitions there were. For NPCs where you can talk to the NPC over and over again, this isn't tragic, but it is inconvenient. There are other places in the game where blocking other transition actions would be catastrophic. There are game NPCs that you can only talk to once. Is it really, really that important that your NPC has the last word? And who knows? If you've got a good sense of humor, you may find it entertaining to let the game NPCs slap down your clever little NPC every once in a while. I know I do.

When to use I_C_T2: As infrequently as possible. Otherwise, use it when you want your NPC to interrupt the speaker but not to do the things the speaker is supposed to do, like die. If you're going to use it, make absolutely sure that the state you are interjecting into has only one possible transition, like EXIT.


Offline berelinde

  • Global Moderator
  • Level 3
  • *****
  • Posts: 1011
  • Karma: +102/-31
    • View Profile
INTERJECT, COPY_TRANS, huh?
« Reply #2 on: July 11, 2012, 06:27:43 AM »
INTERJECT (and COPY_TRANS)

One thing you can't do with either I_C_T or I_C_T2 is allow for PC replies. For that, you need INTERJECT.

INTERJECT follows the same basic format of I_C_T except for one key difference: it can never be a terminal state. At least one NPC must always speak again after the interjection happens. Let's go back to Lady Yuth's dialogue.

Code: [Select]

IF ~!Global("stolenfrom","LOCALS",1)~ THEN BEGIN 0 // from:
  SAY #46479 /* ~Hello there.  I've been watching you since you entered...I suspect you may have need of my wares.~ */
  IF ~~ THEN REPLY #46481 /* ~What is it that you sell?~ */ DO ~SetGlobal("YuthTalk","AR0702",1)~ GOTO 2
  IF ~~ THEN REPLY #46519 /* ~I don't think so, maybe another time.~ */ GOTO 1
  IF ~Global("YuthTalk","AR0702",1)~ THEN REPLY #61053 /* ~Let me see what you have.~ */ DO ~StartStore("scrolls",LastTalkedToBy())~ EXIT
END


Here, I want Will to ask the PC if he thinks it's really necessary.

Code: [Select]

INTERJECT SCROLL01 0 MPSCROLL01.0
== MPWILLJ IF ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID)~ THEN ~Do we really look that untrustworthy?~
END
++ ~You do. When was the last time you combed your hair?~ EXTERN MPWILLJ s1
++ ~Maybe it's all the black leather we're wearing.~ EXTERN MPWILLJ s2
++ ~You'd think she's never seen adventurers before.~ EXTERN MPWILLJ s3

APPEND MPWILLJ

IF ~~ s1
SAY ~It can't have been more than a week ago.~
IF ~~ THEN EXTERN SCROLL01 s4
END

IF ~~ s2
SAY ~Odd, the women usually love it.~
IF ~~ THEN EXTERN SCROLL01 s4
END

IF ~~ s3
SAY ~No kidding.~
IF ~~ THEN EXTERN SCROLL01 s4
END
END

APPEND SCROLL01

IF ~~ s4
SAY ~(sigh) Can I help you?~
COPY_TRANS SCROLL01 0
END
END


A few things to look at in the example above:
  • INTERJECT uses CHAIN construction, so you need to use EXTERN FILE even when you aren't changing dialogue files.
  • You always need an extern. You can *never* end an INTERJECT with the INTERJECT. We'll see that in an example later.
  • After Will talks to the PC and responds to the PC's reply, Lady Yuth speaks again. Her weary "Can I help you?" is followed by a COPY_TRANS. We'll deal with that now.
COPY_TRANS will copy the transitions from a state in another dialogue, including PC replies, so Lady Yuth's "Can I help you?" will look like the following on screen.
Quote

Lady Yuth: (sigh) Can I help you?
            1 What is it that you sell?
            2 I don't think so, maybe another time.
           (3 Let me see what you have) <== you'll only see this one if you have spoken with Lady Yuth before Will joined the party


Now, suppose that Will really does not like Lady Yuth and wants to attack her. I don't know what he has against elitist scroll sellers, but nevermind.

Code: [Select]

INTERJECT SCROLL01 0 MPSCROLL01.0
== MPWILLJ IF ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID)~ THEN ~Elitist snob. I hate you!~
END SCROLL01 a1

APPEND SCROLL01 a1

IF ~~ a1
SAY ~EEK!~
IF ~~ THEN DO ~Enemy()
      ActionOverride("MPWillDoit",Attack("scroll01"))~ EXIT
END
END


Here, I could not possibly have ended the interjection with Will's line even if I wanted to. WeiDU insists on an EXTERN following the interjection. There must be some kind of a transition after the END, whether it be a dialogue file name and state label or PC replies. If there are no PC replies, you don't actually have to type EXTERN. It's implied. END FILE # will do it.

Don't worry, WeiDU will tell you if you're getting it wrong.

When to use INTERJECT: When you want to interrupt a speaker with PC replies and when you want to interrupt the speaker to send the conversation off in another direction entirely. This is not the most compatibility-friendly way to do things, but sometimes, it's necessary, particularly when writing Slayer Change interjections.


Offline berelinde

  • Global Moderator
  • Level 3
  • *****
  • Posts: 1011
  • Karma: +102/-31
    • View Profile
INTERJECT, COPY_TRANS, huh?
« Reply #3 on: July 11, 2012, 06:28:00 AM »
INTERJECT_COPY_TRANS3 and INTERJECT_COPY_TRANS4

These exotic beauties don't see much use unless you're writing quest mods with a lot of NPC participation. They work more or less the same as I_C_T and I_C_T2, respectively, but they work with multiple NPCs who may or may not be in the area at the time.

Ordinarily, if your interjection includes an NPC who isn't in the party, the interjection breaks and subsequent NPCs don't get to speak. With I_C_T3 (and 4), that doesn't happen. All the same rules about I_C_T2 apply to I_C_T4, so avoid it whenever possible, which is most of the time.

Taking Lady Yuth as an example, again:

Code: [Select]

IF ~!Global("stolenfrom","LOCALS",1)~ THEN BEGIN 0 // from:
  SAY #46479 /* ~Hello there.  I've been watching you since you entered...I suspect you may have need of my wares.~ */
  IF ~~ THEN REPLY #46481 /* ~What is it that you sell?~ */ DO ~SetGlobal("YuthTalk","AR0702",1)~ GOTO 2
  IF ~~ THEN REPLY #46519 /* ~I don't think so, maybe another time.~ */ GOTO 1
  IF ~Global("YuthTalk","AR0702",1)~ THEN REPLY #61053 /* ~Let me see what you have.~ */ DO ~StartStore("scrolls",LastTalkedToBy())~ EXIT
END


This time, Will will say his bit and then a few BioWare boys and girls will chime in.
Code: [Select]

I_C_T3 SCROLL01 0 MPSCROLL01.0
== MPWILLJ IF ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID)~ THEN ~Nosy, isn't she?~
== JANJ IF ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID)
      InParty("Jan") InMyArea("Jan") !StateCheck("Jan",CD_STATE_NOTVALID)~ THEN ~I'll say! I haven't had anyone paying this much attention to me since that time somebody lit off the rockets in the elephant cage.~
== NALIAJ IF ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID)
      InParty("Nalia") InMyArea("Nalia") !StateCheck("Nalia",CD_STATE_NOTVALID)~ THEN ~They really do give mages a hard time here.~
== VALYGARJ IF ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID)
      InParty("Valygar") InMyArea("Valygar") !StateCheck("Valygar",CD_STATE_NOTVALID)~ THEN ~Why do we need anything from her anyway?~
== YOSHJ IF ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID)
      InParty("Yoshimo") InMyArea("Yoshimo") !StateCheck("Yoshimo",CD_STATE_NOTVALID)~ THEN ~We have done nothing to excite such suspicion.~
== KELDORJ IF ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID)
      InParty("Keldorn") InMyArea("Keldorn") !StateCheck("Keldorn",CD_STATE_NOTVALID)~ THEN ~She has a right to be cautious. It is her livelihood at stake.~
== JAHEIRAJ IF ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID)
      InParty("Jaheira") InMyArea("Jaheira") !StateCheck("Jaheira",CD_STATE_NOTVALID)~ THEN ~If you did not look like such a hooligan, Will, perhaps you would not draw such unfriendly stares.~
== SCROLL01 IF ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID) ReputationLT(Player1,14)~ THEN ~You do have a rather shifty look about you.~
== SCROLL01 IF ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID) !ReputationLT(Player1,14)~ THEN ~Force of habit.~
END


If I tried to do this using I_C_T, if Jan, Nalia, and Valygar were in the party but not Keldorn or Yoshimo, the interjection would break when it got to Keldorn. Using I_C_T3, Keldorn and Yoshimo are skipped and Jaheira gets to say her line. Since all of these people are reacting to what Will says, you need to detect Will's presence in every line. Otherwise, the first time the party talked to Lady Yuth, if Will wasn't in the party, Will would be skipped and Jan would chime in with "I'll say! I haven't had anyone paying this much attention to me since that time somebody lit off the rockets in the elephant cage." Everyone would probably look at him funny. Not that they don't anyway. Keep in mind that any passbacks need the same conditions as Will's starting line, so Lady Yuth's lines at the end have to have the same conditions as Will's. If you left the conditions off, Lady Yuth would say "You do have a rather shifty look about you" even if Will wasn't in the party.

When to use I_C_T3: When you want multiple NPCs to participate in an interjection.

Offline berelinde

  • Global Moderator
  • Level 3
  • *****
  • Posts: 1011
  • Karma: +102/-31
    • View Profile
INTERJECT, COPY_TRANS, huh?
« Reply #4 on: July 11, 2012, 06:28:39 AM »
EXTEND_BOTTOM

Sometimes, you'll find that you have to do interjections the hard way. Some games, like IWD/IWD2, don't accept INTERJECT_COPY_TRANS in any flavor. I'll go through it briefly, but I'm not going to spend a lot of time on this particular application. It has another, more relevent use.

Taking Lady Yuth's dialogue again... (this tutorial may put you off Lady Yuth forever)

Code: [Select]

IF ~~ THEN BEGIN 7 // from: 3.1
  SAY #61111 /* ~Aye, I'd imagine you would be.  One of the wizards by the name of Corneil decides who gets the license to use magic, here.  Bring plenty of coin if you go to see him.~ */
  IF ~~ THEN GOTO 8
END


I can do something like an interjection by adding another transition to the bottom of this state. But wait! There aren't any PC replies here. All transitions are not PC replies, though all PC replies are transitions. Here, the transition is GOTO 8.

Anyway, here's Will's interjeciton.

Code: [Select]

EXTEND_BOTTOM SCROLL01 0
IF ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID)~ THEN EXTERN MPWILLJ eb0
END

APPEND MPWILLJ

IF ~~ eb0
SAY ~Everything in this city costs money, doesn't it?~
IF ~~ THEN EXTERN SCROLL01 8
END
END


This isn't really a compatibility-friendly way of handling interjections, but back in the early days of WeiDU, this was the only way to do it. And with some games, they don't accept I_C_T.

There are limits to using it, as well. More on that in a minute.

Another, more widespread use of EXTEND_BOTTOM is to add PC replies to a state that already has PC replies. Suppose Lady Yuth has a special item just for Will. Here's the state in question... again.

Code: [Select]

IF ~!Global("stolenfrom","LOCALS",1)~ THEN BEGIN 0 // from:
  SAY #46479 /* ~Hello there.  I've been watching you since you entered...I suspect you may have need of my wares.~ */
  IF ~~ THEN REPLY #46481 /* ~What is it that you sell?~ */ DO ~SetGlobal("YuthTalk","AR0702",1)~ GOTO 2
  IF ~~ THEN REPLY #46519 /* ~I don't think so, maybe another time.~ */ GOTO 1
  IF ~Global("YuthTalk","AR0702",1)~ THEN REPLY #61053 /* ~Let me see what you have.~ */ DO ~StartStore("scrolls",LastTalkedToBy())~ EXIT
END


Now let's add that Will-specific PC reply.

Code: [Select]

EXTEND_BOTTOM SCROLL01 0
+ ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID)~ + ~I'd like to buy something for my friend Will. Do you have anything in mauve?~ + eb2
END

APPEND SCROLL01

IF ~~ eb2
SAY ~Mauve? Why yes! This potion flask looks mauve under the right light.~
IF ~~ THEN DO ~StartStore("scrolls",LastTalkedToBy())~ EXIT
END
END


By now, you should be pretty familiar with the syntax. If you aren't familiar with WeiDU shorthand, the first + is always IF, the second + is always THEN REPLY and the third + is always GOTO. Never EXTERN, always GOTO. Unlike the CHAIN construction of INTERJECT, you can use GOTO in EXTEND_BOTTOM.

Note that EXTEND_BOTTOM uses no state label. No global variable will be set when you use it. If you want one, you'll have to add it using DO ~SetGlobal()~ before the GOTO.

One more thing I want to mention in connnection with EXTEND_BOTTOM. ADD_TRANS_TRIGGER.

Sometimes, you'll want to disable specific dialogue options and offer new choices under certain condtions. Let's use the example above and make it impossible to do anything but ask about the availability of mauve items. You could add conditions to the other three transitions so that they will only be presented after the PC has asked about mauve items. I'm not going to copy Lady Yuth's dialogue again, but I will recopy the added lines, since they will have changed.

Code: [Select]


ADD_TRANS_TRIGGER SCROLL01 0 ~OR(4) Global("MP_AskedMauve","AR0702",1) !InParty("MPWillDoit") !InMyArea("MPWillDoit") StateCheck("MPWillDoit",CD_STATE_NOTVALID)~ DO

EXTEND_BOTTOM SCROLL01 0
+ ~InParty("MPWillDoit") InMyArea("MPWillDoit") !StateCheck("MPWillDoit",CD_STATE_NOTVALID)~ + ~I'd like to buy something for my friend Will. Do you have anything in mauve?~ DO ~Global("MP_AskedMauve","AR0702",1)~ + eb2
END

APPEND SCROLL01

IF ~~ eb2
SAY ~Mauve? Why yes! This potion flask looks mauve under the right light.~
IF ~~ THEN DO ~StartStore("scrolls",LastTalkedToBy())~ EXIT
END
END


The syntax for ADD_TRANS_TRIGGER is
ADD_TRANS_TRIGGER dialogue_file state_number ~triggers~ DO transition_list

If you don't specify a transition_list, as above, WeiDU will add those condtions to *all* transitions. If I wanted to add those conditions to the first transition only, I would use

ADD_TRANS_TRIGGER SCROLL01 0 ~OR(4) Global("MP_AskedMauve","AR0702",1) !InParty("MPWillDoit") !InMyArea("MPWillDoit") StateCheck("MPWillDoit",CD_STATE_NOTVALID)~ DO 0

The first transition is always 0, not 1.

Note that you can never add PC replies to a state that does not already have PC replies. If you want to add PC replies to a state that doesn't have any to begin with, you will need to use INTERJECT and you will need to give somebody something new to say before it happens. If you insist on using EXTEND_BOTTOM despite the fact that you will never see PC replies added to a state that never had them before, WeiDU will compile it no problem, but the transition to the next state will always occur before the PC replies have a chance to show up. It's just one of those things.

When to use EXTEND_BOTTOM: When you are modding a game that does not accept I_C_T. Also, when you want to add PC replies to a state that has other PC replies.


Offline berelinde

  • Global Moderator
  • Level 3
  • *****
  • Posts: 1011
  • Karma: +102/-31
    • View Profile
INTERJECT, COPY_TRANS, huh?
« Reply #5 on: July 11, 2012, 06:28:40 AM »
CHAIN as an interjection

When is an interjection not an interjection? When it starts before the other NPC even begins to speak. If I wanted Lady Yuth to say something the first time she met Will, without delivering her usual speech, I would add a weighted CHAIN to her dialogue file. Sounds kinky.

Code: [Select]
CHAIN
IF WEIGHT #-1 ~
      InParty("MPWillDoit")
      InMyArea("MPWillDoit")
      !StateCheck("MPWillDoit",CD_STATE_NOTVALID)
      Global("MP_WillLadyYuth","AR0702",0)
~ THEN SCROLL01 wly
~You! I was warned that you'd returned to Athkatla, but I scarce believed it!~
DO ~SetGlobal("MP_WillLadyYuth","AR0702",1)~
== MPWILLJ ~My fame precedes me?~
== SCROLL01 ~Notoriety, more like.~
EXIT


If you're familiar with using CHAIN, this will be nothing new. There are two important things to note here. The first is the state weight. This state needs to be weighted lower than her generic greeting state, which has a weight of 0. To accomplish this, simply set the state at WEIGHT #-1. You don't have to do anything crazy, like setting it to WEIGHT #-100, though WeiDU will compile that well enough. Since any negative number will put it at the top, a larger value of a negative number cannot put it further ahead than first. This is especially important where you are dealing with joinable NPCs who may have player-initiated dialogue.

The second thing to note is that you really, really need a closeable variable here. Area variables are fine. If you don't have one, you'll never be able to speak normally to Lady Yuth if Will is in the party. You don't want that. Or maybe you do.

You'll be using this one a lot with Crazy Celvan. For that one, including a RandomNum(2,1) among the other triggers will allow other party NPCs to have their turn.

When to use CHAIN: When you want somebody to say something before their regularly scheduled dialogue.

Offline berelinde

  • Global Moderator
  • Level 3
  • *****
  • Posts: 1011
  • Karma: +102/-31
    • View Profile
INTERJECT, COPY_TRANS, huh?
« Reply #6 on: July 11, 2012, 06:28:41 AM »
Reaction interjections and scenery dialogues

There are a lot of different ways to hanlde these, so this section will be the briefest. This tutorial is intended to make it easier to figure out which kind of interjection to use, not teach you how to code.

Generic scenery interjections. You set a variable when the NPC enters an area and he begins talking at that point. The dialogue that follows can be as long or as short as you like, but I'm going to use a short one to illustrate the principle.

If I wanted Will to comment on the smell of the Docks, I would put this in his override script, MPWILLS.

Code: [Select]

IF
    InParty(Myself)
    AreaCheck("AR0300")
    Global("MP_Docks","LOCALS",0)
    CombatCounter(0)
    See(Player1)
    !StateCheck(Myself,CD_STATE_NOTVALID)
    !StateCheck(Player1,CD_STATE_NOTVALID)
THEN
    RESPONSE #100
        SetGlobal("MP_Docks","LOCALS",1)
END

IF
    InParty(Myself)
    AreaCheck("AR0300")
    Global("MP_Docks","LOCALS",0)
    CombatCounter(0)
    See(Player1)
    !StateCheck(Myself,CD_STATE_NOTVALID)
    !StateCheck(Player1,CD_STATE_NOTVALID)
THEN
    RESPONSE #100
        StartDialogueNoSet(Player1)
END


And then I would put this in his joined dialogue file
Code: [Select]

IF ~Global("MP_Docks","LOCALS",1)~ THEN BEGIN docks
SAY ~Salty air, stale beer, unwashed people, and dead fish... must be the Docks.~
IF ~~ THEN DO ~SetGlobal("MP_Docks","LOCALS",2)~ EXIT
END

Apart from remembering to close the variable, there's nothing to see here.

Reaction interjections are a bit more interesting because they're usually triggered by globals set by other creatures or interjections. Once set, these variables will remain set for the entire game, so unless you do something to negate an interjection if the NPC was not there to witness it, Will will be commenting on events that happened before he joined the party. Fortunately, many of the events tied to these interjections are area-specific. So cue the interjection if the NPC is in the area where the original event took place but negate it if the NPC isn't in the area when the variable is detected. Let's use a Romantic Encounters reaction as an example. The specific encounter is Anishai, and the area is the second floor of MaeVar's Guildhall.

Here's the code that goes in the override script. I'm putting the negation block first so that if the NPC joins the party after the encounter has taken place, the interjection will be negated. Anishai is standing pretty close to the stairs, so I included Edwin's area in the area check as well.

Code: [Select]

IF
    InParty(Myself)                                   // Anishai
    Global("RE_AnishaiSex","GLOBAL",1)
    Global("MPWillREAnishai","LOCALS",0)
    !AreaCheck("AR0303")
    !AreaCheck("AR0304")
    !StateCheck(Myself,CD_STATE_NOTVALID)
    Gender(Player1,FEMALE)
THEN
    RESPONSE #100
        SetGlobal("MPWillREAnishai","LOCALS",9)
END

IF
    InParty(Myself)                                   // Anishai
    Global("RE_AnishaiSex","GLOBAL",1)
    Global("MPWillREAnishai","LOCALS",0)
    !StateCheck(Myself,CD_STATE_NOTVALID)
    OR(2)
        AreaCheck("AR0303")
        AreaCheck("AR0304")
THEN
    RESPONSE #100
        SetGlobal("MPWillREAnishai","LOCALS",1)
END

IF
    InParty(Myself)                                   // Anishai
    Global("MPWillREAnishai","LOCALS",1)
    !StateCheck(Myself,CD_STATE_NOTVALID)
    CombatCounter(0)
    !See([ENEMY])
THEN
    RESPONSE #100
        StartDialogueNoSet(Player1)  
END


And the dialogue:
Code: [Select]

IF ~Global("MPWillREAnishai","LOCALS",1)~ THEN BEGIN re
SAY ~Sounds like somebody had a good time.~
IF ~~ THEN DO ~SetGlobal("MPWillREAnishai","LOCALS",2)~ EXIT
END


The absolute last kind of reaction interjection I'm going to cover here is the kind that happens after the party leaves the area where the precipitating event took place. Because sometimes, you really want to wait until the party is out of the room before the NPC starts making wisecracks. I'm just going to borrow this example from one of my own mods. It's Gavin's reaction to the MaeVar's order to kill Embarl. Since this is a quest related interjection, unless the PC gets assigned the quest, leaves the Docks, recruits Gavin, and then returns to the Docks to kill Embarl, there's no need to worry about negating the interjection. If I really wanted to, I could have done more with ADD_TRANS_ACTION, but nobody has complained about it yet.

In Gavin's override script:
Code: [Select]
IF
    InParty(Myself)
    Global("B!GavEmbarl","GLOBAL",0)
    !AreaCheck("AR0301")
    Global("MaeVarWork","GLOBAL",5)
THEN
    RESPONSE #100
        SetGlobal("B!GavEmbarl","GLOBAL",1)
END

IF
    InParty(Myself)
    Global("B!GavEmbarl","GLOBAL",1)
    !AreaCheck("AR0301")
    !StateCheck(Myself,CD_STATE_NOTVALID)
    !StateCheck(Player1,CD_STATE_NOTVALID)
    CombatCounter(0)  
THEN
    RESPONSE #100
        StartDialogueNoSet(Player1)  
END


And the dialogue
Code: [Select]

IF ~Global("B!GavEmbarl","GLOBAL",1)~ THEN BEGIN BGavEmbarl
SAY ~Note that he asked only for the dagger, . This need not end in bloodshed.~ [bg_blank]
IF ~~ THEN DO ~SetGlobal("B!GavEmbarl","GLOBAL",2)~ EXIT
END


Offline Sir_Carnifex

  • Scourge of Kobolds
  • Modders
  • Level 1
  • *****
  • Posts: 202
  • Karma: +8/-0
  • Scourge of Kobolds
    • View Profile
Re: INTERJECT, COPY_TRANS, huh?
« Reply #7 on: August 18, 2012, 12:01:17 AM »
This tutorial will come in very handy for me so I don't break things.   Thanks for taking the time to write it.

I'd like to point out something that helped a newcomer to scripting such as myself.   Below it is suggested to decompile a dialogue file to find the greeting state.  I find it much, much easier to just open Infinity Explorer because that tells the states, the actions, etc., without having to decompile an individual script.  That said, I've found decompiling to be a good source of actual learning for myself... but not necessary for finding the dialogue state.   Not, of course, to say it's a wrong way of doing it since it's not!  I hate having a ton of decompiled files clogging my filing system.

You want your NPC, Will Doit, to respond to Lady Yuth's greeting. She is the scroll merchant in the Adventure Mart. The first thing you will need to do is decompile Lady Yuth's dialogue using WeiDU. After you have identified her dialogue file, SCROLL01.dlg, you decompile it and find that state 0 is her greeting state. Will Doit's joined dialogue file is MPWillJ and his DV is MPWillDoit.

Anyway, I hope you don't mind my posting on the tutorial page here.   
In progress - Armin Kasun - a mercenary NPC for BG2

On hold - Halbo NPC for BG2, the evil halfling thief, food critic, and party troublemaker