/// <summary> /// Generates the <see cref="DCSMissionObjective"/>. /// </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> public void CreateObjectives(DCSMission mission, MissionTemplate template, DBEntryObjective objectiveDB, DBEntryTheater theaterDB) { // Set the array for the proper number of objective mission.Objectives = new DCSMissionObjective[template.ObjectiveCount]; GenerateObjectivesData(mission, template, objectiveDB, theaterDB); GenerateObjectivesScript(mission); }
/// <summary> /// Decide what should be spawned. /// </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> public void CreateUnitGroups(DCSMission mission, MissionTemplate template, DBEntryObjective objectiveDB, DBEntryCoalition[] coalitionsDB) { Coalition coalition = objectiveDB.UnitGroup.Flags.HasFlag(DBUnitGroupFlags.Friendly) ? mission.CoalitionPlayer : mission.CoalitionEnemy; Side side = objectiveDB.UnitGroup.Flags.HasFlag(DBUnitGroupFlags.Friendly) ? Side.Ally : Side.Enemy; SpawnUnitGroups(mission, template, objectiveDB.UnitGroup, coalitionsDB, side, coalition); if (objectiveDB.AllyUnitGroup.Category != null) { DebugLog.Instance.WriteLine($"Generating Friendly units for objective"); SpawnUnitGroups(mission, template, objectiveDB.AllyUnitGroup, coalitionsDB, Side.Ally, mission.CoalitionPlayer); } }
/// <summary> /// Adds waypoints for the mission objectives. /// </summary> /// <param name="mission">Mission</param> /// <param name="objectiveDB">Objective database entry</param> public void AddObjectiveWaypoints(DCSMission mission, DBEntryObjective objectiveDB) { DebugLog.Instance.WriteLine("Generating objective waypoints...", 1); for (int i = 0; i < mission.Objectives.Length; i++) { Coordinates waypointCoordinates = mission.Objectives[i].WaypointCoordinates; DebugLog.Instance.WriteLine($"Created objective waypoint at {waypointCoordinates}", 2); mission.Waypoints.Add( new DCSMissionWaypoint( waypointCoordinates, mission.Objectives[i].Name, objectiveDB.WaypointOnGround ? 0.0 : 1.0, 1.0)); } }
/// <summary> /// Main unit generation method. /// </summary> /// <param name="mission">Mission to which generated units should be added</param> /// <param name="objectiveDB">Mission objective database entry</param> /// <param name="coalitionDB">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> public void CreateUnitGroups(DCSMission mission, DBEntryObjective objectiveDB, DBEntryCoalition coalitionDB, Coalition?coalition, string[] unitMods) { foreach (AirDefenseRange airDefenseRange in (AirDefenseRange[])Enum.GetValues(typeof(AirDefenseRange))) { DebugLog.Instance.WriteLine($"Adding {airDefenseRange} air defense", 1); if ( ((airDefenseRange == AirDefenseRange.ShortRange) && objectiveDB.Flags.HasFlag(DBEntryObjectiveFlags.NoEnemyAirDefenseShort)) || ((airDefenseRange == AirDefenseRange.MediumRange) && objectiveDB.Flags.HasFlag(DBEntryObjectiveFlags.NoEnemyAirDefenseMedium)) || ((airDefenseRange == AirDefenseRange.LongRange) && objectiveDB.Flags.HasFlag(DBEntryObjectiveFlags.NoEnemyAirDefenseLong))) { DebugLog.Instance.WriteLine($"{airDefenseRange} air defense disabled for this mission objective type, not spawning any units", 1); continue; } AddAirDefenseUnits(mission, airDefenseRange, coalitionDB, coalition, unitMods); } }
/// <summary> /// Return a string describing the task of a player flight group, to display in the flight group table part of the briefing. /// </summary> /// <param name="task">Task assigned to this flight group in the mission package</param> /// <param name="objectiveDB">(optional) Mission objective database entry</param> /// <returns>Name of task as it will appear in the flight group table</returns> private string GetTaskingDescription(MissionTemplateFlightGroupTask task, DBEntryObjective objectiveDB) { switch (task) { default: // case MissionTemplateMPFlightGroupTask.Objectives if (objectiveDB == null) { return("Mission objectives"); } return(objectiveDB.BriefingTaskFlightGroup); case MissionTemplateFlightGroupTask.SupportCAP: return("CAP escort"); case MissionTemplateFlightGroupTask.SupportSEAD: return("SEAD escort"); } }
/// <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="playerCoalitionDB">Player coalition database entry</param> /// <param name="aiEscortTypeCAP">Type of aircraft selected for AI CAP escort</param> /// <param name="aiEscortTypeSEAD">Type of aircraft selected for AI SEAD escort</param> /// <returns>An array of <see cref="UnitFlightGroupBriefingDescription"/> describing the flight groups, to be used in the briefing</returns> public UnitFlightGroupBriefingDescription[] CreateUnitGroups(DCSMission mission, MissionTemplate template, DBEntryObjective objectiveDB, DBEntryCoalition playerCoalitionDB, out string aiEscortTypeCAP, out string aiEscortTypeSEAD) { List <UnitFlightGroupBriefingDescription> briefingFGList = new List <UnitFlightGroupBriefingDescription>(); if (template.MissionType == MissionType.SinglePlayer) { briefingFGList.Add(GenerateSinglePlayerFlightGroup(mission, template, objectiveDB)); } else { briefingFGList.AddRange(GenerateMultiplayerFlightGroups(mission, template, objectiveDB)); } aiEscortTypeCAP = ""; aiEscortTypeSEAD = ""; UnitFlightGroupBriefingDescription?escortDescription; escortDescription = GenerateAIEscort(mission, template, template.SituationFriendlyEscortCAP, MissionTemplateFlightGroupTask.SupportCAP, playerCoalitionDB); if (escortDescription.HasValue) { briefingFGList.Add(escortDescription.Value); aiEscortTypeCAP = escortDescription.Value.Type; } escortDescription = GenerateAIEscort(mission, template, template.SituationFriendlyEscortSEAD, MissionTemplateFlightGroupTask.SupportSEAD, playerCoalitionDB); if (escortDescription.HasValue) { briefingFGList.Add(escortDescription.Value); aiEscortTypeSEAD = escortDescription.Value.Type; } return(briefingFGList.ToArray()); }
/// <summary> /// Returns the proper payload type for a given task. /// </summary> /// <param name="task">Task assigned to this flight group in the mission package</param> /// <param name="objectiveDB">(optional) Mission objective database entry</param> /// <returns>A payload</returns> private UnitTaskPayload GetPayloadByTask(MissionTemplateFlightGroupTask task, DBEntryObjective objectiveDB = null) { switch (task) { default: // case MissionTemplateMPFlightGroupTask.Objectives if (objectiveDB == null) { return(UnitTaskPayload.Default); } return(objectiveDB.Payload); case MissionTemplateFlightGroupTask.SupportCAP: return(UnitTaskPayload.AirToAir); case MissionTemplateFlightGroupTask.SupportSEAD: return(UnitTaskPayload.SEAD); } }
/// <summary> /// Creates the flight group player will lead 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="objectiveDB">Mission objective database entry</param> /// <returns>A <see cref="UnitFlightGroupBriefingDescription"/> describing the flight group, to be used in the briefing</returns> private UnitFlightGroupBriefingDescription GenerateSinglePlayerFlightGroup(DCSMission mission, MissionTemplate template, DBEntryObjective objectiveDB) { var playerFlightGroup = template.PlayerFlightGroups[0]; bool isCarrier = !string.IsNullOrEmpty(playerFlightGroup.Carrier); DebugLog.Instance.WriteLine($"{playerFlightGroup.Carrier} -> {string.Join(",",mission.Carriers.Select(x => x.Name).ToArray())}"); DCSMissionUnitGroup group = UnitMaker.AddUnitGroup( mission, Enumerable.Repeat(playerFlightGroup.Aircraft, playerFlightGroup.Count).ToArray(), Side.Ally, isCarrier? mission.Carriers.First(x => x.Units[0].Name == playerFlightGroup.Carrier).Coordinates : mission.InitialPosition, isCarrier? "GroupAircraftPlayerCarrier" : "GroupAircraftPlayer", "UnitAircraft", Toolbox.BRSkillLevelToDCSSkillLevel(template.SituationFriendlyAISkillLevel), DCSMissionUnitGroupFlags.FirstUnitIsPlayer, objectiveDB.Payload, null, isCarrier? -99 : mission.InitialAirbaseID, true, country: playerFlightGroup.Country, startLocation: playerFlightGroup.StartLocation ); if (group == null) { throw new Exception($"Failed to create group of player aircraft of type \"{playerFlightGroup.Aircraft}\"."); } if (isCarrier) { group.CarrierId = mission.Carriers.First(x => x.Units[0].Name == playerFlightGroup.Carrier).Units[0].ID; } return(new UnitFlightGroupBriefingDescription( group.Name, group.Units.Length, playerFlightGroup.Aircraft, objectiveDB.BriefingTaskFlightGroup, Database.Instance.GetEntry <DBEntryUnit>(playerFlightGroup.Aircraft).AircraftData.GetRadioAsString())); }
/// <summary> /// Creates multiplayer client flight groups. /// </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> /// <returns>An array of <see cref="UnitFlightGroupBriefingDescription"/> describing the flight groups, to be used in the briefing</returns> private UnitFlightGroupBriefingDescription[] GenerateMultiplayerFlightGroups(DCSMission mission, MissionTemplate template, DBEntryObjective objectiveDB) { int totalGroupsCreated = 0; List <UnitFlightGroupBriefingDescription> briefingFGList = new List <UnitFlightGroupBriefingDescription>(); foreach (MissionTemplateFlightGroup fg in template.PlayerFlightGroups) { // Select proper payload for the flight group according to its tasking UnitTaskPayload payload = GetPayloadByTask(fg.Tasking, objectiveDB); bool hasCarrier = !string.IsNullOrEmpty(fg.Carrier); DCSMissionUnitGroup group = UnitMaker.AddUnitGroup( mission, Enumerable.Repeat(fg.Aircraft, fg.Count).ToArray(), Side.Ally, hasCarrier ? mission.Carriers.First(x => x.Units[0].Name == fg.Carrier).Coordinates : mission.InitialPosition, hasCarrier ? "GroupAircraftPlayerCarrier" : "GroupAircraftPlayer", "UnitAircraft", DCSSkillLevel.Client, 0, payload, null, hasCarrier ? -99 : mission.InitialAirbaseID, true, country: fg.Country, startLocation: fg.StartLocation == PlayerStartLocation.Runway ? PlayerStartLocation.ParkingHot : fg.StartLocation); if (group == null) { DebugLog.Instance.WriteLine($"Failed to create group of player aircraft of type \"{fg.Aircraft}\".", 1, DebugLogMessageErrorLevel.Warning); continue; } if (hasCarrier) { group.CarrierId = mission.Carriers.First(x => x.Units[0].Name == fg.Carrier).Units[0].ID; } briefingFGList.Add( new UnitFlightGroupBriefingDescription( group.Name, group.Units.Length, fg.Aircraft, GetTaskingDescription(fg.Tasking, objectiveDB), Database.Instance.GetEntry <DBEntryUnit>(fg.Aircraft).AircraftData.GetRadioAsString())); totalGroupsCreated++; } // Not a single player flight group was created succesfully, abort mission generation if (totalGroupsCreated == 0) { throw new Exception("No player flight groups could be created, mission generation failed."); } return(briefingFGList.ToArray()); }
/// <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> /// Picks a starting airbase for the player(s) /// </summary> /// <param name="mission">Mission for which the starting airbase must be set</param> /// <param name="template">Mission template to use</param> /// <param name="theaterDB">Theater database entry</param> /// <param name="objectiveDB">Objective database entry</param> /// <returns>Information about the starting airbase</returns> public DBEntryTheaterAirbase SelectStartingAirbase(DCSMission mission, MissionTemplate template, DBEntryTheater theaterDB, DBEntryObjective objectiveDB) { List <DBEntryTheaterAirbase[]> airbasesList = new List <DBEntryTheaterAirbase[]>(); // Select all airbases with enough parking spots int requiredParkingSpots = template.GetMissionPackageRequiredParkingSpots(); airbasesList.Add((from DBEntryTheaterAirbase ab in theaterDB.Airbases where ab.ParkingSpots.Length >= requiredParkingSpots select ab).ToArray()); // Select all airbases belonging to the proper coalition (unless all airbase belong to the same coalition) if ((template.OptionsTheaterCountriesCoalitions == CountryCoalition.Default) || (template.OptionsTheaterCountriesCoalitions == CountryCoalition.Inverted)) { Coalition requiredCoalition = template.OptionsTheaterCountriesCoalitions == CountryCoalition.Inverted ? mission.CoalitionEnemy : mission.CoalitionPlayer; airbasesList.Add((from DBEntryTheaterAirbase ab in airbasesList.Last() where ab.Coalition == requiredCoalition select ab).ToArray()); } // If mission must start near water, or some player start on a carrier, select all airbases near water if (objectiveDB.Flags.HasFlag(DBEntryObjectiveFlags.MustStartNearWater) || !string.IsNullOrEmpty(template.PlayerFlightGroups[0].Carrier)) { airbasesList.Add((from DBEntryTheaterAirbase ab in airbasesList.Last() where ab.Flags.HasFlag(DBEntryTheaterAirbaseFlag.NearWater) select ab).ToArray()); } // If a particular airbase name has been specified and an airbase with this name exists, pick it if (!string.IsNullOrEmpty(template.FlightPlanTheaterStartingAirbase)) { airbasesList.Add((from DBEntryTheaterAirbase airbase in theaterDB.Airbases where airbase.Name.ToLowerInvariant() == template.FlightPlanTheaterStartingAirbase.ToLowerInvariant() select airbase).ToArray()); if (airbasesList.Last().Length == 0) { DebugLog.Instance.WriteLine($"Airbase \"{template.FlightPlanTheaterStartingAirbase}\" not found or airbase doesn't have enough parking spots. Selecting a random airbase instead.", 1, DebugLogMessageErrorLevel.Warning); } } // Check for valid airbases in all list, starting from the last one (with the most criteria filtered, and go back to the previous ones // as long as no airbase is found. for (int i = airbasesList.Count - 1; i >= 0; i--) { if (airbasesList[i].Length > 0) { return(Toolbox.RandomFrom(airbasesList[i])); } } throw new Exception($"No airbase found with {requiredParkingSpots} parking spots, cannot spawn all player aircraft."); }
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> /// 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 = 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); // 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); } int windDirection0; // Wind direction at altitude 0, in degrees. Used by carrier groups to make sure carriers sail into the wind. // 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, out windDirection0); } // Generate Carrier DBEntryUnit carrierDB; using (MissionGeneratorCarrier unitGroupGen = new MissionGeneratorCarrier(unitMaker)) carrierDB = unitGroupGen.GenerateCarrier(mission, template, coalitionsDB[(int)mission.CoalitionPlayer], windDirection0); // 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, carrierDB, 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> /// Adds media files, scripts and units associated with mission features. /// </summary> /// <param name="mission">Mission to which features and extensions 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> public void GenerateExtensionsAndFeatures(DCSMission mission, MissionTemplate template, DBEntryObjective objectiveDB, DBEntryCoalition[] coalitionsDB) { int i, j; DBEntryExtension[] extensions = Database.Instance.GetEntries <DBEntryExtension>(template.ScriptExtensions); foreach (DBEntryExtension extension in extensions) { AddIncludedFiles(mission, extension); } DBEntryMissionFeature[] features = Database.Instance.GetEntries <DBEntryMissionFeature>(objectiveDB.MissionFeatures); foreach (DBEntryMissionFeature feature in features) { AddIncludedFiles(mission, feature); // No unit family in the unit group, so not unit group to add if (feature.UnitGroup.Families.Length == 0) { continue; } Side side = feature.UnitGroup.Flags.HasFlag(DBUnitGroupFlags.Friendly) ? Side.Ally : Side.Enemy; UnitFamily unitFamily = Toolbox.RandomFrom(feature.UnitGroup.Families); // Pick units string[] units = coalitionsDB[(int)((side == Side.Ally) ? mission.CoalitionPlayer : mission.CoalitionEnemy)].GetRandomUnits( unitFamily, mission.DateTime.Decade, feature.UnitGroup.Count.GetValue(), template.UnitMods); DCSSkillLevel skillLevel; if (side == Side.Ally) { skillLevel = Toolbox.BRSkillLevelToDCSSkillLevel(template.SituationFriendlyAISkillLevel); } else { skillLevel = Toolbox.IsUnitFamilyAircraft(unitFamily) ? Toolbox.BRSkillLevelToDCSSkillLevel(template.SituationEnemySkillLevelAir) : Toolbox.BRSkillLevelToDCSSkillLevel(template.SituationEnemySkillLevelGround); } DCSMissionUnitGroupFlags flags = 0; List <int> unitGroupsID = new List <int>(); for (i = 0; i < mission.Objectives.Length; i++) { Coordinates[] coordinates = new Coordinates[2]; for (j = 0; j < 2; j++) { switch (feature.UnitGroupCoordinates[j]) { case DBEntryMissionFeatureUnitGroupLocation.Homebase: coordinates[j] = mission.InitialPosition + Coordinates.CreateRandom(2, 6) * Toolbox.NM_TO_METERS; break; case DBEntryMissionFeatureUnitGroupLocation.Objective: coordinates[j] = mission.Objectives[i].Coordinates; break; case DBEntryMissionFeatureUnitGroupLocation.ObjectiveNear: coordinates[j] = mission.Objectives[i].Coordinates + Coordinates.CreateRandom(1.5, 4) * Toolbox.NM_TO_METERS; break; case DBEntryMissionFeatureUnitGroupLocation.Waypoint: coordinates[j] = mission.Objectives[i].WaypointCoordinates; break; case DBEntryMissionFeatureUnitGroupLocation.WaypointNear: coordinates[j] = mission.Objectives[i].WaypointCoordinates + Coordinates.CreateRandom(1.5, 4) * Toolbox.NM_TO_METERS; break; } } DCSMissionUnitGroup group = UnitMaker.AddUnitGroup( mission, units, side, coordinates[0], Toolbox.RandomFrom(feature.UnitGroup.LuaGroup), feature.UnitGroup.LuaUnit, skillLevel, flags, UnitTaskPayload.Default, coordinates[1]); if (group == null) { DebugLog.Instance.WriteLine($"Failed to create mission feature unit group for objective #{i + 1} made of the following units: {string.Join(", ", units)}", 1, DebugLogMessageErrorLevel.Warning); } unitGroupsID.Add(group.GroupID); // Add aircraft group to the queue of aircraft groups to be spawned if (!feature.UnitGroup.Flags.HasFlag(DBUnitGroupFlags.ManualActivation) && ((group.Category == UnitCategory.Helicopter) || (group.Category == UnitCategory.Plane) || feature.UnitGroup.Flags.HasFlag(DBUnitGroupFlags.DelaySpawn))) { mission.AircraftSpawnQueue.Add(new DCSMissionAircraftSpawnQueueItem(group.GroupID, true)); } } if (unitGroupsID.Count > 0) { mission.LuaSettings += $"briefingRoom.mission.featuresUnitGroups.{feature.ID} = {{ {string.Join(", ", unitGroupsID)} }}"; } } }
/// <summary> /// Creates the flight group player will lead 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="objectiveDB">Mission objective database entry</param> /// <returns>A <see cref="UnitFlightGroupBriefingDescription"/> describing the flight group, to be used in the briefing</returns> private UnitFlightGroupBriefingDescription GenerateSinglePlayerFlightGroup(DCSMission mission, MissionTemplate template, DBEntryObjective objectiveDB) { DCSMissionUnitGroup group = UnitMaker.AddUnitGroup( mission, Enumerable.Repeat(template.PlayerSPAircraft, template.PlayerSPWingmen + 1).ToArray(), Side.Ally, mission.Carrier != null ? mission.Carrier.Coordinates : mission.InitialPosition, mission.Carrier != null ? "GroupAircraftPlayerCarrier" : "GroupAircraftPlayer", "UnitAircraft", Toolbox.BRSkillLevelToDCSSkillLevel(template.PlayerAISkillLevel), DCSMissionUnitGroupFlags.FirstUnitIsPlayer, objectiveDB.Payload, null, mission.Carrier != null ? -99 : mission.InitialAirbaseID, true); if (group == null) { throw new Exception($"Failed to create group of player aircraft of type \"{template.PlayerSPAircraft}\"."); } return(new UnitFlightGroupBriefingDescription( group.Name, group.Units.Length, template.PlayerSPAircraft, objectiveDB.BriefingTaskFlightGroup, Database.Instance.GetEntry <DBEntryUnit>(template.PlayerSPAircraft).AircraftData.GetRadioAsString())); }
/// <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> /// Returns the total air-to-air power rating of the player's (and AI escort) flight package /// </summary> /// <param name="template">Mission template to use</param> /// <param name="objectiveDB">Mission objective 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> /// <returns>Total air-to-air power rating of the flight package</returns> private int GetMissionPackageAirPower(MissionTemplate template, DBEntryObjective objectiveDB, string aiEscortTypeCAP, string aiEscortTypeSEAD) { int airPowerRating = 0; DBEntryUnit aircraft; if (template.MissionType == MissionType.SinglePlayer) { // Player flight group aircraft = Database.Instance.GetEntry <DBEntryUnit>(template.PlayerFlightGroups[0].Aircraft); airPowerRating += ((aircraft != null) ? aircraft.AircraftData.AirToAirRating[1] : 1) * (template.PlayerFlightGroups[0].Count); } else // Mission is multi-player { foreach (MissionTemplateFlightGroup fg in template.PlayerFlightGroups) { aircraft = Database.Instance.GetEntry <DBEntryUnit>(fg.Aircraft); if (aircraft == null) // Aircraft doesn't exist { airPowerRating += fg.Count; continue; } bool hasAirToAirLoadout; switch (fg.Tasking) { default: // case MissionTemplateMPFlightGroupTask.Objectives if (objectiveDB.Payload == UnitTaskPayload.Default) { hasAirToAirLoadout = aircraft.Families.Contains(UnitFamily.PlaneFighter) || aircraft.Families.Contains(UnitFamily.PlaneInterceptor); } else if (objectiveDB.Payload == UnitTaskPayload.AirToAir) { hasAirToAirLoadout = true; } else { hasAirToAirLoadout = false; } break; case MissionTemplateFlightGroupTask.SupportCAP: hasAirToAirLoadout = true; break; case MissionTemplateFlightGroupTask.SupportSEAD: hasAirToAirLoadout = false; break; } airPowerRating += aircraft.AircraftData.AirToAirRating[hasAirToAirLoadout ? 1 : 0] * fg.Count; } } // AI CAP escort aircraft = Database.Instance.GetEntry <DBEntryUnit>(aiEscortTypeCAP); airPowerRating += ((aircraft != null) ? aircraft.AircraftData.AirToAirRating[1] : 1) * template.SituationFriendlyEscortCAP; // AI SEAD escort aircraft = Database.Instance.GetEntry <DBEntryUnit>(aiEscortTypeSEAD); airPowerRating += ((aircraft != null) ? aircraft.AircraftData.AirToAirRating[0] : 1) * template.SituationFriendlyEscortSEAD; return(airPowerRating); }
/// <summary> /// Adds media files, scripts and units associated with mission features. /// </summary> /// <param name="mission">Mission to which features and extensions 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> public void GenerateExtensionsAndFeatures(DCSMission mission, MissionTemplate template, DBEntryObjective objectiveDB, DBEntryCoalition[] coalitionsDB) { DBEntryExtension[] extensions = Database.Instance.GetEntries <DBEntryExtension>(template.OptionsScriptExtensions); foreach (DBEntryExtension extension in extensions) { AddIncludedFiles(mission, extension); } DBEntryMissionFeature[] features = Database.Instance.GetEntries <DBEntryMissionFeature>(objectiveDB.MissionFeatures); foreach (DBEntryMissionFeature feature in features) { AddIncludedFiles(mission, feature); // No unit family in the unit group, so not unit group to add if (feature.UnitGroup.Families.Length == 0) { continue; } // TODO: add feature unit group } }
/// <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); }
/// <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); }