Skip to content

Modding Dialogs

Tutorials


Implementation example

private void OnSessionLaunched(CampaignGameStarter starter)
{
    AddDialogs(starter);
}


private void AddDialogs(CampaignGameStarter starter)
{

    starter.AddPlayerLine("tavernkeeper_book", "tavernkeeper_talk", "tavernkeeper_book_seller_location", "Have you seen any book vendor recently?", TavernKeeperOnCondition, () => { GiveGoldAction.ApplyBetweenCharacters(null, Hero.MainHero, -5, false); }, 100, (out TextObject explanation) =>
    {
        if (Hero.MainHero.Gold < 5)
        {
            explanation = new TextObject("Not enough gold...");
            return false;
        }
        else
        {
            explanation = new TextObject("5 {GOLD_ICON}");
            return true;
        }
    });

    starter.AddDialogLine("tavernkeeper_book_a", "tavernkeeper_book_seller_location", "tavernkeeper_books_thanks", "Yeah, saw {VENDOR.FIRSTNAME} recently. Look around the town.", () => { return IsBookVendorInTown(); }, null, 100, null);
    starter.AddDialogLine("tavernkeeper_book_a", "tavernkeeper_book_seller_location", "tavernkeeper_books_thanks", "I heard you can find what you are looking for in {SETTLEMENT}.", () => { return IsBookVendorNearby(); }, null, 100, null);
    starter.AddDialogLine("tavernkeeper_book_b", "tavernkeeper_book_seller_location", "tavernkeeper_books_thanks", "No, haven't heard lately.", null, null, 100, null);

    starter.AddPlayerLine("tavernkeeper_book", "tavernkeeper_books_thanks", "tavernkeeper_pretalk", "Thanks!", null, null, 100, null, null);

    starter.AddDialogLine("tavernkeeper_book", "tavernkeeper_pretalk", "tavernkeeper_talk", "Anything else?", null, null, 100, null);
}

// Condition example
private bool IsBookVendorInTown()
{
    foreach (Hero vendor in _vendorList)
    {
        if (vendor.CurrentSettlement == Settlement.CurrentSettlement)
        {
            StringHelpers.SetCharacterProperties("VENDOR", vendor.CharacterObject, null, false);
            return true;
        }
    }
    return false;
}

Diagram of the example above:


AddPlayerLine

What player (you) says

AddPlayerLine(id, inputToken, outputToken, text, condition, consequence, priority, clickableCondition, persuasionOption)

  • id - just a name for the entry, not used anywhere?
  • inputToken - entry name, where we can come from another dialog line
  • outputToken - where to go from here, to another dialog's line entry (inputToken). outputToken --> inputToken
  • text - what to show
  • condition - on what condition to show this dialog line (can be several, true - show, false - do not show, no condition -> show if no other conditions or other conditions false, hide if more conditions and one of it's true)
  • priority - default 100, if you want to overwrite default dialog, make it greater, like 110 and use same id/inputToken
  • clickableCondition - possible to disable with the explanation (example follows)
  • persuasionOption - something about the persuasions, consult with dnSpy 😁

Disable with Explanation

Disable the PlayerLine with the Explanation like this:

if (Hero.MainHero.Gold < 5)
{
    explanation = new TextObject("Not enough gold..."); //hint-explanation
    return false; // line is disabled
}
else
{
    explanation = new TextObject("5 {GOLD_ICON}"); // hint-explanation
    return true;  // line is enabled
}


AddDialogLine

What NPC says

AddDialogLine(id, inputToken, outputToken, text, condition, consequence, priority, clickableCondition)

Parameters same as in AddPlayerLine.

InputToken - "start" on first dialog entry point and it needs a start condition.

The last line should have outputToken "end" if you want the dialog to end:

starter.AddDialogLine("bookvendor_talk", "bookvendor_talk", "end", "Bye", null, null, 100, null, null);

If you will use outputToken "end" with AddPlayerLine - the game will hang


AddRepeatablePlayerLine

Dynamic method to add several options to choose from.
AddRepeatablePlayerLine
    (string id,
    string inputToken,
    string outputToken,
    string text,
    string continueListingRepeatedObjectsText,
    string continueListingOptionOutputToken,
    ConversationSentence.OnConditionDelegate conditionDelegate,
    ConversationSentence.OnConsequenceDelegate consequenceDelegate,
    int priority = 100,
    ConversationSentence.OnClickableConditionDelegate clickableConditionDelegate = null)

Example in which we can choose what fief to give to our companion:

campaignGameStarter.AddRepeatablePlayerLine("turn_companion_to_lord_has_fief_list", "player_has_fief_list", "player_selected_fief_to_grant", "{=3rHeoq6r}{SETTLEMENT_NAME}.", "{=sxc2D6NJ}I am thinking of a different location.", "check_player_has_fief_to_grant", new ConversationSentence.OnConditionDelegate(CompanionRolesCampaignBehavior.list_player_fief_on_condition), new ConversationSentence.OnConsequenceDelegate(this.list_player_fief_selected_on_consequence), 100, new ConversationSentence.OnClickableConditionDelegate(CompanionRolesCampaignBehavior.list_player_fief_clickable_condition));

private static bool list_player_fief_on_condition()
{
    Settlement settlement = ConversationSentence.CurrentProcessedRepeatObject as Settlement;
    if (settlement != null)
    {
        ConversationSentence.SelectedRepeatLine.SetTextVariable("SETTLEMENT_NAME", settlement.Name);
    }
    return true;
}


AddDialogLineWithVariation

Different answers based on character traits for example:

From LordConversationsCampaignBehaviour

starter.AddDialogLineWithVariation("player_turns_down_surrender", "party_encounter_lord_hostile_attacker_3", "close_window", null, null, 100, "", "", "", "", null).Variation(new object[]
{
    "{=QWzGkQrT}So we fight, then.[if:idle_angry][ib:warrior]",
    "DefaultTag",
    1
}).Variation(new object[]
{
    "{=6i7a1c4E}Very well. Death before dishonor![if:idle_angry][ib:warrior]",
    "PersonaEarnestTag",
    1,
    "ChivalrousTag",
    1
}).Variation(new object[]
{
    "{=FMJuPZlm}I'm not surrendering, so do what you must.[if:idle_angry][ib:warrior]",
    "ChivalrousTag",
    1
}).Variation(new object[]
{
    "{=ZG6kWWwW}I'm not yielding, so let's go to it, then.[if:idle_angry][ib:warrior]",
    "PersonaCurtTag",
    1
}).Variation(new object[]
{
    "{=SPYDUXvx}We meet on the battlefield, then.[if:idle_angry][ib:warrior]",
    "PersonaSoftspokenTag",
    1
}).Variation(new object[]
{
    "{=3WA4MLzx}One way or the other, you'll regret this. If I fall, my people will have their revenge.[if:idle_angry][ib:warrior]",
    "UncharitableTag",
    1
}).Variation(new object[]
{
    "{=yzzY4uXN}Very well. Expect no mercy.[if:idle_angry][ib:warrior]",
    "CruelTag",
    1,
    "FriendlyRelationshipTag",
    -1
});


AddDialogLineMultiAgent

No examples in the game code...

Just a guess that could be used in the dialog with several NPCs.


OneToOneConversationHero

Get the Hero you are talking to in the dialog

Hero.OneToOneConversationHero;


Notes

Requires to reload a game for a new dialog change to appear in-game.

  • [ ] does not show up in the AddDialogLine. Maybe because of the [if:idle_angry][ib:warrior] tags.


String helpers

In ConditionDelegate set variables:

StringHelpers.SetCharacterProperties("HERO", hero.CharacterObject, null, false);
MBTextManager.SetTextVariable("GOLD_ICON", "{=!}<img src=\"General\\Icons\\Coin@2x\" extend=\"8\">", false);
MBTextManager.SetTextVariable("GREETING", "Howdy", false);
MBTextManager.SetTextVariable("SETTLEMENT", settlement.EncyclopediaLinkWithName, false);

Use in the dialog:

starter.AddDialogLine("a", "b", "c", "{GOLD_ICON} {GREETING} {HERO.NAME} in {SETTLEMENT}! {GOLD_ICON}", OnConditionDelegateFunction, null, 100, null);

For random text, generate it in the ConditionDelegate which is run on each menu instance.


Face/Body NPC Tags

Face/Body control tags for NPCs in Dialogs

Control how NPCs behave during the dialog.


Start battle after the dialog

Not tested

You can try declaring the battle as an action and execute it after the conversation ended like this:

.Consequence(delegate { Campaign.Current.ConversationManager.ConversationEndOneShot += YourMethodCall; })