private string GetGroupName(UnitFamily family) { string name = GeneratorTools.ParseRandomString(Database.Instance.Common.UnitGroupNames[(int)family]); int fakeGroupNumber = NextGroupID * 10 + Toolbox.RandomInt(1, 10); name = name.Replace("$N$", fakeGroupNumber.ToString(NumberFormatInfo.InvariantInfo)); name = name.Replace("$NTH$", Toolbox.GetOrdinalAdjective(fakeGroupNumber)); return(name); }
/// <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> /// Generates the mission briefing. /// </summary> /// <param name="mission">Mission</param> /// <param name="template">Template from which the mission should be built</param> /// <param name="airbaseDB">Airbase player will take off from and land back on</param> /// <param name="coalitionsDB">Database entries for the mission coalitions</param> public void GenerateMissionBriefing(DCSMission mission, MissionTemplate template, DBEntryObjective objectiveDB, DBEntryTheaterAirbase airbaseDB, List <UnitFlightGroupBriefingDescription> flightGroups, DBEntryCoalition[] coalitionsDB) { DebugLog.Instance.WriteLine("Generating mission briefing...", 1); // Get mission features DBEntryMissionFeature[] features = Database.Instance.GetEntries <DBEntryMissionFeature>(objectiveDB.MissionFeatures); string description = objectiveDB.BriefingDescriptionByUnitFamily[(int)mission.Objectives[0].TargetFamily]; if (string.IsNullOrEmpty(description)) // No custom briefing for this target family, use the default { description = objectiveDB.BriefingDescription; } description = GeneratorTools.MakeBriefingStringReplacements(GeneratorTools.ParseRandomString(description), mission, coalitionsDB); description = GeneratorTools.SanitizeString(description); // Generate tasks string baseName = airbaseDB.Name; // TODO: this doesn't work for lots of carriers so simplifying for now List <string> tasks = new List <string> { $"Take off from {baseName}" }; string objectiveTask = GeneratorTools.ParseRandomString(objectiveDB.BriefingTask); for (int i = 0; i < mission.Objectives.Length; i++) { string taskString = GeneratorTools.MakeBriefingStringReplacements(objectiveTask, mission, coalitionsDB, i); tasks.Add(taskString); mission.CoreLuaScript += $"briefingRoom.mission.objectives[{i + 1}].task = \"{taskString}\"\r\n"; } tasks.Add($"Return to {baseName}"); DebugLog.Instance.WriteLine($"{tasks.Count} task(s)", 2); // Generate mission remarks... List <string> remarks = new List <string>(); remarks.AddRange( // ...from objective from string remark in objectiveDB.BriefingRemarks select GeneratorTools.MakeBriefingStringReplacements(GeneratorTools.ParseRandomString(remark), mission, coalitionsDB)); foreach (DBEntryMissionFeature feature in features) { remarks.AddRange( // ...from features from string remark in feature.BriefingRemarks select GeneratorTools.MakeBriefingStringReplacements(GeneratorTools.ParseRandomString(remark), mission, coalitionsDB)); } mission.BriefingHTML = CreateHTMLBriefing(mission, template, description, tasks, remarks, flightGroups, airbaseDB, coalitionsDB, objectiveDB); mission.BriefingTXT = CreateTXTBriefing(mission, description, tasks, remarks, flightGroups, airbaseDB); }
/// <summary> /// Generate a random mission name if none is provided in the template, or returns the provided name if there is one. /// </summary> /// <param name="mission">A mission</param> /// <param name="template">Mission template to use</param> public void GenerateMissionName(DCSMission mission, MissionTemplate template) { DebugLog.Instance.WriteLine("Generating mission name...", 1); string fixedName = GeneratorTools.SanitizeString(template.BriefingName); if (!string.IsNullOrEmpty(fixedName)) // A custom mission name has been provided, use it { mission.MissionName = fixedName; } else // No custom name? Generate one { mission.MissionName = Database.Instance.Common.MissionNameTemplate; for (int i = 0; i < DatabaseCommon.MISSION_NAMES_PART_COUNT; i++) { mission.MissionName = mission.MissionName.Replace($"$P{i + 1}$", Toolbox.RandomFrom(Database.Instance.Common.MissionNameParts[i])); } } DebugLog.Instance.WriteLine($"Mission name set to \"{mission.MissionName}\"", 2); }
/// <summary> /// Generates the mission briefing. /// </summary> /// <param name="mission">Mission</param> /// <param name="template">Template from which the mission should be built</param> /// <param name="airbaseDB">Airbase player will take off from and land back on</param> /// <param name="coalitionsDB">Database entries for the mission coalitions</param> public void GenerateMissionBriefing(DCSMission mission, MissionTemplate template, DBEntryObjective objectiveDB, DBEntryTheaterAirbase airbaseDB, List <UnitFlightGroupBriefingDescription> flightGroups, DBEntryCoalition[] coalitionsDB) { DebugLog.Instance.WriteLine("Generating mission briefing...", 1); // Get mission features DBEntryMissionFeature[] features = Database.Instance.GetEntries <DBEntryMissionFeature>(objectiveDB.MissionFeatures); string description = GeneratorTools.SanitizeString(template.BriefingDescription); if (string.IsNullOrEmpty(description)) // No custom mission description has been provided, generate one { description = objectiveDB.BriefingDescriptionByUnitFamily[(int)mission.Objectives[0].TargetFamily]; if (string.IsNullOrEmpty(description)) // No custom briefing for this target family, use the default { description = objectiveDB.BriefingDescription; } description = GeneratorTools.MakeBriefingStringReplacements(GeneratorTools.ParseRandomString(description), mission, coalitionsDB); } description = GeneratorTools.SanitizeString(description); // Generate tasks string baseName = airbaseDB.Name; // TODO: this doesn't work for lots of carriers so simplifying for now List <string> tasks = new List <string> { $"Take off from {baseName}" }; string objectiveTask = GeneratorTools.ParseRandomString(objectiveDB.BriefingTask); for (int i = 0; i < mission.Objectives.Length; i++) { string taskString = GeneratorTools.MakeBriefingStringReplacements(objectiveTask, mission, coalitionsDB, i); tasks.Add(taskString); mission.CoreLuaScript += $"briefingRoom.mission.objectives[{i + 1}].task = \"{taskString}\"\r\n"; } tasks.Add($"Return to {baseName}"); DebugLog.Instance.WriteLine($"{tasks.Count} task(s)", 2); // Generate mission remarks... List <string> remarks = new List <string>(); remarks.AddRange( // ...from objective from string remark in objectiveDB.BriefingRemarks select GeneratorTools.MakeBriefingStringReplacements(GeneratorTools.ParseRandomString(remark), mission, coalitionsDB)); foreach (DBEntryMissionFeature feature in features) { remarks.AddRange( // ...from features from string remark in feature.BriefingRemarks select GeneratorTools.MakeBriefingStringReplacements(GeneratorTools.ParseRandomString(remark), mission, coalitionsDB)); } /* * // Opposition Remarks * string airDefenseNumbers = template.OppositionAirDefense == AmountN.Random? "Unknown":template.OppositionAirDefense.ToString(); * string airDefenseSkill = template.OppositionSkillLevelGround == BRSkillLevel.Random? "Varied":template.OppositionSkillLevelGround.ToString(); * string airForceNumbers = template.OppositionAirForce == AmountN.Random? "Unknown":template.OppositionAirForce.ToString(); * string airForceSkill = template.OppositionSkillLevelAir == BRSkillLevel.Random? "Varied":template.OppositionSkillLevelAir.ToString(); * string allyAirDefenseNumbers = template.PlayerFriendlyAirDefense == AmountN.Random? "Unknown":template.PlayerFriendlyAirDefense.ToString(); * string allyAirDefenseSkill = template.PlayerAISkillLevel == BRSkillLevel.Random? "Varied":template.PlayerAISkillLevel.ToString(); * remarks.AddRange(new List<string>{ * $"Enemy Air Defenses are {airDefenseNumbers} and they are {airDefenseSkill} troops", * $"Expected Enemy Air Force response is {airForceNumbers} and they are {airForceSkill} pilots", * $"Our Air Defenses are {allyAirDefenseNumbers} and they are {allyAirDefenseSkill} troops",}); * remarks.Add("Use the \"F10/Other\" item in the comms for additional options"); * DebugLog.Instance.WriteLine($"{remarks.Count} remark(s)", 2); */ mission.BriefingHTML = CreateHTMLBriefing(mission, template, description, tasks, remarks, flightGroups, airbaseDB, coalitionsDB, objectiveDB); mission.BriefingTXT = CreateTXTBriefing(mission, description, tasks, remarks, flightGroups, airbaseDB); }
private string CreateHTMLBriefing( DCSMission mission, MissionTemplate template, string description, List <string> tasks, List <string> remarks, List <UnitFlightGroupBriefingDescription> flightGroups, DBEntryTheaterAirbase airbaseDB, DBEntryCoalition[] coalitionsDB, DBEntryObjective objectiveDB) { DebugLog.Instance.WriteLine("Generating HTML mission briefing...", 2); if (!File.Exists(HTML_TEMPLATE_FILE)) // Briefing template not found { DebugLog.Instance.WriteLine("HTML template file not found.", 1, DebugLogMessageErrorLevel.Warning); return("HTML template file not found."); } string briefing = File.ReadAllText(HTML_TEMPLATE_FILE); // Title briefing = briefing.Replace("$MISSIONNAME$", mission.MissionName); briefing = briefing.Replace("$MISSIONTYPE$", GeneratorTools.RemoveAfterComma(objectiveDB.ID) + " mission " + ((template.GetMissionType() == MissionType.SinglePlayer) ? "(single-player)" : $"({template.GetPlayerCount()}-players multiplayer)")); // Situation summary briefing = briefing.Replace("$LONGDATE$", mission.DateTime.ToDateString(true)); briefing = briefing.Replace("$LONGTIME$", mission.DateTime.ToTimeString()); briefing = briefing.Replace("$SHORTDATE$", mission.DateTime.ToDateString(false)); briefing = briefing.Replace("$SHORTTIME$", mission.DateTime.ToTimeString()); briefing = briefing.Replace("$WEATHER$", GeneratorTools.GetEnumString(mission.Weather.WeatherLevel)); briefing = briefing.Replace("$WIND$", GeneratorTools.GetEnumString(mission.Weather.WindLevel)); briefing = briefing.Replace("$WINDSPEED$", mission.Weather.WindSpeedAverage.ToString("F0")); // Friends and enemies briefing = briefing.Replace("$PLAYERCOALITION$", GeneratorTools.RemoveAfterComma(template.GetCoalition(mission.CoalitionPlayer))); briefing = briefing.Replace("$ENEMYCOALITION$", GeneratorTools.RemoveAfterComma(template.GetCoalition(mission.CoalitionEnemy))); // Description briefing = briefing.Replace("$DESCRIPTION$", description.Replace("\n", "<br />")); // Tasks string tasksHTML = ""; foreach (string task in tasks) { tasksHTML += $"<li>{task}</li>"; } briefing = briefing.Replace("$TASKS$", tasksHTML); // Remarks string remarksHTML = ""; foreach (string remark in remarks) { remarksHTML += $"<li>{remark}</li>"; } briefing = briefing.Replace("$REMARKS$", remarksHTML); // Flight groups string flightGroupsHTML = ""; foreach (UnitFlightGroupBriefingDescription fg in flightGroups) { flightGroupsHTML += "<tr>" + $"<td>{fg.Callsign}</td>" + $"<td>{fg.Count}×{fg.Type}</td>" + $"<td>{fg.Task}</td><td>{fg.Radio}</td>" + $"<td>{fg.Remarks}</td>" + "</tr>"; } briefing = briefing.Replace("$FLIGHTGROUPS$", flightGroupsHTML); // Airbases string airbasesHTML = "<tr>" + $"<td>{airbaseDB.Name}</td>" + $"<td>{airbaseDB.Runways}</td>" + $"<td>{airbaseDB.ATC}</td>" + $"<td>{airbaseDB.ILS}</td>" + $"<td>{airbaseDB.TACAN}</td>" + "</tr>"; briefing = briefing.Replace("$AIRBASES$", airbasesHTML); string carrierHTML = ""; foreach (var carrier in mission.Carriers) { carrierHTML += "<tr>" + $"<td>{carrier.Units[0].Name}</td>" + $"<td>{carrier.RadioFrequency.ToString("n3")}{carrier.RadioModulation}</td>" + $"<td>{carrier.ILS}</td>" + $"<td>{carrier.TACAN.ToString()}</td>" + "</tr>"; } briefing = briefing.Replace("$CARRIERS$", carrierHTML); // Waypoints string waypointsHTML = ""; double distance; double totalDistance = 0.0; Coordinates currentPosition = mission.InitialPosition; waypointsHTML += $"<tr><td><strong>TAKEOFF</strong></td><td>-</td><td>-</td></tr>"; foreach (DCSMissionWaypoint wp in mission.Waypoints) { distance = currentPosition.GetDistanceFrom(wp.Coordinates); totalDistance += distance; currentPosition = wp.Coordinates; waypointsHTML += $"<tr><td>{wp.Name}</td>" + $"<td>{GeneratorTools.ConvertDistance(distance, template.BriefingUnitSystem)}</td>" + $"<td>{GeneratorTools.ConvertDistance(totalDistance, template.BriefingUnitSystem)}</td></tr>"; } distance = currentPosition.GetDistanceFrom(mission.InitialPosition); totalDistance += distance; waypointsHTML += $"<tr><td><strong>LANDING</strong></td>" + $"<td>{GeneratorTools.ConvertDistance(distance, template.BriefingUnitSystem)}</td>" + $"<td>{GeneratorTools.ConvertDistance(totalDistance, template.BriefingUnitSystem)}</td></tr>"; briefing = briefing.Replace("$WAYPOINTS$", waypointsHTML); return(briefing); }
/// <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> /// Generates the data for the objectives. /// </summary> /// <param name="mission">The mission for which to generate objectives</param> /// <param name="template">Mission template to use</param> /// <param name="objectiveDB">Objective database entry</param> private void GenerateObjectivesData(DCSMission mission, MissionTemplate template, DBEntryObjective objectiveDB, DBEntryTheater theaterDB) { // Keep in mind the position of the last objective/player location. // Start with initial player location. Coordinates lastCoordinates = mission.InitialPosition; // Common family to use for all objectives if DBEntryObjectiveFlags.SingleTargetUnitFamily is set UnitFamily singleObjectiveUnitFamily = objectiveDB.GetRandomUnitFamily(); for (int i = 0; i < template.ObjectiveCount; i++) { // Pick a random unique name, or a waypoint number if objectives shouldn't be named string objectiveName = PickUniqueObjectiveName(); DebugLog.Instance.WriteLine($"Adding objective #{i + 1}, designated {objectiveName}", 1); // Compute a random distance from last position, in nautical miles double objectiveDistanceNM = (template.ObjectiveDistanceNM == 0) ? Toolbox.RandomInt(TemplateTools.MIN_OBJECTIVE_DISTANCE, TemplateTools.MAX_OBJECTIVE_DISTANCE) : template.ObjectiveDistanceNM; if (i > 0) // Objective is not the first one, spawn it close to the previous objective { objectiveDistanceNM /= 5.0; } MinMaxD distanceFromLast = new MinMaxD(OBJECTIVE_DISTANCE_VARIATION_MIN, OBJECTIVE_DISTANCE_VARIATION_MAX) * objectiveDistanceNM; Coordinates objectiveCoordinates; DBEntryTheaterAirbase?airbase = null; if (objectiveDB.UnitGroup.SpawnPoints[0] != TheaterLocationSpawnPointType.Airbase) { // Look for a valid spawn point DBEntryTheaterSpawnPoint?spawnPoint = SpawnPointSelector.GetRandomSpawnPoint( // If spawn point types are specified, use them. Else look for spawn points of any type (objectiveDB.UnitGroup.SpawnPoints.Length > 0) ? objectiveDB.UnitGroup.SpawnPoints : null, // Select spawn points at a proper distance from last location (previous objective or home airbase) lastCoordinates, distanceFromLast, // Make sure no objective is too close to the initial location mission.InitialPosition, new MinMaxD(objectiveDistanceNM * OBJECTIVE_DISTANCE_VARIATION_MIN, 999999999), GeneratorTools.GetEnemySpawnPointCoalition(template)); // No spawn point found for the objective, abort mission creation. if (!spawnPoint.HasValue) { throw new Exception($"Failed to find a spawn point for objective {i + 1}"); } objectiveCoordinates = spawnPoint.Value.Coordinates; } else { airbase = new MissionGeneratorAirbases().SelectObjectiveAirbase(mission, template, theaterDB, lastCoordinates, distanceFromLast, i == 0); if (!airbase.HasValue) { throw new Exception($"Failed to find a airbase point for objective {i + 1}"); } objectiveCoordinates = airbase.Value.Coordinates; } // Set the waypoint coordinates according the the inaccuracy defined in the objective database entry Coordinates waypointCoordinates = objectiveCoordinates + Coordinates.CreateRandom(objectiveDB.WaypointInaccuracy * Toolbox.NM_TO_METERS); // Select an objective family for the target if any or default to VehicleTransport. UnitFamily objectiveUnitFamily = singleObjectiveUnitFamily; if (!objectiveDB.Flags.HasFlag(DBEntryObjectiveFlags.SingleTargetUnitFamily)) { objectiveUnitFamily = objectiveDB.GetRandomUnitFamily(); } // Set the mission objective mission.Objectives[i] = new DCSMissionObjective( objectiveName, objectiveCoordinates, objectiveUnitFamily, waypointCoordinates, airbase.HasValue? airbase.Value.DCSID: 0); // Last position is now the position of this objective lastCoordinates = objectiveCoordinates; } // If the target is a static object, make sure the correct flag is enabled as it has an influence of some scripts mission.ObjectiveIsStatic = objectiveDB.UnitGroup.Category.HasValue && (objectiveDB.UnitGroup.Category.Value == UnitCategory.Static); // Make sure objectives are ordered by distance from the players' starting location mission.Objectives = mission.Objectives.OrderBy(x => mission.InitialPosition.GetDistanceFrom(x.WaypointCoordinates)).ToArray(); }
/// <summary> /// Generates a <see cref="DCSMission"/> from a <see cref="MissionTemplate"/> /// </summary> /// <param name="template">The <see cref="MissionTemplate"/> to use</param> /// <returns>A <see cref="DCSMission"/>, or nuLL if something when wrong</returns> private DCSMission DoMissionGeneration(MissionTemplate template) { DateTime generationStartTime = DateTime.Now; DebugLog.Instance.Clear(); DebugLog.Instance.WriteLine($"Starting mission generation..."); // Check for missing entries in the database GeneratorTools.CheckDBForMissingEntry <DBEntryCoalition>(template.ContextCoalitionBlue); GeneratorTools.CheckDBForMissingEntry <DBEntryCoalition>(template.ContextCoalitionRed); GeneratorTools.CheckDBForMissingEntry <DBEntryObjective>(template.ObjectiveType); GeneratorTools.CheckDBForMissingEntry <DBEntryTheater>(template.TheaterID); // Create the mission and copy some values (theater database entry ID, etc.) from the template DCSMission mission = new DCSMission(); CopyTemplateValues(mission, template); // Get some DB entries from the database for easier reference DBEntryCoalition[] coalitionsDB = new DBEntryCoalition[2]; coalitionsDB[(int)Coalition.Blue] = Database.Instance.GetEntry <DBEntryCoalition>(template.ContextCoalitionBlue); coalitionsDB[(int)Coalition.Red] = Database.Instance.GetEntry <DBEntryCoalition>(template.ContextCoalitionRed); DBEntryObjective objectiveDB; if (template.ObjectiveType == "Random") { objectiveDB = Toolbox.RandomFrom <DBEntryObjective>(Database.Instance.GetAllEntries <DBEntryObjective>().Where(x => x.ID != "Random").ToArray()); } else { objectiveDB = Database.Instance.GetEntry <DBEntryObjective>(template.ObjectiveType); } DBEntryTheater theaterDB = Database.Instance.GetEntry <DBEntryTheater>(template.TheaterID); // Create the unit maker, which will be used to generate unit groups and their properties UnitMaker unitMaker = new UnitMaker(coalitionsDB, theaterDB); // Create a list of flight group descriptions which will be used in the briefing List <UnitFlightGroupBriefingDescription> briefingFGList = new List <UnitFlightGroupBriefingDescription>(); // Setup airbases DBEntryTheaterAirbase airbaseDB; using (MissionGeneratorAirbases airbaseGen = new MissionGeneratorAirbases()) { airbaseDB = airbaseGen.SelectStartingAirbase(mission, template, theaterDB, objectiveDB); mission.InitialAirbaseID = airbaseDB.DCSID; mission.InitialPosition = airbaseDB.Coordinates; airbaseGen.SetupAirbasesCoalitions(mission, template.TheaterRegionsCoalitions, theaterDB); } // Generate mission objectives DebugLog.Instance.WriteLine("Generating mission objectives..."); using (MissionGeneratorObjectives objectives = new MissionGeneratorObjectives(unitMaker.SpawnPointSelector)) objectives.CreateObjectives(mission, template, objectiveDB, theaterDB); // Generate mission date and time DebugLog.Instance.WriteLine("Generating mission date and time..."); using (MissionGeneratorDateTime dateTime = new MissionGeneratorDateTime()) { dateTime.GenerateMissionDate(mission, template, coalitionsDB); dateTime.GenerateMissionTime(mission, template, theaterDB); } // Generate mission weather DebugLog.Instance.WriteLine("Generating mission weather..."); using (MissionGeneratorWeather weather = new MissionGeneratorWeather()) { weather.GenerateWeather(mission, template.EnvironmentWeather, theaterDB); weather.GenerateWind(mission, template.EnvironmentWind, theaterDB); } // Generate Carrier using (MissionGeneratorCarrier unitGroupGen = new MissionGeneratorCarrier(unitMaker)) unitGroupGen.GenerateCarriers(mission, template, coalitionsDB[(int)mission.CoalitionPlayer]); // Generate player unit groups DebugLog.Instance.WriteLine("Generating player unit groups and mission package..."); string aiEscortTypeCAP, aiEscortTypeSEAD; using (MissionGeneratorPlayerFlightGroups unitGroupGen = new MissionGeneratorPlayerFlightGroups(unitMaker)) briefingFGList.AddRange( unitGroupGen.CreateUnitGroups( mission, template, objectiveDB, coalitionsDB[(int)mission.CoalitionPlayer], out aiEscortTypeCAP, out aiEscortTypeSEAD)); // Generate objective unit groups DebugLog.Instance.WriteLine("Generating objectives unit groups..."); using (MissionGeneratorObjectivesUnitGroups unitGroupGen = new MissionGeneratorObjectivesUnitGroups(unitMaker)) unitGroupGen.CreateUnitGroups(mission, template, objectiveDB, coalitionsDB); // Generate friendly support units DebugLog.Instance.WriteLine("Generating friendly support units..."); using (MissionGeneratorSupportUnits unitGroupGen = new MissionGeneratorSupportUnits(unitMaker)) briefingFGList.AddRange(unitGroupGen.CreateUnitGroups(mission, coalitionsDB[(int)mission.CoalitionPlayer], template.OptionsUnitMods)); // Generate enemy air defense unit groups DebugLog.Instance.WriteLine("Generating enemy air defense unit groups..."); using (MissionGeneratorAirDefense unitGroupGen = new MissionGeneratorAirDefense(unitMaker, false, template, mission)) unitGroupGen.CreateUnitGroups(mission, objectiveDB, coalitionsDB[(int)mission.CoalitionEnemy], GeneratorTools.GetEnemySpawnPointCoalition(template), template.OptionsUnitMods); // Generate ally air defense unit groups DebugLog.Instance.WriteLine("Generating friendly air defense unit groups..."); using (MissionGeneratorAirDefense unitGroupGen = new MissionGeneratorAirDefense(unitMaker, true, template, mission)) unitGroupGen.CreateUnitGroups(mission, objectiveDB, coalitionsDB[(int)mission.CoalitionPlayer], GeneratorTools.GetAllySpawnPointCoalition(template), template.OptionsUnitMods); //// Generate enemy fighter patrols DebugLog.Instance.WriteLine("Generating enemy fighter patrol unit groups..."); using (MissionGeneratorEnemyFighterPatrols unitGroupGen = new MissionGeneratorEnemyFighterPatrols(unitMaker)) unitGroupGen.CreateUnitGroups(mission, template, objectiveDB, coalitionsDB[(int)mission.CoalitionEnemy], aiEscortTypeCAP, aiEscortTypeSEAD); //// Generate mission features DebugLog.Instance.WriteLine("Generating mission features unit groups..."); using (MissionGeneratorExtensionsAndFeatures featuresGen = new MissionGeneratorExtensionsAndFeatures(unitMaker)) featuresGen.GenerateExtensionsAndFeatures(mission, template, objectiveDB, coalitionsDB); // Generates the mission flight plan DebugLog.Instance.WriteLine("Generating mission flight plan..."); using (MissionGeneratorFlightPlan flightPlan = new MissionGeneratorFlightPlan()) { flightPlan.SetBullseye(mission); flightPlan.AddObjectiveWaypoints(mission, objectiveDB); flightPlan.AddExtraWaypoints(mission, template); } // Generate briefing. Must be last because it uses information from other generators DebugLog.Instance.WriteLine("Generating mission briefing..."); using (MissionGeneratorBriefing briefing = new MissionGeneratorBriefing()) { briefing.GenerateMissionName(mission, template); briefing.GenerateMissionBriefing(mission, template, objectiveDB, airbaseDB, briefingFGList, coalitionsDB); } // Set if radio sounds are enabled mission.RadioSounds = !template.OptionsPreferences.Contains(MissionTemplatePreferences.DisableRadioSounds); // Add common .ogg vorbis files and make sure each only appears only once. mission.OggFiles.AddRange(Database.Instance.Common.CommonOGG); mission.OggFiles.AddRange(Database.Instance.Common.CommonOGGForGameMode[(int)template.GetMissionType()]); mission.OggFiles = (from string f in mission.OggFiles where !string.IsNullOrEmpty(f.Trim()) select f.Trim()) .Distinct(StringComparer.InvariantCultureIgnoreCase).ToList(); // If radio sounds are disabled, do not include radio .ogg files to save on file size if (!mission.RadioSounds) { mission.OggFiles = (from string f in mission.OggFiles where (f.ToLowerInvariant() == "radio0") || (!f.ToLowerInvariant().StartsWith("radio")) select f).ToList(); } // Make sure included Lua scripts appear only once mission.IncludedLuaScripts = mission.IncludedLuaScripts.Distinct().OrderBy(x => x).ToList(); // Create aircraft queues and finalize the core script CreateAircraftActivationQueues(mission); switch (template.GetMissionType()) { case MissionType.SinglePlayer: mission.CoreLuaScript += "briefingRoom.mission.missionType = brMissionType.SINGLE_PLAYER\r\n"; break; case MissionType.Cooperative: mission.CoreLuaScript += "briefingRoom.mission.missionType = brMissionType.COOPERATIVE\r\n"; break; case MissionType.Versus: mission.CoreLuaScript += "briefingRoom.mission.missionType = brMissionType.VERSUS\r\n"; break; } DebugLog.Instance.WriteLine($"Mission generation completed successfully in {(DateTime.Now - generationStartTime).TotalSeconds.ToString("F3", NumberFormatInfo.InvariantInfo)} second(s)."); unitMaker.Dispose(); return(mission); }
/// <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> /// 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="coalitionsDB">Coalitions database entries</param> /// <param name="moving">Will the group be moving</param> /// <param name="objectiveGroup">If the group should be tracked as an objective</param> public void SpawnUnitGroups(DCSMission mission, MissionTemplate template, DBUnitGroup unitGroup, DBEntryCoalition[] coalitionsDB, Side side, Coalition coalition) { DCSMissionUnitGroupFlags flags = GeneratorTools.ShouldUnitBeHidden(unitGroup, !template.OptionsPreferences.Contains(MissionTemplatePreferences.HideEnemyUnits)) ? DCSMissionUnitGroupFlags.Hidden : 0; for (int i = 0; i < mission.Objectives.Length; i++) { // This objective requires no unit group generation if (!mission.Objectives[i].TargetFamily.HasValue) { continue; } string[] units = coalitionsDB[(int)coalition].GetRandomUnits( mission.Objectives[i].TargetFamily.Value, mission.DateTime.Decade, unitGroup.Count.GetValue(), template.OptionsUnitMods); // Pick the skill level once for each objective so not all target groups have the same skill level when a "random" skill level is chosen. DCSSkillLevel skillLevel; if (side == Side.Ally) { skillLevel = Toolbox.BRSkillLevelToDCSSkillLevel(template.PlayerAISkillLevel); } else { skillLevel = Toolbox.IsUnitFamilyAircraft(mission.Objectives[i].TargetFamily.Value) ? Toolbox.BRSkillLevelToDCSSkillLevel(template.OppositionSkillLevelAir) : Toolbox.BRSkillLevelToDCSSkillLevel(template.OppositionSkillLevelGround); } DCSMissionUnitGroup group; DBEntryTheaterSpawnPoint?spawnPoint = null; if (unitGroup.SpawnPoints[0] != TheaterLocationSpawnPointType.Airbase) { if (unitGroup.Flags.HasFlag(DBUnitGroupFlags.DestinationObjective)) { spawnPoint = UnitMaker.SpawnPointSelector.GetRandomSpawnPoint( unitGroup.SpawnPoints, mission.Objectives[i].Coordinates, unitGroup.DistanceFromPoint); if (!spawnPoint.HasValue) { throw new Exception($"Failed to find spawn point for moving objective unit"); } } } if (unitGroup.Flags.HasFlag(DBUnitGroupFlags.EmbeddedAirDefense)) // Add "embedded" close range surface-to-air defense { if (Toolbox.GetUnitCategoryFromUnitFamily(mission.Objectives[i].TargetFamily.Value) == UnitCategory.Vehicle) // Objectives are ground vehicles, insert air defense units in the group itself { units = GeneratorTools.AddEmbeddedAirDefense(units, template.OppositionAirDefense, coalitionsDB[(int)coalition], mission.DateTime.Decade, template.OptionsUnitMods); } else // Objectives are not ground vehicles, create another group nearby { // TODO: make sure the group is not spawn in water string[] airDefenseGroupUnits = new string[0]; for (int j = 0; j < 2; j++) { airDefenseGroupUnits = GeneratorTools.AddEmbeddedAirDefense(airDefenseGroupUnits, template.OppositionAirDefense, coalitionsDB[(int)coalition], mission.DateTime.Decade, template.OptionsUnitMods); } UnitMaker.AddUnitGroup( mission, airDefenseGroupUnits, side, (spawnPoint != null ? spawnPoint.Value.Coordinates : mission.Objectives[i].Coordinates) + Coordinates.CreateRandom(0.5, 1.5) * Toolbox.NM_TO_METERS, "GroupVehicle", "UnitVehicle", skillLevel, flags); } } group = UnitMaker.AddUnitGroup( mission, units, side, spawnPoint != null? spawnPoint.Value.Coordinates : mission.Objectives[i].Coordinates, Toolbox.RandomFrom(unitGroup.LuaGroup), unitGroup.LuaUnit, skillLevel, flags, coordinates2: getDestination(unitGroup, mission, i), airbaseID: mission.Objectives[i].AirbaseID, requiresParkingSpots: mission.Objectives[i].AirbaseID > 0, requiresOpenAirParking: unitGroup.Flags.HasFlag(DBUnitGroupFlags.AvoidHardenedBunkers) ); // Something went wrong, abort mission generation, objective unit groups are required for the mission to work properly. if (group == null) { throw new Exception($"Failed to create objective unit group for objective #{i + 1} made of the following units: {string.Join(", ", units)}"); } // Add aircraft group to the queue of aircraft groups to be spawned if (!unitGroup.Flags.HasFlag(DBUnitGroupFlags.ManualActivation) && ((group.Category == UnitCategory.Helicopter) || (group.Category == UnitCategory.Plane) || unitGroup.Flags.HasFlag(DBUnitGroupFlags.DelaySpawn))) { mission.AircraftSpawnQueue.Add(new DCSMissionAircraftSpawnQueueItem(group.GroupID, true)); } if (!unitGroup.Flags.HasFlag(DBUnitGroupFlags.NotObjectiveTarget)) { if (mission.ObjectiveIsStatic) { mission.CoreLuaScript += $"briefingRoom.mission.objectives[{i + 1}].groupID = {group.Units[0].ID}\r\n"; } else { // Add the ID of the unit group associated with this objective to the Lua script mission.CoreLuaScript += $"briefingRoom.mission.objectives[{i + 1}].groupID = {group.GroupID}\r\n"; } } } }
/// <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="coalitionsDB">Coalitions database entries</param> /// <param name="moving">Will the group be moving</param> /// <param name="objectiveGroup">If the group should be tracked as an objective</param> public void SpawnUnitGroups(DCSMission mission, MissionTemplate template, DBUnitGroup unitGroup, DBEntryCoalition[] coalitionsDB, Side side, Coalition coalition) { DCSMissionUnitGroupFlags flags = GeneratorTools.ShouldUnitBeHidden(unitGroup, !template.OptionsPreferences.Contains(MissionTemplatePreferences.HideEnemyUnits)) ? DCSMissionUnitGroupFlags.Hidden : 0; for (int i = 0; i < mission.Objectives.Length; i++) { // This objective requires no unit group generation if (!mission.Objectives[i].TargetFamily.HasValue) { continue; } string[] units = coalitionsDB[(int)coalition].GetRandomUnits( mission.Objectives[i].TargetFamily.Value, mission.DateTime.Decade, unitGroup.Count.GetValue(), template.OptionsUnitMods); if (unitGroup.Flags.HasFlag(DBUnitGroupFlags.EmbeddedAirDefense) && coalition != mission.CoalitionPlayer && (Toolbox.GetUnitCategoryFromUnitFamily(mission.Objectives[i].TargetFamily.Value) == UnitCategory.Vehicle)) { units = GeneratorTools.AddEmbeddedAirDefense(units, template.OppositionAirDefense, coalitionsDB[(int)coalition], mission.DateTime.Decade, template.OptionsUnitMods); } // Pick the skill level once for each objective so not all target groups have the same skill level when a // "random" skill level is chosen. DCSSkillLevel skillLevel = Toolbox.IsUnitFamilyAircraft(mission.Objectives[i].TargetFamily.Value) ? Toolbox.BRSkillLevelToDCSSkillLevel(template.OppositionSkillLevelAir) : Toolbox.BRSkillLevelToDCSSkillLevel(template.OppositionSkillLevelGround); DCSMissionUnitGroup group; DBEntryTheaterSpawnPoint?spawnPoint = null; if (unitGroup.Flags.HasFlag(DBUnitGroupFlags.DestinationObjective)) { spawnPoint = UnitMaker.SpawnPointSelector.GetRandomSpawnPoint( unitGroup.SpawnPoints, mission.Objectives[i].Coordinates, unitGroup.DistanceFromPoint); if (!spawnPoint.HasValue) { throw new Exception($"Failed to find spawn point for moving objective unit"); } } group = UnitMaker.AddUnitGroup( mission, units, side, spawnPoint != null? spawnPoint.Value.Coordinates : mission.Objectives[i].Coordinates, Toolbox.RandomFrom(unitGroup.LuaGroup), unitGroup.LuaUnit, skillLevel, flags, coordinates2: getDestination(unitGroup, mission, i)); // Something went wrong, abort mission generation, objective unit groups are required for the mission to work properly. if (group == null) { throw new Exception($"Failed to create objective unit group for objective #{i + 1} made of the following units: {string.Join(", ", units)}"); } // Add aircraft group to the queue of aircraft groups to be spawned if ((group.Category == UnitCategory.Helicopter) || (group.Category == UnitCategory.Plane) || unitGroup.Flags.HasFlag(DBUnitGroupFlags.DelaySpawn)) { mission.AircraftSpawnQueue.Add(new DCSMissionAircraftSpawnQueueItem(group.GroupID, true)); } if (!unitGroup.Flags.HasFlag(DBUnitGroupFlags.NotObjectiveTarget)) { if (mission.ObjectiveIsStatic) { mission.CoreLuaScript += $"briefingRoom.mission.objectives[{i + 1}].groupID = {group.Units[0].ID}\r\n"; } else { // Add the ID of the unit group associated with this objective to the Lua script mission.CoreLuaScript += $"briefingRoom.mission.objectives[{i + 1}].groupID = {group.GroupID}\r\n"; } } } }
/// <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 }