Exemple #1
0
        public void Import(Hero character, bool updateAppearance)
        {
            string filename = GetSaveName(character);
            bool   isPlayer = Helper.CharacterHasTraitDeveloper(character);
            int?   year = null, season = null, day = null, hour = null;
            long?  remaining = null;
            int?   mercy = null, valor = null, honor = null, generosity = null, calculating = null;
            string culture = null;

            foreach (var keyValuePair in Helper.ReadFile(filename))
            {
                var key   = keyValuePair.Key;
                var value = keyValuePair.Value;

                // Parse
                try
                {
                    // Birthday
                    if (key.Equals("Year"))
                    {
                        year = Convert.ToInt32(value);
                    }
                    else if (key.Equals("Season"))
                    {
                        season = Convert.ToInt32(value);
                    }
                    else if (key.Equals("Day"))
                    {
                        day = Convert.ToInt32(value);
                    }
                    else if (key.Equals("Hour"))
                    {
                        hour = Convert.ToInt32(value);
                    }
                    else if (key.Equals("RemainingTicks"))
                    {
                        remaining = Convert.ToInt64(value);
                    }

                    // Culture
                    else if (key.Equals("Culture"))
                    {
                        culture = value;
                    }

                    // Traits
                    // if the character is the player (who has a trait developer), read the XP
                    else if (isPlayer)
                    {
                        if (key.Equals("MercyXP"))
                        {
                            mercy = Convert.ToInt32(value);
                        }
                        else if (key.Equals("ValorXP"))
                        {
                            valor = Convert.ToInt32(value);
                        }
                        else if (key.Equals("HonorXP"))
                        {
                            honor = Convert.ToInt32(value);
                        }
                        else if (key.Equals("GenerosityXP"))
                        {
                            generosity = Convert.ToInt32(value);
                        }
                        else if (key.Equals("CalculatingXP"))
                        {
                            calculating = Convert.ToInt32(value);
                        }
                    }
                    else
                    {
                        // The character doesn't not have a trait developer (non-player). Directly read the traits.
                        if (key.Equals("Mercy"))
                        {
                            mercy = Convert.ToInt32(value);
                        }
                        else if (key.Equals("Valor"))
                        {
                            valor = Convert.ToInt32(value);
                        }
                        else if (key.Equals("Honor"))
                        {
                            honor = Convert.ToInt32(value);
                        }
                        else if (key.Equals("Generosity"))
                        {
                            generosity = Convert.ToInt32(value);
                        }
                        else if (key.Equals("Calculating"))
                        {
                            calculating = Convert.ToInt32(value);
                        }
                    }
                }
                catch { Helper.ShowAndLog($"Failed to parse {key} for {character}"); return; }
            }

            bool readBirthdaySuccess = false;
            bool readCultureSuccess  = false;
            bool readTraitsSuccess   = false;

            // Read Birthday
            if (year.HasValue &&
                season.HasValue &&
                day.HasValue &&
                hour.HasValue &&
                remaining.HasValue)
            {
                var b = CampaignTime.Years(year.Value) +
                        CampaignTime.Seasons(season.Value) +
                        CampaignTime.Days(day.Value) +
                        CampaignTime.Hours(hour.Value);
                long newTicks = b.GetTicks();
                newTicks += remaining.Value;

                var newBirthDay = Helper.CreateCampaignTime(newTicks);
                character.BirthDay = newBirthDay;
                // Update character model
                if (updateAppearance)
                {
                    var dps = character.BodyProperties.DynamicProperties;
                    dps.Age = character.Age;

                    typeof(BodyProperties)
                    .GetField("_dynamicBodyProperties", BindingFlags.Instance | BindingFlags.NonPublic)
                    .SetValue(character.BodyProperties, dps);
                }

                readBirthdaySuccess = true;
            }

            // Read Culture
            if (culture != null)
            {
                CultureCode c;
                if (Enum.TryParse(culture, true, out c) == false)
                {
                    c = CultureCode.Invalid;
                }

                if (c != CultureCode.Invalid)
                {
                    var heroOfThisCulture = Hero.FindFirst((h) => h.Culture.GetCultureCode() == c);
                    if (heroOfThisCulture != null)
                    {
                        character.CharacterObject.Culture = heroOfThisCulture.CharacterObject.Culture;
                        if (character.Clan.Leader == character)
                        {
                            character.Clan.Culture = character.CharacterObject.Culture; // Clan culture changes too
                        }
                        readCultureSuccess = true;
                    }
                    else
                    {
                        //Helper.ShowAndLog($"{character.Name}'s culture not imported: Can't set to culture \"{c.ToString()}\"!");
                    }
                }
                else
                {
                    //Helper.ShowAndLog($"{character.Name}'s culture not imported: Invalid culture \"{culture}\"!");
                }
            }
            else
            {
                //Helper.ShowAndLog($"{character.Name}'s culture not imported: culture value is empty!");
            }

            // Read Traits
            if (mercy.HasValue &&
                valor.HasValue &&
                honor.HasValue &&
                generosity.HasValue &&
                calculating.HasValue)
            {
                if (isPlayer)
                {
                    var ptd              = Campaign.Current.PlayerTraitDeveloper;
                    var deltaMercy       = mercy.Value - ptd.GetPropertyValue(DefaultTraits.Mercy);
                    var deltaValor       = valor.Value - ptd.GetPropertyValue(DefaultTraits.Valor);
                    var deltaHonor       = honor.Value - ptd.GetPropertyValue(DefaultTraits.Honor);
                    var deltaGenerosity  = generosity.Value - ptd.GetPropertyValue(DefaultTraits.Generosity);
                    var deltaCalculating = calculating.Value - ptd.GetPropertyValue(DefaultTraits.Calculating);

                    if (deltaMercy != 0)
                    {
                        ptd.AddTraitXp(DefaultTraits.Mercy, deltaMercy);
                    }
                    if (deltaValor != 0)
                    {
                        ptd.AddTraitXp(DefaultTraits.Valor, deltaValor);
                    }
                    if (deltaHonor != 0)
                    {
                        ptd.AddTraitXp(DefaultTraits.Honor, deltaHonor);
                    }
                    if (deltaGenerosity != 0)
                    {
                        ptd.AddTraitXp(DefaultTraits.Generosity, deltaGenerosity);
                    }
                    if (deltaCalculating != 0)
                    {
                        ptd.AddTraitXp(DefaultTraits.Calculating, deltaCalculating);
                    }
                }
                else
                {
                    // It will clamp to min max value in SetTraitLevel()
                    character.SetTraitLevel(DefaultTraits.Mercy, mercy.Value);
                    character.SetTraitLevel(DefaultTraits.Valor, valor.Value);
                    character.SetTraitLevel(DefaultTraits.Honor, honor.Value);
                    character.SetTraitLevel(DefaultTraits.Generosity, generosity.Value);
                    character.SetTraitLevel(DefaultTraits.Calculating, calculating.Value);
                }

                readTraitsSuccess = true;
            }

            string msg = $"Imported {character.Name}'s ";

            if (readBirthdaySuccess && readCultureSuccess && readTraitsSuccess)
            {
                msg += "birthday, culture and traits.";
            }
            else if (readBirthdaySuccess && readCultureSuccess && !readTraitsSuccess)
            {
                msg += "birthday and culture.";
            }
            else if (readBirthdaySuccess && !readCultureSuccess && readTraitsSuccess)
            {
                msg += "birthday and traits.";
            }
            else if (!readBirthdaySuccess && readCultureSuccess && readTraitsSuccess)
            {
                msg += "culture and traits.";
            }
            else if (readBirthdaySuccess && !readCultureSuccess && !readTraitsSuccess)
            {
                msg += "birthday.";
            }
            else if (!readBirthdaySuccess && readCultureSuccess && !readTraitsSuccess)
            {
                msg += "culture.";
            }
            else if (!readBirthdaySuccess && !readCultureSuccess && readTraitsSuccess)
            {
                msg += "traits.";
            }
            else
            {
                msg = null;
            }
            if (msg != null)
            {
                Helper.ShowAndLog(msg + $" Appearance {(updateAppearance ? string.Empty : "not")} updated.", new Color(0, 1, 0));
            }
        }
Exemple #2
0
        public void Export(Hero character)
        {
            StringBuilder sb = new StringBuilder();

            sb.AppendLine("//Lines started with // are comments.");
            sb.AppendLine(string.Empty);
            sb.AppendLine("//Readme:");
            sb.AppendLine("//Season ranges from 0 to 3. Spring is 0 and Autumn is 2, for example.");
            sb.AppendLine("//Day starts with 0. So if your birthday is Summer 11, Day should be 10.");
            sb.AppendLine("//Use the field \"FormattedBirthday\" to check what your birthday is, after export.");
            sb.AppendLine("//If you want to modify RemainingTicks, remember there are 10000 ticks per game second.");
            sb.AppendLine("//Traits range from -2 to 2.");
            sb.AppendLine(Environment.NewLine);
            sb.AppendLine($"//{character.Name}'s birthday:");
            sb.AppendLine($"Year={character.BirthDay.GetYear}");
            sb.AppendLine($"Season={character.BirthDay.GetSeasonOfYear}");
            sb.AppendLine($"Day={character.BirthDay.GetDayOfSeason}");
            sb.AppendLine($"Hour={character.BirthDay.GetHourOfDay}");
            // Get remaining ticks so we don't lose any milliseconds.
            var b = CampaignTime.Years(character.BirthDay.GetYear) +
                    CampaignTime.Seasons(character.BirthDay.GetSeasonOfYear) +
                    CampaignTime.Days(character.BirthDay.GetDayOfSeason) +
                    CampaignTime.Hours(character.BirthDay.GetHourOfDay);

            sb.AppendLine($"RemainingTicks={(character.BirthDay - b).GetTicks()}");
            sb.AppendLine(Environment.NewLine);

            // Culture
            sb.AppendLine("//Culture:");
            sb.AppendLine($"Culture={character.Culture.GetName()}");
            sb.AppendLine(Environment.NewLine);

            // Is this character the player?
            bool isPlayer = Helper.CharacterHasTraitDeveloper(character);

            // Traits
            string traitPromptStr = isPlayer ? " (Read only. Modify Trait XP to change traits)" : string.Empty;

            sb.AppendLine($"//Traits{traitPromptStr}:");
            var traits = character.GetHeroTraits();

            sb.AppendLine($"Mercy={traits.Mercy}");
            sb.AppendLine($"Valor={traits.Valor}");
            sb.AppendLine($"Honor={traits.Honor}");
            sb.AppendLine($"Generosity={traits.Generosity}");
            sb.AppendLine($"Calculating={traits.Calculating}");
            sb.AppendLine(Environment.NewLine);

            // Trait XP
            if (isPlayer)
            {
                sb.AppendLine("//Trait XP:");
                sb.AppendLine($"MercyXP={Campaign.Current.PlayerTraitDeveloper.GetPropertyValue(DefaultTraits.Mercy)}");
                sb.AppendLine($"ValorXP={Campaign.Current.PlayerTraitDeveloper.GetPropertyValue(DefaultTraits.Valor)}");
                sb.AppendLine($"HonorXP={Campaign.Current.PlayerTraitDeveloper.GetPropertyValue(DefaultTraits.Honor)}");
                sb.AppendLine($"GenerosityXP={Campaign.Current.PlayerTraitDeveloper.GetPropertyValue(DefaultTraits.Generosity)}");
                sb.AppendLine($"CalculatingXP={Campaign.Current.PlayerTraitDeveloper.GetPropertyValue(DefaultTraits.Calculating)}");
                sb.AppendLine(Environment.NewLine);
            }

            // Output.
            sb.AppendLine("//Don't modify this field as it will not be read.");
            sb.AppendLine($"FormattedBirthday={character.BirthDay.ToString()}");
            for (int i = DefaultTraits.Mercy.MinValue; i <= DefaultTraits.Mercy.MaxValue; ++i)
            {
                sb.AppendLine($"MercyXPRequired{i}={Campaign.Current.Models.CharacterDevelopmentModel.GetTraitXpRequiredForTraitLevel(DefaultTraits.Mercy, i)}");
            }
            for (int i = DefaultTraits.Valor.MinValue; i <= DefaultTraits.Valor.MaxValue; ++i)
            {
                sb.AppendLine($"ValorXPRequired{i}={Campaign.Current.Models.CharacterDevelopmentModel.GetTraitXpRequiredForTraitLevel(DefaultTraits.Valor, i)}");
            }
            for (int i = DefaultTraits.Honor.MinValue; i <= DefaultTraits.Honor.MaxValue; ++i)
            {
                sb.AppendLine($"HonorXPRequired{i}={Campaign.Current.Models.CharacterDevelopmentModel.GetTraitXpRequiredForTraitLevel(DefaultTraits.Honor, i)}");
            }
            for (int i = DefaultTraits.Generosity.MinValue; i <= DefaultTraits.Generosity.MaxValue; ++i)
            {
                sb.AppendLine($"GenerosityXPRequired{i}={Campaign.Current.Models.CharacterDevelopmentModel.GetTraitXpRequiredForTraitLevel(DefaultTraits.Generosity, i)}");
            }
            for (int i = DefaultTraits.Calculating.MinValue; i <= DefaultTraits.Calculating.MaxValue; ++i)
            {
                sb.AppendLine($"CalculatingXPRequired{i}={Campaign.Current.Models.CharacterDevelopmentModel.GetTraitXpRequiredForTraitLevel(DefaultTraits.Calculating, i)}");
            }
            File.WriteAllText(this.GetSaveName(character), sb.ToString());

            Helper.ShowAndLog($"Exported {character.Name}'s birthday, culture and traits.");
        }
Exemple #3
0
        private void OnDailyTickClan(Clan clan)
        {
            if (!SeparatismConfig.Settings.AnarchyRebellionsEnabled)
            {
                return;
            }

            var clanFiefsAmount = clan.GetFiefsAmount();

            if (clanFiefsAmount < SeparatismConfig.Settings.CriticalAmountOfFiefsPerSingleClan)
            {
                return;
            }
            var anarchySettlements = clan.Settlements.Where(x => x.IsTown &&
                                                            CampaignTime.Hours(x.LastVisitTimeOfOwner) + CampaignTime.Days(SeparatismConfig.Settings.NumberOfDaysAfterOwnerVisitToKeepOrder) < CampaignTime.Now).ToArray();

            if (anarchySettlements.Length == 0)
            {
                return;
            }

            var availableClans = Clan.All.ReadyToGo().ToArray();

            foreach (var settlement in anarchySettlements.OrderByDescending(x => x.Position2D.Distance(clan.FactionMidPoint)))
            {
                var newRulerClan = availableClans
                                   .Where(x => x.Culture == settlement.Culture)
                                   .OrderByDescending(x => x.TotalStrength)
                                   .FirstOrDefault();
                var rebelRightNow = SeparatismConfig.Settings.DailyAnarchyRebellionChance >= 1 ||
                                    (MBRandom.RandomFloat <= SeparatismConfig.Settings.DailyAnarchyRebellionChance);

                if (newRulerClan != null && rebelRightNow)
                {
                    var rebelSettlements = new List <Settlement>();
                    rebelSettlements.Add(settlement);
                    int bonusSettlements = (SeparatismConfig.Settings.BonusRebelFiefForHighTierClan && newRulerClan.Tier > 4) ? 1 : 0;
                    if (bonusSettlements > 0)
                    {
                        var neighborClanFiefs = new Queue <Settlement>(Settlement
                                                                       .FindSettlementsAroundPosition(settlement.Position2D, 50, x => x.OwnerClan == clan)
                                                                       .Where(x => x.IsCastle)
                                                                       .Except(rebelSettlements)
                                                                       .OrderBy(x => x.Position2D.Distance(settlement.Position2D)));
                        while (bonusSettlements > 0 && neighborClanFiefs.Count > 0)
                        {
                            var nextFief = neighborClanFiefs.Dequeue();
                            if (nextFief.Culture == settlement.Culture)
                            {
                                rebelSettlements.Add(nextFief);
                                bonusSettlements--;
                            }
                        }
                    }

                    var rebelKingdom = GoRebelKingdom(newRulerClan, rebelSettlements);
                    var textObject   = new TextObject("{=Separatism_Anarchy_Rebel}People of {Settlement} have broken from {Kingdom} to call {Ruler} on rulership and found the {RebelKingdom}.", null);
                    textObject.SetTextVariable("Settlement", settlement.Name);
                    textObject.SetTextVariable("Kingdom", clan.Kingdom.Name);
                    textObject.SetTextVariable("Ruler", newRulerClan.Leader.Name);
                    textObject.SetTextVariable("RebelKingdom", rebelKingdom.Name);
                    GameLog.Warn(textObject.ToString());
                    return;
                }
            }
        }
Exemple #4
0
            // main merge method
            private static void Postfix()
            {
                if (Campaign.Current.TimeControlMode == CampaignTimeControlMode.Stop ||
                    Campaign.Current.TimeControlMode == CampaignTimeControlMode.UnstoppableFastForwardForPartyWaitTime ||
                    Campaign.Current.TimeControlMode == CampaignTimeControlMode.FastForwardStop ||
                    Campaign.Current.TimeControlMode == CampaignTimeControlMode.StoppableFastForward)
                {
                    return;
                }

                if (Campaign.CurrentTime - lastChecked < 1f)
                {
                    return;
                }

                lastChecked = Campaign.CurrentTime;
                var hideouts = Settlement.All.Where(s => s.IsHideout).ToList();
                var parties  = MobileParty.All.Where(m =>
                                                     m.Party.IsMobile &&
                                                     m.CurrentSettlement is null &&
                                                     !m.IsUsedByAQuest() &&
                                                     m.IsBandit &&
                                                     m.MemberRoster.TotalManCount >= Globals.Settings.MinPartySizeToConsiderMerge)
                               .ToListQ();

                for (var index = 0; index < parties.Count; index++)
                {
                    //T.Restart();
                    var mobileParty = parties[index];

                    if (hideouts.AnyQ(s => s.Position2D.Distance(mobileParty.Position2D) < MinDistanceFromHideout))
                    {
                        continue;
                    }

                    if (mobileParty.IsTooBusyToMerge())
                    {
                        continue;
                    }

                    var nearbyParties = MobileParty.FindPartiesAroundPosition(mobileParty.Position2D, FindRadius)
                                        .Intersect(parties)
                                        .Except(new[] { mobileParty })
                                        .ToListQ();

                    if (!nearbyParties.Any())
                    {
                        continue;
                    }

                    if (mobileParty.ToString().Contains("manhunter")) // Calradia Expanded Kingdoms
                    {
                        continue;
                    }

                    CampaignTime?lastChangeDate = null;
                    if (mobileParty.IsBM())
                    {
                        lastChangeDate = PartyMilitiaMap[mobileParty].LastMergedOrSplitDate;
                    }

                    if (CampaignTime.Now < lastChangeDate + CampaignTime.Hours(Globals.Settings.CooldownHours))
                    {
                        continue;
                    }

                    var targetParty = nearbyParties.Where(m =>
                                                          IsAvailableBanditParty(m) &&
                                                          m.MemberRoster.TotalManCount + mobileParty.MemberRoster.TotalManCount >= Globals.Settings.MinPartySize)
                                      .ToListQ().GetRandomElement()?.Party;

                    //Mod.Log($">T targetParty {T.ElapsedTicks / 10000F:F3}ms.");
                    // "nobody" is a valid answer
                    if (targetParty is null)
                    {
                        continue;
                    }

                    CampaignTime?targetLastChangeDate = null;
                    if (targetParty.MobileParty.IsBM())
                    {
                        targetLastChangeDate = PartyMilitiaMap[targetParty.MobileParty].LastMergedOrSplitDate;
                    }

                    if (CampaignTime.Now < targetLastChangeDate + CampaignTime.Hours(Globals.Settings.CooldownHours))
                    {
                        continue;
                    }


                    var militiaTotalCount = mobileParty.MemberRoster.TotalManCount + targetParty.MemberRoster.TotalManCount;
                    if (MilitiaPowerPercent > Globals.Settings.GlobalPowerPercent ||
                        militiaTotalCount < Globals.Settings.MinPartySize ||
                        militiaTotalCount > CalculatedMaxPartySize ||
                        NumMountedTroops(mobileParty.MemberRoster) +
                        NumMountedTroops(targetParty.MemberRoster) > militiaTotalCount / 2)
                    {
                        continue;
                    }

                    //Mod.Log($"==> counted {T.ElapsedTicks / 10000F:F3}ms.");
                    if (mobileParty != targetParty.MobileParty.MoveTargetParty &&
                        Campaign.Current.Models.MapDistanceModel.GetDistance(targetParty.MobileParty, mobileParty) > MergeDistance)
                    {
                        //Mod.Log($"{mobileParty} seeking > {targetParty.MobileParty}");
                        mobileParty.SetMoveEscortParty(targetParty.MobileParty);
                        //Mod.Log($"SetNavigationModeParty ==> {T.ElapsedTicks / 10000F:F3}ms");

                        if (targetParty.MobileParty.MoveTargetParty != mobileParty)
                        {
                            //Mod.Log($"{targetParty.MobileParty} seeking back > {mobileParty}");
                            targetParty.MobileParty.SetMoveEscortParty(mobileParty);
                            //Mod.Log($"SetNavigationModeTargetParty ==> {T.ElapsedTicks / 10000F:F3}ms");
                        }

                        continue;
                    }

                    //Mod.Log($"==> found settlement {T.ElapsedTicks / 10000F:F3}ms.");
                    // create a new party merged from the two
                    var rosters = MergeRosters(mobileParty, targetParty);
                    var militia = new Militia(mobileParty.Position2D, rosters[0], rosters[1]);
                    // teleport new militias near the player
                    if (Globals.Settings.TestingMode)
                    {
                        // in case a prisoner
                        var party = Hero.MainHero.PartyBelongedTo ?? Hero.MainHero.PartyBelongedToAsPrisoner.MobileParty;
                        militia.MobileParty.Position2D = party.Position2D;
                    }

                    militia.MobileParty.Party.Visuals.SetMapIconAsDirty();

                    try
                    {
                        // can throw if Clan is null
                        Trash(mobileParty);
                        Trash(targetParty.MobileParty);
                    }
                    catch (Exception ex)
                    {
                        Mod.Log(ex);
                    }

                    DoPowerCalculations();
                    //Mod.Log($"==> Finished all work: {T.ElapsedTicks / 10000F:F3}ms.");
                }

                //Mod.Log($"Looped ==> {T.ElapsedTicks / 10000F:F3}ms");
            }
Exemple #5
0
            private static void Postfix()
            {
                try
                {
                    if (Campaign.Current.TimeControlMode == CampaignTimeControlMode.Stop ||
                        Campaign.Current.TimeControlMode == CampaignTimeControlMode.UnstoppableFastForwardForPartyWaitTime ||
                        Campaign.Current.TimeControlMode == CampaignTimeControlMode.FastForwardStop ||
                        Campaign.Current.TimeControlMode == CampaignTimeControlMode.StoppableFastForward ||
                        GlobalMilitiaPower > CalculatedGlobalPowerLimit)
                    {
                        return;
                    }

                    var parties = MobileParty.All.Where(x =>
                                                        x.Party.IsMobile &&
                                                        x.CurrentSettlement == null &&
                                                        !x.IsCurrentlyUsedByAQuest &&
                                                        x.IsBandit).ToList();
                    //T.Restart();
                    for (var index = 0; index < parties.Count; index++)
                    {
                        var mobileParty = parties[index];
                        if (mobileParty.MoveTargetParty != null &&
                            mobileParty.MoveTargetParty.IsBandit ||
                            // Calradia Expanded Kingdoms
                            mobileParty.ToString().Contains("manhunter") ||
                            mobileParty.IsTooBusyToMerge())
                        {
                            continue;
                        }

                        CampaignTime?lastChangeDate = null;
                        if (PartyMilitiaMap.ContainsKey(mobileParty))
                        {
                            lastChangeDate = PartyMilitiaMap[mobileParty].LastMergedOrSplitDate;
                        }

                        if (CampaignTime.Now < lastChangeDate + CampaignTime.Hours(Globals.Settings.CooldownHours))
                        {
                            continue;
                        }

                        var targetParty = MobileParty.FindPartiesAroundPosition(mobileParty.Position2D, FindRadius,
                                                                                x => x != mobileParty && IsValidParty(x) &&
                                                                                x.MemberRoster.TotalManCount + mobileParty.MemberRoster.TotalManCount >= Globals.Settings.MinPartySize)
                                          .ToList().GetRandomElement()?.Party;

                        // "nobody" is a valid answer
                        if (targetParty == null)
                        {
                            continue;
                        }

                        CampaignTime?targetLastChangeDate = null;
                        if (PartyMilitiaMap.ContainsKey(targetParty.MobileParty))
                        {
                            targetLastChangeDate = PartyMilitiaMap[mobileParty].LastMergedOrSplitDate;
                        }

                        if (CampaignTime.Now < targetLastChangeDate + CampaignTime.Hours(Globals.Settings.CooldownHours))
                        {
                            continue;
                        }

                        var militiaTotalCount = mobileParty.MemberRoster.TotalManCount + targetParty.MemberRoster.TotalManCount;
                        if (militiaTotalCount < Globals.Settings.MinPartySize ||
                            militiaTotalCount > CalculatedMaxPartySize ||
                            mobileParty.Party.TotalStrength > CalculatedMaxPartyStrength ||
                            NumMountedTroops(mobileParty.MemberRoster) + NumMountedTroops(targetParty.MemberRoster) > militiaTotalCount / 2)
                        {
                            continue;
                        }

                        if (Campaign.Current.Models.MapDistanceModel.GetDistance(targetParty.MobileParty, mobileParty) > MergeDistance)
                        {
                            Mod.Log($"{mobileParty} seeking > {targetParty.MobileParty}");
                            mobileParty.SetMoveEscortParty(targetParty.MobileParty);
                            //Mod.Log($"SetNavigationModeParty ==> {T.ElapsedTicks / 10000F:F3}ms");

                            if (targetParty.MobileParty.MoveTargetParty != mobileParty)
                            {
                                Mod.Log($"{targetParty.MobileParty} seeking back > {mobileParty}");
                                targetParty.MobileParty.SetMoveEscortParty(mobileParty);
                                //Mod.Log($"SetNavigationModeTargetParty ==> {T.ElapsedTicks / 10000F:F3}ms");
                            }

                            continue;
                        }

                        if (Settlement.FindSettlementsAroundPosition(mobileParty.Position2D, MinDistanceFromHideout, x => x.IsHideout()).Any())
                        {
                            continue;
                        }

                        // create a new party merged from the two
                        var rosters = MergeRosters(mobileParty, targetParty);
                        var militia = new Militia(mobileParty, rosters[0], rosters[1]);
                        militia.MobileParty.SetMovePatrolAroundPoint(militia.MobileParty.Position2D);

                        // teleport new militias near the player
                        if (TestingMode)
                        {
                            // in case a prisoner
                            var party = Hero.MainHero.PartyBelongedTo ?? Hero.MainHero.PartyBelongedToAsPrisoner.MobileParty;
                            militia.MobileParty.Position2D = party.Position2D;
                        }

                        militia.MobileParty.Party.Visuals.SetMapIconAsDirty();
                        Trash(mobileParty);
                        Trash(targetParty.MobileParty);
                        //Mod.Log($">>> Finished all work: {T.ElapsedTicks / 10000F:F3}ms.");
                    }

                    //Mod.Log($"Looped ==> {T.ElapsedTicks / 10000F:F3}ms");
                }
                catch (Exception ex)
                {
                    Mod.Log(ex);
                }
            }