So for one of my mods which adds new Lords, I want to give them all their own dialogue lines instead of just using the default ones so I can more fully develop their character and make them unique from other characters in the game. For starters I am trying to edit a specific character's first meeting line which is displayed the first time the player meets them. These are located among other lines, in SandBox/ModuleData/voice_strings.xml, with tags for when the conversation should take place. Here is an example for a first meeting:
This one is for a first meeting ("FirstMeetingTag") where the character is curt and has an upper voice register ("VoiceGroupPersonaCurtUpperTag").
The tags are defined in TaleWorlds.CampaignSystem.Conversation.Tags, for example here is the FirstMeetingTag:
As far as I can tell, tags are only referenced in one other location in ConversationTagList within TaleWorlds.Core. Here each tag has a string field such as:
The class also contains a string List which gets populated with all the strings by the class' one method, Contains(string tagId). The method returns a bool of whether or not the string tagId exists in the class/the list:
I'm not exactly sure what calls this method, or the individual ConversationTags isApplicableTo method - as I can't find a reference to any of these anywhere else. I attempted to add my own ConversationTag for a specific character from my mod, as well as patch the ConversationTagList Contains method so that it will return a positive for "ValdyrTag" since I can't add a field directly to the class:
I also added a FirstMeeting line using ValdyrTag to voice_strings.xml:
However, when meeting the character for the fist time in-game, one of the Vanilla strings is still displayed. I've gone as far as to use Harmony patches to patch out the character's name from being eligible for any of the other tags he was being assigned down to the point where it would display the DefaultTag first meeting message which is not supposed to appear unless the character has nothing else eligible:
Things I've determined:
Am I missing something here? Am I going about this the wrong way, or is there an easier way to do this? Any help would be greatly appreciated.
XML:
<string id="str_context_line.first_meeting_cu" text="{=B05S6Fw4}Who are you, and what do you want?">
<tags>
<tag tag_name="FirstMeetingTag" />
<tag tag_name="VoiceGroupPersonaCurtUpperTag" />
</tags>
This one is for a first meeting ("FirstMeetingTag") where the character is curt and has an upper voice register ("VoiceGroupPersonaCurtUpperTag").
The tags are defined in TaleWorlds.CampaignSystem.Conversation.Tags, for example here is the FirstMeetingTag:
C#:
using System;
namespace TaleWorlds.CampaignSystem.Conversation.Tags
{
public class FirstMeetingTag : ConversationTag
{
public override string StringId
{
get
{
return "FirstMeetingTag";
}
}
public override bool IsApplicableTo(CharacterObject character)
{
return Campaign.Current.ConversationManager.CurrentConversationIsFirst;
}
}
}
As far as I can tell, tags are only referenced in one other location in ConversationTagList within TaleWorlds.Core. Here each tag has a string field such as:
public const string VoiceGroupPersonaCurtUpperTag = "VoiceGroupPersonaCurtUpperTag";
The class also contains a string List which gets populated with all the strings by the class' one method, Contains(string tagId). The method returns a bool of whether or not the string tagId exists in the class/the list:
C#:
internal static bool Contains(string tagId)
{
if (ConversationTagList._tagIdList == null)
{
ConversationTagList._tagIdList = new List<string>();
foreach (FieldInfo fieldInfo in typeof(ConversationTagList).GetFields(24))
{
if (fieldInfo.IsLiteral && !fieldInfo.IsInitOnly && fieldInfo.FieldType == typeof(string))
{
ConversationTagList._tagIdList.Add((string)fieldInfo.GetValue(null));
}
}
}
return ConversationTagList._tagIdList.Contains(tagId);
}
I'm not exactly sure what calls this method, or the individual ConversationTags isApplicableTo method - as I can't find a reference to any of these anywhere else. I attempted to add my own ConversationTag for a specific character from my mod, as well as patch the ConversationTagList Contains method so that it will return a positive for "ValdyrTag" since I can't add a field directly to the class:
C#:
using System.Collections.Generic;
using System.Reflection;
using TaleWorlds.CampaignSystem;
using TaleWorlds.Core;
using TaleWorlds.MountAndBlade;
using TaleWorlds.CampaignSystem.Conversation.Tags;
using HarmonyLib;
namespace JotnarOfSturgiaSonsOfFenrir
{
public class Main : MBSubModuleBase
{
protected override void OnSubModuleLoad()
{
base.OnSubModuleLoad();
new Harmony("ThisIsSovereign.Bannerlord.JotnarOfSturgiaSonsOfFenrir").PatchAll();
InformationManager.DisplayMessage(new InformationMessage("Jotnar of Sturgia - Sons of Fenrir 1.0.0 Loaded"));
}
}
public class ValdyrTag : ConversationTag
{
public override string StringId
{
get
{
return "ValdyrTag";
}
}
public override bool IsApplicableTo(CharacterObject character)
{
return character.GetName().ToString() == "Valdyr";
}
}
[HarmonyPatch(typeof(ConversationTagList), "Contains")]
public class JotnarConversationTagList
{
private static List<string> _tagIdList;
public static bool Prefix(ref bool __result, string tagId)
{
if(tagId == "ValdyrTag")
{
__result = true;
return false;
}
if (_tagIdList == null)
{
_tagIdList = new List<string>();
foreach (FieldInfo fieldInfo in typeof(ConversationTagList).GetFields())
{
if (fieldInfo.IsLiteral && !fieldInfo.IsInitOnly && fieldInfo.FieldType == typeof(string))
{
_tagIdList.Add((string)fieldInfo.GetValue(null));
}
}
}
__result = _tagIdList.Contains(tagId);
return false;
}
}
}
I also added a FirstMeeting line using ValdyrTag to voice_strings.xml:
XML:
<string id="str_context_line.first_meeting_valdyr" text="{=*}Valdyr Test">
<tags>
<tag tag_name="FirstMeetingTag" />
<tag tag_name="ValdyrTag" weight="+5" />
</tags>
</string>
However, when meeting the character for the fist time in-game, one of the Vanilla strings is still displayed. I've gone as far as to use Harmony patches to patch out the character's name from being eligible for any of the other tags he was being assigned down to the point where it would display the DefaultTag first meeting message which is not supposed to appear unless the character has nothing else eligible:
XML:
<string id="str_context_line.first_meeting" text="{=!}Yes? Do I know you? (Default - should not appear)">
<tags>
<tag tag_name="DefaultTag" />
<tag tag_name="FirstMeetingTag" />
</tags>
</string>
Things I've determined:
- The string id isn't making the message ineligible to be displayed, I replaced the default string with the name "str_context_line.first_meeting_valdyr" and the game still chooses that for DefaultTag occurrences.
- I tried ValdyrTag with and without weight="+5", which from what I've seen in other lines in the file is used to give certain tags more weight to prioritize a certain string over another when a certain character has multiple matching strings.
- I attempted to ad DefaultTag to the string I added, and placed it above the original DefaultTag/FirstMeetingTag message, and it was still not selected.
Am I missing something here? Am I going about this the wrong way, or is there an easier way to do this? Any help would be greatly appreciated.