Resolved Conversation system is not using weight of tags correctly

Users who are viewing this thread

Hello,

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 :
zovElTk.jpg
Which is from this XML string :
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 :
RX4X93F.jpg
Which is from this XML string :
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:
Hello, sorry for the late reply. We have fixed a considerable amount of the problems we have encountered so far and improved the game performance with the multiple patches we have released. Please make sure that your game, your drivers, and OS are up to date and the game has necessary permissions over your security software. Please let us know if the problem persists after completing these steps and verifying the integrity of the game files through Steam.
 
Hello,
It seems this issue has been fixed, since the LoadFromXML method has been modified to the one I suggested in the original post.
 
Back
Top Bottom