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)); } }
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."); }
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; } } }
// 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"); }
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); } }