private static UnitCallsignFamily GetCallsignFamilyFromUnitFamily(UnitFamily unitFamily) { switch (unitFamily) { case UnitFamily.PlaneAWACS: return(UnitCallsignFamily.AWACS); case UnitFamily.PlaneTankerBasket: case UnitFamily.PlaneTankerBoom: return(UnitCallsignFamily.Tanker); default: return(UnitCallsignFamily.Aircraft); } }
internal static bool IsAirDefense(this UnitFamily unitFamily) { switch (unitFamily) { case UnitFamily.VehicleAAA: case UnitFamily.VehicleAAAStatic: case UnitFamily.VehicleInfantryMANPADS: case UnitFamily.VehicleSAMLong: case UnitFamily.VehicleSAMMedium: case UnitFamily.VehicleSAMShort: case UnitFamily.VehicleSAMShortIR: return(true); } return(false); }
internal DBCommonCarrierGroup() { INIFile ini = new($"{BRPaths.DATABASE}CarrierGroup.ini"); CourseLength = Math.Max(5.0, ini.GetValue <double>("CarrierGroup", "CourseLength")) * Toolbox.NM_TO_METERS; IdealWindOfDeck = Math.Max(0.0, ini.GetValue <double>("CarrierGroup", "IdealWindOfDeck")) * Toolbox.KNOTS_TO_METERS_PER_SECOND; MinimumCarrierSpeed = Math.Max(0.0, ini.GetValue <double>("CarrierGroup", "MinimumCarrierSpeed")) * Toolbox.KNOTS_TO_METERS_PER_SECOND; ShipSpacing = Math.Max(0.1, ini.GetValue <double>("CarrierGroup", "ShipSpacing")) * Toolbox.NM_TO_METERS; EscortUnitFamilies = new UnitFamily[ESCORT_FAMILIES_SHIP_COUNT][]; for (int i = 0; i < ESCORT_FAMILIES_SHIP_COUNT; i++) { EscortUnitFamilies[i] = ini.GetValueArray <UnitFamily>( "CarrierGroup", "EscortUnitFamilies.CarrierCount" + ((i == ESCORT_FAMILIES_SHIP_COUNT - 1) ? "Max" : $"{i + 1}")); } }
internal static object GetTACANCallsign(UnitFamily unitFamily) { switch (unitFamily) { default: return("TCN"); case UnitFamily.ShipCarrierCATOBAR: case UnitFamily.ShipCarrierSTOBAR: case UnitFamily.ShipCarrierSTOVL: return("CVN"); case UnitFamily.PlaneTankerBasket: case UnitFamily.PlaneTankerBoom: return("TKR"); } }
internal static UnitCategory GetUnitCategory(this UnitFamily unitFamily) { switch (unitFamily) { case UnitFamily.HelicopterAttack: case UnitFamily.HelicopterTransport: case UnitFamily.HelicopterUtility: return(UnitCategory.Helicopter); case UnitFamily.PlaneAttack: case UnitFamily.PlaneAWACS: case UnitFamily.PlaneBomber: case UnitFamily.PlaneDrone: case UnitFamily.PlaneFighter: case UnitFamily.PlaneInterceptor: case UnitFamily.PlaneSEAD: case UnitFamily.PlaneStrike: case UnitFamily.PlaneTankerBasket: case UnitFamily.PlaneTankerBoom: case UnitFamily.PlaneTransport: return(UnitCategory.Plane); case UnitFamily.ShipCarrierCATOBAR: case UnitFamily.ShipCarrierSTOBAR: case UnitFamily.ShipCarrierSTOVL: case UnitFamily.ShipCruiser: case UnitFamily.ShipFrigate: case UnitFamily.ShipSpeedboat: case UnitFamily.ShipSubmarine: case UnitFamily.ShipTransport: return(UnitCategory.Ship); case UnitFamily.StaticStructureMilitary: case UnitFamily.StaticStructureProduction: case UnitFamily.FOB: case UnitFamily.StaticStructureOffshore: return(UnitCategory.Static); case UnitFamily.Cargo: return(UnitCategory.Cargo); default: return(UnitCategory.Vehicle); } }
public static DCSMissionUnitGroup FromCoalitionArmyAndUnitFamily( string luaGroup, string luaUnit, DefinitionCoalition army, TimePeriod timePeriod, UnitFamily family, int unitCount, int groupID, Coalition coalition, Coordinates coordinates) { List <string> unitList = new List <string>(); UnitCategory category = HQTools.GetUnitCategoryFromUnitFamily(family); // FIXME: extendSearchToUnitsOfOtherFamilies and returnDefaultIfNoneFound should be parameters string[] availableUnits = army.GetUnits(timePeriod, family, false, false); if (availableUnits.Length == 0) { DebugLog.Instance.Log($" WARNING: Cannot spawn unit group, no units of type {family.ToString().ToUpperInvariant()} for coalition {army.DisplayName.ToUpperInvariant()} in {timePeriod.ToString().ToUpperInvariant()}."); return(null); } do { unitList.AddRange((from u in HQTools.RandomFrom(availableUnits).Split('|') select u.Trim())); } while (unitList.Count < unitCount); // Some unit categories (helicopters and planes) require all units to be the same type if ((category == UnitCategory.Helicopter) || (category == UnitCategory.Plane)) { for (int i = 1; i < unitList.Count; i++) { unitList[i] = unitList[0]; } } DCSMissionUnitGroup uGroup = new DCSMissionUnitGroup( luaGroup, luaUnit, category, groupID, coalition, coordinates, unitList.ToArray()); return(uGroup); }
internal bool IsValidForFamilyCountryAndPeriod(UnitFamily family, Country[] countries, Decade decade) { // Unit does not belong to the required family if (!Families.Contains(family)) { return(false); } foreach (Country country in countries) { if (!Operators.ContainsKey(country)) { continue; } if ((Operators[country][0] <= decade) && (Operators[country][1] >= decade)) { return(true); // Found one operator operating the unit during the required decade } } return(false); }
/// <summary> /// Returns the UnitCategory an UnitFamily belongs to. /// </summary> /// <param name="family">The unit family.</param> /// <returns>The unit category.</returns> public static UnitCategory GetUnitCategoryFromUnitFamily(UnitFamily family) { string roleStr = family.ToString().ToLowerInvariant(); if (roleStr.StartsWith("helicopter")) { return(UnitCategory.Helicopter); } if (roleStr.StartsWith("plane")) { return(UnitCategory.Plane); } if (roleStr.StartsWith("ship")) { return(UnitCategory.Ship); } if (roleStr.StartsWith("static")) { return(UnitCategory.Static); } return(UnitCategory.Vehicle); }
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); } }
/// <summary> /// Add surface-to-air defense groups. /// </summary> /// <param name="mission">Mission to which generated units should be added</param> /// <param name="template">Mission template to use</param> /// <param name="airDefenseRange">Air-defense range category</param> /// <param name="enemyCoalitionDB">Enemy coalition database entry</param> /// <param name="coalition">Coalition of the spawn points air defense must be spawned at, or null to spawn them anywhere</param> /// <param name="unitMods">Unit mods the units can belong to</param> private void AddAirDefenseUnits(DCSMission mission, AirDefenseRange airDefenseRange, DBEntryCoalition enemyCoalitionDB, Coalition?coalition, string[] unitMods) { // Get the proper number of groups int groupCount = Database.Instance.Common. EnemyAirDefense[(int)airDefense].GroupsInArea[(int)airDefenseRange].GetValue(); if (groupCount < 1) { return; // No groups to add, no need to go any further } DCSMissionUnitGroupFlags flags = optionsShowEnemyUnits; UnitFamily[] unitFamilies; TheaterLocationSpawnPointType[] validSpawnPoints; switch (airDefenseRange) { default: // case AirDefenseRange.ShortRange: unitFamilies = new UnitFamily[] { UnitFamily.VehicleAAA, UnitFamily.VehicleAAAStatic, UnitFamily.VehicleInfantryMANPADS, UnitFamily.VehicleSAMShort, UnitFamily.VehicleSAMShort, UnitFamily.VehicleSAMShortIR, UnitFamily.VehicleSAMShortIR }; validSpawnPoints = new TheaterLocationSpawnPointType[] { TheaterLocationSpawnPointType.LandSmall, TheaterLocationSpawnPointType.LandMedium, TheaterLocationSpawnPointType.LandLarge }; break; case AirDefenseRange.MediumRange: unitFamilies = new UnitFamily[] { UnitFamily.VehicleSAMMedium }; validSpawnPoints = new TheaterLocationSpawnPointType[] { TheaterLocationSpawnPointType.LandMedium, TheaterLocationSpawnPointType.LandLarge }; break; case AirDefenseRange.LongRange: unitFamilies = new UnitFamily[] { UnitFamily.VehicleSAMLong }; validSpawnPoints = new TheaterLocationSpawnPointType[] { TheaterLocationSpawnPointType.LandLarge }; break; } for (int i = 0; i < groupCount; i++) { // Find spawn point at the proper distance from the objective(s), but not to close from starting airbase DBEntryTheaterSpawnPoint?spawnPoint = UnitMaker.SpawnPointSelector.GetRandomSpawnPoint( validSpawnPoints, centerPoint, distsFromCenter[(int)airDefenseRange], opposingPoint, new MinMaxD(minDistFromOpposingPoint[(int)airDefenseRange], 99999), coalition); // No spawn point found, stop here. if (!spawnPoint.HasValue) { DebugLog.Instance.WriteLine($"No spawn point found for {airDefenseRange} air defense unit groups", 1, DebugLogMessageErrorLevel.Warning); return; } string[] units = enemyCoalitionDB.GetRandomUnits(Toolbox.RandomFrom(unitFamilies), mission.DateTime.Decade, 1, unitMods); DCSMissionUnitGroup group = UnitMaker.AddUnitGroup( mission, units, ally? Side.Ally : Side.Enemy, spawnPoint.Value.Coordinates, "GroupVehicle", "UnitVehicle", Toolbox.BRSkillLevelToDCSSkillLevel(skillLevel), flags); if (group == null) { DebugLog.Instance.WriteLine($"Failed to add {airDefenseRange} air defense unit group of type {units[0]}", 1, DebugLogMessageErrorLevel.Warning); } } }
private Coordinates PlaceInAirbase(MissionTemplateRecord template, DBEntrySituation situationDB, DBEntryAirbase playerAirbase, List <KeyValuePair <string, object> > extraSettings, DBEntryObjectiveTarget targetDB, DBEntryObjectiveTargetBehavior targetBehaviorDB, ref string luaUnit, Coordinates objectiveCoordinates, int unitCount, UnitFamily objectiveTargetUnitFamily) { int airbaseID = 0; var parkingSpotIDsList = new List <int>(); var parkingSpotCoordinatesList = new List <Coordinates>(); var targetAirbaseOptions = (from DBEntryAirbase airbaseDB in situationDB.GetAirbases(template.OptionsMission.Contains("InvertCountriesCoalitions")) where airbaseDB.DCSID != playerAirbase.DCSID select airbaseDB).OrderBy(x => x.Coordinates.GetDistanceFrom(objectiveCoordinates)); DBEntryAirbase targetAirbase = targetAirbaseOptions.FirstOrDefault(x => template.OptionsMission.Contains("SpawnAnywhere") ? true : x.Coalition == template.ContextPlayerCoalition.GetEnemy()); airbaseID = targetAirbase.DCSID; var parkingSpots = UnitMaker.SpawnPointSelector.GetFreeParkingSpots( targetAirbase.DCSID, unitCount, objectiveTargetUnitFamily, targetBehaviorDB.Location == DBEntryObjectiveTargetBehaviorLocation.SpawnOnAirbaseParkingNoHardenedShelter); parkingSpotIDsList = parkingSpots.Select(x => x.DCSID).ToList(); parkingSpotCoordinatesList = parkingSpots.Select(x => x.Coordinates).ToList(); luaUnit += "Parked"; extraSettings.Add("GroupAirbaseID".ToKeyValuePair(airbaseID)); extraSettings.Add("ParkingID".ToKeyValuePair(parkingSpotIDsList.ToArray())); extraSettings.Add("UnitX".ToKeyValuePair((from Coordinates coordinates in parkingSpotCoordinatesList select coordinates.X).ToArray())); extraSettings.Add("UnitY".ToKeyValuePair((from Coordinates coordinates in parkingSpotCoordinatesList select coordinates.Y).ToArray())); return(targetAirbase.Coordinates); }
private bool ValidateAirfieldParking(List <DBEntryAirbaseParkingSpot> parkingSpots, UnitFamily unitFamily, int unitCount) { var openSpots = parkingSpots.Count(X => X.ParkingType == ParkingSpotType.OpenAirSpawn); if (openSpots >= unitCount) //Is there just enough open spaces { return(true); } // Helicopters if (unitFamily.GetUnitCategory() == UnitCategory.Helicopter) { return(parkingSpots.Count(X => X.ParkingType == ParkingSpotType.HelicopterOnly) + openSpots > unitCount); } // Aircraft that can't use bunkers if (IsBunkerUnsuitable(unitFamily)) { return(parkingSpots.Count(X => X.ParkingType == ParkingSpotType.AirplaneOnly) + openSpots > unitCount); } // Bunkerable aircraft return(parkingSpots.Count(X => X.ParkingType == ParkingSpotType.HardenedAirShelter) + openSpots > unitCount); }
private bool IsBunkerUnsuitable(UnitFamily unitFamily) => LARGE_AIRCRAFT.Contains(unitFamily) || unitFamily.GetUnitCategory() == UnitCategory.Helicopter;
private UnitMakerGroupInfo?AddStaticGroup( Country country, Coalition coalition, DCSSkillLevel?skill, UnitFamily unitFamily, Side side, string[] unitSets, string groupName, UnitCallsign?callsign, string groupTypeLua, Coordinates coordinates, UnitMakerGroupFlags unitMakerGroupFlags, params KeyValuePair <string, object>[] extraSettings ) { List <int> unitsIDList = new List <int>(); var initalGroupId = GroupID; foreach (var unitSet in unitSets) { DBEntryUnit unitDB = Database.Instance.GetEntry <DBEntryUnit>(unitSet); if (unitDB == null) { BriefingRoom.PrintToLog($"Unit \"{unitSet}\" not found.", LogMessageErrorLevel.Warning); continue; } int unitSetIndex = 0; foreach (var DCSID in unitDB.DCSIDs) { var groupHeading = GetGroupHeading(coordinates, extraSettings); SetUnitCoordinatesAndHeading(unitDB, unitSetIndex, coordinates, groupHeading, out Coordinates unitCoordinates, out double unitHeading); var firstUnitID = UnitID; var groupLua = CreateGroup( groupTypeLua, unitCoordinates, groupName, extraSettings ); var unitLua = DCSID == "FARP" ? "UnitStaticFOB" : (unitDB.Category == UnitCategory.Cargo ? "UnitCargo" : "UnitStatic"); var unitsLuaTable = AddUnit( DCSID, groupName, callsign, 1, unitSetIndex, unitDB, unitLua, coordinates, unitMakerGroupFlags, extraSettings ); unitsIDList.Add(UnitID); unitSetIndex++; UnitID++; GeneratorTools.ReplaceKey(ref groupLua, "Units", unitsLuaTable); GeneratorTools.ReplaceKey(ref groupLua, "UnitID", firstUnitID); // Must be after units are added GeneratorTools.ReplaceKey(ref groupLua, "Skill", skill); // Must be after units are added, because skill is set as a unit level GeneratorTools.ReplaceKey(ref groupLua, "Hidden", GeneratorTools.GetHiddenStatus(Template.OptionsFogOfWar, side, unitMakerGroupFlags)); // If "hidden" was not set through custom values AddUnitGroupToTable(country, UnitCategory.Static, groupLua); BriefingRoom.PrintToLog($"Added group of {DCSID} {coalition} {unitFamily} at {coordinates}"); GroupID++; } } if (unitMakerGroupFlags.HasFlag(UnitMakerGroupFlags.EmbeddedAirDefense) && unitFamily != UnitFamily.StaticStructureOffshore) { var firstUnitID = UnitID; string[] airDefenseUnits = GeneratorTools.GetEmbeddedAirDefenseUnits(Template, side); var groupLua = CreateGroup( "GroupVehicle", coordinates, groupName, extraSettings ); var(unitsLuaTable, embeddedunitsIDList) = AddUnits( airDefenseUnits, groupName, callsign, "UnitVehicle", coordinates, unitMakerGroupFlags, extraSettings ); GeneratorTools.ReplaceKey(ref groupLua, "Units", unitsLuaTable); GeneratorTools.ReplaceKey(ref groupLua, "UnitID", firstUnitID); // Must be after units are added GeneratorTools.ReplaceKey(ref groupLua, "Skill", skill); // Must be after units are added, because skill is set as a unit level GeneratorTools.ReplaceKey(ref groupLua, "Hidden", GeneratorTools.GetHiddenStatus(Template.OptionsFogOfWar, side, unitMakerGroupFlags)); // If "hidden" was not set through custom values GroupID++; unitsIDList.AddRange(embeddedunitsIDList); AddUnitGroupToTable(country, UnitCategory.Vehicle, groupLua); BriefingRoom.PrintToLog($"Added group of Embedded Air Defense for Static {coalition} {unitFamily} at {coordinates}"); } DBEntryUnit firstUnitDB = Database.Instance.GetEntry <DBEntryUnit>(unitSets.First()); if (firstUnitDB == null) { return(new UnitMakerGroupInfo(initalGroupId, coordinates, unitsIDList, groupName)); } return(new UnitMakerGroupInfo(initalGroupId, coordinates, unitsIDList, groupName, firstUnitDB.AircraftData.RadioFrequency, firstUnitDB)); }
private List <DBEntryAirbaseParkingSpot> FilterAndSortSuitableSpots(DBEntryAirbaseParkingSpot[] parkingspots, UnitFamily unitFamily, bool requiresOpenAirParking) { var validTypes = new List <ParkingSpotType> { ParkingSpotType.OpenAirSpawn, ParkingSpotType.HardenedAirShelter, ParkingSpotType.AirplaneOnly }; if (unitFamily.GetUnitCategory() == UnitCategory.Helicopter) { validTypes = new List <ParkingSpotType> { ParkingSpotType.OpenAirSpawn, ParkingSpotType.HelicopterOnly, } } ; else if (IsBunkerUnsuitable(unitFamily) || requiresOpenAirParking) { validTypes = new List <ParkingSpotType> { ParkingSpotType.OpenAirSpawn } } ; return(parkingspots.Where(x => validTypes.Contains(x.ParkingType)).OrderBy(x => x.ParkingType).ToList()); }
public static string GetBriefingStringForUnitFamily(UnitFamily unitFamily, bool plural) { return(Database.Instance.Common.UnitBriefingNames[(int)unitFamily][plural ? 1 : 0]); }
internal List <DBEntryAirbaseParkingSpot> GetFreeParkingSpots(int airbaseID, int unitCount, UnitFamily unitFamily, bool requiresOpenAirParking = false) { if (!AirbaseParkingSpots.ContainsKey(airbaseID)) { throw new BriefingRoomException($"Airbase {airbaseID} not found in parking map"); } var airbaseDB = SituationDB.GetAirbases(InvertCoalition).First(x => x.DCSID == airbaseID); var parkingSpots = new List <DBEntryAirbaseParkingSpot>(); DBEntryAirbaseParkingSpot?lastSpot = null; for (int i = 0; i < unitCount; i++) { var viableSpots = FilterAndSortSuitableSpots(AirbaseParkingSpots[airbaseID].ToArray(), unitFamily, requiresOpenAirParking); if (viableSpots.Count == 0) { throw new BriefingRoomException("Airbase didn't have enough suitable parking spots."); } var parkingSpot = viableSpots.First(); if (lastSpot.HasValue) //find nearest spot distance wise in attempt to cluster { parkingSpot = viableSpots .Aggregate((acc, x) => acc.Coordinates.GetDistanceFrom(lastSpot.Value.Coordinates) > x.Coordinates.GetDistanceFrom(lastSpot.Value.Coordinates) ? x : acc); } lastSpot = parkingSpot; AirbaseParkingSpots[airbaseID].Remove(parkingSpot); parkingSpots.Add(parkingSpot); } return(parkingSpots); }
public static bool IsUnitFamilyAircraft(UnitFamily value) { return ((GetUnitCategoryFromUnitFamily(value) == UnitCategory.Helicopter) || (GetUnitCategoryFromUnitFamily(value) == UnitCategory.Plane)); }
private static void CreateTaskString(DCSMission mission, int pluralIndex, ref string taskString, string objectiveName, UnitFamily objectiveTargetUnitFamily) { // Get tasking string for the briefing if (string.IsNullOrEmpty(taskString)) { taskString = "Complete objective $OBJECTIVENAME$"; } GeneratorTools.ReplaceKey(ref taskString, "ObjectiveName", objectiveName); GeneratorTools.ReplaceKey(ref taskString, "UnitFamily", Database.Instance.Common.Names.UnitFamilies[(int)objectiveTargetUnitFamily][pluralIndex]); mission.Briefing.AddItem(DCSMissionBriefingItemType.Task, taskString); }
/// <summary> /// Adds media files, scripts and units associated with mission features. /// </summary> /// <param name="mission">Mission to which features and extensions should be added</param> /// <param name="template">Mission template to use</param> /// <param name="objectiveDB">Mission objective database entry</param> /// <param name="coalitionsDB">Coalitions database entries</param> public void GenerateExtensionsAndFeatures(DCSMission mission, MissionTemplate template, DBEntryObjective objectiveDB, DBEntryCoalition[] coalitionsDB) { int i, j; DBEntryExtension[] extensions = Database.Instance.GetEntries <DBEntryExtension>(template.ScriptExtensions); foreach (DBEntryExtension extension in extensions) { AddIncludedFiles(mission, extension); } DBEntryMissionFeature[] features = Database.Instance.GetEntries <DBEntryMissionFeature>(objectiveDB.MissionFeatures); foreach (DBEntryMissionFeature feature in features) { AddIncludedFiles(mission, feature); // No unit family in the unit group, so not unit group to add if (feature.UnitGroup.Families.Length == 0) { continue; } Side side = feature.UnitGroup.Flags.HasFlag(DBUnitGroupFlags.Friendly) ? Side.Ally : Side.Enemy; UnitFamily unitFamily = Toolbox.RandomFrom(feature.UnitGroup.Families); // Pick units string[] units = coalitionsDB[(int)((side == Side.Ally) ? mission.CoalitionPlayer : mission.CoalitionEnemy)].GetRandomUnits( unitFamily, mission.DateTime.Decade, feature.UnitGroup.Count.GetValue(), template.UnitMods); DCSSkillLevel skillLevel; if (side == Side.Ally) { skillLevel = Toolbox.BRSkillLevelToDCSSkillLevel(template.SituationFriendlyAISkillLevel); } else { skillLevel = Toolbox.IsUnitFamilyAircraft(unitFamily) ? Toolbox.BRSkillLevelToDCSSkillLevel(template.SituationEnemySkillLevelAir) : Toolbox.BRSkillLevelToDCSSkillLevel(template.SituationEnemySkillLevelGround); } DCSMissionUnitGroupFlags flags = 0; List <int> unitGroupsID = new List <int>(); for (i = 0; i < mission.Objectives.Length; i++) { Coordinates[] coordinates = new Coordinates[2]; for (j = 0; j < 2; j++) { switch (feature.UnitGroupCoordinates[j]) { case DBEntryMissionFeatureUnitGroupLocation.Homebase: coordinates[j] = mission.InitialPosition + Coordinates.CreateRandom(2, 6) * Toolbox.NM_TO_METERS; break; case DBEntryMissionFeatureUnitGroupLocation.Objective: coordinates[j] = mission.Objectives[i].Coordinates; break; case DBEntryMissionFeatureUnitGroupLocation.ObjectiveNear: coordinates[j] = mission.Objectives[i].Coordinates + Coordinates.CreateRandom(1.5, 4) * Toolbox.NM_TO_METERS; break; case DBEntryMissionFeatureUnitGroupLocation.Waypoint: coordinates[j] = mission.Objectives[i].WaypointCoordinates; break; case DBEntryMissionFeatureUnitGroupLocation.WaypointNear: coordinates[j] = mission.Objectives[i].WaypointCoordinates + Coordinates.CreateRandom(1.5, 4) * Toolbox.NM_TO_METERS; break; } } DCSMissionUnitGroup group = UnitMaker.AddUnitGroup( mission, units, side, coordinates[0], Toolbox.RandomFrom(feature.UnitGroup.LuaGroup), feature.UnitGroup.LuaUnit, skillLevel, flags, UnitTaskPayload.Default, coordinates[1]); if (group == null) { DebugLog.Instance.WriteLine($"Failed to create mission feature unit group for objective #{i + 1} made of the following units: {string.Join(", ", units)}", 1, DebugLogMessageErrorLevel.Warning); } unitGroupsID.Add(group.GroupID); // Add aircraft group to the queue of aircraft groups to be spawned if (!feature.UnitGroup.Flags.HasFlag(DBUnitGroupFlags.ManualActivation) && ((group.Category == UnitCategory.Helicopter) || (group.Category == UnitCategory.Plane) || feature.UnitGroup.Flags.HasFlag(DBUnitGroupFlags.DelaySpawn))) { mission.AircraftSpawnQueue.Add(new DCSMissionAircraftSpawnQueueItem(group.GroupID, true)); } } if (unitGroupsID.Count > 0) { mission.LuaSettings += $"briefingRoom.mission.featuresUnitGroups.{feature.ID} = {{ {string.Join(", ", unitGroupsID)} }}"; } } }
/// <summary> /// Spawn a group of support units. /// </summary> /// <param name="mission">Mission to which generated units should be added</param> /// <param name="allyCoalitionDB">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 UnitFlightGroupBriefingDescription AddSupportUnit(DCSMission mission, DBEntryCoalition allyCoalitionDB, UnitFamily unitFamily, string[] unitMods, Tacan TACAN = null) { DebugLog.Instance.WriteLine($"Adding {unitFamily} support unit...", 1); string[] validUnitTypes = allyCoalitionDB.GetRandomUnits(unitFamily, mission.DateTime.Decade, 1, unitMods, false); if (validUnitTypes.Length == 0) { DebugLog.Instance.WriteLine($"No support unit found for this role in coalition \"{allyCoalitionDB.ID}\"", 2); return(new UnitFlightGroupBriefingDescription()); // Empty FG info will automatically be discarded } string groupLua; switch (unitFamily) { case UnitFamily.PlaneAWACS: groupLua = "GroupAircraftAWACS"; break; case UnitFamily.PlaneTankerBasket: case UnitFamily.PlaneTankerBoom: groupLua = "GroupAircraftTanker"; break; default: // Should never happen return(new UnitFlightGroupBriefingDescription()); // Empty FG info will automatically be discarded } Coordinates location = GeneratorTools.GetCoordinatesOnFlightPath(mission, .5) + Coordinates.CreateRandom(8, 12) * Toolbox.NM_TO_METERS; Coordinates location2 = location + Coordinates.CreateRandom(12, 20) * Toolbox.NM_TO_METERS; string unitType = Toolbox.RandomFrom(validUnitTypes); DCSMissionUnitGroup group = UnitMaker.AddUnitGroup( mission, new string[] { unitType }, Side.Ally, location, groupLua, "UnitAircraft", DCSSkillLevel.Excellent, 0, UnitTaskPayload.Default, location2); if (group == null) { return(new UnitFlightGroupBriefingDescription()); // 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 return(new UnitFlightGroupBriefingDescription( group.Name, group.Units.Length, unitType, (unitFamily == UnitFamily.PlaneAWACS) ? "Early warning" : "Refueling", Database.Instance.GetEntry <DBEntryUnit>(unitType).AircraftData.GetRadioAsString(), TACAN != null? $"TACAN: {TACAN.ToString()}":"")); }
internal UnitMakerGroupInfo?AddUnitGroup( string[] units, Side side, UnitFamily unitFamily, string groupTypeLua, string unitTypeLua, Coordinates coordinates, UnitMakerGroupFlags unitMakerGroupFlags = 0, params KeyValuePair <string, object>[] extraSettings) { if (units.Length == 0) { return(null); } Coalition coalition = (side == Side.Ally) ? PlayerCoalition : PlayerCoalition.GetEnemy(); Country country = (coalition == Coalition.Blue) ? Country.CJTFBlue : Country.CJTFRed; if (extraSettings.Any(x => x.Key == "Country")) { country = (Country)extraSettings.First(x => x.Key == "Country").Value; } var skill = GeneratorTools.GetDefaultSkillLevel(Template, side); if (extraSettings.Any(x => x.Key == "Skill")) { skill = (DCSSkillLevel)extraSettings.First(x => x.Key == "Skill").Value; } var isUsingSkynet = Template.MissionFeatures.Contains("SkynetIADS"); string groupName; UnitCallsign?callsign = null; if (unitFamily.GetUnitCategory().IsAircraft()) { callsign = CallsignGenerator.GetCallsign(unitFamily, coalition, side, isUsingSkynet); groupName = callsign.Value.GroupName; if (extraSettings.Any(x => x.Key == "PlayerStartingType") && extraSettings.First(x => x.Key == "PlayerStartingType").Value.ToString() == "TakeOffParking") { groupName += "(C)"; } } else { groupName = GeneratorTools.GetGroupName(GroupID, unitFamily, side, isUsingSkynet); } if (unitFamily.GetUnitCategory() == UnitCategory.Static || unitFamily.GetUnitCategory() == UnitCategory.Cargo && unitFamily != UnitFamily.FOB) { return(AddStaticGroup( country, coalition, skill, unitFamily, side, units, groupName, callsign, groupTypeLua, coordinates, unitMakerGroupFlags, extraSettings )); } var groupLua = CreateGroup( groupTypeLua, coordinates, groupName, extraSettings ); int firstUnitID = UnitID; var(unitsLuaTable, unitsIDList) = AddUnits( units, groupName, callsign, unitTypeLua, coordinates, unitMakerGroupFlags, extraSettings ); if (unitsIDList.Count == 0) { return(null); // No valid units added to this group } GeneratorTools.ReplaceKey(ref groupLua, "Units", unitsLuaTable); DBEntryUnit firstUnitDB = units.Select(x => Database.Instance.GetEntry <DBEntryUnit>(x)).First(x => x != null); var aircraftCategories = new UnitCategory[] { UnitCategory.Helicopter, UnitCategory.Plane }; var isAircraft = firstUnitDB != null && aircraftCategories.Contains(firstUnitDB.Category); if (isAircraft) { groupLua = ApplyAircraftFields(groupLua, firstUnitDB, extraSettings); if (unitMakerGroupFlags.HasFlag(UnitMakerGroupFlags.ImmediateAircraftSpawn)) { Mission.AppendValue("AircraftActivatorCurrentQueue", $"{GroupID},"); } else if (unitMakerGroupFlags.HasFlag(UnitMakerGroupFlags.RadioAircraftSpawn)) { Mission.AppendValue("AircraftRadioActivator", $"{{{GroupID}, \"{groupName}\"}},"); } else if (groupTypeLua != "GroupAircraftParkedUncontrolled") { Mission.AppendValue("AircraftActivatorReserveQueue", $"{GroupID},"); } } GeneratorTools.ReplaceKey(ref groupLua, "UnitID", firstUnitID); // Must be after units are added GeneratorTools.ReplaceKey(ref groupLua, "Skill", skill); // Must be after units are added, because skill is set as a unit level GeneratorTools.ReplaceKey(ref groupLua, "Hidden", GeneratorTools.GetHiddenStatus(Template.OptionsFogOfWar, side, unitMakerGroupFlags)); // If "hidden" was not set through custom values AddUnitGroupToTable(country, unitFamily.GetUnitCategory(), groupLua); BriefingRoom.PrintToLog($"Added group of {units.Length} {coalition} {unitFamily} at {coordinates}"); GroupID++; if (firstUnitDB == null) { return(new UnitMakerGroupInfo(GroupID - 1, coordinates, unitsIDList, groupName)); } return(new UnitMakerGroupInfo(GroupID - 1, coordinates, unitsIDList, groupName, firstUnitDB.AircraftData.RadioFrequency, firstUnitDB)); }
/// <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> /// 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 }