Jump to content

Adding Randomization for Banter (Complete Noob)


Recommended Posts

Before I post my actual problem I would like to explain that I am a complete noob and decided to take it upon myself to add a certain NPC to my Baldurs Gate game (for a little fun). So please be aware that some of my code will be unorthodox, messy and probably hard to follow. However, it functions from my petty testing using CTRL + I.


I have also been slowly trawling through "theacefes" NPC creation guide



What I was wondering was if there was any way to make the dialogue in the "npcnameB.d" (you know the file, the B file :D) file randomized. That is, occurs in no specific order.


I encounted this problem while testing as I found that my banter (while holding ctrl + i) came in the order it was written. I Figured I could jumble up the banter myself however it would make it incredibly messy and difficult to find again.


Anyway: This is what I currently have:

IF ~InParty("ZZDappy")
THEN ZZDappyB DappyMinsc1
 ~Why do you have a hamster?~
DO ~SetGlobal("ZZDappyMinsc","GLOBAL",1)~
 == BMINSC ~Why do you wear a silly hat?~
 == ZZDappyB ~Nah bruv, dis hat ain't silly.~
  = ~Your rodent is silly doe!~
 == BMINSC ~Get your ass over here for a butt-kicking!~
 == ZZDappyB ~Allow it!~
IF ~InParty("ZZDappy")
THEN ZZDappyB DappyMinsc2
DO ~SetGlobal ("ZZDappyMinsc2","Global",1)~
 == BMINSC ~Go away.~
 == ZZDappyB ~Man's raw.~
//DappyPC1 (Expect MANY of these)
IF ~InParty("ZZDappy")
THEN ZZDappyB DappyPC1
 ~Oi <CHARNAME>, you got a blem?~
DO ~SetGlobal ("DappyPCTalk","GLOBAL",1)~
++ ~A what?~ + DappyPC1-1
++ ~Here you go.~ + DappyPC1-2
IF ~~ THEN ZZDappyB DappyPC1-1
 ~Nvm man.~
IF ~~ THEN ZZDappyB DappyPC1-2
~THX Bro x.~
IF ~InParty("ZZDappy")
THEN ZZDappyB DappyPC2
 ~Oi <CHARNAME>, see that peng bird?~
DO ~SetGlobal ("DappyPCTalk2","GLOBAL",1)~
++ ~Peng?~ + DappyPC2-1
++ ~Yeah I do.~ + DappyPC2-2
IF ~~ THEN ZZDappyB DappyPC2-1
 ~Hot Bruv, I don't Believe you man!~
IF ~~ THEN ZZDappyB DappyPC2-2
 ~I'm gonna tap that.~
IF ~InParty("ZZDappy")
THEN ZZDappyB DappyPC3
 ~Oi <CHARNAME>, This will be our third interaction together.~
DO ~SetGlobal ("DappyPCTalk3","GLOBAL",1)~
++ ~Really?~ + DappyPC3-1
++ ~No Way~ + DappyPC3-2
IF ~~ THEN ZZDappyB DappyPC3-1
 ~Yeah, Want to go for an additional comment?~
DO ~SetGlobal ("DappyPCTalk3","GLOBAL",2)~
++ ~Okay!~ + DappyPC3-3
++ ~No~ + DappyPC3-4
IF ~~ THEN ZZDappyB DappyPC3-3
 ~Pretty Cool.~
IF ~~ THEN ZZDappyB DappyPC3-4
 ~Okay Then.~
IF ~~ THEN ZZDappyB DappyPC3-2


I currently use ZZ as a prefix. All text will change, I just quickly wrote it in for testing purposes.


But anyway, as it stands all text does work. But is delivered in that order.

As Banter is as Banter does, this has no significance in which order it comes in. But I would like it to be slightly random so that perhaps I would get NPC-PC banter then NPC-Minsc banter rather than NPC-Minsc twice in succession.


If you would like me to further elaborate, just post :)


Thank you in advance!


PS: If you have any tips on restructuring or cool tips in general that will be great.

Link to comment

Three easy ways of doing this -


1. randomize the banter by using RandomNum() to have things skip around within the CHAIN construct itself. Example:


CHAIN IF ~RandomNum(3,1) InParty("ZZDappy") InParty(Player1) !ActuallyInCombat() Global("DappyPCTalk2","GLOBAL",0)~ THEN ZZDappyB DappyPC2   ~Oi <CHARNAME>, see that peng bird?~ DO ~SetGlobal ("DappyPCTalk2","GLOBAL",1)~ END ++ ~Peng?~ + DappyPC2-1 ++ ~Yeah I do.~ + DappyPC2-2[font=monospace] [/font]

sets up a 33.3% chance that the banter will be true when the engine calls - and since the global closes, each banter runs only once. If the engine fails to roll a 1, it looks for the next available true banter, so you can load up a bunch of these and it will let regular banters happen any time it rolls the number... which i think it does once for all the dialogs within a state, but once per actual called dialog (we would want someone really knowledgeable to answer that one and help with percentages. BioWare uses this kind of thing in some of the scenery non-joinable NPCs to have a varied reaction when a player clicks on them multiple times. This is the easiest one to try and play around with.


2. Make a series of banters that start basically the same way, but follow different pathways, so when you follow the initial ++ reply state, it goes to a master state that looks something like this:


IF ~~ dappymasterstate
 SAY ~Well, now...~
 IF ~RandomNum(10,1)~ THEN GOTO dappy_says_a_witty_banterline_1
 IF ~RandomNum(10,2)~ THEN GOTO dappy_says_a_witty_banterline_2
 IF ~RandomNum(10,3)~ THEN GOTO dappy_says_a_witty_banterline_3
 IF ~RandomNum(10,4)~ THEN GOTO dappy_says_a_witty_banterline_4
 IF ~RandomNum(10,5)~ THEN GOTO dappy_says_a_witty_banterline_5
<<on up to 10>>


3. Skip banters, and instead use Zrayen's "randomize by script block" idea, moving to the joined dialog file - add this kind of thing to the npc script;

Global ("DappyPCTalk2","GLOBAL",0)
SetGlobal ("DappyPCTalk2","GLOBAL",1)
SetGlobal ("DappyPCTalk2","GLOBAL",1)
SetGlobal ("DappyPCTalk2","GLOBAL",1)


with a dialog entry for each of DappyPCTalk2a, DappyPCTalk2b, DappyPCTalk2c that closes that variable, sets the

Global ("DappyPCTalk2","GLOBAL",0), and sets the global timer so that the initial script block can rin. Fot this to work, you need to set it up on the joined dialog file rather than the banter.

Link to comment

Thank you for your indepth reply. I ended choosing the 1st way of randomizing.


Alas I have another query on the use of these. I initially tested playing with: (Code is ommitting everything else)

IF ~RandomNum(2,1)
IF ~RandomNum(2,2)


IF ~RandomNum(3,1)
IF ~RandomNum(3,2)
IF ~RandomNum(3,3)


Would this be the correct way to go about it, while testing all banter came in a different order (which is was I wanted -so that fixed the problem). This would make it very easy for me to write later on.

For example if I want to push DappyMinsc Banters to 20. I would be required to use RandomNum(20,#) and then adjust accordingly and thus giving out a 1/20 chance of a banter being used.

However when used with DappyPC banter say if I have 30 or some, and use "RandomNum(30,#)" for this set. Giving it a 1/30 of each each banter being played.

Lending itself to be more probable that a DappyMinsc banter will be used.

(I apologise if this is a trivial thing)


Just another thing, say if I only want a specific banter to only up after would putting:

IF ~RandomNum(3,3)

Ensure this? (As DappyPCTalk2 is the conversation I want to have had occured before it.) I then can ommit the RandomNum(#,#) from the third convo.



I have been doing some testing by using RandomNum(5,#) which would give all 5 banters (at the moment) equal chance of being played, so that if I were to set the conditions above I need not worry about banters (that I would want to occur in sequence) out of sequence.


Although a problem I can see with using a collective RandomNum (While I can use a find and replace tool to) apply the "RandomNum(#," I would then have to add "#)"

Link to comment

If you want a follow up "chain" of conversation, like


Banter27 = PC says he likes cheese, Dappy thinks that is wierd.

Banter27a = Dappy Asks What Kind Of Cheese, PC Responds with Stilton

Banter27b = Dappy Gives PC The Cheese, Hilarity Ensues


then the safest way to do that is as you described - make the condition of 27a be clearing 27, and 27b be clearing 27a. Clump them together and drop off the RandomNum(#,#) call on 27a and 27b - you are then taking advantage of what you firts saw as a challenge, and you have a random start to a 3 talk sequence.



I think, though, while it works, you are still not quite getting how it works, which will mess you up if you are expecting things to be randomized a certain way and it doesn't work out that way -


within a dialog's replies, the engine sees a Randomnum() call and chooses, holding that value throughout the dialog


SAY ~Heya. How's life?~

+ ~RandomNum(2,1)~ + ~Lousy. This plays 50% of the time.~ + nextstate_Lousy

+ ~RandomNum(2,2)~ + ~Great. This plays 50% of the time.~ + nextstate_Great


the above will always have only one response, 50/50 choice. The engine, in effect, rolled the dice once at the start, and finds either 1 or 2, and applies it to all the reply states it finds in the particular set of replies.


In fact, you can stack them, so


SAY ~Heya. How's life?~

+ ~RandomNum(2,1)~ + ~Lousy. This set plays 50% of the time.~ + nextstate_Lousy

+ ~RandomNum(2,1)~ + ~Silly. This set plays 50% of the time.~ + nextstate_Silly

+ ~RandomNum(2,1)~ + ~Extravagant. This set plays 50% of the time.~ + nextstate_Extravagant

+ ~RandomNum(2,2)~ + ~Great. This set plays 50% of the time.~ + nextstate_Great

+ ~RandomNum(2,2)~ + ~Wonderful. This set plays 50% of the time.~ + nextstate_Wonderful

+ ~RandomNum(2,2)~ + ~Wild. This set plays 50% of the time.~ + nextstate_Wild


so you can have one state that 50% of the time has one complete set, and 50% on the time has the other set.


This logic breaks down when you are triggering a talk. A simple test of this is to trigger a talk with RandomNum(2,1). There is a 50% chance that the talk will play. The number is rolled, and that is that - the engine looks for the next talk. so, a sample sequence:


ENGINE> Hmmm. A Talk. RandomNum(2,1). Rolling dice - I rolled a 2. Nope. Go on to the next one.

ENGINE> Hmmm. A Talk. RandomNum(2,2). Rolling dice - I rolled a 1. Nope. Go on to the next one.

ENGINE> Hmmm. A Talk. RandomNum(3,1). Rolling dice - I rolled a 1. Yep. Trigger this talk.


You would assume that the engine holds that value for the search, but in my testing I have had this occur - it looks like it does not hold random numbers for evaluation in all of the potential banters. You could test it, though, to make sure.


Basically, short story, if you use RandomNum(3,1) RandomNum(3,2) RandomNum(3,3) in three successive banters, you are not ensuring that one of the three plays. In fact, you are saying the same thing in all three - 33.333333333333333etc.% chance of this talk playing, and 66.66666etc. % of it being skipped.


There are two ways to get higher likelyhoods of a banter playing using this particular set of ideas. One is to change the likelyhood by changing the "one-in-a-set-of-numbers" value

RandomNum(2,1) = 50%

RandomNum(3,1) = 30%

RandomNum(4,1) = 25%

RandomNum(5,1) = 20%



Or, second, weight the RandomNum() response by using OR(2). Example:

OR(2) RandomNum(3,1) RandomNum(3,2) = 60%

OR(3) RandomNum(4,1) RandomNum(4,2) RandomNum(4,3) = 75%

OR(4) RandomNum(5,1) RandomNum(5,2) RandomNum(5,3) RandomNum(5,4) = 80%



I have never tried it or tested it, but you probably can use the ! to reverse things, so instead of writing the OR statement like this


OR(2) RandomNum(3,1) RandomNum(3,2) = 60%


you could probably do it like this


!RandomNum(3,1) = 60%


but again, I have never tried actually using RandomNum in a NOT statement.


On the relative weighting between a Dappy/Minsc banter, well - the only way I can think of to make it work in the engine and still have a thing play within a game (remember, a 1 in 5 chance still has a likelyhood of that banter never ever showing up in an entire game... theengine could be unlucky and never roll a 1) I think the most obvious way is the most wasteful - just put more than one set of the Dappy/minsc banters into the stack. Make them identical except for the initial statename, and you will close out the other versions whenever one of them plays. But at that level of detail, I don't think the cost in terms of time and troubleshooting and adding surplus dialog blocks to the mod is worth it. And I speak from experience - I'm the idiot who writes things like


/* NPC Initiated Flirts : Random Heavy Flirts */
IF ~Global("c-aranheavyflirt","GLOBAL",1)~ THEN BEGIN a1350
 SAY ~[ARAN] (He settles his belt with one thumb)~
 IF ~RandomNum(36,36)~ THEN GOTO a1351
 IF ~RandomNum(36,35)~ THEN GOTO a1352
 IF ~RandomNum(36,34)~ THEN GOTO a1353
 IF ~RandomNum(36,33)~ THEN GOTO a1354
 IF ~RandomNum(36,32)~ THEN GOTO a1355
 IF ~RandomNum(36,31)~ THEN GOTO a1356
 IF ~RandomNum(36,30)~ THEN GOTO a1357
 IF ~RandomNum(36,29)~ THEN GOTO a1358
 IF ~RandomNum(36,28)~ THEN GOTO a1359
 IF ~RandomNum(36,27)~ THEN GOTO a1360
 IF ~RandomNum(36,26)~ THEN GOTO a1361
 IF ~RandomNum(36,25)~ THEN GOTO a1362
 IF ~RandomNum(36,24)~ THEN GOTO a1362
 IF ~RandomNum(36,23)~ THEN GOTO a1362
 IF ~RandomNum(36,22)~ THEN GOTO a1363
 IF ~RandomNum(36,21)~ THEN GOTO a1364
 IF ~RandomNum(36,20)~ THEN GOTO a1365
 IF ~RandomNum(36,19)~ THEN GOTO a1366
 IF ~RandomNum(36,18)~ THEN GOTO a1367
 IF ~RandomNum(36,17)~ THEN GOTO a1368
 IF ~RandomNum(36,16)~ THEN GOTO a1369
 IF ~RandomNum(36,15)~ THEN GOTO a1373
 IF ~RandomNum(36,14)~ THEN GOTO a1378
 IF ~RandomNum(36,13)~ THEN GOTO a1403
 IF ~RandomNum(36,12)~ THEN GOTO a1404
 IF ~RandomNum(36,11)~ THEN GOTO a1405
 IF ~RandomNum(36,10)~ THEN GOTO a1406
 IF ~RandomNum(36,9)~ THEN GOTO a1407
 IF ~RandomNum(36,8)~ THEN GOTO a1408
 IF ~RandomNum(36,7)~ THEN GOTO a1409
 IF ~RandomNum(36,6)~ THEN GOTO a1410
 IF ~RandomNum(36,5)~ THEN GOTO a1411
 IF ~RandomNum(36,4)~ THEN GOTO a1412
 IF ~RandomNum(36,3)~ THEN GOTO a1413
 IF ~RandomNum(36,2)~ THEN GOTO a1414
 IF ~RandomNum(36,1)~ THEN GOTO a1415
 IF ~AreaType(OUTDOOR) TimeOfDay(NIGHT) Global("c-aranstarflirt","GLOBAL",0)~ THEN GOTO a1423 /* c-aranstarflirt */
 IF ~AreaType(CITY) Global("c-arancityflirt","GLOBAL",0)~ THEN DO ~SetGlobal("c-arancityflirt","GLOBAL",1)~ GOTO a1424 /* c-arancityflirt */
 IF ~AreaType(DUNGEON) Global("c-arandungeonflirt","GLOBAL",0)~ THEN DO ~SetGlobal("c-arandungeonflirt","GLOBAL",1)~ GOTO a1427  /* c-arandungeonflirt */
 /* Jewelry Light Flirt Additions:  */
 IF ~RandomNum(2,1) HasItemEquiped("AMUL04",Player1) Global("c-ahjewel2","LOCALS",0)~ THEN GOTO a1444 /* Studded Necklace with Zios Gems */
 IF ~RandomNum(2,1) HasItemEquiped("AMUL05",Player1) Global("c-ahjewel2","LOCALS",0)~ THEN GOTO a1445 /* Bluestone Necklace */
 IF ~RandomNum(2,1) HasItemEquiped("AMUL06",Player1) Global("c-ahjewel2","LOCALS",0)~ THEN GOTO a1446 /* Agni Mani Necklace */
 IF ~RandomNum(2,1) HasItemEquiped("AMUL07",Player1) Global("c-ahjewel2","LOCALS",0)~ THEN GOTO a1447 /*  Rainbow Obsidian Necklace */
 IF ~RandomNum(2,1) HasItemEquiped("AMUL08",Player1) Global("c-ahjewel2","LOCALS",0)~ THEN GOTO a1448 /* Tiger Cowrie Shell Necklace */
 IF ~RandomNum(2,1) HasItemEquiped("AMUL09",Player1) Global("c-ahjewel3","LOCALS",0)~ THEN GOTO a1449 /*  Silver Necklace */
 IF ~RandomNum(2,1) HasItemEquiped("AMUL10",Player1) Global("c-ahjewel3","LOCALS",0)~ THEN GOTO a1450 /*  Gold Necklace */
 IF ~RandomNum(2,1) HasItemEquiped("AMUL11",Player1) Global("c-ahjewel3","LOCALS",0)~ THEN GOTO a1451 /* Pearl Necklace */
 IF ~RandomNum(2,1) HasItemEquiped("AMUL16",Player1) Global("c-ahjewel1","LOCALS",0)~ THEN GOTO a1452 /* Amulet of Metaspell Influence (+1 2nd level spell) */
 IF ~RandomNum(2,1) HasItemEquiped("AMUL18",Player1) Global("c-ahjewel1","LOCALS",0)~ THEN GOTO a1453 /* Wolfsbane Charm +2 vs Lycanthropes */
 IF ~RandomNum(2,1) HasItemEquiped("AMUL22",Player1) Global("c-ahjewel1","LOCALS",0)~ THEN GOTO a1454 /* Periapt of Proof Against Poison */
 IF ~RandomNum(2,1) HasItemEquiped("AMUL01",Player1) Global("c-ahjewel1","LOCALS",0)~ THEN GOTO a1443 /*  Necklace of Missiles */
 IF ~RandomNum(2,1) HasItemEquiped("AMUL12",Player1) Global("c-ahjewel1","LOCALS",0)~ THEN GOTO a1442 /*  Laeral's Tear Necklace (3000 gp) */
 IF ~RandomNum(2,1) HasItemEquiped("RE_valm",Player1) Global("c-aljewelv","LOCALS",0)~ THEN GOTO a1499 /* Romantic Encounters: Valygar item */
 IF ~HPPercentLT(Player1,40)~ THEN GOTO a1396




Parsing this block out (it evaluates bottom to top), the engine


A. Looks at the player and sees if she has less than 40% of her hitpoints left. IF she does, then the engine skips to state a1396 ELSE it continues evaluation with B.


B. Rolls a 1 or a 2.

IF 1 THEN check for jewelry equipped on the player and the comment never having been made before - if one set of two conditions evaluates true, follow that pathway ELSE GOTO C.



C. IF AreaType(DUNGEON) Global("c-arandungeonflirt","GLOBAL",0) THEN follow this pathway, ELSE GOTO D.


D. IF AreaType(CITY) Global("c-arancityflirt","GLOBAL",0) THEN follow this pathway, ELSE GOTO E.


E. IF AreaType(OUTDOOR) TimeOfDay(NIGHT) Global("c-aranstarflirt","GLOBAL",0) THEN follow this pathway, ELSE GOTO F.


and finally,

F. 100% chance that 1 of 32 different flirts will play if nothing upstream of this set of RandomNum(#,#) has drawn off the dialog before now.

Link to comment

O_O I'm going to have to process all of this! (You are right about me not understanding it quite correctly)

I was doing some servere thinking and I noticed that perhaps your "HeavyFlirt" code you posted at the bottom of last post could be the most similar thing as to what I may be looking for. I assume that the HeavyFlirt has a bank of statements of which your NPC could use when complementing on the necklace.


Assuming if it was to be the case, would something like this be a possible solution?

Could this be used to set up a bank of propositions I want Dappy to question my PC or other NPCs with? Or have I still not got my head around "RandomNum()" yet :p

IF ~RandomNum(2,1) InParty("ZZDappy") InParty("Player1") !ActuallyInCombat Global("a1234","GLOBAL",0)~ THEN GOTO a1234
IF ~RandomNum(2,1) InParty("ZZDappy") InParty("Player1") !ActuallyInCombat Global("a1235","GLOBAL",0)~ THEN GOTO a1235
IF ~RandomNum(2,1) InParty("ZZDappy") InParty("Player1") !ActuallyInCombat Global("a1236","GLOBAL",0)~ THEN GOTO a1236
IF ~RandomNum(2,1) InParty("ZZDappy") InParty("Player1") !ActuallyInCombat Global("a1237","GLOBAL",0)~ THEN GOTO a1237

THEN ZZDappyB a1234
	~Blah Blah Random Statement?~
DO ~SetGlobal("a1234","GLOBAL",1)~
+RandomNum(2,1)+ ~Response 1~ + a1234-1
+RandomNum(2,1)+ ~Response 2~ + a1234-2
+RandomNum(2,2)+ ~Response 3~ + a1234-3
+RandomNum(2,2)+ ~Response 4~ + a1234-4

(Haven't tested, gone to sleep at 5am)

Regardless if it is incorrect or not you have been a huge help none-the-less and I am very gratefull for you spending the time to help :)

PS: I was really impressed by that heavyflirt code.

Link to comment

It appears to not work, I will attempt to make a working code to test.




With the early morning testing I did after seeing the above didn't work I found this suits what I wanted to occur perfectly:

CHAIN IF ~RandomNum(3,1) InParty("ZZDappy") InParty(Player1) !ActuallyInCombat() Global("DappyPCTalk1","GLOBAL",0)~ THEN ZZDappyB DappyPC1
~Cheese?~ DO ~SetGlobal ("DappyPCTalk1","GLOBAL",1)~ END
++ ~Yes~ + DappyPC1-1
++ ~No~ + DappyPC1-2
CHAIN IF ~~ THEN ZZDappyB DappyPC1-1 ~Yes~ EXIT
CHAIN IF ~~ THEN ZZDappyB DappyPC1-2 ~No~ EXIT
CHAIN IF ~RandomNum(3,2) InParty("ZZDappy") InParty(Player1) !ActuallyInCombat() Global("DappyPCTalk2","GLOBAL",0)~ THEN ZZDappyB DappyPC2
~Women?~ DO ~SetGlobal ("DappyPCTalk2","GLOBAL",1)~ END
++ ~Yes~ + DappyPC2-1
++ ~No~ + DappyPC2-2
CHAIN IF ~~ THEN ZZDappyB DappyPC2-1 ~Yes~ EXIT
CHAIN IF ~~ THEN ZZDappyB DappyPC2-2 ~No~ EXIT
CHAIN IF ~RandomNum(3,3) InParty("ZZDappy") InParty(Player1) !ActuallyInCombat() Global("DappyPCTalk3","GLOBAL",0)~ THEN ZZDappyB DappyPC3
~Cats??~ DO ~SetGlobal ("DappyPCTalk3","GLOBAL",1)~ END
++ ~Yes~ + DappyPC3-1
++ ~No~ + DappyPC3-2
CHAIN IF ~~ THEN ZZDappyB DappyPC3-1 ~Yes~ EXIT
CHAIN IF ~~ THEN ZZDappyB DappyPC3-2 ~No~ EXIT

After testing, Dappy showed variation in the order of which he had asked "Cheese?" "women" or "cats" which is what I wanted to acheive in this thread.


Although I really wanted to achieve a stack and then work off that, but I just couldn't manage it.

IF ~RandomNum(3,1) InParty("ZZDappy") InParty(Player1) !ActuallyInCombat() Global("DappyPCTalk1","GLOBAL",0)~ THEN ZZDappyB DappyPC1
IF ~RandomNum(3,2) InParty("ZZDappy") InParty(Player1) !ActuallyInCombat() Global("DappyPCTalk2","GLOBAL",0)~ THEN ZZDappyB DappyPC2
IF ~RandomNum(3,3) InParty("ZZDappy") InParty(Player1) !ActuallyInCombat() Global("DappyPCTalk3","GLOBAL",0)~ THEN ZZDappyB DappyPC3

(The Stack)

Again, Thank you <333

Link to comment


This topic is now archived and is closed to further replies.

  • Create New...