static void Prefix(TurnDirector __instance) { if (!__instance.IsInterleaved && !__instance.IsInterleavePending && __instance.CurrentRound >= Mod.Config.Ambush.EnableOnRound && ModState.PotentialAmbushOrigins.Count != 0 && __instance.ActiveTurnActor is Team activeTeam && activeTeam.IsLocalPlayer) { // Re-Filter the candidates to try to catch buildings that were marked contract objectives CandidateBuildingsHelper.FilterOnTurnActorIncrement(__instance.Combat); // Determine potential ambush sites based upon the origins Dictionary <Vector3, List <BattleTech.Building> > ambushSites = new Dictionary <Vector3, List <BattleTech.Building> >(); foreach (Vector3 origin in ModState.PotentialAmbushOrigins) { // For the given origin, find how many potential buildings there are. List <BattleTech.Building> originCandidates = CandidateBuildingsHelper.ClosestCandidatesToPosition(origin, Mod.Config.Ambush.SearchRadius); Mod.Log.Debug?.Write($" Found {originCandidates.Count} candidate buildings for originPos: {origin}"); ambushSites.Add(origin, originCandidates); } ModState.PotentialAmbushOrigins.Clear(); // reset potential origins for next round // Determine if the unit was ambushed this turn bool wasAmbushed = false; if (ModState.Ambushes < Mod.Config.Ambush.MaxPerMap && ambushSites.Count > 0) { float roll = Mod.Random.Next(1, 100); int threshold = (int)Math.Ceiling(ModState.CurrentAmbushChance * 100f); if (roll <= threshold) { Mod.Log.Info?.Write($" Roll: {roll} is under current threshold: {threshold}, enabling possible ambush"); wasAmbushed = true; } else { ModState.CurrentAmbushChance += Mod.Config.Ambush.ChancePerActor; Mod.Log.Info?.Write($" Roll: {roll} was over threshold: {threshold}, increasing ambush chance to: {ModState.CurrentAmbushChance} for next position."); } } if (wasAmbushed) { // Sort the ambushSites by number of buildings to maximize ambush success Mod.Log.Debug?.Write("Sorting sites by potential buildings."); List <KeyValuePair <Vector3, List <BattleTech.Building> > > sortedSites = ambushSites.ToList(); sortedSites.Sort((x, y) => x.Value.Count.CompareTo(y.Value.Count)); Vector3 ambushOrigin = sortedSites[0].Key; Mod.Log.Debug?.Write($"Spawning an ambush at position: {ambushOrigin}"); // Randomly determine an ambush type by weight List <AmbushType> shuffledTypes = new List <AmbushType>(); shuffledTypes.AddRange(Mod.Config.Ambush.AmbushTypes); shuffledTypes.Shuffle <AmbushType>(); AmbushType ambushType = shuffledTypes[0]; Mod.Log.Info?.Write($"Ambush type of: {ambushType} will be applied at position: {ambushOrigin}"); switch (ambushType) { case AmbushType.Explosion: ExplosionAmbushHelper.SpawnAmbush(ambushOrigin); break; case AmbushType.Infantry: InfantryAmbushHelper.SpawnAmbush(ambushOrigin); break; case AmbushType.BattleArmor: SpawnAmbushHelper.SpawnAmbush(ambushOrigin, AmbushType.BattleArmor); break; case AmbushType.Mech: SpawnAmbushHelper.SpawnAmbush(ambushOrigin, AmbushType.Mech); break; case AmbushType.Vehicle: SpawnAmbushHelper.SpawnAmbush(ambushOrigin, AmbushType.Vehicle); break; default: Mod.Log.Error?.Write($"UNKNOWN AMBUSH TYPE: {ambushType} - CANNOT PROCEED!"); break; } // Record a successful ambush and reset the weighting ModState.AmbushOrigins.Add(ambushOrigin); ModState.Ambushes++; ModState.CurrentAmbushChance = Mod.Config.Ambush.BaseChance; } } }
public static void Postfix(TurnDirector __instance, MessageCenterMessage message) { Mod.Log.Trace?.Write("TD:OICC - entered."); ModState.IsUrbanBiome = ModState.Combat.ActiveContract.ContractBiome == Biome.BIOMESKIN.urbanHighTech; if (!ModState.IsUrbanBiome) { Mod.Log.Info?.Write($"Contract has non-urban biome ({ModState.Combat.ActiveContract.ContractBiome}). Skipping processing."); return; } Mod.Log.Info?.Write($"Contract has Urban High Tech biome, enabling mod features."); // Check contract exclusions foreach (string overrrideId in Mod.Config.Ambush.BlacklistedContracts) { if (overrrideId.Equals(ModState.Combat.ActiveContract.Override.ID, StringComparison.InvariantCultureIgnoreCase)) { Mod.Log.Info?.Write($"Contract with override ID '{overrrideId}' is excluded, skipping processing."); ModState.IsUrbanBiome = false; return; } } Mod.Log.Info?.Write($"Contract with override ID: {ModState.Combat.ActiveContract.Override.ID} is not blacklisted, enabling ambushes."); ModState.ContractDifficulty = ModState.Combat.ActiveContract.Override.finalDifficulty; Mod.Log.Info?.Write($"Using contractOverride finalDifficulty of: {ModState.ContractDifficulty}"); foreach (Team team in ModState.Combat.Teams) { if (team.GUID == TeamDefinition.TargetsTeamDefinitionGuid) { ModState.TargetTeam = team; } else if (team.GUID == TeamDefinition.TargetsAllyTeamDefinitionGuid) { ModState.TargetAllyTeam = team; } else if (team.GUID == TeamDefinition.HostileToAllTeamDefinitionGuid) { ModState.HostileToAllTeam = team; } } Mod.Log.Info?.Write($"" + $"TargetTeam identified as: {ModState.TargetTeam?.DisplayName} " + $"TargetAllyTeam identified as: {ModState.TargetAllyTeam?.DisplayName} " + $"HostileToAllTeam identified as: {ModState.HostileToAllTeam?.DisplayName}."); // TODO: Make this much more powerful and varied. ModState.AmbushTeam = ModState.TargetTeam; Mod.Log.Info?.Write($"Using team: {ModState.AmbushTeam.DisplayName} as Ambush team."); // Filter the AmbushDefs by contract difficulty. If we don't have ambushes for our contract difficulty, // there's a configuration error - abort! This has to come before data loading as it sets // the AmbushDefs for the current contract bool haveAmbushes = FilterAmbushes(); if (!haveAmbushes) { ModState.IsUrbanBiome = false; Mod.Log.Warn?.Write("Incorrect filter configuration - disabling ambushes!"); return; } // Load any resources necessary for our ambush try { DataLoadHelper.LoadAmbushResources(ModState.Combat); } catch (Exception e) { Mod.Log.Error?.Write(e, "Failed to load ambush resources due to exception!"); ModState.IsUrbanBiome = false; } // Find candidate buildings CandidateBuildingsHelper.DoInitialFilter(__instance.Combat); Mod.Log.Info?.Write($"Contract initially has: {ModState.CandidateBuildings.Count} candidate buildings"); // Devestate buildings DevestationHelper.DevestateBuildings(); Mod.Log.Info?.Write($"After devestation, map has {ModState.CandidateBuildings.Count} candidate buildings."); }