Jump to content

CHAIN in .D files


Recommended Posts

All right, so I'm not a modder but I'm definitely a nosy end-user, and I was looking at the .tra and .d files for Romantic Encounters and thought, there has to be a better way to read those than painstakingly going from number to number in a side-by-side notepad. I thought, how hard can it be? So I started writing a tiny little text-base dialog "stepper". Answer is, it would be easier if I understood the scripting in .d better. Now most of the stuff is self-explanatory or I can ignore it for my uses at the start, but I'm a bit confused about CHAIN, or perhaps about the firing of dialog in general. I looked around the tutorials forums a bit, but only found a reference to CHAIN in a pdf and did not understand the explanation given there.

So. When there is CHAIN above a dialog "step", you know the block between IF ~~THEN file talk.number ... END, does that mean that talk will always fire immediately after the one above it? Or does the ~~, which usually contains conditions, if I understood right, mean that it will always fire? But then why isn't that the first thing out of the NPC's mouth? Are the blocks processed in order from top to bottom?

I suppose I'm looking for a bit of context. I think the .d gets compiled by WeiDU? When does it get called, by what? Does it execute sequentially top to bottom, following the "gotos", until it reaches an END? But there's CHAINs after ENDs so I'm not sure how that works?

I'm happy to read an introduction on .d language if you point me to one (or to use some other tool I don't know about to nose about dialog trees if one exists). Thanks a lot!

Link to comment

I'm still learning the nuisances of modding myself, but from my understanding so far; CHAIN is used to start the chain of a dialogue then to chain multiple NPC dialogue responses together. Example below;

Spoiler

CHAIN IF~GLOBAL("RandomGlobal","GLOBAL/LOCALS",1)~ THEN DIALOGUESTRING1
(NPC response here)
END
IF~~THEN REPLY @# EXTERN DIALOGUESTRING2

CHAIN DIALOGUESTRING2
etc. etc.

The first line of CHAIN IF~GLOBAL("RandomGlobal","GLOBAL/LOCALS",1)~ THEN DIALOGUESTRING1 starts the dialogue chain providing the GLOBAL condition is true. The NPC will then start their first line of dialogue which starts at DIALOGUESTRING1 in this case. You then have any possible replies from the PC (this can be different again if the NPC is talking for several lines).

EXTERN DIALOGUESTRING2 is where the dialogue will go next based on the PC response chosen. CHAIN DIALOGUESTRING2 is essentially the next 'link' in the dialogue chain which will only display if that particular dialogue is selected. So if there was an IF~~THEN REPLY @# EXTERN DIALOGUESTRING3 as well, selecting that would chain to DIALOGUESTRING3 next instead. That will then usually continue until the end of said chain where there are no dialogue options, and/or the dialogue hits an EXIT trigger. It can get a lot more complicated than that because a lot of dialogues rely on a GlobalTimer before it triggers or if using other variables instead of a GLOBAL variable.

I think I explained that right. If I didn't, I'm sure someone will correct me but I've been playing with dialogues a lot recently, so it is relatively fresh for me. I also hope that made relative sense as well.

Link to comment

But, but, if CHAIN just starts the dialog, why is there not a CHAIN everywhere? Why is there a need for CHAIN sometimes, and sometimes not? For example, here, 1.8 doesn't need a chain, but 1.9 does? What's the difference?

IF ~~ THEN BEGIN Laran1.8
SAY @17
++ @18 + Laran1.23
++ @19 + Laran1.24
END
END

CHAIN
IF ~~ THEN ~B!LARAN~ Laran1.9
@20
== ~BANOMEN~ IF ~InParty("Anomen") InMyArea("Anomen") !StateCheck("Anomen",CD_STATE_NOTVALID)~ THEN @21

(and also I have no idea what the second END there does. all the IFs are ENDed)

Link to comment

That part I do not know sorry. I've only seen and used instances of CHAIN at the start and throughout the dialogue. My assumption would be that it might have something to do with whether or not there is a Global trigger or some other trigger. I've seen CHAIN IF to start dialogue chains the most as they generally rely on some kind of Global being of some value.

I don't know why there would be two ENDs either.

Link to comment

CHAIn doesn't start a dialogue. A state with a trigger variable does. CHAIN is just a way to code dialogue. It is very helpful if you have more than one NPC / character who should say something, or different lines for different conditions (romance / not in romance for example).

Thus: if there is a trigger variable before the statename, it can start a dialogue. if not, it's somewhere in the middle of a dialogue.

The end of a dialogue is marked by EXIT, btw. All the ENDs in a d-file are just syntax.

Link to comment
16 hours ago, DalreiDal said:

Why is there a need for CHAIN sometimes, and sometimes not? For example, here, 1.8 doesn't need a chain, but 1.9 does? What's the difference?

IF ~~ THEN BEGIN Laran1.8
SAY @17
++ @18 + Laran1.23
++ @19 + Laran1.24
END
END

CHAIN
IF ~~ THEN ~B!LARAN~ Laran1.9
@20
== ~BANOMEN~ IF ~InParty("Anomen") InMyArea("Anomen") !StateCheck("Anomen",CD_STATE_NOTVALID)~ THEN @21

(and also I have no idea what the second END there does. all the IFs are ENDed)

To explain in short your code snippet:

The first END is from the state "Laran1.8". The second END is to end a section where probably ~B!Laran.dlg~ was appended, there is probably an APPEND ~B!Laran~ above somewhere where all following states should go into (the installer needs to know this). With the END, the installer knows that now appending to this dlg is done.

After that there is a CHAIN, because now Anomen will make an interjection into the dialogue if he is present and it's much simpler to code this way than with single states. You'll see that with the CHAIN, there is again the definition where the first line should go into - ~B!Laran.dlg~ again. The line afterwards (@21) is from Anomen.

That is what I meant, with CHAIN this is very simple to code. Without CHAIN, the state "Laran1.9" would have to be closed, there would have to be an EXTERN to Anomen's dlg wrappend in an APPEND - END, and back to ~B!Laran.dlg~ etc...

CHAIN is just a simple way of letting lots of differently flagged dlgs talk, either from different cres or for different conditions.

Hope it makes sense.

In case you speak German or do not shy from using e.g. DeepL for translation, I wrote a little CHAIn tutorial here. Not sure whether this clears things up a litlle.

Link to comment

I'm sorry, I tried reading the translation of your german text, but I think it translated the code as well, and I, ah, didn't really understand it. But thanks for your insight here.

Let me rephrase to make sure I understand. So when you make a .d file, you start with BEGIN ~uniquename~ and uniquename must be the same as the filename?

Then there is a APPEND ~uniquename~...END block. In this block, several IF ~...~ THEN BEGIN ... END blocks are located. These play in "goto" order, following the player responses.

Once the end of that APPENDed block is reached, i.e. gotos have been followed to the end, the script keeps reading the CHAINs, executing the  IF ~~ BEGIN END blocks where the conditions are fulfilled. It does this to all CHAIN blocks in order they appear. CHAIN is used for other NPCs interjections.

More APPEND blocks may be present below the first APPEND and the first CHAIN. This block is not triggered except if a "goto" in the first APPENDed/CHAINed blocks calls to them? Or perhaps they are executed sequentially after the first APPEND and CHAIN if EXIT has not been reached?

Link to comment
14 minutes ago, DalreiDal said:

you start with BEGIN ~uniquename~

BEGIN dlgname is needed if you define a new dlg, for your own NPC / quest giver for example.

The name of the d-file is of no importance.

16 minutes ago, DalreiDal said:

Once the end of that APPENDed block is reached, i.e. gotos have been followed to the end, the script keeps reading the CHAINs

Well.. you can also mix APPEND and CHAIN. I like to do that, because to me, being able to read the d-file is more important than some code order.

The GOTO (or "+" in short syntax) always specifies the name of the following dialogue state (name/number it has inside the d file. Upon installing, all names are transferred into state numbers in the dlg that is created).

17 minutes ago, DalreiDal said:

CHAIN is used for other NPCs interjections.

CHAIn *can* be used for other NPC interjections. It can also be used to add an NPC line the PC only sees if certain conditions are met at the time the dialogue happens (e.g. in romance etc.).

19 minutes ago, DalreiDal said:

More APPEND blocks may be present below the first APPEND and the first CHAIN. This block is not triggered except if a "goto" in the first APPENDed/CHAINed blocks calls to them?

A dialogue is only started by a block that has a trigger variable. All others need a GOTO transition to happen inside a dialogue. it's of no importance whether this is above or below a CHAIN. I can only repeat: CHAIN is just a different way of coding the dialogue states. It has nothing to do with starting or ending a dialogue.

To read a d-file, start with a state / CHAIN that has trigger variables in their head. These give a clue as to when this dialogue will happen. (For scripted dialogues, it's probably just one variable.) Then follow the dialogue from GOTO name to GOTO name until it ends with EXIT. Hope it helps.

Link to comment

I think we might be bamboozled by vocabulary. What is a "dialog state"? Is that IF ~...~ THEN BEGIN ... END block? That is one dialog state?

But... if you just follow from GOTO to GOTO, why do you need CHAIN at all? What does CHAIN do? For example here, there is a line earlier that sends you to Laran1.17. What is the point of the CHAIN there, if you were going to get to Laran1.17 anyway?

CHAIN
IF ~~ THEN ~B!LARAN~ Laran1.17

What does an APPEND do? Why would you need/want to have several APPENDs in one .d file?

Link to comment
7 hours ago, DalreiDal said:

What does an APPEND do?

APPEND ~dlgname~ ... END tells the installer: the following content should go into the dlg "dlgname".

7 hours ago, DalreiDal said:

Why would you need/want to have several APPENDs in one .d file?

Various reasons: because you want to add dialogue to different dlgs. Bcause you had a CHAIN in between and need to tell the installer which dlg the following content should be put into again.

7 hours ago, DalreiDal said:

What is a "dialog state"? Is that IF ~...~ THEN BEGIN ... END block? That is one dialog state?

That is a dialogue state if it is coded not in CHAIN, yes. If the d-file uses CHAIN syntax, the installer will break it into such states inside the compiled DLG. You could compare a d-file content with a compiled dlg in the game to see the difference. CHAIN makes it easier to code, and easier to read, that's all.

 

7 hours ago, DalreiDal said:

But... if you just follow from GOTO to GOTO, why do you need CHAIN at all?

This is a misunderstanding.

In your example, the Laran1.17 is the name of the CHAIN. In what you quoted, there is no GOTO, it's the definition header of the CHAIN. With "GOTO" I meant the transitions at the bottom of a CHAIN or state.

Meaning, if you have somewhere else something like "GOTO Laran1.17" or, in short syntax: "+ Laran1.17", then you would continue reading the dialogue with this CHAIN.

An - hopefully easy - example, with explanations.

BEGIN dlgname1 //this defines our mod's new dlg for our quest giver

IF ~NumTimesTalkedTo(0)~ THEN greeting //header: trigger variable (never talked to before) and name ("greeting")
SAY ~You need to wait, I'm busy!~
IF ~~ THEN EXIT // dialogue ends if imoen is not in party
IF ~IsValidForPartyDialogue("imoen")~ THEN + imoen_protest //dialogue continues below if Imoen is in party - this is executed first - bottom to top!
END //this concludes the state "greeting"

CHAIN //now we use CHAIN because two characters will talk 
IF ~~ THEN ~imoens_dlg~ imoen_protest //this is the header of the CHAIN. The next line is for Imoen, so the dlg needs to be specified. No trigger variable, because this is an ongoing dialogue from above.
~Ey, don't be so rude!~ // Imoen's line
== dlgname1 //dialogue goes back to quest giver
~I had no intention to be rude! I'm just busy!~
END //this is for the installer. Now follow transitions or reply options:
++ ~It's alright, we will wait.~ + follow_up_1 //PC reply option. Continue reading with state "follow_up_1" below.
++ ~My friend is right, you know. We are busy, too!~ + follow_up_2 //PC reply option. Continue reading with state "follow_up_2" below.

APPEND dlgname1 //the following content is for our quest giver again

IF ~~ THEN follow_up_1 //state 1
SAY ~Thanks. It's crazy busy lately!~
IF ~~ THEN + follow_up_2 //dialogue continues below
END

IF ~~ THEN follow_up_2 // state 2
SAY ~I can't clone myself. Go and come back later then!~
IF ~~ THEN EXIT //dialogue ends
END

END //APPEND's end

 

Link to comment

Maybe it gets clearer if I explain it differently. Let's focus on what is actually important for you who wants to read the dialogue. You do not necessarily understand the whole syntax, if you know what specifies what is going on. I'll break it down. the only things that are important for reading this I put in bold:

Quote

BEGIN dlgname1 // who is talking the following dialogue

IF ~NumTimesTalkedTo(0)~ //when does this happen?

THEN greeting //name of the state, in case it gets called somewhere else
SAY ~You need to wait, I'm busy!~ //actual text line
IF ~~ THEN EXIT // dialogue ends if imoen is not in party
IF ~IsValidForPartyDialogue("imoen")~ THEN + imoen_protest //continue with "imoen_protest" if Imeon is in party

CHAIN //now we use CHAIN because two characters will talk
IF ~~ THEN ~imoens_dlg~ imoen_protest //here the dialogue continues
~Ey, don't be so rude!~ // Imoen's line
== dlgname1 //dialogue goes back to quest giver
~I had no intention to be rude! I'm just busy!~ //quest giver's line
END //this is for the installer. Now follow transitions or reply options:
++ ~It's alright, we will wait.~ + follow_up_1 //PC reply option. Continue reading with state "follow_up_1" below.
++ ~My friend is right, you know. We are busy, too!~ + follow_up_2 //PC reply option. Continue reading with state "follow_up_2" below.

APPEND dlgname1 //the following content is for our quest giver again

IF ~~ THEN follow_up_1 //state 1
SAY ~Thanks. It's crazy busy lately!~
IF ~~ THEN + follow_up_2 //dialogue continues below
END

IF ~~ THEN follow_up_2 // state 2
SAY ~I can't clone myself. Go and come back later then!~
IF ~~ THEN EXIT //dialogue ends
END

END //APPEND's end

Ignore the rest, ENDs, etc.

Link to comment

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.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...