OK, let's run down this whole idea.
The basic way of building a state is to create
IF ~condition~ THEN BEGIN statename
SAY ~text~
IF ~condition~ THEN EXIT
END
This can be built into fancier options;
example>>
moving the state above other states so it gets evaluated (and played if the conditions are met) first:IF WEIGHT #-1 ~condition~ THEN BEGIN statename
SAY ~text~
IF ~condition~ THEN EXIT
END
example>>
adding other states to the dialog IF WEIGHT #-1 ~condition~ THEN BEGIN statename
SAY ~text~
IF ~~ THEN REPLY ~option 1~ EXIT
IF ~condition 1~ THEN REPLY ~option 2~ EXIT
IF ~condition 2~ THEN REPLY ~option 3~ EXIT
IF ~condition 3~ THEN EXIT
END
We already know in this structure that the engine grabs the first state available. We also know that while scripts like .BCS .DLG are evaluated top to bottom, replies within states are evaluated bottom to top;
we see
- if no condition, REPLY ~option 1~ EXIT
- if ~condition 1~ THEN REPLY ~option 2~ EXIT
- if ~condition 2~ THEN REPLY ~option 3~ EXIT
- if ~condition 3~ THEN EXIT
the engine evaluates them in this order and lets them show up if they meet the conditions:
- if ~condition 3~ THEN EXIT
- if ~condition 2~ THEN REPLY ~option 3~ EXIT
- if ~condition 1~ THEN REPLY ~option 2~ EXIT
- if no condition, REPLY ~option 1~ EXIT
In this example, note that the first response evaluated "hijacks" the state - if condition 3 is met, the dialog immediately exits with no reply or action or transition. Let's extend that idea, and instead of exiting, go someplace else in the transition:
example>>
externing to another NPC's dialogIF WEIGHT #-1 ~condition~ THEN BEGIN statename
SAY ~text~
IF ~~ THEN REPLY ~option 1~ EXIT
IF ~condition 1~ THEN REPLY ~option 2~ EXIT
IF ~condition 2~ THEN REPLY ~option 3~ EXIT
IF ~condition 3~ THEN EXTERN ~dialogfile_of_other_npc~ statename_for_new_state
END
This last one is the way you can manually add "interjection points" in a regular dialog that send the conversation over to the other
NPC. Here is an example:
example>>
manual code of interjectionsIF WEIGHT #-1 ~condition~ THEN BEGIN statename
SAY ~text~
IF ~~ THEN REPLY ~option 1~ EXIT
IF ~condition 1~ THEN REPLY ~option 2~ EXIT
IF ~condition 2~ THEN REPLY ~option 3~ EXIT
IF ~InParty("Khalid")~ THEN EXTERN ~KHALIDJ~ KhalidTalks
IF ~InParty("Jaheira")~ THEN EXTERN ~JAHEIJ~ Jaheiratalks
IF ~InParty("Imoen2") !InParty("Jaheira") !InParty("Khalid")~ THEN EXTERN ~IMOEN2~ Imoen_Without_J_and_K
END
In this last example, the engine looks and says:
- If Imoen is there without Jaheira or Khalid, don't say anything - just jump to Imoen's dialog file and reference state "Imoen_Without_J_and_K" and say that.
- If Jaheira is in the party, don't say anything - just jump to Jaheira's dialog file and reference state "Jaheiratalks" and say that.
- If Khalid is in the party, don't say anything - just jump to Khalid's dialog file and reference state "Jaheiratalks" and say that.
- If ~condition 2~ is met, print out ~option 2~ and then if the player clicks on this, EXIT.
- If ~condition 1~ is met, print out ~option 2~ and then if the player clicks on this, EXIT.
- Since there are no conditions on this option, display ~option 1~ and then if the player clicks on this, EXIT.
This is the traditional way of coding
NPC responses within a dialog file. Basically, if the conditions are right, EXTERN to the
NPC, then EXTERN back to the original speaker. Or even keep it going, EXTERN - ing all over the place, building replies for NPCs. It has some advantages, in that it follows usual coding conventions and it provides a simple way of moving between files. But at some point, it gets frustrating and complicated to code, and can fall prey to missteps. To get Jaheira, Khalid, and Imoen all talking, you need to manually build each option and refer back to the original one:
(note this is just ONE of the several pathways you would have to build - you would have to build Jaheira alone, Jaheira going to Khaild to joning, Imoen alone, Imoen with Jaheira, Imoen with Khalid, Imoen with Jaheira and khalid, etc., etc. all tying back into the same state in SHERMAN.DLG)
APPEND ~KHALIDJ~
IF ~~ THEN BEGIN KhalidTalksJahiera
SAY ~I d- don't normally deal with any of the Orcish folk, half or n- not, but Sherman could help us greatly. His size and fearsomness alone would cause m- many to avoid confrontations. B- But as my wife said, the d- decision is yours to make.~
IF ~~ THEN EXTERN ~SHERMAN~ joinornot
END
IF ~~ THEN BEGIN KhalidTalksNOJahiera
SAY ~I d- don't normally deal with any of the Orcish folk, half or n- not, but Sherman could help us greatly. And his buddy Grant, for that m-matter, unless we are in the US Civil War. His size and fearsomness alone would cause m- many to avoid confrontations. B- But the d- decision is yours to make.~
IF ~~ THEN EXTERN ~SHERMAN~ joinornot
END
END
Please note I kept it simple - you have to do more than just check if an
NPC is in the party or not. They could be in the party but dead, in the party but left outside of the
FAI looting Tarnesh's corpse, etc. - the stricter the condition writing, the less bug reports/crashes/etc. Of course, if you condition it down to every third gameday between 9am and 10am for Elven Female Fighter PCs Carrying Carsomyr And In A Romance With Gavin But Still Ogling Ajantis, expect that exactly one person will ever see that content - you. And you might not see it, either
But a good check is to make sure they are alive, in the party, in the area, and ok to talk.
So as you can see, there is alot of convoluted state creation and linking to be done in order to get what you want done. It also means keeping track of stuff across multiple files. Bleh.
Luckily, the Old Guard Moddarz gave up on this kind of horrible confusing manual creation long ago. The simpler way of coding up a dialogue with multiple
NPC input without interrupting the flow of ideas is called CHAIN.
WARNING: CHAIN construction can be fun, and there is a good older tutorial at CoM that shows how to build *everything* using CHAIN, but most modern coders avoid its use unless you are having an NPC speaking within your dialog. It is generally better to use the tool for its intended function.In a CHAIN construction, you work directly with what amounts to a series of interjections (I_C_T, I_C_T2, I_C_T3, I_C_T4 all tangental to this discussion, but check the
WeiDU docs for why you might want to use some of these in other situations).
Here is the idea in CHAIN, broken down:
CHAIN IF ~condition~ THEN ~target_dialog_file~ statename
~text~
== ~NPC1~ ~NPC1 always says this~
END
IF ~~ THEN REPLY ~option1~ DO ~something~ EXIT
IF ~condition 1~ THEN REPLY ~option2~ EXIT
Extending this idea,
CHAIN IF ~condition~ THEN ~target_dialog_file~ statename
~text~
== ~NPC1~ ~NPC1 always says this~
== ~NPC1~ IF ~condition~ THEN ~NPC1 speaks if the condition is met~
== ~NPC2~ ~NPC2 always says this~
== ~NPC2~ IF ~condition~ THEN ~NPC2 says this, and sets a special global that it has been said~ DO ~SetGlobal("NPC2SaidX","GLOBAL",1)~
END
IF ~~ THEN REPLY ~PC always gets to reply with this~ EXIT
IF ~condition~ THEN REPLY ~PC gets to answer if a condition is met~ EXIT
IF ~condition~ THEN EXTERN ~target_dialog_file~ statename
Some considerations:
- == ~BKHALI~ IF ~InParty("khalid") InMyArea("khalid") !StateCheck("khalid",CD_STATE_NOTVALID)~ THEN ~Khalid Talks Here~
is a good idea in CHAIN construction, because the dialog will crash if it can't find a speaker. If Khalid is not present, you don't want to call on his banter or joined file... basically, add the conditions to get NPCs to talk only when they are present and everything is ok for the talk.
- Replies can be eliminated completely:
CHAIN IF WEIGHT #-1 ~condition~ THEN ~target_dialog_file~ statename~text~== ~NPC1~ ~NPC1 always says this~== ~NPC1~ IF ~condition~ THEN ~NPC1 speaks if the condition is met~== ~NPC2~ ~NPC2 always says this~== ~NPC1~ ~NPC1 always says this~ EXIT
Just make sure the transition is from someone who's always present, or there will be trouble The condition set should include at least two of the participants being ok for dialog.
- The original speaker can interject the same way, using his own file:
CHAIN IF ~condition~ THEN BEGIN ~SHERMAN~ ShermanInFAI~Yo, bro. I be chillin'. Whazzup?~== ~JAHEIRAJ~ ~Why are you talking this way, Sherman?~== ~KHALIDJ~ IF ~InParty("Khalid")~ THEN ~N- now d- d- dear...~== ~JAHEIRAJ~ IF ~InParty("Khalid")~ THEN ~Khalid, dearest, chill. The dude is flippin' out.~== ~SHERMAN~ ~Sorry, I just saw YouTube's "White and Nerdy" and couldn't resist.~ENDIF ~~ THEN REPLY ~OK, Join Up.~ DO ~JoinParty() SetGlobal("ShermanRebuffed",GLOBAL",2)~ EXITIF ~~ THEN REPLY ~You just wrong, cuz. Fly. NOT.~ EXIT
Evaluates to
Sherman + Jahiera in the party
SHERMAN: ~Yo, bro. I be chillin'. Whazzup?~
JAHEIRA: ~Why are you talking this way, Sherman?~
SHERMAN: ~Sorry, I just saw YouTube's "White and Nerdy" and couldn't resist.~
[PC Options]
Khalid in the party without Jaheira
SHERMAN: ~Yo, bro. I be chillin'. Whazzup?~
KHALID: ~N- now d- d- dear...~
//GAME CRASH BECAUSE JAHEIRA ISN'T PRESENT
[PC Options]
Sherman + Jahiera + Khalid in the party
SHERMAN: ~Yo, bro. I be chillin'. Whazzup?~
JAHEIRA: ~Why are you talking this way, Sherman?~
KHALID: ~N- now d- d- dear...~
JAHEIRA: ~Khalid, dearest, chill. The dude is flippin' out.~
SHERMAN: ~Sorry, I just saw YouTube's "White and Nerdy" and couldn't resist.~
[PC Options]
Sherman, no Jahiera, no Khalid
SHERMAN: ~Yo, bro. I be chillin'. Whazzup?~
SHERMAN: ~Sorry, I just saw YouTube's "White and Nerdy" and couldn't resist.~
[PC Options]
OK, so let's code your example, and fix up any loose ends.
Here is how it might work for your dialogue above:
/* Rebuffed: will go to Friendly Arm Inn and wait in common room. "ShermanRebuffed" set when the party says no the first time and sends him away. */
CHAIN IF WEIGHT#-1 ~Global("ShermanRebuffed","GLOBAL",1)~ THEN ~SHERMAN~ ShermanWaitsFAI
~We meet again. My offer still stands, I am willing to add my skills to your group, especially if you are going to Nashkell to help them.~
== ~JAHEIJ~ IF ~InParty("jaheira") InMyArea("jaheira") !StateCheck("jaheira",CD_STATE_NOTVALID)~ THEN ~<CHARNAME>, he has a point about his skills. He is a good cleric and a good fighter and could help us, but the decision is your to make.~
== ~KHALIDJ~ IF ~OR(3) !InParty("jaheira") !InMyArea("jaheira") StateCheck("jaheira",CD_STATE_NOTVALID) InParty("khalid") InMyArea("khalid") !StateCheck("khalid",CD_STATE_NOTVALID)~ THEN ~ I d- don't normally deal with any of the Orcish folk, half or n- not, but he could help us greatly. His size and fearsomness alone would cause m many to avoid confrontations. As always, the d- decision is yours to make.~
== ~KHALIDJ~ IF ~InParty("jaheira") InMyArea("jaheira") !StateCheck("jaheira",CD_STATE_NOTVALID) InParty("khalid") InMyArea("khalid") !StateCheck("khalid",CD_STATE_NOTVALID)~ THEN ~ I d- don't normally deal with any of the Orcish folk, half or n- not, but he could help us greatly. His size and fearsomness alone would cause m many to avoid confrontations. B- But as my wife said, the d- decision is yours to make.~
== ~IMOEN2~ IF ~InParty("imoen2") InMyArea("imoen2") !StateCheck("imoen2",CD_STATE_NOTVALID) InParty("khalid") InMyArea("khalid") !StateCheck("khalid",CD_STATE_NOTVALID) InParty("jaheira") InMyArea("jaheira") !StateCheck("jaheira",CD_STATE_NOTVALID)~ THEN ~I think that Auntie Jaheira and Uncle Khalid are right <CHARNAME>. But you have to decide.~
== ~IMOEN2~ IF ~InParty("imoen2") InMyArea("imoen2") !StateCheck("imoen2",CD_STATE_NOTVALID) OR(6) !InParty("khalid") !InMyArea("khalid") StateCheck("khalid",CD_STATE_NOTVALID) !InParty("jaheira") !InMyArea("jaheira") StateCheck("jaheira",CD_STATE_NOTVALID~ THEN ~I think we should take him in. He's big, he's a Cleric, and he can fight. we need the support of his spells to heal us and he can scare away a lot of trouble. But you have to decide.~
END
++ ~Very well, I think that you would make a good addition to the party Sherman, please join us.~ DO ~JoinParty() SetGlobal("ShermanRebuffed","GLOBAL",2)~ EXIT
++ ~I still am a little leary of you, meaning no offence, but I think that we may need you in the future. Can you wait here for us?~ EXTERN ~SHERMAN~ waitHerelonger
APPEND ~SHERMAN~
IF ~~ waitHerelonger
SAY ~Sure. The barmaid is kinda cute, for an ettin. I'll be over there if you want me to join up later.~
IF ~~ THEN EXIT
END
END
Let's back up and break it down further.
CHAIN // start the sequence, outside of any APPEND or BEGIN or anything else.
IF WEIGHT#-1 // set this dialog state to "evaluated before other stuff already installed"
~Global("ShermanRebuffed","GLOBAL",1)~ // if this value is set; best set one condition from the initial conversation path so there is no confusion
THEN ~SHERMAN~ ShermanWaitsFAI // build all this stuff into SHERMAN.DLG in a state called "ShermanWaitsFAI"
~We meet again. My offer still stands, I am willing to add my skills to your group, especially if you are going to Nashkell to help them.~ // Sherman says this.
== ~JAHEIJ~ IF ~InParty("jaheira") InMyArea("jaheira") !StateCheck("jaheira",CD_STATE_NOTVALID)~ THEN ~<CHARNAME>, he has a point about his skills. He is a good cleric and a good fighter and could help us, but the decision is your to make.~ // If Jaheira is in the party, is in the area, and is not silenced/dead/etc., then this line will run. If any one of those conditions is not met, the line won't run.
//tougher one, and one you missed originally: what if Jaheira is dead, or silenced, or not in the party, but Khalid is?
== ~KHALIDJ~ IF ~OR(3) !InParty("jaheira") !InMyArea("jaheira") StateCheck("jaheira",CD_STATE_NOTVALID) InParty("khalid") InMyArea("khalid") !StateCheck("khalid",CD_STATE_NOTVALID)~ THEN ~ I d- don't normally deal with any of the Orcish folk, half or n- not, but he could help us greatly. His size and fearsomness alone would cause m many to avoid confrontations. As always, the d- decision is yours to make.~// this says
IF
JAHEIRA IS ANY OF THE THREE (Not in party, or Not in this area, or silenced/dead/turnedtostone/etc.)
AND
KHALID IS ALL OF THE THREE (In Party, In My Area, OK to talk)
THEN play this line.== ~KHALIDJ~ IF ~InParty("jaheira") InMyArea("jaheira") StateCheck("jaheira",CD_STATE_NOTVALID) InParty("khalid") InMyArea("khalid") !StateCheck("khalid",CD_STATE_NOTVALID)~ THEN ~ I d- don't normally deal with any of the Orcish folk, half or n- not, but he could help us greatly. His size and fearsomness alone would cause m many to avoid confrontations. B- But as my wife said, the d- decision is yours to make.~ // OK, you get it now; here Khalid is only chiming in if he is ok and present and in the party, etc. and Jaheira is also ok, in the party, etc.
Slip over Imoen, as this is all more of the same.
END // start PC responses
++ ~Very well, I think that you would make a good addition to the party Sherman, please join us.~ DO ~JoinParty() SetGlobal("ShermanRebuffed","GLOBAL",2)~ EXIT // ++ is shorthand for the if the reply stuff; say this PC response, do those actions.
++ ~I still am a little leary of you, meaning no offence, but I think that we may need you in the future. Can you wait here for us?~ EXTERN ~SHERMAN~ waitHerelonger // say this as a PC reply, and send us back to Sherman at the end for his response.
APPEND ~SHERMAN~ // go build this in SHERMAN.DLG
IF ~~ waitHerelonger
SAY ~Sure. The barmaid is kinda cute, for an ettin. I'll be over there if you want me to join up later.~
IF ~~ THEN EXIT
END
ENDSummary: CHAIN is incredibly useful for getting
NPC comments into your content. It can quickly cover multiple interjectors, set specialized globals, and the argument has been made that it is technically possible to code an entire mod as a single long CHAIN. It also has some pitfalls... especially with making sure you don't accidentally make a CHAIN blow up your game by asking for NPCs to talk when they are not present, or are dead, etc.
P.S. - your question about dialog files and dvs for Imoen are another topic entirely; starting points are Ascenscion64's
BGT docs, which are a tutorial all in themselves, and I have a
G3 tutorial about cross-platform modding variables that will help you out later on. But for right now, stick with what you know. If you are modding on the
BGT platform, use "Imoen2" for the DV, BIMOEN2 for the banter file, IMOEN2 for the joined.
Hope this helps, and I didn't aim too low or too high...
EDIT: Other resources about CHAIN usage.
For the extreme usage of this, which I don't personally recommend for any but the most experienced heavy-duty modder, but the concepts are good and there are some interesting points:
Zyreaen's older but still good
"Abuse of CHAIN"For basics with a flair for the fun,
Blue's original
"The Road to Banter" , which I like immenselytheaceface's quick modern overview, with a breakdown of conditions
"Chain is so cool..."
Edited by cmorgan, 04 April 2008 - 04:12 PM.