Modding Tutorial Part 2: Make Your NPC Comment and Move Along at the End of SoD

Version 3, by jastey

Contents

  1. The Set-Up: Make Biff comment on what happened
  2. NPCs leave the group after The Incident
  3. Biff visits the PC in Prison
  4. Make Biff wait at the Prison Exit
  5. Biff should say something to the PC after the escape
  6. Biff should rejoin the group (if possible)
  7. Credits, Used Tools, and Helpful Links
  8. Version History

This tutorial deals with how to integrate your NPC into the events and cutscenes at the end of SoD. Biff the Understudy will be the example NPC in this guide.

Note: This tutorial contains heavy SoD spoilers.

Some nomenclature:

xx used as a modding prefix in this tutorial. If you don't have a prefix, check the IE Community Filename Prefix Reservations List and register one at Black Wyrm Lair Forums.
xxBiff Biff's script name (death variable)
xxBiffs.bcs Biff's SoD override script
xxBiffsP.dlg Biff's kickout (post) dialogue

1. The Set-Up: Make Biff comment on what happened

After the PC dealt with the main foe and returned from literally hell, the game is basically over except for the PC's "fall". As soon as the player let the PC talk to Sergeant Dazzo on the stairs in bd4300.are to go to rest after the celebrations, the PC's fall is initialized which consists of several cutscenes with a few dialogues in between.

In case your NPC will not accompany the PC into Irenicus Dungeon in BGII (story wise), and you want to save your NPC from the emotional and moral dilemma of witnessing the PC's murder accusations and then just wave "bye" and disappear, you could also make them say good-bye and actually leave the moment the group retreats for rest. This could be done by I_C_T into BDDAZZO.dlg states 2 and 3.

The first of the cutscenes leading to Skie's death and the accusations towards the PC is the cutscene where the Hooded Man makes the PC believe they killed Skie. At the end of this scene, when the Flaming Fist accuses the PC of being a murderer, all NPCs come into the room and some of them comment on what happened. Up to that point, all NPC of the group are handled by the cutscenes directly (bdcut60a.bcs and bdcut60b.bcs) and no further patching is needed, unless you want your NPC to participate even if they are not in the group.

We look at Biff being part of the group now. As already said, some NPCs give a comment on the murder scene. Actually, all of the NPCs have a comment, but it is scripted so there is a 50% chance of each whether they comment or not.

Also, there is two different possible comments with regard to whether the NPCs believe in the PC's guilt or innocence, depending on the PC's reputation. Of course, for your own NPC, you could trigger positive or negative reactions depending on what you like (romance / friendship status, custom count of good deeds, etc.) if you do not want to restrict it to reputation alone.

The comments are handled in bd4100.bcs

where we add the following xxbd4100.baf for Biff:
/* xxbd4100.baf. CHARNAME is accused of murder. Biff comments */

/* first script block: Biff makes a comment in case of lower reputation */
IF
	Global("bd_plot","global",591)
	!GlobalTimerNotExpired("bd_mdd1352_ot_timer","bd4100") //this is SoD's own timer and makes sure all comments fire before the next scripted event kicks in 
	Global("xxBiff_SoDMurderAccusations","bd4100",0)
	ReputationLT(Player1,10) //checks for low reputation. Replace this with whatever you want the NPC's reaction to be dependent on 
	IsValidForPartyDialogue("xxBiff")  
THEN
//	RESPONSE #100 //We want Biff to comment always, so we leave these two lines out. Take these in if you want a 50% chance of your NPC not commenting on the murder accusations. 
//		SetGlobal("xxBiff_SoDMurderAccusations","bd4100",1)
	RESPONSE #100
		SetGlobal("xxBiff_SoDMurderAccusations","bd4100",1)
		SetGlobalTimer("bd_mdd1352_ot_timer","bd4100",ONE_ROUND) //set the timer according to how long the comment is. ONE_ROUND equals ONE_MINUTE = 6 seconds realtime. You can put in the amount of realtime seconds as a number directly.
		DisplayStringHead("xxBiff",~Well, <CHARNAME> might have not been the friendliest, but I know the script so... Nope, I don't think <PRO_HESHE> did it.~)  
END

/* second script block: Biff makes a comment in case of higher reputation */
IF
	Global("bd_plot","global",591)
	!GlobalTimerNotExpired("bd_mdd1352_ot_timer","bd4100")
	Global("xxBiff_SoDMurderAccusations","bd4100",0)
	!ReputationLT(Player1,10) //checks for high reputation. If you used something else above, here goes the direct opposite of the trigger you used above.
	IsValidForPartyDialogue("xxBiff")  
THEN
//	RESPONSE #100 //uncomment these 2 lines for the original SoD 50% chance
//		SetGlobal("xxBiff_SoDMurderAccusations","bd4100",1)
	RESPONSE #100
		SetGlobal("xxBiff_SoDMurderAccusations","bd4100",1)
		SetGlobalTimer("bd_mdd1352_ot_timer","bd4100",ONE_ROUND)
		DisplayStringHead("xxBiff",~Whoa, <CHARNAME>'s such a nice fellow. (And I know the script so... Nope, I don't think <PRO_HESHE> did it.)~)  
END

In the tp2, this line specifies the patching to bd4100.bcs (note the EXTEND_TOP):

/* Biff comments on murder accusation */
EXTEND_TOP ~bd4100.bcs~ ~%MOD_FOLDER%/baf/xxbd4100.baf~
EVALUATE_BUFFER

2. NPCs leave the group after The Incident

After the scene with the murder accusation is through, the NPCs leave the group and the PC finds themselves alone in prison.

If we do nothing here, the NPC will be treated like a multiplayer NPC and just be with the PC inside the prison.

The leaving of the NPCs is handled by bdcut61.bcs. There is the following to be noted for how this is handled:

  1. Is your NPC supposed to appear after the PC leaves the prison, either to say good-bye or because his story places him into Irenicus Dungeon? If not, you do not need to set the variable in the example below.
  2. Is your NPC a continuous NPC that can be met in BGII again? Then you might want to add any "NPC left the group" or "romance no longer open" variables you need to set, either because your NPC makes a reappearance in BGII and you want to make them continuous, or to account for the game being continued in BGII and you don't want the player to start the game with any misleading variables etc.

We add the following for Biff (xxbdcut61.baf):

/*xxbdcut61.baf: Biff leaves the group after the murder accusations */
IF
	InParty("xxBiff")  
THEN
	RESPONSE #100
		CutSceneId("xxBiff")
		ActionOverride("xxBiff",LeaveParty())
		SetGlobal("xxBiff_SoDparty_epilogue","GLOBAL",1) //This will make Biff appear after the PC was released from prison.
		SetGlobal("xxBiff_Kickout","GLOBAL",1) //this is for inner mod detection, for example if the game's a continuous EET game. Put here whatever you need for your own mod.
		ActionOverride("xxBiff",DestroyAllFragileEquipment(ADAMANTINE)) //this destroys all drow equipment, just in case the NPC had any.
END

And to patch bdcut61.bcs with our xxbdcut61.baf, we need this line in the tp2 (note the EXTEND_TOP):

/* after PC gets arrested for murder: Biff leaves party */
EXTEND_TOP ~bdcut61.bcs~ ~%MOD_FOLDER%/baf/xxbdcut61.baf~
  EVALUATE_BUFFER

3. Biff visits the PC in Prison

While in prison, the PC gets some visits depending on romance status of NPCs. The order of visits is the following: First Corwin, if Corwin is not romance interest, then the romance interest will come to visit next, then the "Hooded Man", and finally whoever lets the PC escape (depending on which ending the player gets).

The example below will show how to let Biff visit the PC in prison as the first NPC. The area in question is bd0104.are and the script triggering the visits is bd0104.bcs.

This is what we patch to bd0104.bcs:

/* xxbd0104.baf : Biff visits the PC in prison (as first NPC) */
IF
	Global("chapter","global",13)
	!GlobalTimerNotExpired("bd_jail_visitors_timer","bd0104")
	!GlobalTimerNotExpired("bd_jail_visitors2_timer","bd0104")
	Global("bd_jail_visitors","bd0104",0) //first visit
	Global("xxBiff_jail_visitors","bd0104",0) //check variable for this script block
	Global("xxBiff_SoDparty_epilogue","GLOBAL",1) //some variable that determines whether Biff will appear. For Biff, we take the variable that determines whether he will wait outside the prison
	!Dead("xxBiff")  
THEN
	RESPONSE #100
		SetGlobal("xxBiff_jail_visitors","bd0104",1)
		SetGlobalTimer("bd_jail_visitors_timer","bd0104",FIVE_ROUNDS)
		SetGlobalTimer("bd_jail_visitors2_timer","bd0104",TEN_ROUNDS)
		MoveGlobal("bd0104","xxBiff",[720.620])  //coordinates true for all visiting NPCs
		ActionOverride("xxBiff",ChangeAIScript("bdvisit",OVERRIDE)) //this script will handle the "NPC walks up to the PC's prison door and initiates dialogue"
END
	  

To patch xxbd0104.baf to bd0104.bcs, we need this line in the tp2 (note the EXTEND_TOP):

/* Biff visits PC in Prison */
EXTEND_TOP ~bd0104.bcs~ ~%MOD_FOLDER%/baf/xxbd0104.baf~
  EVALUATE_BUFFER

The dialogue goes into a .d-file. Again, Biff is not in the group, so we use his kickout dialogue xxBiffsP.dlg:

/* (T2) Biff visits PC in prison */

APPEND xxBiffsP 

IF WEIGHT #-1 //we make this higher weighted so it triggers instead of his valid kickout dialogue
~Global("xxBiff_jail_visitors","bd0104",1)~ THEN prison_visit
SAY ~Hey there, <CHARNAME>. I know it sounds weird, but... you'll be fine, so hang in there, alright?~
IF ~~ THEN DO ~SetGlobal("xxBiff_jail_visitors","bd0104",2) 
SetGlobal("bd_visit_player","locals",1) //this will let Biff EscapeArea() via bdvisit.bcs
~ EXIT
END
END //APPEND

4. Make Biff wait at the Prison Exit

After the PC's escape from the prison Imoen and the other NPCs of the "BGII canon" party will be waiting outside in the wilderness: Jaheira and Khalid, and Dynaheir and Minsc. The area is bd6200.are. If your NPC is not supposed to be captured by Irenicus, then you are practically done, because any NPC appearing now will be dragged into the abduction scene and will find themselves inside Irenicus Dungeon (or be killed) - unless you make your NPC leave after Imoen brought the PC to the waiting group outside of the exit. Biff should be here, so we need to add him to bd6200.bcs.

We add two script blocks to the script:

  1. The first one will put Biff to the meeting place outside the prison.
  2. The second one lets him move with the group when they start walking after the greetings.

The according xxbd6200.baf looks like this:

/* end of SoD. Biff will be at the meeting place */

IF
	Global("chapter","global",13)
	Global("xxBiff_SoDEndMove","bd6200",0)
	!Dead("xxBiff")  
	!InPartyAllowDead("xxBiff")
	Global("xxBiff_SoDparty_epilogue","GLOBAL",1) //this is the variable that was set in the xxbdcut61.baf
THEN
	RESPONSE #0
		SetGlobal("xxBiff_SoDEndMove","bd6200",1)
		MoveGlobal("bd6200","xxBiff",[xxx.yyy]) //chose coordinates near the meeting place: the clearing at the southeast. For example: Minsc is waiting at [1660.1120]
		ActionOverride("xxBiff",SetSequence(SEQ_READY)) //from IESDP: This action instructs the active creature to perform the specified animation sequence. Values are from seq.ids.
		ActionOverride("xxBiff",Face(NW)) //depending on where the NPC stands, they should face the north east
		ActionOverride("xxBiff",SetGlobal("bd_joined","locals",0)) //we remember these from tutorial 1
		ActionOverride("xxBiff",SetGlobal("bd_retreat","locals",0))
		ActionOverride("xxBiff",SaveObjectLocation("LOCALS","bd_default_loc",Myself))
		ReallyForceSpellDeadRES("bdrejuve","xxBiff")  // heals and removes spell effects
		ChangeEnemyAlly("xxBiff",NEUTRAL)  
		ChangeSpecifics("xxBiff",ALLIES)  
		ActionOverride("xxBiff",ChangeAIScript("xxBiffs",OVERRIDE))
		ActionOverride("xxBiff",ChangeAIScript("",CLASS))
		ActionOverride("xxBiff",ChangeAIScript("SHOUT",RACE))
		ActionOverride("xxBiff",ChangeAIScript("BDFIGH01",GENERAL)) //Biff gets the fighter script. Have a look at bdparty.bcs to see what other scripts are available for the different classes.
		ActionOverride("xxBiff",ChangeAIScript("",DEFAULT))
		ActionOverride("xxBiff",SetDialog("xxBiffsP")) //we need the dialogue because all present NPCs comment on the PC's escape
		Continue()
END

/* Biff moves across area with the others */
IF
	Global("bd_plot","global",670)
	Global("xxBiff_SodMovesbd6200","bd6200",0)
	InMyArea("xxBiff")  
THEN
	RESPONSE #100
		SetGlobal("xxBiff_SodMovesbd6200","bd6200",1)
		ActionOverride("xxBiff",MoveToPoint([1410.1410])) //all NPCs move towards this point, no need for other coordinates
		Continue()
END

And for the tp2 (note the EXTEND_TOP):

/* Biff waits in front of the prison exit */
EXTEND_TOP ~bd6200.bcs~ ~%MOD_FOLDER%/baf/xxbd6200.baf~
  EVALUATE_BUFFER

5. Biff should say something to the PC after the escape

After the escape from the prison and meeting with the canon NPC party, Imoen initiates dialogue and every NPC present says something to the recent events. We want Biff to say something here, too, so we need to I_C_T into bdimoen.dlg state 99. And since Biff isn't in the party at that point, this interjection needs to go into his kickout dialogue xxBiffsP.dlg.

The following is what we put into a .d file for compilation. The first part is for the "assassin ending" (Global("bd_player_exiled","global",0)): the PC didn't convince the Dukes of their innocence and had to flee from the prison with the help of an unknown assassin. The second one is for the ending where the Dukes set the PC free and the PC is guided to the exit by an FF soldier (for !Global("bd_player_exiled","global",0)). Note: I_C_T3 means that there are several interjections with different triggers, but all of them should trigger if the conditions are met (and not the first one determines whether any of them shows, as it is for I_C_T):

/* PC escaped from the prison. All NPCs say a greeting */
I_C_T3 BDIMOEN 99 xxBiff_SoDMurderComment 
== xxBiffsP IF ~Global("bd_player_exiled","global",0)
!Dead("xxBiff")
InMyArea("xxBiff")
!InParty("xxBiff")~ THEN ~Good you made it out. Sorry about all the blood.~
== xxBiffsP IF ~!Global("bd_player_exiled","global",0)
!Dead("xxBiff")
InMyArea("xxBiff")
!InParty("xxBiff")~ THEN ~Good to see you. I like this ending way better!~
END

6. Biff should rejoin the group (if possible)

After the PC and all present NPCs start moving and the scene fades to black, the cutscene bdcut64x.bcs handles the rejoining of the canon NPCs (Imoen - first time she joins in SoD!, Minsc, Dynaheir, Jaheira, Khalid) before calling bdcut65.bcs which transfers and moves the group onto bd6100.are, the final abduction area. This takes into consideration whether there is enough empty slots for the NPCs (for e.g. multiplayer game with more than one protagonist), and also whether the canon NPCs are actually still alive and present.

This means, that with all canon NPCs alive (Imoen will be alive no matter what, but the other four might not be alive any more and won't be present then), the group is full with 6 people, and there is no "room" for your NPC. Still, if there is room, they should join the party.

For Biff, we want the following to happen: if there is still room in the group (NumInPartyLT(6)), he should join. By joining, he will be present in the final abduction scene as a party member. For BG:EE with SoD, what follows is the actual abduction scene where the group walks into a clearing in the forest, the player gets the controls back for 2 seconds, then Imoen says it feels a little off and the endmovie starts.

If Biff doesn't join, he will remain in the bd6200.are, scripting wise, and will not be present at the abduction scene. For SoD this is sufficient, though, as the player has hardly time to realize the missing of a non-party member. For EET, there is a lengthier cutscene which shows the actual fight prior to the abduction, and there the presence of your NPC might be interesting because the player might actually notice the absence. Unfortunately, I couldn't get the cutscene to work if Biff is supposed to follow albeit not in party, he either starts walking long after everyone else or the cutscene didn't work properly, so I can't give a template for this here.

This is what we add to bdcut64x.bcs for Biff:

/* xxbdcut64x.baf
Let Biff join after PC escaped from prison (if there is still a free slot in the group) */
IF
	Global("xxBiff_SoDEndMove","bd6200",1) //same variable as used in xxbd6200.baf to spawn Biff!
	Global("bd_party_check","bd6200",5) //all canon NPCs were already considered
	!Dead("xxBiff")  
	!InParty("xxBiff") 
	InMyArea("xxBiff") 
	NumInPartyLT(6)
THEN
	RESPONSE #100
		CutSceneId(Player1)
		SetGlobal("xxBiff_SoDEndMove","bd6200",2)
		ActionOverride("xxBiff",JoinParty())
		SmallWait(10)
		Continue()
END

And for the tp2:

/* (T2) Let Biff join after PC escaped from prison (if there is still a free slot in the group) */
EXTEND_TOP ~bdcut64x.bcs~ ~%MOD_FOLDER%/baf/xxbdcut64x.baf~
  EVALUATE_BUFFER

Credits, Used Tools, and Helpful Links

Thank you to Argent77 for the html template!

Used Tools:

Near Infinity

IESDP

grepWin

Notepad++

Links:

BeamDog Forums

The Gibberlings 3

Kerzenburgforum

Spellhold Studios

Pocket PLane Group

Prefix Reservation at Black Wyrm Lair Forums

Version History

Version 3:

Version 2:

Version 1: