private void OnDailyHeroTick(Hero hero) { // 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 (!SuitorQualifiesForNobleMarriageSystem(hero)) { 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 = FindBestNobleMaiden(hero); var spawned = false; // Were there no eligible female nobles? if (wife is null) { wife = ConsiderMaidenOfLesserNobility(hero, clanFitness); if (wife is null) { return; } else { spawned = true; } } // Get married! Util.Log.Print($" -> MARRIAGE ({(spawned ? "spawned" : "regular")}): {GetHeroTrace(wife, spawned ? -1 : GetClanFitness(wife.Clan))}"); MarriageAction.Apply(hero, wife); }
private static void MarryHero(Hero hero) { if (Hero.MainHero.Spouse == hero || Hero.MainHero.ExSpouses.Contains(hero)) { return; } //1.4.3 结婚后,第二个英雄状态会变成逃亡状态,并且会被所在部队移除, 所在部队还会解散。 Hero mainSpouse = Hero.MainHero.Spouse; bool needAddPlayerTroop = true; if (Hero.MainHero.PartyBelongedTo.MemberRoster.Contains(hero.CharacterObject)) { Hero.MainHero.PartyBelongedTo.MemberRoster.RemoveTroop(hero.CharacterObject, 1); } if (hero.PartyBelongedTo != null) { MobileParty partyBelongedTo = hero.PartyBelongedTo; partyBelongedTo.MemberRoster.RemoveTroop(hero.CharacterObject, 1); partyBelongedTo.RemoveParty(); //partyBelongedTo.Party.Owner = partyBelongedTo.MemberRoster.First().Character; } if (null == hero.Clan) { hero.Clan = Hero.MainHero.Clan; } MarriageAction.Apply(Hero.MainHero, hero); hero.ChangeState(Hero.CharacterStates.Active); if (needAddPlayerTroop) { Hero.MainHero.PartyBelongedTo.MemberRoster.AddToCounts(hero.CharacterObject, 1); } SpouseOperation.RemoveRepeatExspouses(Hero.MainHero, Hero.MainHero.Spouse); TextObject textObject = GameTexts.FindText("sue_more_spouses_marry_target", null); StringHelpers.SetCharacterProperties("SUE_HERO", hero.CharacterObject, null, textObject); InformationManager.AddQuickInformation(textObject, 0, null, "event:/ui/notification/quest_finished"); if (null != mainSpouse) { SpouseOperation.SetPrimarySpouse(mainSpouse); } }
internal void ChangeSpouse(Hero hero, Hero spouseHero) { try { if (hero == null) { return; } Hero heroSpouse = hero.Spouse; if (!hero.IsHumanPlayerCharacter && hero.IsFactionLeader) { RemoveFactionLeader(hero); } else if (!spouseHero.IsHumanPlayerCharacter && spouseHero.IsFactionLeader) { RemoveFactionLeader(spouseHero); } if (heroSpouse != null) { TextObject textObject = GameTexts.FindText("str_CE_spouse_leave"); textObject.SetTextVariable("HERO", hero.Name); textObject.SetTextVariable("SPOUSE", heroSpouse.Name); InformationManager.DisplayMessage(new InformationMessage(textObject.ToString(), Colors.Magenta)); if (heroSpouse.Father != null) { heroSpouse.Clan = heroSpouse.Father.Clan; } else if (heroSpouse.Mother != null) { heroSpouse.Clan = heroSpouse.Mother.Clan; } hero.Spouse = null; } if (spouseHero == null) { return; } if (hero.Clan == spouseHero.Clan) { return; } Hero spouseHeroSpouse = spouseHero.Spouse; if (spouseHeroSpouse != null) { TextObject textObject3 = GameTexts.FindText("str_CE_spouse_leave"); textObject3.SetTextVariable("HERO", hero.Name); textObject3.SetTextVariable("SPOUSE", spouseHeroSpouse.Name); InformationManager.DisplayMessage(new InformationMessage(textObject3.ToString(), Colors.Magenta)); if (spouseHeroSpouse.Father != null) { spouseHeroSpouse.Clan = spouseHeroSpouse.Father.Clan; } else if (spouseHeroSpouse.Mother != null) { spouseHeroSpouse.Clan = spouseHeroSpouse.Mother.Clan; } spouseHero.Spouse = null; } MarriageAction.Apply(hero, spouseHero); } catch (Exception e) { CECustomHandler.ForceLogToFile("Failed ChangeSpouse " + e + " HERO1: " + hero + " HERO2: " + spouseHero); } }
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); }
private void CastleAbandonSettlementMenuOption(CampaignGameStarter campaignGameStarter) { campaignGameStarter.AddGameMenuOption("ruler_castle_menu", "ruler_castle_abandon", "Abandon Rulership of Castle", (GameMenuOption.OnConditionDelegate)(args => { args.optionLeaveType = GameMenuOption.LeaveType.Manage; if (Settlement.CurrentSettlement.OwnerClan.Kingdom != null && Settlement.CurrentSettlement.OwnerClan.Kingdom.Leader != Hero.MainHero) { args.Tooltip = new TextObject("You must be the faction leader to abandon a settlement"); args.IsEnabled = false; } args.Tooltip = new TextObject("A minor noble will take control of the settlement"); return(true); }), (GameMenuOption.OnConsequenceDelegate)(args => { InformationManager.ShowInquiry(new InquiryData("Abandon Castle", "Are you sure you want to abandon this castle?", true, true, "Yes", "No", (Action)(() => { List <CharacterObject> source = new List <CharacterObject>(); CultureObject culture = Settlement.CurrentSettlement.Culture; foreach (CharacterObject characterObject in CharacterObject.Templates.Where <CharacterObject>((Func <CharacterObject, bool>)(x => x.Occupation == Occupation.Lord)).ToList <CharacterObject>()) { if (characterObject.Culture == culture) { source.Add(characterObject); } } CharacterObject template = source[rng.Next(0, source.Count - 1)]; Hero NewHero = HeroCreator.CreateSpecialHero(template, Settlement.CurrentSettlement, null, Hero.MainHero.Clan, rng.Next(25, 30)); NewHero.ChangeState(Hero.CharacterStates.Active); HeroCreationCampaignBehavior herocreationbehavior = new HeroCreationCampaignBehavior(); herocreationbehavior.DeriveSkillsFromTraits(NewHero, template); List <CharacterObject> source2 = new List <CharacterObject>(); foreach (CharacterObject characterObject in CharacterObject.Templates.Where <CharacterObject>((Func <CharacterObject, bool>)(x => x.Occupation == Occupation.Lord)).ToList <CharacterObject>()) { if (characterObject.Culture == culture) { source2.Add(characterObject); } } CharacterObject template2 = source2[rng.Next(0, source2.Count - 1)]; template2.IsFemale = true; Hero NewHero2 = HeroCreator.CreateSpecialHero(template2, Settlement.CurrentSettlement, null, Hero.MainHero.Clan, rng.Next(25, 30)); NewHero2.ChangeState(Hero.CharacterStates.Active); herocreationbehavior.DeriveSkillsFromTraits(NewHero2, template2); template2.IsFemale = false; Clan clan = MBObjectManager.Instance.CreateObject <Clan>(); Banner ClanBanner = Banner.CreateRandomClanBanner(); TextObject clanName = culture.ClanNameList[rng.Next(0, culture.ClanNameList.Count)]; clan.InitializeClan(clanName, clanName, culture, ClanBanner); clan.SetLeader(NewHero); FieldInfo field = clan.GetType().GetField("_tier", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if ((FieldInfo)null != field) { field.SetValue((object)clan, rng.Next(2, 4)); } NewHero.Clan = clan; NewHero.IsNoble = true; MobileParty newMobileParty1 = clan.CreateNewMobileParty(NewHero); newMobileParty1.ItemRoster.AddToCounts(DefaultItems.Grain, 10); newMobileParty1.ItemRoster.AddToCounts(DefaultItems.Meat, 5); NewHero2.Clan = clan; NewHero2.IsNoble = true; MobileParty newMobileParty2 = clan.CreateNewMobileParty(NewHero2); newMobileParty2.ItemRoster.AddToCounts(DefaultItems.Grain, 10); newMobileParty2.ItemRoster.AddToCounts(DefaultItems.Meat, 5); ChangeOwnerOfSettlementAction.ApplyByKingDecision(NewHero, Settlement.CurrentSettlement); clan.UpdateHomeSettlement(Settlement.CurrentSettlement); MarriageAction.Apply(NewHero, NewHero2); ChangeRelationAction.ApplyPlayerRelation(NewHero, 40); }), (Action)(() => { GameMenu.SwitchToMenu("castle"); })), true); }), index: 1);;; }