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); }
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); }