/// <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">The mission.</param>
        /// <param name="template">The provided mission name in the template.</param>
        public void GenerateMissionName(DCSMission mission, string templateMissionName)
        {
            DebugLog.Instance.Log("Generating mission name...");

            if (templateMissionName == null)
            {
                templateMissionName = "";
            }

            // If a custom mission name was provided, use it...
            if (!string.IsNullOrEmpty(templateMissionName.Trim()))
            {
                mission.BriefingName = templateMissionName.Trim();
                return;
            }

            // ...else generate a random name.
            // First get a random template then replace parts 1 to MAX_MISSION_NAME_PARTS ($PART1$, $PART2$, $PART3$...) by random parts.
            string name = HQTools.RandomFrom(Language.GetStringArray("Mission", "Name.Template"));

            for (int i = 1; i <= MAX_MISSION_NAME_PARTS; i++)
            {
                name = name.Replace(
                    $"$P{i.ToString()}$",
                    HQTools.RandomFrom(Language.GetStringArray("Mission", $"Name.Part{i.ToString()}")));
            }

            mission.BriefingName = name;

            DebugLog.Instance.Log("");
        }
Esempio n. 2
0
        // Adds "embedded" air defense to ground vehicle groups
        public void AddAirDefenseUnits(DefinitionCoalition army, TimePeriod timePeriod, LibraryCommonSettingsEnemyAirDefense airDefenseSettings)
        {
            if (Category != UnitCategory.Vehicle)
            {
                return;
            }

            List <string> unitList = new List <string>(Units);

            if (HQTools.RandomDouble() < airDefenseSettings.EmbeddedChance)
            {
                int airDefCount = airDefenseSettings.EmbeddedCount.GetValue();

                for (int i = 0; i < airDefCount; i++)
                {
                    string airDefUnit =
                        HQTools.RandomFrom(
                            army.GetUnits(timePeriod, HQTools.RandomFrom(airDefenseSettings.EmbeddedFamilies), true, false));
                    if (airDefUnit == null)
                    {
                        continue;
                    }

                    unitList.Add(airDefUnit);
                }
            }

            // Randomize list order so air defense units are not always at the end
            Units = unitList.OrderBy(x => HQTools.RandomInt()).ToList();

            CheckAbsolueMaxUnitCount();
        }
Esempio n. 3
0
        /// <summary>
        /// Generates weather settings (precipitation, cloud coverage, temperature...) for the mission.
        /// Must be called after mission date has been set because min/max temperature changes every month.
        /// </summary>
        /// <param name="mission">The mission.</param>
        /// <param name="weather">The preferred type of weather (clear, cloudy, storm...).</param>
        /// <param name="theater">Theater definition from which to get weather info for this part of the world.</param>
        public void GenerateWeather(DCSMission mission, Weather weather, DefinitionTheater theater)
        {
            DebugLog.Instance.Log("Generating weather conditions...");

            mission.WeatherTemperature = theater.Temperature[(int)mission.DateMonth].GetValue();

            DebugLog.Instance.Log($"  Weather should be {weather.ToString().ToUpperInvariant()}");
            mission.WeatherLevel = (weather == Weather.Random) ? HQTools.RandomFrom(RANDOM_WEATHER) : weather;

            DebugLog.Instance.Log($"    Weather quality set to {mission.WeatherLevel}");

            // Clouds and precipitations
            mission.WeatherCloudBase           = theater.Weather[(int)mission.WeatherLevel].CloudsBase.GetValue();
            mission.WeatherCloudsDensity       = theater.Weather[(int)mission.WeatherLevel].CloudsDensity.GetValue();
            mission.WeatherCloudsPrecipitation = HQTools.RandomFrom(theater.Weather[(int)mission.WeatherLevel].CloudsPrecipitation);
            mission.WeatherCloudsThickness     = theater.Weather[(int)mission.WeatherLevel].CloudsThickness.GetValue();

            // Dust
            mission.WeatherDustEnabled = HQTools.RandomFrom(theater.Weather[(int)mission.WeatherLevel].DustEnabled);
            mission.WeatherDustDensity = mission.WeatherDustEnabled ? theater.Weather[(int)mission.WeatherLevel].DustDensity.GetValue() : 0;

            // Fog
            mission.WeatherFogEnabled    = HQTools.RandomFrom(theater.Weather[(int)mission.WeatherLevel].FogEnabled);
            mission.WeatherFogThickness  = mission.WeatherFogEnabled ? theater.Weather[(int)mission.WeatherLevel].FogThickness.GetValue() : 0;
            mission.WeatherFogVisibility = mission.WeatherFogEnabled ? theater.Weather[(int)mission.WeatherLevel].FogVisibility.GetValue() : 0;

            // Pressure, turbulence and visiblity
            mission.WeatherQNH        = theater.Weather[(int)mission.WeatherLevel].QNH.GetValue();
            mission.WeatherTurbulence = theater.Weather[(int)mission.WeatherLevel].Turbulence.GetValue();
            mission.WeatherVisibility = theater.Weather[(int)mission.WeatherLevel].Visibility.GetValue();

            DebugLog.Instance.Log($"    Cloud base altitude set to {mission.WeatherCloudBase} m");
            DebugLog.Instance.Log($"    Cloud density set to {mission.WeatherCloudBase}");
            DebugLog.Instance.Log($"    Precipitation set to {mission.WeatherCloudsPrecipitation}");
            DebugLog.Instance.Log($"    Cloud thickness set to {mission.WeatherCloudsThickness} m");

            DebugLog.Instance.Log($"    Dust set to {mission.WeatherDustEnabled}");
            DebugLog.Instance.Log($"    Dust density set to {mission.WeatherDustDensity}");

            DebugLog.Instance.Log($"    Fog set to {mission.WeatherFogEnabled}");
            DebugLog.Instance.Log($"    Fog thickness set to {mission.WeatherFogThickness}");
            DebugLog.Instance.Log($"    Fog visibility set to {mission.WeatherFogVisibility} m");

            DebugLog.Instance.Log($"    QNH set to {mission.WeatherQNH}");
            DebugLog.Instance.Log($"    Turbulence set to {mission.WeatherTurbulence} m/s");
            DebugLog.Instance.Log($"    Visibility set to {mission.WeatherVisibility} m");

            DebugLog.Instance.Log();
        }
Esempio n. 4
0
        public static DCSMissionUnitGroup FromCoalitionArmyAndUnitFamily(
            string luaGroup, string luaUnit,
            DefinitionCoalition army, TimePeriod timePeriod, UnitFamily family, int unitCount,
            int groupID, Coalition coalition, Coordinates coordinates)
        {
            List <string> unitList = new List <string>();

            UnitCategory category = HQTools.GetUnitCategoryFromUnitFamily(family);

            // FIXME: extendSearchToUnitsOfOtherFamilies and returnDefaultIfNoneFound should be parameters
            string[] availableUnits = army.GetUnits(timePeriod, family, false, false);

            if (availableUnits.Length == 0)
            {
                DebugLog.Instance.Log($"    WARNING: Cannot spawn unit group, no units of type {family.ToString().ToUpperInvariant()} for coalition {army.DisplayName.ToUpperInvariant()} in {timePeriod.ToString().ToUpperInvariant()}.");
                return(null);
            }

            do
            {
                unitList.AddRange((from u in HQTools.RandomFrom(availableUnits).Split('|') select u.Trim()));
            } while (unitList.Count < unitCount);

            // Some unit categories (helicopters and planes) require all units to be the same type
            if ((category == UnitCategory.Helicopter) || (category == UnitCategory.Plane))
            {
                for (int i = 1; i < unitList.Count; i++)
                {
                    unitList[i] = unitList[0];
                }
            }

            DCSMissionUnitGroup uGroup = new DCSMissionUnitGroup(
                luaGroup, luaUnit,
                category, groupID, coalition, coordinates,
                unitList.ToArray());

            return(uGroup);
        }
Esempio n. 5
0
        /// <summary>
        /// Randomizes parts of a string.
        /// </summary>
        /// <param name="rndStr">The string to randomize</param>
        /// <returns>A randomized string.</returns>
        private string ParseRandomString(string rndStr)
        {
            Regex regex = new Regex("{.*?}");

            if (!regex.IsMatch(rndStr))
            {
                return(rndStr.Replace("{", "").Replace("}", "").Replace("|", ""));
            }

            int lastOpen = -1;

            for (int i = 0; i < rndStr.Length; i++)
            {
                char c = rndStr[i];

                if (c == '{')
                {
                    lastOpen = i;
                }
                else if (c == '}')
                {
                    if (lastOpen == -1)
                    {
                        continue;
                    }

                    string subStr = rndStr.Substring(lastOpen + 1, i - lastOpen - 1);
                    subStr = HQTools.RandomFrom(subStr.Split('|'));

                    string outStr = rndStr.Substring(0, lastOpen) + subStr + rndStr.Substring(i + 1);
                    return(ParseRandomString(outStr));
                }
            }

            return(rndStr.Replace("{", "").Replace("}", "").Replace("|", ""));
        }
        public DefinitionTheaterSpawnPoint?GetRandomSpawnPoint(
            TheaterLocationSpawnPointType[] validTypes = null, DCSCountry[] validCountries = null,
            MinMaxD?distanceFrom  = null, Coordinates?distanceOrigin  = null,
            MinMaxD?distanceFrom2 = null, Coordinates?distanceOrigin2 = null)
        {
            IEnumerable <DefinitionTheaterSpawnPoint> validSP = (from DefinitionTheaterSpawnPoint s in SpawnPoints where !UsedSpawnPointsID.Contains(s.UniqueID) select s);

            if (validTypes != null)
            {
                validSP = (from DefinitionTheaterSpawnPoint s in validSP where validTypes.Contains(s.PointType) select s);
            }

            if (validCountries != null)
            {
                validSP = (from DefinitionTheaterSpawnPoint s in validSP where validCountries.Contains(s.Country) select s);
            }

            if (distanceFrom.HasValue && distanceOrigin.HasValue)
            {
                if (validSP.Count() == 0)
                {
                    return(null);
                }

                MinMaxD searchRange = distanceFrom.Value;

                IEnumerable <DefinitionTheaterSpawnPoint> validSPInRange = (from DefinitionTheaterSpawnPoint s in validSP select s);

                do
                {
                    validSPInRange = (from DefinitionTheaterSpawnPoint s in validSP where searchRange.Contains(distanceOrigin.Value.GetDistanceFrom(s.Coordinates)) select s);
                    searchRange    = new MinMaxD(searchRange.Min * 0.9, Math.Max(100, searchRange.Max * 1.1));
                } while (validSPInRange.Count() == 0);

                validSP = (from DefinitionTheaterSpawnPoint s in validSPInRange select s);
            }

            if (distanceFrom2.HasValue && distanceOrigin2.HasValue)
            {
                if (validSP.Count() == 0)
                {
                    return(null);
                }

                MinMaxD searchRange = distanceFrom2.Value;

                IEnumerable <DefinitionTheaterSpawnPoint> validSPInRange = (from DefinitionTheaterSpawnPoint s in validSP select s);

                do
                {
                    validSPInRange = (from DefinitionTheaterSpawnPoint s in validSP where searchRange.Contains(distanceOrigin2.Value.GetDistanceFrom(s.Coordinates)) select s);
                    searchRange    = new MinMaxD(searchRange.Min * 0.9, Math.Max(100, searchRange.Max * 1.1));
                } while (validSPInRange.Count() == 0);

                validSP = (from DefinitionTheaterSpawnPoint s in validSPInRange select s);
            }

            if (validSP.Count() == 0)
            {
                return(null);
            }

            DefinitionTheaterSpawnPoint selectedSpawnPoint = HQTools.RandomFrom(validSP.ToArray());

            UsedSpawnPointsID.Add(selectedSpawnPoint.UniqueID);
            return(selectedSpawnPoint);
        }
        public DCSMission Generate(MissionTemplate template, out string errorMessage)
        {
            int i;

            errorMessage = "";

            // Clear log, begin timing then create an instance of the HQ mission class
            Stopwatch  stopwatch = new Stopwatch(); stopwatch.Start();
            DCSMission mission   = new DCSMission();

            DebugLog.Instance.Clear();
            DebugLog.Instance.Log($"STARTING MISSION GENERATION AT {DateTime.Now.ToLongTimeString()}...");
            DebugLog.Instance.Log(new string('=', DebugLog.Instance.GetLastMessage().Length));
            DebugLog.Instance.Log();

            try
            {
                using (MissionGeneratorTemplateChecker templateChecker = new MissionGeneratorTemplateChecker())
                { templateChecker.CheckTemplate(template); }

                if (template.GetPlayerCount() < 1)
                {
                    throw new HQ4DCSException("Mission must include at least one player-controlled aircraft.");
                }

                // Pick definitions
                DefinitionCoalition[] coalitions = new DefinitionCoalition[2];
                coalitions[(int)Coalition.Blue] = Library.Instance.GetDefinition <DefinitionCoalition>(template.ContextCoalitionBlue);
                coalitions[(int)Coalition.Red]  = Library.Instance.GetDefinition <DefinitionCoalition>(template.ContextCoalitionRed);

                DefinitionLanguage  languageDef  = Library.Instance.GetDefinition <DefinitionLanguage>(template.PreferencesLanguage.ToLowerInvariant());
                DefinitionObjective objectiveDef = Library.Instance.GetDefinition <DefinitionObjective>(template.ObjectiveType.ToLowerInvariant());
                DefinitionTheater   theaterDef   = Library.Instance.GetDefinition <DefinitionTheater>(template.ContextTheater);
                theaterDef.ResetUsedSpawnPoints();

                // Create a list of all available objective names
                List <string> objectiveNames = languageDef.GetStringArray("Mission", "Waypoint.ObjectiveNames").ToList();

                // Create unit generators
                MissionGeneratorCallsign   callsignGenerator   = new MissionGeneratorCallsign(coalitions[(int)Coalition.Blue].NATOCallsigns, coalitions[(int)Coalition.Red].NATOCallsigns);
                MissionGeneratorUnitGroups unitGroupsGenerator = new MissionGeneratorUnitGroups(languageDef, callsignGenerator);

                // Copy values from the template to the mission
                mission.TheaterDefinition   = template.ContextTheater;
                mission.ObjectiveDefinition = template.ObjectiveType;
                mission.Language            = template.PreferencesLanguage;
                mission.CoalitionPlayer     = template.ContextPlayerCoalition;
                mission.SinglePlayer        = (template.GetPlayerCount() < 2);
                mission.UseNATOCallsigns    = coalitions[(int)template.ContextPlayerCoalition].NATOCallsigns;

                // Make sure no countries are shared between both coalitions
                mission.Countries[(int)Coalition.Blue] = coalitions[(int)Coalition.Blue].Countries.ToArray();
                mission.Countries[(int)Coalition.Red]  = coalitions[(int)Coalition.Red].Countries.Except(coalitions[(int)Coalition.Blue].Countries).ToArray();

                if (mission.Countries[(int)Coalition.Red].Length == 0)
                {
                    throw new HQ4DCSException("Red and blue coalitions cannot share the same countries.");
                }

                switch (template.BriefingUnits)
                {
                case UnitSystem.ByCoalition:
                    mission.BriefingImperialUnits = (coalitions[(int)mission.CoalitionPlayer].UnitSystem == UnitSystem.Imperial); break;

                case UnitSystem.Imperial: mission.BriefingImperialUnits = true; break;

                case UnitSystem.Metric: mission.BriefingImperialUnits = false; break;
                }

                // Generate mission environment parameters (weather, time of day, date...)
                using (MissionGeneratorEnvironment environment = new MissionGeneratorEnvironment())
                {
                    environment.GenerateMissionDate(mission, template.ContextTimePeriod, template.EnvironmentSeason);
                    environment.GenerateMissionTime(mission, template.EnvironmentTimeOfDay, theaterDef);
                    environment.GenerateWeather(mission, template.EnvironmentWeather, theaterDef);
                    environment.GenerateWind(mission, template.EnvironmentWind, theaterDef);
                }

                // Randomly select players' airbase
                DefinitionTheaterAirbase missionAirbase = HQTools.RandomFrom((from DefinitionTheaterAirbase ab in theaterDef.Airbases where ab.Coalition == template.ContextPlayerCoalition select ab).ToArray());

                // Randomly select objective spawn points
                int objectiveCount = (int)template.ObjectiveCount;
                if (objectiveCount == 0)
                {
                    objectiveCount = HQTools.RandomFrom(1, 1, 1, 2, 2, 3, 3, 4, 5);                      // Random objective count
                }
                //AmountR objectiveDistance = template.ObjectiveDistance;
                //if (objectiveDistance == AmountR.Random) objectiveDistance =
                //        HQTools.RandomFrom(AmountR.VeryLow, AmountR.VeryLow, AmountR.Low, AmountR.Low, AmountR.Low, AmountR.Average, AmountR.Average, AmountR.Average, AmountR.High, AmountR.High, AmountR.VeryHigh);
                List <DCSMissionObjectiveLocation> objectivesList = new List <DCSMissionObjectiveLocation>();
                List <DCSMissionWaypoint>          waypointsList  = new List <DCSMissionWaypoint>();
                for (i = 0; i < objectiveCount; i++)
                {
                    // If this is the first objective, measure distance from the airbase. Else measure distance from the previous objective.
                    Coordinates previousPoint = (i == 0) ? missionAirbase.Coordinates : objectivesList[i - 1].Coordinates;

                    MinMaxD distanceFromLastPoint = new MinMaxD(template.ObjectiveDistance * 0.75, template.ObjectiveDistance * 1.25) * HQTools.NM_TO_METERS;
                    if (i > 0)
                    {
                        distanceFromLastPoint /= 4.0;
                    }

                    DefinitionTheaterSpawnPoint?spawnPoint =
                        theaterDef.GetRandomSpawnPoint(objectiveDef.SpawnPointType, null, distanceFromLastPoint, previousPoint);

                    if (!spawnPoint.HasValue) // No valid spawn point, throw an error
                    {
                        throw new HQ4DCSException($"Cannot find a valid spawn point for objective #{i + 1}");
                    }

                    // Select a random name for the objective
                    string objName;
                    if (objectiveNames.Count == 0)
                    {
                        objName = $"OBJECTIVE{(i + 1).ToString("00")}";
                    }
                    else
                    {
                        objName = HQTools.RandomFrom(objectiveNames);
                        objectiveNames.Remove(objName);
                    }

                    objectivesList.Add(new DCSMissionObjectiveLocation(spawnPoint.Value.Coordinates, objName, objectiveDef.WaypointOnGround ? 0.0 : 1.0, 0));

                    // Add a waypoint for each objective
                    waypointsList.Add(new DCSMissionWaypoint(spawnPoint.Value.Coordinates + Coordinates.CreateRandomInaccuracy(objectiveDef.WaypointInaccuracy), objName));
                }

                // If required, add additional waypoints on the way to & from the objectives
                if (template.PreferencesExtraWaypoints && (waypointsList.Count > 0))
                {
                    Coordinates firstWPCoos = waypointsList.First().Coordinates;
                    Coordinates lastWPCoos  = waypointsList.Last().Coordinates;

                    int wpBeforeCount = HQTools.RandomMinMax(1, 3);
                    for (i = 0; i < wpBeforeCount; i++)
                    {
                        waypointsList.Insert(i,
                                             new DCSMissionWaypoint(
                                                 Coordinates.Lerp(missionAirbase.Coordinates, firstWPCoos, (double)(i + 1) / (wpBeforeCount + 1)) +
                                                 Coordinates.CreateRandomInaccuracy(firstWPCoos.GetDistanceFrom(missionAirbase.Coordinates) * 0.05, firstWPCoos.GetDistanceFrom(missionAirbase.Coordinates) * 0.15),
                                                 $"WP{(i + 1).ToString()}"));
                    }

                    int wpAfterCount = HQTools.RandomMinMax(1, 2);
                    for (i = 0; i < wpAfterCount; i++)
                    {
                        waypointsList.Add(
                            new DCSMissionWaypoint(
                                Coordinates.Lerp(lastWPCoos, missionAirbase.Coordinates, (double)(i + 1) / (wpAfterCount + 1)) +
                                Coordinates.CreateRandomInaccuracy(lastWPCoos.GetDistanceFrom(missionAirbase.Coordinates) * 0.05, lastWPCoos.GetDistanceFrom(missionAirbase.Coordinates) * 0.15),
                                $"WP{(waypointsList.Count + 1).ToString()}"));
                    }
                }

                mission.Objectives = objectivesList.ToArray();
                mission.Waypoints  = waypointsList.ToArray();

                mission.TotalFlightPlanDistance = 0.0;
                for (i = 0; i <= mission.Waypoints.Length; i++)
                {
                    if (i == 0) // first point, add distance between the takeoff airbase and the first waypoint
                    {
                        mission.TotalFlightPlanDistance += missionAirbase.Coordinates.GetDistanceFrom(mission.Waypoints.First().Coordinates);
                    }
                    else if (i == mission.Waypoints.Length) // last point, add distance between last waypoint and landing airbase
                    {
                        mission.TotalFlightPlanDistance += missionAirbase.Coordinates.GetDistanceFrom(mission.Waypoints.Last().Coordinates);
                    }
                    else // any other point, add distance between this waypoint and the last one
                    {
                        mission.TotalFlightPlanDistance += mission.Waypoints[i].Coordinates.GetDistanceFrom(mission.Waypoints[i - 1].Coordinates);
                    }
                }

                // Create a list of used player aircraft types, so the proper kneeboard subdirectories can be created in the .miz file
                mission.UsedPlayerAircraftTypes =
                    (from MissionTemplatePlayerFlightGroup pfg in template.FlightPackagePlayers select pfg.AircraftType).Distinct().OrderBy(x => x).ToArray();

                // Generate bullseyes and map center
                mission.MapCenter = Coordinates.GetCenter(
                    (from DCSMissionObjectiveLocation o in mission.Objectives select o.Coordinates).Union(new Coordinates[] { missionAirbase.Coordinates }).ToArray());
                mission.Bullseye = new Coordinates[2];
                for (i = 0; i < 2; i++)
                {
                    mission.Bullseye[i] = mission.MapCenter + Coordinates.CreateRandomInaccuracy(10000, 20000);
                }

                // Copy scripts
                //mission.ScriptsMission = missionObjective.ScriptMission.ToList();
                //mission.ScriptsObjective = missionObjective.ScriptObjective.ToList();

                mission.RealismAllowExternalViews = template.RealismAllowExternalViews;
                mission.RealismBirdStrikes        = template.RealismBirdStrikes;
                mission.RealismRandomFailures     = template.RealismRandomFailures;

                // Create list of airbase alignment from the theater definition
                mission.AirbasesCoalition.Clear();
                foreach (DefinitionTheaterAirbase ab in theaterDef.Airbases)
                {
                    if (mission.AirbasesCoalition.ContainsKey(ab.DCSID))
                    {
                        continue;
                    }

                    Coalition airbaseCoalition = ab.Coalition;
                    switch (template.ContextCountriesCoalitions)
                    {
                    case CountriesCoalition.AllBlue: airbaseCoalition = Coalition.Blue; break;

                    case CountriesCoalition.AllRed: airbaseCoalition = Coalition.Red; break;

                    case CountriesCoalition.Inverted: airbaseCoalition = (Coalition)(1 - (int)ab.Coalition); break;
                    }

                    mission.AirbasesCoalition.Add(ab.DCSID, airbaseCoalition);
                }

                // Make sure the starting airbase belongs to the players' coalition no matter which coalition other airbases belong to
                if (mission.AirbasesCoalition.ContainsKey(missionAirbase.DCSID))
                {
                    mission.AirbasesCoalition[missionAirbase.DCSID] = template.ContextPlayerCoalition;
                }

                List <string> oggFilesList = new List <string>();
                oggFilesList.AddRange(Library.Instance.Common.SharedOggFiles); // Default wave files
                oggFilesList.AddRange(objectiveDef.IncludeOgg);                // Objective wave files
                mission.OggFiles = oggFilesList.Distinct().ToArray();

                //mission.Scripts

                /*
                 * // Generate mission flight plan
                 * using (GeneratorFlightPlan flightPlan = new GeneratorFlightPlan(Library, language, csGenerator))
                 * {
                 *  flightPlan.SelectTakeoffAndLandingAirbases(mission, theater);
                 *  mission.MapCenter = mission.Airbases[0].Coordinates; // Center the map on the starting airdrome
                 *  flightPlan.GenerateObjectiveLocations(mission, template, theater, missionObjective);
                 *  flightPlan.GenerateWaypoints(mission, template, theater, missionObjective);
                 *  flightPlan.GenerateBullseye(mission);
                 * }
                 */

                // Generate units
                AmountNR selectedEnemyAirDefense, selectedEnemyCAP; // We have to store these values here because they're used in the briefing remarks
                using (MissionGeneratorUnitGroups unitGenerator = new MissionGeneratorUnitGroups(languageDef, callsignGenerator))
                {
                    foreach (MissionTemplatePlayerFlightGroup pfg in template.FlightPackagePlayers)
                    {
                        unitGenerator.AddPlayerFlightGroup(mission, template, pfg, objectiveDef, missionAirbase);
                    }

                    //unitGroups.GeneratePlayerFlightGroups(mission, template, missionObjective);
                    //unitGroups.GenerateAIEscortFlightGroups(mission, template, coalitions, template.FlightGroupsAICAP, UnitFamily.PlaneFighter, "GroupPlaneEscortCAP", AircraftPayloadType.A2A, DCSAircraftTask.CAP);
                    //unitGroups.GenerateAIEscortFlightGroups(mission, template, coalitions, template.FlightGroupsAISEAD, UnitFamily.PlaneSEAD, "GroupPlaneEscortSEAD", AircraftPayloadType.SEAD, DCSAircraftTask.SEAD);

                    unitGenerator.AddObjectiveUnitGroupsAtEachObjective(mission, template, objectiveDef, coalitions);
                    //unitGroups.GenerateObjectiveUnitGroupsAtCenter(mission, template, missionObjective, coalitions);
                    unitGenerator.AddFriendlySupportAircraft(mission, template, coalitions[(int)mission.CoalitionPlayer], theaterDef, missionAirbase);
                    unitGenerator.AddEnemyAirDefenseUnits(mission, template, theaterDef, objectiveDef, coalitions, missionAirbase, out selectedEnemyAirDefense);
                    unitGenerator.AddFriendlyAirDefenseUnits(mission, template, theaterDef, objectiveDef, coalitions, missionAirbase, out AmountNR selectedFriendlyAirDefense);
                    unitGenerator.AddCombatAirPatrolUnits(mission, template, theaterDef, coalitions, missionAirbase, out AmountNR selectedFriendlyCAP, out selectedEnemyCAP);
                }

                using (MissionGeneratorBriefing briefingGenerator = new MissionGeneratorBriefing(languageDef))
                {
                    // Add briefing remarks
                    for (i = 0; i < objectiveDef.BriefingRemarks.Length; i++)
                    {
                        mission.BriefingRemarks.Add(languageDef.GetStringRandom("Briefing", $"Remark.{objectiveDef.BriefingRemarks}"));
                    }
                    mission.BriefingRemarks.Add(languageDef.GetStringRandom("Briefing", $"Remark.EnemyAirDefense.{selectedEnemyAirDefense}"));
                    mission.BriefingRemarks.Add(languageDef.GetStringRandom("Briefing", $"Remark.EnemyCAP.{selectedEnemyCAP}"));

                    mission.BriefingTasks.Add(languageDef.GetString("Briefing", "Task.TakeOffFrom", "Airbase", missionAirbase.Name));
                    for (i = 0; i < mission.Objectives.Length; i++)
                    {
                        mission.BriefingTasks.Add(languageDef.GetString("Briefing", $"Task.{objectiveDef.BriefingTask}", "Objective", mission.Objectives[i].Name.ToUpperInvariant()));
                    }
                    mission.BriefingTasks.Add(languageDef.GetString("Briefing", "Task.LandAt", "Airbase", missionAirbase.Name));

                    briefingGenerator.GenerateMissionName(mission, template.BriefingName);
                    briefingGenerator.GenerateMissionDescription(mission, template.BriefingDescription, objectiveDef);

                    /*
                     *  briefing.GenerateMissionTasks(mission, template, missionObjective);
                     *  briefing.GenerateMissionRemarks(mission, template, missionObjective);
                     */
                    briefingGenerator.GenerateRawTextBriefing(mission, template /*, missionObjective*/);
                    briefingGenerator.GenerateHTMLBriefing(mission, template /*, missionObjective*/);
                }

                stopwatch.Stop();
                DebugLog.Instance.Log();
                DebugLog.Instance.Log($"COMPLETED MISSION GENERATION AT {DateTime.Now.ToLongTimeString()} (TOOK {stopwatch.Elapsed.TotalMilliseconds.ToString("F0")} MILLISECONDS).");
                DebugLog.Instance.Log();
                mission.GenerationLog = DebugLog.Instance.GetFullLog();
            }
#if DEBUG
            catch (HQ4DCSException e)
#else
            catch (Exception e)
#endif
            {
                stopwatch.Stop();
                DebugLog.Instance.Log($"ERROR: {e.Message}");
                DebugLog.Instance.Log();
                DebugLog.Instance.Log($"MISSION GENERATION FAILED.");
                DebugLog.Instance.Log();
                errorMessage = e.Message;

                mission.Dispose();
                mission = null;
            }

            DebugLog.Instance.SaveToFileAndClear("MissionGeneration");
            return(mission);
        }