public static void SpawnAmbush(Vector3 ambushOrigin) { if (!Mod.Config.InfantryAmbush.Enabled) { return; } int infantrySpawns = Mod.Random.Next(ModState.InfantryAmbushDefForContract.MinSpawns, ModState.InfantryAmbushDefForContract.MaxSpawns); Mod.Log.Debug?.Write($"Spawning up to {infantrySpawns} infantry spawns as part of this ambush."); // Create a new lance in the target team Lance ambushLance = TeamHelper.CreateAmbushLance(ModState.AmbushTeam); // Build list of candidate trap buildings List <BattleTech.Building> candidates = CandidateBuildingsHelper.ClosestCandidatesToPosition(ambushOrigin, Mod.Config.Ambush.SearchRadius); if (candidates.Count < ModState.InfantryAmbushDefForContract.MinSpawns) { Mod.Log.Debug?.Write($"Insufficient candidate buildings to spawn an infantry ambush. Skipping."); return; } // Make sure we don't spawn more turrets than buildings if (infantrySpawns > candidates.Count) { infantrySpawns = candidates.Count; } List <AbstractActor> spawnedActors = new List <AbstractActor>(); List <BattleTech.Building> spawnBuildings = new List <BattleTech.Building>(); for (int i = 0; i < infantrySpawns; i++) { BattleTech.Building spawnBuildingShell = candidates.ElementAt(i); // Spawn a turret trap AbstractActor ambushTurret = SpawnAmbushTurret(ModState.AmbushTeam, ambushLance, spawnBuildingShell, ambushOrigin); spawnedActors.Add(ambushTurret); spawnBuildings.Add(spawnBuildingShell); Mod.Log.Info?.Write($"Spawned turret: {ambushTurret.DisplayName} in building: {spawnBuildingShell.DisplayName}"); } // Remove any buildings that are part of this ambush from candidates ModState.CandidateBuildings.RemoveAll(x => spawnBuildings.Contains(x)); // Determine the targets that should be prioritized by the enemies List <ICombatant> targets = new List <ICombatant>(); foreach (ICombatant combatant in ModState.Combat.GetAllCombatants()) { if (!combatant.IsDead && !combatant.IsFlaggedForDeath && combatant.team != null && ModState.Combat.HostilityMatrix.IsLocalPlayerFriendly(combatant.team)) { if (Vector3.Distance(ambushOrigin, combatant.CurrentPosition) <= Mod.Config.Ambush.SearchRadius) { targets.Add(combatant); } } } Mod.Log.Info?.Write($"Adding InfantryAmbushSequence for {spawnedActors.Count} actors."); try { InfantryAmbushSequence ambushSequence = new InfantryAmbushSequence(ModState.Combat, ambushOrigin, spawnedActors, spawnBuildings, targets, Mod.Config.InfantryAmbush.FreeAttackEnabled); ModState.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(ambushSequence)); } catch (Exception e) { Mod.Log.Error?.Write(e, "Failed to create AES sequence due to error!"); } }
public static void SpawnAmbush(Vector3 ambushOrigin, AmbushType ambushType) { // Determine how many units we're spawning int minSpawns = 0, maxSpawns = 0; if (ambushType == AmbushType.BattleArmor) { if (!Mod.Config.BattleArmorAmbush.Enabled) { return; } minSpawns = ModState.BattleArmorAmbushDefForContract.MinSpawns; maxSpawns = ModState.BattleArmorAmbushDefForContract.MaxSpawns; } else if (ambushType == AmbushType.Mech) { if (!Mod.Config.MechAmbush.Enabled) { return; } minSpawns = ModState.MechAmbushDefForContract.MinSpawns; maxSpawns = ModState.MechAmbushDefForContract.MaxSpawns; } else if (ambushType == AmbushType.Vehicle) { if (!Mod.Config.VehicleAmbush.Enabled) { return; } minSpawns = ModState.VehicleAmbushDefForContract.MinSpawns; maxSpawns = ModState.VehicleAmbushDefForContract.MaxSpawns; } int actorsToSpawn = Mod.Random.Next(minSpawns, maxSpawns); Mod.Log.Debug?.Write($"Spawning {actorsToSpawn} actors as part of this ambush."); // Starting with the closest building, look through the buildings and determine how many locations can support a unit. List <BattleTech.Building> candidates = CandidateBuildingsHelper.ClosestCandidatesToPosition(ambushOrigin, Mod.Config.Ambush.SearchRadius); if (candidates.Count < minSpawns) { Mod.Log.Debug?.Write($"Insufficient candidate buildings for a spawn ambush. Skipping."); return; } // Create a new lance in the target team Lance ambushLance = TeamHelper.CreateAmbushLance(ModState.AmbushTeam); ModState.CurrentSpawningLance = ambushLance; EncounterLayerData encounterLayerData = ModState.Combat.EncounterLayerData; List <BattleTech.Building> buildingsToLevel = new List <BattleTech.Building>(); List <AbstractActor> spawnedActors = new List <AbstractActor>(); foreach (BattleTech.Building building in candidates) { if (actorsToSpawn == 0) { break; // nothing more to do, end processing. } // Spawn one unit at the origin of the building buildingsToLevel.Add(building); Mod.Log.Debug?.Write("Spawning actor at building origin."); AbstractActor spawnedActor = null; if (ambushType == AmbushType.BattleArmor) { spawnedActor = SpawnAmbushMech(ModState.AmbushTeam, ambushLance, ambushOrigin, building.CurrentPosition, building.CurrentRotation, ModState.BattleArmorAmbushDefForContract.SpawnPool); } else if (ambushType == AmbushType.Mech) { spawnedActor = SpawnAmbushMech(ModState.AmbushTeam, ambushLance, ambushOrigin, building.CurrentPosition, building.CurrentRotation, ModState.MechAmbushDefForContract.SpawnPool); } else if (ambushType == AmbushType.Vehicle) { spawnedActor = SpawnAmbushVehicle(ModState.AmbushTeam, ambushLance, ambushOrigin, building.CurrentPosition, building.CurrentRotation); } spawnedActors.Add(spawnedActor); actorsToSpawn--; // Iterate through adjacent hexes to see if we can spawn more units in the building List <Vector3> adjacentHexes = ModState.Combat.HexGrid.GetGridPointsAroundPointWithinRadius(ambushOrigin, 3); // 3 hexes should cover most large buidlings foreach (Vector3 adjacentHex in adjacentHexes) { if (actorsToSpawn == 0) { break; } Point cellPoint = new Point(ModState.Combat.MapMetaData.GetXIndex(adjacentHex.x), ModState.Combat.MapMetaData.GetZIndex(adjacentHex.z)); Mod.Log.Debug?.Write($" Evaluating point {cellPoint.X}, {cellPoint.Z} for potential ambush."); if (encounterLayerData.mapEncounterLayerDataCells[cellPoint.Z, cellPoint.X].HasSpecifiedBuilding(building.GUID)) { Mod.Log.Debug?.Write($"Spawning actor at adjacent hex at position: {adjacentHex}"); AbstractActor additionalSpawn = null; if (ambushType == AmbushType.BattleArmor) { additionalSpawn = SpawnAmbushMech(ModState.AmbushTeam, ambushLance, ambushOrigin, adjacentHex, building.CurrentRotation, ModState.BattleArmorAmbushDefForContract.SpawnPool); } else if (ambushType == AmbushType.Mech) { additionalSpawn = SpawnAmbushMech(ModState.AmbushTeam, ambushLance, ambushOrigin, adjacentHex, building.CurrentRotation, ModState.MechAmbushDefForContract.SpawnPool); } else if (ambushType == AmbushType.Vehicle) { additionalSpawn = SpawnAmbushVehicle(ModState.AmbushTeam, ambushLance, ambushOrigin, adjacentHex, building.CurrentRotation); } spawnedActors.Add(additionalSpawn); actorsToSpawn--; } else { Mod.Log.Debug?.Write($" Hex {adjacentHex} is outside of the main building, skipping."); } } } // Remove any buildings that are part of this ambush from candidates ModState.CandidateBuildings.RemoveAll(x => buildingsToLevel.Contains(x)); // Determine the targets that should be prioritized by the enemies List <ICombatant> targets = new List <ICombatant>(); foreach (ICombatant combatant in ModState.Combat.GetAllCombatants()) { if (!combatant.IsDead && !combatant.IsFlaggedForDeath && combatant.team != null && ModState.Combat.HostilityMatrix.IsLocalPlayerFriendly(combatant.team)) { if (Vector3.Distance(ambushOrigin, combatant.CurrentPosition) <= Mod.Config.Ambush.SearchRadius) { targets.Add(combatant); } } } bool applyAttacks = false; if (ambushType == AmbushType.BattleArmor && Mod.Config.BattleArmorAmbush.FreeAttackEnabled) { applyAttacks = true; } if (ambushType == AmbushType.Mech && Mod.Config.MechAmbush.FreeAttackEnabled) { applyAttacks = true; } if (ambushType == AmbushType.Vehicle && Mod.Config.VehicleAmbush.FreeAttackEnabled) { applyAttacks = true; } Mod.Log.Info?.Write($"Adding SpawnAmbushSequence for {spawnedActors.Count} actors and {buildingsToLevel.Count} buildings to be leveled."); try { SpawnAmbushSequence ambushSequence = new SpawnAmbushSequence(ModState.Combat, ambushOrigin, spawnedActors, buildingsToLevel, targets, applyAttacks); ModState.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(ambushSequence)); } catch (Exception e) { Mod.Log.Error?.Write(e, "Failed to create AES sequence due to error!"); } }