Conversation
Conversation or dialogue (simplified to dialog in American English) is the system built into Mount&Blade that allows two persons to communicate with one another in a menu-based interface. Each individual entry in the module_dialogs.py file is referred to officially as a dialogue or a dialog, but for purposes of clarity in this article it is referred to here as a dialogue entry, particularly to avoid confusion with pop-up message boxes.
Contents |
[edit] Implementation
The conversation system operates in a simple, discrete way.
- The player chooses to Talk to an agent in a scene, or the game selects an appropriate topic if conversation was started automatically.
- In a town or other scene, or when encountering someone normally on the world map, the topic is "start".
- After winning a battle started by an ally on the world map, conversation is started automatically with the topic "party_relieved".
- After winning a battle and a hero was present in the enemy's prisoners, conversation is started automatically with the (former) prisoner with the topic "prisoner_liberated".
- After winning a battle against an enemy hero, conversation is started automatically with the enemy hero with the topic "enemy_defeated".
- If the conversation was started automatically by a trigger, the topic is "event_triggered".
- If talking to a person in the party, the topic is "member_chat".
- This topic becomes the new "dialogue state".
- The game processes the list of dialogue tuples and selects the list of topics where the actor matches the agent's troop ID and the topic matches the initial dialogue state.
- For each of the topics that match, the condition block is run.
- If any condition function in the block fails, further code stops executing in the precondition block.
- If all of the code in the block succeeds, or there is no code in the block, the dialogue topic is selected.
- If the actor ID is modified with the "plyr" flag, then it searches for additional topics to select. Otherwise, only the first matching dialog topic is selected.
- Once the selection of topics is available, the selected dialogue text is displayed.
- If the selection is for the "plyr", then a number of dialogue choices are presented in a menu for the player to choose from; if only one choice is available, it is spoken automatically by the player.
- After a dialogue choice has been displayed, or after the player chooses an option from the menu, the consequences block is run.
- The current dialogue state changes to the topic specified as the next topic.
- If the next topic is instead assigned to the special value "close_window", then conversation ends.
- Otherwise, conversation goes back to step 3 above, matching the new dialogue state rather than the starting dialogue state.
[edit] Default variables
Several global variables and registers are automatically assigned to you every time you start conversation in the default Mount&Blade module, due to the existence of a preprocessing dialogue entry at the very top of the standard module_dialogs.py.
Variable | Game Version | Contains |
---|---|---|
$g_talk_troop | All | The troop of the actor. |
$g_talk_agent | All | The agent of the actor. |
$g_talk_troop_faction | All | The faction the actor belongs to. |
$g_talk_troop_relation | All | The relationship level between the player and the actor, ranging from -100 to 100. |
$g_talk_troop_effective_relation | All | A modified relationship level which doubles the player's positive relation at Persuasion level 10, or doubles the player's negative relation at Persuasion level 0. |
$g_talk_troop_faction_relation | All | The relationship level between the player faction and the actor's faction. |
$g_talk_troop_party | All | The party the actor belongs to, if applicable. |
$g_current_hours | All | The current hour of the game since the beginning, counting upward from 0 to infinity. |
$g_talk_troop_last_talk_time | All | The last hour of the game since the beginning that the actor was spoken to by the player. |
$g_time_since_last_talk | The difference between the two values, in hours, for convenience. | |
$g_talk_troop_met | Set to 1 if the player has spoken to this hero (lord) before. | |
$g_enemy_strength | The calculated party strength score of all of the enemies in a world map encounter. | |
$g_ally_strength | The calculated party strength score of all of the allies. | |
$g_strength_ratio | The calculated ratio of ally strength to enemy strength (higher is better for the player), with 100 meaning equal footing. |
TODO Complete list
[edit] Format
Dialogue is specified in module_dialogs, with each tuple containing a list of values that specify how the conversation is to be conducted.
The essentials of the dialogue format are below:
#[actor, "topic", [(condition_function,0,1), (call_script, "script_condition_block")], "Text spoken by person.", "next_topic", [(call_script, "script_consequences_block")], #[actor|plyr, "next_topic", [(condition_function,0,1), (call_script, "script_condition_block")], "Text spoken by player.", "next_topic", [(call_script, "script_consequences_block")],
[edit] Actor
In a dialogue tuple, the actor or partner is the troop who is being conversed with by the player. The special troop "anyone" means that the dialogue topic is available regardless of the specific actor in the conversation. Instead of a troop, a party template can also be used in combination with the flag "party_tpl" to allow any encountered party to talk to the player, with either a hero or the strongest troop conducting the conversation on the party's behalf.
The actor is always assigned to the person the player is talking to. Even if the player or a third party is talking, the actor ID always remains the same.
[edit] Flags
For lines of dialogue that are intended to be spoken by the player, the flag "plyr" is added to the actor name. These will be presented in a menu if there is more than one choice, or automatically spoken if there is only one choice available. In game, if there is only one choice, the camera will automatically pan to the player as he or she speaks. Otherwise, the camera will remain focused on the NPC.
For lines of dialogue that are to be spoken by a third party present in the scene, the flag "other(troop)" is added to the actor name, with troop assigned to the troop ID of the other person in the conversation. Note that in encounters in the world map, a new scene is automatically produced and there will be no way to introduce a third partyverify; this function works only for scenes such as towns and interiors.
If you want the conversation to proceed automatically, append the flag "auto_proceed" to the actor name. Note that this will skip the dialogue text completely and is usually intended only if you want to use one dialogue topic to accomplish a certain bit of code (e.g., playing a sound effect) before immediately displaying the next, or if you want two or more distinct actors to redirect the player to the same dialogue topic when conversation is started.
If you want a dialogue topic to iterate through a number of in-game objects or even a set number, use the repeat_for_x flags as follows. See below in special notes for more information.
[edit] Topic
The topic specifies in which dialogue state the dialogue entry should appear. This can be any string desired (no dynamic text allowed) as a custom dialogue state which is linked to from another topic, or one of the initial dialogue states below.
[edit] Initial dialogue state
When the player enters a conversation, the game automatically assigns a dialogue state according to circumstance:
Situation | Automatic | Speaker | Dialogue State |
---|---|---|---|
Player activates agent in scene with "Talk" function | No | Troop | "start" |
Player clicks on and catches party on world map | No | Party template or troop (hero) | "start" |
Player clicks "Talk" for a party member in Party menu | No | Troop | "member_chat" |
Player caught by aggressive party on world map | Yes | Party template or troop (hero) | "start" |
Player joins an ally on world map and wins the battle | Yes | Party template or troop (hero) | "party_relieved" |
Player defeats an enemy party led by a hero | Yes | Troop | "enemy_defeated" |
Player defeats an enemy party containing a captive hero | Yes | Troop | "prisoner_liberated" |
Scripted trigger forces a conversation with a hero or agent | Yes | Troop | "event_triggered" |
Where:
- "situation" is what caused the conversation to be entered,
- "automatic" is whether this happens automatically without the player choosing to start the conversation,
- "speaker" is whether you should use a troop ID or party template ID as the actor in the dialogue entry, and
- "dialogue state" is the initial dialogue state assigned when entering conversation.
For automatic dialogue, all possible events will activate conversations sequentially; each time a conversation ends by being directed to the "close_window" topic, the next available conversation will start automatically. For instance, after defeating an enemy party that contained allied prisoners, you will speak to the leader of the enemy party; after exiting the conversation with the leader of the enemy party, another conversation will start automatically with the first prisoner, and so forth for each prisoner remaining.
The order given above is the order in which the automatic conversations are processed. The allied lords and party leader will be spoken to before the enemy leader is spoken to, and the enemy leader will be spoken to before the allied prisoners will be spoken to.
[edit] Condition Block
The condition block serves two purposes.
The first purpose is to limit the appearance of dialogue topics according to conditions. It contains a list of condition operations which, if any return false, the block will cancel immediately and the dialogue choice will not be available. A condition operation marked with this_or_next will not cancel the dialogue entry unless all of the marked
The second purpose is to run scripts or to have script code embedded directly. This code will evaluate immediately before the dialogue is displayed and is used to load registers and string registers with information prior to the dialogue string being displayed. It is also used to accomplish other tasks, like rewarding the player, modifying the NPC's relation to the player, and so forth.
[edit] Preprocessing
The condition block can also be used simply to "preprocess" — to run code instead of showing dialogue, in advance of displaying other dialogue — which is especially useful to assign global variables related to the next dialogue entries in the file. Creating a preprocessing dialogue entry is simply a matter of writing your code in the condition block, and placing a condition function guaranteed to return false at the end (rather than at the beginning). This prevents that particular dialogue entry from being selected, even though all of the code in the block has already been executed. As soon as the game engine reaches the dialogue entry matching the corresponding topic, it will run the code automatically, halting only when it reaches the guaranteed-false condition. The dialogue text and next topic parameters are completely ignored in this case, although it is usually a good idea to set the dialogue text to a warning string that it is a processing block and should never be displayed, and to set the next topic to "close_window" to prevent being locked in conversation in case the condition function is somehow accidentally omitted or deleted.
Using preprocessing can accomplish a number of useful tasks, such as choosing a random number and then checking that number in the topics below to determine which of the topics will display, or simply pre-loading variables, registers, and string registers in a clearly-defined place in the module_dialogs.py file rather than having to insert this script code into the first of several possible choices.
[edit] Dialogue text
The dialogue text is the string that will be displayed to the player verbatim. It can make use of registers, string registers, conditional strings, and gender strings to be modified as necessary.
One of the simplest ways to have a dynamic dialogue text is to set the dialogue text directly to a string register. That way, you have full script control over what is shown to the player by assigning whatever text you'd like to display into the string register using the condition block, in advance of actually displaying it.
[edit] Next topic
The next topic is what the current dialogue state will change to when this dialogue entry is chosen, whether by clicking in the menu in the case of the player, or automatically after the player clicks the mouse when in conversation with an NPC.
Alternatively, it can be set to "close_window" to end the conversation and return the player back to main gameplay.
[edit] Consequences block
The consequences block is a set of script instructions executed after the dialogue entry is chosen. Unlike the condition block, any condition function in the consequences block will not halt execution of the code, except in the case of cancelling a try_begin block.verify
[edit] Special notes
[edit] Playing speech
To add voice effects to the player, you must combine two dialogue entries sequentially, one to present the menu choice which will play the sound effect when clicked, and one to present the dialogue text and give the player the "Click to continue" prompt.
As an example, the following dialogue entries allow the player to say the bandit line to an NPC, which for some reason has been Cockneyed; this is strictly for show and it doesn't do anything else.
[anyone|plyr, "start", [], "[Yell a bandit curse at the NPC.]", "bandit_yell", [(play_sound, "snd_encounter_looters")]], [anyone|plyr, "bandit_yell", [], "Out for a lickle stroll, are we, wot wot!", "start", []], #go back to start
This method also works for NPCs, if you append the auto_proceed flag to the first dialogue entry.
[edit] Iteration
The following iteration flags are available for repeating one set of conditions and consequences over a number of objects. Examples include awarding fiefs to lords (this may use both repeat_for_parties and repeat_for_troops) and iterating through a troop's inventory (with repeat_for_100).
- repeat_for_factions
- repeat_for_parties
- repeat_for_troops
- repeat_for_100
- repeat_for_1000
In almost all cases, the iteration flags are used to give the player a list of options. Using this for other NPCs is left as an exercise for the reader. With use of the (store_repeat_object) operation, you can access the current object and do processing to exclude them from being shown, or store their names with string operations for display. store_repeat_object is also used in the consequence block, often to store the object actually selected into a global for processing at the next dialog. Note that it is also important to include an option to back out of the list of options - the repeat_for_x flags will not exclude other dialog entries from being considered.
In the following example, the player can try to procure a loan from the guildmaster. Since we don't really want increments of 1 for the player to deal with, repeat_for_100 is used and the value of store_repeat_object is multiplied by a factor of 250. The conditions are having the same faction as the town, and a high enough relation with the town (since the guild master's talk relation is equivalent to the town relation).
[anyone|plyr,"mayor_talk", [(eq, "$players_kingdom", "$g_encountered_party_relation"),], "I want to take out a loan.", "mayor_loan",[(store_mul, "$temp", "$g_talk_troop_relation", 150),]], [anyone,"mayor_loan", [(lt, "$temp", 600)], "Your standing with this town and the merchant's guild is insufficient. As such, we are unable to provide you with a loan.", "mayor_pretalk",[]], [anyone,"mayor_loan", [(ge, "$temp", 600),(call_script, "script_game_get_money_text", "$temp"),], "Of course. According to our accounts, you can loan up to {s1} from the guild.", "mayor_loan_amount",[]],
Now the global $temp holds the upper limit which the player can receive. This is used to limit the selection in the actual iteration. Note that the same string registers are safely used in the iteration, as they will be overwritten after each pass but not before they are displayed as an option. Note also the first dialog option, which backs out of this conversation. Accordingly, the amount check below that excludes "0 denars" as an option.
[anyone|plyr, "mayor_loan_amount", [], "I'd rather not.", "mayor_pretalk", []], [anyone|plyr|repeat_for_100, "mayor_loan_amount", [(store_repeat_object, ":loan"), (val_mul, ":loan", 250),(is_between, ":loan", 250, "$temp"), (call_script, "script_game_get_money_text", ":loan"),], "{s1}.", "mayor_loan_taken", [(store_repeat_object, ":loan"),(store_mul, "$temp2", ":loan", 250),]],
Now the global $temp2 holds the amount the player actually selected. This is passed to the following block for processing.
[anyone,"mayor_loan_taken",[(call_script, "script_game_get_money_text", "$temp2"),], "Of course. You have taken a loan of {s1}. We will be accumulating interest every week.", "mayor_pretalk",[(call_script, "script_change_debt_to_troop", "$g_talk_troop", "$temp2"),(troop_add_gold, "trp_player", "$temp2")]],