Пример #1
0
        /// <summary>
        /// Generates the data for the objectives.
        /// </summary>
        /// <param name="mission">The mission for which to generate objectives</param>
        /// <param name="template">Mission template to use</param>
        /// <param name="objectiveDB">Objective database entry</param>
        private void GenerateObjectivesData(DCSMission mission, MissionTemplate template, DBEntryObjective objectiveDB, DBEntryTheater theaterDB)
        {
            // Keep in mind the position of the last objective/player location.
            // Start with initial player location.
            Coordinates lastCoordinates = mission.InitialPosition;

            // Common family to use for all objectives if DBEntryObjectiveFlags.SingleTargetUnitFamily is set
            UnitFamily singleObjectiveUnitFamily = objectiveDB.GetRandomUnitFamily();

            for (int i = 0; i < template.ObjectiveCount; i++)
            {
                // Pick a random unique name, or a waypoint number if objectives shouldn't be named
                string objectiveName = PickUniqueObjectiveName();

                DebugLog.Instance.WriteLine($"Adding objective #{i + 1}, designated {objectiveName}", 1);

                // Compute a random distance from last position, in nautical miles
                double objectiveDistanceNM =
                    (template.ObjectiveDistanceNM == 0) ?
                    Toolbox.RandomInt(TemplateTools.MIN_OBJECTIVE_DISTANCE, TemplateTools.MAX_OBJECTIVE_DISTANCE) :
                    template.ObjectiveDistanceNM;

                if (i > 0) // Objective is not the first one, spawn it close to the previous objective
                {
                    objectiveDistanceNM /= 5.0;
                }

                MinMaxD distanceFromLast =
                    new MinMaxD(OBJECTIVE_DISTANCE_VARIATION_MIN, OBJECTIVE_DISTANCE_VARIATION_MAX) * objectiveDistanceNM;
                Coordinates           objectiveCoordinates;
                DBEntryTheaterAirbase?airbase = null;

                if (objectiveDB.UnitGroup.SpawnPoints[0] != TheaterLocationSpawnPointType.Airbase)
                {
                    // Look for a valid spawn point
                    DBEntryTheaterSpawnPoint?spawnPoint =
                        SpawnPointSelector.GetRandomSpawnPoint(
                            // If spawn point types are specified, use them. Else look for spawn points of any type
                            (objectiveDB.UnitGroup.SpawnPoints.Length > 0) ? objectiveDB.UnitGroup.SpawnPoints : null,
                            // Select spawn points at a proper distance from last location (previous objective or home airbase)
                            lastCoordinates, distanceFromLast,
                            // Make sure no objective is too close to the initial location
                            mission.InitialPosition, new MinMaxD(objectiveDistanceNM * OBJECTIVE_DISTANCE_VARIATION_MIN, 999999999),
                            GeneratorTools.GetEnemySpawnPointCoalition(template));
                    // No spawn point found for the objective, abort mission creation.
                    if (!spawnPoint.HasValue)
                    {
                        throw new Exception($"Failed to find a spawn point for objective {i + 1}");
                    }
                    objectiveCoordinates = spawnPoint.Value.Coordinates;
                }
                else
                {
                    airbase = new MissionGeneratorAirbases().SelectObjectiveAirbase(mission, template, theaterDB, lastCoordinates, distanceFromLast, i == 0);
                    if (!airbase.HasValue)
                    {
                        throw new Exception($"Failed to find a airbase point for objective {i + 1}");
                    }
                    objectiveCoordinates = airbase.Value.Coordinates;
                }


                // Set the waypoint coordinates according the the inaccuracy defined in the objective database entry
                Coordinates waypointCoordinates =
                    objectiveCoordinates +
                    Coordinates.CreateRandom(objectiveDB.WaypointInaccuracy * Toolbox.NM_TO_METERS);

                // Select an objective family for the target if any or default to VehicleTransport.
                UnitFamily objectiveUnitFamily = singleObjectiveUnitFamily;

                if (!objectiveDB.Flags.HasFlag(DBEntryObjectiveFlags.SingleTargetUnitFamily))
                {
                    objectiveUnitFamily = objectiveDB.GetRandomUnitFamily();
                }

                // Set the mission objective
                mission.Objectives[i] = new DCSMissionObjective(
                    objectiveName, objectiveCoordinates, objectiveUnitFamily, waypointCoordinates, airbase.HasValue? airbase.Value.DCSID: 0);

                // Last position is now the position of this objective
                lastCoordinates = objectiveCoordinates;
            }

            // If the target is a static object, make sure the correct flag is enabled as it has an influence of some scripts
            mission.ObjectiveIsStatic = objectiveDB.UnitGroup.Category.HasValue && (objectiveDB.UnitGroup.Category.Value == UnitCategory.Static);

            // Make sure objectives are ordered by distance from the players' starting location
            mission.Objectives = mission.Objectives.OrderBy(x => mission.InitialPosition.GetDistanceFrom(x.WaypointCoordinates)).ToArray();
        }
        /// <summary>
        /// Main unit generation method.
        /// </summary>
        /// <param name="mission">Mission to which generated units should be added</param>
        /// <param name="template">Mission template to use</param>
        /// <param name="objectiveDB">Mission objective database entry</param>
        /// <param name="enemyCoalitionDB">Enemy coalition database entry</param>
        /// <param name="aiEscortTypeCAP">Type of aircraft selected for player AI CAP escort (single-player only)</param>
        /// <param name="aiEscortTypeSEAD">Type of aircraft selected for player AI SEAD escort (single-player only)</param>
        public void CreateUnitGroups(DCSMission mission, MissionTemplate template, DBEntryObjective objectiveDB, DBEntryCoalition enemyCoalitionDB, string aiEscortTypeCAP, string aiEscortTypeSEAD)
        {
            if (objectiveDB.Flags.HasFlag(DBEntryObjectiveFlags.NoEnemyCAP))
            {
                DebugLog.Instance.WriteLine("Enemy CAP disabled for this mission objective type, not spawning any units", 1);
                return;
            }
            int totalAirForcePower =
                (int)(GetMissionPackageAirPower(template, objectiveDB, aiEscortTypeCAP, aiEscortTypeSEAD) *
                      Database.Instance.Common.EnemyCAPRelativePower[(int)template.SituationEnemyAirForce.Get()]);

            DebugLog.Instance.WriteLine($"Enemy air power set to {totalAirForcePower}...", 1);

            DCSMissionUnitGroupFlags flags = template.Realism.Contains(RealismOption.HideEnemyUnits) ? DCSMissionUnitGroupFlags.Hidden : 0;

            int aircraftCount = 0;
            int groupCount    = 0;

            while (totalAirForcePower > 0)
            {
                string[] unitTypes = enemyCoalitionDB.GetRandomUnits(UnitFamily.PlaneFighter, mission.DateTime.Decade, 1, template.UnitMods);
                if (unitTypes.Length == 0)
                {
                    DebugLog.Instance.WriteLine("No valid units found for enemy fighter patrols.", 1, DebugLogMessageErrorLevel.Warning);
                    break;
                }

                // Find spawn point at the proper distance from the objective(s), but not to close from starting airbase
                DBEntryTheaterSpawnPoint?spawnPoint =
                    UnitMaker.SpawnPointSelector.GetRandomSpawnPoint(
                        null,
                        mission.ObjectivesCenter, Database.Instance.Common.EnemyCAPDistanceFromObjectives,
                        mission.InitialPosition, new MinMaxD(Database.Instance.Common.EnemyCAPMinDistanceFromTakeOffLocation, 99999),
                        GeneratorTools.GetEnemySpawnPointCoalition(template));

                if (!spawnPoint.HasValue) // No spawn point found, stop here.
                {
                    DebugLog.Instance.WriteLine("No spawn point found for enemy fighter patrol group.", 1, DebugLogMessageErrorLevel.Warning);
                    break;
                }

                int unitPower = Database.Instance.GetEntry <DBEntryUnit>(unitTypes[0]).AircraftData.AirToAirRating[1];
                int groupSize = 1;
                if (totalAirForcePower >= unitPower * 2)
                {
                    groupSize = 2;
                }
                if (Toolbox.RandomDouble() < .3)
                {
                    if (totalAirForcePower >= unitPower * 4)
                    {
                        groupSize = 4;
                    }
                    else if (totalAirForcePower >= unitPower * 3)
                    {
                        groupSize = 3;
                    }
                }
                totalAirForcePower -= unitPower * groupSize;

                DCSMissionUnitGroup group = UnitMaker.AddUnitGroup(
                    mission, Enumerable.Repeat(unitTypes[0], groupSize).ToArray(),
                    Side.Enemy, spawnPoint.Value.Coordinates,
                    "GroupAircraftCAP", "UnitAircraft",
                    Toolbox.BRSkillLevelToDCSSkillLevel(template.SituationEnemySkillLevelAir),
                    flags, UnitTaskPayload.AirToAir,
                    mission.ObjectivesCenter + Coordinates.CreateRandom(20, 40) * Toolbox.NM_TO_METERS);

                if (group == null)
                {
                    DebugLog.Instance.WriteLine($"Failed to add a group of {groupSize}× {unitTypes[0]} at {spawnPoint.Value.Coordinates}", 1, DebugLogMessageErrorLevel.Warning);
                }
                else
                {
                    DebugLog.Instance.WriteLine($"Added a group of {groupSize}× {unitTypes[0]} at {spawnPoint.Value.Coordinates}");
                    mission.AircraftSpawnQueue.Add(new DCSMissionAircraftSpawnQueueItem(group.GroupID, template.SituationEnemyCAPOnStationChance.RollChance()));
                }

                aircraftCount += groupSize;
                groupCount++;
            }
        }
        /// <summary>
        /// Generates a <see cref="DCSMission"/> from a <see cref="MissionTemplate"/>
        /// </summary>
        /// <param name="template">The <see cref="MissionTemplate"/> to use</param>
        /// <returns>A <see cref="DCSMission"/>, or nuLL if something when wrong</returns>
        private DCSMission DoMissionGeneration(MissionTemplate template)
        {
            DateTime generationStartTime = DateTime.Now;

            DebugLog.Instance.Clear();
            DebugLog.Instance.WriteLine($"Starting mission generation...");

            // Check for missing entries in the database
            GeneratorTools.CheckDBForMissingEntry <DBEntryCoalition>(template.ContextCoalitionBlue);
            GeneratorTools.CheckDBForMissingEntry <DBEntryCoalition>(template.ContextCoalitionRed);
            GeneratorTools.CheckDBForMissingEntry <DBEntryObjective>(template.ObjectiveType);
            GeneratorTools.CheckDBForMissingEntry <DBEntryTheater>(template.TheaterID);

            // Create the mission and copy some values (theater database entry ID, etc.) from the template
            DCSMission mission = new DCSMission();

            CopyTemplateValues(mission, template);

            // Get some DB entries from the database for easier reference
            DBEntryCoalition[] coalitionsDB = new DBEntryCoalition[2];
            coalitionsDB[(int)Coalition.Blue] = Database.Instance.GetEntry <DBEntryCoalition>(template.ContextCoalitionBlue);
            coalitionsDB[(int)Coalition.Red]  = Database.Instance.GetEntry <DBEntryCoalition>(template.ContextCoalitionRed);
            DBEntryObjective objectiveDB;

            if (template.ObjectiveType == "Random")
            {
                objectiveDB = Toolbox.RandomFrom <DBEntryObjective>(Database.Instance.GetAllEntries <DBEntryObjective>().Where(x => x.ID != "Random").ToArray());
            }
            else
            {
                objectiveDB = Database.Instance.GetEntry <DBEntryObjective>(template.ObjectiveType);
            }
            DBEntryTheater theaterDB = Database.Instance.GetEntry <DBEntryTheater>(template.TheaterID);

            // Create the unit maker, which will be used to generate unit groups and their properties
            UnitMaker unitMaker = new UnitMaker(coalitionsDB, theaterDB);

            // Create a list of flight group descriptions which will be used in the briefing
            List <UnitFlightGroupBriefingDescription> briefingFGList = new List <UnitFlightGroupBriefingDescription>();

            // Setup airbases
            DBEntryTheaterAirbase airbaseDB;

            using (MissionGeneratorAirbases airbaseGen = new MissionGeneratorAirbases())
            {
                airbaseDB = airbaseGen.SelectStartingAirbase(mission, template, theaterDB, objectiveDB);

                mission.InitialAirbaseID = airbaseDB.DCSID;
                mission.InitialPosition  = airbaseDB.Coordinates;

                airbaseGen.SetupAirbasesCoalitions(mission, template.TheaterRegionsCoalitions, theaterDB);
            }

            // Generate mission objectives
            DebugLog.Instance.WriteLine("Generating mission objectives...");
            using (MissionGeneratorObjectives objectives = new MissionGeneratorObjectives(unitMaker.SpawnPointSelector))
                objectives.CreateObjectives(mission, template, objectiveDB, theaterDB);

            // Generate mission date and time
            DebugLog.Instance.WriteLine("Generating mission date and time...");
            using (MissionGeneratorDateTime dateTime = new MissionGeneratorDateTime())
            {
                dateTime.GenerateMissionDate(mission, template, coalitionsDB);
                dateTime.GenerateMissionTime(mission, template, theaterDB);
            }

            // Generate mission weather
            DebugLog.Instance.WriteLine("Generating mission weather...");
            using (MissionGeneratorWeather weather = new MissionGeneratorWeather())
            {
                weather.GenerateWeather(mission, template.EnvironmentWeather, theaterDB);
                weather.GenerateWind(mission, template.EnvironmentWind, theaterDB);
            }

            // Generate Carrier
            using (MissionGeneratorCarrier unitGroupGen = new MissionGeneratorCarrier(unitMaker))
                unitGroupGen.GenerateCarriers(mission, template, coalitionsDB[(int)mission.CoalitionPlayer]);


            // Generate player unit groups
            DebugLog.Instance.WriteLine("Generating player unit groups and mission package...");
            string aiEscortTypeCAP, aiEscortTypeSEAD;

            using (MissionGeneratorPlayerFlightGroups unitGroupGen = new MissionGeneratorPlayerFlightGroups(unitMaker))
                briefingFGList.AddRange(
                    unitGroupGen.CreateUnitGroups(
                        mission, template, objectiveDB, coalitionsDB[(int)mission.CoalitionPlayer],
                        out aiEscortTypeCAP, out aiEscortTypeSEAD));

            // Generate objective unit groups
            DebugLog.Instance.WriteLine("Generating objectives unit groups...");
            using (MissionGeneratorObjectivesUnitGroups unitGroupGen = new MissionGeneratorObjectivesUnitGroups(unitMaker))
                unitGroupGen.CreateUnitGroups(mission, template, objectiveDB, coalitionsDB);

            // Generate friendly support units
            DebugLog.Instance.WriteLine("Generating friendly support units...");
            using (MissionGeneratorSupportUnits unitGroupGen = new MissionGeneratorSupportUnits(unitMaker))
                briefingFGList.AddRange(unitGroupGen.CreateUnitGroups(mission, coalitionsDB[(int)mission.CoalitionPlayer], template.OptionsUnitMods));

            // Generate enemy air defense unit groups
            DebugLog.Instance.WriteLine("Generating enemy air defense unit groups...");
            using (MissionGeneratorAirDefense unitGroupGen = new MissionGeneratorAirDefense(unitMaker, false, template, mission))
                unitGroupGen.CreateUnitGroups(mission, objectiveDB, coalitionsDB[(int)mission.CoalitionEnemy], GeneratorTools.GetEnemySpawnPointCoalition(template), template.OptionsUnitMods);

            // Generate ally air defense unit groups
            DebugLog.Instance.WriteLine("Generating friendly air defense unit groups...");
            using (MissionGeneratorAirDefense unitGroupGen = new MissionGeneratorAirDefense(unitMaker, true, template, mission))
                unitGroupGen.CreateUnitGroups(mission, objectiveDB, coalitionsDB[(int)mission.CoalitionPlayer], GeneratorTools.GetAllySpawnPointCoalition(template), template.OptionsUnitMods);

            //// Generate enemy fighter patrols
            DebugLog.Instance.WriteLine("Generating enemy fighter patrol unit groups...");
            using (MissionGeneratorEnemyFighterPatrols unitGroupGen = new MissionGeneratorEnemyFighterPatrols(unitMaker))
                unitGroupGen.CreateUnitGroups(mission, template, objectiveDB, coalitionsDB[(int)mission.CoalitionEnemy], aiEscortTypeCAP, aiEscortTypeSEAD);

            //// Generate mission features
            DebugLog.Instance.WriteLine("Generating mission features unit groups...");
            using (MissionGeneratorExtensionsAndFeatures featuresGen = new MissionGeneratorExtensionsAndFeatures(unitMaker))
                featuresGen.GenerateExtensionsAndFeatures(mission, template, objectiveDB, coalitionsDB);

            // Generates the mission flight plan
            DebugLog.Instance.WriteLine("Generating mission flight plan...");
            using (MissionGeneratorFlightPlan flightPlan = new MissionGeneratorFlightPlan())
            {
                flightPlan.SetBullseye(mission);
                flightPlan.AddObjectiveWaypoints(mission, objectiveDB);
                flightPlan.AddExtraWaypoints(mission, template);
            }

            // Generate briefing. Must be last because it uses information from other generators
            DebugLog.Instance.WriteLine("Generating mission briefing...");
            using (MissionGeneratorBriefing briefing = new MissionGeneratorBriefing())
            {
                briefing.GenerateMissionName(mission, template);
                briefing.GenerateMissionBriefing(mission, template, objectiveDB, airbaseDB, briefingFGList, coalitionsDB);
            }

            // Set if radio sounds are enabled
            mission.RadioSounds = !template.OptionsPreferences.Contains(MissionTemplatePreferences.DisableRadioSounds);

            // Add common .ogg vorbis files and make sure each only appears only once.
            mission.OggFiles.AddRange(Database.Instance.Common.CommonOGG);
            mission.OggFiles.AddRange(Database.Instance.Common.CommonOGGForGameMode[(int)template.GetMissionType()]);
            mission.OggFiles =
                (from string f in mission.OggFiles
                 where !string.IsNullOrEmpty(f.Trim()) select f.Trim())
                .Distinct(StringComparer.InvariantCultureIgnoreCase).ToList();

            // If radio sounds are disabled, do not include radio .ogg files to save on file size
            if (!mission.RadioSounds)
            {
                mission.OggFiles =
                    (from string f in mission.OggFiles
                     where (f.ToLowerInvariant() == "radio0") || (!f.ToLowerInvariant().StartsWith("radio")) select f).ToList();
            }

            // Make sure included Lua scripts appear only once
            mission.IncludedLuaScripts = mission.IncludedLuaScripts.Distinct().OrderBy(x => x).ToList();

            // Create aircraft queues and finalize the core script
            CreateAircraftActivationQueues(mission);
            switch (template.GetMissionType())
            {
            case MissionType.SinglePlayer:
                mission.CoreLuaScript += "briefingRoom.mission.missionType = brMissionType.SINGLE_PLAYER\r\n"; break;

            case MissionType.Cooperative:
                mission.CoreLuaScript += "briefingRoom.mission.missionType = brMissionType.COOPERATIVE\r\n"; break;

            case MissionType.Versus:
                mission.CoreLuaScript += "briefingRoom.mission.missionType = brMissionType.VERSUS\r\n"; break;
            }

            DebugLog.Instance.WriteLine($"Mission generation completed successfully in {(DateTime.Now - generationStartTime).TotalSeconds.ToString("F3", NumberFormatInfo.InvariantInfo)} second(s).");

            unitMaker.Dispose();

            return(mission);
        }
        /// <summary>
        /// Spawn a group of support units.
        /// </summary>
        /// <param name="mission">Mission to which generated units should be added</param>
        /// <param name="enemyCoalitionDB">Ally coalition database entry</param>
        /// <param name="unitFamily">Family of support unit to spawn</param>
        /// <param name="unitMods">Unit mods selected units can belong to</param>
        /// <param name="TACAN">TACAN info for the unit, if any</param>
        private void AddSupportUnit(DCSMission mission, MissionTemplate template, DBEntryCoalition enemyCoalitionDB, UnitFamily unitFamily, string[] unitMods, Tacan TACAN = null)
        {
            DebugLog.Instance.WriteLine($"Adding {unitFamily} enemy support unit...", 1);

            string[] validUnitTypes = enemyCoalitionDB.GetRandomUnits(unitFamily, mission.DateTime.Decade, 1, unitMods, false);

            if (validUnitTypes.Length == 0)
            {
                DebugLog.Instance.WriteLine($"No support unit found for this role in coalition \"{enemyCoalitionDB.ID}\"", 2);
                return; // Empty FG info will automatically be discarded
            }

            string groupLua;

            switch (unitFamily)
            {
            case UnitFamily.PlaneAWACS:
                groupLua = "GroupAircraftAWACSMortal";
                break;

            case UnitFamily.PlaneTankerBasket:
            case UnitFamily.PlaneTankerBoom:
                groupLua = "GroupAircraftTankerMortal";
                break;

            default:     // Should never happen
                return;  // Empty FG info will automatically be discarded
            }

            DBEntryTheaterSpawnPoint?spawnPoint =
                UnitMaker.SpawnPointSelector.GetRandomSpawnPoint(
                    null,
                    mission.ObjectivesCenter, Database.Instance.Common.EnemyCAPDistanceFromObjectives,
                    mission.InitialPosition, new MinMaxD(Database.Instance.Common.EnemyCAPMinDistanceFromTakeOffLocation, 99999),
                    GeneratorTools.GetEnemySpawnPointCoalition(template));

            if (!spawnPoint.HasValue) // No spawn point found, stop here.
            {
                DebugLog.Instance.WriteLine("No spawn point found for enemy fighter patrol group.", 1, DebugLogMessageErrorLevel.Warning);
                return;
            }

            Coordinates location2 = spawnPoint.Value.Coordinates + Coordinates.CreateRandom(12, 20) * Toolbox.NM_TO_METERS;

            string unitType = Toolbox.RandomFrom(validUnitTypes);

            DCSMissionUnitGroup group = UnitMaker.AddUnitGroup(
                mission, new string[] { unitType },
                Side.Enemy, spawnPoint.Value.Coordinates,
                groupLua, "UnitAircraft",
                Toolbox.BRSkillLevelToDCSSkillLevel(template.SituationEnemySkillLevelAir), 0,
                UnitTaskPayload.Default,
                location2);

            if (group == null)
            {
                return; // Empty FG info will automatically be discarded
            }
            group.TACAN = TACAN;
            mission.AircraftSpawnQueue.Insert(0, new DCSMissionAircraftSpawnQueueItem(group.GroupID, true)); // Support aircraft must be activated first
        }