Jump to content


Photo

Coding Interjections


  • Please log in to reply
12 replies to this topic

#1 Kaeloree

Kaeloree

    Head Molder

  • Administrator
  • 9198 posts

Posted 22 September 2008 - 12:44 AM

Index
<a href="#intro">Introduction</a>
1. <a href="#tut1">Planning and preparation</a>
2. <a href="#tut2">A basic interjection</a>
3. <a href="#tut3">Interjection types and when to use them</a>
...... 3.1. <a href="#tut3_1">INTERJECT_COPY_TRANS (ICT)</a>
...... 3.2. <a href="#tut3_2">INTERJECT_COPY_TRANS2 (ICT2)</a>
...... 3.3. <a href="#tut3_3">INTERJECT_COPY_TRANS3 (ICT3)</a>
...... 3.4. <a href="#tut3_4">INTERJECT_COPY_TRANS4 (ICT4)</a>
...... 3.5. <a href="#tut3_5">INTERJECT</a>
...... 3.6. <a href="#tut3_6">Passbacks</a>
4. <a href="#tut4">Conditions, actions and more</a>
...... 4.1 <a href="#tut4_1">Performing actions</a>
...... 4.2 <a href="#tut4_2">Conditions</a>
...... 4.3 <a href="#tut4_3">PC options in interjections</a>
5. <a href="#tut5">Conclusion</a>


<a name="intro"></a>Introduction
Interjections are by far one of the most important part of an NPC. They often tell us far more about a character than dialogue or banter, by showing how they react to the world around them. Take, for example, Madam Nin in the Copper Coronet. If the PC is romancing our NPC, they're probably not going to like it if Madam Nin is a little too friendly. We can demonstrate this by adding in a line from our NPC--but how do we do that?

This tutorial is going to show you how, and depending on the situation which commands you need to use.

Throughout this tutorial I'm using examples from various available mods--thank you to Miss Sakaki and Feuille (creators of The Luxleys), ....

Let's get started, shall we?



#2 Kaeloree

Kaeloree

    Head Molder

  • Administrator
  • 9198 posts

Posted 22 September 2008 - 12:45 AM

<a name="tut1"></a>Planning and preparation
This tutorial assumes you have a very (very) basic knowledge of dialogues and dialogue files. I suggest you read the following tutorials before you read this one:

To create interjections, you will need either Infinity Explorer (I recommend IE because it shows the dialogue structure and includes other NPCs' comments as well, making it far easier to decide where your character should comment) or Near Infinity.

You will also need a text editor (ConTEXT is the one I use, but Crimson Editor is also widely used), and I suggest you download and use the WeiDU highlighters.

EDIT jastey: argent77 also wrote a WeiDU highlighter for Notepad++

Lastly, throughout this tutorial, ## denotes usage of your own modding prefix. If you don't have a modding prefix, go and register one over at Blackwyrm Lair's Community Prefix list.


Edited by jastey, 05 April 2019 - 10:40 AM.


#3 Kaeloree

Kaeloree

    Head Molder

  • Administrator
  • 9198 posts

Posted 22 September 2008 - 12:46 AM

<a name="tut2"></a>A basic interjection
Coding interjections is simple, once you know how. It's just a matter of finding where in the dialogue you want your NPC to interject and determining what type of interjection you need to make it.

The majority of interjections use the "basic" form of interjection coding. This is called INTERJECT_COPY_TRANS, and looks like this (from the Luxley Family mod):

INTERJECT_COPY_TRANS MAZZY 20 FHLSMaz1
== FHLSEBJ
IF ~InParty("FHLSEB") !StateCheck("FHLSEB",CD_STATE_NOTVALID)~ THEN
~I think this would be an excellent time to remember that discretion is the better part of valour.~
END
Look confusing? Let's break it down and make it simpler.

INTERJECT_COPY_TRANS DIALOGUEFILE STATENUMBER ##INTERJECTION_NAME
== NPCJ
IF ~InParty("MyNPC") !StateCheck("MyNPC",CD_STATE_NOTVALID)~ THEN
~Blah blah blah.~
END
Let's explain some parts which might be a little confusing at first.
  • DIALOGUEFILE, funnily enough, pertains to the dialogue file you want to interject into. In our above example, we want to interject into MAZZY.

  • STATENUMBER references the number of the dialogue state you want to interject into. If you have a dialogue open in Near Infinity, go to the "Edit" tab. The state number of a particular line of dialogue will be on the left hand side (State 1, State 2 etc). In Infinity Explorer, it will be in the top line of the dialogue box, labelled "phrase X".

  • INTERJECTION_NAME This is pretty self-explanatory. But one extremely important thing to note is that the interjection name must be unique! Always use your modder prefix, otherwise if your interjection name is the same as another variable in the game, it will cause errors. This is because INTERJECTION_NAME becomes a variable; it is set to 0 when the interjection hasn't happened, and 1 when it has.
Replace NPCJ with your NPC's J (joined) dialogue file name and "MyNPC" with your NPC's death variable. (The death variable, if you don't already know, is what is used to reference an NPC within the game, and should be unique.)



Let's create our own interjection. Say our NPC, Drogar, hates drow and should have an interjection saying so before Viconia joins the party. First up, we need to open Near Infinity or Infinity Explorer and find Viconia's unjoined dialogue file (VICONI.DLG). Next, we find a line where it would be appropriate for Drogar to interject. This would be the line: "It would be both a privilege and an honor to join you, CHARNAME. What say you to my proposal?"

After this line, I want the following exchange to occur.

Drogar: CHARNAME, she's a ruddy drow! If she be joinin' us, I can't be promisin' there'll continue to be an "us" with me in it for much longer!
Viconia: And this would detriment PRO_HIMHER...how?

Several NPCs already interject into this dialogue at this point, so we'll make Drogar interject as well. Because there are no special circumstances, we use the basic interjection form, INTERJECT_COPY_TRANS. This will ensure that after Drogar has his interjection, and any other NPCs will also have theirs. If we look at the original dialogue, the state in which we want Drogar to interject is state 2 (in Infinity Explorer, phrase 2).

INTERJECT_COPY_TRANS VICONI 2 ##DrogarVicJoin
== DrogarJ
IF ~InParty("Drogar") !StateCheck("Drogar",CD_STATE_NOTVALID)~ THEN
~CHARNAME, she's a ruddy drow!  If she be joinin' us, I can't be promisin' there'll continue to be an "us" with me in it for much longer!~
== VICONI ~And this would detriment PRO_HIMHER...how?~
END
Fairly simple, hey?

#4 Kaeloree

Kaeloree

    Head Molder

  • Administrator
  • 9198 posts

Posted 22 September 2008 - 12:46 AM

<a name="tut3"></a>Interjection types and when to use them
There are five different ways to code an interjection, and though we generally use INTERJECT_COPY_TRANS, in some cases we need to use one of the other methods. Two of the methods I've outlined, ICT2 and ICT4, should not be used unless you know exactly what you're doing and have a good reason for doing it; they're here for completion's sake, not because you should be using them.

There are a variety of reasons we use these different methods, and I'll attempt to explain each, as well as why and where we use them.

Here's a very brief overview, with more detailed explanations below!
  • Use ICT if it is a normal interjection, and there are no actions performed by the original speaker.
  • Use ICT with a passback if it is a normal interjection with actions that need to be performed by the original speaker.
  • Use ICT3 if the different "states" (lines) of the interjection have different conditions and should be evaluated independently of other states/lines in the interjection, and if there are no actions performed by the original speaker.
  • Use ICT3 with a passback if the different "states" (lines) of the interjection have different conditions and should be evaluated independently of other states/lines in the interjection, and if there are actions that need to be performed by the original speaker.
  • Use INTERJECT if you want to hijack the flow of conversation and/or want PC replies.



<a name="tut3_1"></a>INTERJECT_COPY_TRANS (ICT)
INTERJECT_COPY_TRANS is the form we use most often when coding interjections, and is usually shortened to ICT. You can find a more in-depth tutorial and explanation of ICT here.

It should be used when there are no actions being performed immediately after the state you want to interject into. If there are, you should use a passback (have the original NPC say a line after your NPC) or use INTERJECT_COPY_TRANS2.

It should not be used if, for example, you want your NPC to say different things depending on the state of a variable; for this you would use INTERJECT_COPY_TRANS3.




<a name="tut3_2"></a>INTERJECT_COPY_TRANS2 (ICT2)
WARNING: Unless you know exactly what you are doing and have a very good reason for doing so, do not use ICT2, use ICT with a passback instead.

INTERJECT_COPY_TRANS2 is what we use when the NPC we are talking to performs an action after the state we want to interject into. You can find a more in-depth tutorial and explanation of ICT here.

However, there is a danger with using ICT2. If the state you are interjecting into has PC options and each has a different action, it can cause errors. For example, you wouldn't use ICT2 in this instance:

NPC: This is my dagger, and I shall do what I like with it!
PC: Not if I take it from you! (Gives the PC the dagger.)
PC: Fine. (Sets a variable.)
PC: Not if I kill you first! (Turns the NPC into an enemy).
If we used ICT2 here, it would cause problems, so it is better to use ICT with a <a href="#tut3_6">passback</a>.




<a name="tut3_3"></a>INTERJECT_COPY_TRANS3 (ICT3)
ICT3 works just like ICT, except that each "state" (or line) is treated as the first, meaning every line's conditions are evaluated.

Say you want your NPC to say something different depending on at which point they are in a quest.

INTERJECT_COPY_TRANS3 DIALOGUEFILE STATENUMBER ##INTERJECTION_NAME
== NPCJ IF ~Global("##Quest","GLOBAL",0)~ THEN ~This is what I'll say if my quest isn't done yet.~
== NPCJ IF ~Global("##Quest","GLOBAL",1)~ THEN ~This is what I'll say if my quest is in progress!~
== NPCJ IF ~Global("##Quest","GLOBAL",2)~ THEN ~This is what I'll say if my quest is complete.~
END
Here's another example illustrating creating interjections for multiple NPCs at the same place in a dialogue:

INTERJECT_COPY_TRANS3 DIALOGUEFILE STATENUMBER ##INTERJECTION_NAME
== NPC1J IF ~InParty("NPC1") !StateCheck("NPC1",CD_STATE_NOTVALID)~ THEN ~I am NPC1. Pleased to meet you.~
== NPC2J IF ~InParty("NPC2") !StateCheck("NPC2",CD_STATE_NOTVALID)~ THEN ~Shove off.~
== NPC3J IF ~InParty("NPC3") !StateCheck("NPC3",CD_STATE_NOTVALID)~ THEN ~Ooh, you're a pretty one, aren'tcha?~
END
If you tried to do this using ICT only the first line would be evaluated, and if the conditions weren't met the engine would skip over the rest of the interjection entirely, negating the other states/lines.




<a name="tut3_4"></a>INTERJECT_COPY_TRANS4 (ICT4)
WARNING: Unless you know exactly what you are doing and have a very good reason for doing so, do not use ICT4, use ICT3 with a passback instead.

ICT4 is a combination of ICT2 and ICT3; each line is evaluated as the first, and any actions are kept with the original speaker.




<a name="tut3_5"></a>INTERJECT
INTERJECT was the original interjection format, and though it is now better to use the newer ICT structure, there are times when INTERJECT is appropriate.

You should use INTERJECT when you want to hijack the flow of conversation or add PC dialogues to an interjection. For example, INTERJECT would be the right form to use when an NPC "takes over" a dialogue by making a decision which would have previously been made by the PC.

INTERJECT DIALOGUENAME STATENUMBER ##INTERJECTION_NAME
== NPCJ IF ~InParty("NPC1") !StateCheck("NPC1",CD_STATE_NOTVALID)~ THEN
~<insert some witty commentary!>~
COPY_TRANS DIALOGUENAME STATENUMBER
The structure of INTERJECT is a little different to ICT, as you can see. The beginning works the same, but in the last line, you decide where in the dialogue you want the dialogue to go after your interjection. So if your NPC makes a decision, you can link to the state in which the original speaker reacts to the decision. Generally the use of INTERJECT is frowned upon as it can "railroad" the PC and it nullifies any other NPC's comments.



<a name="tut3_6"></a>Passbacks
A passback is when you give the original NPC a line of dialogue after your NPC. This ensures that the any actions originally being performed by the original speaker will remain with the original speaker. If you didn't use a passback, then the actions would be transferred to your NPC--which is probably not what you want! For example:

(Original line) NPC: You adventurers are all the same--whiny, spineless cowards, the lot of you!
Drogar: You want to take that back, ye ungrateful swine!
NPC: I really don't.
Code example (completely fabricated):

I_C_T ALLIYA 4 ##Alliyainterjection
== DROGARJ IF ~InParty("Drogar") !StateCheck("Drogar",CD_STATE_NOTVALID)~ THEN
~You want to take that back, ye ungrateful swine!~
== ALLIYA ~I really don't.~
END


#5 Kaeloree

Kaeloree

    Head Molder

  • Administrator
  • 9198 posts

Posted 22 September 2008 - 12:47 AM

<a name="tut4"></a>Conditions, actions and more

<a name="tut4_1"></a>Performing actions
In an interjection, you may want to set a variable or some such. This is very simple; all you need to do is add a DO ~~ line. Here's an example from Iylos:

// Balthazar
INTERJECT BALTH 8 LK#IylosBalth
  == LK#IYLJ IF ~InParty("Iylos") InMyArea("Iylos")~ THEN ~Balthazar, are you even going to acknowledge me?~
  == BALTH ~Of course.  I was simply hoping to wait until my business with <CHARNAME> was concluded.  Will you join me in my quarters, Iylos?~ 
  = ~At your earliest convenience, of course.~ 
  DO ~SetGlobal("LK#IylosDialogueFadeNow","GLOBAL",1)~
COPY_TRANS BALTH 8
As you can see, we're setting the (lengthy) variable "LK#IylosDialogueFadeNow". Like in a normal dialogue, you can perform actions in this way--but be careful not to enact anything which might break the original dialogue.

Interjections work somewhat like CHAIN, and so you can do things with it like you do in CHAIN. This includes, when an NPC says a second line, just using "=" instead of "== NPCJ".


<a name="tut4_2"></a>Conditions
You may also want to add more conditions to your interjections. Perhaps your NPC will only make a comment if he or she is romancing the PC, or has spoken with someone? You can do this by adding in further conditions to the first set.

INTERJECT DIALOGUEFILE STATENUMBER ##INTERJECTION_NAME
== NPCJ
IF ~InParty("MyNPC") !StateCheck("MyNPC",CD_STATE_NOTVALID) Global("##MyNPCHasSpokenToAnother","GLOBAL",1) OR(2) Global("##MyNPCRomanceActive","GLOBAL",1) Global("##MyNPCRomanceActive","GLOBAL",2)~ THEN
~Blah blah blah.~
END
Remember that if the first line's conditions aren't met, then any subsequent lines will be ignored, so if you have a comment which changes depending on the situation you'll probably want to use ICT3 or ICT4.


<a name="tut4_3"></a>PC options in interjections
Finally you can add PC options to an interjection, to have a little conversation with your NPC before bringing it back to the original dialogue. Here's an example, from my imaginary dwarven NPC Drogar. This is for when the PC goes to meet Bodhi in the Graveyard district.

INTERJECT BODHI 4 LK#DrogBodhi.0
  == LK#DROGJ IF ~InParty("Drogar") !StateCheck("Drogar",CD_STATE_NOTVALID)~ THEN ~CHARNAME, where I come from, vampires spell bad luck--however pretty they may be.  But she does have a point.  We don't be knowin' anything about the thieves or what they really want.~
END
  ++ ~You're right, we don't.~ EXTERN LK#DROGJ LK#DrogBodhi.1
  ++ ~If you say so.~ EXTERN LK#DROGJ LK#DrogBodhi.1
  ++ ~This is none of your damn business, Drogar, so shut your mouth.~ EXTERN LK#DROGJ LK#DrogBodhi.2

APPEND LK#DROGJ
  IF ~~ LK#DrogBodhi.1
	SAY ~I'd advise ye to listen to what she has to say, at least.~
	COPY_TRANS BODHI 5
  END

  IF ~~ LK#DrogBodhi.2
	SAY ~An' there's your temper again.~
	= ~Just listen to what she has to say is all I ask.~
	COPY_TRANS BODHI 5
  END
END


#6 Kaeloree

Kaeloree

    Head Molder

  • Administrator
  • 9198 posts

Posted 22 September 2008 - 12:47 AM

<a name="tut5"></a>Conclusion
So that's it--pretty simple, really. If you're having any trouble, the friendly folks at our IE Modding Help forum are happy to lend you a hand, so don't be afraid to post! No question is too stupid; we all have to start somewhere, remember.

A few final notes:
  • The use of tab spaces before lines is unnecessary, and completely up to the coder. I like it because it makes the code look cleaner.
  • I_C_T and INTERJECT_COPY_TRANS are both usable; neither is the "wrong way." I tend to use I_C_T because it's quicker to type.
I hope you find this tutorial useful--big thanks to the bigg for correcting me the correct usage of the various interjection forms, and also thanks to jastey, berelinde, cmorgan and SConrad for their research and posts over the years on interjections, which were invaluable in writing this tutorial.

Cheers!
Liam

#7 jastey

jastey
  • Administrator
  • 3218 posts

Posted 22 September 2008 - 05:01 AM

I love you!

#8 Kulyok

Kulyok
  • Modder
  • 2450 posts

Posted 22 September 2008 - 05:49 AM

However, there is a danger with using ICT2. If the state you are interjecting into has PC options and each has a different action, it can cause errors.


What problems and errors are we talking about? Except a warning line during Weidu installation, I mean.

#9 berelinde

berelinde

    Troublemaker

  • Modder
  • 4916 posts

Posted 22 September 2008 - 07:00 AM

In the example K'aeloree gives, where the three different actions are giving the PC a dagger, setting a variable, and turning the speaker hostile, using ICT2 here would invariably give the PC the dagger. The first action is always performed.

There is a workaround to this. By moving the action to the taget state, i.e. not the transition, the desired outcome would still be reached. You can do this for each of the exampes K'aeloree gives, but it becomes more complicated if the second action is StartStore(), since that ends the dialogue with the action.

Take this hypothetical state:

IF ~~ THEN BEGIN SHOPKEEP 0
SAY ~Can I interest you in my wares?~
IF ~~ THEN REPLY ~Not today, thanks.~ EXIT
IF ~~ THEN REPLY ~Yes, show me what you have.~ DO ~StartStore()~ EXIT
END

Suppose you wanted to have your NPC interject with "Why would we want to see what you're selling?" In these examples, I am using no modder shorthand. This is deliberate.

I_C_T2 SHOPKEEP 0 ##label
== ##NPCJ IF ~InParty("##DV") InMyArea("##DV") !StateCheck("##DV",CD_STATE_NOTVALID)~ THEN ~Why would we want to see what you're selling?~
END

The dialogue would always just end, with no store being opened. I found this out the hard way with Kelddath Ormlyr's dialogue, as did Domi when she wanted Kivan to interject into the same state.

You can do this very, very easily, in an unobtrusive, compatibility-friendly fashion using I_C_T with a passback:
I_C_T SHOPKEEP 0 ##label
== ##NPCJ IF ~InParty("##DV") InMyArea("##DV") !StateCheck("##DV",CD_STATE_NOTVALID)~ THEN ~Why would we want to see what you're selling?~
== ##NPCJ IF ~InParty("##DV") InMyArea("##DV") !StateCheck("##DV",CD_STATE_NOTVALID)~ THEN ~I sell the finest widgets in Amn! Care to see?~
END

You could also use an INTERJECT to divert the dialogue to some new sates, but that is not the most compatibility-friendly way to do it.
INTERJECT SHOPKEEP 0 ##label
== ##NPCJ IF ~InParty("##DV") InMyArea("##DV") !StateCheck("##DV",CD_STATE_NOTVALID)~ THEN ~Why would we want to see what you're selling?~
END
IF ~~ THEN REPLY ~Not today, thanks.~ EXIT
IF ~~ THEN REPLY ~Yes, show me what you have.~ EXTERN SHOPKEEP ##label2

APPEND SHOPKEEP

IF ~~ THEN BEGIN ##label2
SAY ~You won't be sorry.~
IF ~~ THEN DO ~StartStore()~ EXIT
END
END

There are two problems with that. The first is that INTERJECT introduces compatibilty issues. You can break someone else's mod, and someone else can break yours. Installation order becomes more important, as well.

Now let's take K'aeloree's example, coded.

IF ~~ THEN BEGIN BIONPC 15
SAY ~This is my dagger, and I shall do what I like with it!~
IF ~~ THEN REPLY ~Not if I take it from you!~ DO ~GiveItem("dagg01",Player1)~ EXIT //not sure about the syntax of GiveItem()
IF ~~ THEN REPLY ~Fine.~ THEN DO ~SetGlobal("KeepDagger","GLOBAL",1)~ EXIT
IF ~~ THEN REPLY ~Not if I kill you first!~ THEN DO ~Enemy()~ EXIT
END

If you wanted to move the actions to new states, taking them out of the transitins, you would have to use INTERJECT and add three new states, since all the original transitions end the dialogue there. This introduces the same complications I gave in my StartStore() example. Using INTERJECT followed by COPY_TRANS would just disable the content added by anybody who took the trouble to use I_C_T with a passback.

Edits: corrected typos produced by having sticky keys.

Edited by berelinde, 22 September 2008 - 07:06 AM.

"Imagination is given to man to console him for what he is not; a sense of humor, for what he is." - Oscar Wilde

berelinde's mods
TolkienAcrossTheWater website
TolkienAcrossTheWater Forum


#10 Kaeloree

Kaeloree

    Head Molder

  • Administrator
  • 9198 posts

Posted 22 September 2008 - 01:46 PM

Thanks for fielding that, b! :)

jastey: Heh, I thought it was about time a real tutorial was written on the subject. I just hope it isn't too confusing!

#11 Kulyok

Kulyok
  • Modder
  • 2450 posts

Posted 22 September 2008 - 10:03 PM

This is interesting - thank you. I really should test these thingies sometime.

And great to see all the answers in one place.

#12 -jastey*-

-jastey*-
  • Guest

Posted 22 September 2008 - 10:16 PM

jastey: Heh, I thought it was about time a real tutorial was written on the subject. I just hope it isn't too confusing!

I think it's great. I haven't read it thoroughly yet, though, I have to admit. I was planning on writing such a tutorial for, I don't know, ages. You saved me a lot of work! Not writing a tutorial leads to the case of giving short explanations all over the place, which in the end is more work, so having this tutorial to link to is really cool. :cheers:

#13 Kaeloree

Kaeloree

    Head Molder

  • Administrator
  • 9198 posts

Posted 22 September 2008 - 10:45 PM

I figured it would be easier to have one consolidated thread--and I'll endeavor to keep it as updated as I can, so that we won't need to have to explain things over and over again!

I'll edit the tut later to link to berelinde's explanation, as well. :cheers: Thanks, b :D