private Hero?ConsiderMaidenOfLesserNobility(Hero hero, int clanFitness)
        {
            string spawnMsg = " -> No eligible noble candidates to marry.";

            if (!Config.SpawnNobleWives || Config.SpawnedMarriageChanceMult < 0.01f)
            {
                Util.Log.Print(spawnMsg);
                return(null);
            }

            // If CF >= 3, then we never spawn a wife.
            if (clanFitness >= 3)
            {
                Util.Log.Print(spawnMsg + $" Can't spawn wife: clan fitness of {clanFitness} is too high.");
                return(null);
            }

            // Likewise, at 0 < CF < 3, there are restrictions upon spawning a wife based
            // upon how many preexisting children the hero has sired and/or whether they're
            // too old.
            int childCount     = hero.Children.Count();
            int maleChildCount = hero.Children.Where(h => !h.IsFemale).Count();

            Hero?CannotSpawnWifeDueToClanFitness()
            {
                Util.Log.Print(spawnMsg + $" Can't spawn wife: my clan fitness of {clanFitness} is too high for my prior children"
                               + $" (sons: {maleChildCount}; all: {childCount}) or my age of {hero.Age:F0}.");
                return(null);
            }

            if (clanFitness == 2 && (childCount >= 2 || maleChildCount >= 1 || hero.Age >= 60))
            {
                return(CannotSpawnWifeDueToClanFitness());
            }

            if (clanFitness == 1 && (childCount >= 3 || maleChildCount >= 2 || hero.Age >= 70))
            {
                return(CannotSpawnWifeDueToClanFitness());
            }

            // Now, the base chance from here (taking into account that our clan fitness level
            // has already significantly affected the odds of reaching this point) is simply
            // 40%, with up to two +5% bonuses or two -5% maluses for however many children short
            // of 2 we do not already have (i.e., in [30%, 50%]).

            float spawnChance = 0.4f + Math.Max(-0.1f, 0.05f * (2 - childCount));

            spawnChance *= Config.SpawnedMarriageChanceMult; // Modified by our config
            var chanceStr = $" (chance was {spawnChance * 100:F0}%)";

            if (MBRandom.RandomFloat > spawnChance)
            {
                Util.Log.Print(spawnMsg + $" Decided not to marry into lesser nobility{chanceStr}.");
                return(null);
            }

            Util.Log.Print(spawnMsg + $" Marrying into lesser nobility{chanceStr}...");

            int wifeAgeMin = Math.Max(22, marriageModel !.MinimumMarriageAgeFemale);
            int wifeAgeMax = Math.Min(maxAgeFemale - 5, wifeAgeMin + 5);

            var wife = HeroUtil.SpawnNoble(hero.Clan, wifeAgeMin, wifeAgeMax, isFemale: true);

            if (wife is null)
            {
                Util.Log.Print(" ---> ERROR: Could not find character template to spawn female noble!");
                return(null);
            }

            return(wife);
        }
Example #2
0
        private void OnDailyHeroTick(Hero hero)
        {
            // Very early exit conditions:
            if (hero.IsFemale || !hero.IsNoble)
            {
                return;
            }

            // We only evaluate marriage once per human year, and we use an offset to distribute hero
            // marriages more evenly throughout that year:
            int daysOffset  = hero.Id.GetHashCode() % daysPerHumanYear;
            int daysElapsed = (int)Campaign.Current.CampaignStartTime.ElapsedDaysUntilNow;

            // Is this not the right day to do our yearly marriage tick?
            if ((daysElapsed + daysOffset) % daysPerHumanYear != 0)
            {
                return;
            }

            // Does this hero even qualify for a marriage evaluation?
            if (hero.IsDead ||
                !hero.IsActive ||
                hero.Clan is null ||
                hero.Clan.Kingdom is null ||
                (int)hero.Age < minAgeMale ||
                !Campaign.Current.Models.MarriageModel.IsSuitableForMarriage(hero) ||
                hero.Clan.IsClanTypeMercenary ||
                hero.Clan == Clan.PlayerClan)
            {
                return;
            }

            var clanFitness    = GetClanFitness(hero.Clan);
            var marriageChance = GetAnnualMarriageChance(clanFitness);

            Util.Log.Print($"[{CampaignTime.Now}] {GetHeroTrace(hero, clanFitness)}: Considering marriage ({marriageChance * 100:F1}% chance)...");

            if (MBRandom.RandomFloat > marriageChance)
            {
                Util.Log.Print(" -> Decided not to marry for now.");
                return;
            }

            // Find eligible candidates for marriage in order of preference
            var wife = Kingdom.All
                       .Where(k =>
                              !k.IsEliminated &&
                              IsKingdomAllowedForMarriageByConfig(hero, k))
                       .SelectMany(k => k.Clans)
                       .Where(c =>
                              !c.IsEliminated &&
                              !c.IsClanTypeMercenary &&
                              c != Clan.PlayerClan)
                       .SelectMany(c => c.Lords)
                       .Where(h =>
                              h.IsFemale &&
                              h.IsAlive &&
                              h.IsNoble &&
                              h.IsActive &&
                              h.Spouse is null &&
                              IsMaidenAllowedForMarriageByConfig(hero, h) &&
                              Campaign.Current.Models.MarriageModel.IsCoupleSuitableForMarriage(hero, h))
                       .OrderByDescending(h => GetNobleMatchScore(hero, h))
                       .FirstOrDefault();

            var marriageType = string.Empty;

            // Were there no eligible female nobles?
            if (wife is null)
            {
                string spawnMsg = " -> No eligible noble candidates to marry.";

                if (!Config.SpawnNobleWives || Config.SpawnedMarriageChanceMult < 0.01f)
                {
                    Util.Log.Print(spawnMsg);
                    return;
                }

                // If CF >= 3, then we never spawn a wife.
                if (clanFitness >= 3)
                {
                    Util.Log.Print(spawnMsg + " Can't try to spawn wife (clan fitness is too high).");
                    return;
                }

                // Likewise, at 0 < CF < 3, there are restrictions upon spawning a wife based
                // upon how many preexisting children the hero has sired and/or whether they're
                // too old.
                int childCount     = hero.Children.Count();
                int maleChildCount = hero.Children.Where(h => !h.IsFemale).Count();

                if (clanFitness == 2 && (childCount >= 2 || maleChildCount >= 1 || hero.Age >= 60) ||
                    clanFitness == 1 && (childCount >= 3 || maleChildCount >= 2 || hero.Age >= 65))
                {
                    Util.Log.Print(spawnMsg + " Can't try to spawn wife (clan fitness is too high for our prior children / age).");
                    return;
                }

                // Now, the base chance from here (taking into account that our clan fitness level
                // has already significantly affected the odds of reaching this point) is simply
                // 40%, with up to two +5% bonuses or two -5% maluses for however many children short
                // of 2 we do not already have (i.e., in [30%, 50%]).

                float spawnChance = 0.4f + Math.Max(-0.1f, 0.05f * (2 - childCount));
                spawnChance *= Config.SpawnedMarriageChanceMult; // Modified by our config
                var chanceStr = $" (chance was {spawnChance * 100:F0}%)";

                if (MBRandom.RandomFloat > spawnChance)
                {
                    Util.Log.Print(spawnMsg + $" Decided not to spawn wife{chanceStr}.");
                    return;
                }

                Util.Log.Print(spawnMsg + $" Spawning wife{chanceStr}...");
                marriageType = " (spawned)";

                int wifeAgeMin = Campaign.Current.Models.MarriageModel.MinimumMarriageAgeFemale;
                int wifeAgeMax = Math.Min(maxAgeFemale - 5, wifeAgeMin + 5);

                wife = HeroUtil.SpawnNoble(hero.Clan, wifeAgeMin, wifeAgeMax, isFemale: true);

                if (wife is null)
                {
                    Util.Log.Print(" ---> ERROR: Could not find character template to spawn female noble!");
                    return;
                }

                wife.IsFertile = true;
            }

            // Get married!
            Util.Log.Print($" -> MARRIAGE{marriageType}: {GetHeroTrace(wife, GetClanFitness(wife.Clan))}");
            MarriageAction.Apply(hero, wife);
        }