internal static string[] GetEmbeddedAirDefenseUnits(MissionTemplateRecord template, Side side, Country?country = null) { DBCommonAirDefenseLevel airDefenseInfo = (side == Side.Ally) ? Database.Instance.Common.AirDefense.AirDefenseLevels[(int)template.SituationFriendlyAirDefense.Get()] : Database.Instance.Common.AirDefense.AirDefenseLevels[(int)template.SituationEnemyAirDefense.Get()]; DBEntryCoalition unitsCoalitionDB = Database.Instance.GetEntry <DBEntryCoalition>(template.GetCoalitionID(side)); if (unitsCoalitionDB == null) { return(new string[0]); } List <string> units = new List <string>(); if (Toolbox.RandomDouble() >= airDefenseInfo.EmbeddedChance) { return(new string[0]); } int airDefenseUnitsCount = airDefenseInfo.EmbeddedUnitCount.GetValue(); for (int i = 0; i < airDefenseUnitsCount; i++) { var families = new List <UnitFamily> { UnitFamily.VehicleAAA, UnitFamily.VehicleAAA, UnitFamily.VehicleSAMShortIR, UnitFamily.VehicleSAMShortIR, UnitFamily.VehicleSAMShort }; units.AddRange(unitsCoalitionDB.GetRandomUnits(families, template.ContextDecade, 1, template.Mods, country).Item2); } return(units.ToArray()); }
/// <summary> /// Spawn a group of support units. /// </summary> /// <param name="mission">Mission to which generated units should be added</param> /// <param name="allyCoalitionDB">Ally coalition database entry</param> /// <param name="unitFamily">Family of support unit to spawn</param> /// <param name="unitMods">Unit mods selected units can belong to</param> /// <param name="TACAN">TACAN info for the unit, if any</param> private UnitFlightGroupBriefingDescription AddSupportUnit(DCSMission mission, DBEntryCoalition allyCoalitionDB, UnitFamily unitFamily, string[] unitMods, Tacan TACAN = null) { DebugLog.Instance.WriteLine($"Adding {unitFamily} support unit...", 1); string[] validUnitTypes = allyCoalitionDB.GetRandomUnits(unitFamily, mission.DateTime.Decade, 1, unitMods, false); if (validUnitTypes.Length == 0) { DebugLog.Instance.WriteLine($"No support unit found for this role in coalition \"{allyCoalitionDB.ID}\"", 2); return(new UnitFlightGroupBriefingDescription()); // Empty FG info will automatically be discarded } string groupLua; switch (unitFamily) { case UnitFamily.PlaneAWACS: groupLua = "GroupAircraftAWACS"; break; case UnitFamily.PlaneTankerBasket: case UnitFamily.PlaneTankerBoom: groupLua = "GroupAircraftTanker"; break; default: // Should never happen return(new UnitFlightGroupBriefingDescription()); // Empty FG info will automatically be discarded } Coordinates location = GeneratorTools.GetCoordinatesOnFlightPath(mission, .5) + Coordinates.CreateRandom(8, 12) * Toolbox.NM_TO_METERS; Coordinates location2 = location + Coordinates.CreateRandom(12, 20) * Toolbox.NM_TO_METERS; string unitType = Toolbox.RandomFrom(validUnitTypes); DCSMissionUnitGroup group = UnitMaker.AddUnitGroup( mission, new string[] { unitType }, Side.Ally, location, groupLua, "UnitAircraft", DCSSkillLevel.Excellent, 0, UnitTaskPayload.Default, location2); if (group == null) { return(new UnitFlightGroupBriefingDescription()); // Empty FG info will automatically be discarded } group.TACAN = TACAN; mission.AircraftSpawnQueue.Insert(0, new DCSMissionAircraftSpawnQueueItem(group.GroupID, true)); // Support aircraft must be activated first return(new UnitFlightGroupBriefingDescription( group.Name, group.Units.Length, unitType, (unitFamily == UnitFamily.PlaneAWACS) ? "Early warning" : "Refueling", Database.Instance.GetEntry <DBEntryUnit>(unitType).AircraftData.GetRadioAsString(), TACAN != null? $"TACAN: {TACAN.ToString()}":"")); }
/// <summary> /// Adds "embedded" short-range air-defense units to an unit group. /// </summary> /// <param name="units">Array of units in the group</param> /// <param name="airDefenseLevel">Air defense level setting to use, from the mission template</param> /// <param name="coalitionDB">Database entry for the coalition to use for air-defense units</param> /// <param name="decade">Decade during which the units must be operated</param> /// <param name="unitMods">Unit mods the units can belong to</param> /// <returns>Updated array of units with added embedded air defense units</returns> public static string[] AddEmbeddedAirDefense(string[] units, AmountN airDefenseLevel, DBEntryCoalition coalitionDB, Decade decade, string[] unitMods) { int airDefenseLevelInt = (int)airDefenseLevel.Get(); // No luck this time, don't add anything if (Toolbox.RandomDouble() >= Database.Instance.Common.EnemyAirDefense[airDefenseLevelInt].EmbeddedChance) { return(units); } // Convert the unit array to an open-ended list so that units can be added List <string> unitsList = new List <string>(units); // Add some air defense units int embeddedCount = Database.Instance.Common.EnemyAirDefense[airDefenseLevelInt].EmbeddedUnitCount.GetValue(); for (int i = 0; i < embeddedCount; i++) { unitsList.AddRange( coalitionDB.GetRandomUnits(Toolbox.RandomFrom(EMBEDDED_AIR_DEFENSE_FAMILIES), decade, 1, unitMods)); } if (unitsList.Count == 0) { return(new string[0]); } // Randomize unit order so embbedded air defense units are not always at the end of the group // but keep unit #0 at its place, because the first unit of the group is used to determine the group type, and we don't want // a artillery platoon to be named "air defense bataillon" because the first unit is a AAA. string unit0 = unitsList[0]; unitsList.RemoveAt(0); unitsList = unitsList.OrderBy(x => Toolbox.RandomInt()).ToList(); unitsList.Insert(0, unit0); return(unitsList.ToArray()); }
internal UnitMakerGroupInfo?AddUnitGroup( List <UnitFamily> families, int unitCount, Side side, string groupLua, string unitLua, Coordinates coordinates, UnitMakerGroupFlags unitMakerGroupFlags = 0, params KeyValuePair <string, object>[] extraSettings) { if (unitCount <= 0) { throw new BriefingRoomException("Asking for a zero units"); } if (families.Count <= 0) { throw new BriefingRoomException("No Unit Families Provided"); } DBEntryCoalition unitsCoalitionDB = CoalitionsDB[(int)((side == Side.Ally) ? PlayerCoalition : PlayerCoalition.GetEnemy())]; var(country, units) = unitsCoalitionDB.GetRandomUnits(families, Template.ContextDecade, unitCount, Template.Mods); if (units.Count == 0) { throw new BriefingRoomException($"Found no units for {string.Join(", ", families)} {country}"); } if (country != Country.ALL) { extraSettings = extraSettings.Append("Country".ToKeyValuePair(country)).ToArray(); } if (unitMakerGroupFlags.HasFlag(UnitMakerGroupFlags.EmbeddedAirDefense) && (families.First().GetUnitCategory() == UnitCategory.Vehicle)) { string[] airDefenseUnits = GeneratorTools.GetEmbeddedAirDefenseUnits(Template, side, country != Country.ALL ? country : null); units.AddRange(airDefenseUnits); } return(AddUnitGroup(Toolbox.ShuffleArray(units.ToArray()), side, families.First(), groupLua, unitLua, coordinates, unitMakerGroupFlags, extraSettings)); }
/// <summary> /// Creates an AI escort flight group in a single-player mission. /// </summary> /// <param name="mission">Mission to which generated units should be added</param> /// <param name="template">Mission template to use</param> /// <param name="count">Number of aircraft in the flight group</param> /// <param name="task">Escort task the flight group will be assigned with</param> /// <param name="playerCoalitionDB">Player coalition database entry</param> /// <returns>A <see cref="UnitFlightGroupBriefingDescription"/> describing the flight group, to be used in the briefing</returns> private UnitFlightGroupBriefingDescription?GenerateAIEscort(DCSMission mission, MissionTemplate template, int count, MissionTemplateFlightGroupTask task, DBEntryCoalition playerCoalitionDB) { if (count < 1) { return(null); // No aircraft, nothing to generate. } // Select proper payload for the flight group according to its tasking UnitTaskPayload payload = GetPayloadByTask(task, null); string groupLua; string[] aircraft; switch (task) { default: return(null); // Should never happen case MissionTemplateFlightGroupTask.SupportCAP: //groupLua = (template.GetMissionType() == MissionType.SinglePlayer) ? "GroupAircraftPlayerEscortCAP" : "GroupAircraftCAP"; groupLua = "GroupAircraftCAP"; aircraft = playerCoalitionDB.GetRandomUnits(UnitFamily.PlaneFighter, mission.DateTime.Decade, count, template.UnitMods); break; case MissionTemplateFlightGroupTask.SupportSEAD: //groupLua = (template.GetMissionType() == MissionType.SinglePlayer) ? "GroupAircraftPlayerEscortSEAD" : "GroupAircraftSEAD"; groupLua = "GroupAircraftSEAD"; aircraft = playerCoalitionDB.GetRandomUnits(UnitFamily.PlaneSEAD, mission.DateTime.Decade, count, template.UnitMods); break; } Coordinates position = mission.InitialPosition; // Player starts on runway, so escort starts in the air above the airfield (so player doesn't have to wait for them to take off) // OR mission is MP, so escorts start in air (but won't be spawned until at least one player takes off) // Add a random distance so they don't crash into each other. if ((template.PlayerFlightGroups[0].StartLocation == PlayerStartLocation.Runway) || (template.MissionType != MissionType.SinglePlayer)) { position += Coordinates.CreateRandom(2, 4) * Toolbox.NM_TO_METERS; } DCSMissionUnitGroup group; //if (template.GetMissionType() == MissionType.SinglePlayer) // group = UnitMaker.AddUnitGroup( // mission, aircraft, // Side.Ally, position, // groupLua, "UnitAircraft", // Toolbox.BRSkillLevelToDCSSkillLevel(template.PlayerAISkillLevel), 0, // payload, null, mission.InitialAirbaseID, true); //else group = UnitMaker.AddUnitGroup( mission, aircraft, Side.Ally, position, groupLua, "UnitAircraft", Toolbox.BRSkillLevelToDCSSkillLevel(template.SituationFriendlyAISkillLevel), 0, payload, mission.ObjectivesCenter); if (group == null) { DebugLog.Instance.WriteLine($"Failed to create AI escort flight group tasked with {task} with aircraft of type \"{aircraft[0]}\".", 1, DebugLogMessageErrorLevel.Warning); return(null); } switch (task) { default: return(null); // Should never happen case MissionTemplateFlightGroupTask.SupportCAP: mission.EscortCAPGroupId = group.GroupID; break; case MissionTemplateFlightGroupTask.SupportSEAD: mission.EscortSEADGroupId = group.GroupID; break; } return (new UnitFlightGroupBriefingDescription( group.Name, group.Units.Length, aircraft[0], GetTaskingDescription(task, null), Database.Instance.GetEntry <DBEntryUnit>(aircraft[0]).AircraftData.GetRadioAsString())); }
/// <summary> /// /// </summary> /// <param name="mission"></param> /// <param name="template"></param> /// <param name="playerCoalitionDB"></param> /// <param name="windDirection0">Wind direction at altitude 0, in degrees. Used by carrier groups to make sure carriers sail into the wind.</param> /// <returns></returns> public void GenerateCarriers(DCSMission mission, MissionTemplate template, DBEntryCoalition playerCoalitionDB) { var carriers = new string[] {}; if (template.GetMissionType() == MissionType.SinglePlayer) { if (string.IsNullOrEmpty(template.PlayerSPCarrier)) { return; } carriers = carriers.Append(template.PlayerSPCarrier).ToArray(); } else { carriers = template.PlayerMPFlightGroups.Aggregate(new string[] {}, (acc, x) => !string.IsNullOrEmpty(x.Carrier)? acc.Append(x.Carrier).ToArray() : acc); } if (carriers.Length == 0) { return; } foreach (string carrier in carriers) { DBEntryTheaterSpawnPoint?spawnPoint = UnitMaker.SpawnPointSelector.GetRandomSpawnPoint( // If spawn point types are specified, use them. Else look for spawn points of any type new TheaterLocationSpawnPointType[] { TheaterLocationSpawnPointType.Sea }, // Select spawn points at a proper distance from last location (previous objective or home airbase) mission.InitialPosition, new MinMaxD(10, 50), // Make sure no objective is too close to the initial location null, null, GeneratorTools.GetAllySpawnPointCoalition(template)); if (!spawnPoint.HasValue) { throw new Exception($"Failed to find a spawn point for Carrier"); } Coordinates position = mission.InitialPosition; DCSMissionUnitGroup group; string[] ships = new string[] { carrier }; foreach (var ship in new UnitFamily[] { UnitFamily.ShipFrigate, UnitFamily.ShipFrigate, UnitFamily.ShipCruiser, UnitFamily.ShipCruiser, UnitFamily.ShipTransport }) { ships = ships.Append(playerCoalitionDB.GetRandomUnits(ship, mission.DateTime.Decade, 1, template.OptionsUnitMods)[0]).ToArray(); } DebugLog.Instance.WriteLine($"Ships to be spawned {ships.Aggregate((acc, x) => $"{acc}, {x}")}", 1, DebugLogMessageErrorLevel.Warning); group = UnitMaker.AddUnitGroup( mission, ships, Side.Ally, spawnPoint.Value.Coordinates, "GroupCarrier", "UnitShip", Toolbox.BRSkillLevelToDCSSkillLevel(template.PlayerAISkillLevel)); if (group == null) { DebugLog.Instance.WriteLine($"Failed to create AI Carrier with ship of type \"{carrier}\".", 1, DebugLogMessageErrorLevel.Warning); } else { //set all units against the wind int windDirection = mission.Weather.WindDirection[0]; double WindSpeed = mission.Weather.WindSpeed[0]; double windOverDeck = 12.8611; // 25kts group.Speed = windOverDeck - WindSpeed; if (group.Speed <= 2.6) { group.Speed = 2.57222; //5kts } double heading = Toolbox.ClampAngle((windDirection + 180) * Toolbox.DEGREES_TO_RADIANS); foreach (DCSMissionUnitGroupUnit unit in group.Units) { unit.Heading = heading; } group.Units[0].Coordinates = group.Coordinates; group.Coordinates2 = Coordinates.FromAngleAndDistance(group.Coordinates, (group.Speed * Toolbox.METERS_PER_SECOND_TO_KNOTS) * Toolbox.NM_TO_METERS, heading); } string cvnId = mission.Carriers.Length > 0? (mission.Carriers.Length + 1).ToString() : ""; group.TACAN = new Tacan(74 + mission.Carriers.Length, $"CVN{cvnId}"); group.ILS = 11 + mission.Carriers.Length; group.RadioFrequency = 127.5f + mission.Carriers.Length; mission.Carriers = mission.Carriers.Append(group).ToArray(); } return; }
/// <summary> /// Add surface-to-air defense groups. /// </summary> /// <param name="mission">Mission to which generated units should be added</param> /// <param name="template">Mission template to use</param> /// <param name="airDefenseRange">Air-defense range category</param> /// <param name="enemyCoalitionDB">Enemy coalition database entry</param> /// <param name="coalition">Coalition of the spawn points air defense must be spawned at, or null to spawn them anywhere</param> /// <param name="unitMods">Unit mods the units can belong to</param> private void AddAirDefenseUnits(DCSMission mission, AirDefenseRange airDefenseRange, DBEntryCoalition enemyCoalitionDB, Coalition?coalition, string[] unitMods) { // Get the proper number of groups int groupCount = Database.Instance.Common. EnemyAirDefense[(int)airDefense].GroupsInArea[(int)airDefenseRange].GetValue(); if (groupCount < 1) { return; // No groups to add, no need to go any further } DCSMissionUnitGroupFlags flags = optionsShowEnemyUnits; UnitFamily[] unitFamilies; TheaterLocationSpawnPointType[] validSpawnPoints; switch (airDefenseRange) { default: // case AirDefenseRange.ShortRange: unitFamilies = new UnitFamily[] { UnitFamily.VehicleAAA, UnitFamily.VehicleAAAStatic, UnitFamily.VehicleInfantryMANPADS, UnitFamily.VehicleSAMShort, UnitFamily.VehicleSAMShort, UnitFamily.VehicleSAMShortIR, UnitFamily.VehicleSAMShortIR }; validSpawnPoints = new TheaterLocationSpawnPointType[] { TheaterLocationSpawnPointType.LandSmall, TheaterLocationSpawnPointType.LandMedium, TheaterLocationSpawnPointType.LandLarge }; break; case AirDefenseRange.MediumRange: unitFamilies = new UnitFamily[] { UnitFamily.VehicleSAMMedium }; validSpawnPoints = new TheaterLocationSpawnPointType[] { TheaterLocationSpawnPointType.LandMedium, TheaterLocationSpawnPointType.LandLarge }; break; case AirDefenseRange.LongRange: unitFamilies = new UnitFamily[] { UnitFamily.VehicleSAMLong }; validSpawnPoints = new TheaterLocationSpawnPointType[] { TheaterLocationSpawnPointType.LandLarge }; break; } for (int i = 0; i < groupCount; i++) { // Find spawn point at the proper distance from the objective(s), but not to close from starting airbase DBEntryTheaterSpawnPoint?spawnPoint = UnitMaker.SpawnPointSelector.GetRandomSpawnPoint( validSpawnPoints, centerPoint, distsFromCenter[(int)airDefenseRange], opposingPoint, new MinMaxD(minDistFromOpposingPoint[(int)airDefenseRange], 99999), coalition); // No spawn point found, stop here. if (!spawnPoint.HasValue) { DebugLog.Instance.WriteLine($"No spawn point found for {airDefenseRange} air defense unit groups", 1, DebugLogMessageErrorLevel.Warning); return; } string[] units = enemyCoalitionDB.GetRandomUnits(Toolbox.RandomFrom(unitFamilies), mission.DateTime.Decade, 1, unitMods); DCSMissionUnitGroup group = UnitMaker.AddUnitGroup( mission, units, ally? Side.Ally : Side.Enemy, spawnPoint.Value.Coordinates, "GroupVehicle", "UnitVehicle", Toolbox.BRSkillLevelToDCSSkillLevel(skillLevel), flags); if (group == null) { DebugLog.Instance.WriteLine($"Failed to add {airDefenseRange} air defense unit group of type {units[0]}", 1, DebugLogMessageErrorLevel.Warning); } } }
/// <summary> /// /// </summary> /// <param name="mission"></param> /// <param name="template"></param> /// <param name="playerCoalitionDB"></param> /// <param name="windDirection0">Wind direction at altitude 0, in degrees. Used by carrier groups to make sure carriers sail into the wind.</param> /// <returns></returns> public DBEntryUnit GenerateCarrier(DCSMission mission, MissionTemplate template, DBEntryCoalition playerCoalitionDB, int windDirection0) { if (string.IsNullOrEmpty(template.PlayerCarrier)) { return(null); } DBEntryTheaterSpawnPoint?spawnPoint = UnitMaker.SpawnPointSelector.GetRandomSpawnPoint( // If spawn point types are specified, use them. Else look for spawn points of any type new TheaterLocationSpawnPointType[] { TheaterLocationSpawnPointType.Sea }, // Select spawn points at a proper distance from last location (previous objective or home airbase) mission.InitialPosition, new MinMaxD(10, 50), // Make sure no objective is too close to the initial location null, null, GeneratorTools.GetAllySpawnPointCoalition(template)); if (!spawnPoint.HasValue) { throw new Exception($"Failed to find a spawn point for Carrier"); } Coordinates position = mission.InitialPosition; DCSMissionUnitGroup group; string[] ships = new string[] { template.PlayerCarrier }; foreach (var ship in new UnitFamily[] { UnitFamily.ShipFrigate, UnitFamily.ShipFrigate, UnitFamily.ShipCruiser, UnitFamily.ShipCruiser, UnitFamily.ShipTransport }) { ships = ships.Append(playerCoalitionDB.GetRandomUnits(ship, mission.DateTime.Decade, 1, template.OptionsUnitMods)[0]).ToArray(); } DebugLog.Instance.WriteLine($"Ships to be spawned {ships.Aggregate((acc, x) => $"{acc}, {x}")}", 1, DebugLogMessageErrorLevel.Warning); group = UnitMaker.AddUnitGroup( mission, ships, Side.Ally, spawnPoint.Value.Coordinates, "GroupCarrier", "UnitShip", Toolbox.BRSkillLevelToDCSSkillLevel(template.PlayerAISkillLevel)); if (group == null) { DebugLog.Instance.WriteLine($"Failed to create AI Carrier with ship of type \"{template.PlayerCarrier}\".", 1, DebugLogMessageErrorLevel.Warning); } else { //set all units against the wind double heading = Toolbox.ClampAngle((windDirection0 + 180) * Toolbox.DEGREES_TO_RADIANS); foreach (DCSMissionUnitGroupUnit unit in group.Units) { unit.Heading = heading; } } mission.Carrier = group.Units[0]; return((from DBEntryUnit unit in Database.Instance.GetAllEntries <DBEntryUnit>() where unit.ID == template.PlayerCarrier select unit).ToArray()[0]); }
/// <summary> /// Main unit generation method. /// </summary> /// <param name="mission">Mission to which generated units should be added</param> /// <param name="template">Mission template to use</param> /// <param name="objectiveDB">Mission objective database entry</param> /// <param name="enemyCoalitionDB">Enemy coalition database entry</param> /// <param name="aiEscortTypeCAP">Type of aircraft selected for player AI CAP escort (single-player only)</param> /// <param name="aiEscortTypeSEAD">Type of aircraft selected for player AI SEAD escort (single-player only)</param> public void CreateUnitGroups(DCSMission mission, MissionTemplate template, DBEntryObjective objectiveDB, DBEntryCoalition enemyCoalitionDB, string aiEscortTypeCAP, string aiEscortTypeSEAD) { if (objectiveDB.Flags.HasFlag(DBEntryObjectiveFlags.NoEnemyCAP)) { DebugLog.Instance.WriteLine("Enemy CAP disabled for this mission objective type, not spawning any units", 1); return; } int totalAirForcePower = (int)(GetMissionPackageAirPower(template, objectiveDB, aiEscortTypeCAP, aiEscortTypeSEAD) * Database.Instance.Common.EnemyCAPRelativePower[(int)template.SituationEnemyAirForce.Get()]); DebugLog.Instance.WriteLine($"Enemy air power set to {totalAirForcePower}...", 1); DCSMissionUnitGroupFlags flags = template.Realism.Contains(RealismOption.HideEnemyUnits) ? DCSMissionUnitGroupFlags.Hidden : 0; int aircraftCount = 0; int groupCount = 0; while (totalAirForcePower > 0) { string[] unitTypes = enemyCoalitionDB.GetRandomUnits(UnitFamily.PlaneFighter, mission.DateTime.Decade, 1, template.UnitMods); if (unitTypes.Length == 0) { DebugLog.Instance.WriteLine("No valid units found for enemy fighter patrols.", 1, DebugLogMessageErrorLevel.Warning); break; } // Find spawn point at the proper distance from the objective(s), but not to close from starting airbase DBEntryTheaterSpawnPoint?spawnPoint = UnitMaker.SpawnPointSelector.GetRandomSpawnPoint( null, mission.ObjectivesCenter, Database.Instance.Common.EnemyCAPDistanceFromObjectives, mission.InitialPosition, new MinMaxD(Database.Instance.Common.EnemyCAPMinDistanceFromTakeOffLocation, 99999), GeneratorTools.GetEnemySpawnPointCoalition(template)); if (!spawnPoint.HasValue) // No spawn point found, stop here. { DebugLog.Instance.WriteLine("No spawn point found for enemy fighter patrol group.", 1, DebugLogMessageErrorLevel.Warning); break; } int unitPower = Database.Instance.GetEntry <DBEntryUnit>(unitTypes[0]).AircraftData.AirToAirRating[1]; int groupSize = 1; if (totalAirForcePower >= unitPower * 2) { groupSize = 2; } if (Toolbox.RandomDouble() < .3) { if (totalAirForcePower >= unitPower * 4) { groupSize = 4; } else if (totalAirForcePower >= unitPower * 3) { groupSize = 3; } } totalAirForcePower -= unitPower * groupSize; DCSMissionUnitGroup group = UnitMaker.AddUnitGroup( mission, Enumerable.Repeat(unitTypes[0], groupSize).ToArray(), Side.Enemy, spawnPoint.Value.Coordinates, "GroupAircraftCAP", "UnitAircraft", Toolbox.BRSkillLevelToDCSSkillLevel(template.SituationEnemySkillLevelAir), flags, UnitTaskPayload.AirToAir, mission.ObjectivesCenter + Coordinates.CreateRandom(20, 40) * Toolbox.NM_TO_METERS); if (group == null) { DebugLog.Instance.WriteLine($"Failed to add a group of {groupSize}× {unitTypes[0]} at {spawnPoint.Value.Coordinates}", 1, DebugLogMessageErrorLevel.Warning); } else { DebugLog.Instance.WriteLine($"Added a group of {groupSize}× {unitTypes[0]} at {spawnPoint.Value.Coordinates}"); mission.AircraftSpawnQueue.Add(new DCSMissionAircraftSpawnQueueItem(group.GroupID, template.SituationEnemyCAPOnStationChance.RollChance())); } aircraftCount += groupSize; groupCount++; } }
/// <summary> /// Spawn a group of support units. /// </summary> /// <param name="mission">Mission to which generated units should be added</param> /// <param name="enemyCoalitionDB">Ally coalition database entry</param> /// <param name="unitFamily">Family of support unit to spawn</param> /// <param name="unitMods">Unit mods selected units can belong to</param> /// <param name="TACAN">TACAN info for the unit, if any</param> private void AddSupportUnit(DCSMission mission, MissionTemplate template, DBEntryCoalition enemyCoalitionDB, UnitFamily unitFamily, string[] unitMods, Tacan TACAN = null) { DebugLog.Instance.WriteLine($"Adding {unitFamily} enemy support unit...", 1); string[] validUnitTypes = enemyCoalitionDB.GetRandomUnits(unitFamily, mission.DateTime.Decade, 1, unitMods, false); if (validUnitTypes.Length == 0) { DebugLog.Instance.WriteLine($"No support unit found for this role in coalition \"{enemyCoalitionDB.ID}\"", 2); return; // Empty FG info will automatically be discarded } string groupLua; switch (unitFamily) { case UnitFamily.PlaneAWACS: groupLua = "GroupAircraftAWACSMortal"; break; case UnitFamily.PlaneTankerBasket: case UnitFamily.PlaneTankerBoom: groupLua = "GroupAircraftTankerMortal"; break; default: // Should never happen return; // Empty FG info will automatically be discarded } DBEntryTheaterSpawnPoint?spawnPoint = UnitMaker.SpawnPointSelector.GetRandomSpawnPoint( null, mission.ObjectivesCenter, Database.Instance.Common.EnemyCAPDistanceFromObjectives, mission.InitialPosition, new MinMaxD(Database.Instance.Common.EnemyCAPMinDistanceFromTakeOffLocation, 99999), GeneratorTools.GetEnemySpawnPointCoalition(template)); if (!spawnPoint.HasValue) // No spawn point found, stop here. { DebugLog.Instance.WriteLine("No spawn point found for enemy fighter patrol group.", 1, DebugLogMessageErrorLevel.Warning); return; } Coordinates location2 = spawnPoint.Value.Coordinates + Coordinates.CreateRandom(12, 20) * Toolbox.NM_TO_METERS; string unitType = Toolbox.RandomFrom(validUnitTypes); DCSMissionUnitGroup group = UnitMaker.AddUnitGroup( mission, new string[] { unitType }, Side.Enemy, spawnPoint.Value.Coordinates, groupLua, "UnitAircraft", Toolbox.BRSkillLevelToDCSSkillLevel(template.SituationEnemySkillLevelAir), 0, UnitTaskPayload.Default, location2); if (group == null) { return; // Empty FG info will automatically be discarded } group.TACAN = TACAN; mission.AircraftSpawnQueue.Insert(0, new DCSMissionAircraftSpawnQueueItem(group.GroupID, true)); // Support aircraft must be activated first }