示例#1
0
        protected override bool OnLoad(string iniFilePath)
        {
            //int i;

            var ini = new INIFile(iniFilePath);

            string[] badCountries = (from country in ini.GetValueArray <string>("Coalition", "Countries").Distinct() where !Enum.TryParse <Country>(country, true, out _) select country).ToArray();
            if (badCountries.Length > 0)
            {
                BriefingRoom.PrintToLog($"Bad countr{(badCountries.Length == 1 ? "y" : "ies")} in coalition \"{ID}\": {string.Join(", ", badCountries)}", LogMessageErrorLevel.Warning);
            }

            Countries = ini.GetValueArray <Country>("Coalition", "Countries").Append(Country.ALL).Distinct().OrderBy(x => x).ToArray();
            if (Countries.Length == 0)
            {
                BriefingRoom.PrintToLog($"No country in coalition \"{ID}\", coalition was ignored.", LogMessageErrorLevel.Warning);
                return(false);
            }

            DefaultUnitList = ini.GetValue <string>("Coalition", "DefaultUnitList");
            if (!Database.EntryExists <DBEntryDefaultUnitList>(DefaultUnitList))
            {
                BriefingRoom.PrintToLog($"Default unit list \"{DefaultUnitList}\" required by coalition \"{ID}\" doesn't exist. Coalition was ignored.", LogMessageErrorLevel.Warning);
                return(false);
            }

            NATOCallsigns = ini.GetValue("Coalition", "NATOCallsigns", false);

            return(true);
        }
示例#2
0
        private Dictionary <Country, List <string> > SelectValidUnits(List <UnitFamily> families, Decade decade, List <string> unitMods)
        {
            var validUnits = new Dictionary <Country, List <string> >();

            foreach (Country country in Countries)
            {
                validUnits[country] = (
                    from DBEntryUnit unit in Database.GetAllEntries <DBEntryUnit>()
                    where unit.Families.Intersect(families).ToList().Count > 0 && unit.Operators.ContainsKey(country) &&
                    (string.IsNullOrEmpty(unit.RequiredMod) || unitMods.Contains(unit.RequiredMod, StringComparer.InvariantCultureIgnoreCase)) &&
                    (unit.Operators[country][0] <= decade) && (unit.Operators[country][1] >= decade)
                    select unit.ID).Distinct().ToList();
            }

            validUnits = validUnits.Where(x => x.Value.Count > 0).ToDictionary(x => x.Key, x => x.Value);

            // At least one unit found, return it
            if (validUnits.Count > 0)
            {
                return(validUnits);
            }

            BriefingRoom.PrintToLog($"No Units of types {string.Join(", ", families)} found in coalition of {string.Join(", ", Countries.Where(x => x != Country.ALL))} forced to use defaults", LogMessageErrorLevel.Info);
            return(new Dictionary <Country, List <string> > {
                { Country.ALL, Database.GetEntry <DBEntryDefaultUnitList>(DefaultUnitList).DefaultUnits[(int)families.First(), (int)decade].ToList() }
            });
        }
示例#3
0
        public DBCommonNames()
        {
            int i;

            BriefingRoom.PrintToLog("Loading common global settings...");
            INIFile ini = new($"{BRPaths.DATABASE}Names.ini");

            MissionNameTemplate = ini.GetValue <string>("Mission", "Template");
            for (i = 0; i < MISSION_NAMES_PART_COUNT; i++)
            {
                MissionNameParts[i] = ini.GetValueArray <string>("Mission", $"Part{i + 1}");
            }

            for (i = 0; i < Toolbox.EnumCount <UnitFamily>(); i++)
            {
                UnitFamilies[i] = ini.GetValueArray <string>("UnitFamilies", ((UnitFamily)i).ToString());
                Array.Resize(ref UnitFamilies[i], 2);
                UnitGroups[i] = ini.GetValue <string>("UnitGroups", ((UnitFamily)i).ToString());
            }

            WPEgressName      = ini.GetValue <string>("Waypoints", "Egress").ToUpperInvariant();
            WPFinalName       = ini.GetValue <string>("Waypoints", "Final").ToUpperInvariant();
            WPIngressName     = ini.GetValue <string>("Waypoints", "Ingress").ToUpperInvariant();
            WPInitialName     = ini.GetValue <string>("Waypoints", "Initial").ToUpperInvariant();
            WPObjectivesNames = (from string wpName in ini.GetValueArray <string>("Waypoints", "Objectives") select wpName.ToUpperInvariant()).ToArray();
        }
示例#4
0
        internal static Tuple <double, double> GenerateWind(DCSMission mission, MissionTemplateRecord template, int turbulenceFromWeather)
        {
            var windSpeedAtSeaLevel     = 0.0;
            var windDirectionAtSeaLevel = 0.0;

            Wind windLevel = template.EnvironmentWind == Wind.Random ? PickRandomWindLevel() : template.EnvironmentWind;

            BriefingRoom.PrintToLog($"Wind speed level set to \"{windLevel}\".");

            int windAverage = 0;

            for (int i = 0; i < 3; i++)
            {
                int windSpeed     = Database.Instance.Common.Wind[(int)windLevel].Wind.GetValue();
                int windDirection = windSpeed > 0 ? Toolbox.RandomInt(0, 360) : 0;
                if (i == 0)
                {
                    windSpeedAtSeaLevel     = windSpeed;
                    windDirectionAtSeaLevel = windDirection * Toolbox.DEGREES_TO_RADIANS;
                }
                windAverage += windSpeed;

                mission.SetValue($"WeatherWindSpeed{i + 1}", windSpeed);
                mission.SetValue($"WeatherWindDirection{i + 1}", windDirection);
            }
            windAverage /= 3;

            mission.SetValue($"WeatherWindName", windLevel.ToString()); // TODO: get name from attribute
            mission.SetValue($"WeatherWindSpeedAverage", windAverage);

            mission.SetValue("WeatherGroundTurbulence", Database.Instance.Common.Wind[(int)windLevel].Turbulence.GetValue() + turbulenceFromWeather);
            return(new(windSpeedAtSeaLevel, windDirectionAtSeaLevel));
        }
示例#5
0
        protected override bool OnLoad(string iniFilePath)
        {
            var ini = new INIFile(iniFilePath);

            BriefingName = new string[2]
            {
                ini.GetValue <string>("ObjectiveTarget", "Briefing.UnitName.Singular"),
                ini.GetValue <string>("ObjectiveTarget", "Briefing.UnitName.Plural")
            };

            UnitFamilies = Toolbox.SetSingleCategoryFamilies(ini.GetValueArray <UnitFamily>("ObjectiveTarget", "Units.Families"));
            if (UnitFamilies.Length == 0)
            {
                BriefingRoom.PrintToLog($"No unit categories for objective target \"{ID}\"", LogMessageErrorLevel.Warning);
                return(false);
            }

            UnitCount = new MinMaxI[Toolbox.EnumCount <Amount>()];
            foreach (Amount amount in Toolbox.GetEnumValues <Amount>())
            {
                UnitCount[(int)amount] = ini.GetValue <MinMaxI>("ObjectiveTarget", $"Units.Count.{amount}");
            }

            ValidSpawnPoints = DatabaseTools.CheckSpawnPoints(ini.GetValueArray <SpawnPointType>("ObjectiveTarget", "ValidSpawnPoints"));

            return(true);
        }
示例#6
0
        private void SetValue(string key, string value, bool append)
        {
            if (string.IsNullOrEmpty(key))
            {
                return;
            }
            key   = key.ToUpperInvariant();
            value = value ?? "";
            value = value.Replace("\r\n", "\n");

            string displayedValue = value.Replace("\n", " ");

            if (displayedValue.Length > MAX_VALUE_LENGTH_DISPLAY)
            {
                displayedValue = displayedValue.Substring(0, MAX_VALUE_LENGTH_DISPLAY) + "...";
            }

            BriefingRoom.PrintToLog($"Mission parameter \"{key.ToLowerInvariant()}\" {(append ? "appended with" : "set to")} \"{displayedValue}\".");

            if (!Values.ContainsKey(key))
            {
                Values.Add(key, value);
            }
            else
            {
                Values[key] = append ? Values[key] + value : value;
            }
        }
示例#7
0
        private static void GenerateFOB(
            UnitMaker unitMaker, ZoneMaker zoneMaker, MissionTemplateFlightGroupRecord flightGroup, Dictionary <string, UnitMakerGroupInfo> carrierDictionary,
            DCSMission mission, MissionTemplateRecord template, Coordinates landbaseCoordinates, Coordinates objectivesCenter)
        {
            DBEntryTheater theaterDB = Database.Instance.GetEntry <DBEntryTheater>(template.ContextTheater);

            if (theaterDB == null)
            {
                return;                    // Theater doesn't exist. Should never happen.
            }
            Coordinates?spawnPoint =
                unitMaker.SpawnPointSelector.GetRandomSpawnPoint(
                    new SpawnPointType[] { SpawnPointType.LandLarge },
                    landbaseCoordinates,
                    new MinMaxD(5, template.FlightPlanObjectiveDistance),
                    objectivesCenter,
                    new MinMaxD(10, template.FlightPlanObjectiveDistance / 2), template.ContextPlayerCoalition);

            if (!spawnPoint.HasValue)
            {
                BriefingRoom.PrintToLog($"No spawn point found for FOB air defense unit groups", LogMessageErrorLevel.Warning);
                return;
            }

            DBEntryUnit unitDB = Database.Instance.GetEntry <DBEntryUnit>(flightGroup.Carrier);

            if (unitDB == null)
            {
                return;                 // Unit doesn't exist or is not a carrier
            }
            double radioFrequency = 127.5 + carrierDictionary.Count;
            var    FOBNames       = new List <string> {
                "FOB_London",
                "FOB_Dallas",
                "FOB_Paris",
                "FOB_Moscow",
                "FOB_Berlin"
            };

            UnitMakerGroupInfo?groupInfo =
                unitMaker.AddUnitGroup(
                    unitDB.Families[0], 1, Side.Ally,
                    "GroupStatic", "UnitStaticFOB",
                    spawnPoint.Value, 0,
                    "FOBCallSignIndex".ToKeyValuePair(FOBNames.IndexOf(flightGroup.Carrier) + 1),
                    "RadioBand".ToKeyValuePair((int)RadioModulation.AM),
                    "RadioFrequency".ToKeyValuePair(GeneratorTools.GetRadioFrenquency(radioFrequency)));

            if (!groupInfo.HasValue || (groupInfo.Value.UnitsID.Length == 0))
            {
                return;                                                               // Couldn't generate group
            }
            zoneMaker.AddCTLDPickupZone(spawnPoint.Value, true);
            mission.Briefing.AddItem(
                DCSMissionBriefingItemType.Airbase,
                $"{unitDB.UIDisplayName}\t-\t{GeneratorTools.FormatRadioFrequency(radioFrequency)}\t\t");
            carrierDictionary.Add(flightGroup.Carrier, groupInfo.Value); // This bit limits FOBS to one per game think about how we can fix this
        }
示例#8
0
        protected override bool OnLoad(string iniFilePath)
        {
            var ini = new INIFile(iniFilePath);

            // Unit info
            DCSIDs = (from string u in ini.GetValueArray <string>("Unit", "DCSID") select u.Trim()).ToArray();
            if (DCSIDs.Length < 1)
            {
                BriefingRoom.PrintToLog($"Unit {ID} contains no DCS unit ID, unit was ignored.", LogMessageErrorLevel.Warning);
                return(false);
            }
            Families = ini.GetValueArray <UnitFamily>("Unit", "Families");
            if (Families.Length == 0)
            {
                BriefingRoom.PrintToLog($"Unit {ID} has no family, unit was ignored.", LogMessageErrorLevel.Warning);
                return(false);
            }
            // Make sure all unit families belong to same category (unit cannot be a helicopter and a ground vehicle at the same time, for instance)
            Families = (from UnitFamily f in Families where f.GetUnitCategory() == Category select f).Distinct().ToArray();
            ExtraLua = ini.GetValue <string>("Unit", "ExtraLua").Trim();
            if (!string.IsNullOrEmpty(ExtraLua) && !ExtraLua.EndsWith(","))
            {
                ExtraLua += ",";
            }
            Flags             = ini.GetValueArrayAsEnumFlags <DBEntryUnitFlags>("Unit", "Flags");
            OffsetCoordinates = (from string s in ini.GetValueArray <string>("Unit", "Offset.Coordinates", ';') select new Coordinates(s)).ToArray();
            OffsetHeading     = ini.GetValueArray <double>("Unit", "Offset.Heading");
            Shape             = ini.GetValueArray <string>("Unit", "Shape");
            RequiredMod       = ini.GetValue <string>("Unit", "RequiredMod");

            AircraftData = new DBEntryUnitAircraftData();

            // Load the list of operators
            Operators = new Dictionary <Country, Decade[]>();
            foreach (string k in ini.GetKeysInSection("Operators"))
            {
                if (!Enum.TryParse(k, true, out Country country))
                {
                    BriefingRoom.PrintToLog($"Country {k} in unit {ID} doesn't exist.", LogMessageErrorLevel.Warning);
                    continue;
                }

                if (Operators.ContainsKey(country))
                {
                    continue;
                }
                Operators.Add(country, ini.GetValueArrayAsMinMaxEnum <Decade>("Operators", k));
            }

            if (IsAircraft)                                // Load aircraft-specific data, if required
            {
                DCSIDs       = new string[] { DCSIDs[0] }; // Aircraft can not have multiple unit types in their group
                AircraftData = new DBEntryUnitAircraftData(ini, iniFilePath.Contains(BRPaths.CUSTOMDATABASE));
            }

            return(true);
        }
示例#9
0
        internal void GenerateMissionFeature(DCSMission mission, string featureID, string objectiveName, int objectiveIndex, int objectiveGroupID, Coordinates objectiveCoordinates, Side objectiveTargetSide, bool hideEnemy = false)
        {
            DBEntryFeatureObjective featureDB = Database.Instance.GetEntry <DBEntryFeatureObjective>(featureID);

            if (featureDB == null) // Feature doesn't exist
            {
                BriefingRoom.PrintToLog($"Objective feature {featureID} not found.", LogMessageErrorLevel.Warning);
                return;
            }

            double spawnDistance = Math.Max(1.0, featureDB.UnitGroupSpawnDistance) * Toolbox.NM_TO_METERS;

            Coordinates coordinates;

            if (featureDB.UnitGroupFlags.HasFlag(FeatureUnitGroupFlags.SpawnOnObjective))
            {
                coordinates = objectiveCoordinates + Coordinates.CreateRandom(10, 50);
            }
            else
            {
                Coordinates?spawnPoint =
                    _unitMaker.SpawnPointSelector.GetRandomSpawnPoint(
                        featureDB.UnitGroupValidSpawnPoints, objectiveCoordinates,
                        new MinMaxD(spawnDistance * .75, spawnDistance * 1.5));

                if (!spawnPoint.HasValue) // No spawn point found
                {
                    BriefingRoom.PrintToLog($"No spawn point found for objective feature {featureID}.", LogMessageErrorLevel.Warning);
                    return;
                }

                coordinates = spawnPoint.Value;
            }

            Coordinates coordinates2 = coordinates + Coordinates.CreateRandom(10, 20) * Toolbox.NM_TO_METERS;

            Dictionary <string, object> extraSettings = new Dictionary <string, object>(StringComparer.InvariantCultureIgnoreCase);

            extraSettings.AddIfKeyUnused("ObjectiveName", objectiveName);
            extraSettings.AddIfKeyUnused("ObjectiveIndex", objectiveIndex + 1);
            extraSettings.AddIfKeyUnused("ObjectiveGroupID", objectiveGroupID);

            if (featureID == "TargetDesignationLaser")
            {
                var laserCode = _template.OptionsMission.Contains("SingleLaserCode") ? 1688 : getNextLaserCode();
                extraSettings.AddIfKeyUnused("LASERCODE", laserCode);
                mission.Briefing.AddItem(DCSMissionBriefingItemType.JTAC, $"{objectiveName}\t{laserCode}");
            }

            UnitMakerGroupInfo?groupInfo = AddMissionFeature(
                featureDB, mission,
                coordinates, coordinates2,
                ref extraSettings, objectiveTargetSide, hideEnemy);

            AddBriefingRemarkFromFeature(featureDB, mission, false, groupInfo, extraSettings);
        }
示例#10
0
        private void LoadEntries <T>(string subDirectory) where T : DBEntry, new()
        {
            BriefingRoom.PrintToLog($"Loading {subDirectory.ToLowerInvariant()}...");

            string directory = $"{BRPaths.DATABASE}{subDirectory}";

            if (!Directory.Exists(directory))
            {
                throw new Exception($"Directory {directory} not found.");
            }

            Type   dbType        = typeof(T);
            string shortTypeName = dbType.Name.Substring(7).ToLowerInvariant();

            if (!DBEntries.ContainsKey(dbType))
            {
                DBEntries.Add(dbType, new Dictionary <string, DBEntry>(StringComparer.InvariantCultureIgnoreCase));
            }

            DBEntries[dbType].Clear();

            foreach (string filePath in Directory.EnumerateFiles(directory, "*.ini", SearchOption.AllDirectories))
            {
                string id = Path.GetFileNameWithoutExtension(filePath).Replace(",", "").Trim(); // No commas in file names, so we don't break comma-separated arrays

                if (DBEntries[dbType].ContainsKey(id))
                {
                    continue;
                }
                T entry = new T();
                if (!entry.Load(this, id, filePath))
                {
                    continue;
                }
                DBEntries[dbType].Add(id, entry);
                BriefingRoom.PrintToLog($"Loaded {shortTypeName} \"{id}\"");
            }
            BriefingRoom.PrintToLog($"Found {DBEntries[dbType].Count} database entries of type \"{typeof(T).Name}\"");

            bool mustHaveAtLeastOneEntry = true;

            if ((dbType == typeof(DBEntryDefaultUnitList)) ||
                (dbType == typeof(DBEntryFeatureMission)) ||
                (dbType == typeof(DBEntryFeatureObjective)))
            {
                mustHaveAtLeastOneEntry = false;
            }

            // If a required database type has no entries, raise an error.
            if ((DBEntries[dbType].Count == 0) && mustHaveAtLeastOneEntry)
            {
                throw new BriefingRoomException($"No valid database entries found in the \"{subDirectory}\" directory");
            }
        }
示例#11
0
        protected override bool OnLoad(string iniFilePath)
        {
            int i, j;

            DefaultUnits = new string[Toolbox.EnumCount <UnitFamily>(), Toolbox.EnumCount <Decade>()][];
            for (i = 0; i < Toolbox.EnumCount <UnitFamily>(); i++)
            {
                for (j = 0; j < Toolbox.EnumCount <Decade>(); j++)
                {
                    DefaultUnits[i, j] = new string[0];
                }
            }

            var ini = new INIFile(iniFilePath);

            foreach (UnitFamily family in Toolbox.GetEnumValues <UnitFamily>())
            {
                foreach (Decade decade in Toolbox.GetEnumValues <Decade>())
                {
                    string[] units = GetValidDBEntryIDs <DBEntryUnit>(ini.GetValueArray <string>($"{decade}", $"{family}"), out string[] invalidUnits);

                    foreach (string u in invalidUnits)
                    {
                        BriefingRoom.PrintToLog($"Unit \"{u}\" not found in default unit list \"{ID}\".", LogMessageErrorLevel.Warning);
                    }

                    if (units.Length == 0)
                    {
                        continue;
                    }

                    for (i = (int)decade; i <= (int)Decade.Decade2020; i++)
                    {
                        DefaultUnits[(int)family, i] = units.ToArray();
                    }
                }
            }

            for (i = 0; i < Toolbox.EnumCount <UnitFamily>(); i++)
            {
                for (j = 0; j < Toolbox.EnumCount <Decade>(); j++)
                {
                    if (DefaultUnits[i, j].Length == 0)
                    {
                        BriefingRoom.PrintToLog($"Default unit list \"{ID}\" has no unit of family \"{(UnitFamily)i}\" during {(Decade)j}, unit list was ignored.", LogMessageErrorLevel.Warning);
                        return(false);
                    }
                }
            }


            return(true);
        }
示例#12
0
        internal static async Task <DCSMission> GenerateRetryableAsync(MissionTemplate template, bool useObjectivePresets)
        {
            var templateRecord = new MissionTemplateRecord(template);
            var mission        = await Policy
                                 .HandleResult <DCSMission>(x => x.IsExtremeDistance(template, out double distance))
                                 .Or <BriefingRoomException>()
                                 .RetryAsync(3)
                                 .ExecuteAsync(() => GenerateAsync(templateRecord, useObjectivePresets));

            if (mission.IsExtremeDistance(template, out double distance))
            {
                BriefingRoom.PrintToLog($"Distance to objectives exceeds 1.7x of requested distance. ({Math.Round(distance, 2)}NM)", LogMessageErrorLevel.Warning);
            }

            return(mission);
        }
示例#13
0
        private (string unitsLua, List <int> unitsIDList) AddUnits(
            string[] unitSets,
            string groupName,
            UnitCallsign?callsign,
            string unitTypeLua,
            Coordinates coordinates,
            UnitMakerGroupFlags unitMakerGroupFlags,
            params KeyValuePair <string, object>[] extraSettings
            )
        {
            string     unitsLuaTable = "";
            int        unitLuaIndex  = 1;
            List <int> unitsIDList   = new List <int>();

            foreach (var unitSet in unitSets)
            {
                DBEntryUnit unitDB = Database.Instance.GetEntry <DBEntryUnit>(unitSet);
                if (unitDB == null)
                {
                    BriefingRoom.PrintToLog($"Unit \"{unitSet}\" not found.", LogMessageErrorLevel.Warning);
                    continue;
                }
                int unitSetIndex = 0;
                foreach (string DCSID in unitDB.DCSIDs)
                {
                    unitsLuaTable += AddUnit(
                        DCSID,
                        groupName,
                        callsign,
                        unitLuaIndex,
                        unitSetIndex,
                        unitDB,
                        unitTypeLua,
                        coordinates,
                        unitMakerGroupFlags,
                        extraSettings
                        );

                    unitsIDList.Add(UnitID);
                    unitSetIndex++;
                    unitLuaIndex++;
                    UnitID++;
                }
            }
            return(unitsLuaTable, unitsIDList);
        }
示例#14
0
        internal static async Task <DCSCampaign> GenerateAsync(CampaignTemplate campaignTemplate)
        {
            DCSCampaign campaign = new();

            campaign.Name = GeneratorTools.GenerateMissionName(campaignTemplate.BriefingCampaignName);;
            string baseFileName = Toolbox.RemoveInvalidPathCharacters(campaign.Name);

            DateTime date = GenerateCampaignDate(campaignTemplate);

            campaignTemplate.Player.AIWingmen = true; //Make sure wingmen is always true for campaign

            for (int i = 0; i < campaignTemplate.MissionsCount; i++)
            {
                // Increment the date by a few days for each mission after the first
                if (i > 0)
                {
                    date = IncrementDate(date);
                }

                MissionTemplateRecord template = CreateMissionTemplate(campaignTemplate, campaign.Name, i, (int)campaignTemplate.MissionsObjectiveCount);

                DCSMission mission = await MissionGenerator.GenerateAsync(template, true);

                // TODO: mission.DateTime.Day = date.Day; mission.DateTime.Month = date.Month; mission.DateTime.Year = date.Year;
                if (mission == null)
                {
                    BriefingRoom.PrintToLog($"Failed to generate mission {i + 1} in the campaign.", LogMessageErrorLevel.Warning);
                    continue;
                }

                campaign.AddMission(mission);
            }

            if (campaign.MissionCount < 1) // No missions generated, something went very wrong.
            {
                throw new BriefingRoomException($"Campaign has no valid mission.");
            }


            CreateImageFiles(campaignTemplate, campaign, baseFileName);

            campaign.CMPFile = GetCMPFile(campaignTemplate, campaign.Name);

            return(campaign);
        }
示例#15
0
        internal void GenerateMissionFeature(DCSMission mission, string featureID, Coordinates initialCoordinates, Coordinates objectivesCenter)
        {
            DBEntryFeatureMission featureDB = Database.Instance.GetEntry <DBEntryFeatureMission>(featureID);

            if (featureDB == null) // Feature doesn't exist
            {
                BriefingRoom.PrintToLog($"Mission feature {featureID} not found.", LogMessageErrorLevel.Warning);
                return;
            }
            Coalition coalition = featureDB.UnitGroupFlags.HasFlag(FeatureUnitGroupFlags.Friendly) ? _template.ContextPlayerCoalition : _template.ContextPlayerCoalition.GetEnemy();

            Coordinates pointSearchCenter = Coordinates.Lerp(initialCoordinates, objectivesCenter, featureDB.UnitGroupSpawnDistance);
            Coordinates?spawnPoint        =
                _unitMaker.SpawnPointSelector.GetRandomSpawnPoint(
                    featureDB.UnitGroupValidSpawnPoints, pointSearchCenter,
                    featureDB.UnitGroupFlags.HasFlag(FeatureUnitGroupFlags.AwayFromMissionArea) ? new MinMaxD(50, 100) : new MinMaxD(0, 5),
                    coalition: featureDB.UnitGroupFlags.HasFlag(FeatureUnitGroupFlags.IgnoreBorders) ? null : coalition
                    );

            if (!spawnPoint.HasValue) // No spawn point found
            {
                BriefingRoom.PrintToLog($"No spawn point found for mission feature {featureID}.", LogMessageErrorLevel.Warning);
                return;
            }

            var goPoint = spawnPoint.Value;

            if (featureDB.UnitGroupFlags.HasFlag(FeatureUnitGroupFlags.MoveTowardObjectives))
            {
                goPoint = objectivesCenter;
            }
            else if (featureDB.UnitGroupFlags.HasFlag(FeatureUnitGroupFlags.MoveTowardPlayerBase))
            {
                goPoint = initialCoordinates;
            }

            Coordinates coordinates2 = goPoint + Coordinates.CreateRandom(5, 20) * Toolbox.NM_TO_METERS;
            Dictionary <string, object> extraSettings = new Dictionary <string, object>();
            UnitMakerGroupInfo?         groupInfo     = AddMissionFeature(featureDB, mission, spawnPoint.Value, coordinates2, ref extraSettings);

            AddBriefingRemarkFromFeature(featureDB, mission, false, groupInfo, extraSettings);
        }
示例#16
0
        protected override bool OnLoad(string iniFilePath)
        {
            var ini = new INIFile(iniFilePath);

            BriefingRemarks = new string[2][];
            BriefingRemarks[(int)Side.Ally]  = ini.GetValueArray <string>("Briefing", "Remarks", ';');
            BriefingRemarks[(int)Side.Enemy] = ini.GetValueArray <string>("Briefing", "Remarks.Enemy", ';');

            // Included files
            IncludeLua         = Toolbox.AddMissingFileExtensions(ini.GetValueArray <string>("Include", "Lua"), ".lua");
            IncludeLuaSettings = ini.GetValue <string>("Lua", "LuaSettings");
            IncludeOgg         = Toolbox.AddMissingFileExtensions(ini.GetValueArray <string>("Include", "Ogg"), ".ogg");

            foreach (string f in IncludeLua)
            {
                if (!File.Exists($"{SourceLuaDirectory}{f}"))
                {
                    BriefingRoom.PrintToLog($"File \"{SourceLuaDirectory}{f}\", required by feature \"{ID}\", doesn't exist.", LogMessageErrorLevel.Warning);
                }
            }

            foreach (string f in IncludeOgg)
            {
                if (!File.Exists($"{BRPaths.INCLUDE_OGG}{f}"))
                {
                    BriefingRoom.PrintToLog($"File \"{BRPaths.INCLUDE_OGG}{f}\", required by feature \"{ID}\", doesn't exist.", LogMessageErrorLevel.Warning);
                }
            }

            // Unit group
            UnitGroupFamilies         = ini.GetValueArray <UnitFamily>("UnitGroup", "Families");
            UnitGroupFlags            = ini.GetValueArrayAsEnumFlags <FeatureUnitGroupFlags>("UnitGroup", "Flags");
            UnitGroupLuaGroup         = ini.GetValue <string>("UnitGroup", "Lua.Group");
            UnitGroupLuaUnit          = ini.GetValue <string>("UnitGroup", "Lua.Unit");
            UnitGroupSize             = ini.GetValue <MinMaxI>("UnitGroup", "Size");
            ExtraGroups               = ini.GetValue <MinMaxI>("UnitGroup", "ExtraGroups");
            UnitGroupSpawnDistance    = ini.GetValue <double>("UnitGroup", "SpawnDistance");
            UnitGroupPayload          = ini.GetValue <string>("UnitGroup", "Payload", "default");
            UnitGroupValidSpawnPoints = DatabaseTools.CheckSpawnPoints(ini.GetValueArray <SpawnPointType>("UnitGroup", "ValidSpawnPoints"));

            return(true);
        }
示例#17
0
        internal static Month GenerateMissionDate(DCSMission mission, MissionTemplateRecord template)
        {
            int   day;
            Month month;

            // Select a random year from the most recent coalition's decade.
            var year = Toolbox.GetRandomYearFromDecade(template.ContextDecade);

            BriefingRoom.PrintToLog($"No fixed date provided in the mission template, generating date in decade {template.ContextDecade}");

            if (template.EnvironmentSeason == Season.Random) // Random season, pick any day of the year.
            {
                month = (Month)Toolbox.RandomInt(12);
                day   = Toolbox.RandomMinMax(1, GeneratorTools.GetDaysPerMonth(month, year));
            }
            else // Pick a date according to the desired season
            {
                Month[] seasonMonths = GetMonthsForSeason(template.EnvironmentSeason);

                int monthIndex = Toolbox.RandomInt(4);
                month = seasonMonths[monthIndex];
                switch (monthIndex)
                {
                case 0:     // First month of the season, season begins on the 21st
                    day = Toolbox.RandomMinMax(21, GeneratorTools.GetDaysPerMonth(month, year)); break;

                case 3:     // Last month of the season, season ends on the 20th
                    day = Toolbox.RandomMinMax(1, 20); break;

                default:
                    day = Toolbox.RandomMinMax(1, GeneratorTools.GetDaysPerMonth(month, year)); break;
                }
            }

            mission.SetValue("DateDay", day);
            mission.SetValue("DateMonth", (int)month + 1);
            mission.SetValue("DateYear", year);
            mission.SetValue("BriefingDate", $"{(int)month + 1:00}/{day:00}/{year:0000}");

            BriefingRoom.PrintToLog($"Misson date set to {day} {month} {year}.");
            return(month);
        }
示例#18
0
        internal void Load()
        {
            int i;

            BriefingRoom.PrintToLog("Loading common global settings...");
            INIFile commonIni = new($"{BRPaths.DATABASE}Common.ini");

            CommonOGG = commonIni.GetValueArray <string>("Include", "CommonOgg");
            foreach (string f in CommonOGG)
            {
                if (!File.Exists($"{BRPaths.INCLUDE_OGG}{f}.ogg"))
                {
                    BriefingRoom.PrintToLog($"File \"Include\\Ogg\\{f}.ogg\" doesn't exist.", LogMessageErrorLevel.Warning);
                }
            }


            BriefingRoom.PrintToLog("Loading common air defense settings...");
            AirDefense = new DBCommonAirDefense();

            BriefingRoom.PrintToLog("Loading common CAP settings...");
            CAP = new DBCommonCAP();

            BriefingRoom.PrintToLog("Loading common carrier group settings...");
            CarrierGroup = new DBCommonCarrierGroup();

            BriefingRoom.PrintToLog("Loading common names settings...");
            Names = new DBCommonNames();

            BriefingRoom.PrintToLog("Loading common briefing settings...");
            Briefing = new DBCommonBriefing();

            BriefingRoom.PrintToLog("Loading common wind settings...");
            INIFile windIni = new($"{BRPaths.DATABASE}Wind.ini");

            Wind = new DBCommonWind[Toolbox.EnumCount <Wind>() - 1]; // -1 because we don't want "Random"
            for (i = 0; i < Wind.Length; i++)
            {
                Wind[i] = new DBCommonWind(windIni, ((Wind)i).ToString());
            }
        }
示例#19
0
        internal static void GenerateIngressAndEgressWaypoints(MissionTemplateRecord template, List <Waypoint> waypoints, Coordinates averageInitialLocation, Coordinates objectivesCenter)
        {
            if (!template.MissionFeatures.Contains("IngressEgressWaypoints"))
            {
                return;
            }

            BriefingRoom.PrintToLog($"Generating ingress and egress waypoints...");

            double      flightPathLength    = (objectivesCenter - averageInitialLocation).GetLength();
            double      ingressDeviation    = Math.Max(4.0, flightPathLength * .15);
            Coordinates baseIngressPosition = averageInitialLocation + (objectivesCenter - averageInitialLocation) * .7f;

            waypoints.Insert(0,
                             new Waypoint(
                                 Database.Instance.Common.Names.WPIngressName,
                                 baseIngressPosition + Coordinates.CreateRandom(ingressDeviation * 0.9, ingressDeviation * 1.1)));

            waypoints.Add(
                new Waypoint(
                    Database.Instance.Common.Names.WPEgressName,
                    baseIngressPosition + Coordinates.CreateRandom(ingressDeviation * 0.9, ingressDeviation * 1.1)));
        }
示例#20
0
        protected override bool OnLoad(string iniFilePath)
        {
            var ini = new INIFile(iniFilePath);

            Features = Database.CheckIDs <DBEntryFeatureObjective>(ini.GetValueArray <string>("ObjectivePreset", "Features"));
            Options  = ini.GetValueArray <ObjectiveOption>("ObjectivePreset", "Options");
            Targets  = Database.CheckIDs <DBEntryObjectiveTarget>(ini.GetValueArray <string>("ObjectivePreset", "Targets"));
            if (Targets.Length == 0)
            {
                BriefingRoom.PrintToLog($"No valid targets for objective preset \"{ID}\"", LogMessageErrorLevel.Warning); return(false);
            }
            TargetsBehaviors = Database.CheckIDs <DBEntryObjectiveTargetBehavior>(ini.GetValueArray <string>("ObjectivePreset", "TargetsBehaviors"));
            if (TargetsBehaviors.Length == 0)
            {
                BriefingRoom.PrintToLog($"No valid target behaviors for objective preset \"{ID}\"", LogMessageErrorLevel.Warning); return(false);
            }
            Tasks = Database.CheckIDs <DBEntryObjectiveTask>(ini.GetValueArray <string>("ObjectivePreset", "Tasks"));
            if (Tasks.Length == 0)
            {
                BriefingRoom.PrintToLog($"No valid tasks for objective preset \"{ID}\"", LogMessageErrorLevel.Warning); return(false);
            }

            return(true);
        }
示例#21
0
        protected override bool OnLoad(string iniFilePath)
        {
            var ini = new INIFile(iniFilePath);

            BriefingDescription = ini.GetValue <string>("Briefing", "Description");
            if (!Database.Instance.EntryExists <DBEntryBriefingDescription>(BriefingDescription))
            {
                BriefingRoom.PrintToLog($"Objective task \"{ID}\" references non-existing briefing description \"{BriefingDescription}\".", LogMessageErrorLevel.Warning);
                return(false);
            }

            BriefingTask    = new string[2];
            BriefingTask[0] = ini.GetValue <string>("Briefing", "Task.Singular");
            BriefingTask[1] = ini.GetValue <string>("Briefing", "Task.Plural");

            BriefingRemarks = ini.GetValueArray <string>("Briefing", "Remarks", ';');

            CompletionTriggerLua = Toolbox.AddMissingFileExtension(ini.GetValue <string>("ObjectiveTask", "CompletionTriggerLua"), ".lua");
            if (!File.Exists($"{BRPaths.INCLUDE_LUA_OBJECTIVETRIGGERS}{CompletionTriggerLua}"))
            {
                BriefingRoom.PrintToLog($"Completion trigger Lua file {CompletionTriggerLua} for objective task \"{ID}\" not found.", LogMessageErrorLevel.Warning);
                return(false);
            }

            TargetSide = ini.GetValue <Side>("ObjectiveTask", "TargetSide");

            ValidUnitCategories = ini.GetValueArray <UnitCategory>("ObjectiveTask", "ValidUnitCategories").Distinct().ToArray();
            if (ValidUnitCategories.Length == 0)
            {
                ValidUnitCategories = Toolbox.GetEnumValues <UnitCategory>();                                 // No category means all categories
            }
            // Included files
            IncludeOgg = Toolbox.AddMissingFileExtensions(ini.GetValueArray <string>("Include", "Ogg"), ".ogg");

            return(true);
        }
示例#22
0
        private static void CreateCAPGroups(
            UnitMaker unitMaker, MissionTemplateRecord template, Side side,
            Coalition coalition, AmountNR capAmount, Coordinates centerPoint,
            Coordinates opposingPoint, Coordinates destination, ref List <int> capAircraftGroupIDs)
        {
            var commonCAPDB             = Database.Instance.Common.CAP;
            DBCommonCAPLevel capLevelDB = commonCAPDB.CAPLevels[(int)capAmount];

            int unitsLeftToSpawn = capLevelDB.UnitCount.GetValue();

            if (unitsLeftToSpawn < 1)
            {
                return;                        // No groups to add, no need to go any further
            }
            do
            {
                int groupSize = Toolbox.RandomFrom(commonCAPDB.GroupSize);
                groupSize         = Math.Min(unitsLeftToSpawn, groupSize);
                unitsLeftToSpawn -= groupSize;

                // Find spawn point at the proper distance from the objective(s), but not to close from starting airbase
                Coordinates?spawnPoint =
                    unitMaker.SpawnPointSelector.GetRandomSpawnPoint(
                        new SpawnPointType[] { SpawnPointType.Air },
                        centerPoint,
                        commonCAPDB.DistanceFromCenter,
                        opposingPoint,
                        new MinMaxD(commonCAPDB.MinDistanceFromOpposingPoint, 99999),
                        GeneratorTools.GetSpawnPointCoalition(template, side));

                // No spawn point found, stop here.
                if (!spawnPoint.HasValue)
                {
                    BriefingRoom.PrintToLog($"No spawn point found for {coalition} combat air patrols.", LogMessageErrorLevel.Warning);
                    return;
                }

                Coordinates groupDestination = destination + Coordinates.CreateRandom(10, 20) * Toolbox.NM_TO_METERS;

                var extraSettings = new Dictionary <string, object> {
                    { "Payload", "Air-To-Air" },
                    { "GroupX2", groupDestination.X },
                    { "GroupY2", groupDestination.Y }
                };

                var luaUnit  = commonCAPDB.LuaUnit;
                var luaGroup = commonCAPDB.LuaGroup;
                var spawnpointCoordinates = spawnPoint.Value;
                var unitFamilies          = commonCAPDB.UnitFamilies.ToList();
                if (template.MissionFeatures.Contains("ContextGroundStartAircraft"))
                {
                    luaGroup += "Parked";
                    luaUnit  += "Parked";
                    var(airbase, parkingSpotIDsList, parkingSpotCoordinatesList) = unitMaker.SpawnPointSelector.GetAirbaseAndParking(template, spawnPoint.Value, groupSize, coalition, unitFamilies.First());
                    spawnpointCoordinates = airbase.Coordinates;
                    extraSettings.AddIfKeyUnused("ParkingID", parkingSpotIDsList.ToArray());
                    extraSettings.AddIfKeyUnused("GroupAirbaseID", airbase.DCSID);
                    extraSettings.AddIfKeyUnused("UnitX", (from Coordinates coordinates in parkingSpotCoordinatesList select coordinates.X).ToArray());
                    extraSettings.AddIfKeyUnused("UnitY", (from Coordinates coordinates in parkingSpotCoordinatesList select coordinates.Y).ToArray());
                }


                UnitMakerGroupInfo?groupInfo = unitMaker.AddUnitGroup(
                    unitFamilies, groupSize, side,
                    luaGroup, luaUnit,
                    spawnpointCoordinates,
                    0,
                    extraSettings.ToArray());

                if (!groupInfo.HasValue) // Failed to generate a group
                {
                    BriefingRoom.PrintToLog($"Failed to find units for {coalition} air defense unit group.", LogMessageErrorLevel.Warning);
                }

                capAircraftGroupIDs.Add(groupInfo.Value.GroupID);
            } while (unitsLeftToSpawn > 0);
        }
示例#23
0
        protected override bool OnLoad(string iniFilePath)
        {
            int i;

            var ini = new INIFile(iniFilePath);

            // [Briefing] section
            BriefingNames = ini.GetValueArray <string>("Briefing", "Names");

            // [Theater] section
            DCSID               = ini.GetValue <string>("Theater", "DCSID");
            DefaultMapCenter    = ini.GetValue <Coordinates>("Theater", "DefaultMapCenter");
            MagneticDeclination = ini.GetValue <double>("Theater", "MagneticDeclination");

            // [Daytime] section
            DayTime = new MinMaxI[12];
            for (i = 0; i < 12; i++)
            {
                MinMaxI?dayTimeValue = ParseMinMaxTime(ini.GetValueArray <string>("Daytime", ((Month)i).ToString()));

                if (!dayTimeValue.HasValue) // Cast failed
                {
                    BriefingRoom.PrintToLog(
                        $"Wrong format for daytime value for month {(Month)i} in theater {ID}, using default value",
                        LogMessageErrorLevel.Warning);
                }

                DayTime[i] = dayTimeValue ?? DEFAULT_DAYTIME;
            }


            // Water Coordinates
            WaterCoordinates = new List <Coordinates>();
            foreach (string key in ini.GetKeysInSection("WaterCoordinates"))
            {
                WaterCoordinates.Add(ini.GetValue <Coordinates>("WaterCoordinates", key));
            }


            List <DBEntryTheaterSpawnPoint> spawnPointsList = new List <DBEntryTheaterSpawnPoint>();

            foreach (string key in ini.GetKeysInSection("SpawnPoints"))
            {
                DBEntryTheaterSpawnPoint sp = new DBEntryTheaterSpawnPoint();
                if (sp.Load(ini, key))
                {
                    spawnPointsList.Add(sp);
                }
            }
            SpawnPoints = spawnPointsList.ToArray();

            WaterExclusionCoordinates = new List <List <Coordinates> >();
            if (ini.GetSections().Contains("waterexclusioncoordinates"))
            {
                // Water Exclusion Coordinates
                var tempList = new List <Coordinates>();
                var groupID  = ini.GetKeysInSection("WaterExclusionCoordinates").First().Split(".")[0];
                foreach (string key in ini.GetKeysInSection("WaterExclusionCoordinates"))
                {
                    var newGroupId = key.Split(".")[0];
                    if (groupID != newGroupId)
                    {
                        groupID = newGroupId;
                        WaterExclusionCoordinates.Add(tempList);
                        tempList = new List <Coordinates>();
                    }
                    tempList.Add(ini.GetValue <Coordinates>("WaterExclusionCoordinates", key));
                }
                WaterExclusionCoordinates.Add(tempList);
            }

            // [Temperature] section
            Temperature = new MinMaxI[12];
            for (i = 0; i < 12; i++)
            {
                Temperature[i] = ini.GetValue <MinMaxI>("Temperature", ((Month)i).ToString());
            }

            return(true);
        }
示例#24
0
        internal static void GeneratePlayerFlightGroup(
            UnitMaker unitMaker,
            DCSMission mission,
            MissionTemplateRecord template,
            MissionTemplateFlightGroupRecord flightGroup,
            DBEntryAirbase playerAirbase,
            List <Waypoint> waypoints,
            Dictionary <string, UnitMakerGroupInfo> carrierDictionary,
            Coordinates averageInitialLocation,
            Coordinates objectivesCenter)
        {
            DBEntryAirbase  airbase             = playerAirbase;
            List <Waypoint> flightWaypoints     = new List <Waypoint>(waypoints);
            Coordinates     groupStartingCoords = playerAirbase.Coordinates;

            var package = template.AircraftPackages.FirstOrDefault(x => x.FlightGroupIndexes.Contains(template.PlayerFlightGroups.IndexOf(flightGroup)));

            if (package is not null)
            {
                var missionPackage = mission.MissionPackages.First(x => x.RecordIndex == template.AircraftPackages.IndexOf(package));
                flightWaypoints     = missionPackage.Waypoints;
                airbase             = missionPackage.Airbase;
                groupStartingCoords = missionPackage.Airbase.Coordinates;
            }
            DBEntryUnit unitDB = Database.Instance.GetEntry <DBEntryUnit>(flightGroup.Aircraft);

            // Not an unit, or not a player-controllable unit, abort.
            if ((unitDB == null) || !unitDB.AircraftData.PlayerControllable)
            {
                throw new BriefingRoomException($"Player flight group unit {flightGroup.Aircraft} does not exist or is not player-controllable.");
            }
            if (unitDB.AircraftData.MinimumRunwayLengthFt > 0 && airbase.RunwayLengthFt < unitDB.AircraftData.MinimumRunwayLengthFt)
            {
                BriefingRoom.PrintToLog($"Runway at {airbase.Name}({airbase.RunwayLengthFt}ft) is shorter than {unitDB.UIDisplayName}({unitDB.AircraftData.MinimumRunwayLengthFt}ft) required runway length.", LogMessageErrorLevel.Warning);
            }

            List <int>         parkingSpotIDsList         = new List <int>();
            List <Coordinates> parkingSpotCoordinatesList = new List <Coordinates>();
            var    groupLuaFile  = "GroupAircraftPlayer";
            var    carrierUnitID = 0;
            string carrierName   = null;
            var    side          = flightGroup.Hostile ? Side.Enemy : Side.Ally;
            var    country       = flightGroup.Country;
            var    payload       = flightGroup.Payload;
            var    extraSettings = new Dictionary <string, object>();
            UnitMakerGroupFlags unitMakerGroupFlags = flightGroup.AIWingmen ? UnitMakerGroupFlags.FirstUnitIsClient : 0;
            DCSSkillLevel       skillLevel          = flightGroup.AIWingmen ? Toolbox.RandomFrom(DCSSkillLevel.High, DCSSkillLevel.Excellent) : DCSSkillLevel.Client;

            if (!string.IsNullOrEmpty(flightGroup.Carrier) && carrierDictionary.ContainsKey(flightGroup.Carrier) && !flightGroup.Hostile) // Carrier take off
            {
                var carrier = carrierDictionary[flightGroup.Carrier];
                if (carrier.UnitDB.Families.Contains(UnitFamily.ShipCarrierSTOVL) && flightGroup.Carrier != "LHA_Tarawa")
                {
                    extraSettings.AddIfKeyUnused("Speed", 0);
                    unitMakerGroupFlags = 0;
                    skillLevel          = DCSSkillLevel.Client;
                    if (flightGroup.Aircraft == "AV8BNA")
                    {
                        payload = "EMPTY";
                    }
                }
                groupLuaFile  = "GroupAircraftPlayerCarrier";
                carrierUnitID = carrier.UnitsID[0];
                carrierName   = carrier.UnitDB.UIDisplayName;

                for (int i = 0; i < flightGroup.Count; i++)
                {
                    parkingSpotIDsList.Add(i + 1);
                    parkingSpotCoordinatesList.Add(carrier.Coordinates);
                }
                groupStartingCoords = carrier.Coordinates;
            }
            else if (flightGroup.Hostile)
            {
                var coalition = GeneratorTools.GetSpawnPointCoalition(template, side, true);
                var(hostileAirbase, hostileParkingSpotIDsList, hostileParkingSpotCoordinatesList) = unitMaker.SpawnPointSelector.GetAirbaseAndParking(template, objectivesCenter, flightGroup.Count, coalition.Value, unitDB.Families.First());
                parkingSpotIDsList         = hostileParkingSpotIDsList;
                parkingSpotCoordinatesList = hostileParkingSpotCoordinatesList;
                groupStartingCoords        = hostileParkingSpotCoordinatesList.First();
                airbase = hostileAirbase;

                if (country == Country.CJTFBlue || country == Country.CJTFRed)
                {
                    country = coalition == Coalition.Blue ? Country.CJTFBlue : Country.CJTFRed;
                }
            }
            else // Land airbase take off
            {
                var parkingSpots = unitMaker.SpawnPointSelector.GetFreeParkingSpots(airbase.DCSID, flightGroup.Count, unitDB.Families[0]);
                parkingSpotIDsList         = parkingSpots.Select(x => x.DCSID).ToList();
                parkingSpotCoordinatesList = parkingSpots.Select(x => x.Coordinates).ToList();
                groupStartingCoords        = parkingSpotCoordinatesList.First();
            }



            extraSettings.AddIfKeyUnused("Payload", payload);
            extraSettings.AddIfKeyUnused("Skill", skillLevel);
            extraSettings.AddIfKeyUnused("PlayerStartingAction", GeneratorTools.GetPlayerStartingAction(flightGroup.StartLocation));
            extraSettings.AddIfKeyUnused("PlayerStartingType", GeneratorTools.GetPlayerStartingType(flightGroup.StartLocation));
            extraSettings.AddIfKeyUnused("Country", country);
            extraSettings.AddIfKeyUnused("InitialWPName", Database.Instance.Common.Names.WPInitialName);
            extraSettings.AddIfKeyUnused("FinalWPName", Database.Instance.Common.Names.WPFinalName);
            extraSettings.AddIfKeyUnused("ParkingID", parkingSpotIDsList.ToArray());
            extraSettings.AddIfKeyUnused("PlayerWaypoints", GenerateFlightPlanLua(flightWaypoints));
            extraSettings.AddIfKeyUnused("LastPlayerWaypointIndex", flightWaypoints.Count + 2);
            extraSettings.AddIfKeyUnused("LinkUnit", carrierUnitID);
            extraSettings.AddIfKeyUnused("UnitX", (from Coordinates coordinates in parkingSpotCoordinatesList select coordinates.X).ToArray());
            extraSettings.AddIfKeyUnused("UnitY", (from Coordinates coordinates in parkingSpotCoordinatesList select coordinates.Y).ToArray());
            extraSettings.AddIfKeyUnused("MissionAirbaseX", groupStartingCoords.X);
            extraSettings.AddIfKeyUnused("MissionAirbaseY", groupStartingCoords.Y);
            extraSettings.AddIfKeyUnused("MissionAirbaseID", airbase.DCSID);
            extraSettings.AddIfKeyUnused("Livery", flightGroup.Livery);

            UnitMakerGroupInfo?groupInfo = unitMaker.AddUnitGroup(
                Enumerable.Repeat(flightGroup.Aircraft, flightGroup.Count).ToArray(), side, unitDB.Families[0],
                groupLuaFile, "UnitAircraftParked", groupStartingCoords,
                unitMakerGroupFlags,
                extraSettings.ToArray()
                );

            if (!groupInfo.HasValue)
            {
                BriefingRoom.PrintToLog("Failed to generate player flight group.", LogMessageErrorLevel.Warning);
                return;
            }

            SaveFlightGroup(mission, groupInfo, flightGroup, unitDB, carrierName ?? airbase.Name);
            SaveWaypointsToBriefing(
                mission,
                groupStartingCoords,
                flightWaypoints,
                template.OptionsMission.Contains("ImperialUnitsForBriefing"),
                groupInfo);
        }
示例#25
0
        internal static async Task <DCSMission> GenerateAsync(MissionTemplateRecord template, bool useObjectivePresets)
        {
            // Check for missing entries in the database
            GeneratorTools.CheckDBForMissingEntry <DBEntryCoalition>(template.ContextCoalitionBlue);
            GeneratorTools.CheckDBForMissingEntry <DBEntryCoalition>(template.ContextCoalitionRed);
            GeneratorTools.CheckDBForMissingEntry <DBEntryWeatherPreset>(template.EnvironmentWeatherPreset, true);
            GeneratorTools.CheckDBForMissingEntry <DBEntryTheater>(template.ContextTheater);
            if (!template.PlayerFlightGroups.Any(x => !x.Hostile))
            {
                throw new BriefingRoomException("Cannot have all players on hostile side.");
            }

            var mission = new DCSMission();

            var waypoints = new List <Waypoint>();
            var immediateActivationAircraftGroupsIDs = new List <int>();
            var lateActivationAircraftGroupsIDs      = new List <int>();

            var theaterDB   = Database.Instance.GetEntry <DBEntryTheater>(template.ContextTheater);
            var situationDB = Toolbox.RandomFrom(
                Database.Instance.GetAllEntries <DBEntrySituation>()
                .Where(x => x.Theater == template.ContextTheater.ToLower())
                .ToArray()
                );

            if (template.ContextSituation.StartsWith(template.ContextTheater))
            {
                situationDB = Database.Instance.GetEntry <DBEntrySituation>(template.ContextSituation);
            }


            var coalitionsDB = new DBEntryCoalition[]
            {
                Database.Instance.GetEntry <DBEntryCoalition>(template.ContextCoalitionBlue),
                Database.Instance.GetEntry <DBEntryCoalition>(template.ContextCoalitionRed)
            };

            // Copy values from the template
            mission.SetValue("BriefingTheater", theaterDB.UIDisplayName);
            mission.SetValue("BriefingSituation", situationDB.UIDisplayName);
            mission.SetValue("BriefingAllyCoalition", coalitionsDB[(int)template.ContextPlayerCoalition].UIDisplayName);
            mission.SetValue("BriefingEnemyCoalition", coalitionsDB[(int)template.ContextPlayerCoalition.GetEnemy()].UIDisplayName);
            mission.SetValue("EnableAudioRadioMessages", !template.OptionsMission.Contains("RadioMessagesTextOnly"));
            mission.SetValue("LuaPlayerCoalition", $"coalition.side.{template.ContextPlayerCoalition.ToString().ToUpperInvariant()}");
            mission.SetValue("LuaEnemyCoalition", $"coalition.side.{template.ContextPlayerCoalition.GetEnemy().ToString().ToUpperInvariant()}");
            mission.SetValue("TheaterID", theaterDB.DCSID);
            mission.SetValue("AircraftActivatorCurrentQueue", ""); // Just to make sure aircraft groups spawning queues are empty
            mission.SetValue("AircraftActivatorReserveQueue", "");
            mission.SetValue("MissionPlayerSlots", template.GetPlayerSlotsCount() == 1 ? "Single-player mission" : $"{template.GetPlayerSlotsCount()}-players mission");


            foreach (string oggFile in Database.Instance.Common.CommonOGG)
            {
                mission.AddMediaFile($"l10n/DEFAULT/{Toolbox.AddMissingFileExtension(oggFile, ".ogg")}", $"{BRPaths.INCLUDE_OGG}{Toolbox.AddMissingFileExtension(oggFile, ".ogg")}");
            }


            var coalitionsCountries = MissionGeneratorCountries.GenerateCountries(mission, template);


            var unitMaker = new UnitMaker(mission, template, coalitionsDB, theaterDB, situationDB, template.ContextPlayerCoalition, coalitionsCountries, template.GetPlayerSlotsCount() == 1);

            var drawingMaker = new DrawingMaker(mission, template, theaterDB, situationDB);
            var zoneMaker    = new ZoneMaker(unitMaker);


            BriefingRoom.PrintToLog("Generating mission date and time...");
            var month = MissionGeneratorDateTime.GenerateMissionDate(mission, template);

            MissionGeneratorDateTime.GenerateMissionTime(mission, template, theaterDB, month);


            BriefingRoom.PrintToLog("Setting up airbases...");
            var airbasesGenerator = new MissionGeneratorAirbases(template, situationDB);
            var requiredRunway    = template.PlayerFlightGroups.Select(x => Database.Instance.GetEntry <DBEntryUnit>(x.Aircraft).AircraftData.MinimumRunwayLengthFt).Max();
            var playerAirbase     = airbasesGenerator.SelectStartingAirbase(mission, template.FlightPlanTheaterStartingAirbase, requiredRunway: requiredRunway);

            mission.Briefing.AddItem(DCSMissionBriefingItemType.Airbase, $"{playerAirbase.Name}\t{playerAirbase.Runways}\t{playerAirbase.ATC}\t{playerAirbase.ILS}\t{playerAirbase.TACAN}");
            airbasesGenerator.SelectStartingAirbaseForPackages(mission, playerAirbase);
            airbasesGenerator.SetupAirbasesCoalitions(mission, playerAirbase);
            zoneMaker.AddAirbaseZones(playerAirbase, mission.MissionPackages);
            mission.SetValue("PlayerAirbaseName", playerAirbase.Name);
            mission.SetValue("MissionAirbaseX", playerAirbase.Coordinates.X);
            mission.SetValue("MissionAirbaseY", playerAirbase.Coordinates.Y);


            BriefingRoom.PrintToLog("Generating mission weather...");
            var turbulenceFromWeather = MissionGeneratorWeather.GenerateWeather(mission, template, theaterDB, month, playerAirbase);

            var(windSpeedAtSeaLevel, windDirectionAtSeaLevel) = MissionGeneratorWeather.GenerateWind(mission, template, turbulenceFromWeather);

            // Generate objectives
            BriefingRoom.PrintToLog("Generating objectives...");
            var objectiveCoordinates        = new List <Coordinates>();
            var objectiveTargetUnitFamilies = new List <UnitFamily>();
            var lastObjectiveCoordinates    = playerAirbase.Coordinates;
            var objectivesGenerator         = new MissionGeneratorObjectives(unitMaker, drawingMaker, template);
            var objectiveGroupedWaypoints   = new List <List <Waypoint> >();
            var i = 0;

            foreach (var objectiveTemplate in template.Objectives)
            {
                var(objectiveCoords, waypointGroup) = objectivesGenerator.GenerateObjective(
                    mission, template, situationDB,
                    objectiveTemplate, lastObjectiveCoordinates, playerAirbase, useObjectivePresets,
                    ref i, ref objectiveCoordinates, ref waypoints, ref objectiveTargetUnitFamilies);
                lastObjectiveCoordinates = objectiveCoords;
                objectiveGroupedWaypoints.Add(waypointGroup);
                i++;
            }
            var objectivesCenter = (objectiveCoordinates.Count == 0) ? playerAirbase.Coordinates : Coordinates.Sum(objectiveCoordinates) / objectiveCoordinates.Count;

            mission.SetValue("MissionCenterX", objectivesCenter.X);
            mission.SetValue("MissionCenterY", objectivesCenter.Y);

            // Generate carrier groups
            BriefingRoom.PrintToLog("Generating carrier groups...");
            var carrierDictionary = MissionGeneratorCarrierGroup.GenerateCarrierGroup(
                unitMaker, zoneMaker, mission, template,
                playerAirbase.Coordinates, objectivesCenter,
                windSpeedAtSeaLevel, windDirectionAtSeaLevel);
            var averageInitialPosition = playerAirbase.Coordinates;

            if (carrierDictionary.Count > 0)
            {
                averageInitialPosition = (averageInitialPosition + carrierDictionary.First().Value.Coordinates) / 2.0;
            }

            // Generate extra flight plan info
            MissionGeneratorFlightPlan.GenerateBullseyes(mission, objectivesCenter);
            MissionGeneratorFlightPlan.GenerateObjectiveWPCoordinatesLua(template, mission, waypoints, drawingMaker);
            MissionGeneratorFlightPlan.GenerateAircraftPackageWaypoints(template, mission, objectiveGroupedWaypoints, averageInitialPosition, objectivesCenter);
            MissionGeneratorFlightPlan.GenerateIngressAndEgressWaypoints(template, waypoints, averageInitialPosition, objectivesCenter);

            // Generate surface-to-air defenses
            MissionGeneratorAirDefense.GenerateAirDefense(template, unitMaker, averageInitialPosition, objectivesCenter);

            // Generate combat air patrols
            var capGroupsID = MissionGeneratorCombatAirPatrols.GenerateCAP(unitMaker, template, averageInitialPosition, objectivesCenter);

            foreach (int capGroupID in capGroupsID) // Add 50% of CAP groups to the list of A/C activated on takeoff, the other 50% to the list of A/C activated later.
            {
                if (Toolbox.RandomChance(2))
                {
                    immediateActivationAircraftGroupsIDs.Add(capGroupID);
                }
                else
                {
                    lateActivationAircraftGroupsIDs.Add(capGroupID);
                }
            }

            // Generate player flight groups
            BriefingRoom.PrintToLog("Generating player flight groups...");
            foreach (var templateFlightGroup in template.PlayerFlightGroups)
            {
                MissionGeneratorPlayerFlightGroups.GeneratePlayerFlightGroup(unitMaker, mission, template, templateFlightGroup, playerAirbase, waypoints, carrierDictionary, averageInitialPosition, objectivesCenter);
            }

            // Generate mission features
            BriefingRoom.PrintToLog("Generating mission features...");
            mission.AppendValue("ScriptMissionFeatures", ""); // Just in case there's no features
            var missionFeaturesGenerator = new MissionGeneratorFeaturesMission(unitMaker, template);

            foreach (var templateFeature in template.MissionFeatures)
            {
                missionFeaturesGenerator.GenerateMissionFeature(mission, templateFeature, playerAirbase.Coordinates, objectivesCenter);
            }


            // Add ogg files to the media files dictionary
            foreach (string mediaFile in mission.GetMediaFileNames())
            {
                if (!mediaFile.ToLowerInvariant().EndsWith(".ogg"))
                {
                    continue;                                                 // Not an .ogg file
                }
                mission.AppendValue("MapResourcesFiles", $"[\"ResKey_Snd_{Path.GetFileNameWithoutExtension(mediaFile)}\"] = \"{Path.GetFileName(mediaFile)}\",\n");
            }

            // Get unit tables from the unit maker (MUST BE DONE AFTER ALL UNITS ARE GENERATED)
            mission.SetValue("CountriesBlue", unitMaker.GetUnitsLuaTable(Coalition.Blue));
            mission.SetValue("CountriesRed", unitMaker.GetUnitsLuaTable(Coalition.Red));
            mission.SetValue("Drawings", drawingMaker.GetLuaDrawings());
            mission.SetValue("Zones", zoneMaker.GetLuaZones());

            // Generate briefing and additional mission info
            BriefingRoom.PrintToLog("Generating briefing...");
            var missionName = GeneratorTools.GenerateMissionName(template.BriefingMissionName);

            mission.Briefing.Name = missionName;
            mission.SetValue("MISSIONNAME", missionName);

            MissionGeneratorBriefing.GenerateMissionBriefingDescription(mission, template, objectiveTargetUnitFamilies, situationDB);
            mission.SetValue("DescriptionText", mission.Briefing.GetBriefingAsRawText("\\\n"));

            // Generate mission options
            BriefingRoom.PrintToLog("Generating options...");
            MissionGeneratorOptions.GenerateForcedOptions(mission, template);

            // Generate warehouses
            BriefingRoom.PrintToLog("Generating warehouses...");
            MissionGeneratorWarehouses.GenerateWarehouses(mission);

            // Generate image files
            BriefingRoom.PrintToLog("Generating images...");
            MissionGeneratorImages.GenerateTitle(mission, template);
            await MissionGeneratorImages.GenerateKneeboardImagesAsync(mission);

            return(mission);
        }
示例#26
0
        internal Tuple <Country, List <string> > GetRandomUnits(List <UnitFamily> families, Decade decade, int count, List <string> unitMods, Country?requiredCountry = null)
        {
            // Count is zero, return an empty array.
            if (count < 1)
            {
                throw new BriefingRoomException("Asking for a zero unit list");
            }
            if (families.Select(x => x.GetUnitCategory()).Any(x => x != families.First().GetUnitCategory()))
            {
                throw new BriefingRoomException($"Cannot mix Categories in types {string.Join(", ", families)}");
            }

            UnitCategory category = families.First().GetUnitCategory();
            bool         allowDifferentUnitTypes = false;

            switch (category)
            {
            // Units are planes or helicopters, make sure unit count does not exceed the maximum flight group size
            case UnitCategory.Helicopter:
            case UnitCategory.Plane:
                count = Toolbox.Clamp(count, 1, Toolbox.MAXIMUM_FLIGHT_GROUP_SIZE);
                break;

            // Units are ships or static buildings, only one unit per group (that's the law in DCS World, buddy)
            case UnitCategory.Ship:
            case UnitCategory.Static:
                count = 1;
                break;

            // Units are ground vehicles, allow multiple unit types in the group
            case UnitCategory.Vehicle:
                allowDifferentUnitTypes = true;
                break;
            }

            var validUnits = SelectValidUnits(families, decade, unitMods);

            var selectableUnits = new List <string>();
            var country         = Toolbox.RandomFrom(validUnits.Keys.ToList());

            if (requiredCountry.HasValue)
            {
                if (validUnits.ContainsKey(requiredCountry.Value))
                {
                    country = requiredCountry.Value;
                }
                else
                {
                    BriefingRoom.PrintToLog($"Could not find suitable units for {requiredCountry.Value} using units from other coalition members.", LogMessageErrorLevel.Info);
                }
            }

            selectableUnits = validUnits[country];



            // Different unit types allowed in the group, pick a random type for each unit.
            if (allowDifferentUnitTypes)
            {
                List <string> selectedUnits = new List <string>();
                for (int i = 0; i < count; i++)
                {
                    selectedUnits.Add(Toolbox.RandomFrom(selectableUnits));
                }

                return(new (country, selectedUnits.ToList()));
            }

            // Different unit types NOT allowed in the group, pick a random type and fill the whole array with it.
            string unit = Toolbox.RandomFrom(selectableUnits);

            return(new (country, Enumerable.Repeat(unit, count).ToList()));
        }
示例#27
0
        private static void CreateAirDefenseGroups(
            MissionTemplateRecord template, UnitMaker unitMaker, Side side, Coalition coalition,
            AmountNR airDefenseAmount, AirDefenseRange airDefenseRange,
            Coordinates centerPoint, Coordinates opposingPoint)
        {
            var commonAirDefenseDB = Database.Instance.Common.AirDefense;
            DBCommonAirDefenseLevel airDefenseLevelDB = commonAirDefenseDB.AirDefenseLevels[(int)airDefenseAmount];

            int groupCount = airDefenseLevelDB.GroupsInArea[(int)airDefenseRange].GetValue();

            if (groupCount < 1)
            {
                return;                  // No groups to add, no need to go any further
            }
            List <UnitFamily> unitFamilies;

            SpawnPointType[] validSpawnPoints;
            switch (airDefenseRange)
            {
            case AirDefenseRange.MediumRange:
                unitFamilies = new List <UnitFamily> {
                    UnitFamily.VehicleSAMMedium
                };
                validSpawnPoints = new SpawnPointType[] { SpawnPointType.LandLarge };
                break;

            case AirDefenseRange.LongRange:
                unitFamilies = new List <UnitFamily> {
                    UnitFamily.VehicleSAMLong
                };
                validSpawnPoints = new SpawnPointType[] { SpawnPointType.LandLarge };
                break;

            default:     // case AirDefenseRange.ShortRange:
                unitFamilies = new List <UnitFamily> {
                    UnitFamily.VehicleAAA, UnitFamily.VehicleAAAStatic, UnitFamily.VehicleInfantryMANPADS, UnitFamily.VehicleSAMShort, UnitFamily.VehicleSAMShort, UnitFamily.VehicleSAMShortIR, UnitFamily.VehicleSAMShortIR
                };
                validSpawnPoints = new SpawnPointType[] { SpawnPointType.LandSmall, SpawnPointType.LandMedium, SpawnPointType.LandLarge };
                break;
            }

            for (int i = 0; i < groupCount; i++)
            {
                // Find spawn point at the proper distance
                Coordinates?spawnPoint =
                    unitMaker.SpawnPointSelector.GetRandomSpawnPoint(
                        validSpawnPoints,
                        centerPoint,
                        commonAirDefenseDB.DistanceFromCenter[(int)side, (int)airDefenseRange],
                        opposingPoint,
                        new MinMaxD(commonAirDefenseDB.MinDistanceFromOpposingPoint[(int)side, (int)airDefenseRange], 99999),
                        GeneratorTools.GetSpawnPointCoalition(template, side));

                // No spawn point found, stop here.
                if (!spawnPoint.HasValue)
                {
                    BriefingRoom.PrintToLog($"No spawn point found for {airDefenseRange} air defense unit groups", LogMessageErrorLevel.Warning);
                    return;
                }

                var groupInfo = unitMaker.AddUnitGroup(
                    unitFamilies, 1, side,
                    "GroupVehicle", "UnitVehicle",
                    spawnPoint.Value);

                if (!groupInfo.HasValue) // Failed to generate a group
                {
                    BriefingRoom.PrintToLog(
                        $"Failed to add {airDefenseRange} air defense unit group for {coalition} coalition.",
                        LogMessageErrorLevel.Warning);
                }
            }
        }
示例#28
0
        private UnitMakerGroupInfo?AddStaticGroup(
            Country country,
            Coalition coalition,
            DCSSkillLevel?skill,
            UnitFamily unitFamily,
            Side side,
            string[] unitSets,
            string groupName,
            UnitCallsign?callsign,
            string groupTypeLua,
            Coordinates coordinates,
            UnitMakerGroupFlags unitMakerGroupFlags,
            params KeyValuePair <string, object>[] extraSettings
            )
        {
            List <int> unitsIDList   = new List <int>();
            var        initalGroupId = GroupID;

            foreach (var unitSet in unitSets)
            {
                DBEntryUnit unitDB = Database.Instance.GetEntry <DBEntryUnit>(unitSet);
                if (unitDB == null)
                {
                    BriefingRoom.PrintToLog($"Unit \"{unitSet}\" not found.", LogMessageErrorLevel.Warning);
                    continue;
                }
                int unitSetIndex = 0;
                foreach (var DCSID in unitDB.DCSIDs)
                {
                    var groupHeading = GetGroupHeading(coordinates, extraSettings);
                    SetUnitCoordinatesAndHeading(unitDB, unitSetIndex, coordinates, groupHeading, out Coordinates unitCoordinates, out double unitHeading);
                    var firstUnitID = UnitID;
                    var groupLua    = CreateGroup(
                        groupTypeLua,
                        unitCoordinates,
                        groupName,
                        extraSettings
                        );
                    var unitLua       = DCSID == "FARP" ? "UnitStaticFOB" : (unitDB.Category == UnitCategory.Cargo ? "UnitCargo" : "UnitStatic");
                    var unitsLuaTable = AddUnit(
                        DCSID,
                        groupName,
                        callsign,
                        1,
                        unitSetIndex,
                        unitDB,
                        unitLua,
                        coordinates,
                        unitMakerGroupFlags,
                        extraSettings
                        );

                    unitsIDList.Add(UnitID);
                    unitSetIndex++;
                    UnitID++;

                    GeneratorTools.ReplaceKey(ref groupLua, "Units", unitsLuaTable);


                    GeneratorTools.ReplaceKey(ref groupLua, "UnitID", firstUnitID);                                                                         // Must be after units are added
                    GeneratorTools.ReplaceKey(ref groupLua, "Skill", skill);                                                                                // Must be after units are added, because skill is set as a unit level
                    GeneratorTools.ReplaceKey(ref groupLua, "Hidden", GeneratorTools.GetHiddenStatus(Template.OptionsFogOfWar, side, unitMakerGroupFlags)); // If "hidden" was not set through custom values

                    AddUnitGroupToTable(country, UnitCategory.Static, groupLua);

                    BriefingRoom.PrintToLog($"Added group of {DCSID} {coalition} {unitFamily} at {coordinates}");

                    GroupID++;
                }
            }

            if (unitMakerGroupFlags.HasFlag(UnitMakerGroupFlags.EmbeddedAirDefense) && unitFamily != UnitFamily.StaticStructureOffshore)
            {
                var      firstUnitID     = UnitID;
                string[] airDefenseUnits = GeneratorTools.GetEmbeddedAirDefenseUnits(Template, side);
                var      groupLua        = CreateGroup(
                    "GroupVehicle",
                    coordinates,
                    groupName,
                    extraSettings
                    );
                var(unitsLuaTable, embeddedunitsIDList) = AddUnits(
                    airDefenseUnits,
                    groupName,
                    callsign,
                    "UnitVehicle",
                    coordinates,
                    unitMakerGroupFlags,
                    extraSettings
                    );
                GeneratorTools.ReplaceKey(ref groupLua, "Units", unitsLuaTable);
                GeneratorTools.ReplaceKey(ref groupLua, "UnitID", firstUnitID);                                                                         // Must be after units are added
                GeneratorTools.ReplaceKey(ref groupLua, "Skill", skill);                                                                                // Must be after units are added, because skill is set as a unit level
                GeneratorTools.ReplaceKey(ref groupLua, "Hidden", GeneratorTools.GetHiddenStatus(Template.OptionsFogOfWar, side, unitMakerGroupFlags)); // If "hidden" was not set through custom values
                GroupID++;
                unitsIDList.AddRange(embeddedunitsIDList);
                AddUnitGroupToTable(country, UnitCategory.Vehicle, groupLua);
                BriefingRoom.PrintToLog($"Added group of Embedded Air Defense for Static {coalition} {unitFamily} at {coordinates}");
            }

            DBEntryUnit firstUnitDB = Database.Instance.GetEntry <DBEntryUnit>(unitSets.First());

            if (firstUnitDB == null)
            {
                return(new UnitMakerGroupInfo(initalGroupId, coordinates, unitsIDList, groupName));
            }
            return(new UnitMakerGroupInfo(initalGroupId, coordinates, unitsIDList, groupName, firstUnitDB.AircraftData.RadioFrequency, firstUnitDB));
        }
示例#29
0
        internal UnitMakerGroupInfo?AddUnitGroup(
            string[] units, Side side, UnitFamily unitFamily,
            string groupTypeLua, string unitTypeLua,
            Coordinates coordinates,
            UnitMakerGroupFlags unitMakerGroupFlags = 0,
            params KeyValuePair <string, object>[] extraSettings)
        {
            if (units.Length == 0)
            {
                return(null);
            }

            Coalition coalition = (side == Side.Ally) ? PlayerCoalition : PlayerCoalition.GetEnemy();
            Country   country   = (coalition == Coalition.Blue) ? Country.CJTFBlue : Country.CJTFRed;

            if (extraSettings.Any(x => x.Key == "Country"))
            {
                country = (Country)extraSettings.First(x => x.Key == "Country").Value;
            }

            var skill = GeneratorTools.GetDefaultSkillLevel(Template, side);

            if (extraSettings.Any(x => x.Key == "Skill"))
            {
                skill = (DCSSkillLevel)extraSettings.First(x => x.Key == "Skill").Value;
            }


            var          isUsingSkynet = Template.MissionFeatures.Contains("SkynetIADS");
            string       groupName;
            UnitCallsign?callsign = null;

            if (unitFamily.GetUnitCategory().IsAircraft())
            {
                callsign  = CallsignGenerator.GetCallsign(unitFamily, coalition, side, isUsingSkynet);
                groupName = callsign.Value.GroupName;
                if (extraSettings.Any(x => x.Key == "PlayerStartingType") && extraSettings.First(x => x.Key == "PlayerStartingType").Value.ToString() == "TakeOffParking")
                {
                    groupName += "(C)";
                }
            }
            else
            {
                groupName = GeneratorTools.GetGroupName(GroupID, unitFamily, side, isUsingSkynet);
            }

            if (unitFamily.GetUnitCategory() == UnitCategory.Static || unitFamily.GetUnitCategory() == UnitCategory.Cargo && unitFamily != UnitFamily.FOB)
            {
                return(AddStaticGroup(
                           country,
                           coalition,
                           skill,
                           unitFamily,
                           side,
                           units,
                           groupName,
                           callsign,
                           groupTypeLua,
                           coordinates,
                           unitMakerGroupFlags,
                           extraSettings
                           ));
            }

            var groupLua = CreateGroup(
                groupTypeLua,
                coordinates,
                groupName,
                extraSettings
                );


            int firstUnitID = UnitID;

            var(unitsLuaTable, unitsIDList) = AddUnits(
                units,
                groupName,
                callsign,
                unitTypeLua,
                coordinates,
                unitMakerGroupFlags,
                extraSettings
                );


            if (unitsIDList.Count == 0)
            {
                return(null);                        // No valid units added to this group
            }
            GeneratorTools.ReplaceKey(ref groupLua, "Units", unitsLuaTable);

            DBEntryUnit firstUnitDB        = units.Select(x => Database.Instance.GetEntry <DBEntryUnit>(x)).First(x => x != null);
            var         aircraftCategories = new UnitCategory[] { UnitCategory.Helicopter, UnitCategory.Plane };
            var         isAircraft         = firstUnitDB != null && aircraftCategories.Contains(firstUnitDB.Category);

            if (isAircraft)
            {
                groupLua = ApplyAircraftFields(groupLua, firstUnitDB, extraSettings);
                if (unitMakerGroupFlags.HasFlag(UnitMakerGroupFlags.ImmediateAircraftSpawn))
                {
                    Mission.AppendValue("AircraftActivatorCurrentQueue", $"{GroupID},");
                }
                else if (unitMakerGroupFlags.HasFlag(UnitMakerGroupFlags.RadioAircraftSpawn))
                {
                    Mission.AppendValue("AircraftRadioActivator", $"{{{GroupID}, \"{groupName}\"}},");
                }
                else if (groupTypeLua != "GroupAircraftParkedUncontrolled")
                {
                    Mission.AppendValue("AircraftActivatorReserveQueue", $"{GroupID},");
                }
            }

            GeneratorTools.ReplaceKey(ref groupLua, "UnitID", firstUnitID);                                                                         // Must be after units are added
            GeneratorTools.ReplaceKey(ref groupLua, "Skill", skill);                                                                                // Must be after units are added, because skill is set as a unit level
            GeneratorTools.ReplaceKey(ref groupLua, "Hidden", GeneratorTools.GetHiddenStatus(Template.OptionsFogOfWar, side, unitMakerGroupFlags)); // If "hidden" was not set through custom values

            AddUnitGroupToTable(country, unitFamily.GetUnitCategory(), groupLua);

            BriefingRoom.PrintToLog($"Added group of {units.Length} {coalition} {unitFamily} at {coordinates}");

            GroupID++;

            if (firstUnitDB == null)
            {
                return(new UnitMakerGroupInfo(GroupID - 1, coordinates, unitsIDList, groupName));
            }
            return(new UnitMakerGroupInfo(GroupID - 1, coordinates, unitsIDList, groupName, firstUnitDB.AircraftData.RadioFrequency, firstUnitDB));
        }