Many players play romances. Or have BG1 NPC Project installed. And from time to time, it happens that you press a button, and romance music plays, but nothing happens. And sometimes, Imoen or Xan or Ajantis will misfire their scenery talk. "<CHARNAME>, wow, is this a diamond?" - "(sigh) No, Imoen, a bounty notice."
Normally it happens when the player issues a command at the same time the NPC is about to speak his piece. This means that the conditions for the banter are met, the banter is ready to trigger, but it does not, because it was interrupted. This also means that it will very probably trigger next time the banter engine looks for a next banter or lovetalk to fire, and will probably look out of place.
(Sometimes it also happens in the case of very bad scripting, but this is not the point of this tutorial).
So, what to do?
Imagine that your NPC is called "MyNPC", has a script file MyNPC.baf and a banter file BMyNPC.d. Both are really text files, and you can open and edit them with Notepad, Crimson Editor or Context Editor, whichever you prefer.
Now, imagine you want to fire a banter on entering the Graveyard District in Athkatla. And you do not want it to trigger anywhere else, which can well happen, if the player issues a command at the same time your NPC is about to speak.
The trick is FIRST to set the variable for the banter in MyNPC.baf, THEN keep triggering the banter until it runs(again, in MyNPC.baf), and FINALLY, in your BMyNPC.d, increase the variable by one, so the banter will never ever run again. It works wonders.
(Bioware does it from time to time, too. Unfortunately, not always).
So, the code. First, your script file, MyNPC.baf:
IF
InParty(Myself)
See(Player1) // ensures that your NPC is in party and sees Player1
AreaCheck("AR0800") // the banter happens in the Graveyard District of Athkatla
CombatCounter(0)
!See([ENEMY]) // the banter will not run during combat
// You could add other conditions, depending on your banter.
Global("MyGraveyardBanter","GLOBAL",0) //"0" means that the banter hasn't happened, yet
THEN
RESPONSE #100
SetGlobal("MyGraveyardBanter","GLOBAL",1) //"1" means that it is time for the banter to happen, and for the next block in the script to start working...
END
IF
InParty(Myself)
See(Player1) // ensures that your NPC is in party and sees Player1
Global("MyGraveyardBanter","GLOBAL",1) //"1" means that it is time for the banter to happen, so we trigger it
THEN
RESPONSE #100
Interact(Player1) // calls forth a banter from BMyNPC.d
END
And now, your banter file, BMyNPC.d:
IF ~Global("MyGraveyardBanter","GLOBAL",1)~ MyGraveyardBanterStart
// the condition in the banter must match the variable in the script precisely, or we are in trouble.
SAY ~Hey, <CHARNAME>, that's a graveyard! Let us lie down and die!~
++ ~Cool idea.~ DO ~SetGlobal("MyGraveyardBanter","GLOBAL",2)~ + MyGraveyardBanter1.1
++ ~Nah, first we must meet Bodhi.~ DO ~SetGlobal("MyGraveyardBanter","GLOBAL",2)~ + MyGraveyardBanter1.2
++ ~I don't care either way.~ DO ~SetGlobal("MyGraveyardBanter","GLOBAL",2)~ + MyGraveyardBanter1.2
END
// you must ensure that your variable is increased in each and every reply, so the condition Global("MyGraveyardBanter","GLOBAL",1) is never met again, and the banter never runs again.
IF ~~ MyGraveyardBanter1.1
SAY ~Finally!~
IF ~~ EXIT
END
IF ~~ MyGraveyardBanter1.2
SAY ~Aw, you're no fun at all...~
IF ~~ EXIT
END
If your banter is more complicated: say, you want it to trigger two minutes after you enter the graveyard, there is one extra step: setting a timer. So, your code will look like this:
Script file, MyNPC.baf:
IF
InParty(Myself)
AreaCheck("AR0800") // the timer sets if the party is in the Graveyard District of Athkatla and NPC is in party, no other conditions
Global("MyGraveyardBanter","GLOBAL",0) //"0" means that the banter hasn't happened, and the timer hasn't been set, yet
THEN
RESPONSE #100
RealSetGlobalTimer("MyGraveyardTimer","GLOBAL",120) // We set a timer for two minutes
SetGlobal("MyGraveyardBanter","GLOBAL",1) //"1" now means that the timer is set. Now we're waiting until it expires...
END
IF
InParty(Myself)
See(Player1) // ensures that your NPC is in party and sees Player1
AreaCheck("AR0800") // the banter happens in the Graveyard District of Athkatla
CombatCounter(0)
!See([ENEMY]) // the banter will not run during combat
RealGlobalTimerExpired("MyGraveyardTimer","GLOBAL") // The timer has expired, two minutes have passed
Global("MyGraveyardBanter","GLOBAL",1) // The timer has been set
THEN
RESPONSE #100
SetGlobal("MyGraveyardBanter","GLOBAL",2) // The timer has now expired, it is actually time to run the banter
END
IF
InParty(Myself)
See(Player1) // ensures that your NPC is in party and sees Player1
Global("MyGraveyardBanter","GLOBAL",2) // Time to trigger the banter
THEN
RESPONSE #100
Interact(Player1) // calls forth a banter from BMyNPC.d
END
Banter file, BMyNPC.d:
IF ~Global("MyGraveyardBanter","GLOBAL",2)~ MyGraveyardBanterStart
// the condition in the banter must match the variable in the script precisely, or we are in trouble.
SAY ~Hey, <CHARNAME>, that's a graveyard! I've just noticed!~
++ ~Slow, aren't you?~ DO ~SetGlobal("MyGraveyardBanter","GLOBAL",3)~ + MyGraveyardBanter1.1
++ ~Really? Oh! A graveyard!~ DO ~SetGlobal("MyGraveyardBanter","GLOBAL",3)~ + MyGraveyardBanter1.2
++ ~Huh? What?~ DO ~SetGlobal("MyGraveyardBanter","GLOBAL",3)~ + MyGraveyardBanter1.2
END
// you must ensure that your variable is increased in each and every reply, so the condition Global("MyGraveyardBanter","GLOBAL",1) is never met again, and the banter never runs again.
IF ~~ MyGraveyardBanter1.1
SAY ~So much for trying to keep the conversation going, I see.~
IF ~~ EXIT
END
IF ~~ MyGraveyardBanter1.2
SAY ~Do you think the time is right to lie down and die? Ahhh... never mind.~
IF ~~ EXIT
END
And now, the most interesting part: romances and friendships. Same thing, only instead of one variable, we've got many.
Script file, MyNPC.baf:
//This block makes sure that a romance begins, if Player1 meets the requirements: here she should be a female human
IF
InParty(Myself)
Gender(Player1,FEMALE)
Race(Player1,HUMAN) //the conditions for romance match
Global("MyNPCLoveTalk","GLOBAL",0) //makes sure this block runs only once: afterwards MyNPCLoveTalk sets to 1
THEN
RESPONSE #100
RealSetGlobalTimer("MyNPCRomanceTimer","GLOBAL",3000)
SetGlobal("MyNPCRomanceActive","GLOBAL",1)
SetGlobal("MyNPCLoveTalk","GLOBAL",1)
END
IF
InParty(Myself)
See(Player1) // MyNPC is in party and sees Player1
RealGlobalTimerExpired("MyRomanceTimer","GLOBAL") // the timer has expired, so it is time for a new lovetalk
Global("MyNPCRomanceActive","GLOBAL",1) // romance is still active
!AreaType(DUNGEON) // no romancing underground
CombatCounter(0)
!See([ENEMY]) // no romancing during combat
OR(4) // let us pretend we have only four lovetalks. You could have as many as you like, of course.
Global("MyNPCLoveTalk","GLOBAL",1)
Global("MyNPCLoveTalk","GLOBAL",3)
Global("MyNPCLoveTalk","GLOBAL",5)
Global("MyNPCLoveTalk","GLOBAL",7) // "1,3,5,7" means - "between lovetalks", "2,4,6,8" means "it is time for a lovetalk"
THEN
RESPONSE #100
IncrementGlobal("MyNPCLoveTalk","GLOBAL",1) // the variable increases by one, telling that it is time for a lovetalk
END
IF
InParty(Myself)
See(Player1) // ensures that your NPC is in party and sees Player1
RealGlobalTimerExpired("MyRomanceTimer","GLOBAL") // this is actually important: this script block will only be fully checked when the timer has expired, so the performance wouldn't slow down
OR(4)
Global("MyNPCLoveTalk","GLOBAL",2)
Global("MyNPCLoveTalk","GLOBAL",4)
Global("MyNPCLoveTalk","GLOBAL",6)
Global("MyNPCLoveTalk","GLOBAL",8)
THEN
RESPONSE #100
Interact(Player1)
END
Banter file, BMyNPC.d: an example of a lovetalk's beginning
IF ~Global("MyNPCLoveTalk","GLOBAL",2)~ MyLovetalk1Starts
SAY ~Hey, <CHARNAME>, why don't we...~
++ ~NO!~ DO ~IncrementGlobal("MyNPCLoveTalk","GLOBAL",1) RealSetGlobalTimer("MyRomanceTimer","GLOBAL",3000)~ + MyLovetalk1.1
++ ~Ye-e-es?~ DO ~IncrementGlobal("MyNPCLoveTalk","GLOBAL",1) RealSetGlobalTimer("MyRomanceTimer","GLOBAL",3000)~ + MyLovetalk1.2
++ ~Oh, do go away.~ DO ~IncrementGlobal("MyNPCLoveTalk","GLOBAL",1) RealSetGlobalTimer("MyRomanceTimer","GLOBAL",3000)~ + MyLovetalk1.3
END
So, this is all. Your banters, friend- and lovetalks will now run smoothly, flawlessly, and without delay - if you check and re-check that the variables in your script file match the variables in your banter file perfectly.
Enjoy, and if you have any questions, feel free to ask.