Ejemplo n.º 1
0
        private static Tuple <Coordinates, Coordinates> GetSpawnAndDestination(
            UnitMaker unitMaker, MissionTemplateRecord template, DBEntryTheater theaterDB,
            List <Coordinates> usedCoordinates, Coordinates landbaseCoordinates, Coordinates objectivesCenter,
            double carrierPathDeg)
        {
            var         travelMinMax            = new MinMaxD(Database.Instance.Common.CarrierGroup.CourseLength, Database.Instance.Common.CarrierGroup.CourseLength * 2);
            Coordinates?carrierGroupCoordinates = null;
            Coordinates?destinationPath         = null;
            var         iteration   = 0;
            var         maxDistance = 25;

            while (iteration < 100)
            {
                carrierGroupCoordinates = unitMaker.SpawnPointSelector.GetRandomSpawnPoint(
                    new SpawnPointType[] { SpawnPointType.Sea },
                    landbaseCoordinates,
                    new MinMaxD(10, maxDistance),
                    objectivesCenter,
                    new MinMaxD(10, 99999),
                    GeneratorTools.GetSpawnPointCoalition(template, Side.Ally));
                if (!carrierGroupCoordinates.HasValue)
                {
                    maxDistance += 25;
                    continue;
                }
                var minDist = usedCoordinates.Aggregate(99999999.0, (acc, x) => x.GetDistanceFrom(carrierGroupCoordinates.Value) < acc ? x.GetDistanceFrom(carrierGroupCoordinates.Value) : acc);
                if (minDist < Database.Instance.Common.CarrierGroup.ShipSpacing)
                {
                    continue;
                }

                destinationPath = Coordinates.FromAngleAndDistance(carrierGroupCoordinates.Value, travelMinMax, carrierPathDeg);
                if (ShapeManager.IsPosValid(destinationPath.Value, theaterDB.WaterCoordinates, theaterDB.WaterExclusionCoordinates))
                {
                    break;
                }
                iteration++;
                if (iteration > 10)
                {
                    maxDistance += 1;
                }
            }

            if (!carrierGroupCoordinates.HasValue)
            {
                throw new BriefingRoomException($"Carrier spawnpoint could not be found.");
            }
            if (!destinationPath.HasValue)
            {
                throw new BriefingRoomException($"Carrier destination could not be found.");
            }
            if (!ShapeManager.IsPosValid(destinationPath.Value, theaterDB.WaterCoordinates, theaterDB.WaterExclusionCoordinates))
            {
                throw new BriefingRoomException($"Carrier waypoint is on shore");
            }

            return(new(carrierGroupCoordinates.Value, destinationPath.Value));
        }
Ejemplo n.º 2
0
        private void SpawnExtraGroups(T featureDB, DCSMission mission, Side groupSide, UnitMakerGroupFlags groupFlags, Coordinates coordinates, Coordinates coordinates2, Dictionary <string, object> extraSettings)
        {
            foreach (var i in Enumerable.Range(1, featureDB.ExtraGroups.GetValue()))
            {
                var groupLua    = featureDB.UnitGroupLuaGroup;
                var unitCount   = featureDB.UnitGroupSize.GetValue();
                var unitFamily  = Toolbox.RandomFrom(featureDB.UnitGroupFamilies);
                var luaUnit     = featureDB.UnitGroupLuaUnit;
                var spawnCoords = _unitMaker.SpawnPointSelector.GetRandomSpawnPoint(
                    featureDB.UnitGroupValidSpawnPoints, coordinates,
                    new MinMaxD(0, 5),
                    coalition: GeneratorTools.GetSpawnPointCoalition(_template, groupSide)
                    );
                if (!spawnCoords.HasValue)
                {
                    continue;
                }

                SetAirbase(featureDB, unitFamily, ref groupLua, ref luaUnit, groupSide, ref coordinates, coordinates2, unitCount, ref extraSettings);

                var groupInfo = _unitMaker.AddUnitGroup(
                    unitFamily, unitCount,
                    groupSide,
                    groupLua, luaUnit,
                    spawnCoords.Value, groupFlags,
                    extraSettings.ToArray());

                if (
                    groupSide == Side.Ally &&
                    groupInfo.HasValue &&
                    groupInfo.Value.UnitDB != null &&
                    groupInfo.Value.UnitDB.IsAircraft)
                {
                    mission.Briefing.AddItem(DCSMissionBriefingItemType.FlightGroup,
                                             $"{groupInfo.Value.Name}\t" +
                                             $"{unitCount}× {groupInfo.Value.UnitDB.UIDisplayName}\t" +
                                             $"{GeneratorTools.FormatRadioFrequency(groupInfo.Value.Frequency)}\t" +
                                             $"{Toolbox.FormatPayload(featureDB.UnitGroupPayload)}");
                }
            }
        }
Ejemplo n.º 3
0
        private Coordinates GetSpawnCoordinates(MissionTemplateRecord template, Coordinates lastCoordinates, DBEntryAirbase playerAirbase, DBEntryObjectiveTarget targetDB)
        {
            int objectiveDistance = template.FlightPlanObjectiveDistance;

            if (objectiveDistance < 1)
            {
                objectiveDistance = Toolbox.RandomInt(40, 160);
            }

            int objectiveSeperation = template.FlightPlanObjectiveSeperation;

            if (objectiveSeperation < 1)
            {
                objectiveSeperation = Toolbox.RandomInt(10, 100);
            }

            Coordinates?spawnPoint = UnitMaker.SpawnPointSelector.GetRandomSpawnPoint(
                targetDB.ValidSpawnPoints,
                playerAirbase.Coordinates,
                new MinMaxD(
                    objectiveDistance * OBJECTIVE_DISTANCE_VARIATION_MIN,
                    objectiveDistance * OBJECTIVE_DISTANCE_VARIATION_MAX),
                lastCoordinates,
                new MinMaxD(
                    objectiveSeperation * OBJECTIVE_DISTANCE_VARIATION_MIN,
                    objectiveSeperation * OBJECTIVE_DISTANCE_VARIATION_MAX),
                GeneratorTools.GetSpawnPointCoalition(template, Side.Enemy));

            if (!spawnPoint.HasValue)
            {
                throw new BriefingRoomException($"Failed to spawn objective unit group. {String.Join(",", targetDB.ValidSpawnPoints.Select(x => x.ToString()).ToList())} Please try again (Consider Adusting Flight Plan)");
            }

            Coordinates objectiveCoordinates = spawnPoint.Value;

            return(objectiveCoordinates);
        }
Ejemplo n.º 4
0
        private void SetAirbase(T featureDB, UnitFamily unitFamily, ref string groupLua, ref string luaUnit, Side groupSide, ref Coordinates coordinates, Coordinates coordinates2, int unitCount, ref Dictionary <string, object> extraSettings)
        {
            if ((_template.MissionFeatures.Contains("ContextGroundStartAircraft") || featureDB.UnitGroupFlags.HasFlag(FeatureUnitGroupFlags.GroundStart)) && Toolbox.IsAircraft(unitFamily.GetUnitCategory()))
            {
                if (groupLua != "GroupAircraftParkedUncontrolled")
                {
                    groupLua += "Parked";
                }
                luaUnit += "Parked";
                var(airbase, parkingSpotIDsList, parkingSpotCoordinatesList) = _unitMaker.SpawnPointSelector.GetAirbaseAndParking(
                    _template, coordinates, unitCount,
                    GeneratorTools.GetSpawnPointCoalition(_template, groupSide, true).Value,
                    unitFamily);
                coordinates = airbase.Coordinates;
                extraSettings["ParkingID"]      = parkingSpotIDsList.ToArray();
                extraSettings["GroupAirbaseID"] = airbase.DCSID;
                extraSettings["UnitX"]          = (from Coordinates unitCoordinates in parkingSpotCoordinatesList select unitCoordinates.X).ToArray();
                extraSettings["UnitY"]          = (from Coordinates unitCoordinates in parkingSpotCoordinatesList select unitCoordinates.Y).ToArray();

                var midPoint = Coordinates.Lerp(coordinates, coordinates2, 0.4);
                extraSettings.AddIfKeyUnused("GroupMidX", midPoint.X);
                extraSettings.AddIfKeyUnused("GroupMidY", midPoint.Y);
            }
        }
Ejemplo n.º 5
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);
        }
Ejemplo n.º 6
0
        internal Tuple <Coordinates, List <Waypoint> > GenerateObjective(
            DCSMission mission,
            MissionTemplateRecord template,
            DBEntrySituation situationDB,
            MissionTemplateObjectiveRecord objectiveTemplate,
            Coordinates lastCoordinates,
            DBEntryAirbase playerAirbase,
            bool useObjectivePreset,
            ref int objectiveIndex,
            ref List <Coordinates> objectiveCoordinatesList,
            ref List <Waypoint> waypoints,
            ref List <UnitFamily> objectiveTargetUnitFamilies)
        {
            var extraSettings = new List <KeyValuePair <string, object> >();
            var waypointList  = new List <Waypoint>();

            string[] featuresID;
            DBEntryObjectiveTarget         targetDB;
            DBEntryObjectiveTargetBehavior targetBehaviorDB;
            DBEntryObjectiveTask           taskDB;

            ObjectiveOption[] objectiveOptions;

            GetObjectiveData(objectiveTemplate, useObjectivePreset, out featuresID, out targetDB, out targetBehaviorDB, out taskDB, out objectiveOptions);
            var         luaUnit = targetBehaviorDB.UnitLua[(int)targetDB.UnitCategory];
            Coordinates objectiveCoordinates = GetSpawnCoordinates(template, lastCoordinates, playerAirbase, targetDB);

            // Spawn target on airbase
            var unitCount = targetDB.UnitCount[(int)objectiveTemplate.TargetCount].GetValue();
            var objectiveTargetUnitFamily = Toolbox.RandomFrom(targetDB.UnitFamilies);

            if (AIRBASE_LOCATIONS.Contains(targetBehaviorDB.Location) && targetDB.UnitCategory.IsAircraft())
            {
                objectiveCoordinates = PlaceInAirbase(template, situationDB, playerAirbase, extraSettings, targetDB, targetBehaviorDB, ref luaUnit, objectiveCoordinates, unitCount, objectiveTargetUnitFamily);
            }

            UnitMakerGroupFlags groupFlags = 0;

            if (objectiveOptions.Contains(ObjectiveOption.ShowTarget))
            {
                groupFlags = UnitMakerGroupFlags.NeverHidden;
            }
            else if (objectiveOptions.Contains(ObjectiveOption.HideTarget))
            {
                groupFlags = UnitMakerGroupFlags.AlwaysHidden;
            }
            if (objectiveOptions.Contains(ObjectiveOption.EmbeddedAirDefense))
            {
                groupFlags |= UnitMakerGroupFlags.EmbeddedAirDefense;
            }

            // Set destination point for moving unit groups
            Coordinates destinationPoint = objectiveCoordinates +
                                           (
                (targetDB.UnitCategory == UnitCategory.Plane ? Coordinates.CreateRandom(30, 60) : Coordinates.CreateRandom(10, 20)) *
                Toolbox.NM_TO_METERS
                                           );

            if (targetBehaviorDB.Location == DBEntryObjectiveTargetBehaviorLocation.GoToPlayerAirbase)
            {
                destinationPoint = playerAirbase.Coordinates;
            }


            var unitCoordinates = objectiveCoordinates;
            var objectiveName   = Toolbox.RandomFrom(ObjectiveNames);

            if (TRANSPORT_TASKS.Contains(taskDB.ID))
            {
                Coordinates?spawnPoint = UnitMaker.SpawnPointSelector.GetRandomSpawnPoint(
                    targetDB.ValidSpawnPoints,
                    playerAirbase.Coordinates,
                    new MinMaxD(1, 5),
                    coalition: GeneratorTools.GetSpawnPointCoalition(template, Side.Ally));
                if (!spawnPoint.HasValue) // Failed to generate target group
                {
                    throw new BriefingRoomException($"Failed to find Cargo SpawnPoint");
                }
                unitCoordinates = spawnPoint.Value;
                var cargoWaypoint = GenerateObjectiveWaypoint(objectiveTemplate, unitCoordinates, $"{objectiveName} Pickup", template, true);
                waypoints.Add(cargoWaypoint);
                waypointList.Add(cargoWaypoint);
                if (taskDB.isEscort())
                {
                    extraSettings.Add("GroupX2".ToKeyValuePair(objectiveCoordinates.X));
                    extraSettings.Add("GroupY2".ToKeyValuePair(objectiveCoordinates.Y));
                    groupFlags |= UnitMakerGroupFlags.RadioAircraftSpawn;
                }
            }

            extraSettings.Add("GroupX2".ToKeyValuePair(destinationPoint.X));
            extraSettings.Add("GroupY2".ToKeyValuePair(destinationPoint.Y));

            UnitMakerGroupInfo?targetGroupInfo = UnitMaker.AddUnitGroup(
                objectiveTargetUnitFamily, unitCount,
                taskDB.TargetSide,
                targetBehaviorDB.GroupLua[(int)targetDB.UnitCategory], luaUnit,
                unitCoordinates,
                groupFlags,
                extraSettings.ToArray());

            if (!targetGroupInfo.HasValue) // Failed to generate target group
            {
                throw new BriefingRoomException($"Failed to generate group for objective.");
            }

            if (objectiveOptions.Contains(ObjectiveOption.EmbeddedAirDefense) && (targetDB.UnitCategory == UnitCategory.Static))
            {
                AddEmbeddedAirDefenseUnits(template, targetDB, targetBehaviorDB, taskDB, objectiveOptions, objectiveCoordinates, groupFlags, extraSettings);
            }

            var pluralIndex = targetGroupInfo.Value.UnitsID.Length == 1 ? 0 : 1;
            var taskString  = GeneratorTools.ParseRandomString(taskDB.BriefingTask[pluralIndex], mission).Replace("\"", "''");

            // Pick a name, then remove it from the list
            ObjectiveNames.Remove(objectiveName);
            CreateTaskString(mission, pluralIndex, ref taskString, objectiveName, objectiveTargetUnitFamily);

            CreateLua(mission, template, targetDB, taskDB, objectiveIndex, objectiveName, targetGroupInfo, taskString);

            // Add briefing remarks for this objective task
            if (taskDB.BriefingRemarks.Length > 0)
            {
                string remark = Toolbox.RandomFrom(taskDB.BriefingRemarks);
                GeneratorTools.ReplaceKey(ref remark, "ObjectiveName", objectiveName);
                GeneratorTools.ReplaceKey(ref remark, "UnitFamily", Database.Instance.Common.Names.UnitFamilies[(int)objectiveTargetUnitFamily][pluralIndex]);
                mission.Briefing.AddItem(DCSMissionBriefingItemType.Remark, remark);
            }

            // Add feature ogg files
            foreach (string oggFile in taskDB.IncludeOgg)
            {
                mission.AddMediaFile($"l10n/DEFAULT/{oggFile}", $"{BRPaths.INCLUDE_OGG}{oggFile}");
            }

            // Add objective features Lua for this objective
            mission.AppendValue("ScriptObjectivesFeatures", ""); // Just in case there's no features
            foreach (string featureID in featuresID)
            {
                FeaturesGenerator.GenerateMissionFeature(mission, featureID, objectiveName, objectiveIndex, targetGroupInfo.Value.GroupID, objectiveCoordinates, taskDB.TargetSide, objectiveOptions.Contains(ObjectiveOption.HideTarget));
            }

            objectiveCoordinatesList.Add(objectiveCoordinates);
            var waypoint = GenerateObjectiveWaypoint(objectiveTemplate, objectiveCoordinates, objectiveName, template);

            waypoints.Add(waypoint);
            waypointList.Add(waypoint);
            objectiveTargetUnitFamilies.Add(objectiveTargetUnitFamily);

            var preValidSpawns = targetDB.ValidSpawnPoints.ToList();

            foreach (var subTasks in objectiveTemplate.SubTasks)
            {
                objectiveIndex++;
                GenerateSubTask(mission, template, situationDB,
                                subTasks, objectiveCoordinates,
                                playerAirbase,
                                preValidSpawns,
                                targetBehaviorDB.Location,
                                featuresID,
                                ref objectiveIndex,
                                ref objectiveCoordinatesList, ref waypoints, ref waypointList, ref objectiveTargetUnitFamilies);
            }
            return(new (objectiveCoordinates, waypointList));
        }
Ejemplo n.º 7
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);
                }
            }
        }
Ejemplo n.º 8
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);
        }