Author Topic: The Road to Banter - by Blue, the Immortal Bard  (Read 18029 times)

Offline Ghreyfain

  • Moderator
  • Planewalker
  • *****
  • Posts: 4705
  • Gender: Male
    • Pocket Plane Group
The Road to Banter - by Blue, the Immortal Bard
« on: April 01, 2004, 03:48:19 AM »
The Road to Banter - by Blue, the Immortal Bard

Revised for CHAIN improvements 5-5-03. - JC

Seeing as how I'm the unofficial Queen of Banter, I suppose it's only fitting that I come up with a tutorial for it.
And seeing as how the Queen of Banter can be lazy sometimes, it's only fitting that I have to come and clean up her mess months later. -JC

This is pretty much only covering standard NPC banters. LoveTalks are a little different, but not much, but I still haven't messed with them any.

Also, take note that this walkthrough only guides you in the process of making a banter in WeiDU.

Step One: Get Thee to a Text Editor!

Notepad will work, but you won't be happy. You want something that'll take note of line numbers for you, or you'll be in a whole new world of aggravation during compilation. I personally use PFE, but offhand I can't think of any others. I'll edit this to give you some addresses to go to when I stop being lazy and go find them.

Context http://www.fixedsys.com/context/ is another option.

But, anyway, it doesn't matter much what you use, just some things are better than others.

Step Two: Filenames - They're Not Just Filler Anymore

It's important you name your files the right thing. All the dialog you write fits into four categories: Introductory dialog (Usually just the character's name, but cannot be over seven letters), Parting dialog (XXXXXXP), interject/quest dialog (XXXXXXJ), and banter (BXXXXXX). So, for instance, if you have an NPC named Blue, you would name her introductory dialog BLUE, her parting dialog BLUEP, interjects BLUEJ, and banter would be BBLUE.

So, the first thing you do when you open up your editor to start writing banter is you tell WeiDU you are beginning a banter file; it should read like this: BEGIN ~BXXXXXX~ When you save that file, make sure you save it with a .D suffix after the name.

So far, in the hypothetical NPC Blue, we have a text file named BBLUE.D, and, in the file itself, we have BEGIN ~BBLUE~ written. Now we get into the actual writing...

Step Three: Say What You Mean

It's highly suggested you have your banter written out before you start coding it; that way, you always have what you need to refer to on hand. Now, for example, this is what we want NPC-Blue to say in one of her banters:

BLUE: Agh! What am I doing in a computer game? I should never have eaten that leftover pizza.
JAN: Have a turnip! It'll make you feel better. The same thing happened to my third-cousin-twice-removed-once-by-marriage Philroy waaaaay back just two weeks ago -
BLUE: And to make things worse, I'm stuck in a banter with HIM! Give me a sentence that's less than four words long, please?
JAN: What are you talking about?
BLUE: Argh! That's FIVE words, FIVE!

Obviously, I don't recommend you follow my writing style. But, anyway, let's just say that's what we want to go into the game. Most banters can be created entirely within the syntax of the CHAIN function. Watch how this starts. I'll put Blue's first line into the CHAIN, then stop and explain what's going on.

CHAIN
IF ~InParty("Jan")
See("Jan")
!StateCheck("Jan",STATE_SLEEPING)
Global("JanBlueTalk","LOCALS",0)~ THEN BBLUE BlueDistressed
~Agh! What am I doing in a computer game? I should never have eaten that leftover pizza.~
DO ~SetGlobal("JanBlueTalk","LOCALS",1)~
.
.
.


Comments: Before NPCs talk to each other, they first run through a quick list of checks to make sure the other NPC is in the party, that they can see each other, and that he/she isn't asleep/dead. All that changes is you would replace the "Jan" with the death variable of whatever NPC you're wanting to banter with. (In most cases, this will be the NPC's name. It's very easy to check in NI or InfExp, though.)

As for the Global check there, that's just something to check if a banter has happened before - if it hasn't, the banter starts, and you see that we set the global to 1 so it won't occur again. After that, we EXTERN to the next place the banter is supposed to go, which is Jan's banter file - which we know has to be BJAN, and then we tell it which part of BJAN the banter links to.
Note: "BlueDistressed" is a WeiDU label, used internally as a name for this banter. If for some reason we had a dialog EXTERN to this CHAIN, we would EXTERN BBLUE BlueDistressed. These labels ARE case-sensitive.
JanBlueTalk is a variable that we check to ensure that the dialogue can only happen once: it must be zero when we start (any variable that has never been set is automatically zero) and then we tell the game to set it to 1, so it will never occur again.

Now, using CHAIN we can finish this dialogue.

CHAIN
IF ~InParty("Jan")
See("Jan")
!StateCheck("Jan",STATE_SLEEPING)
Global("JanBlueTalk","LOCALS",0)~ THEN BBLUE BlueDistressed
~Agh! What am I doing in a computer game? I should never have eaten that leftover pizza.~
DO ~SetGlobal("JanBlueTalk","LOCALS",1)~
== BJAN
~Have a turnip! It'll make you feel better. The same thing happened to my third-cousin-twice-removed-once-by-marriage Philroy waaaaay back just two weeks ago -~
== BBLUE
~And to make things worse, I'm stuck in a banter with HIM! Give me a sentence that's less than four words long, please?~
== BJAN
~What are you talking about?~
== BBLUE
~Argh! That's FIVE words, FIVE!~
EXIT

Comments: Note that in the first version of this tutorial we couldn't start or end the banter inside the CHAIN, but now we can. Note the EXIT command at the end of the banter CHAIN - this is what ends the conversation as a whole.
If for some reason you decide you need to end your CHAIN with a long-form state, you can still use the old END command, such as END BBLUE EndBanter, to go to a new state called EndBanter.

So here's how the whole thing should look; my comments are included behind // marks.

BEGIN ~BBLUE~ // We are in a banter file, specifically, the banter file for the NPC Blue.
CHAIN // Start a CHAIN dialogue
IF ~InParty("Jan") // Is the NPC Blue wants to talk to in the party?
See("Jan") // Can she SEE the NPC?
!StateCheck("Jan",STATE_SLEEPING) // Is the NPC conscious?
Global("JanBlueTalk","LOCALS",0)~ THEN BBLUE BlueDistressed // Have we had this talk before?
SAY ~Agh! What am I doing in a computer game? I should never have eaten that leftover pizza.~ // Blue should say this.
DO ~SetGlobal("JanBlueTalk","LOCALS",1)~ // Okay, make sure this talk won't happen again.

== BJAN // Identify that we're switching to Jan's line
~Have a turnip! It'll make you feel better. The same thing happened to my third-cousin-twice-removed-once-by-marriage Philroy waaaaay back just two weeks ago -~ // Jan babbles.
== BBLUE // Okay, now we want Blue to say something, so we go back to her banter file.
~And to make things worse, I'm stuck in a banter with HIM! Give me a sentence that's less than four words long, please?~ // And that's what she says.
== BJAN // Alright, back to Jan.
~What are you talking about?~ // That's what he says.
== BBLUE // Back to Blue...
~Argh! That's FIVE words, FIVE!~ // Blue gives her ending line.
EXIT // Alright, this conversation is totally done.




And thus ends a simple banter.

Step Four: A Question of Style

When you only have a handful of conversations to put in, this doesn't matter as much. When you have A LOT, though, things can turn into a jumbled mess that's impossible to sort out. So, there's need for a little organization. My personal method is having all the beginning blocks first, all the ending blocks next, all the CHAINs next, and then all the APPENDs listed out last. Note that CHAIN implicitly ends a dialogue you have started with BEGIN, as in BEGIN BBLUE. The only real rule of thumb is to keep stuff that YOUR NPC specifically deals with (The beginning and ending blocks he/she says) separated from the stuff that other NPCs will say (APPENDs, which I'll explain momentarily.) CHAINs, while seeming like they should go into the APPEND block of whomever starts the CHAIN, all go into your NPC's banter file, but AFTER all the beginning/ending IF/THEN blocks. That's how it works.

Step Five: APPENDs - Look Who's Talking Now!

The next thing you should sink your teeth into are APPENDs. APPEND lets you add new states to an existing dialogue file, so that means they are what allows other allows other NPCs to begin or end a discussion. Really, they're not very difficult at all, as long as you remember a few rules that apply to them. So, for instance, let's say this is the dialog we want to put in:

HAER'DALIS: Blue, while I must certainly admit that you have a lovely bottom, my butt is far superior. Look at those cheeks flex!
BLUE: Pbbbt. Whatever! I have GOT to get out of here...

Very short, so we will not use CHAIN. Haer'Dalis starts off this discussion. BHAERDA (his banter file) already exists in the game, so in order to add the start of a new banter, we need the APPEND commend. We use it to add the new block to start this discussion, and then use the END keyword to tell WeiDU to stop APPENDing new lines to BHAERDA.

APPEND BHAERDA

IF ~InParty("Blue")
See("Blue")
!StateCheck("Blue",STATE_SLEEPING)
Global("BlueButt","LOCALS",0)~ THEN BEGIN ButtTalk
SAY ~Blue, while I must certainly admit that you have a lovely bottom, my butt is far superior. Look at those cheeks flex!~
IF ~~ THEN DO ~SetGlobal("BlueButt","LOCALS",1)~ EXTERN MoreButt
END
END

Comments: This time, all those checks apply to Blue, since this is Haer'Dalis wanting to talk to her. See how everything follows the same format, though? What makes APPENDs different is, naturally, the APPEND command, and the extra END at the bottom - we first have the standard END to signify the block is finished, but then we have another one so we stop APPENDing.

Now, go back to Blue's banter file, and do her ending block.

IF ~~ THEN BEGIN MoreButt
SAY ~Pbbbt. Whatever! I have GOT to get out of here...~
IF ~~ THEN EXIT
END

Comments: Again, note that the label names match. A word of caution as well - if you intend to put the blocks that are in Blue's banter file AFTER an APPEND block, you'll have to do this:

APPEND BBLUE
IF ~~ THEN BEGIN MoreButt
SAY ~Pbbbt. Whatever! I have GOT to get out of here...~
IF ~~ THEN EXIT
END
END

Which is fine if you want to keep things in order, but you can save yourself a step by just putting ALL of the new NPC's beginning and ending blocks at the top of the file, and the APPENDs at the bottom. It puts things out of order, but if your label names match where they're supposed to, there will be no trouble.

You can have more APPEND blocks as well, as long as you separate them properly and keep them all apart from the custom NPC's banter file. For instance, let's say Viconia, if she's in the party, just HAS to have the final word in the butt talk (incidentally, this shows that multiple NPCs can have a conversation, even in CHAINs - just specify the proper banter file, and check for their ability to participate in the dialog.)

So, instead of the dialog ending on Blue's comment, we EXTERN to Viconia's banter file instead:

IF ~~ THEN BEGIN MoreButt
SAY ~Pbbbt. Whatever! I have GOT to get out of here...~
IF ~IsValidForPartyDialog("Viconia")~ THEN EXTERN BVICONI VicButt
IF ~!IsValidForPartyDialog("Viconia")~ THEN EXIT
END

Comments: Note that we make sure if Viconia's NOT in the party/able to comment, that the dialog just ceases like it's supposed to. Remember, filenames will not always exactly be the NPC's name - they can only be seven characters long. But note that we have to use a new label name to go to the new file. Here's what we have to do next to let Viconia get in her two cents:

APPEND BVICONI

IF ~~ THEN BEGIN VicButt
SAY ~Idiotic surface-dwellers. I am Drow, so clearly it is MY butt that reigns supreme.~
IF ~~ THEN EXIT
END
END


So, with everything we've done so far, this is how the banter file looks, without my commentary marks:

BEGIN ~BBLUE~

IF ~~ THEN BEGIN MoreButt
SAY ~Pbbbt. Whatever! I have GOT to get out of here...~
IF ~IsValidForPartyDialog("Viconia")~ THEN EXTERN BVICONI VicButt
IF ~!IsValidForPartyDialog("Viconia")~ THEN EXIT
END

CHAIN
IF ~InParty("Jan")
See("Jan")
!StateCheck("Jan",STATE_SLEEPING)
Global("JanBlueTalk","LOCALS",0)~ THEN BBLUE BlueDistressed
~Agh! What am I doing in a computer game? I should never have eaten that leftover pizza.~
DO ~SetGlobal("JanBlueTalk","LOCALS",1)~
== BJAN
~Have a turnip! It'll make you feel better. The same thing happened to my third-cousin-twice-removed-once-by-marriage Philroy waaaaay back just two weeks ago -~
== BBLUE
~And to make things worse, I'm stuck in a banter with HIM! Give me a sentence that's less than four words long, please?~
== BJAN
~What are you talking about?~
== BBLUE
~Argh! That's FIVE words, FIVE!~
EXIT


APPEND BHAERDA

IF ~InParty("Blue")
See("Blue")
!StateCheck("Blue",STATE_SLEEPING)
Global("BlueButt","LOCALS",0)~ THEN BEGIN ButtTalk
SAY ~Blue, while I must certainly admit that you have a lovely bottom, my butt is far superior. Look at those cheeks flex!~
IF ~~ THEN DO ~SetGlobal("BlueButt","LOCALS",1)~ EXTERN BBLUE MoreButt
END
END

APPEND BVICONI

IF ~~ THEN BEGIN VicButt
SAY ~Idiotic surface-dwellers. I am Drow, so clearly it is MY butt that reigns supreme.~
IF ~~ THEN EXIT
END
END

Comments: Note that you can put the CHAINs, APPENDs, and the beginning and ending IF/THEN blocks together without a problem.

Step Six: Higher Learning

You can set other conditional checks. For instance, if you want an NPC to comment on trees when the party is in a forest, all you have to do is specify AreaType(FOREST) in with the rest of the condition checks. Check the conditional checks that are mentioned in existing banter, and you'll get what you need. This walkthrough should be able to take you through most types of simple banter, however. For a more complex example to look at, just read on - it inclues multiple NPC participation, area checks, and all that lovely stuff.

Sample banter to be coded:
(If Aerie, Yoshimo, and Minsc are in the party, and the party's in a city...)
BLUE: Pssst! Aerie! Did you know that Yoshimo is working for Irenicus?
AERIE: (gasp) Say it isn't so?
BLUE: Well, played through the entire game with you three times, I'd hope you would have figured it out by now.
YOSHIMO: Hey! That was supposed to be a secret!
BLUE: Dude, it SAYS 'Bounty Hunter' on your character sheet, you think we couldn't figure it out?
YOSHIMO: Minsc is a ranger, but there's not much that is 'ranger' about him! And explain Cernd being a Shapeshifter and Valygar a Stalker, why don't you, hmm?
MINSC: Minsc is best type of ranger! Boo is my animal companion! I just go berserk and kill things more than I stay in a forest.
BLUE: And you throw nuts at squirrels, Minsc, how much can you possibly follow the ranger code, anyway?
MINSC: Be nice, little girl!
BLUE: I mean, really, it's not nice to throw nuts at things. Hey, Aerie, catch!
AERIE: Ow! Ow! Quit throwing things at me!
BLUE: See? Not nice at --- woah!
MINSC: Rarr! No one flings nuts at Minsc's witch! No one! RAAAAAGH!
YOSHIMO: Not bad, Minsc. I didn't know you could toss a bard from the Promenade to Trademeet.
MINSC: Is no trouble. Right, Boo? Boo..?
MINSC: Noooo! She took Boo! To Trademeet, post-haste!
YOSHIMO: Heh heh. Silly ranger. Yes, you're a good little Boo, aren't you?

Remember, Blue is beginning this conversation, so she owns the CHAIN and therefore the first line. It works out to look like this:

CHAIN
IF ~InParty("Aerie")
InParty("Minsc")
InParty"Yoshimo")
See("Aerie")
See("Minsc")
See("Yoshimo")
!StateCheck("Aerie",STATE_SLEEPING)
!StateCheck("Minsc",STATE_SLEEPING)
!StateCheck("Yoshimo",STATE_SLEEPING)
AreaType(CITY)
Global("BardTossing","LOCALS",0)~ THEN BBLUE BlueGetsTossed
~Pssst! Aerie! Did you know that Yoshimo is working for Irenicus?~
DO ~SetGlobal("BardTossing","LOCALS",1)~
== BAERIE
~(gasp) Say it isn't so?~
== BBLUE
~Well, played through the entire game with you three times, I'd hope you would have figured it out by now.~
== BYOSHIM
~Hey! That was supposed to be a secret!~
== BBLUE
~Dude, it SAYS 'Bounty Hunter' on your character sheet, you think we couldn't connect the dots?~
== BYOSHIM
~So? Minsc is a ranger, but there's not much that is 'ranger' about him! And explain Cernd being a Shapeshifter and Valygar a Stalker, why don't you, hmm?~
== BMINSC
~Minsc is best type of ranger! Boo is my animal companion! I just go berserk and kill things more than I see a forest.~
== BBLUE
~Yeah, and you throw nuts at squirrels, Minsc, how much can you possibly follow the ranger code?~
== BMINSC
~Be nice, little girl!~
== BBLUE
~I mean, really, it's not nice to throw nuts at things. Hey, Aerie, catch!~
== BAERIE
~Ow! Ow! Quit throwing things at me!~
== BBLUE
~See? Not nice at --- woah!~
== BMINSC
~Rarr! No one flings nuts at Minsc's witch! No one! RAAAAAGH!~
== BYOSHIM
~Not bad, Minsc. I didn't know you could toss a bard from the Promenade to Trademeet.~
== BMINSC
~Is no trouble. Right, Boo? Boo..?~
=
~Noooo! She took Boo! To Trademeet, post-haste!~
== BYOSHIM
~Heh heh. Silly ranger. Yes, you're a good little Boo, aren't you?~
EXIT

Final Note: Be sure to read the WeiDU tutorials on CHAIN, Multi-Say, INTERJECT, and all that good stuff. This is just meant to get you banter-writing in the least painful way possible, but I don't stop and explain WHY stuff does what it does. For this, it's enough to tell you that it just works.

The WeiDU ReadMe: http://www.cs.berkeley.edu/~weimer/bgate/WeiDU/README-WeiDU.html
Earn Money Sleeping.