Tutorial: Creating New Dialogs with the Mod Tools

Users who are viewing this thread

Fujiwara

Sergeant at Arms
If the combat is the meat of M&B, dialogs are definitely the potatoes. Through them, the plot moves forward, quests are assigned, and rewards are given. Using the MBMS, new content is deceptively easy. This tutorial is design to walk modders through the process with a minimum of pain and suffering. I’ll be using my “The Chosen One” mini-mod as example material.

Step One: Look Who’s Talking

Speech doesn’t just appear randomly from the void; you need a speaker. So, we go to the module_troops.py and make one:

Go to the bottom of the file, where a space is reserved for new NPC’s.

["zithael","Zithael","Zithael",tf_female|tf_hero, scn_zendar_merchant|entry(3),0,fac_commoners,[itm_woolen_dress,itm_leather_boots],
def_attrib|level(30),wp(200),knows_common,0x000000000000000100000802d8000489]
Each troop listing contains the following fields:

  • 1) the ID, which is used to refer to the troop in the other files
    2) the actual name of the troop
    3) the plural version of #2, if any
    4) troop flags (tf…how clever), which are found in the header_troops.py file.
    5) the location of the troop, in this case the Zendar mercantile
    6) zero (this field is reserved)
    7) the troop’s faction
    :cool: the items the troop carries/wears
    9) the troop’s experience level
    10) the troop’s weapon proficiencies
    11) the troop’s skills and abilities
    12) the troop’s face

Step Two: Putting words in peoples’ mouths

So, having created someone to talk to, let’s give her something to say. Open module_dialogs.py, and go down to the following line near the end of the file:

[anyone,"start", [], "Hello. What can I do for you?", "free",[]],

Any new dialogs must be placed ABOVE this line in the file; otherwise, the character you just created will talk about him/herself and not what you want him/her (curse the lack of non-gender specific pronouns in English!) to say. Now, let’s begin:

[trp_zithael,"start",[],"Greetings, {playername}.","zithael_conversation_2",[]],

Dialogs are contained in brackets, with elements separated by commas, and begin with the speaker, in this case trp_zithael. Next is the active dialog-state, which is in our example, “start”. The module_dialogs,py file gives a list possible initial states, which I will gracefully reprint here:

If the dialog is started by meeting a party on the map, initially, the active dialog state is "party_encounter"
If the dialog is started by speaking to an NPC in a town, initially, the active dialog state is "start"
If the dialog is started by helping a party defeat another party, initially, the active dialog state is "party_relieved"
If the dialog is started by liberating a prisoner, initially, the active dialog state is "prisoner_liberated"
If the dialog is started by defeating a party led by a hero, initially, the active dialog state is "enemy_defeated"
If the dialog is started by a trigger, initially, the active dialog state is "event_triggered"

Since we meet Zithael in Zendar, we use the active state “start” to initiate the conversation.

Following the dialog-state is a conditional evaluation block. The conditionals are very powerful, and can evaluate a wide variety of objects. The conditionals take the form (operator,object,value). A long and complete list of operators is found in the header_operations.py file, but the most common are:

ge: greater than or equal to
eq: equal to
gt: greater than
le: less than or equal to
neq: not equal to
lt: less than

The object can be any pre-defined object, such as $character_class, or a user defined object created in a previous dialog. NB: DO NOT attempt to evaluate an object you have not yet defined in a previous dialog. This will crash M&B.

The value is what you what trying to evaluate for. If the conditional evaluates ‘true’, the dialog will be spoken; otherwise it will not.

Next is the actual dialog, surrounded by double quotes. You see in the example that the object {playername} is used in this dialog. This allows the speaker to say the player’s name. The braces are important and must be included.

Next is the ending dialog state, which can be either “close_window”, if the dialog is to end, or a user-defined state. Here we have “zithael_conversation_2”. This state will be picked up by the next defined dialog with “zithael_conversation_2” as its active state (the second field). This allows dialog continuity.

Finally there is a consequences block, which has the same format as the conditional block and uses the same operators. This block is generally used to assign values to object that can later be evaluated in the conditional block, but can do just about anything there is an operator for, which I’ll show below.

[trp_zithael,"zithael_conversation_2",[],"I seek the Chosen One. Dost thou think\
thou art whom which I seek?","zithael_conversation_3",[]],
[trp_zithael|plyr,"zithael_conversation_2",[],"Yes, I am the Chosen One.",
"zithael_conversation_3",[(assign,"$chosen_one",1)]],
[trp_zithael|plyr,"zithael_conversation_2",[],"No, you have the wrong person.",
"zithael_conversation_3",[(assign,"$chosen_one",0)]],

Here we have the second part of our conversation with Zithael. Note how the active dialog-state is picked up from the first dialog with the “zithael_conversation_2” state declaration. The second and third dialogs have the flag “|plyr” in the speaker field. This allows the player character to talk back. In game, all three dialogs will appear on the same screen with the two player dialogs shown as choices to be made, as all three have the same active dialog-state. The second and third dialogs also have a consequence block, based on the player’s choice in the conversation. Here, we assign the object $chosen_one a value of 1 or 0, based on a yes or no answer to Zithael’s question. The format of the object is important, as it is a string object and must be preceded by a $-symbol and enclosed in quotes.

[trp_zithael,"zithael_conversation_3",[(eq,"$character_class",0),(eq,"$chosen_one",1)],
"Thou hast great insight into thy character, {playername}.\
My search hath ended.","zithael_conversation_4",[(assign,"$chosen_one",2)]],
[trp_zithael,"zithael_conversation_3",[(eq,"$character_class",0),(eq,"$chosen_one",0)],
"Doubt clouds thy spirit, {playername}.\
Perhaps thou art not the Chosen One.","zithael_conversation_5",[]],
[trp_zithael,"zithael_conversation_3",[(neq,"$character_class",0),(eq,"$chosen_one",1)],
"Thou boasteth much, {playername}.\
However, thou art not the Chosen One.","zithael_conversation_5",[]],
[trp_zithael,"zithael_conversation_3",[(neq,"$character_class",0),(eq,"$chosen_one",0)],
"Thy humility is pleasing to me, {playername}.\
Thou art not the Chosen One.","zithael_conversation_5",[]],

Here is the next part of the conversation. Note the conditional operations. All conditionals must evaluate to true for the dialog to be valid. Therefore, in the first dialog, $character_class must be 0 and $chosen_one must be 1, corresponding to the squire class, assigned at character creation, and a yes response to Zithael’s question. If the conditional evaluates false, control passes to the next dialog, and so on until one evaluates true. In this manner, complicated branching dialogs can be built, and offhand comments to an NPC could have far-reaching effects later on in the game via the setting of control objects in previous conversations. In our example, the dialog of interest is the first one, as the rest simply lead to an end to the conversation. Note: the forward slash at the end of some of the lines indicates a line break inside a string in Python syntax. Use it to keep line lengths manageable.

[trp_zithael,"zithael_conversation_4",[(eq,"$chosen_one",2),(neq,"$chosen_one",3)],"I bestow upon thee this\
Sacred Gear, {playername} the Chosen. Use it well to bring\
peace and justice to Calradia.","zithael_conversation_5",[
(troop_add_item,"trp_player","itm_mearas"),
(troop_add_item, "trp_player","itm_arrows_of_hope"),
(troop_add_item, "trp_player","itm_erdricks_gauntlets"),
(troop_add_item, "trp_player","itm_boots_of_athena"),
(troop_add_item, "trp_player","itm_erdricks_armor"),
(troop_add_item, "trp_player","itm_helm_of_athena"),
(troop_add_item, "trp_player","itm_hammer_of_peace"),
(troop_add_item, "trp_player","itm_sword_of_justice"),
(troop_add_item, "trp_player","itm_shield_of_victory"),
(troop_add_item, "trp_player","itm_arc_of_serenity"),
(add_xp_as_reward, 500000),
(assign,"$chosen_one",3)]],

Here is the gist of the conversation. Using the consequences block, Zithael gives the player a load of absurdly powerful items and 500,000 xp, making the player into a demi-god of sorts (I only take damage from some dark knights and sniper crossbows). Study the header_operations.py file, as every operation is contained there and they can give you good ideas as to what is possible in the conditional and consequence blocks.

[trp_zithael,"zithael_conversation_5",[],
"Good-bye, {playername}.","close_window",[]],

This is the final dialog in this conversation. The end-state for all conversations should be “close_window”.

Remember, order in the dialogs file is important, especially if you’re building a large, convoluted set of conversations with a lot of control objects.

Happy yakking
 
Nice. I was working on such a tutorial myself. But yours is a lot clearer. :smile:

One addendum: it is not necessary to repeat "trp_zithael" for every line she speaks. In fact, you only need to write it once (for the "start" line). Thereafter, you can just call it "anyone", so long as the line IDs are linked, e.g. the following would work just as well:

[trp_zithael,"start",[],"Greetings, {playername}.","zithael_conversation_2",[]],
[anyone,"zithael_conversation_2",[],"I seek the Chosen One. Dost thou think\
thou art whom which I seek?","zithael_conversation_3",[]],
[anyone|plyr,"zithael_conversation_2",[],"Yes, I am the Chosen One.",
"zithael_conversation_3",[(assign,"$chosen_one",1)]],
[anyone|plyr,"zithael_conversation_2",[],"No, you have the wrong person.",
"zithael_conversation_3",[(assign,"$chosen_one",0)]],

This is particularly useful if you want to have more than one person have this conversation. For instance, suppose there are two figures, Zithael & Angelica, who can have this conversation with the player. Instead of repeating this whole thing for each, all you need to do is differentiate the kick-off line by trp_, and let the rest be carried by "anyone" lines.

[trp_zithael,"start",[],"Greetings, {playername}.","zithael_conversation_2",[]],
[trp_angelica,"start",[],"Greetings, {playername}.","zithael_conversation_2",[]],
[anyone,"zithael_conversation_2",[],"I seek the Chosen One. Dost thou think\
thou art whom which I seek?","zithael_conversation_3",[]],
[anyone|plyr,"zithael_conversation_2",[],"Yes, I am the Chosen One.",
"zithael_conversation_3",[(assign,"$chosen_one",1)]],
[anyone|plyr,"zithael_conversation_2",[],"No, you have the wrong person.",
"zithael_conversation_3",[(assign,"$chosen_one",0)]],

So Zithael & Angelica will go through the same conversation with you. It's a lot more economical. :grin:
 
Thanks! And yes, you have an excellent point, especially if you want to create a new set of generic dialogs spoken by a particular group of people, i.e. merchants or smiths. Spot on.
 
The only problem I have is that I can't find the documents
 
Oof, old thread. You'll need the module system and python I think. The forge should have all the info you need. Also, this tutorial is most likely outdated though it's the only one I've seen. Can't help you any further as I've never touched the module system.
 
Back
Top Bottom