private static void Postfix(MobileParty mobileParty, ref ExplainedNumber __result) { if (PartyMilitiaMap.ContainsKey(mobileParty)) { __result.AddFactor(SpeedModifier, new TextObject("Bandit Militia")); } }
internal static IEnumerable <CodeInstruction> CalculateBasePartySpeedPatch(IEnumerable <CodeInstruction> ins) { // ReSharper disable once EntityNameCapturedOnly.Local float SlowBM(MobileParty mobileParty, float input) { if (PartyMilitiaMap.ContainsKey(mobileParty)) { return(input * 0.15f); } return(input); } var codes = ins.ToListQ(); for (var index = 0; index < codes.Count; index++) { if (codes[index].opcode == OpCodes.Call && codes[index + 1].opcode == OpCodes.Stloc_S && codes[index + 2].opcode == OpCodes.Ldloca_S && codes[index + 3].opcode == OpCodes.Ldloc_S) { codes.InsertRange(index + 1, new List <CodeInstruction> { new(OpCodes.Dup), new(OpCodes.Ldarg_1), new(OpCodes.Call, AccessTools.Method(typeof(MilitiaPatches), nameof(SlowBM))) });
public Militia(MobileParty mobileParty, TroopRoster party, TroopRoster prisoners) { Banner = Banners.GetRandomElement(); BannerKey = Banner.Serialize(); Spawn(mobileParty, party, prisoners); TrainMilitia(); PartyMilitiaMap.Add(MobileParty, this); LogMilitiaFormed(MobileParty); }
internal static void Trash(MobileParty mobileParty) { Mod.Log("Trashing " + mobileParty.Name); PartyMilitiaMap.Remove(mobileParty); // added as workaround/fix for issue seen in 1.5.9 where TroopRoster.Count is wrong and TroopRoster.Clear() throws Traverse.Create(mobileParty.MemberRoster).Field <int>("_count").Value = mobileParty.MemberRoster.GetTroopRoster().Count(x => x.Character != null); mobileParty.MemberRoster.UpdateVersion(); mobileParty.RemoveParty(); }
private void Spawn(Vec2 position, TroopRoster party, TroopRoster prisoners) { var partyClan = GetMostPrevalent(party) ?? Clan.BanditFactions.First(); MobileParty = ModBanditMilitiaPartyComponent.CreateBanditParty(partyClan); MobileParty.InitializeMobilePartyAroundPosition(party, prisoners, position, 0); PartyMilitiaMap.Add(MobileParty, this); PartyImageMap.Add(MobileParty, new ImageIdentifierVM(Banner)); var leaderHero = MobileParty.MemberRoster.GetTroopRoster().ToListQ()[0].Character.HeroObject; MobileParty.PartyComponent.ChangePartyLeader(leaderHero); Hero = MobileParty.LeaderHero; Hero.Gold = Convert.ToInt32(MobileParty.Party.TotalStrength * Globals.GoldMap[Globals.Settings.GoldReward.SelectedValue]); if (MobileParty.ActualClan.Leader is null) { MobileParty.ActualClan.SetLeader(Hero); } if (MobileParty.MemberRoster.GetTroopRoster().Any(t => t.Character.IsMounted)) { var mount = Mounts.GetRandomElement(); Hero.BattleEquipment[10] = new EquipmentElement(mount); if (mount.HorseComponent.Monster.MonsterUsage == "camel") { Hero.BattleEquipment[11] = new EquipmentElement(Saddles.Where(saddle => saddle.Name.ToString().ToLower().Contains("camel")).ToList().GetRandomElement()); } else { Hero.BattleEquipment[11] = new EquipmentElement(Saddles.Where(saddle => !saddle.Name.ToString().ToLower().Contains("camel")).ToList().GetRandomElement()); } } var getLocalizedText = AccessTools.Method(typeof(MBTextManager), "GetLocalizedText"); Name = (string)getLocalizedText.Invoke(null, new object[] { $"{Possess(Hero.FirstName.ToString())} Bandit Militia" }); MobileParty.SetCustomName(new TextObject(Name)); MobileParty.LeaderHero.StringId += "Bandit_Militia"; MobileParty.ShouldJoinPlayerBattles = true; var tracker = Globals.MobilePartyTrackerVM?.Trackers?.FirstOrDefault(t => t.TrackedParty == MobileParty); if (Globals.Settings.Trackers && tracker is null && MobileParty.MemberRoster.TotalManCount >= Globals.Settings.TrackedSizeMinimum) { tracker = new MobilePartyTrackItemVM(MobileParty, MapScreen.Instance.MapCamera, null); Globals.MobilePartyTrackerVM?.Trackers?.Add(tracker); }
private static void OnMilitiaRemoved(PartyBase partyBase) { if (!IsBM(partyBase.MobileParty)) { return; } Mod.Log($">>> OnMilitiaRemoved - {partyBase.Name}."); if (partyBase.MobileParty.LeaderHero?.CurrentSettlement != null) { Traverse.Create(HeroesWithoutParty(partyBase.MobileParty.LeaderHero?.CurrentSettlement)).Field <List <Hero> >("_list").Value.Remove(partyBase.MobileParty.LeaderHero); Mod.Log($">>> FLUSH OnMilitiaRemoved bandit hero without party - {partyBase.MobileParty.LeaderHero.Name} at {partyBase.MobileParty.LeaderHero?.CurrentSettlement}."); } PartyMilitiaMap.Remove(partyBase.MobileParty); }
private static void FlushBanditMilitias() { PartyMilitiaMap.Clear(); var hasLogged = false; var partiesToRemove = MobileParty.All.Where(x => x.StringId.StartsWith("Bandit_Militia")).ToList(); foreach (var mobileParty in partiesToRemove) { if (!hasLogged) { Mod.Log($">>> FLUSH {partiesToRemove.Count} Bandit Militias", LogLevel.Info); hasLogged = true; } Trash(mobileParty); } }
private static void Postfix(MobileParty __instance, MobileParty targetParty, ref bool __result) { if (__result && !targetParty.IsGarrison && !targetParty.IsMilitia && PartyMilitiaMap.ContainsKey(__instance)) //Militias.Any(x => x.MobileParty == __instance)) { if (targetParty == MobileParty.MainParty) { __result = true; } else { var party1Strength = __instance.GetTotalStrengthWithFollowers(); var party2Strength = targetParty.GetTotalStrengthWithFollowers(); var delta = (party1Strength - party2Strength) / party1Strength * 100; __result = delta <= Globals.Settings.PartyStrengthDeltaPercent; } } }
private static void Postfix() { Mod.Log("MapScreen.OnInitialize"); //Mod.Log("Clans:"); //Clan.All.Do(x => Mod.Log($"Name: {x.Name} MapFaction: {x.MapFaction} Culture: {x.Culture}")); //Mod.Log("Bandit Clans:"); //Clan.BanditFactions.Do(x => Mod.Log($"Name: {x.Name} MapFaction: {x.MapFaction} Culture: {x.Culture}")); HeroCreatorCopy.VeteransRespect = PerkObject.All.First(x => x.StringId == "LeadershipVeteransRespect"); HeroCreatorCopy.Leadership = SkillObject.All.First(x => x.StringId == "Leadership"); EquipmentItems.Clear(); PopulateItems(); Recruits = CharacterObject.All.Where(x => x.Level == 11 && x.Occupation == Occupation.Soldier && !x.StringId.StartsWith("regular_fighter") && !x.StringId.StartsWith("veteran_borrowed_troop") && !x.StringId.EndsWith("_tier_1") && !x.StringId.Contains("_militia_") && !x.StringId.Equals("sturgian_warrior_son") && !x.StringId.Equals("khuzait_noble_son") && !x.StringId.Equals("imperial_vigla_recruit") && !x.StringId.Equals("battanian_highborn_youth") && !x.StringId.Equals("vlandian_squire") && !x.StringId.Equals("aserai_youth") && !x.StringId.Equals("poacher")); // used for armour foreach (ItemObject.ItemTypeEnum value in Enum.GetValues(typeof(ItemObject.ItemTypeEnum))) { ItemTypes[value] = Items.FindAll(x => x.Type == value && x.Value >= 1000 && x.Value <= Globals.Settings.MaxItemValue * Variance).ToList(); } // front-load BanditEquipment.Clear(); for (var i = 0; i < 500; i++) { BanditEquipment.Add(BuildViableEquipmentSet()); } PartyMilitiaMap.Clear(); Hideouts = Settlement.FindAll(x => x.IsHideout()).ToList(); var militias = MobileParty.All.Where(x => x != null && x.StringId.StartsWith("Bandit_Militia")).ToList(); for (var i = 0; i < militias.Count; i++) { var militia = militias[i]; if (militia.LeaderHero == null) { Mod.Log("Leaderless militia found and removed."); Trash(militia); } else { var recreatedMilitia = new Militia(militia); PartyMilitiaMap.Add(recreatedMilitia.MobileParty, recreatedMilitia); } } Mod.Log($"Militias: {militias.Count} (registered {PartyMilitiaMap.Count})"); // 1.5.8 is dropping the militia settlements at some point, I haven't figured out where ReHome(); DailyCalculations(); // have to patch it late because of its static constructor (type initialization exception) Mod.harmony.Patch( AccessTools.Method(typeof(EncounterGameMenuBehavior), "game_menu_encounter_on_init"), new HarmonyMethod(AccessTools.Method(typeof(Helper), nameof(FixMapEventFuckery)))); }
private static void OnPartyRemoved(PartyBase party) { PartyMilitiaMap.Remove(party.MobileParty); }
private static void Postfix() { Mod.Log("MapScreen.OnInitialize"); MinSplitSize = Globals.Settings.MinPartySize * 2; EquipmentItems.Clear(); PopulateItems(); // 1.7 changed CreateHeroAtOccupation to only fish from this: NotableAndWandererTemplates // this has no effect on 1.6.5 since the property doesn't exist var characterObjects = CharacterObject.All.Where(x => x.Occupation is Occupation.Bandit && x.Name.Contains("Boss")).ToList().GetReadOnlyList(); foreach (var clan in Clan.BanditFactions) { Traverse.Create(clan.Culture).Property <IReadOnlyList <CharacterObject> >("NotableAndWandererTemplates").Value = characterObjects; } var filter = new List <string> { "regular_fighter", "veteran_borrowed_troop", }; Recruits = CharacterObject.All.Where(c => c.Level == 11 && c.Occupation == Occupation.Soldier && !filter.Contains(c.StringId) && !c.StringId.EndsWith("_tier_1")); // used for armour foreach (ItemObject.ItemTypeEnum value in Enum.GetValues(typeof(ItemObject.ItemTypeEnum))) { ItemTypes[value] = Items.All.Where(x => x.Type == value && x.Value >= 1000 && x.Value <= Globals.Settings.MaxItemValue).ToList(); } // front-load BanditEquipment.Clear(); for (var i = 0; i < 1000; i++) { BanditEquipment.Add(BuildViableEquipmentSet()); } PartyMilitiaMap.Clear(); Hideouts = Settlement.FindAll(x => x.IsHideout).ToList(); // considers leaderless militias var militias = MobileParty.All.Where(m => m.LeaderHero is not null && m.StringId.StartsWith("Bandit_Militia")).ToList(); for (var i = 0; i < militias.Count; i++) { var militia = militias[i]; var recreatedMilitia = new Militia(militia); SetMilitiaPatrol(recreatedMilitia.MobileParty); PartyMilitiaMap.Add(recreatedMilitia.MobileParty, recreatedMilitia); } DoPowerCalculations(true); FlushMilitiaCharacterObjects(); // 1.6 is dropping the militia settlements at some point, I haven't figured out where ReHome(); Mod.Log($"Militias: {militias.Count} (registered {PartyMilitiaMap.Count})"); RunLateManualPatches(); }
internal static bool IsBM(MobileParty mobileParty) { return(mobileParty != null && PartyMilitiaMap.ContainsKey(mobileParty)); }
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); } }