public void Generate(CampaignTemplate campaignTemplate, string campaignFilePath) { string campaignName = Path.GetFileNameWithoutExtension(campaignFilePath); string campaignDirectory = Path.GetDirectoryName(campaignFilePath); DCSMissionDateTime date = GenerateCampaignDate(campaignTemplate); using (MissionGenerator generator = new MissionGenerator()) { for (int i = 0; i < campaignTemplate.MissionsCount; i++) { // Increment the date by a few days for each mission after the first if (i > 0) { IncrementDate(ref date); } MissionTemplate template = CreateMissionTemplate(campaignTemplate, i); DCSMission mission = generator.Generate(template); mission.MissionName = $"{campaignName}, phase {i + 1}"; mission.DateTime.Day = date.Day; mission.DateTime.Month = date.Month; mission.DateTime.Year = date.Year; MizFile miz = mission.ExportToMiz(); miz.SaveToFile(Path.Combine(campaignDirectory, $"{campaignName}{i + 1:00}.miz")); } } CreateImageFiles(campaignTemplate, campaignFilePath); CreateCMPFile(campaignTemplate, campaignFilePath); }
/// <summary> /// Generates wind settings for the mission. Must be called once mission weather level has been set, as weather is used for auto wind. /// </summary> /// <param name="mission">The mission.</param> /// <param name="wind">The preferred wind speed.</param> /// <param name="theater">Theater definition from which to get wind info for this part of the world.</param> public void GenerateWind(DCSMission mission, Wind wind, DefinitionTheater theater) { DebugLog.Instance.Log("Generating wind..."); DebugLog.Instance.Log($" Wind speed should be {wind.ToString().ToUpperInvariant()}"); // If auto, speed depends on weather, so we never end up with no wind in a storm mission.WindLevel = (wind == Wind.Auto) ? (Wind)(HQTools.Clamp((int)mission.WeatherLevel + HQTools.RandomMinMax(-1, 1), 0, (int)Wind.StrongGale)) : wind; DebugLog.Instance.Log($" Wind speed level set to {mission.WindLevel}"); for (int i = 0; i < 3; i++) { mission.WeatherWindSpeed[i] = Math.Max(0, theater.Wind[(int)mission.WindLevel].Wind.GetValue()); mission.WeatherWindDirection[i] = (mission.WeatherWindSpeed[i] > 0) ? HQTools.RandomInt(0, 360) : 0; DebugLog.Instance.Log($" Wind speed at {WIND_ALTITUDE[i]} meters set to {mission.WeatherWindSpeed[i]} m/s, direction of {mission.WeatherWindDirection[i]}"); } // Turbulence = max(weatherTurbulence, windTurbulence) mission.WeatherTurbulence = Math.Max(mission.WeatherTurbulence, theater.Wind[(int)mission.WindLevel].Turbulence.GetValue()); DebugLog.Instance.Log($" Turbulence updated to {mission.WeatherTurbulence} m/s"); DebugLog.Instance.Log(); }
internal static void GenerateBullseyes(DCSMission mission, Coordinates objectivesCenter) { mission.SetValue("BullseyeBlueX", objectivesCenter.X + GetBullseyeRandomDistance()); mission.SetValue("BullseyeBlueY", objectivesCenter.Y + GetBullseyeRandomDistance()); mission.SetValue("BullseyeRedX", objectivesCenter.X + GetBullseyeRandomDistance()); mission.SetValue("BullseyeRedY", objectivesCenter.Y + GetBullseyeRandomDistance()); }
/// <summary> /// Directly copies some simple values (theater database entry ID, etc.) from the template. /// </summary> /// <param name="mission">The mission</param> /// <param name="template">Mission template to use</param> private void CopyTemplateValues(DCSMission mission, MissionTemplate template) { mission.Coalitions[(int)Coalition.Blue] = template.GetCoalition(Coalition.Blue); mission.Coalitions[(int)Coalition.Red] = template.GetCoalition(Coalition.Red); mission.CivilianTraffic = template.OptionsCivilianTraffic; mission.CoalitionPlayer = template.ContextCoalitionPlayer; mission.Weather.CloudsPreset = template.EnvironmentCloudPreset.Get(); mission.RadioAssists = !template.Realism.Contains(RealismOption.DisableDCSRadioAssists); mission.Theater = template.ContextTheater; mission.CountryBlues = new List <Country> { Country.CJTFBlue }; mission.CountryReds = new List <Country> { Country.CJTFRed }; var countries = template.PlayerFlightGroups.Select(x => x.Country).Distinct().ToList(); if (template.ContextCoalitionPlayer == Coalition.Blue) { mission.CountryBlues.AddRange(countries); } else { mission.CountryReds.AddRange(countries); } mission.CountryBlues = mission.CountryBlues.Distinct().ToList(); mission.CountryReds = mission.CountryReds.Distinct().ToList(); mission.EndMode = template.OptionsEndMode; mission.RealismOptions = template.Realism; }
private static void SaveWaypointsToBriefing(DCSMission mission, Coordinates initialCoordinates, List <Waypoint> waypoints, bool useImperialSystem, UnitMakerGroupInfo?groupInfo) { double totalDistance = 0; Coordinates lastWP = initialCoordinates; // Add first (takeoff) and last (landing) waypoints to get a complete list of all waypoints List <Waypoint> allWaypoints = new List <Waypoint>(waypoints); allWaypoints.Insert(0, new Waypoint(Database.Instance.Common.Names.WPInitialName, initialCoordinates)); allWaypoints.Add(new Waypoint(Database.Instance.Common.Names.WPFinalName, initialCoordinates)); mission.Briefing.AddItem(DCSMissionBriefingItemType.Waypoint, $"\t{groupInfo.Value.Name}\t"); List <string> waypointTextRows = new List <string>(); foreach (Waypoint waypoint in allWaypoints) { double distanceFromLast = waypoint.Coordinates.GetDistanceFrom(lastWP); totalDistance += distanceFromLast; lastWP = waypoint.Coordinates; string waypointText = waypoint.Name + "\t" + (useImperialSystem ? $"{distanceFromLast * Toolbox.METERS_TO_NM:F0} nm" : $"{distanceFromLast / 1000.0:F0} Km") + "\t" + (useImperialSystem ? $"{totalDistance * Toolbox.METERS_TO_NM:F0} nm" : $"{totalDistance / 1000.0:F0} Km"); mission.Briefing.AddItem(DCSMissionBriefingItemType.Waypoint, waypointText); waypointTextRows.Add(waypointText); } mission.Briefing.FlightBriefings.Add(new DCSMissionFlightBriefing { Name = groupInfo.Value.Name, Type = groupInfo.Value.UnitDB.DCSIDs.First(), Waypoints = waypointTextRows }); }
private void CreateAircraftActivationQueues(DCSMission mission) { string[] initialQueue = (from DCSMissionAircraftSpawnQueueItem queueItem in mission.AircraftSpawnQueue where queueItem.SpawnOnStart select queueItem.GroupID.ToString()).ToArray(); mission.CoreLuaScript += $"briefingRoom.aircraftActivator.currentQueue = {{ {string.Join(",", initialQueue)} }}\r\n"; List <string> extraQueues = (from DCSMissionAircraftSpawnQueueItem queueItem in mission.AircraftSpawnQueue where !queueItem.SpawnOnStart select queueItem.GroupID.ToString()).ToList(); int totalExtraQueues = extraQueues.Count; mission.CoreLuaScript += "briefingRoom.aircraftActivator.extraQueues = { "; for (int i = 0; i < mission.Objectives.Length; i++) { int length = (i == mission.Objectives.Length - 1) ? extraQueues.Count : totalExtraQueues / mission.Objectives.Length; mission.CoreLuaScript += $" {{ {string.Join(",", extraQueues.Take(length))} }}"; if (i < mission.Objectives.Length - 1) { mission.CoreLuaScript += ", "; } extraQueues.RemoveRange(0, length); } mission.CoreLuaScript += "}\r\n"; mission.CoreLuaScript += $"briefingRoom.aircraftActivator.escortCAP = {mission.EscortCAPGroupId}\r\n"; mission.CoreLuaScript += $"briefingRoom.aircraftActivator.escortSEAD = {mission.EscortSEADGroupId}\r\n"; }
internal DBEntryAirbase SelectStartingAirbase(DCSMission mission, string selectedAirbaseID, int requiredParkingSpots = 0, int requiredRunway = 0) { // Get total number of required parking spots for flight groups if (requiredParkingSpots == 0) { requiredParkingSpots = _template.PlayerFlightGroups.Sum(x => x.Count); } // Select all airbases for this theater DBEntryAirbase[] airbases = _situationDB.GetAirbases(_template.OptionsMission.Contains("InvertCountriesCoalitions")); // If a particular airbase name has been specified and an airbase with this name exists, pick it if (!string.IsNullOrEmpty(selectedAirbaseID)) { var airbase = airbases.FirstOrDefault(x => x.ID == selectedAirbaseID); if (airbase is null) { throw new BriefingRoomException($"No airbase found with ID \"{selectedAirbaseID}\", cannot spawn player aircraft."); } return(airbase); } var opts = airbases.Where(x => x.ParkingSpots.Length >= requiredParkingSpots && x.Coalition == _template.ContextPlayerCoalition && x.RunwayLengthFt > requiredRunway && (MissionPrefersShoreAirbase() ? x.Flags.HasFlag(AirbaseFlag.NearWater) : true) ).ToList(); if (opts.Count == 0) { throw new BriefingRoomException($"No airbase found, cannot spawn player aircraft."); } return(Toolbox.RandomFrom(opts)); }
internal void SelectStartingAirbaseForPackages(DCSMission mission, DBEntryAirbase homeBase) { var missionPackages = new List <DCSMissionPackage>(); foreach (var package in _template.AircraftPackages) { if (package.StartingAirbase == "home") { missionPackages.Add(new DCSMissionPackage(_template.AircraftPackages.IndexOf(package), homeBase)); continue; } var flights = _template.PlayerFlightGroups.Where((v, i) => package.FlightGroupIndexes.Contains(i)); var requiredSpots = flights.Sum(x => x.Count); var requiredRunway = flights.Select(x => Database.Instance.GetEntry <DBEntryUnit>(x.Aircraft).AircraftData.MinimumRunwayLengthFt).Max(); var airbase = SelectStartingAirbase(mission, package.StartingAirbase, requiredSpots, requiredRunway); if (missionPackages.Any(x => x.Airbase == airbase)) { mission.Briefing.AddItem(DCSMissionBriefingItemType.Airbase, $"{airbase.Name}\t{airbase.Runways}\t{airbase.ATC}\t{airbase.ILS}\t{airbase.TACAN}"); } missionPackages.Add(new DCSMissionPackage(_template.AircraftPackages.IndexOf(package), airbase)); } mission.MissionPackages.AddRange(missionPackages); }
private static void CreateLua(DCSMission mission, MissionTemplateRecord template, DBEntryObjectiveTarget targetDB, DBEntryObjectiveTask taskDB, int objectiveIndex, string objectiveName, UnitMakerGroupInfo?targetGroupInfo, string taskString) { // Add Lua table for this objective string objectiveLua = $"briefingRoom.mission.objectives[{objectiveIndex + 1}] = {{ "; objectiveLua += $"complete = false, "; objectiveLua += $"groupID = {targetGroupInfo.Value.GroupID}, "; objectiveLua += $"hideTargetCount = false, "; objectiveLua += $"name = \"{objectiveName}\", "; objectiveLua += $"targetCategory = Unit.Category.{targetDB.UnitCategory.ToLuaName()}, "; objectiveLua += $"taskType = \"{taskDB.ID}\", "; objectiveLua += $"task = \"{taskString}\", "; objectiveLua += $"unitsCount = {targetGroupInfo.Value.UnitsID.Length}, "; objectiveLua += $"unitsID = {{ {string.Join(", ", targetGroupInfo.Value.UnitsID)} }} "; objectiveLua += "}\n"; // Add F10 sub-menu for this objective objectiveLua += $"briefingRoom.f10Menu.objectives[{objectiveIndex + 1}] = missionCommands.addSubMenuForCoalition(coalition.side.{template.ContextPlayerCoalition.ToString().ToUpperInvariant()}, \"Objective {objectiveName}\", nil)\n"; mission.AppendValue("ScriptObjectives", objectiveLua); // Add objective trigger Lua for this objective string triggerLua = Toolbox.ReadAllTextIfFileExists($"{BRPaths.INCLUDE_LUA_OBJECTIVETRIGGERS}{taskDB.CompletionTriggerLua}"); GeneratorTools.ReplaceKey(ref triggerLua, "ObjectiveIndex", objectiveIndex + 1); mission.AppendValue("ScriptObjectivesTriggers", triggerLua); }
internal static int GenerateWeather(DCSMission mission, MissionTemplateRecord template, DBEntryTheater theaterDB, Month month, DBEntryAirbase playerAirbase) { var baseAlt = template.OptionsMission.Contains("SeaLevelRefCloud") ? 0.0 : playerAirbase.Elevation; if (template.OptionsMission.Contains("HighCloud")) { baseAlt += 2000; } DBEntryWeatherPreset weatherDB; if (string.IsNullOrEmpty(template.EnvironmentWeatherPreset)) // Random weather { weatherDB = Toolbox.RandomFrom(Database.Instance.GetAllEntries <DBEntryWeatherPreset>()); } else { weatherDB = Database.Instance.GetEntry <DBEntryWeatherPreset>(template.EnvironmentWeatherPreset); } mission.SetValue("WeatherName", weatherDB.BriefingDescription); mission.SetValue("WeatherCloudsBase", weatherDB.CloudsBase.GetValue() + baseAlt); mission.SetValue("WeatherCloudsPreset", Toolbox.RandomFrom(weatherDB.CloudsPresets)); mission.SetValue("WeatherCloudsThickness", weatherDB.CloudsThickness.GetValue()); mission.SetValue("WeatherDust", weatherDB.Dust); mission.SetValue("WeatherDustDensity", weatherDB.DustDensity.GetValue()); mission.SetValue("WeatherFog", weatherDB.Fog); mission.SetValue("WeatherFogThickness", weatherDB.FogThickness.GetValue()); mission.SetValue("WeatherFogVisibility", weatherDB.FogVisibility.GetValue()); mission.SetValue("WeatherQNH", weatherDB.QNH.GetValue()); mission.SetValue("WeatherTemperature", theaterDB.Temperature[(int)month].GetValue()); mission.SetValue("WeatherVisibility", weatherDB.Visibility.GetValue()); return(weatherDB.Turbulence.GetValue()); }
/// <summary> /// Creates the raw text briefing, to be used for the mission description inside DCS World. /// </summary> /// <param name="description"></param> /// <param name="tasks"></param> /// <param name="remarks"></param> /// <param name="flightGroups"></param> /// <param name="airbaseDB">Airbase player will take off from and land back on</param> /// <returns></returns> private string CreateTXTBriefing( DCSMission mission, string description, List <string> tasks, List <string> remarks, List <UnitFlightGroupBriefingDescription> flightGroups, DBEntryTheaterAirbase airbaseDB) { DebugLog.Instance.WriteLine("Generating raw text mission briefing...", 2); string briefing = description + "\n\n"; briefing += "TASKS:\n"; foreach (string t in tasks) { briefing += $"- {t}\n"; } briefing += "\n"; briefing += "REMARKS:\n"; foreach (string r in remarks) { briefing += $"- {r}\n"; } briefing += "\n"; briefing += "MISSION PACKAGE:\n"; foreach (UnitFlightGroupBriefingDescription fg in flightGroups) { briefing += $"- {fg.Callsign} ({fg.Count}×{fg.Type}, {fg.Task}) - {fg.Radio}{(string.IsNullOrEmpty(fg.Remarks) ? "" : $", {fg.Remarks}")}\n"; }
internal static void GenerateTitle(DCSMission mission, MissionTemplateRecord template) { ImageMaker imageMaker = new(); imageMaker.TextOverlay.Alignment = ContentAlignment.MiddleCenter; imageMaker.TextOverlay.Text = mission.Briefing.Name; List <ImageMakerLayer> imageLayers = new List <ImageMakerLayer>(); string[] theaterImages = Directory.GetFiles($"{BRPaths.INCLUDE_JPG}Theaters\\", $"{Database.Instance.GetEntry<DBEntryTheater>(template.ContextTheater).DCSID}*.jpg"); if (theaterImages.Length == 0) { imageLayers.Add(new ImageMakerLayer("_default.jpg")); } else { imageLayers.Add(new ImageMakerLayer("Theaters\\" + Path.GetFileName(Toolbox.RandomFrom(theaterImages)))); } imageLayers.Add(new ImageMakerLayer($"Flags\\{template.GetCoalitionID(template.ContextPlayerCoalition)}.png", ContentAlignment.TopLeft, 8, 8, 0, .5)); byte[] imageBytes = imageMaker.GetImageBytes(imageLayers.ToArray()); mission.AddMediaFile($"l10n/DEFAULT/title_{mission.UniqueID}.jpg", imageBytes); }
/// <summary> /// Main unit generation method. /// </summary> /// <param name="mission">Mission to which generated units should be added</param> /// <param name="template">Mission template to use</param> /// <param name="objectiveDB">Mission objective database entry</param> /// <param name="playerCoalitionDB">Player coalition database entry</param> /// <param name="aiEscortTypeCAP">Type of aircraft selected for AI CAP escort</param> /// <param name="aiEscortTypeSEAD">Type of aircraft selected for AI SEAD escort</param> /// <returns>An array of <see cref="UnitFlightGroupBriefingDescription"/> describing the flight groups, to be used in the briefing</returns> public UnitFlightGroupBriefingDescription[] CreateUnitGroups(DCSMission mission, MissionTemplate template, DBEntryObjective objectiveDB, DBEntryCoalition playerCoalitionDB, out string aiEscortTypeCAP, out string aiEscortTypeSEAD) { List <UnitFlightGroupBriefingDescription> briefingFGList = new List <UnitFlightGroupBriefingDescription>(); if (template.MissionType == MissionType.SinglePlayer) { briefingFGList.Add(GenerateSinglePlayerFlightGroup(mission, template, objectiveDB)); } else { briefingFGList.AddRange(GenerateMultiplayerFlightGroups(mission, template, objectiveDB)); } aiEscortTypeCAP = ""; aiEscortTypeSEAD = ""; UnitFlightGroupBriefingDescription?escortDescription; escortDescription = GenerateAIEscort(mission, template, template.SituationFriendlyEscortCAP, MissionTemplateFlightGroupTask.SupportCAP, playerCoalitionDB); if (escortDescription.HasValue) { briefingFGList.Add(escortDescription.Value); aiEscortTypeCAP = escortDescription.Value.Type; } escortDescription = GenerateAIEscort(mission, template, template.SituationFriendlyEscortSEAD, MissionTemplateFlightGroupTask.SupportSEAD, playerCoalitionDB); if (escortDescription.HasValue) { briefingFGList.Add(escortDescription.Value); aiEscortTypeSEAD = escortDescription.Value.Type; } return(briefingFGList.ToArray()); }
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)); }
protected void AddBriefingRemarkFromFeature(T featureDB, DCSMission mission, bool useEnemyRemarkIfAvailable, UnitMakerGroupInfo?groupInfo, Dictionary <string, object> stringReplacements) { string[] remarks; if (useEnemyRemarkIfAvailable && featureDB.BriefingRemarks[(int)Side.Enemy].Length > 0) { remarks = featureDB.BriefingRemarks[(int)Side.Enemy].ToArray(); } else { remarks = featureDB.BriefingRemarks[(int)Side.Ally].ToArray(); } if (remarks.Length == 0) { return; // No briefing remarks for this feature } string remark = Toolbox.RandomFrom(remarks); foreach (KeyValuePair <string, object> stringReplacement in stringReplacements) { GeneratorTools.ReplaceKey(ref remark, stringReplacement.Key, stringReplacement.Value.ToString()); } if (groupInfo.HasValue) { GeneratorTools.ReplaceKey(ref remark, "GroupName", groupInfo.Value.Name); GeneratorTools.ReplaceKey(ref remark, "GroupFrequency", GeneratorTools.FormatRadioFrequency(groupInfo.Value.Frequency)); GeneratorTools.ReplaceKey(ref remark, "GroupUnitName", groupInfo.Value.UnitDB.UIDisplayName); } mission.Briefing.AddItem(DCSMissionBriefingItemType.Remark, remark, featureDB is DBEntryFeatureMission); }
internal static byte[] ExportToMizBytes(DCSMission mission) { Dictionary <string, byte[]> MizFileEntries = new Dictionary <string, byte[]>(); AddStringValueToEntries(MizFileEntries, "briefing.html", mission.Briefing.GetBriefingAsHTML(true)); AddStringValueToEntries(MizFileEntries, "credits.txt", "Generated with BriefingRoom for DCS World (https://akaagar.itch.io/briefing-room-for-dcs)"); AddLuaFileToEntries(MizFileEntries, "mission", "Mission.lua", mission); AddLuaFileToEntries(MizFileEntries, "options", "Options.lua", null); AddStringValueToEntries(MizFileEntries, "theatre", mission.GetValue("TheaterID")); AddLuaFileToEntries(MizFileEntries, "warehouses", "Warehouses.lua", mission); AddLuaFileToEntries(MizFileEntries, "l10n/DEFAULT/dictionary", "Dictionary.lua", null); AddLuaFileToEntries(MizFileEntries, "l10n/DEFAULT/mapResource", "MapResource.lua", mission); AddLuaFileToEntries(MizFileEntries, "l10n/DEFAULT/script.lua", "Script.lua", mission); foreach (string mediaFile in mission.GetMediaFileNames()) { byte[] fileBytes = mission.GetMediaFile(mediaFile); if (fileBytes == null) { continue; } MizFileEntries.Add(mediaFile, fileBytes); } return(Toolbox.ZipData(MizFileEntries)); }
/// <summary> /// Generates the title image for the mission. /// </summary> /// <param name="mission">The misison which requires a title image.</param> /// <returns>The mission title image, as an array of bytes for a JPEG file.</returns> private byte[] GetTitleImage(DCSMission mission) { byte[] imageBytes; using (ImageMaker imgMaker = new ImageMaker()) { imgMaker.TextOverlay.Text = mission.MissionName; imgMaker.TextOverlay.Alignment = ContentAlignment.BottomCenter; List <ImageMakerLayer> layers = new List <ImageMakerLayer>(); string[] theaterImages = Directory.GetFiles($"{BRPaths.INCLUDE_JPG}Theaters\\", $"{mission.Theater}*.jpg"); if (theaterImages.Length == 0) { layers.Add(new ImageMakerLayer("_default.jpg")); } else { layers.Add(new ImageMakerLayer("Theaters\\" + Path.GetFileName(Toolbox.RandomFrom(theaterImages)))); } layers.Add(new ImageMakerLayer($"Flags\\{GeneratorTools.RemoveAfterComma(mission.Coalitions[(int)mission.CoalitionPlayer])}.png", ContentAlignment.TopLeft, 8, 8, 0, .5)); imageBytes = imgMaker.GetImageBytes(layers.ToArray()); } return(imageBytes); }
/// <summary> /// Sets the coalition to which the various airbases on the theater belong. /// </summary> /// <param name="mission">Mission for which airbase coalitions must be set</param> /// <param name="theaterAirbasesCoalitions">Airbase coalition setting</param> /// <param name="theaterDB">Theater database entry</param> public void SetupAirbasesCoalitions(DCSMission mission, CountryCoalition theaterAirbasesCoalitions, DBEntryTheater theaterDB) { mission.AirbasesCoalition.Clear(); foreach (DBEntryTheaterAirbase ab in theaterDB.Airbases) { // Airbase ID already exists in the mission if (mission.AirbasesCoalition.ContainsKey(ab.DCSID)) { continue; } // Airbase is the player starting airbase, always set it to the player coalition if (ab.DCSID == mission.InitialAirbaseID) { mission.AirbasesCoalition.Add(ab.DCSID, mission.CoalitionPlayer); continue; } // Other airbases are assigned to a coalition according to the theater and the template settings Coalition airbaseCoalition = ab.Coalition; switch (theaterAirbasesCoalitions) { case CountryCoalition.AllBlue: airbaseCoalition = Coalition.Blue; break; case CountryCoalition.AllRed: airbaseCoalition = Coalition.Red; break; case CountryCoalition.Inverted: airbaseCoalition = (Coalition)(1 - (int)ab.Coalition); break; } mission.AirbasesCoalition.Add(ab.DCSID, airbaseCoalition); } }
/// <summary> /// Generate a random mission name if none is provided in the template, or returns the provided name if there is one. /// </summary> /// <param name="mission">The mission.</param> /// <param name="template">The provided mission name in the template.</param> public void GenerateMissionName(DCSMission mission, string templateMissionName) { DebugLog.Instance.Log("Generating mission name..."); if (templateMissionName == null) { templateMissionName = ""; } // If a custom mission name was provided, use it... if (!string.IsNullOrEmpty(templateMissionName.Trim())) { mission.BriefingName = templateMissionName.Trim(); return; } // ...else generate a random name. // First get a random template then replace parts 1 to MAX_MISSION_NAME_PARTS ($PART1$, $PART2$, $PART3$...) by random parts. string name = HQTools.RandomFrom(Language.GetStringArray("Mission", "Name.Template")); for (int i = 1; i <= MAX_MISSION_NAME_PARTS; i++) { name = name.Replace( $"$P{i.ToString()}$", HQTools.RandomFrom(Language.GetStringArray("Mission", $"Name.Part{i.ToString()}"))); } mission.BriefingName = name; DebugLog.Instance.Log(""); }
/// <summary> /// Creates the flight group player will lead in a single-player mission. /// </summary> /// <param name="mission">Mission to which generated units should be added</param> /// <param name="template">Mission template to use</param> /// <param name="objectiveDB">Mission objective database entry</param> /// <returns>A <see cref="UnitFlightGroupBriefingDescription"/> describing the flight group, to be used in the briefing</returns> private UnitFlightGroupBriefingDescription GenerateSinglePlayerFlightGroup(DCSMission mission, MissionTemplate template, DBEntryObjective objectiveDB) { var playerFlightGroup = template.PlayerFlightGroups[0]; bool isCarrier = !string.IsNullOrEmpty(playerFlightGroup.Carrier); DebugLog.Instance.WriteLine($"{playerFlightGroup.Carrier} -> {string.Join(",",mission.Carriers.Select(x => x.Name).ToArray())}"); DCSMissionUnitGroup group = UnitMaker.AddUnitGroup( mission, Enumerable.Repeat(playerFlightGroup.Aircraft, playerFlightGroup.Count).ToArray(), Side.Ally, isCarrier? mission.Carriers.First(x => x.Units[0].Name == playerFlightGroup.Carrier).Coordinates : mission.InitialPosition, isCarrier? "GroupAircraftPlayerCarrier" : "GroupAircraftPlayer", "UnitAircraft", Toolbox.BRSkillLevelToDCSSkillLevel(template.SituationFriendlyAISkillLevel), DCSMissionUnitGroupFlags.FirstUnitIsPlayer, objectiveDB.Payload, null, isCarrier? -99 : mission.InitialAirbaseID, true, country: playerFlightGroup.Country, startLocation: playerFlightGroup.StartLocation ); if (group == null) { throw new Exception($"Failed to create group of player aircraft of type \"{playerFlightGroup.Aircraft}\"."); } if (isCarrier) { group.CarrierId = mission.Carriers.First(x => x.Units[0].Name == playerFlightGroup.Carrier).Units[0].ID; } return(new UnitFlightGroupBriefingDescription( group.Name, group.Units.Length, playerFlightGroup.Aircraft, objectiveDB.BriefingTaskFlightGroup, Database.Instance.GetEntry <DBEntryUnit>(playerFlightGroup.Aircraft).AircraftData.GetRadioAsString())); }
internal void AddMission(DCSMission mission) { if (mission == null) { return; } Missions.Add(mission); }
/// <summary> /// Read values from the mission and make replacements in the Lua file /// </summary> /// <param name="lua">Lua string</param> /// <param name="mission">HQ4DCS mission to use</param> private void MakeCommonReplacements(ref string lua, DCSMission mission) { HQTools.ReplaceKey(ref lua, "UnitNames", CreateUnitNamesTable(mission.UseNATOCallsigns)); HQTools.ReplaceKey(ref lua, "ObjectiveNames", CreateObjectiveNamesTable(mission)); HQTools.ReplaceKey(ref lua, "PlayerCoalition", mission.CoalitionPlayer.ToString().ToUpperInvariant()); HQTools.ReplaceKey(ref lua, "EnemyCoalition", mission.CoalitionEnemy.ToString().ToUpperInvariant()); HQTools.ReplaceKey(ref lua, "ObjectiveCount", mission.Objectives.Length); }
/// <summary> /// Generates the <see cref="DCSMissionObjective"/>. /// </summary> /// <param name="mission">The mission for which to generate objectives</param> /// <param name="template">Mission template to use</param> /// <param name="objectiveDB">Objective database entry</param> public void CreateObjectives(DCSMission mission, MissionTemplate template, DBEntryObjective objectiveDB, DBEntryTheater theaterDB) { // Set the array for the proper number of objective mission.Objectives = new DCSMissionObjective[template.ObjectiveCount]; GenerateObjectivesData(mission, template, objectiveDB, theaterDB); GenerateObjectivesScript(mission); }
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 }
/// <summary> /// Exports a BriefingRoom mission to a DCS .miz file. /// </summary> /// <param name="mission">The mission to export</param> /// <returns>A MizFile if everything went well, null if an error happened.</returns> public MizFile ExportToMizFile(DCSMission mission) { string resourceOggString; if (mission == null) { return(null); } DebugLog.Instance.Clear(); DebugLog.Instance.WriteLine($"Started MIZ file export..."); DateTime exportStartTime = DateTime.Now; MizFile miz = new MizFile(); miz.AddEntry("Credits.txt", $"Generated with BriefingRoom for DCS World ({BriefingRoom.WEBSITE_URL})"); DebugLog.Instance.WriteLine(" Adding \"briefing.html\" entry...", 1); miz.AddEntry("briefing.html", mission.BriefingHTML); DebugLog.Instance.WriteLine(" Adding \"mission\" entry...", 1); using (MizMakerLuaMission luaMission = new MizMakerLuaMission()) miz.AddEntry("mission", luaMission.MakeLua(mission)); DebugLog.Instance.WriteLine(" Adding \"options\" entry...", 1); miz.AddEntry("options", LuaTools.ReadIncludeLuaFile("Options.lua")); DebugLog.Instance.WriteLine(" Adding \"theater\" entry...", 1); miz.AddEntry("theatre", mission.Theater); DebugLog.Instance.WriteLine(" Adding \"warehouses\" entry...", 1); using (MizMakerLuaWarehouse luaWarehouses = new MizMakerLuaWarehouse()) miz.AddEntry("warehouses", luaWarehouses.MakeLua(mission)); DebugLog.Instance.WriteLine(" Adding \"l10n/DEFAULT/dictionary\" entry...", 1); miz.AddEntry("l10n/DEFAULT/dictionary", LuaTools.ReadIncludeLuaFile("Dictionary.lua")); DebugLog.Instance.WriteLine(" Adding \"l10n/DEFAULT/script.lua\" entry...", 1); using (MizMakerLuaScript luaScript = new MizMakerLuaScript()) miz.AddEntry("l10n/DEFAULT/script.lua", luaScript.MakeLua(mission), false); DebugLog.Instance.WriteLine(" Adding .ogg audio media files...", 1); using (MizMakerMediaAudio oggMedia = new MizMakerMediaAudio()) oggMedia.AddMediaFiles(miz, mission, out resourceOggString); DebugLog.Instance.WriteLine(" Adding \"l10n/DEFAULT/mapResource\" entry...", 1); using (MizMakerLuaMapResource luaMapResource = new MizMakerLuaMapResource()) miz.AddEntry("l10n/DEFAULT/mapResource", luaMapResource.MakeLua(mission, resourceOggString)); DebugLog.Instance.WriteLine(" Adding .jpg image media files...", 1); using (MizMakerMediaImages jpgMedia = new MizMakerMediaImages()) jpgMedia.AddMediaFiles(miz, mission); DebugLog.Instance.WriteLine($"Export to .miz file completed in {(DateTime.Now - exportStartTime).TotalSeconds.ToString("F3", NumberFormatInfo.InvariantInfo)} second(s)."); return(miz); }
/// <summary> /// Generates the content of the Lua file. /// </summary> /// <param name="mission">The mission from which to generate the .Miz file.</param> /// <param name="resourceOggString">An string containing the Lua declaring all embedded .ogg files.</param> /// <returns>The contents of the Lua file.</returns> public string MakeLua(DCSMission mission, string resourceOggString) { string lua = LuaTools.ReadIncludeLuaFile("MapResource.lua"); LuaTools.ReplaceKey(ref lua, "OggFiles", resourceOggString); LuaTools.ReplaceKey(ref lua, "MissionID", mission.UniqueID); return(lua); }
/// <summary> /// Returns coordinates situated somewhere between the initial airbase and the center point of objectives. /// </summary> /// <param name="mission">The DCS mission</param> /// <param name="lerpValue">Progess on the airbase to objectives center distance (0.0=airbase, 1.0=objective center)</param> /// <returns>A set of coordinates</returns> public static Coordinates GetCoordinatesOnFlightPath(DCSMission mission, double lerpValue) { Coordinates coordinates = Coordinates.Lerp(mission.InitialPosition, mission.ObjectivesCenter, lerpValue); double distance = mission.InitialPosition.GetDistanceFrom(mission.ObjectivesCenter); // Create some random variation proportional to the total flight path distance distance = Math.Max(20.0, distance); return(coordinates + Coordinates.CreateRandom(distance / 8, distance / 4)); }
public void AddMediaFiles(MizFile miz, DCSMission mission, out string resourceOggString) { resourceOggString = ""; foreach (string ogg in mission.OggFiles) { AddOggFile(miz, ogg, ref resourceOggString); } }
/// <summary> /// Sets the mission bullseye for both coalitions. /// </summary> /// <param name="mission">The mission</param> public void SetBullseye(DCSMission mission) { for (int i = 0; i < 2; i++) { mission.Bullseye[i] = mission.ObjectivesCenter + Coordinates.CreateRandom(20 * Toolbox.NM_TO_METERS, 40 * Toolbox.NM_TO_METERS); } }
/// <summary> /// Generates the list of tasks for the mission. /// </summary> /// <param name="mission"></param> /// <param name="template"></param> /// <param name="missionTask"></param> //public void GenerateMissionTasks(HQMission mission, MissionTemplate template, DefinitionMissionObjective missionTask) //{ // HQDebugLog.Instance.Log("Generating mission briefing objectives..."); // mission.BriefingTasks.Clear(); // mission.BriefingTasks.Add(Language.GetStringRandom("BriefingCommon", $"Task.TakeOff").Replace("$AIRBASE$", mission.Airbases[0].Name)); // foreach (HQMissionObjectiveLocation o in mission.Objectives) // mission.BriefingTasks.Add(Language.GetStringRandom("BriefingMission", $"Task.{missionTask.BriefingTask}").Replace("$NAME$", o.Name)); // mission.BriefingTasks.Add(Language.GetStringRandom("BriefingCommon", $"Task.Land").Replace("$AIRBASE$", mission.Airbases[1].Name)); // HQDebugLog.Instance.Log(""); //} //public void GenerateMissionRemarks(HQMission mission, MissionTemplate template, DefinitionMissionObjective missionTask) //{ // HQDebugLog.Instance.Log("Generating mission briefing remarks..."); // mission.BriefingRemarks.Clear(); // foreach (string s in missionTask.BriefingRemarks) // mission.BriefingRemarks.Add(Language.GetStringRandom("BriefingMission", $"Remark.{s}")); // HQDebugLog.Instance.Log(""); //} public void GenerateRawTextBriefing(DCSMission mission, MissionTemplate template) { DebugLog.Instance.Log("Generating raw text MIZ briefing..."); string text = ""; if (template.GetPlayerCount() == 1) { text += $"{Language.GetString("Briefing", "Subtitle.SinglePlayer")}\n\n"; } else { text += $"{Language.GetString("Briefing", "Subtitle.PvE").Replace("$PLAYERS$", HQTools.ValToString(template.GetPlayerCount()))}\n\n"; } text += mission.BriefingDescription + "\n\n"; // Tasks text += $"{Language.GetString("Briefing", "Section.Tasks").ToUpperInvariant()}{Language.Semicolon}\n"; foreach (string t in mission.BriefingTasks) { text += $"- {t}\n"; } if (mission.BriefingTasks.Count == 0) { text += $"- {Language.GetString("Briefing", "Misc.None")}\n"; } text += "\n"; // Remarks text += $"{Language.GetString("Briefing", "Section.Remarks").ToUpperInvariant()}{Language.Semicolon}\n"; if (mission.BriefingImperialUnits) { text += $"- {Language.GetString("Briefing", "Remark.TotalFlightPlanNM", "Distance", (mission.TotalFlightPlanDistance * HQTools.METERS_TO_NM).ToString("F0"))}\n"; } else { text += $"- {Language.GetString("Briefing", "Remark.TotalFlightPlanKM", "Distance", (mission.TotalFlightPlanDistance / 1000.0).ToString("F0"))}\n"; } foreach (string t in mission.BriefingRemarks) { text += $"- {t}\n"; } text += "\n"; // Flight package //text += $"{GetString("Section.Package").ToUpperInvariant()}{Language.Semicolon}\n"; //foreach (HQMissionBriefingFlightGroup fg in (from HQMissionBriefingFlightGroup f in mission.BriefingFlightPackage where !f.IsSupport select f).OrderBy(x => x.Task)) // text += $"- {fg.Callsign} ({fg.UnitCount}x {GetUnitName(fg.UnitType)}), {HQTools.ValToString(fg.Frequency, "F1")} Mhz\n"; // Make sure endlines are in the proper format (escaped LF) or it can cause bugs. text = text.Replace("\r\n", "\n").Trim(' ', '\n', '\t').Replace("\n", "\\\n"); mission.BriefingRawText = text; DebugLog.Instance.Log(""); }