Hello,
While digging in the Conversation System source code, I noticed this strange part in TaleWorlds.Core.GameTextManager :
The XmlAttribute "weight" is not used at all. Instead, all tags get a default weight of 1 regardless of what was written in the XML.
In addition, when the TaleWorlds.Core.GameTextManager.ChoiceTag constructor is called, it checks if weight < 0 to determine if the tag is "Reversed".
Since weight is forced to 1, a tag can never be "Reversed" :
These ChoiceTags are then used by the function "FindMatchingScore" to find the most adequate text to show in conversations :
I believe this causes the matching score obtained to be incorrect in many cases, and a lot of conversation lines to never appear, or to appear when they should not.
For example, talking to Iyalas originally leads to this comment :
Which is from this XML string :
I tried this very quick fix with Harmony and it seems to work, but I'm a beginner in C# and testing conversations is not easy:
Anyway, with this "fix", we obtain this comment from Iyalas :
Which is from this XML string :
This is not a game breaking bug but it might prevent people from seeing the many conversations variations that were written, and it will be a problem for modders who want to add their own lines.
While digging in the Conversation System source code, I noticed this strange part in TaleWorlds.Core.GameTextManager :
C#:
private void LoadFromXML(XmlDocument doc)
{
...
XmlAttributeCollection attributes = childNodes[i].Attributes;
if (attributes != null)
{
int weight = 1;
XmlAttribute xmlAttribute = attributes["weight"]; // Not used ??
GameTextManager.ChoiceTag item = new GameTextManager.ChoiceTag(attributes["tag_name"].Value, weight);
list.Add(item);
}
...
}
The XmlAttribute "weight" is not used at all. Instead, all tags get a default weight of 1 regardless of what was written in the XML.
In addition, when the TaleWorlds.Core.GameTextManager.ChoiceTag constructor is called, it checks if weight < 0 to determine if the tag is "Reversed".
Since weight is forced to 1, a tag can never be "Reversed" :
C#:
public ChoiceTag(string tagName, int weight)
{
this = default(GameTextManager.ChoiceTag);
this.TagName = tagName;
this.Weight = (uint)Math.Abs(weight);
this.IsTagReversed = (weight < 0); // Always false since weight is forced to 1
}
These ChoiceTags are then used by the function "FindMatchingScore" to find the most adequate text to show in conversations :
C#:
private float FindMatchingScore(CharacterObject character, GameTextManager.ChoiceTag[] choiceTags)
{
float num = 0f;
foreach (GameTextManager.ChoiceTag choiceTag in choiceTags)
{
if (choiceTag.TagName != "DefaultTag")
{
if (this.IsTagApplicable(choiceTag.TagName, character) == choiceTag.IsTagReversed) // Tags are never "Reversed" since weight is forced to 1
{
return -2.14748365E+09f;
}
uint weight = choiceTag.Weight;
num += weight;
}
}
return num;
}
I believe this causes the matching score obtained to be incorrect in many cases, and a lot of conversation lines to never appear, or to appear when they should not.
For example, talking to Iyalas originally leads to this comment :
XML:
<string id="str_comment_intro.famous_softspoken" text="{=EIThhVgG}I know your name. It's good to meet.">
<tags>
<tag tag_name="PlayerIsFamousTag" />
<tag tag_name="PersonaSoftspokenTag" weight="1" />
</tags>
</string>
I tried this very quick fix with Harmony and it seems to work, but I'm a beginner in C# and testing conversations is not easy:
C#:
using System;
using System.Collections.Generic;
using HarmonyLib;
using TaleWorlds.Core;
using TaleWorlds.Engine;
using TaleWorlds.Library;
using System.Xml;
using TaleWorlds.Localization;
using TaleWorlds.MountAndBlade;
namespace TestTagFix
{
public class TestTagFixSubModule : MBSubModuleBase
{
private static readonly Type patchType = typeof(TestTagFixSubModule);
protected override void OnBeforeInitialModuleScreenSetAsRoot()
{
base.OnBeforeInitialModuleScreenSetAsRoot();
var harmony = new Harmony("test.tag.fix");
try
{
var originalLoadFromXMLEvents = AccessTools.Method(AccessTools.TypeByName("GameTextManager"), "LoadFromXML");
var prefixLoadFromXMLEvents = new HarmonyMethod(methodType: patchType, methodName: nameof(LoadFromXMLPrefix));
harmony.Patch(
original: originalLoadFromXMLEvents,
prefix: prefixLoadFromXMLEvents
);
InformationManager.DisplayMessage(new InformationMessage("GameTextManager.LoadFromXML() replaced successfully", Colors.Green));
}
catch (Exception ex)
{
InformationManager.DisplayMessage(new InformationMessage("Error while replacing GameTextManager.LoadFromXML() : " + ex.Message, Colors.Red));
MBDebug.ConsolePrint(ex.StackTrace, Debug.DebugColor.White);
}
}
public static Boolean LoadFromXMLPrefix(Object __instance, XmlDocument doc)
{
Debug.Print("loading strings.xml:", 0, Debug.DebugColor.White, 17592186044416UL);
if (doc.ChildNodes.Count <= 1)
{
throw new TWXmlLoadException("Incorrect XML document format.");
}
if (doc.ChildNodes[1].Name != "base")
{
throw new TWXmlLoadException("Incorrect XML document format.");
}
if (doc.ChildNodes[1].ChildNodes[0].Name != "strings")
{
throw new TWXmlLoadException("Incorrect XML document format.");
}
XmlNode xmlNode = null;
if (doc.ChildNodes[1].ChildNodes[0].Name == "strings")
{
xmlNode = doc.ChildNodes[1].ChildNodes[0].ChildNodes[0];
}
while (xmlNode != null)
{
if (xmlNode.Name == "string" && xmlNode.NodeType != XmlNodeType.Comment)
{
if (xmlNode.Attributes == null)
{
throw new TWXmlLoadException("Node attributes are null.");
}
string[] array = xmlNode.Attributes["id"].Value.Split(new char[]
{
'.'
});
string id = array[0];
GameText gameText = ((GameTextManager)__instance).AddGameText(id);
string variationId = "";
if (array.Length > 1)
{
variationId = array[1];
}
TextObject text;
if (xmlNode.Attributes["sound_path"] != null)
{
text = new TextObject(xmlNode.Attributes["text"].Value + "[sp:" + xmlNode.Attributes["sound_path"].Value + "]", null);
}
else
{
text = new TextObject(xmlNode.Attributes["text"].Value, null);
}
List<GameTextManager.ChoiceTag> list = new List<GameTextManager.ChoiceTag>();
foreach (object obj in xmlNode.ChildNodes)
{
XmlNode xmlNode2 = (XmlNode)obj;
if (xmlNode2.Name == "tags")
{
XmlNodeList childNodes = xmlNode2.ChildNodes;
for (int i = 0; i < childNodes.Count; i++)
{
XmlAttributeCollection attributes = childNodes[i].Attributes;
if (attributes != null)
{
// Fix for correct weight attribution
int weight = 1;
if (attributes["weight"] != null)
{
int.TryParse(attributes["weight"].Value, out weight);
}
GameTextManager.ChoiceTag item = new GameTextManager.ChoiceTag(attributes["tag_name"].Value, weight);
list.Add(item);
}
}
}
}
gameText.AddVariationWithId(variationId, text, list);
}
xmlNode = xmlNode.NextSibling;
}
return false;
}
}
}
Anyway, with this "fix", we obtain this comment from Iyalas :
XML:
<string id="str_comment_intro.female_famous_goodnatured" text="{=Wt4iJPcd}I've heard of you! It's very good to finally make your acquaintance.">
<tags>
<tag tag_name="PlayerIsFamousTag" weight="2" />
<tag tag_name="MercyTag" weight="1" />
<tag tag_name="AttackingTag" weight="-1" />
</tags>
</string>
This is not a game breaking bug but it might prevent people from seeing the many conversations variations that were written, and it will be a problem for modders who want to add their own lines.
Last edited: