public CharacterSelectionVM(CharacterSelectionParams p)
            : base()
        {
            ModuleLogger.Log("begin character selection vm construction");
            this._params       = p;
            this.allCharacters = new SortedDictionary <string, SortedDictionary <string, List <CharacterInfo> > >();
            foreach (var c1 in p.characters)
            {
                if (!this.allCharacters.ContainsKey(c1.culture))
                {
                    this.allCharacters.Add(c1.culture, new SortedDictionary <string, List <CharacterInfo> >());
                }
                var cultureDict = this.allCharacters[c1.culture];
                if (!cultureDict.ContainsKey(c1.defaultGroup))
                {
                    cultureDict.Add(c1.defaultGroup, new List <CharacterInfo>());
                }
                cultureDict[c1.defaultGroup].Add(c1);
            }
            var c = p.characters[p.selectedIndex];

            Cultures = new MBBindingList <NameVM>();
            foreach (var culture in this.allCharacters.Keys)
            {
                Cultures.Add(new NameVM {
                    Name = culture
                });
            }
            Groups = new MBBindingList <NameVM>();
            foreach (var group in this.allCharacters[c.culture].Keys)
            {
                Groups.Add(new NameVM {
                    Name = group
                });
            }
            Characters = new MBBindingList <CharacterVM>();
            foreach (var character in this.allCharacters[c.culture][c.defaultGroup])
            {
                Characters.Add(new CharacterVM(character));
            }
            SelectedCultureIndex   = Cultures.FindIndex(n => n.Name == c.culture);
            SelectedGroupIndex     = Groups.FindIndex(n => n.Name == c.defaultGroup);
            SelectedCharacterIndex = Characters.FindIndex(n => n.character == c);
            ModuleLogger.Log("end character selection vm construction");
        }
        private static void SortAnyParty(MBBindingList <PartyCharacterVM> toSort, PartyBase party,
                                         TroopRoster rosterToSort, PartySort sorter)
        {
            if (rosterToSort == null || rosterToSort.Count == 0 || toSort == null || toSort.IsEmpty())
            {
                return;
            }

            CharacterObject leaderOfParty = party?.LeaderHero?.CharacterObject;

            // Sort the list, this is done for the visual unit cards to be properly positioned after the sort
            // This is not yet persisted to the actual roster, that is done after this.
            toSort.StableSort(sorter);

            // Sanity check to ensure the leader is *always* at the top of the party.
            if (leaderOfParty != null)
            {
                var index = toSort.FindIndex((character) => character.Character.Equals(leaderOfParty));
                PartyCharacterVM leaderVm = toSort[index];
                toSort.RemoveAt(index);
                toSort.Insert(0, leaderVm);
            }

            // Here we manually clear the roster while ignoring the party leader.
            // Don't use `rosterToSort.Clear()` as that seems to cause the party leader to get unset permanently afterward, which stops upgrades from working.
            rosterToSort.RemoveIf((item) => item.Character != leaderOfParty);

            // Re-add the correctly sorted troops to the roster. We need to do it in this janky way due to the fact that we can't easily sort
            // the underlying roster array.
            foreach (PartyCharacterVM character in toSort)
            {
                if (character.Character != leaderOfParty)
                {
                    rosterToSort.AddToCounts(
                        character.Troop.Character, character.Troop.Number, false, character.Troop.WoundedNumber,
                        character.Troop.Xp);
                }
            }
        }