diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..73fab72 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +_[Dd]ebug[Oo]utput/ +[Ii]nclude/[Ss]cript.lua +*.miz diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..36e2333 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "Lua.workspace.library": ["Libraries/dcs-world-api.lua"], + "Lua.diagnostics.disable": ["deprecated"] +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..e26fb8e --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,16 @@ +{ + "key": "ctrl+shift+b", + "isDefault": true, + "label": "Make project", + "type": "shell", + "command": "./Make.bat", + "presentation": { + "echo": true, + "reveal": "always", + "focus": true, + "panel": "shared", + "showReuseMessage": false, + "clear": true + }, + "group": { "kind": "build", "isDefault": true } +} diff --git a/API/dcs-world-api.lua b/API/dcs-world-api.lua new file mode 100644 index 0000000..1c18603 --- /dev/null +++ b/API/dcs-world-api.lua @@ -0,0 +1,4868 @@ +--[[ DCS World Lua Type Definitions +Generated from schema: dcs-world-api-schema.json +DO NOT MODIFY - AUTO-GENERATED FILE +Generated on: 2025-05-25T17:18:57.070587 +--]] + +---@meta + +-- Global Namespaces and Classes +--- Provides functions for logging and accessing mission environment data in the DCS World scripting environment. All logging messages are written to the dcs.log file in the user's Saved Games folder. +---@class env +---@field mission table Table containing the complete mission data structure as defined in the mission file. +---@field warehouses table Table containing the complete warehouse inventory data structure for the current mission. +env = env or {} +--- Writes an informational message to the DCS log file, with an optional in-game notification popup. +---@param message string Text content to write to the log file. +---@param showMessageBox? boolean `true` to display an in-game popup with the message, `false` to log silently. +function env.info(message, showMessageBox) end + +--- Writes a warning message to the DCS log file, with an optional in-game notification popup. +---@param message string Text content to write to the log file. +---@param showMessageBox? boolean `true` to display an in-game popup with the message, `false` to log silently. +function env.warning(message, showMessageBox) end + +--- Writes an error message to the DCS log file, with an optional in-game notification popup. +---@param message string Text content to write to the log file. +---@param showMessageBox? boolean `true` to display an in-game popup with the message, `false` to log silently. +function env.error(message, showMessageBox) end + +--- Configures whether Lua runtime errors generate in-game popup notifications. +---@param enabled boolean `true` to display error message boxes for Lua errors, `false` to suppress them. +function env.setErrorMessageBoxEnabled(enabled) end + +--- Retrieves a value from the mission dictionary using the specified key. +---@param key string Dictionary key to look up in the mission environment. +---@return any +function env.getValueDictByKey(key) end + +--- Shows the training interface. Note: Undocumented function in the DCS API. +function env.showTraining() end + + +--- Provides functions for multiplayer networking, including chat, player management, and server administration. +---@class net +---@field CHAT_ALL number #READONLY Constant for targeting chat messages to all players. +---@field CHAT_TEAM number #READONLY Constant for targeting chat messages to team members only. +---@field ERR_BAD_CALLSIGN number #READONLY Error code indicating an invalid player callsign. +---@field ERR_BANNED number #READONLY Error code indicating a banned player. +---@field ERR_CONNECT_FAILED number #READONLY Error code indicating a connection failure. +---@field ERR_DENIED_TRIAL_ONLY number #READONLY Error code indicating denial due to trial version limitations. +---@field ERR_INVALID_ADDRESS number #READONLY Error code indicating an invalid network address. +---@field ERR_INVALID_PASSWORD number #READONLY Error code indicating an incorrect password. +---@field ERR_KICKED number #READONLY Error code indicating a player was kicked. +---@field ERR_NOT_ALLOWED number #READONLY Error code indicating an operation is not permitted. +---@field ERR_PROTOCOL_ERROR number #READONLY Error code indicating a network protocol error. +---@field ERR_REFUSED number #READONLY Error code indicating a connection was refused. +---@field ERR_SERVER_FULL number #READONLY Error code indicating the server has reached capacity. +---@field ERR_TAINTED_CLIENT number #READONLY Error code indicating a client with modified files. +---@field ERR_THATS_OKAY number #READONLY Error code indicating a successful operation. +---@field ERR_TIMEOUT number #READONLY Error code indicating a network timeout. +---@field ERR_WRONG_VERSION number #READONLY Error code indicating incompatible DCS versions. +---@field GAME_MODE_CONQUEST number #READONLY Game mode constant for Conquest missions. +---@field GAME_MODE_LAST_MAN_STANDING number #READONLY Game mode constant for Last Man Standing missions. +---@field GAME_MODE_MISSION number #READONLY Game mode constant for standard missions. +---@field GAME_MODE_TEAM_DEATH_MATCH number #READONLY Game mode constant for Team Deathmatch missions. +---@field PS_CAR number #READONLY Statistic ID for counting player's ground vehicle kills. +---@field PS_CRASH number #READONLY Statistic ID for counting player's crashes. +---@field PS_EJECT number #READONLY Statistic ID for counting player's ejections. +---@field PS_EXTRA_ALLY_AAA number #READONLY Statistic ID for counting player's friendly AAA kills. +---@field PS_EXTRA_ALLY_FIGHTERS number #READONLY Statistic ID for counting player's friendly fighter kills. +---@field PS_EXTRA_ALLY_SAM number #READONLY Statistic ID for counting player's friendly SAM kills. +---@field PS_EXTRA_ALLY_TRANSPORTS number #READONLY Statistic ID for counting player's friendly transport kills. +---@field PS_EXTRA_ALLY_TROOPS number #READONLY Statistic ID for counting player's friendly ground troop kills. +---@field PS_EXTRA_ENEMY_AAA number #READONLY Statistic ID for counting player's enemy AAA kills. +---@field PS_EXTRA_ENEMY_FIGHTERS number #READONLY Statistic ID for counting player's enemy fighter kills. +---@field PS_EXTRA_ENEMY_SAM number #READONLY Statistic ID for counting player's enemy SAM kills. +---@field PS_EXTRA_ENEMY_TRANSPORTS number #READONLY Statistic ID for counting player's enemy transport kills. +---@field PS_EXTRA_ENEMY_TROOPS number #READONLY Statistic ID for counting player's enemy ground troop kills. +---@field PS_LAND number #READONLY Statistic ID for counting player's successful landings. +---@field PS_PING number #READONLY Statistic ID for player's network latency in milliseconds. +---@field PS_PLANE number #READONLY Statistic ID for counting player's aircraft kills. +---@field PS_SCORE number #READONLY Statistic ID for player's total mission score. +---@field PS_SHIP number #READONLY Statistic ID for counting player's naval vessel kills. +---@field RESUME_MANUAL number #READONLY Constant for manual mission resumption control. +---@field RESUME_ON_LOAD number #READONLY Constant for automatic mission resumption on load. +---@field RESUME_WITH_CLIENTS number #READONLY Constant for mission resumption when clients connect. +net = net or {} +--- Sends a chat message to players in the multiplayer session. +---@version 2.5.0 +---@param message string Text content of the message. +---@param all boolean When `true`, sends to all players; when `false`, sends to current coalition only. +function net.send_chat(message, all) end + +--- Sends a targeted chat message to a specific player, optionally appearing from another player. +---@version 2.5.0 +---@param message string Text content of the message. +---@param playerId number Target player's unique identifier. +---@param fromId? number Source player's ID that the message will appear to come from. +function net.send_chat_to(message, playerId, fromId) end + +--- Returns a numerically indexed table of player IDs currently connected to the server. +---@version 2.5.0 +---@return table +function net.get_player_list() end + +--- Returns the current player's unique identifier (always 1 for server scripts). +---@version 2.5.0 +---@return number +function net.get_my_player_id() end + +--- Returns the server's player ID, which is always 1. +---@version 2.5.0 +---@return number +function net.get_server_id() end + +--- Returns information about a player, either as a complete table or a specific attribute value. +---@version 2.5.0 +---@param playerId number Target player's unique identifier. +---@param attribute? string Specific attribute name to return (e.g., `'name'`, `'ucid'`, `'ping'`). +---@return table +function net.get_player_info(playerId, attribute) end + +--- Removes a player from the server with an optional displayed message. +---@version 2.5.0 +---@param playerId number Target player's unique identifier. +---@param message string Explanation message displayed to the kicked player. +---@return boolean +function net.kick(playerId, message) end + +--- Returns a specific statistical value for a player (e.g., kills, deaths). +---@version 2.5.0 +---@param playerId number Target player's unique identifier. +---@param statID number Statistic identifier (one of the `net.PS_*` constants). +---@return number +function net.get_stat(playerId, statID) end + +--- Returns a player's display name (equivalent to `net.get_player_info(playerID, 'name')`). +---@version 2.5.0 +---@param playerId number Target player's unique identifier. +---@return string +function net.get_name(playerId) end + +--- Returns a player's coalition ID and slot ID as two separate values. +---@version 2.5.0 +---@param playerId number Target player's unique identifier. +---@return number +function net.get_slot(playerId) end + +--- Moves a player to a specified coalition and aircraft/vehicle slot. +---@version 2.5.0 +---@param playerID number Target player's unique identifier. +---@param sideId number Coalition ID (0 for spectators, 1 for Red, 2 for Blue). +---@param slotId string Unit ID or `UnitID_Seat` for multicrew positions. +---@return boolean +function net.force_player_slot(playerID, sideId, slotId) end + +--- Serializes a Lua value into a JSON string. +---@version 2.5.0 +---@param lua_value any Lua value to convert to JSON. +---@return string +function net.lua2json(lua_value) end + +--- Parses a JSON string into equivalent Lua data. +---@version 2.5.0 +---@param json_string string JSON string to convert to Lua. +---@return any +function net.json2lua(json_string) end + +--- Executes a Lua code string in a specific DCS World environment context. +---@version 2.5.0 +---@param state string Target environment (`'config'`, `'mission'`, or `'export'`). +---@param dostring string Lua code to execute. +---@return string +function net.dostring_in(state, dostring) end + +--- Writes a message to the DCS server log file. +---@version 2.5.0 +---@param message string Text to write to the log. +function net.log(message) end + +--- Likely retrieves the server host information (undocumented in DCS API). +---@param unknown? any Unknown parameter(s). +---@return any +function net.get_server_host(unknown) end + +--- Likely checks if an IP address is a loopback address (undocumented in DCS API). +---@param unknown? any Unknown parameter(s). +---@return any +function net.is_loopback_address(unknown) end + +--- Likely checks if an IP address is in a private range (undocumented in DCS API). +---@param unknown? any Unknown parameter(s). +---@return any +function net.is_private_address(unknown) end + +--- Likely provides chat message reception functionality (undocumented in DCS API). +---@param unknown? any Unknown parameter(s). +---@return any +function net.recv_chat(unknown) end + +--- Likely sets a player's name (undocumented in DCS API). +---@param unknown? any Unknown parameter(s). +---@return any +function net.set_name(unknown) end + +--- Likely changes a player's slot (undocumented in DCS API). +---@param unknown? any Unknown parameter(s). +---@return any +function net.set_slot(unknown) end + +--- Likely outputs debug trace information (undocumented in DCS API). +---@param unknown? any Unknown parameter(s). +---@return any +function net.trace(unknown) end + + +--- Provides functions for measuring mission time and scheduling deferred function execution in the DCS World environment. +---@class timer +timer = timer or {} +--- Returns the elapsed mission time in seconds since mission start, which pauses when the game is paused. +---@version 1.2.0 +---@return number +--- ### Examples +--- ```lua +--- if timer.getTime() > 20 then +--- doWhatever() +--- end +--- +--- ``` +function timer.getTime() end + +--- Returns `true` if the simulation is currently paused, otherwise returns `false`. +---@version 2.5.0 +---@return boolean +function timer.getPause() end + +--- Returns the game world time in seconds, based on the mission start time and continuously increasing regardless of pause state. +---@version 1.2.0 +---@return number +--- ### Examples +--- ```lua +--- if timer.getAbsTime() + 20 > env.mission.start_time then +--- doWhatever() +--- end +--- +--- ``` +function timer.getAbsTime() end + +--- Returns the mission start time in seconds, for calculating total elapsed mission time when combined with `getAbsTime()`. +---@version 1.2.0 +---@return number +--- ### Examples +--- ```lua +--- if timer.getAbsTime() - timer.getTime0() > 20 then +--- doWhatever() +--- end +--- +--- ``` +function timer.getTime0() end + +--- Schedules a function to execute at a specific mission time, with optional repetition if the function returns a future time value. +---@version 1.2.0 +---@param functionToCall fun(...) Function to be executed at the scheduled time. +---@param anyFunctionArguement any Parameters to pass to the scheduled function. +---@param modelTime number Mission time in seconds when the function should execute. +---@return number +--- ### Examples +--- --- The following will run a function named "main" 120 seconds from one the code would run. +--- ```lua +--- timer.scheduleFunction(main, {}, timer.getTime() + 120) +--- +--- ``` +--- --- The following example sets up a repetitive call loop where function CheckStatus is called every 5 seconds or. +--- ```lua +--- function CheckStatus(ourArgument, time) +--- -- Do things to check, use ourArgument (which is the scheduleFunction's second argument) +--- if ourArgument == 53 and someExternalCondition then +--- -- Keep going +--- return time + 5 +--- else +--- -- That's it we're done looping +--- return nil +--- end +--- end +--- timer.scheduleFunction(CheckStatus, 53, timer.getTime() + 5) +--- +--- ``` +--- --- This function will check if any red coalition units are in a trigger zone named "anyReds" and will set the flag "zoneOccupied" to true. This function will schedule itself to run every 60 seconds. +--- ```lua +--- local function checkZone(zoneName) +--- timer.scheduleFunction(checkZone, zoneName, timer.getTime() + 60) +--- local zone = trigger.misc.getZone(zoneName) +--- local groups = coalition.getGroups(1) +--- local count = 0 +--- for i = 1, #groups do +--- local units = groups[i]:getUnits() +--- for j = 1, #units do +--- local unitPos = units[j]:getpoint() +--- if math.sqrt((zone.point.x - unitPos.x)^2 + (zone.point.z - unitPos.z)^2) < zone.radius then +--- count = count + 1 +--- end +--- end +--- end +--- if count > 0 then +--- trigger.action.setUserFlag("zoneOccupied", true) +--- else +--- trigger.action.setUserFlag("zoneOccupied", false) +--- end +--- end +--- checkZone("anyReds") +--- +--- ``` +function timer.scheduleFunction(functionToCall, anyFunctionArguement, modelTime) end + +--- Cancels a previously scheduled function, preventing it from executing. +---@version 1.2.0 +---@param functionId number Identifier returned by `scheduleFunction()` for the function to cancel. +--- ### Examples +--- --- The following will run a function named "main" 120 seconds from one the code would run. +--- ```lua +--- local id = timer.scheduleFunction(main, {}, timer.getTime() + 120) +--- +--- ``` +--- --- If further down in the code it was decided to stop main() from running it may look like this. +--- ```lua +--- if abort == true then +--- timer.removeFunction(id) +--- end +--- +--- ``` +function timer.removeFunction(functionId) end + +--- Modifies the execution time of a previously scheduled function. +---@version 1.2.0 +---@param functionId number Identifier returned by `scheduleFunction()` for the function to reschedule. +---@param modelTime number New mission time in seconds when the function should execute. +--- ### Examples +--- --- The following will run a function named "main" 120 seconds from one the code would run. +--- ```lua +--- local id = timer.scheduleFunction(main, {}, timer.getTime() + 120) +--- +--- ``` +--- --- If further down in the code it was decided to run the main() function sooner it could look like this +--- ```lua +--- if mustGoFaster == true then +--- timer.setFunctionTime(id, timer.getTime() + 1) +--- end +--- +--- ``` +function timer.setFunctionTime(functionId, modelTime) end + + +--- Provides functions for mission triggers, flags, messaging, special effects, and F10 map interface in the DCS World environment. +---@version 1.2.0 +---@class trigger +---@field action table Contains functions that perform mission actions equivalent to Mission Editor trigger actions. +---@field misc table Contains utility functions for trigger operations and flag management. +trigger = trigger or {} + +--- Provides functions for querying atmospheric conditions in the DCS World environment, including wind, temperature, and pressure data. +---@class atmosphere +atmosphere = atmosphere or {} +--- Returns a `Vec3` representing the wind velocity vector at the specified position in the DCS World coordinate system. +---@version 1.2.6 +---@param vec3 Vec3 +---@return Vec3 +function atmosphere.getWind(vec3) end + +--- Returns a `Vec3` representing the wind velocity vector with turbulence effects at the specified position in the DCS World coordinate system. +---@version 1.2.6 +---@param vec3 Vec3 +---@return Vec3 +function atmosphere.getWindWithTurbulence(vec3) end + +--- Returns two `number` values representing the temperature (in Kelvins) and pressure (in Pascals) at the specified position in the DCS World coordinate system. +---@version 2.0.6 +---@param vec3 Vec3 +---@return number +function atmosphere.getTemperatureAndPressure(vec3) end + + +--- Provides functions and constants for controlling artificial intelligence behavior in the DCS World environment. +---@class AI +---@field Option table Provides a hierarchical structure of options for configuring AI unit and group behavior, including engagement rules, formation patterns, and reaction settings. +---@field Task table Provides constants and table structures for creating and assigning mission tasks to AI-controlled units and groups. +---@field Skill AI.Skill Provides enumerator values for setting AI proficiency levels, affecting decision-making, accuracy, and tactical behavior. +AI = AI or {} + +--- Provides functions for converting between different coordinate systems in the DCS World environment, including Latitude/Longitude, Local (XYZ), and Military Grid Reference System (MGRS). +---@class coord +coord = coord or {} +--- Converts geographical coordinates (Latitude/Longitude) to DCS World local coordinates, returning a Vec2 position vector. +---@version 1.2.0 +---@param lat LatLon|number Latitude value in decimal degrees, or a LatLon structure. If a LatLon is provided, the lon parameter is ignored. +---@param lon? number Longitude value in decimal degrees. Ignored if lat parameter is a LatLon structure. +---@return Vec2 +function coord.LLtoLO(lat, lon) end + +--- Converts DCS World local coordinates to geographical coordinates, returning a LatLon structure. +---@version 1.2.0 +---@param x number X coordinate in the DCS World coordinate system. +---@param y number Y coordinate (not Z) in the DCS World coordinate system. +---@return LatLon +function coord.LOtoLL(x, y) end + +--- Converts geographical coordinates (Latitude/Longitude) to Military Grid Reference System (MGRS) coordinates. +---@version 1.2.0 +---@param lat LatLon|number Latitude value in decimal degrees, or a LatLon structure. If a LatLon is provided, the lon parameter is ignored. +---@param lon number Longitude value in decimal degrees. Ignored if lat parameter is a LatLon structure. +---@return MGRS +function coord.LLtoMGRS(lat, lon) end + +--- Converts Military Grid Reference System (MGRS) coordinates to geographical coordinates, returning a LatLon structure. +---@version 1.2.0 +---@param mgrs MGRS MGRS coordinate object with UTMZone, MGRSDigraph, Easting, and Northing fields. +---@return LatLon +function coord.MGRStoLL(mgrs) end + + +--- Provides functions for creating and managing voice chat rooms in multiplayer missions. +---@class VoiceChat +---@field RadioHandlers VoiceChat.RadioHandlers Enumerator for radio functionality constants used in voice communications. +---@field RadioHandlersSingletons VoiceChat.RadioHandlersSingletons Enumerator for intercom functionality constants used in voice communications. +---@field Side VoiceChat.Side Enumerator for coalition sides (NEUTRAL, RED, BLUE, ALL) in voice chat rooms. +---@field RoomType VoiceChat.RoomType Enumerator for voice chat room types (PERSISTENT, MULTICREW, MANAGEABLE). +VoiceChat = VoiceChat or {} +--- Creates a voice chat room for players in a multiplayer mission. +---@version 2.5.6 +---@param roomName string +---@param side VoiceChat.Side +---@param roomType VoiceChat.RoomType +--- ### Examples +--- ```lua +--- VoiceChat.CreateRoom("SRSIsBetter", 2, 0) +--- ``` +function VoiceChat.createRoom(roomName, side, roomType) end + +--- Adds a user to a voice chat room. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.addUser(unknown) end + +--- Changes a user's slot in a voice chat room. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.changeSlot(unknown) end + +--- Modifies voice chat options for a room or user. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.changeVoiceChatOption(unknown) end + +--- Removes a voice chat room from the mission. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.deleteRoom(unknown) end + +--- Returns a list of peers with access to a voice chat room. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getAccessPeersList(unknown) end + +--- Returns the currently active voice chat room for a user. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getActiveRoom(unknown) end + +--- Returns the radio currently being used for transmission. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getCurrentTransmittingRadio(unknown) end + +--- Returns `true` if voice encryption is enabled, `false` otherwise. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getEncryptionEnabled(unknown) end + +--- Returns data about the intercom system. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getIntercomData(unknown) end + +--- Returns information about the intercom's status indicators. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getIntercomIndication(unknown) end + +--- Returns the current microphone mode for the intercom. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getIntercomMicMode(unknown) end + +--- Returns the current volume level for the intercom. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getIntercomVolume(unknown) end + +--- Returns `true` if voice chat is controlled by an external system, `false` otherwise. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getIsExternallyControlled(unknown) end + +--- Returns information about the most recently used transmitting radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getLastTransmittingRadio(unknown) end + +--- Returns the current microphone activation mode. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getMicMode(unknown) end + +--- Returns current voice chat system options. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getOptions(unknown) end + +--- Returns audio state information for a specified peer. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getPeerAudioState(unknown) end + +--- Returns a list of all peers in voice chat. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getPeersList(unknown) end + +--- Returns the currently selected radio channel. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getRadioChannel(unknown) end + +--- Returns the encryption key for a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getRadioEncryptionKey(unknown) end + +--- Returns the frequency of a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getRadioFrequency(unknown) end + +--- Returns radio frequencies for specified units. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getRadioFrequencyByUnits(unknown) end + +--- Returns the frequency of the guard receiver on a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getRadioGuardReceiverFrequency(unknown) end + +--- Returns the modulation type of the guard receiver on a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getRadioGuardReceiverModulation(unknown) end + +--- Returns the on/off state of the guard receiver on a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getRadioGuardReceiverOnOff(unknown) end + +--- Returns status indicator information for a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getRadioIndication(unknown) end + +--- Returns a list of available radios for the current unit. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getRadioList(unknown) end + +--- Returns the modulation type for a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getRadioModulation(unknown) end + +--- Returns the on/off state of a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getRadioOnOff(unknown) end + +--- Returns the power setting of a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getRadioPower(unknown) end + +--- Returns the squelch on/off state for a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getRadioSquelchOnOff(unknown) end + +--- Returns the volume level for a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getRadioVolume(unknown) end + +--- Returns a list of all voice chat rooms in the mission. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getRooms(unknown) end + +--- Returns the coalition side of a voice chat room or user. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getSide(unknown) end + +--- Returns the current voice chat operating mode. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.getVoiceChatMode(unknown) end + +--- Returns `true` if a player is in a unit with voice chat capabilities, `false` otherwise. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.isInUnit(unknown) end + +--- Returns `true` if an intercom system is available, `false` otherwise. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.isIntercom(unknown) end + +--- Returns `true` if a specified radio can transmit, `false` otherwise. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.isRadioAvailableForTransmission(unknown) end + +--- Removes a user from a voice chat room. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.leaveRoom(unknown) end + +--- Handles peer connection events in voice chat. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.onPeerConnect(unknown) end + +--- Handles peer disconnection events in voice chat. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.onPeerDisconnect(unknown) end + +--- Handles state processing events in voice chat. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.onProcessState(unknown) end + +--- Handles RTC connection change events. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.onRtcConnectionChange(unknown) end + +--- Handles RTC failure events. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.onRtcFailure(unknown) end + +--- Handles RTC signaling change events. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.onRtcSignalingChange(unknown) end + +--- Enables or disables voice for a specific peer. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.peerVoiceEnable(unknown) end + +--- Toggles encryption for a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.radioEncryptionOnOff(unknown) end + +--- Attempts to reconnect to the voice chat system. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.reconnect(unknown) end + +--- Removes a user from the voice chat system. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.removeUser(unknown) end + +--- Sets the active voice chat room for a user. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setActiveRoom(unknown) end + +--- Enables or disables voice encryption. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setEncryptionEnabled(unknown) end + +--- Sets the microphone mode for the intercom system. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setIntercomMicMode(unknown) end + +--- Enables or disables external control of the voice chat system. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setIsExternallyControlled(unknown) end + +--- Sets the microphone activation mode. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setMicMode(unknown) end + +--- Configures voice chat system options. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setOptions(unknown) end + +--- Sets the active channel on a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setRadioChannel(unknown) end + +--- Sets the encryption key for a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setRadioEncryptionKey(unknown) end + +--- Sets the frequency for a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setRadioFrequency(unknown) end + +--- Sets the guard receiver frequency for a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setRadioGuardReceiverFrequency(unknown) end + +--- Sets the guard receiver modulation type for a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setRadioGuardReceiverModulation(unknown) end + +--- Enables or disables the guard receiver for a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setRadioGuardReceiverOnOff(unknown) end + +--- Sets the modulation type for a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setRadioModulation(unknown) end + +--- Turns a specified radio on or off. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setRadioOnOff(unknown) end + +--- Sets the power level for a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setRadioPower(unknown) end + +--- Enables or disables squelch for a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setRadioSquelchOnOff(unknown) end + +--- Sets the volume level for a specified radio. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setRadioVolume(unknown) end + +--- Sets the volume level for audio tracks in voice chat. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setTrackAudioVolume(unknown) end + +--- Sets the operating mode for voice chat. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.setVoiceChatMode(unknown) end + +--- Starts or stops a test of sound filter options. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.startStopTestSoundFilterOptions(unknown) end + +--- Starts a voice stream for a specified user or room. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.startStream(unknown) end + +--- Starts the audio level test meter. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.startTestPeekMeter(unknown) end + +--- Stops a voice stream for a specified user or room. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.stopStream(unknown) end + +--- Stops the audio level test meter. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.stopTestPeekMeter(unknown) end + +--- Tests the peak meter functionality. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.testCallPeekMeterFunc(unknown) end + +--- Updates the volume levels for crew communications. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.updateCrewVolume(unknown) end + +--- Updates the parameters for sound filtering in voice chat. +---@param unknown? any Unknown parameter(s) +---@return any +function VoiceChat.updateSoundFilterParameters(unknown) end + + +--- Provides functions for manipulating positions, terrain queries, and random number generation within the DCS World. **Warning:** This class is not formally documented by ED and descriptions and params may be incorrect. +---@class Disposition +Disposition = Disposition or {} +--- Unknown use case, possibly related to route drift or movement. +---@version 0.0.0 +---@param pos1 Vec3 First position vector. +---@param pos2 Vec3 Second position vector. +---@param coalitionId coalition.side Coalition side enumeration value. +---@return table +function Disposition.DriftRoute(pos1, pos2, coalitionId) end + +--- Returns zones around runway strips in an elliptical pattern. +---@version 0.0.0 +---@param numAreas number Unknown +---@param numPositions number Unknown +---@param perim table Unknown +---@param degrees number Unknown +---@param radiusRatio number Unknown +---@return table +function Disposition.getElipsSideZones(numAreas, numPositions, perim, degrees, radiusRatio) end + +--- Returns the terrain height at the specified position in the DCS World coordinate system? See: land.getHeight() +---@version 0.0.0 +---@param pos Vec3 Position vector in the DCS World coordinate system. +---@return number +function Disposition.getPointHeight(pos) end + +--- Checks if water exists at the specified position within given parameters? See: land.getSurfaceType() +---@version 0.0.0 +---@param pos Vec3 3D Position +---@param a number Unknown +---@param b number Unknown +---@return boolean +function Disposition.getPointWater(pos, a, b) end + +--- Generates a random number within the specified range. +---@version 0.0.0 +---@param isFloat boolean If true, returns a floating-point number; otherwise, returns an integer. +---@param min number Minimum value of the random range. +---@param max number Maximum value of the random range. +---@return number +function Disposition.getRandom(isFloat, min, max) end + +--- Unknown function, likely related to random selection within a specified range or container. +---@version 0.0.0 +---@return any +function Disposition.getRandomIn() end + +--- Randomly shuffles the elements of the input table. +---@version 0.0.0 +---@param t table Table to be randomly shuffled. +---@return table +function Disposition.getRandomSort(t) end + +--- Unknown function +---@version 0.0.0 +---@param thresholdPos Vec3 Unknown +---@param pos Vec3 Unknown +---@param a number Unknown +---@param b number Unknown +---@return boolean +function Disposition.getRouteAwayWater(thresholdPos, pos, a, b) end + +--- Returns the perimeter of a runway defined by runway data? See: airbase.getRunways() +---@version 0.0.0 +---@param runway table Runway data table obtained from airbase:getRunways(). +---@return table +function Disposition.getRunwayPerimetr(runway) end + +--- Finds clear positions within an area for placing units. Assumed behavior. +---@version 0.0.0 +---@param pos Vec3 Center position vector for the search area. +---@param radius number Radius of the search area. +---@param posRadius number Required clear radius around each position. +---@param numPositions number Number of positions to find. +---@return table +function Disposition.getSimpleZones(pos, radius, posRadius, numPositions) end + +--- Returns zones along runway edges based on the provided perimeter. +---@version 0.0.0 +---@param numPositions number Number of positions to generate. +---@param perim table Perimeter table defining the runway edges. +---@return table +function Disposition.getThresholdFourZones(numPositions, perim) end + +--- Unknown function, likely related to setting a marker at a specific point. May be related to world and trigger functions. +---@version 0.0.0 +---@return any +function Disposition.setMarkerPoint() end + + +--- Provides functions for creating and managing interactive menu commands in the F10 radio menu. +---@class missionCommands +missionCommands = missionCommands or {} +--- Adds an interactive command to the F10 radio menu for all players that executes a specified Lua function when selected. +---@version 1.2.4 +---@param name string Display text for the menu command. +---@param path? nil|table Path to parent menu as a sequence table of menu names. If `nil`, command is added to root menu. +---@param functionToRun fun(...) Function to execute when command is selected. +---@param anyArguement? any Optional value passed to `functionToRun` when executed. +---@return table +function missionCommands.addCommand(name, path, functionToRun, anyArguement) end + +--- Creates a submenu in the F10 radio menu for all players, which can contain additional commands or nested submenus. +---@version 1.2.4 +---@param name string Display text for the submenu. +---@param path? table Path to parent menu as a sequence table of menu names. If `nil`, submenu is added to root menu. +---@return table +function missionCommands.addSubMenu(name, path) end + +--- Removes a menu item or entire submenu from the F10 radio menu for all players. +---@version 1.2.4 +---@param path? nil|table Path to the menu item as a sequence table of menu names. If `nil`, removes all items. +function missionCommands.removeItem(path) end + +--- Adds an interactive command to the F10 radio menu for players of a specific coalition. +---@version 1.2.4 +---@param coalition_side coalition.side Target coalition (`coalition.side.NEUTRAL`, `coalition.side.RED`, or `coalition.side.BLUE`). +---@param name string Display text for the menu command. +---@param path? nil|table Path to parent menu as a sequence table of menu names. If `nil`, command is added to root menu. +---@param functionToRun fun(...) Function to execute when command is selected. +---@param anyArguement? any Optional value passed to `functionToRun` when executed. +---@return table +function missionCommands.addCommandForCoalition(coalition_side, name, path, functionToRun, anyArguement) end + +--- Creates a submenu in the F10 radio menu for players of a specific coalition. +---@version 1.2.4 +---@param coalitionSide number Target coalition as a `coalition.side` enum value. +---@param name string Display text for the submenu. +---@param path? table Path to parent menu as a sequence table of menu names. If `nil`, submenu is added to root menu. +---@return table +function missionCommands.addSubMenuForCoalition(coalitionSide, name, path) end + +--- Removes a menu item or entire submenu from the F10 radio menu for a specific coalition. +---@version 1.2.4 +---@param coalitionSide number Target coalition as a `coalition.side` enum value. +---@param path? nil|table Path to the menu item as a sequence table of menu names. If `nil`, removes all items for the coalition. +function missionCommands.removeItemForCoalition(coalitionSide, path) end + +--- Adds an interactive command to the F10 radio menu for players in a specific group. +---@version 1.2.4 +---@param groupId number ID of the target group. +---@param name string Display text for the menu command. +---@param path? nil|table Path to parent menu as a sequence table of menu names. If `nil`, command is added to root menu. +---@param functionToRun fun(...) Function to execute when command is selected. +---@param anyArguement? any Optional value passed to `functionToRun` when executed. +---@return table +function missionCommands.addCommandForGroup(groupId, name, path, functionToRun, anyArguement) end + +--- Creates a submenu in the F10 radio menu for players in a specific group. +---@version 1.2.4 +---@param groupId number ID of the target group. +---@param name string Display text for the submenu. +---@param path? table Path to parent menu as a sequence table of menu names. If `nil`, submenu is added to root menu. +---@return table +function missionCommands.addSubMenuForGroup(groupId, name, path) end + +--- Removes a menu item or entire submenu from the F10 radio menu for a specific group. +---@version 1.2.4 +---@param groupId number ID of the target group. +---@param path? nil|table Path to the menu item as a sequence table of menu names. If `nil`, removes all items for the group. +function missionCommands.removeItemForGroup(groupId, path) end + +--- Executes a radio menu action programmatically without user interaction. +---@version 2.5.0 +---@param path table Path to the menu command as a sequence table of menu names. +function missionCommands.doAction(path) end + + +--- Provides functions and constants for controlling core DCS World simulation behavior and accessing mission data. Note: Only available in server environment. +---@class dcs +---@field UNIT_NAME string #READONLY Constant string identifier for accessing a unit's name property. +---@field UNIT_TYPE string #READONLY Constant string identifier for accessing a unit's type property. +---@field UNIT_HEADING string #READONLY Constant string identifier for accessing a unit's heading/direction property (in radians). +---@field UNIT_CATEGORY string #READONLY Constant string identifier for accessing a unit's category property. +---@field UNIT_GROUPNAME string #READONLY Constant string identifier for accessing a unit's group name property. +---@field UNIT_GROUPID string #READONLY Constant string identifier for accessing a unit's group ID property. +---@field UNIT_CALLSIGN string #READONLY Constant string identifier for accessing a unit's callsign property. +---@field UNIT_HIDDEN string #READONLY Constant string identifier for accessing a unit's visibility status in the Mission Editor. +---@field UNIT_COALITION string #READONLY Constant string identifier for accessing a unit's coalition affiliation (e.g., 'blue', 'red', 'unknown'). +---@field UNIT_COUNTRY_ID string #READONLY Constant string identifier for accessing a unit's country ID property. +---@field UNIT_TASK string #READONLY Constant string identifier for accessing a unit's group task property. +---@field UNIT_PLAYER_NAME string #READONLY Constant string identifier for accessing the player name controlling a unit, applicable only to player-controllable units. +---@field UNIT_ROLE string #READONLY Constant string identifier for accessing a unit's role property (e.g., 'artillery_commander', 'instructor'). +---@field UNIT_INVISIBLE_MAP_ICON string #READONLY Constant string identifier for accessing a unit's map icon visibility status in the Mission Editor. +dcs = dcs or {} +--- Returns a table representing the current mission data structure as stored in the mission file. +---@version 2.5.0 +---@return table +function dcs.getCurrentMission() end + +--- Pauses or resumes the simulation, affecting all connected clients in multiplayer. +---@version 2.5.0 +---@param action boolean `true` to pause the simulation, `false` to resume it. +function dcs.setPause(action) end + +--- Returns a `boolean` indicating whether the simulation is currently paused. +---@version 2.5.0 +---@return boolean +function dcs.getPause() end + +--- Terminates the current mission and returns to the mission selection screen. +---@version 2.5.0 +function dcs.stopMission() end + +--- Closes the DCS World application completely, terminating all processes. +---@version 2.5.0 +function dcs.exitProcess() end + +--- Returns `true` if the current simulation is in multiplayer mode, `false` if in single-player mode. +---@version 2.5.0 +---@return boolean +function dcs.isMultiplayer() end + +--- Returns `true` if the current instance is running as a dedicated server or single player host, `false` otherwise. +---@version 2.5.0 +---@return boolean +function dcs.isServer() end + +--- Returns a `number` representing the total simulation time in seconds since the DCS application was launched. +---@version 2.5.0 +---@return number +function dcs.getModelTime() end + +--- Returns a `number` representing the elapsed time in seconds since the current mission started. +---@version 2.5.0 +---@return number +function dcs.getRealTime() end + +--- Returns a table containing the mission options as stored in the options.lua file within the mission package. +---@version 2.5.0 +---@return table +function dcs.getMissionOptions() end + +--- Returns a map-like table where keys are coalition IDs and values are tables containing information about coalitions with available player slots. +---@version 2.5.0 +---@return table +function dcs.getAvailableCoalitions() end + +--- Returns a numerically indexed table of tables containing information about player slots available in the specified coalition. +---@version 2.5.0 +---@param coaId number|string Coalition identifier (numeric ID or string name). +---@return DCSAvailableSlotInfo[] +function dcs.getAvailableSlots(coaId) end + +--- Returns a table containing information about all player-controllable slots available in the current mission. +---@return DCSAvailableSlotInfo +function dcs.getAvailableSlotsAll() end + + +--- Represents a physical entity in the DCS World with position, orientation, and identity properties. +---@class Object +Object = Object or {} +--- Returns `true` if this object currently exists in the mission environment. +---@return boolean +function Object:isExist() end + +--- Removes this object from the mission without generating events, causing it to immediately disappear. +---@return fun(...) +function Object:destroy() end + +--- Returns two `Object.Category` values indicating this object's primary and secondary classification. +---@return Object.Category +function Object:getCategory() end + +--- Returns the type name of this object as defined in the DCS database. +---@return string +function Object:getTypeName() end + +--- Returns `true` if this object possesses the specified attribute as defined in the DCS database. +---@param attribute Attributes|string Attribute name to check for. Can be any valid attribute from the Attributes enum. +---@return boolean +function Object:hasAttribute(attribute) end + +--- Returns this object's name identifier, which may be either its mission editor name or runtime ID depending on context. +---@return string +function Object:getName() end + +--- Returns a `Vec3` representing this object's position in the DCS World coordinate system. +---@return Vec3 +function Object:getPoint() end + +--- Returns a `Position3` containing both this object's location and orientation vectors in the DCS World coordinate system. +---@return Position3 +function Object:getPosition() end + +--- Returns a `Vec3` representing this object's velocity components in the DCS World coordinate system. +---@return Vec3 +function Object:getVelocity() end + +--- Returns `true` if this object is currently airborne rather than on the ground. +---@return boolean +function Object:inAir() end + +--- Returns a table containing all attributes this object possesses as defined in the DCS database. +---@version 1.2.0 +---@return ObjectAttributes +function Object:getAttributes() end + +--- Aborts the current cargo selection operation for this object. +---@version 2.5.0 +function Object:cancelChoosingCargo() end + + +--- Provides functions for querying terrain geometry in the mission environment. +---@class land +land = land or {} +--- Returns terrain height (distance from sea level) at the specified point. +---@version 1.2.0 +---@param point Vec2 Position coordinates to check. +---@return number +function land.getHeight(point) end + +--- Returns both terrain height and seabed depth at the specified point as `{number, number}` where the first value is height above sea level and the second is seabed depth. +---@version 1.2.0 +---@param point Vec2 Position coordinates to check. +---@return table +function land.getSurfaceHeightWithSeabed(point) end + +--- Returns the surface type enum at the specified point. +---@version 1.2.0 +---@param point Vec2 Position coordinates to check. +---@return land.SurfaceType +function land.getSurfaceType(point) end + +--- Performs line-of-sight check between two points, returning whether terrain obstructs visibility. +--- +--- Note: This only tests terrain collision - buildings and other objects are not considered. +--- When working with ground objects, offset the Y-value (height) to prevent false negatives from the origin point clipping into terrain. +---@version 1.2.0 +---@param origin Vec3 Starting point of the line-of-sight check. +---@param destination Vec3 Ending point of the line-of-sight check. +---@return boolean +function land.isVisible(origin, destination) end + +--- Returns the intersection point where a ray originating from `origin` in the given direction intersects terrain, or `nil` if no intersection within specified distance. +---@version 1.2.0 +---@param origin Vec3 Starting point of the ray. +---@param direction Vec3 Normalized direction vector of the ray. +---@param distance number Maximum distance to check for intersection. +---@return Vec3|nil +function land.getIP(origin, direction, distance) end + +--- Returns a table of `Vec3` points representing the terrain profile between two points. +---@version 1.2.0 +---@param origin Vec3 Starting point of the profile. +---@param destination Vec3 Ending point of the profile. +---@return Vec3Array +function land.profile(origin, destination) end + +--- Returns X and Y coordinates of the nearest point on a road from the given position. +--- +--- Note: This function accepts individual X/Y coordinates rather than `Vec2` or `Vec3` objects. +---@version 2.5 +---@param roadType string Road type to search. Valid values: `'roads'`, `'railroads'`. +---@param xCoord number X-coordinate (map X) of reference point. +---@param yCoord number Y-coordinate (map Z) of reference point. +---@return Vec2 +function land.getClosestPointOnRoads(roadType, xCoord, yCoord) end + +--- Returns a route as a sequence of points along roads between start and destination. +--- +--- The result is a numerically-indexed table of `Vec2` points from start to destination. +--- +--- Note: When using railroad paths, the parameter value should be `'rails'` (not `'railroads'`). +---@version 2.5 +---@param roadType string Road type to use. Valid values: `'roads'`, `'rails'`. +---@param xCoord number X-coordinate (map X) of starting point. +---@param yCoord number Y-coordinate (map Z) of starting point. +---@param destX number X-coordinate (map X) of destination point. +---@param destY number Y-coordinate (map Z) of destination point. +---@return Vec2Array +function land.findPathOnRoads(roadType, xCoord, yCoord, destX, destY) end + + +--- Represents static environmental structures in the DCS World mission area, such as buildings, bridges, and terrain objects. +---@class SceneryObject : Object +SceneryObject = SceneryObject or {} +--- Returns the current health value of this scenery object, where 0 indicates destruction. +---@version 1.2.0 +---@return number +function SceneryObject:getLife() end + +--- Returns a table containing detailed properties of this scenery object based on its type. +---@version 1.2.0 +---@return SceneryObjectDesc +function SceneryObject:getDesc() end + +--- Returns a table containing detailed properties of a scenery object type without requiring an instance. +---@version 1.2.0 +---@param typeName string Type name of the scenery object to retrieve information about. +---@return SceneryObjectDesc +function SceneryObject.getDescByName(typeName) end + + +--- Represents a targeting designation beam (laser or infrared) used for marking targets in the DCS World environment. +---@class Spot +Spot = Spot or {} +--- Removes this spot from the mission, immediately terminating the targeting beam. +---@version 1.2.0 +function Spot:destroy() end + +--- Returns two category enumerator values identifying this spot's primary and secondary classifications. +---@version 1.2.0 +---@return Object.Category +---@return Spot.Category +function Spot:getCategory() end + +--- Returns a `Spot.Category` enumerator value indicating whether this spot is infrared or laser. +---@version 2.9.2 +---@return Spot.Category +function Spot:getCategoryEx() end + +--- Returns a `Vec3` representing the target point of this beam in the DCS World coordinate system. +---@version 1.2.0 +---@return Vec3 +function Spot:getPoint() end + +--- Changes the target endpoint of this beam to a new position in the DCS World coordinate system. +---@version 1.2.6 +---@param point Vec3 New target position in the DCS World coordinate system. +function Spot:setPoint(point) end + +--- Returns the 4-digit laser code number used for target designation with this beam. +---@version 1.2.6 +---@return number +function Spot:getCode() end + +--- Sets a new 4-digit laser code (1111-1788) for this beam, determining which guided weapons can track it. +---@version 1.2.6 +---@param code number New laser code value between 1111 and 1788. +function Spot:setCode(code) end + +--- Creates an infrared targeting beam from a source object to a specified point, visible only through night vision devices. +---@version 1.2.6 +---@param source Object Source object that will emit the infrared beam. +---@param localRef? Vec3|nil Optional local reference point on the source object. If `nil`, uses the object's center. +---@param point Vec3 Target point for the infrared beam in the DCS World coordinate system. +---@return Spot +function Spot.createInfraRed(source, localRef, point) end + +--- Creates a laser targeting beam from a source object to a specified point in the DCS World coordinate system. +---@version 1.2.6 +---@param source Object Source object that will emit the laser beam. +---@param localRef? Vec3|nil Optional local reference point on the source object. If `nil`, uses the object's center. +---@param point Vec3 Target point for the laser beam in the DCS World coordinate system. +---@param laserCode? number Optional 4-digit laser code between 1111 and 1788. If omitted, creates an infrared beam. +---@return Spot +function Spot.createLaser(source, localRef, point, laserCode) end + + +--- Represents non-moving structures and objects placed in the DCS World mission environment. +---@class StaticObject : Object, CoalitionObject +StaticObject = StaticObject or {} +--- Returns the current health value of this static object, where values below 1 indicate destruction. +---@version 1.2.0 +---@return number +function StaticObject:getLife() end + +--- Returns a table containing detailed properties of this static object based on its type. +---@version 1.2.0 +---@return StaticObjectDesc +function StaticObject:getDesc() end + +--- Returns a `number` representing the unique mission identifier of this static object. +---@version 1.2.0 +---@return number +function StaticObject:getID() end + +--- Returns a `coalition.side` enumerator value indicating which faction this static object belongs to. +---@version 1.2.0 +---@return coalition.side +function StaticObject:getCoalition() end + +--- Returns a `country.id` enumerator value identifying the nation this static object belongs to. +---@version 1.2.0 +---@return country.id +function StaticObject:getCountry() end + +--- Returns a `string` name of the force group this static object belongs to, or `nil` if not assigned. +---@version 1.2.0 +---@return nil|string +function StaticObject:getForcesName() end + +--- Returns the current animation parameter value for a specific argument on this static object's 3D model. +---@version 1.2.0 +---@param arg number Animation argument identifier to query. +---@return number +function StaticObject:getDrawArgumentValue(arg) end + +--- Initiates the cargo selection process for this static object. +---@version 2.5.0 +function StaticObject:chooseCargo() end + +--- Returns a `string` containing the human-readable name of the cargo attached to this static object. +---@version 2.5.0 +---@return string +function StaticObject:getCargoDisplayName() end + +--- Returns a `number` representing the weight of the cargo attached to this static object. +---@version 2.5.0 +---@return number +function StaticObject:getCargoWeight() end + +--- Returns a `StaticObject` with the specified name, or `nil` if no such object exists. +---@version 1.2.0 +---@param name string Unique identifier of the static object to retrieve. +---@return StaticObject|nil +function StaticObject.getByName(name) end + +--- Returns a table containing detailed properties of a static object type without requiring an instance. +---@version 1.2.0 +---@param typeName string Type name of the static object to retrieve information about. +---@return StaticObjectDesc +function StaticObject.getDescByName(typeName) end + + +--- Provides functions for managing faction-based entities in the DCS World environment, including unit information retrieval, group spawning, and static object creation. +---@class coalition +---@field side coalition.side Provides access to the coalition side enumerator for faction identification. +---@field service coalition.service Provides access to the service type enumerator for communications services. +coalition = coalition or {} +--- Adds a new group to the mission for the specified coalition and country, returning a `function` that can be called to complete the spawn process. +---@version 1.2.0 +---@param coalition coalition.side Coalition side enumeration value. +---@param country country.id Country identifier within the coalition. +---@param groupData GroupSpawnData Table defining the group configuration to be spawned. +---@return fun(...) +function coalition.addGroup(coalition, country, groupData) end + +--- Adds a dynamic (runtime-created) group to the mission for the specified coalition and country, returning a `function` that finalizes the spawn process. +---@version 2.8.0 +---@param coalition coalition.side Coalition side enumeration value. +---@param country country.id Country identifier within the coalition. +---@param groupData GroupSpawnData Table defining the dynamic group configuration to be spawned. +---@return fun(...) +function coalition.add_dyn_group(coalition, country, groupData) end + +--- Removes a previously added dynamic group from the mission, returning `true` if successful. +---@version 2.8.0 +---@param groupName string Name of the dynamic group to remove from the mission. +---@return boolean +function coalition.remove_dyn_group(groupName) end + +--- Adds a new static object to the mission for the specified country, returning a `StaticObject`. The coalition is automatically determined from the country identifier. +---@version 1.2.0 +---@param country country.id Country identifier that determines which coalition the static object will belong to. +---@param staticData StaticObjectSpawnData Table defining the static object configuration to be spawned. +---@return StaticObject +function coalition.addStaticObject(country, staticData) end + +--- Returns a numerically indexed table of `Group` objects belonging to the specified coalition and country. +---@version 1.2.0 +---@param coalition coalition.side Coalition side enumeration value. +---@param country country.id Country identifier within the coalition. +---@return table +function coalition.getGroups(coalition, country) end + +--- Returns a numerically indexed table of `StaticObject` objects belonging to the specified coalition and country. +---@version 1.2.0 +---@param coalition coalition.side Coalition side enumeration value. +---@param country country.id Country identifier within the coalition. +---@return table +function coalition.getStaticObjects(coalition, country) end + +--- Returns a numerically indexed table of `Airbase` objects belonging to the specified coalition and country. +---@version 1.2.0 +---@param coalition coalition.side Coalition side enumeration value. +---@param country country.id Country identifier within the coalition. +---@return table +function coalition.getAirbases(coalition, country) end + +--- Returns a numerically indexed table of `Unit` objects representing player-controlled units in the specified coalition. +---@version 1.2.0 +---@param coalition coalition.side Coalition side enumeration value. +---@return table +function coalition.getPlayers(coalition) end + +--- Returns a numerically indexed table of `Unit` objects providing the specified service type within the coalition. +---@version 1.2.0 +---@param coalition coalition.side Coalition side enumeration value. +---@param service coalition.service Service type enumeration value (e.g., ATC, AWACS, TANKER, FAC). +---@return table +function coalition.getServiceProviders(coalition, service) end + +--- Adds a reference point for the specified coalition, returning a `function` that finalizes the addition. +---@version 1.2.0 +---@param coalition coalition.side Coalition side enumeration value. +---@param refPoint RefPoint Table defining the reference point to be added. +---@return fun(...) +function coalition.addRefPoint(coalition, refPoint) end + +--- Returns a table of reference points defined for the specified coalition. +---@version 1.2.0 +---@param coalition coalition.side Coalition side enumeration value. +---@return table +function coalition.getRefPoints(coalition) end + +--- Returns a table representing the primary reference point for the specified coalition. +---@version 1.2.0 +---@param coalition coalition.side Coalition side enumeration value. +---@return table +function coalition.getMainRefPoint(coalition) end + +--- Returns a `number` representing the coalition identifier that the specified country belongs to. +---@version 1.2.0 +---@param country country.id Country identifier to query. +---@return number +function coalition.getCountryCoalition(country) end + +--- Returns `true` if cargo selection is possible for the specified unit within the coalition. +---@version 2.8.0 +---@param coalition coalition.side Coalition side enumeration value. +---@param unitId number Unique identifier of the unit to check. +---@return boolean +function coalition.checkChooseCargo(coalition, unitId) end + +--- Returns `true` if a descent operation (e.g., paratrooper drop) is possible for the specified unit. +---@version 2.8.0 +---@param coalition coalition.side Coalition side enumeration value. +---@param unitId number Unique identifier of the unit to check. +---@return boolean +function coalition.checkDescent(coalition, unitId) end + +--- Returns a table of all available descent operations (e.g., paratroopers, cargo) for the specified coalition. +---@version 2.8.0 +---@param coalition coalition.side Coalition side enumeration value. +---@return table +function coalition.getAllDescents(coalition) end + +--- Returns a table of all descent operations (e.g., paratroopers, cargo) currently loaded on the specified unit. +---@version 2.8.0 +---@param unitId number Unique identifier of the unit to query. +---@return table +function coalition.getDescentsOnBoard(unitId) end + + +--- Represents an AI behavior controller that manages tasking, commands, and detection capabilities for units and groups in the DCS World environment. +---@class Controller +Controller = Controller or {} +--- Assigns a task to this controller's associated units or groups, replacing any current task in the queue. +---@param task ControllerTask Table defining either a main task or enroute task to assign +function Controller:setTask(task) end + +--- Clears all tasks from this controller's task queue, causing controlled units to cease their current activity. +function Controller:resetTask() end + +--- Adds a task to the front of this controller's task queue, making it the highest priority task to execute. +---@param task ControllerTask Table defining either a main task or enroute task to prioritize +function Controller:pushTask(task) end + +--- Removes the highest priority task from this controller's task queue. +function Controller:popTask() end + +--- Returns `true` if this controller currently has at least one task in its queue. +---@return boolean +function Controller:hasTask() end + +--- Issues an immediate command to this controller that executes instantly and does not affect the current task queue. +---@param command ControllerCommand Table defining the command to execute +function Controller:setCommand(command) end + +--- Configures a behavior option for this controller that affects how it performs all tasks and commands. +---@param optionId AIOptionId Option identifier from AI.Option namespaces (Air, Ground, or Naval) +---@param optionValue FormationType|boolean|number|string Value to set for the specified option +function Controller:setOption(optionId, optionValue) end + +--- Enables or disables the AI behavior for this controller's ground or naval units. Note: Does not work with aircraft or helicopters. +---@param value boolean `true` to enable AI behavior, `false` to disable +function Controller:setOnOff(value) end + +--- Sets the altitude for this controller's aircraft group, with options to maintain across waypoints and specify altitude type. +---@param altitude number Target altitude in meters +---@param keep? boolean `true` to maintain altitude across waypoints, `false` to return to route-defined altitudes +---@param altType? string Altitude reference type: "BARO" (barometric) or "RADIO" (radar) +function Controller:setAltitude(altitude, keep, altType) end + +--- Sets the movement speed for this controller's group, with option to maintain across waypoints. +---@param speed number Target speed in meters per second +---@param keep? boolean `true` to maintain speed across waypoints, `false` to return to route-defined speeds +function Controller:setSpeed(speed, keep) end + +--- Forces a unit-level controller to have awareness of a specific target without natural detection. Note: Does not work at group level. +---@param object Object Target object to become aware of +---@param _type boolean `true` to know the target type, `false` otherwise +---@param distance boolean `true` to know the target distance, `false` otherwise +function Controller:knowTarget(object, _type, distance) end + +--- Returns multiple values indicating whether and how the specified target is detected by this controller's unit or group. +---@param Target Object Target object to check detection status +---@param detectionType1? Controller.Detection First detection method to check +---@param detectionType2? Controller.Detection Second detection method to check +---@param detectionType3? Controller.Detection Third detection method to check +---@return boolean +---@return number +---@return Vec3 +function Controller:isTargetDetected(Target, detectionType1, detectionType2, detectionType3) end + +--- Returns a numerically indexed table of tables containing detection information about targets detected by this controller, optionally filtered by detection methods. +---@param detectionType1? Controller.Detection First detection method to filter by +---@param detectionType2? Controller.Detection Second detection method to filter by +---@param detectionType3? Controller.Detection Third detection method to filter by +---@return ControllerDetectedTargetArray +function Controller:getDetectedTargets(detectionType1, detectionType2, detectionType3) end + + +--- Represents airport and carrier facilities in the DCS World environment. +---@class Airbase : Object, CoalitionObject +Airbase = Airbase or {} +--- Returns a `string` representing the localized callsign of this airbase. +---@return string +function Airbase:getCallsign() end + +--- Returns a `Unit` object, a `StaticObject`, or `nil` if no object exists at the specified index. +---@param UnitIndex number +---@return StaticObject|Unit|nil +function Airbase:getUnit(UnitIndex) end + +--- Returns a `number` representing the unique mission ID of this airbase. +---@return number +function Airbase:getID() end + +--- Returns an `Object.Category` enumerator value indicating the general category of this object. +---@version 1.2.0 +---@return Object.Category +function Airbase:getCategory() end + +--- Returns an `Airbase.Category` enumerator value indicating the specific type of this airbase. +---@return Airbase.Category +function Airbase:getCategoryEx() end + +--- Returns a numerically indexed table where each element is a table detailing an airbase parking spot, optionally filtered by availability. +---@param available? boolean +---@return AirbaseParking[] +function Airbase:getParking(available) end + +--- Returns a numerically indexed table where each element is a table detailing runway information, including dimensions, course, and name. +---@return AirbaseRunway[] +function Airbase:getRunways() end + +--- Returns a `Vec3` representing the position of the airbase's dispatcher tower in the DCS World coordinate system, or `nil` if not available. +---@return Vec3|nil +function Airbase:getDispatcherTowerPos() end + +--- Returns a `boolean` indicating whether the ATC for this airbase is in silent mode. +---@return boolean +function Airbase:getRadioSilentMode() end + +--- Sets the silent mode status for the airbase's ATC, determining if it responds to radio communications from aircraft. +---@param silent boolean +function Airbase:setRadioSilentMode(silent) end + +--- Enables or disables the auto-capture mechanic for this airbase, affecting how control can change between coalitions. +---@param setting boolean +function Airbase:autoCapture(setting) end + +--- Returns a `boolean` indicating whether the auto-capture feature is enabled for this airbase. +---@return boolean +function Airbase:autoCaptureIsOn() end + +--- Changes this airbase's coalition to the specified side, affecting which faction controls it. +---@param coa coalition.side +function Airbase:setCoalition(coa) end + +--- Returns a `Warehouse` object associated with this airbase, used to manage its inventory. +---@return Warehouse +function Airbase:getWarehouse() end + +--- Returns a `string` representing the name of this airbase. +---@version 1.2.0 +---@return string +function Airbase:getName() end + +--- Returns a `string` representing the type name of this airbase. +---@version 1.2.0 +---@return string +function Airbase:getTypeName() end + +--- Returns a `Vec3` representing the position of this airbase in the DCS World coordinate system. +---@version 1.2.0 +---@return Vec3 +function Airbase:getPoint() end + +--- Returns a `Position3` representing the precise position and orientation of this airbase in the DCS World coordinate system. +---@version 1.2.0 +---@return Position3 +function Airbase:getPosition() end + +--- Returns a `Vec3` representing the velocity of this airbase in the DCS World coordinate system. +---@version 1.2.0 +---@return Vec3 +function Airbase:getVelocity() end + +--- Returns a table containing a detailed description of this airbase, including its ID, callsign, category, and operational data. +---@version 1.2.0 +---@return AirbaseDesc +function Airbase:getDesc() end + +--- Returns a `coalition.side` enumerator value indicating which coalition controls this airbase. +---@version 1.2.0 +---@return coalition.side +function Airbase:getCoalition() end + +--- Returns an `Airbase` object corresponding to the airbase with the specified name, or `nil` if no such airbase exists. +---@param name string +---@return Airbase|nil +function Airbase.getByName(name) end + +--- Returns a communicator object for the specified airbase. +---@param airbase Airbase +---@return unknown +function Airbase.getCommunicator(airbase) end + +--- Returns a country ID for the specified airbase. +---@param airbase Airbase +---@return number +function Airbase.getCountry(airbase) end + +--- Returns a table containing detailed information about the airbase with the specified name. +---@param name string +---@return AirbaseDesc|nil +function Airbase.getDescByName(name) end + +--- Returns a string representing the forces name for the airbase. +---@param airbase Airbase +---@return string +function Airbase.getForcesName(airbase) end + +--- Returns the current life/health value of the specified airbase. +---@param airbase Airbase +---@return number +function Airbase.getLife(airbase) end + +--- Returns the nearest airbase object to the specified point. +---@param point Vec3 +---@return Airbase|nil +function Airbase.getNearest(point) end + +--- Returns the world ID of the specified airbase. +---@param airbase Airbase +---@return number +function Airbase.getWorldID(airbase) end + + +--- Represents a collection of related units that operate together as a tactical entity in the DCS World environment. +---@class Group : CoalitionObject +Group = Group or {} +--- Returns `true` if this group currently exists in the mission environment. +---@version 1.2.0 +---@return boolean +function Group:isExist() end + +--- Activates this group if it has delayed start or late activation settings, making it appear in the mission. +---@version 1.2.0 +---@return fun(...) +function Group:activate() end + +--- Removes this group and all its units from the game world without triggering events, causing them to completely disappear. +---@version 1.2.0 +---@return fun(...) +function Group:destroy() end + +--- Returns an enumerator value indicating both the generic object category and specific group type for this group. +---@version 1.2.0 +---@return Object.Category +---@return Group.Category +function Group:getCategory() end + +--- Returns a `Group.Category` enumerator value indicating the specific type of this group (e.g., AIRPLANE, HELICOPTER, GROUND). +---@version 2.9.2 +---@return Group.Category +function Group:getCategoryEx() end + +--- Returns a `coalition.side` enumerator value indicating which faction this group belongs to. +---@version 1.2.4 +---@return coalition.side +function Group:getCoalition() end + +--- Returns a `string` representing the unique name identifier of this group. +---@version 1.2.0 +---@return string +function Group:getName() end + +--- Returns a `number` representing the unique mission ID of this group. +---@version 1.2.0 +---@return number +function Group:getID() end + +--- Returns a `Unit` object at the specified index within this group, or `nil` if no unit exists at that index. +---@version 1.2.0 +---@param UnitIndex number Numeric index of the unit to retrieve, starting from 1. +---@return Unit|nil +function Group:getUnit(UnitIndex) end + +--- Returns a numerically indexed table of `Unit` objects belonging to this group, ordered by their position in the group. +---@version 1.2.0 +---@return Unit[] +function Group:getUnits() end + +--- Returns a `number` representing the current count of units in this group, which decreases as units are destroyed. +---@version 1.2.0 +---@return number +function Group:getSize() end + +--- Returns a `number` representing the original count of units in this group as defined at creation, which remains constant regardless of unit losses. +---@version 1.2.6 +---@return number +function Group:getInitialSize() end + +--- Returns a `Controller` object that can be used to manage AI behavior for this group. Note: Ship and ground groups can only be controlled at group level. +---@version 1.2.0 +---@return Controller +function Group:getController() end + +--- Sets the radar emission status for all applicable units in this group, allowing control of detection signatures without changing AI behavior. +---@version 2.7.0 +---@param setting boolean `true` to enable radar emissions, `false` to disable them. +function Group:enableEmission(setting) end + +--- Returns `true` if any unit in this group is currently in the process of loading cargo. +---@version 2.5.5 +---@return boolean +function Group:embarking() end + +--- Creates a map marker visible to this group at the specified position with optional text label. +---@version 2.5.0 +---@param point Vec3 Position in the DCS World coordinate system where the marker will appear. +---@param text? string Optional text to display with the marker. +function Group:markGroup(point, text) end + +--- Returns a `Group` object with the specified name, or `nil` if no such group exists. Works with both active and inactive groups. +---@version 1.2.0 +---@param name string Unique identifier of the group to retrieve. +---@return Group|nil +function Group.getByName(name) end + + +--- Represents a unit entity in the DCS World, including airplanes, helicopters, vehicles, ships, and armed ground structures. +---@class Unit : Object, CoalitionObject +Unit = Unit or {} +--- Returns `true` if the unit is activated in the mission, `false` otherwise. +---@version 1.2.0 +---@return boolean +function Unit:isActive() end + +--- Returns the `string` name of the player controlling this unit, or `nil` if AI-controlled. +---@version 1.2.4 +---@return nil|string +function Unit:getPlayerName() end + +--- Returns the `number` representing the unique mission ID of the unit. +---@version 1.2.0 +---@return number +function Unit:getID() end + +--- Returns the `number` representing the index of the unit within its group. This index persists even as other units in the group are destroyed. +---@version 1.2.0 +---@return number +function Unit:getNumber() end + +--- Returns a `Unit.Category` enumerator representing the specific category of the unit. +---@version 2.9.2 +---@return Unit.Category +function Unit:getCategoryEx() end + +--- Returns the `number` representing the runtime object ID of the unit. Every simulation object has a unique objectID. +---@version 1.2.4 +---@return number +function Unit:getObjectID() end + +--- Returns the `Controller` object for this unit. Note: Ships and ground units are only controllable at a group level. +---@version 1.2.0 +---@return Controller +function Unit:getController() end + +--- Returns the `Group` object that this unit belongs to. +---@version 1.2.0 +---@return Group +function Unit:getGroup() end + +--- Returns a `string` representing the localized callsign of the unit. +---@version 1.2.6 +---@return string +function Unit:getCallsign() end + +--- Returns a `number` representing the current hit points of the unit. Values below 1 indicate the unit is destroyed. +---@version 1.2.0 +---@return number +function Unit:getLife() end + +--- Returns a `number` representing the maximum hit points of the unit. This value never changes during a mission. +---@version 1.2.0 +---@return number +function Unit:getLife0() end + +--- Returns a `number` representing the remaining fuel percentage (0.0 to 1.0+). Values above 1.0 indicate external fuel tanks. +---@version 1.2.3 +---@return number +function Unit:getFuel() end + +--- Returns a numerically indexed table of ammunition data for all weapons loaded on the unit. +---@version 1.2.4 +---@return UnitAmmoItem[] +function Unit:getAmmo() end + +--- Returns a numerically indexed table containing all sensors available on the unit. +---@version 1.2.0 +---@return UnitSensor[] +function Unit:getSensors() end + +--- Returns `true` if the unit has the specified sensor type and subcategory, `false` otherwise. +---@version 1.2.0 +---@param sensorType Unit.SensorType Type of sensor to check for. +---@param subCategory number Subcategory of the sensor type to check for. +---@return boolean +function Unit:hasSensors(sensorType, subCategory) end + +--- Returns information about the unit's radar status: operational state and tracked object (if any). +---@version 1.2.0 +---@return boolean +---@return Object|nil +function Unit:getRadar() end + +--- Returns a `number` representing the current value of a specified animation parameter on the unit's 3D model. +---@version 1.2.0 +---@param arg number The argument number for the animation parameter. +---@return number +function Unit:getDrawArgumentValue(arg) end + +--- Returns a numerically indexed table of nearby friendly cargo objects sorted by distance, or `nil` if not applicable. +---@version 2.5.5 +---@return Object[]|nil +function Unit:getNearestCargos() end + +--- Enables or disables the unit's emissions, affecting radar and other detectable systems without changing AI state. +---@version 2.7.0 +---@param setting boolean True to enable emissions, false to disable. +function Unit:enableEmission(setting) end + +--- Returns a `number` representing the infantry capacity of an aircraft, or `nil` for non-aircraft units. +---@version 2.5.6 +---@return nil|number +function Unit:getDescentCapacity() end + +--- Returns a table containing detailed technical specifications of the unit. The exact type of descriptor returned depends on the unit's category: AIRPLANE units return UnitDescAirplane, HELICOPTER units return UnitDescHelicopter, GROUND_UNIT units return UnitDescVehicle, SHIP units return UnitDescShip, and other types return the base UnitDesc. +---@version 1.2.0 +---@return UnitDesc|UnitDescAircraft|UnitDescAirplane|UnitDescHelicopter|UnitDescShip|UnitDescVehicle +function Unit:getDesc() end + +--- Returns the `Airbase` object where the unit is stationed or landed, or `nil` if not at an airbase. +---@version unknown +---@return Airbase|nil +function Unit:getAirbase() end + +--- Returns `true` if the unit can land on a ship, `false` otherwise. +---@version unknown +---@return boolean +function Unit:canShipLanding() end + +--- Returns `true` if the unit's ramp is currently open, `false` otherwise. +---@version unknown +---@return boolean +function Unit:checkOpenRamp() end + +--- Initiates the disembarkation process for troops or cargo from this transport unit. +---@version unknown +function Unit:disembarking() end + +--- Returns a numerically indexed table of cargo objects currently loaded on the unit. +---@version unknown +---@return Object[] +function Unit:getCargosOnBoard() end + +--- Returns a `coalition.side` enumerator representing the unit's coalition alignment. +---@version 1.2.4 +---@return coalition.side +function Unit:getCoalition() end + +--- Returns the communication system object for this unit. +---@version unknown +---@return unknown +function Unit:getCommunicator() end + +--- Returns a `country.id` enumerator representing the unit's country affiliation. +---@version 1.2.0 +---@return country.id +function Unit:getCountry() end + +--- Returns information about troops currently loaded on the unit. +---@version unknown +---@return unknown +function Unit:getDescentOnBoard() end + +--- Returns a `string` name of the military force this unit belongs to, or `nil` if not specified. +---@version unknown +---@return nil|string +function Unit:getForcesName() end + +--- Returns a `number` representing the low fuel threshold for this unit, or `nil` if not applicable. +---@version unknown +---@return nil|number +function Unit:getFuelLowState() end + +--- Returns a numerically indexed table of nearby cargo objects suitable for aircraft loading, or `nil` if not applicable. +---@version unknown +---@return Object[]|nil +function Unit:getNearestCargosForAircraft() end + +--- Returns information about available seating in the unit. +---@version unknown +---@return unknown +function Unit:getSeats() end + +--- Returns `true` if the unit is or has aircraft carrier capabilities, `false` otherwise. +---@version unknown +---@return boolean +function Unit:hasCarrier() end + +--- Loads specified cargo or troops onto this transport unit. +---@version unknown +function Unit:LoadOnBoard() end + +--- Marks a task for disembarking troops or cargo from this unit. +---@version unknown +function Unit:markDisembarkingTask() end + +--- Displays the legacy carrier operations menu for this unit. +---@version unknown +function Unit:OldCarrierMenuShow() end + +--- Opens the cargo ramp on this transport unit to allow loading/unloading. +---@version unknown +function Unit:openRamp() end + +--- Initiates the unloading process for cargo carried by this unit. +---@version unknown +function Unit:UnloadCargo() end + +--- Returns `true` if the unit has VTOL landing capabilities, `false` otherwise. +---@version unknown +---@return boolean +function Unit:vtolableLA() end + +--- Returns a `Unit` object with the specified name, or `nil` if not found. Provides access to both activated and non-activated units. +---@version 1.2.0 +---@param name string Name of the unit as defined in the mission editor or mission. +---@return Unit|nil +function Unit.getByName(name) end + +--- Returns a table containing detailed description of the specified unit type. The exact type of descriptor returned depends on the unit's category: AIRPLANE units return UnitDescAirplane, HELICOPTER units return UnitDescHelicopter, GROUND_UNIT units return UnitDescVehicle, SHIP units return UnitDescShip, and other types return the base UnitDesc. Functions even for unit types not present in the current mission. +---@version 1.2.4 +---@param typeName string Internal type name of the unit, e.g. 'FA-18C_hornet'. +---@return UnitDesc|UnitDescAircraft|UnitDescAirplane|UnitDescHelicopter|UnitDescShip|UnitDescVehicle +function Unit.getDescByName(typeName) end + + +--- Represents a storage facility at airbases that manages aircraft, munitions, and fuel resources available to coalition forces. +---@class Warehouse +Warehouse = Warehouse or {} +--- Adds the specified quantity of an item to the warehouse inventory. +---@param itemName_or_wsType string|table +---@param count number +function Warehouse:addItem(itemName_or_wsType, count) end + +--- Returns a `number` representing the quantity of the specified item in the warehouse. +---@param itemName_or_wsType string|table +---@return number +function Warehouse:getItemCount(itemName_or_wsType) end + +--- Sets the exact quantity of an item in the warehouse inventory, replacing any existing amount. +---@param itemName_or_wsType string|table +---@param count number +function Warehouse:setItem(itemName_or_wsType, count) end + +--- Removes the specified quantity of an item from the warehouse inventory. +---@param itemName_or_wsType string|table +---@param count number +function Warehouse:removeItem(itemName_or_wsType, count) end + +--- Adds the specified amount of liquid fuel to the warehouse inventory. +---@param liquidType LiquidType +---@param count number +function Warehouse:addLiquid(liquidType, count) end + +--- Returns a `number` representing the quantity of the specified liquid fuel in the warehouse. +---@param liquidType LiquidType +---@return number +function Warehouse:getLiquidAmount(liquidType) end + +--- Sets the exact amount of a liquid fuel in the warehouse inventory, replacing any existing amount. +---@param liquidType LiquidType +---@param count number +function Warehouse:setLiquidAmount(liquidType, count) end + +--- Removes the specified amount of liquid fuel from the warehouse inventory. +---@param liquidType LiquidType +---@param count number +function Warehouse:removeLiquid(liquidType, count) end + +--- Returns the `Airbase` object that owns this warehouse. +---@return Airbase +function Warehouse:getOwner() end + +--- Returns a table containing a complete inventory of all items in the warehouse. +---@param itemName_or_wsType string|table +---@return table +function Warehouse:getInventory(itemName_or_wsType) end + +--- Returns a `Warehouse` object with the specified name, or `nil` if not found. +---@version 2.8.8 +---@param name string The name of the warehouse. +---@return Warehouse|nil +function Warehouse.getByName(name) end + +--- Returns a `Warehouse` object associated with a cargo static object, or `nil` if not applicable. +---@version 2.8.8 +---@param cargo StaticObject The cargo static object. +---@return Warehouse|nil +function Warehouse.getCargoAsWarehouse(cargo) end + +--- Returns a table of all warehouses in the mission, indexed by warehouse name. +---@version 2.8.8 +---@return table +function Warehouse.getResourceMap() end + + +--- Represents a weapon entity in the DCS World, including shells, rockets, missiles, and bombs. +---@class Weapon : Object, CoalitionObject +Weapon = Weapon or {} +--- Returns an `Object.Category` enumerator and a `Weapon.Category` enumerator representing the weapon's classification. +---@version 1.2.0 +---@return Object.Category +---@return Weapon.Category +function Weapon:getCategory() end + +--- Returns a `Vec3` representing the weapon's position in the DCS World coordinate system. +---@version 1.2.0 +---@return Vec3 +function Weapon:getPoint() end + +--- Returns a `Vec3` representing the weapon's velocity vector in the DCS World coordinate system. +---@version 1.2.0 +---@return Vec3 +function Weapon:getVelocity() end + +--- Returns a `coalition.side` enumerator representing the weapon's coalition alignment. +---@version 1.2.0 +---@return coalition.side +function Weapon:getCoalition() end + +--- Returns a `string` representing the weapon's name in the mission. +---@version 1.2.0 +---@return string +function Weapon:getName() end + +--- Returns a `string` representing the weapon's type designation. +---@version 1.2.0 +---@return string +function Weapon:getTypeName() end + +--- Returns a table containing detailed technical specifications of the weapon. The exact structure depends on the weapon category. +---@version 1.2.0 +---@return WeaponDesc|WeaponDescBomb|WeaponDescMissile|WeaponDescRocket +function Weapon:getDesc() end + +--- Removes the weapon from the mission without generating destruction events. +---@version 1.2.0 +function Weapon:destroy() end + +--- Returns `true` if the weapon exists in the mission, `false` otherwise. +---@version 1.2.0 +---@return boolean +function Weapon:isExist() end + +--- Returns a `Position3` representing the weapon's position and orientation in the DCS World coordinate system. +---@version 1.2.0 +---@return Position3 +function Weapon:getPosition() end + +--- Returns the `Unit` object that launched this weapon. +---@version 1.2.4 +---@return Unit +function Weapon:getLauncher() end + +--- Returns the `Object` that this guided weapon is targeting, or `nil` for unguided weapons or ground-targeted weapons. +---@version 1.2.4 +---@return Object|nil +function Weapon:getTarget() end + +--- Returns a `Weapon.Category` enumerator representing the specific weapon classification (SHELL, MISSILE, ROCKET, BOMB). +---@version 1.2.4 +---@return Weapon.Category +function Weapon:getCategoryEx() end + +--- Returns a `country.id` enumerator representing the country that owns this weapon (via its launcher). +---@version 1.2.0 +---@return country.id +function Weapon:getCountry() end + +--- Returns a `string` representing the force name this weapon belongs to, or `nil` if not assigned to a specific force. +---@return nil|string +function Weapon:getForcesName() end + + +--- Provides functions for managing events, accessing game world information, and controlling weather conditions in the DCS World. +---@class world +---@field weather table Provides functions for controlling fog and weather conditions in the mission.@version 2.9.10 +---@field event world.event #READONLY Enumerator for event types that can occur in the DCS World simulation. +---@field BirthPlace world.BirthPlace #READONLY Enumerator for spawn locations of aircraft and helicopters. +---@field VolumeType world.VolumeType #READONLY Enumerator for 3D volume types used in spatial queries. +---@field eventHandlers table #READONLY Table of all registered event handler functions. +---@field persistenceHandlers table #READONLY Table of all registered persistence handler functions. +world = world or {} +--- Registers an event handler table to be called when simulator events occur. The table must contain an onEvent method. +---@version 1.2.0 +---@param handler EventHandlerTable A table containing an onEvent method that will be called with event data when events occur. +function world.addEventHandler(handler) end + +--- Unregisters a previously added event handler table. +---@version 1.2.0 +---@param handler EventHandlerTable The event handler table to remove. Must be the same table that was passed to addEventHandler. +function world.removeEventHandler(handler) end + +--- Handles simulator events internally for the DCS Mission environment. Different event types produce different event data structures. +---@param eventData EventData Event data containing information about the triggered event. The specific event type determines which fields are available. +function world.onEvent(eventData) end + +--- Returns the `Unit` controlled by the player, or `nil` if no unit is directly controlled. +---@version 1.2.4 +---@return Unit|nil +function world.getPlayer() end + +--- Returns a numerically indexed table of all airbase objects in the mission. +---@version 1.2.4 +---@return AirbaseArray +function world.getAirbases() end + +--- Returns objects within a specified 3D volume, optionally applying a handler function to each found object. +---@version 1.2.4 +---@param category Object.Category|ObjectCategoryArray The category or categories of objects to search for. +---@param searchVolume table A table defining the search volume. +---@param Handler? fun(...) An optional handler function to run on each found object. +---@param data? any Optional data to pass to the handler function. +---@return ObjectArray +function world.searchObjects(category, searchVolume, Handler, data) end + +--- Returns a table of all active map markers and drawn shapes in the mission. +---@version 2.5.1 +---@return table +function world.getMarkPanels() end + +--- Removes debris within a specified volume, returning the number of items cleared. +---@version 2.8.4 +---@param searchVolume table A table defining the volume from which to remove junk. +---@return number +function world.removeJunk(searchVolume) end + +--- Returns mission persistence data used for saving mission state. +---@return table +function world.getPersistenceData() end + +--- Executes all registered persistence handler functions. +function world.runPersistenceHandlers() end + +--- Registers a handler function for mission persistence operations. +---@param handler fun(...) The persistence handler function. +function world.setPersistenceHandler(handler) end + + + +-- Type Definitions (Enums, Aliases, Records/Classes) +--- Represents a value whose type is not strictly defined or may vary across different contexts in the DCS World scripting environment. +---@alias unknown boolean|number|string|table + +trigger.smokeColor = trigger.smokeColor or {} +--- Enumerator for smoke colors, used to specify colored smoke effects in trigger actions. +---@version 1.2.0 +---@enum trigger.smokeColor +trigger.smokeColor = { + Green = 0, + Red = 1, + White = 2, + Orange = 3, + Blue = 4 +} + +trigger.flareColor = trigger.flareColor or {} +--- Enumerator for flare colors, used to specify colored illumination flares in trigger actions. +---@version 1.2.0 +---@enum trigger.flareColor +trigger.flareColor = { + Green = 0, + Red = 1, + White = 2, + Yellow = 3 +} + +MarkupShapeId = MarkupShapeId or {} +--- Enumerator for markup geometric shape types, used with trigger.action.markupToAll to create map annotations. +---@version 2.5.6 +---@enum MarkupShapeId +MarkupShapeId = { + Line = 1, + Circle = 2, + Rect = 3, + Arrow = 4, + Text = 5, + Quad = 6, + Freeform = 7 +} + +MarkupLineType = MarkupLineType or {} +--- Enumerator for line style patterns, used to customize map markup line appearances in trigger actions. +---@version 2.5.5 +---@enum MarkupLineType +MarkupLineType = { + NoLine = 0, + Solid = 1, + Dashed = 2, + Dotted = 3, + DotDash = 4, + LongDash = 5, + TwoDash = 6 +} + +BigSmokeType = BigSmokeType or {} +--- Enumerator for smoke and fire effect variants, used to specify visual effect intensity and characteristics in trigger actions. +---@version 1.2.0 +---@enum BigSmokeType +BigSmokeType = { + SmallSmokeAndFire = 1, + MediumSmokeAndFire = 2, + LargeSmokeAndFire = 3, + HugeSmokeAndFire = 4, + SmallSmoke = 5, + MediumSmoke = 6, + LargeSmoke = 7, + HugeSmoke = 8 +} + +RadioModulation = RadioModulation or {} +--- Enumerator for radio transmission modulation types, used to specify signal modulation in trigger communication actions. +---@version 1.2.0 +---@enum RadioModulation +RadioModulation = { + AM = 0, + FM = 1 +} + +--- Defines the structure of a Lua table representing a color with red, green, blue, and alpha components, each normalized between 0.0 and 1.0. +--- (Data structure definition for ColorRGBA. Not a globally accessible table.) +--- ### Examples +--- ```lua +--- {r = 1.0, g = 0.0, b = 0.0, a = 1.0} -- Solid Red +--- ``` +--- ```lua +--- {r = 0.0, g = 1.0, b = 0.0, a = 0.5} -- Semi-transparent Green +--- ``` +---@class ColorRGBA +---@field r number Red component value between 0.0 (no red) and 1.0 (maximum red intensity). +---@field g number Green component value between 0.0 (no green) and 1.0 (maximum green intensity). +---@field b number Blue component value between 0.0 (no blue) and 1.0 (maximum blue intensity). +---@field a number Alpha (transparency) component value between 0.0 (fully transparent) and 1.0 (fully opaque). + +--- Represents a 2D vector or point, typically used for map coordinates in the DCS World coordinate system. It is a Lua table with `x` and `y` keys. +--- (Data structure definition for Vec2. Not a globally accessible table.) +---@class Vec2 +---@field x number X coordinate, which represents the north-south direction in the DCS World coordinate system. North is positive, South is negative. +---@field y number Y coordinate, which represents the east-west direction in the DCS World coordinate system. East is positive, West is negative. + +--- Represents a 3D vector or point, typically used for positions or velocities in the DCS World coordinate system. It is a Lua table with `x`, `y`, and `z` keys. +--- (Data structure definition for Vec3. Not a globally accessible table.) +---@class Vec3 +---@field x number X coordinate, which represents the north-south direction in the DCS World coordinate system. North is positive, South is negative. +---@field y number Y coordinate, which represents the elevation direction in the DCS World coordinate system. Up is positive, Down is negative. +---@field z number Z coordinate, which represents the east-west direction in the DCS World coordinate system. East is positive, West is negative. + +Callsigns_JTAC = Callsigns_JTAC or {} +--- Enumerator for Joint Terminal Attack Controller (JTAC) callsigns, used to identify JTAC units in communications and mission planning. +---@version 1.2.4 +---@enum Callsigns_JTAC +Callsigns_JTAC = { + Axeman = 1, + Darknight = 2, + Warrior = 3, + Pointer = 4, + Eyeball = 5, + Moonbeam = 6, + Whiplash = 7, + Finger = 8, + Pinpoint = 9, + Ferret = 10, + Shaba = 11, + Playboy = 12, + Hammer = 13, + Jaguar = 14, + Deathstar = 15, + Anvil = 16, + Firefly = 17, + Mantis = 18, + Badger = 19 +} + +--- Defines the structure of a Lua table representing a group to be spawned with coalition.addGroup or coalition.add_dyn_group functions. +--- (Data structure definition for GroupSpawnData. Not a globally accessible table.) +---@class GroupSpawnData +---@field name string A string identifier for the group, must be unique within the mission. +---@field task string The primary mission task assigned to the group, determines default behavior patterns. +---@field units table A numerically indexed table of unit definitions for all units in the group, each containing position, type, and other properties. +---@field x number X coordinate of the group's reference position in the DCS World coordinate system. +---@field y number Y coordinate of the group's reference position in the DCS World coordinate system. +---@field start_time number Time in seconds after mission start when the group will spawn (0 for immediate spawn). +---@field visible boolean Controls visibility of the group before its scheduled start time in the mission editor. +---@field taskSelected boolean Indicates if the task is selected for execution when the group spawns. +---@field route table A table defining waypoints and assigned tasks for the group's route, determining movement patterns. +---@field hidden boolean Controls visibility of the group on the F10 map view for players. +---@field groupId number Optional unique numeric identifier for the group, used for scripting references. + +--- Defines the structure of a Lua table representing a Military Grid Reference System (MGRS) coordinate used for precise position referencing in the DCS World. +--- (Data structure definition for MGRS. Not a globally accessible table.) +---@version 1.2.0 +---@class MGRS +---@field UTMZone string The Universal Transverse Mercator (UTM) zone designation indicating the longitude zone. +---@field MGRSDigraph string The MGRS grid square designator consisting of two letters identifying a specific 100km square. +---@field Easting number Easting coordinate in meters, measuring the distance eastward from the zone's central meridian. +---@field Northing number Northing coordinate in meters, measuring the distance northward from the equator. + +AI.Task = AI.Task or {} +AI.Task.WeaponExpend = AI.Task.WeaponExpend or {} +--- Enumerator for ammunition expenditure levels per attack run, used in AI task assignments. +---@version 1.2.4 +---@enum AI.Task.WeaponExpend +AI.Task.WeaponExpend = { + QUARTER = "Quarter", + TWO = "Two", + ONE = "One", + FOUR = "Four", + HALF = "Half", + ALL = "All" +} + +AI.Task.Designation = AI.Task.Designation or {} +--- Enumerator for target designation methods used by Forward Air Controllers (FAC) and Joint Terminal Attack Controllers (JTAC). +---@version 1.2.4 +---@enum AI.Task.Designation +AI.Task.Designation = { + NO = "No", + WP = "WP", + IR_POINTER = "IR-Pointer", + LASER = "Laser", + AUTO = "Auto" +} + +AI.Task.OrbitPattern = AI.Task.OrbitPattern or {} +--- Enumerator for aircraft orbit patterns used in patrol and surveillance tasks. +---@version 1.2.4 +---@enum AI.Task.OrbitPattern +AI.Task.OrbitPattern = { + RACE_TRACK = "Race-Track", + CIRCLE = "Circle" +} + +AI.Task.TurnMethod = AI.Task.TurnMethod or {} +--- Enumerator for waypoint turn methods used in AI navigation. +---@version 1.1 +---@enum AI.Task.TurnMethod +AI.Task.TurnMethod = { + FLY_OVER_POINT = "Fly Over Point", + FIN_POINT = "Fin Point" +} + +AI.Task.VehicleFormation = AI.Task.VehicleFormation or {} +--- Enumerator for ground vehicle formation patterns used in group movement. +---@version 1.2.0 +---@enum AI.Task.VehicleFormation +AI.Task.VehicleFormation = { + VEE = "Vee", + ECHELON_RIGHT = "EchelonR", + OFF_ROAD = "Off Road", + RANK = "Rank", + ECHELON_LEFT = "EchelonL", + ON_ROAD = "On Road", + CONE = "Cone", + DIAMOND = "Diamond" +} + +AI.Task.AltitudeType = AI.Task.AltitudeType or {} +--- Enumerator for altitude reference systems used in AI flight tasks. +---@version 1.2.0 +---@enum AI.Task.AltitudeType +AI.Task.AltitudeType = { + RADIO = "RADIO", + BARO = "BARO" +} + +AI.Task.WaypointType = AI.Task.WaypointType or {} +--- Enumerator for waypoint types used in AI flight planning. +---@version 1.1 +---@enum AI.Task.WaypointType +AI.Task.WaypointType = { + TAKEOFF = "TakeOff", + TAKEOFF_PARKING = "TakeOffParking", + TURNING_POINT = "Turning Point", + TAKEOFF_PARKING_HOT = "TakeOffParkingHot", + LAND = "Land" +} + +AI.Skill = AI.Skill or {} +--- Enumerator for AI difficulty and competence levels assigned to units. +---@version 1.2.0 +---@enum AI.Skill +AI.Skill = { + PLAYER = "PLAYER", + CLIENT = "CLIENT", + AVERAGE = "AVERAGE", + GOOD = "GOOD", + HIGH = "HIGH", + EXCELLENT = "EXCELLENT" +} + +--- Defines the structure of options applicable to AI-controlled aircraft. +---@version 1.2.0 +---@class AI.Option.Air +AI.Option = AI.Option or {} +AI.Option.Air = AI.Option.Air or {} + +AI.Option.Air.id = AI.Option.Air.id or {} +--- Enumerator for option identifiers applicable to AI-controlled aircraft. +---@version 1.2.0 +---@enum AI.Option.Air.id +AI.Option.Air.id = { + NO_OPTION = -1, + ROE = 0, + REACTION_ON_THREAT = 1, + RADAR_USING = 3, + FLARE_USING = 4, + FORMATION = 5, + RTB_ON_BINGO = 6, + SILENCE = 7, + RTB_ON_OUT_OF_AMMO = 10, + ECM_USING = 13, + PROHIBIT_AA = 14, + PROHIBIT_JETT = 15, + PROHIBIT_AB = 16, + PROHIBIT_AG = 17, + MISSILE_ATTACK = 18, + PROHIBIT_WP_PASS_REPORT = 19, + OPTION_RADIO_USAGE_CONTACT = 21, + OPTION_RADIO_USAGE_ENGAGE = 22, + OPTION_RADIO_USAGE_KILL = 23, + JETT_TANKS_IF_EMPTY = 25, + FORCED_ATTACK = 26, + PREFER_VERTICAL = 32, + ALLOW_FORMATION_SIDE_SWAP = 35 +} + +--- Defines the structure of option values for AI-controlled aircraft settings. +---@version 1.2.0 +---@class AI.Option.Air.val +AI.Option.Air.val = AI.Option.Air.val or {} + +AI.Option.Air.val.ROE = AI.Option.Air.val.ROE or {} +--- Enumerator for Rules of Engagement settings available to AI-controlled aircraft. +---@version 1.2.4 +---@enum AI.Option.Air.val.ROE +AI.Option.Air.val.ROE = { + WEAPON_FREE = 0, + OPEN_FIRE_WEAPON_FREE = 1, + OPEN_FIRE = 2, + RETURN_FIRE = 3, + WEAPON_HOLD = 4 +} + +AI.Option.Air.val.REACTION_ON_THREAT = AI.Option.Air.val.REACTION_ON_THREAT or {} +--- Enumerator for threat response behaviors available to AI-controlled aircraft. +---@version 1.2.4 +---@enum AI.Option.Air.val.REACTION_ON_THREAT +AI.Option.Air.val.REACTION_ON_THREAT = { + NO_REACTION = 0, + PASSIVE_DEFENCE = 1, + EVADE_FIRE = 2, + BYPASS_AND_ESCAPE = 3, + ALLOW_ABORT_MISSION = 4 +} + +AI.Option.Air.val.RADAR_USING = AI.Option.Air.val.RADAR_USING or {} +--- Enumerator for radar usage policies available to AI-controlled aircraft. +---@version 1.2.4 +---@enum AI.Option.Air.val.RADAR_USING +AI.Option.Air.val.RADAR_USING = { + NEVER = 0, + FOR_ATTACK_ONLY = 1, + FOR_SEARCH_IF_REQUIRED = 2, + FOR_CONTINUOUS_SEARCH = 3 +} + +AI.Option.Air.val.FLARE_USING = AI.Option.Air.val.FLARE_USING or {} +--- Enumerator for flare countermeasure usage policies available to AI-controlled aircraft. +---@version 1.2.4 +---@enum AI.Option.Air.val.FLARE_USING +AI.Option.Air.val.FLARE_USING = { + NEVER = 0, + AGAINST_FIRED_MISSILE = 1, + WHEN_FLYING_IN_SAM_WEZ = 2, + WHEN_FLYING_NEAR_ENEMIES = 3 +} + +AI.Option.Air.val.ECM_USING = AI.Option.Air.val.ECM_USING or {} +--- Enumerator for electronic countermeasure usage policies available to AI-controlled aircraft. +---@version 1.5.0 +---@enum AI.Option.Air.val.ECM_USING +AI.Option.Air.val.ECM_USING = { + NEVER_USE = 0, + USE_IF_ONLY_LOCK_BY_RADAR = 1, + USE_IF_DETECTED_LOCK_BY_RADAR = 2, + ALWAYS_USE = 3 +} + +AI.Option.Air.val.MISSILE_ATTACK = AI.Option.Air.val.MISSILE_ATTACK or {} +--- Enumerator for missile engagement range policies available to AI-controlled aircraft. +---@version 1.5.0 +---@enum AI.Option.Air.val.MISSILE_ATTACK +AI.Option.Air.val.MISSILE_ATTACK = { + MAX_RANGE = 0, + NEZ_RANGE = 1, + HALF_WAY_RMAX_NEZ = 2, + TARGET_THREAT_EST = 3, + RANDOM_RANGE = 4 +} + +--- Defines the structure of options applicable to AI-controlled ground units. +---@version 1.2.0 +---@class AI.Option.Ground +AI.Option.Ground = AI.Option.Ground or {} + +AI.Option.Ground.id = AI.Option.Ground.id or {} +--- Enumerator for option identifiers applicable to AI-controlled ground units. +---@version 1.2.0 +---@enum AI.Option.Ground.id +AI.Option.Ground.id = { + NO_OPTION = -1, + ROE = 0, + FORMATION = 5, + DISPERSE_ON_ATTACK = 8, + ALARM_STATE = 9, + ENGAGE_AIR_WEAPONS = 20, + AC_ENGAGEMENT_RANGE_RESTRICTION = 24, + EVASION_OF_ARM = 31 +} + +--- Defines the structure of option values for AI-controlled ground unit settings. +---@version 1.2.0 +---@class AI.Option.Ground.val +AI.Option.Ground.val = AI.Option.Ground.val or {} + +AI.Option.Ground.val.ALARM_STATE = AI.Option.Ground.val.ALARM_STATE or {} +--- Enumerator for alert readiness levels available to AI-controlled ground units. +---@version 1.2.4 +---@enum AI.Option.Ground.val.ALARM_STATE +AI.Option.Ground.val.ALARM_STATE = { + AUTO = 0, + GREEN = 1, + RED = 2 +} + +AI.Option.Ground.val.ROE = AI.Option.Ground.val.ROE or {} +--- Enumerator for Rules of Engagement settings available to AI-controlled ground units. +---@version 1.2.4 +---@enum AI.Option.Ground.val.ROE +AI.Option.Ground.val.ROE = { + OPEN_FIRE = 2, + RETURN_FIRE = 3, + WEAPON_HOLD = 4 +} + +--- Defines the structure of options applicable to AI-controlled naval units. +---@version 1.2.0 +---@class AI.Option.Naval +AI.Option.Naval = AI.Option.Naval or {} + +AI.Option.Naval.id = AI.Option.Naval.id or {} +--- Enumerator for option identifiers applicable to AI-controlled naval units. +---@version 1.2.0 +---@enum AI.Option.Naval.id +AI.Option.Naval.id = { + NO_OPTION = -1, + ROE = 0 +} + +--- Defines the structure of option values for AI-controlled naval unit settings. +---@version 1.2.0 +---@class AI.Option.Naval.val +AI.Option.Naval.val = AI.Option.Naval.val or {} + +AI.Option.Naval.val.ROE = AI.Option.Naval.val.ROE or {} +--- Enumerator for Rules of Engagement settings available to AI-controlled naval units. +---@version 1.2.4 +---@enum AI.Option.Naval.val.ROE +AI.Option.Naval.val.ROE = { + OPEN_FIRE = 2, + RETURN_FIRE = 3, + WEAPON_HOLD = 4 +} + +FormationType = FormationType or {} +--- Enumerator for all formation patterns used across different AI unit types. +---@version 1.2.0 +---@enum FormationType +FormationType = { + LINE_ABREAST = 1, + TRAIL = 2, + WEDGE = 3, + ECHELON_RIGHT_AIR = 4, + ECHELON_LEFT_AIR = 5, + FINGER_FOUR = 6, + SPREAD_FOUR = 7, + HEL_WEDGE = 8, + HEL_ECHELON = 9, + HEL_FRONT = 10, + HEL_COLUMN = 11, + WW2_BOMBER_ELEMENT = 12, + WW2_BOMBER_ELEMENT_HEIGHT = 13, + WW2_FIGHTER_VIC = 14, + GROUND_VEE = "Vee", + GROUND_ECHELON_RIGHT = "EchelonR", + GROUND_OFF_ROAD = "Off Road", + GROUND_RANK = "Rank", + GROUND_ECHELON_LEFT = "EchelonL", + GROUND_ON_ROAD = "On Road", + GROUND_CONE = "Cone", + GROUND_DIAMOND = "Diamond" +} + +Unit.Category = Unit.Category or {} +--- Enumerator for unit categories, used to classify entities by their basic type and operational domain. +---@enum Unit.Category +Unit.Category = { + AIRPLANE = 0, + HELICOPTER = 1, + GROUND_UNIT = 2, + SHIP = 3, + STRUCTURE = 4 +} + +Unit.RefuelingSystem = Unit.RefuelingSystem or {} +--- Enumerator for aerial refueling system types, used to specify compatible air-to-air refueling equipment configurations. +---@enum Unit.RefuelingSystem +Unit.RefuelingSystem = { + BOOM_AND_RECEPTACLE = 0, + PROBE_AND_DROGUE = 1 +} + +Unit.SensorType = Unit.SensorType or {} +--- Enumerator for sensor system categories, used to classify detection and targeting equipment on units. +---@enum Unit.SensorType +Unit.SensorType = { + OPTIC = 0, + RADAR = 1, + IRST = 2, + RWR = 3 +} + +Unit.OpticType = Unit.OpticType or {} +--- Enumerator for optical sensor technologies, used to specify visual detection capabilities of units. +---@enum Unit.OpticType +Unit.OpticType = { + TV = 0, + LLTV = 1, + IR = 2 +} + +Unit.RadarType = Unit.RadarType or {} +--- Enumerator for radar system classifications, used to differentiate between air search and surface search capabilities. +---@enum Unit.RadarType +Unit.RadarType = { + AS = 0, + SS = 1 +} + +--- A table of capability and characteristic flags that define the features and abilities of a unit. Fields correspond to attributes from the Attributes enum. +--- (Data structure definition for UnitAttributes. Not a globally accessible table.) +---@class UnitAttributes + +--- Defines the structure of a Lua table representing detection ranges based on aspect angle within a hemisphere. +--- (Data structure definition for UnitSensorHemisphereDistance. Not a globally accessible table.) +---@class UnitSensorHemisphereDistance +---@field tailOn number A numeric value representing the maximum detection distance in meters when facing the rear of the target. +---@field headOn number A numeric value representing the maximum detection distance in meters when facing the front of the target. + +--- Defines the structure of a Lua table representing performance characteristics specific to fixed-wing aircraft in the DCS World. Includes all fields from UnitDesc and UnitDescAircraft plus the following airplane-specific fields. +--- (Data structure definition for UnitDescAirplane. Not a globally accessible table.) +---@class UnitDescAirplane +---@field speedMax0 number A numeric value representing the maximum true airspeed in meters per second at sea level. +---@field speedMax10K number A numeric value representing the maximum true airspeed in meters per second at 10,000 meters altitude. + +--- Defines the structure of a Lua table representing performance characteristics specific to rotary-wing aircraft in the DCS World. Includes all fields from UnitDesc and UnitDescAircraft plus the following helicopter-specific fields. +--- (Data structure definition for UnitDescHelicopter. Not a globally accessible table.) +---@class UnitDescHelicopter +---@field HmaxStat number A numeric value representing the maximum hover ceiling in meters (altitude at which the helicopter can maintain a stable hover). + +--- Defines the structure of a Lua table representing performance characteristics specific to ground vehicles in the DCS World. Includes all fields from UnitDesc plus the following vehicle-specific fields. +--- (Data structure definition for UnitDescVehicle. Not a globally accessible table.) +---@class UnitDescVehicle +---@field maxSlopeAngle number A numeric value representing the maximum terrain slope angle in radians that the vehicle can traverse. +---@field riverCrossing boolean A boolean value indicating whether the vehicle has amphibious capabilities to cross water obstacles. +---@field speedMaxOffRoad nil|number A numeric value representing the maximum speed in meters per second when traveling on unpaved terrain. + +--- Defines the structure of a Lua table representing performance characteristics specific to naval vessels in the DCS World. Includes all fields from UnitDesc plus any ship-specific fields. +--- (Data structure definition for UnitDescShip. Not a globally accessible table.) +---@class UnitDescShip + +Weapon.Category = Weapon.Category or {} +--- Enumerator for weapon class classifications, used to categorize different types of munitions by their fundamental operational characteristics. +---@enum Weapon.Category +Weapon.Category = { + SHELL = 0, + MISSILE = 1, + ROCKET = 2, + BOMB = 3, + TORPEDO = 4 +} + +Weapon.GuidanceType = Weapon.GuidanceType or {} +--- Enumerator for weapon guidance technologies, used to specify the targeting and course correction mechanisms of guided munitions. +---@enum Weapon.GuidanceType +Weapon.GuidanceType = { + INS = 1, + IR = 2, + RADAR_ACTIVE = 3, + RADAR_SEMI_ACTIVE = 4, + RADAR_PASSIVE = 5, + TV = 6, + LASER = 7, + TELE = 8 +} + +Weapon.MissileCategory = Weapon.MissileCategory or {} +--- Enumerator for missile operational roles, used to classify missiles by their intended target types and operational domains. +---@enum Weapon.MissileCategory +Weapon.MissileCategory = { + AAM = 1, + SAM = 2, + BM = 3, + ANTI_SHIP = 4, + CRUISE = 5, + OTHER = 6 +} + +Weapon.WarheadType = Weapon.WarheadType or {} +--- Enumerator for warhead mechanisms, used to specify the damage-producing method employed by a weapon's terminal effect component. +---@enum Weapon.WarheadType +Weapon.WarheadType = { + AP = 0, + HE = 1, + SHAPED_EXPLOSIVE = 2 +} + +Weapon.flag = Weapon.flag or {} +--- Enumerator for weapon capability flags, used to identify specific weapon properties and subtypes through bit field values. +---@enum Weapon.flag +Weapon.flag = { + NoWeapon = 0, + LGB = 2, + TvGB = 4, + SNSGB = 8, + HEBomb = 16, + Penetrator = 32, + NapalmBomb = 64, + FAEBomb = 128, + ClusterBomb = 256, + Dispencer = 512, + CandleBomb = 1024, + ParachuteBomb = 2147483648, + GuidedBomb = 14, + AnyBomb = 2147485694, + AnyUnguidedBomb = 2147485680, + LightRocket = 2048, + CandleRocket = 8192, + HeavyRocket = 16384, + MarkerRocket = 4096, + AnyRocket = 30720, + SAR_AAM = 67108864, + AR_AAM = 134217728, + IR_AAM = 33554432, + SRAAM = 4194304, + MRAAM = 8388608, + LRAAM = 16777216, + AnyAAM = 264241152, + AnyAAWeapon = 1069547520, + AntiRadarMissile = 32768, + AntiRadarMissile2 = 1073741824, + AntiShipMissile = 65536, + AntiTankMissile = 131072, + FireAndForgetASM = 262144, + LaserASM = 524288, + TeleASM = 1048576, + CruiseMissile = 2097152, + GuidedASM = 1572864, + TacticASM = 1835008, + AnyASM = 4161536, + AnyAutonomousMissile = 36012032, + AnyMissile = 268402688, + AnyAGWeapon = 2956984318, + Torpedo = 4294967296, + AnyTorpedo = 4294967296, + GUN_POD = 268435456, + BuiltInCannon = 536870912, + Cannons = 805306368, + AnyShell = 258503344128, + ConventionalShell = 206963736576, + GuidedShell = 137438953472, + IlluminationShell = 34359738368, + MarkerShell = 51539607552, + SmokeShell = 17179869184, + SubmunitionDispenserShell = 68719476736, + Decoys = 8589934592, + ArmWeapon = 213674609662, + GuidedWeapon = 137707356174, + MarkerWeapon = 51539620864, + UnguidedWeapon = 2952822768, + AnyWeapon = 265214230526, + AllWeapon = -1 +} + +Attributes = Attributes or {} +--- Enumerator for unit capability and classification attributes, used to identify unit properties and targeting criteria in the DCS World. +---@version 2.5.0 +---@enum Attributes +Attributes = { + plane_carrier = "plane_carrier", + no_tail_trail = "no_tail_trail", + cord = "cord", + ski_jump = "ski_jump", + catapult = "catapult", + low_reflection_vessel = "low_reflection_vessel", + AA_flak = "AA_flak", + AA_missile = "AA_missile", + ["Cruise missiles"] = "Cruise missiles", + ["Anti-Ship missiles"] = "Anti-Ship missiles", + Missiles = "Missiles", + Fighters = "Fighters", + Interceptors = "Interceptors", + ["Multirole fighters"] = "Multirole fighters", + Bombers = "Bombers", + Battleplanes = "Battleplanes", + AWACS = "AWACS", + Tankers = "Tankers", + Aux = "Aux", + Transports = "Transports", + ["Strategic bombers"] = "Strategic bombers", + UAVs = "UAVs", + ["Attack helicopters"] = "Attack helicopters", + ["Transport helicopters"] = "Transport helicopters", + Planes = "Planes", + Helicopters = "Helicopters", + Cars = "Cars", + Trucks = "Trucks", + Infantry = "Infantry", + Tanks = "Tanks", + Artillery = "Artillery", + MLRS = "MLRS", + IFV = "IFV", + APC = "APC", + Fortifications = "Fortifications", + ["Armed vehicles"] = "Armed vehicles", + ["Static AAA"] = "Static AAA", + ["Mobile AAA"] = "Mobile AAA", + ["SAM SR"] = "SAM SR", + ["SAM TR"] = "SAM TR", + ["SAM LL"] = "SAM LL", + ["SAM CC"] = "SAM CC", + ["SAM AUX"] = "SAM AUX", + ["SR SAM"] = "SR SAM", + ["MR SAM"] = "MR SAM", + ["LR SAM"] = "LR SAM", + ["SAM elements"] = "SAM elements", + ["IR Guided SAM"] = "IR Guided SAM", + SAM = "SAM", + ["SAM related"] = "SAM related", + AAA = "AAA", + EWR = "EWR", + ["Air Defence vehicles"] = "Air Defence vehicles", + MANPADS = "MANPADS", + ["MANPADS AUX"] = "MANPADS AUX", + ["Unarmed vehicles"] = "Unarmed vehicles", + ["Armed ground units"] = "Armed ground units", + ["Armed Air Defence"] = "Armed Air Defence", + ["Air Defence"] = "Air Defence", + ["Aircraft Carriers"] = "Aircraft Carriers", + Cruisers = "Cruisers", + Destroyers = "Destroyers", + Frigates = "Frigates", + Corvettes = "Corvettes", + ["Heavy armed ships"] = "Heavy armed ships", + ["Light armed ships"] = "Light armed ships", + ["Armed ships"] = "Armed ships", + ["Unarmed ships"] = "Unarmed ships", + Air = "Air", + ["Ground vehicles"] = "Ground vehicles", + Ships = "Ships", + Buildings = "Buildings", + HeavyArmoredUnits = "HeavyArmoredUnits", + ATGM = "ATGM", + ["Old Tanks"] = "Old Tanks", + ["Modern Tanks"] = "Modern Tanks", + LightArmoredUnits = "LightArmoredUnits", + ["Rocket Attack Valid AirDefence"] = "Rocket Attack Valid AirDefence", + ["Battle airplanes"] = "Battle airplanes", + All = "All", + ["Infantry carriers"] = "Infantry carriers", + Vehicles = "Vehicles", + ["Ground Units"] = "Ground Units", + ["Ground Units Non Airdefence"] = "Ground Units Non Airdefence", + ["Armored vehicles"] = "Armored vehicles", + ["AntiAir Armed Vehicles"] = "AntiAir Armed Vehicles", + Airfields = "Airfields", + Heliports = "Heliports", + ["Grass Airfields"] = "Grass Airfields", + Point = "Point", + NonArmoredUnits = "NonArmoredUnits", + NonAndLightArmoredUnits = "NonAndLightArmoredUnits", + human_vehicle = "human_vehicle", + RADAR_BAND1_FOR_ARM = "RADAR_BAND1_FOR_ARM", + RADAR_BAND2_FOR_ARM = "RADAR_BAND2_FOR_ARM", + Prone = "Prone", + DetectionByAWACS = "DetectionByAWACS", + Datalink = "Datalink", + CustomAimPoint = "CustomAimPoint", + ["Indirect fire"] = "Indirect fire", + Refuelable = "Refuelable", + Weapon = "Weapon", + Shell = "Shell", + Rocket = "Rocket", + Bomb = "Bomb", + Missile = "Missile" +} + +--- Defines the structure of a Lua table representing geographical coordinates using latitude and longitude values. +--- (Data structure definition for LatLon. Not a globally accessible table.) +---@version 1.2.0 +---@class LatLon +---@field lat number Latitude value in decimal degrees, with positive values representing north of the equator and negative values representing south. +---@field lon number Longitude value in decimal degrees, with positive values representing east of the prime meridian and negative values representing west. + +BeaconType = BeaconType or {} +--- Enumerator for types of beacons that can be activated. +---@version 1.2.4 +---@enum BeaconType +BeaconType = { + BEACON_TYPE_NULL = 0, + BEACON_TYPE_VOR = 1, + BEACON_TYPE_DME = 2, + BEACON_TYPE_VOR_DME = 3, + BEACON_TYPE_TACAN = 4, + BEACON_TYPE_VORTAC = 5, + BEACON_TYPE_RSBN = 32, + BEACON_TYPE_BROADCAST_STATION = 1024, + BEACON_TYPE_HOMER = 8, + BEACON_TYPE_AIRPORT_HOMER = 4104, + BEACON_TYPE_AIRPORT_HOMER_WITH_MARKER = 4136, + BEACON_TYPE_ILS_FAR_HOMER = 16408, + BEACON_TYPE_ILS_NEAR_HOMER = 16456, + BEACON_TYPE_ILS_LOCALIZER = 16640, + BEACON_TYPE_ILS_GLIDESLOPE = 16896, + BEACON_TYPE_NAUTICAL_HOMER = 32776 +} + +BeaconSystemName = BeaconSystemName or {} +--- Enumerator for types of beacon systems. +---@version 1.2.4 +---@enum BeaconSystemName +BeaconSystemName = { + PAR_10 = 1, + RSBN_5 = 2, + TACAN = 3, + TACAN_TANKER = 4, + ILS_LOCALIZER = 5, + ILS_GLIDESLOPE = 6, + BROADCAST_STATION = 7 +} + +--- Defines the structure of a Lua table representing an event handler object that can be registered with world.addEventHandler. +--- (Data structure definition for EventHandlerTable. Not a globally accessible table.) +---@class EventHandlerTable +---@field onEvent fun(...) Function that handles simulator events when they occur. Called with (self, event) parameters where event is of type EventData. + +--- Defines the structure of a Lua table representing spawn parameters for a static object used with coalition.addStaticObject. +--- (Data structure definition for StaticObjectSpawnData. Not a globally accessible table.) +---@class StaticObjectSpawnData +---@field name string A string identifier for the static object. +---@field _type string A string specifying the classification or model type of the static object. +---@field x number X coordinate of the static object's position in the DCS World coordinate system. +---@field y number Y coordinate of the static object's position in the DCS World coordinate system. +---@field heading number Orientation angle in radians representing the static object's heading in the DCS World. +---@field category string A string specifying the functional category of the static object. +---@field dead boolean Controls whether the static object spawns in a destroyed state. +---@field shape_name string A string identifying the 3D model resource used to render the static object. +---@field rate number A numeric value controlling the visual appearance rate, typically set to 100. +---@field canCargo boolean Controls whether the static object can be loaded as cargo. +---@field mass number A numeric value specifying the object's mass in kilograms. + +VoiceChat.Side = VoiceChat.Side or {} +--- Enumerator for coalition sides, used to define which players have access to specific voice chat rooms. +---@version 2.5.6 +---@enum VoiceChat.Side +VoiceChat.Side = { + NEUTRAL = 0, + RED = 1, + BLUE = 2, + ALL = 3 +} + +VoiceChat.RoomType = VoiceChat.RoomType or {} +--- Enumerator for voice chat room categories, used to specify the behavior and persistence of communication channels. Note: Only PERSISTENT (0) is reliable for scripted room creation. +---@version 2.5.6 +---@enum VoiceChat.RoomType +VoiceChat.RoomType = { + PERSISTENT = 0, + MULTICREW = 1, + MANAGEABLE = 2 +} + +VoiceChat.RadioHandlers = VoiceChat.RadioHandlers or {} +--- Enumerator for radio control properties, used to access and modify aircraft radio communication settings through the VoiceChat API. +---@version 2.8.0 +---@enum VoiceChat.RadioHandlers +VoiceChat.RadioHandlers = { + ON_OFF_STATUS = 0, + FREQUENCY = 1, + SOUND_VOLUME = 2, + CHANNEL = 3, + MODULATION = 4, + GUARD_STATUS = 5, + ENCRYPTION_STATUS = 6, + CRYPTO_KEY = 7, + SQUELCH_STATUS = 8, + TRANSMITTER_PWR = 9, + IS_TRANSMITTING = 10, + TRANSMISSION_ENABLED = 11, + EXTERNALLY_CONTROLLED = 12, + CURRENT_RECEIVING_RADIO = 13 +} + +VoiceChat.RadioHandlersSingletons = VoiceChat.RadioHandlersSingletons or {} +--- Enumerator for intercom system properties, used to access and modify aircraft internal communication settings through the VoiceChat API. +---@version 2.8.0 +---@enum VoiceChat.RadioHandlersSingletons +VoiceChat.RadioHandlersSingletons = { + INTERCOM_SOUND_VOLUME = 0, + INTERCOM_HOT_MIKE_STATUS = 1, + INITIALIZATION_COMPLETE = 3 +} + +country = country or {} +country.id = country.id or {} +--- Enumerator for country identifiers, used to specify nation affiliation for units and coalition forces in the DCS World. +---@enum country.id +country.id = { + RUSSIA = 0, + UKRAINE = 1, + USA = 2, + TURKEY = 3, + UK = 4, + FRANCE = 5, + GERMANY = 6, + AGGRESSORS = 7, + CANADA = 8, + SPAIN = 9, + THE_NETHERLANDS = 10, + BELGIUM = 11, + NORWAY = 12, + DENMARK = 13, + ISRAEL = 15, + GEORGIA = 16, + INSURGENTS = 17, + ABKHAZIA = 18, + SOUTH_OSETIA = 19, + ITALY = 20, + AUSTRALIA = 21, + SWITZERLAND = 22, + AUSTRIA = 23, + BELARUS = 24, + BULGARIA = 25, + CHEZH_REPUBLIC = 26, + CHINA = 27, + CROATIA = 28, + EGYPT = 29, + FINLAND = 30, + GREECE = 31, + HUNGARY = 32, + INDIA = 33, + IRAN = 34, + IRAQ = 35, + JAPAN = 36, + KAZAKHSTAN = 37, + NORTH_KOREA = 38, + PAKISTAN = 39, + POLAND = 40, + ROMANIA = 41, + SAUDI_ARABIA = 42, + SERBIA = 43, + SLOVAKIA = 44, + SOUTH_KOREA = 45, + SWEDEN = 46, + SYRIA = 47, + YEMEN = 48, + VIETNAM = 49, + VENEZUELA = 50, + TUNISIA = 51, + THAILAND = 52, + SUDAN = 53, + PHILIPPINES = 54, + MOROCCO = 55, + MEXICO = 56, + MALAYSIA = 57, + LIBYA = 58, + JORDAN = 59, + INDONESIA = 60, + HONDURAS = 61, + ETHIOPIA = 62, + CHILE = 63, + BRAZIL = 64, + BAHRAIN = 65, + THIRDREICH = 66, + YUGOSLAVIA = 67, + USSR = 68, + ITALIAN_SOCIAL_REPUBLIC = 69, + ALGERIA = 70, + KUWAIT = 71, + QATAR = 72, + OMAN = 73, + UNITED_ARAB_EMIRATES = 74, + SOUTH_AFRICA = 75, + CUBA = 76, + PORTUGAL = 77, + GDR = 78, + LEBANON = 79, + CJTF_BLUE = 80, + CJTF_RED = 81, + UN_PEACEKEEPERS = 82, + Argentina = 83, + Cyprus = 84, + Slovenia = 85, + BOLIVIA = 86, + GHANA = 87, + NIGERIA = 88, + PERU = 89, + ECUADOR = 90, + AFGHANISTAN = 91, + ["NEW ZEALAND"] = 92 +} + +country.name = country.name or {} +--- Enumerator for retrieving country names from numeric identifiers, providing a reverse lookup of country.id values. +---@enum country.name +country.name = { + ["0"] = "RUSSIA", + ["1"] = "UKRAINE", + ["2"] = "USA", + ["3"] = "TURKEY", + ["4"] = "UK", + ["5"] = "FRANCE", + ["6"] = "GERMANY", + ["7"] = "AGGRESSORS", + ["8"] = "CANADA", + ["9"] = "SPAIN", + ["10"] = "THE_NETHERLANDS", + ["11"] = "BELGIUM", + ["12"] = "NORWAY", + ["13"] = "DENMARK", + ["14"] = "ISRAEL", + ["15"] = "GEORGIA", + ["16"] = "INSURGENTS", + ["17"] = "ABKHAZIA", + ["18"] = "SOUTH_OSETIA", + ["19"] = "ITALY", + ["20"] = "AUSTRALIA", + ["21"] = "SWITZERLAND", + ["22"] = "AUSTRIA", + ["23"] = "BELARUS", + ["24"] = "BULGARIA", + ["25"] = "CHEZH_REPUBLIC", + ["26"] = "CHINA", + ["27"] = "CROATIA", + ["28"] = "EGYPT", + ["29"] = "FINLAND", + ["30"] = "GREECE", + ["31"] = "HUNGARY", + ["32"] = "INDIA", + ["33"] = "IRAN", + ["34"] = "IRAQ", + ["35"] = "JAPAN", + ["36"] = "KAZAKHSTAN", + ["37"] = "NORTH_KOREA", + ["38"] = "PAKISTAN", + ["39"] = "POLAND", + ["40"] = "ROMANIA", + ["41"] = "SAUDI_ARABIA", + ["42"] = "SERBIA", + ["43"] = "SLOVAKIA", + ["44"] = "SOUTH_KOREA", + ["45"] = "SWEDEN", + ["46"] = "SYRIA", + ["47"] = "YEMEN", + ["48"] = "VIETNAM", + ["49"] = "VENEZUELA", + ["50"] = "TUNISIA", + ["51"] = "THAILAND", + ["52"] = "SUDAN", + ["53"] = "PHILIPPINES", + ["54"] = "MOROCCO", + ["55"] = "MEXICO", + ["56"] = "MALAYSIA", + ["57"] = "LIBYA", + ["58"] = "JORDAN", + ["59"] = "INDONESIA", + ["60"] = "HONDURAS", + ["61"] = "ETHIOPIA", + ["62"] = "CHILE", + ["63"] = "BRAZIL", + ["64"] = "BAHRAIN", + ["65"] = "THIRDREICH", + ["66"] = "YUGOSLAVIA", + ["67"] = "USSR", + ["68"] = "ITALIAN_SOCIAL_REPUBLIC", + ["69"] = "ALGERIA", + ["70"] = "KUWAIT", + ["71"] = "QATAR", + ["72"] = "OMAN", + ["73"] = "UNITED_ARAB_EMIRATES", + ["74"] = "SOUTH_AFRICA", + ["75"] = "CUBA", + ["76"] = "PORTUGAL", + ["77"] = "GDR", + ["78"] = "LEBANON", + ["79"] = "CJTF_BLUE", + ["80"] = "CJTF_RED", + ["81"] = "UN_PEACEKEEPERS", + ["82"] = "ARGENTINA", + ["83"] = "CYPRUS", + ["84"] = "SLOVENIA", + ["85"] = "BOLIVIA", + ["86"] = "GHANA", + ["87"] = "NIGERIA", + ["88"] = "PERU", + ["89"] = "ECUADOR", + ["90"] = "AFGHANISTAN", + ["91"] = "NEW_ZEALAND" +} + +--- Defines the structure of a Lua table detailing configuration options for a group attack mission. +--- (Data structure definition for TaskAttackGroupParams. Not a globally accessible table.) +---@class TaskAttackGroupParams +---@field groupId number Unique ID of the group to attack. +---@field weaponType number Weapon flag type to use for the attack. +---@field expend string Quantity of weapons to expend during the attack (QUARTER, TWO, ONE, FOUR, HALF, ALL). +---@field directionEnabled boolean Determines whether to use the specified direction for attack. +---@field direction number Attack direction in radians, defining the approach vector. +---@field altitudeEnabled boolean Determines whether to use the specified altitude for attack. +---@field altitude number Attack altitude in meters. +---@field attackQtyLimit boolean Determines whether to use the attackQty parameter as a limit. +---@field attackQty number Number of attack passes the group will perform on the target. + +--- Defines the structure of a Lua table detailing configuration options for a unit attack mission. +--- (Data structure definition for TaskAttackUnitParams. Not a globally accessible table.) +---@class TaskAttackUnitParams +---@field unitId number Unique ID of the unit to attack. +---@field weaponType number Weapon flag type to use for the attack. +---@field expend string Quantity of weapons to expend during the attack (QUARTER, TWO, ONE, FOUR, HALF, ALL). +---@field direction number Attack direction in radians, defining the approach vector. +---@field attackQtyLimit boolean Determines whether to use the attackQty parameter as a limit. +---@field attackQty number Number of attack passes the group will perform on the target. +---@field groupAttack boolean Determines whether each aircraft in the group will attack individually (true) or as a coordinated unit (false). + +TaskId = TaskId or {} +--- Enumerator for task identifiers used to assign specific behaviors to Controller entities in the DCS World. +---@enum TaskId +TaskId = { + TaskAttackGroup = "AttackGroup", + TaskAttackUnit = "AttackUnit", + TaskBombing = "Bombing", + TaskStrafing = "Strafing", + TaskCarpetBombing = "CarpetBombing", + TaskAttackMapObject = "AttackMapObject", + TaskBombingRunway = "BombingRunway", + TaskOrbit = "orbit", + TaskRefueling = "refueling", + TaskLand = "land", + TaskFollow = "follow", + TaskFollowBigFormation = "followBigFormation", + TaskEscort = "escort", + TaskEmbarking = "Embarking", + TaskFireAtPoint = "fireAtPoint", + TaskHold = "hold", + TaskFACAttackGroup = "FAC_AttackGroup", + TaskEmbarkToTransport = "EmbarkToTransport", + TaskDisembarkFromTransport = "DisembarkFromTransport", + TaskCargoTransportation = "CargoTransportation", + TaskGoToWaypoint = "goToWaypoint", + TaskGroundEscort = "groundEscort", + TaskRecoveryTanker = "RecoveryTanker" +} + +--- Parameters for the BombingRunway task +--- (Data structure definition for TaskBombingRunwayParams. Not a globally accessible table.) +---@class TaskBombingRunwayParams +---@field runwayId number Index of the airbase for which is to be bombed +---@field weaponType number Weapon flag type to use for the attack +---@field expend string Quantity of weapons to expend (QUARTER, TWO, ONE, FOUR, HALF, ALL) +---@field attackQty number Number of times the group will attack the target +---@field attackQtyLimit boolean Whether to use the attackQty parameter +---@field direction number If provided the AI will attack from this azimuth and ignore bombing along the length of the runway +---@field groupAttack boolean If true then each aircraft in the group will attack the runway + +--- Defines the structure of a Lua table detailing configuration options for a naval recovery tanker mission. +--- (Data structure definition for TaskRecoveryTankerParams. Not a globally accessible table.) +---@class TaskRecoveryTankerParams +---@field groupId number The group ID of the naval group to follow and provide tanker services to. +---@field speed number Speed of the tanker in meters per second while performing its orbit pattern. +---@field altitude number Altitude in meters at which the tanker will orbit above the naval group. +---@field lastWptIndexFlag boolean Determines whether the tanker task should terminate when the naval group reaches a specific waypoint. +---@field lastWptIndex number Waypoint index of the naval group that, when reached, will cause the recovery tanker to end its task. + +--- Defines the structure of a Lua table for hold parameters, which is currently empty as the task requires no additional configuration. +--- (Data structure definition for TaskHoldParams. Not a globally accessible table.) +---@class TaskHoldParams + +--- Defines the structure of a Lua table for refueling parameters, which is currently empty as the task requires no additional configuration. +--- (Data structure definition for TaskRefuelingParams. Not a globally accessible table.) +---@class TaskRefuelingParams + +--- Defines the structure of a Lua table containing the action that will be executed as a task. +--- (Data structure definition for WrappedActionParams. Not a globally accessible table.) +---@class WrappedActionParams +---@field action table The command or action object to be executed as a task. + +--- Defines the structure of a Lua table detailing the collection of tasks to be executed as part of a combination. +--- (Data structure definition for ComboTaskParams. Not a globally accessible table.) +---@class ComboTaskParams +---@field tasks table A numerically indexed table of task objects to be executed. + +--- Defines the structure of a Lua table containing an ordered sequence of waypoints that form a route. +--- (Data structure definition for MissionRoute. Not a globally accessible table.) +---@class MissionRoute +---@field points table A numerically indexed table of waypoint objects that define the mission route. + +--- Defines the structure of a Lua table representing a single navigation point within a mission route. +--- (Data structure definition for MissionWaypoint. Not a globally accessible table.) +---@class MissionWaypoint +---@field _type string Waypoint type identifier (e.g., 'TakeOff', 'Land', 'Turning Point'). +---@field airdromeId number Unique identifier of the airdrome for takeoff or landing waypoints. +---@field timeReFuAr number Time in minutes allocated for refueling and rearming at an airdrome. +---@field helipadId number Unique identifier of the helipad for helicopter operations. +---@field linkUnit number Unique identifier of the linked unit (same as helipadId but required for certain operations). +---@field action string Turn method the aircraft will use when approaching this waypoint. +---@field x number X coordinate of the waypoint in the DCS World coordinate system. +---@field y number Y coordinate of the waypoint in the DCS World coordinate system. +---@field alt number Altitude of the waypoint in meters. +---@field alt_type string Altitude measurement reference ('RADIO' for AGL, 'BARO' for MSL). +---@field speed number Speed in meters per second the aircraft will maintain at this waypoint. +---@field speed_locked boolean Determines whether the speed value is fixed and cannot be optimized by AI. +---@field ETA number Estimated time of arrival at the waypoint in seconds from mission start. +---@field ETA_locked boolean Determines whether the ETA value is fixed and AI must adjust speed to meet it. +---@field name string Descriptive name of the waypoint for identification purposes. +---@field task table Task to be performed when the aircraft reaches this waypoint. + +--- Defines the structure of a Lua table detailing conditions that must be met for a task to start. +--- (Data structure definition for ControlledTaskCondition. Not a globally accessible table.) +---@class ControlledTaskCondition +---@field time number Time in seconds since mission start when the task should begin. +---@field userFlag string Flag identifier to check for task activation. +---@field userFlagValue boolean Required value of the user flag to activate the task. +---@field probability number Probability (0-100) that the task will execute when conditions are met. + +--- Defines the structure of a Lua table detailing conditions that will trigger task termination. +--- (Data structure definition for ControlledTaskStopCondition. Not a globally accessible table.) +---@class ControlledTaskStopCondition +---@field time number Time in seconds since mission start when the task should terminate. +---@field userFlag string Flag identifier to check for task termination. +---@field userFlagValue boolean Required value of the user flag to terminate the task. +---@field duration number Duration in seconds that the task will run before terminating. +---@field lastWaypoint number Waypoint number that, when reached, will terminate the task. + +--- Defines the structure of a Lua table containing parameters for the AWACS task (no parameters required). +--- (Data structure definition for TaskEnRouteAWACSParams. Not a globally accessible table.) +---@class TaskEnRouteAWACSParams + +TaskEnRouteId = TaskEnRouteId or {} +--- Enumerator for en-route task identifiers that can be assigned to Controller entities but are not globally accessible. +---@enum TaskEnRouteId +TaskEnRouteId = { + TaskEnRouteEngageTargets = "engageTargets", + TaskEnRouteEngageTargetsInZone = "engageTargetsInZone", + TaskEnRouteEngageGroup = "engageGroup", + TaskEnRouteEngageUnit = "engageUnit", + TaskEnRouteAWACS = "awacs", + TaskEnRouteTanker = "tanker", + TaskEnRouteEWR = "ewr", + TaskEnRouteFACEngageGroup = "FAC_engageGroup", + TaskEnRouteFAC = "FAC" +} + +--- Defines the structure of a Lua table containing parameters for the engageUnit task. +--- (Data structure definition for TaskEnRouteEngageUnitParams. Not a globally accessible table.) +---@class TaskEnRouteEngageUnitParams +---@field unitId number Unique identifier of the target unit. +---@field weaponType number Defines the preferred weapon type to engage the enemy. +---@field expend string Defines how many munitions the AI will expend per attack run (QUARTER, TWO, ONE, FOUR, HALF, ALL). +---@field attackQty number Number of times the group will attack if the target is still alive and AI still have ammo. +---@field direction number Defines the direction from which the flight will engage from (in radians). +---@field attackQtyLimit boolean Determines if the attack quantity limit is enabled. +---@field groupAttack boolean If true, each aircraft in the group will attack the unit. +---@field priority number The priority of the tasking, where lower numbers indicate higher importance (default: 0). + +--- Defines the structure of a Lua table containing parameters for the EWR task (no parameters required). +--- (Data structure definition for TaskEnRouteEWRParams. Not a globally accessible table.) +---@class TaskEnRouteEWRParams + +--- Defines the structure of a Lua table containing parameters for the engageTargets task. +--- (Data structure definition for TaskEnRouteEngageTargetsParams. Not a globally accessible table.) +---@class TaskEnRouteEngageTargetsParams +---@field targetTypes table Table of target categories to engage. +---@field priority number Priority of the task, where lower numbers indicate higher importance. +---@field value string The value of the task. +---@field maxDistEnabled boolean Whether to use the maxDist parameter. +---@field maxDist number Maximum distance to search for targets in meters. +---@field maxAltEnabled boolean Whether to use the maxAlt parameter. +---@field maxAlt number Maximum altitude for targets in meters. +---@field minAltEnabled boolean Whether to use the minAlt parameter. +---@field minAlt number Minimum altitude for targets in meters. + +--- Defines the structure of a Lua table containing parameters for the tanker task (no parameters required). +--- (Data structure definition for TaskEnRouteTankerParams. Not a globally accessible table.) +---@class TaskEnRouteTankerParams + +--- Defines the structure of a Lua table containing parameters for the engageGroup task. +--- (Data structure definition for TaskEnRouteEngageGroupParams. Not a globally accessible table.) +---@class TaskEnRouteEngageGroupParams +---@field groupId number Unique identifier for the target group. +---@field weaponType number Defines the preferred weapon type to engage the enemy. +---@field expend string Defines how many munitions the AI will expend per attack run (QUARTER, TWO, ONE, FOUR, HALF, ALL). +---@field attackQty number Number of times the group will attack if the target is still alive and AI still have ammo. +---@field direction number Defines the direction from which the flight will engage from (in radians). +---@field attackQtyLimit boolean Determines if the attack quantity limit is enabled. +---@field priority number The priority of the tasking, where lower numbers indicate higher importance (default: 0). + +--- Defines the structure of a Lua table containing parameters for the radio message transmission command. +--- (Data structure definition for CommandTransmitMessageParams. Not a globally accessible table.) +---@class CommandTransmitMessageParams +---@field file string Path to the sound file that will be played as the radio transmission. +---@field duration number Display time in seconds for the message subtitles (ignored when loop is true). +---@field subtitle string Text displayed in the radio message queue representing the transmission content. +---@field loop boolean Transmission mode where true causes the message to repeat continuously until stopped. + +--- Defines the structure of a Lua table containing parameters for the EPLRS data link toggle command. +--- (Data structure definition for CommandEPLRSParams. Not a globally accessible table.) +---@class CommandEPLRSParams +---@field value boolean EPLRS state where true activates the data link, false deactivates it. +---@field groupId number Track number assigned to the first unit in the group (only relevant for ground vehicle groups). + +--- Defines the structure of a Lua table containing parameters for the action switching command. +--- (Data structure definition for CommandSwitchActionParams. Not a globally accessible table.) +---@class CommandSwitchActionParams +---@field actionIndex number Index of the target action in the group's task queue to make active. + +--- Defines the structure of a Lua table containing parameters for the group activation command. +--- (Data structure definition for CommandStartParams. Not a globally accessible table.) +---@class CommandStartParams + +--- Defines the structure of a Lua table containing parameters for the ICLS beacon deactivation command. +--- (Data structure definition for CommandDeactivateICLSParams. Not a globally accessible table.) +---@class CommandDeactivateICLSParams + +--- Defines the structure of a Lua table containing parameters for the Link 4 activation command. +--- (Data structure definition for CommandActivateLink4Params. Not a globally accessible table.) +---@class CommandActivateLink4Params +---@field unitId number ID of the ship unit that will broadcast the Link 4 signal (must have Link 4 capability). +---@field frequency number Operating frequency in Hertz for the data link communications. +---@field name string Descriptive name of the Link 4 system for identification purposes only. + +--- Defines the structure of a Lua table containing parameters for the ACLS activation command. +--- (Data structure definition for CommandActivateACLSParams. Not a globally accessible table.) +---@class CommandActivateACLSParams +---@field unitId number ID of the ship unit that will provide the ACLS functionality (requires carrier with appropriate systems). +---@field name string Descriptive name of the ACLS for identification purposes only. + +--- Defines the structure of a Lua table containing parameters for the ICLS beacon activation. +--- (Data structure definition for CommandActivateICLSParams. Not a globally accessible table.) +---@class CommandActivateICLSParams +---@field _type number Fixed value of 131584 identifying an ICLS beacon type. +---@field channel number ICLS channel number (1-20) that aircraft will tune to for landing guidance. +---@field unitId number ID of the ship unit that will broadcast the ICLS beacon signal. +---@field name string Descriptive name of the ICLS beacon for identification purposes only. + +--- Defines the structure of a Lua table containing parameters for the beacon deactivation command. +--- (Data structure definition for CommandDeactivateBeaconParams. Not a globally accessible table.) +---@class CommandDeactivateBeaconParams + +--- Defines the structure of a Lua table containing parameters for the transmission termination command. +--- (Data structure definition for CommandStopTransmissionParams. Not a globally accessible table.) +---@class CommandStopTransmissionParams + +--- Defines the structure of a Lua table containing parameters for the smoke toggle command. +--- (Data structure definition for CommandSmokeOnOffParams. Not a globally accessible table.) +---@class CommandSmokeOnOffParams +---@field value boolean Smoke state where true activates smoke emission, false deactivates it. + +--- Defines the structure of a Lua table containing parameters for the immortality toggle command. +--- (Data structure definition for CommandSetImmortalParams. Not a globally accessible table.) +---@class CommandSetImmortalParams +---@field value boolean Immortality state where true makes the group immune to all damage, false restores normal vulnerability. + +--- Defines the structure of a Lua table containing parameters for the unlimited fuel toggle command. +--- (Data structure definition for CommandSetUnlimitedFuelParams. Not a globally accessible table.) +---@class CommandSetUnlimitedFuelParams +---@field value boolean Fuel state where true prevents fuel depletion during operation, false restores normal fuel consumption. + +--- Defines the structure of a Lua table containing parameters for the route control command. +--- (Data structure definition for CommandStopRouteParams. Not a globally accessible table.) +---@class CommandStopRouteParams +---@field value boolean Movement state where true halts the group in place, false allows it to resume following its route. + +--- Defines the structure of a Lua table containing parameters for the Link 4 deactivation command. +--- (Data structure definition for CommandDeactivateLink4Params. Not a globally accessible table.) +---@class CommandDeactivateLink4Params + +--- Defines the structure of a Lua table containing parameters for the invisibility toggle command. +--- (Data structure definition for CommandSetInvisibleParams. Not a globally accessible table.) +---@class CommandSetInvisibleParams +---@field value boolean Invisibility state where true makes the group undetectable by enemy AI, false restores normal detection. + +CommandId = CommandId or {} +--- Enumerator for command identifiers that can be sent to Controller entities, used to specify the type of command being issued. +---@enum CommandId +CommandId = { + CommandScript = "script", + CommandSetCallsign = "setCallsign", + CommandSetFrequency = "setFrequency", + CommandSetFrequencyForUnit = "setFrequencyForUnit", + CommandSwitchWaypoint = "switchWaypoint", + CommandStopRoute = "stopRoute", + CommandSwitchAction = "switchAction", + CommandSetInvisible = "setInvisible", + CommandSetImmortal = "setImmortal", + CommandSetUnlimitedFuel = "setUnlimitedFuel", + CommandActivateBeacon = "ActivateBeacon", + CommandDeactivateBeacon = "DeactivateBeacon", + CommandActivateICLS = "ActivateICLS", + CommandDeactivateICLS = "DeactivateICLS", + CommandEPLRS = "EPLRS", + CommandStart = "start", + CommandTransmitMessage = "transmitMessage", + CommandStopTransmission = "stopTransmission", + CommandSmokeOnOff = "smoke_on_off", + CommandActivateLink4 = "ActivateLink4", + CommandDeactivateLink4 = "DeactivateLink4", + CommandActivateACLS = "ActivateACLS", + CommandDeactivateACLS = "DeactivateACLS", + CommandLoadingShip = "LoadingShip" +} + +--- Defines the structure of a Lua table containing parameters for the ship cargo loading command. +--- (Data structure definition for CommandLoadingShipParams. Not a globally accessible table.) +---@class CommandLoadingShipParams +---@field cargo number Cargo load percentage (0-100) determining how much the ship sits in water (lower values raise the waterline). +---@field unitId number ID of the ship unit whose cargo load level will be modified. + +--- Defines the structure of a Lua table containing parameters for the ACLS deactivation command. +--- (Data structure definition for CommandDeactivateACLSParams. Not a globally accessible table.) +---@class CommandDeactivateACLSParams + +--- Defines the structure of a Lua table containing parameters for the unit-specific frequency change command. +--- (Data structure definition for CommandSetFrequencyForUnitParams. Not a globally accessible table.) +---@class CommandSetFrequencyForUnitParams +---@field frequency number Radio frequency in Hertz (note: mission editor displays MHz, multiply by 1,000,000 to convert). +---@field modulation number Radio modulation type (0 = AM, 1 = FM). +---@field power number Radio transmit power in watts, determining broadcast range. +---@field unitId number ID of the specific unit within the group whose radio frequency will be modified. + +--- Defines the structure of a Lua table containing parameters for the callsign change command. +--- (Data structure definition for CommandSetCallsignParams. Not a globally accessible table.) +---@class CommandSetCallsignParams +---@field callname number Callsign name identifier (varies by unit type; 1-19 for JTAC units per Callsigns_JTAC enum). +---@field number number Numeric suffix for the callsign (1-9), used to distinguish between units with the same callname. + +--- Defines the structure of a Lua table containing parameters for the frequency change command. +--- (Data structure definition for CommandSetFrequencyParams. Not a globally accessible table.) +---@class CommandSetFrequencyParams +---@field frequency number Radio frequency in Hertz (note: mission editor displays MHz, multiply by 1,000,000 to convert). +---@field modulation number Radio modulation type (0 = AM, 1 = FM). +---@field power number Radio transmit power in watts, determining broadcast range. + +--- Defines the structure of a Lua table containing parameters for the waypoint switching command. +--- (Data structure definition for CommandSwitchWaypointParams. Not a globally accessible table.) +---@class CommandSwitchWaypointParams +---@field fromWaypointIndex number Index of the waypoint where the group will begin its new route leg. +---@field goToWaypointIndex number Index of the destination waypoint the group will navigate toward. + +--- Defines the structure of a Lua table containing parameters for the script execution command. +--- (Data structure definition for CommandScriptParams. Not a globally accessible table.) +---@class CommandScriptParams +---@field command string Lua code string to be executed within the group's context. + +Spot.Category = Spot.Category or {} +--- Defines the types of targeting beams available in the DCS World environment. +---@enum Spot.Category +Spot.Category = { + INFRA_RED = 0, + LASER = 1 +} + +env.Mode = env.Mode or {} +--- Enumerator for mission execution lifecycle states, used to determine the current operational phase of a mission in the DCS World environment. +---@enum env.Mode +env.Mode = { + INIT = 0, + USER = 1, + START = 2, + SIMULATION = 4, + STOP = 5, + FINISH = 6 +} + +Object.Category = Object.Category or {} +--- Defines the fundamental categories of objects in the DCS World environment. +---@enum Object.Category +Object.Category = { + VOID = 0, + UNIT = 1, + WEAPON = 2, + STATIC = 3, + BASE = 4, + SCENERY = 5, + CARGO = 6 +} + +--- Defines the structure of a table containing attribute flags for an object. Each field is a boolean indicating whether the object has that specific attribute. +--- (Data structure definition for ObjectAttributes. Not a globally accessible table.) +---@class ObjectAttributes + +land.SurfaceType = land.SurfaceType or {} +--- Defines terrain surface types in the DCS World environment. +---@enum land.SurfaceType +land.SurfaceType = { + LAND = 1, + SHALLOW_WATER = 2, + WATER = 3, + ROAD = 4, + RUNWAY = 5 +} + +coalition.side = coalition.side or {} +--- Enumerator for coalition sides, used to identify the different factions in the DCS World environment. +---@enum coalition.side +coalition.side = { + NEUTRAL = 0, + RED = 1, + BLUE = 2 +} + +coalition.service = coalition.service or {} +--- Enumerator for coalition service types, used to categorize radio communication services available to each faction. +---@enum coalition.service +coalition.service = { + ATC = 0, + AWACS = 1, + TANKER = 2, + FAC = 3, + MAX = 4 +} + +--- Defines the structure of a Lua table containing information about a player-controllable slot in a mission, including its unit ID, type, role, and other identifying data. +--- (Data structure definition for DCSAvailableSlotInfo. Not a globally accessible table.) +---@version 1.2.0 +---@class DCSAvailableSlotInfo + +Controller.Detection = Controller.Detection or {} +--- Enumerator for detection method types, used to specify or filter how controllers detect targets in the DCS World environment. +---@enum Controller.Detection +Controller.Detection = { + VISUAL = 1, + OPTIC = 2, + RADAR = 4, + IRST = 8, + RWR = 16, + DLINK = 32 +} + +LiquidType = LiquidType or {} +--- Enumerator for liquid fuel types, used to specify particular fuels within a `Warehouse` inventory. +---@enum LiquidType +LiquidType = { + jetfuel = 0, + Aviation_gasoline = 1, + MW50 = 2, + Diesel = 3 +} + +StaticObject.Category = StaticObject.Category or {} +--- Defines the categories of static objects in the DCS World environment. +---@enum StaticObject.Category +StaticObject.Category = { + VOID = 0, + UNIT = 1, + WEAPON = 2, + STATIC = 3, + BASE = 4, + SCENERY = 5, + CARGO = 6 +} + +world.BirthPlace = world.BirthPlace or {} +--- Enumerator for aircraft and helicopter spawn locations, used in birth events. +---@enum world.BirthPlace +world.BirthPlace = { + wsBirthPlace_Air = 1, + wsBirthPlace_RunWay = 4, + wsBirthPlace_Park = 5, + wsBirthPlace_Heliport_Hot = 9, + wsBirthPlace_Heliport_Cold = 10, + wsBirthPlace_Ship = 3, + wsBirthPlace_Ship_Hot = 12, + wsBirthPlace_Ship_Cold = 11 +} + +world.VolumeType = world.VolumeType or {} +--- Enumerator for 3D volume types used in spatial queries within the DCS World. +---@enum world.VolumeType +world.VolumeType = { + SEGMENT = 0, + BOX = 1, + SPHERE = 2, + PYRAMID = 3 +} + +world.event = world.event or {} +--- Enumerator for event types that occur during mission execution in the DCS World. +---@version 1.2.0 +---@enum world.event +world.event = { + S_EVENT_INVALID = 0, + S_EVENT_SHOT = 1, + S_EVENT_HIT = 2, + S_EVENT_TAKEOFF = 3, + S_EVENT_LAND = 4, + S_EVENT_CRASH = 5, + S_EVENT_EJECTION = 6, + S_EVENT_REFUELING = 7, + S_EVENT_DEAD = 8, + S_EVENT_PILOT_DEAD = 9, + S_EVENT_BASE_CAPTURED = 10, + S_EVENT_MISSION_START = 11, + S_EVENT_MISSION_END = 12, + S_EVENT_TOOK_CONTROL = 13, + S_EVENT_REFUELING_STOP = 14, + S_EVENT_BIRTH = 15, + S_EVENT_HUMAN_FAILURE = 16, + S_EVENT_DETAILED_FAILURE = 17, + S_EVENT_ENGINE_STARTUP = 18, + S_EVENT_ENGINE_SHUTDOWN = 19, + S_EVENT_PLAYER_ENTER_UNIT = 20, + S_EVENT_PLAYER_LEAVE_UNIT = 21, + S_EVENT_PLAYER_COMMENT = 22, + S_EVENT_SHOOTING_START = 23, + S_EVENT_SHOOTING_END = 24, + S_EVENT_MARK_ADDED = 25, + S_EVENT_MARK_CHANGE = 26, + S_EVENT_MARK_REMOVED = 27, + S_EVENT_KILL = 28, + S_EVENT_SCORE = 29, + S_EVENT_UNIT_LOST = 30, + S_EVENT_LANDING_AFTER_EJECTION = 31, + S_EVENT_PARATROOPER_LENDING = 32, + S_EVENT_DISCARD_CHAIR_AFTER_EJECTION = 33, + S_EVENT_WEAPON_ADD = 34, + S_EVENT_TRIGGER_ZONE = 35, + S_EVENT_LANDING_QUALITY_MARK = 36, + S_EVENT_BDA = 37, + S_EVENT_AI_ABORT_MISSION = 38, + S_EVENT_DAYNIGHT = 39, + S_EVENT_FLIGHT_TIME = 40, + S_EVENT_PLAYER_SELF_KILL_PILOT = 41, + S_EVENT_PLAYER_CAPTURE_AIRFIELD = 42, + S_EVENT_EMERGENCY_LANDING = 43, + S_EVENT_UNIT_CREATE_TASK = 44, + S_EVENT_UNIT_DELETE_TASK = 45, + S_EVENT_SIMULATION_START = 46, + S_EVENT_WEAPON_REARM = 47, + S_EVENT_WEAPON_DROP = 48, + S_EVENT_UNIT_TASK_COMPLETE = 49, + S_EVENT_UNIT_TASK_STAGE = 50, + S_EVENT_MAC_EXTRA_SCORE = 51, + S_EVENT_MISSION_RESTART = 52, + S_EVENT_MISSION_WINNER = 53, + S_EVENT_RUNWAY_TAKEOFF = 54, + S_EVENT_RUNWAY_TOUCH = 55, + S_EVENT_MAC_LMS_RESTART = 56, + S_EVENT_SIMULATION_FREEZE = 57, + S_EVENT_SIMULATION_UNFREEZE = 58, + S_EVENT_HUMAN_AIRCRAFT_REPAIR_START = 59, + S_EVENT_HUMAN_AIRCRAFT_REPAIR_FINISH = 60, + S_EVENT_MAX = 61 +} + +--- Represents all Objects those may belong to a coalition: units, airbases, static objects, weapon. Non-final class. +--- (Data structure definition for CoalitionObject. Not a globally accessible table.) +---@version 1.2.4 +---@class CoalitionObject +---@field getCoalition fun(...) Returns an enumerator that defines the coalition that an object currently belongs to. +---@field getCountry fun(...) Returns an enumerator that defines the country that an object currently belongs to. + +Group.Category = Group.Category or {} +--- Enumerator for group categories, used to classify different types of unit collections in the DCS World environment. +---@enum Group.Category +Group.Category = { + AIRPLANE = 0, + HELICOPTER = 1, + GROUND = 2, + SHIP = 3, + TRAIN = 4 +} + +Airbase.Category = Airbase.Category or {} +--- Enumerator for airbase category types. +---@enum Airbase.Category +Airbase.Category = { + AIRDROME = 0, + HELIPAD = 1, + SHIP = 2 +} + +--- Information about an airbase parking spot. +--- (Data structure definition for AirbaseParking. Not a globally accessible table.) +---@class AirbaseParking +---@field Term_Type number Terminal type identifier. +---@field Term_Index number Terminal index number. +---@field Term_Index_0 number Alternative terminal index. +---@field Term_Details table Additional details about the terminal. + +--- Detailed information about an airbase. +--- (Data structure definition for AirbaseDesc. Not a globally accessible table.) +---@class AirbaseDesc +---@field category number Category identifier of the airbase. +---@field id number Unique identifier for the airbase. +---@field callsign string Radio callsign of the airbase. +---@field display_name string Human-readable name of the airbase. + +--- Parameters for the AttackMapObject task +--- (Data structure definition for TaskAttackMapObjectParams. Not a globally accessible table.) +---@class TaskAttackMapObjectParams +---@field point Vec2 Vec2 coordinate of the target point +---@field x number X coordinate of the target (alternative to point) +---@field y number Y coordinate of the target (alternative to point) +---@field weaponType number Weapon flag type to use for the attack +---@field expend string Quantity of weapons to expend (QUARTER, TWO, ONE, FOUR, HALF, ALL) +---@field attackQty number Number of times the group will attack if the target +---@field attackQtyLimit boolean If true the attackQty value will be followed +---@field direction number Attack direction in radians +---@field groupAttack boolean If true then each aircraft in the group will attack the target + +--- Parameters for the Bombing task +--- (Data structure definition for TaskBombingParams. Not a globally accessible table.) +---@class TaskBombingParams +---@field point Vec2 Vec2 coordinate of the target +---@field x number X coordinate of the target (alternative to point) +---@field y number Y coordinate of the target (alternative to point) +---@field weaponType number Weapon flag type to use for the attack +---@field expend string Quantity of weapons to expend (QUARTER, TWO, ONE, FOUR, HALF, ALL) +---@field attackQty number Number of times the group will attack the target +---@field attackQtyLimit boolean Whether to use the attackQty parameter +---@field direction number Attack direction in radians +---@field groupAttack boolean If true then each aircraft in the group will attack the point +---@field altitude number Altitude in meters for the attack +---@field altitudeEnabled boolean Whether to use the altitude parameter +---@field attackType string Attack profile to use (e.g. 'Dive' for dive bombing) + +--- Parameters for the CarpetBombing task +--- (Data structure definition for TaskCarpetBombingParams. Not a globally accessible table.) +---@class TaskCarpetBombingParams +---@field attackType string Type of attack, typically 'Carpet' +---@field carpetLength number Distance in meters the pattern should cover +---@field point Vec2 Vec2 coordinate of the target +---@field x number X coordinate of the target (alternative to point) +---@field y number Y coordinate of the target (alternative to point) +---@field weaponType number Weapon flag type to use for the attack +---@field expend string Quantity of weapons to expend (QUARTER, TWO, ONE, FOUR, HALF, ALL) +---@field attackQty number Number of times the group will attack the target +---@field attackQtyLimit boolean Whether to use the attackQty parameter +---@field groupAttack boolean If true then each aircraft in the group will attack the point +---@field altitude number Altitude in meters for the attack +---@field altitudeEnabled boolean Whether to use the altitude parameter + +--- Defines the structure of a Lua table containing parameters for the engageTargetsInZone task. +--- (Data structure definition for TaskEnRouteEngageTargetsInZoneParams. Not a globally accessible table.) +---@class TaskEnRouteEngageTargetsInZoneParams +---@field point Vec2 A `Vec2` point defining the center of the area the group will engage targets within in the DCS World coordinate system. +---@field zoneRadius number Radius in meters defining the size of the area the group will engage targets within. +---@field targetTypes table Table of attribute names that define valid targets. +---@field priority number The priority of the tasking, where lower numbers indicate higher importance (default: 0). + +--- Parameters for the fireAtPoint task +--- (Data structure definition for TaskFireAtPointParams. Not a globally accessible table.) +---@class TaskFireAtPointParams +---@field point Vec2 Vec2 coordinate to define where the AI will aim +---@field x number X coordinate of the target (alternative to point) +---@field y number Y coordinate of the target (alternative to point) +---@field radius number Optional radius in meters that defines the area AI will attempt to hit +---@field expendQty number Specifies number of shots to be fired +---@field expendQtyEnabled boolean Whether or not expendQty will be used +---@field weaponType number Weapon flag type to use for the attack +---@field altitude number If present the task will be focused on shooting at the specified altitude for the point +---@field alt_type number Determines if the altitude is defined by AGL (1) or MSL (0) +---@field counterbattaryRadius number The radius in meters from the group leader that the group will move in random directions after completing the fireAtPoint task + +--- Defines the structure of a Lua table detailing configuration options for a helicopter landing operation. +--- (Data structure definition for TaskLandParams. Not a globally accessible table.) +---@class TaskLandParams +---@field point Vec2 A `Vec2` representing the landing coordinates in the DCS World coordinate system where the helicopter will attempt to touch down. +---@field durationFlag boolean Determines whether the helicopter will remain on the ground for a specific duration before taking off again. +---@field duration number Time in seconds that the helicopter will remain landed before automatically taking off if durationFlag is true. + +--- Defines the structure of a Lua table detailing configuration options for aircraft orbit patterns and behavior. +--- (Data structure definition for TaskOrbitParams. Not a globally accessible table.) +---@class TaskOrbitParams +---@field pattern string Type of orbit pattern the AI will execute (RACE_TRACK, CIRCLE, Anchored). +---@field point Vec2 A `Vec2` representing the primary orbit point in the DCS World coordinate system. +---@field point2 Vec2 A `Vec2` representing the secondary point for a Race-Track orbit pattern in the DCS World coordinate system. +---@field speed number Speed in meters per second the AI will maintain during the orbit pattern. +---@field altitude number Altitude in meters the AI will maintain during the orbit. +---@field hotLegDir number Heading in radians that the aircraft will fly for the return leg of the anchored orbit pattern. +---@field legLength number Distance in meters that the aircraft will fly before turning in an anchored orbit pattern. +---@field width number Distance in meters that represents the diameter of the anchored orbit pattern. +---@field clockWise boolean Determines whether the anchored orbit will fly clockwise (true) or anti-clockwise (false). + +--- Defines the structure of a Lua table detailing configuration options for a strafing attack mission. +--- (Data structure definition for TaskStrafingParams. Not a globally accessible table.) +---@class TaskStrafingParams +---@field point Vec2 A `Vec2` representing the target coordinates in the DCS World coordinate system. +---@field x number X coordinate of the target (alternative to using the point field). +---@field y number Y coordinate of the target (alternative to using the point field). +---@field weaponType number Weapon flag type to use for the attack. +---@field expend string Quantity of weapons to expend during the attack (QUARTER, TWO, ONE, FOUR, HALF, ALL). +---@field attackQty number Number of attack passes the group will perform on the target. +---@field attackQtyLimit boolean Determines whether to use the attackQty parameter as a limit. +---@field direction number Attack direction in radians, defining the approach vector. +---@field directionEnabled boolean Determines whether to use the specified direction for attack. +---@field groupAttack boolean Determines whether each aircraft in the group will attack individually (true) or as a coordinated unit (false). +---@field length number Total length of the strafing target area in meters. + +--- Represents a numerically indexed Lua table (sequence) of `Vec2` points in the DCS World coordinate system. +---@alias Vec2Array Vec2[] + +--- Information about an airbase runway. +--- (Data structure definition for AirbaseRunway. Not a globally accessible table.) +---@class AirbaseRunway +---@field course number Runway heading in degrees. +---@field name string Runway identifier. +---@field position Vec3 Runway position in world coordinates. +---@field width number Runway width in meters. +---@field length number Runway length in meters. + +--- Defines the structure of a Lua table representing a 3D bounding box in the DCS World coordinate system, specified by minimum and maximum corner points. +--- (Data structure definition for Box3. Not a globally accessible table.) +---@class Box3 +---@field min Vec3 A `Vec3` representing the minimum corner point (lowest x, y, z values) of the bounding box in the DCS World coordinate system. +---@field max Vec3 A `Vec3` representing the maximum corner point (highest x, y, z values) of the bounding box in the DCS World coordinate system. + +--- Base cargo object used in cargo-related events. +--- (Data structure definition for Cargo. Not a globally accessible table.) +---@class Cargo +---@field mass number Mass of the cargo in kilograms. +---@field position Vec3 A `Vec3` representing the cargo's current position in the DCS World coordinate system. +---@field displayName string The human-readable name of the cargo shown in mission interfaces and logs. +---@field id number Unique identifier of the cargo. +---@field name string Name of the cargo. + +--- Dynamic cargo object that can be moved during mission. +--- (Data structure definition for DynamicCargo. Not a globally accessible table.) +---@class DynamicCargo +---@field id number Unique identifier of the dynamic cargo. +---@field name string Name of the dynamic cargo. +---@field _type string The type classification of cargo, determining its visual model and behavior properties. +---@field mass number Mass of the dynamic cargo in kilograms. +---@field position Vec3 Current 3D position of the cargo. + +--- Defines the structure of a Lua table representing both position and orientation in the DCS World coordinate system. +--- (Data structure definition for Position3. Not a globally accessible table.) +---@class Position3 +---@field p Vec3 A `Vec3` representing the object's position in the DCS World coordinate system. +---@field x Vec3 A normalized `Vec3` representing the object's forward direction vector in the DCS World coordinate system. +---@field y Vec3 A normalized `Vec3` representing the object's upward direction vector in the DCS World coordinate system. +---@field z Vec3 A normalized `Vec3` representing the object's rightward direction vector in the DCS World coordinate system. + +--- Defines the structure of a Lua table representing a reference point used by Joint Terminal Attack Controllers (JTACs) and other mission elements for targeting and navigation. +--- (Data structure definition for RefPoint. Not a globally accessible table.) +---@version 1.2.0 +---@class RefPoint +---@field callsign string A string identifier serving as the callsign or designation for the reference point. +---@field _type number A numeric identifier categorizing the reference point's purpose or classification. +---@field point Vec3 A `Vec3` representing the precise 3D position of the reference point in the DCS World coordinate system. + +--- Represents a numerically indexed Lua table (sequence) of `Vec3` points in the DCS World coordinate system. +---@alias Vec3Array Vec3[] + +--- Defines the structure of a Lua table containing parameters for the FAC_EngageGroup task. +--- (Data structure definition for TaskEnRouteFACEngageGroupParams. Not a globally accessible table.) +---@class TaskEnRouteFACEngageGroupParams +---@field groupId number ID of the group that is to be assigned by JTAC. +---@field weaponType number Weapon flag type that defines the preferred weapon of choice. +---@field designation string Type of designation to be used (NO, WP, IR_POINTER, LASER, AUTO). +---@field datalink boolean Determines whether the JTAC will send the 9-line via SADL, enabled by default. +---@field frequency number Radio frequency to use for the JTAC communications. +---@field modulation number Radio modulation type for JTAC communications. +---@field callname Callsigns_JTAC JTAC callsign identifier (Axeman, Darknight, etc.). +---@field number number JTAC callsign number. +---@field priority number The priority of the tasking, where lower numbers indicate higher importance (default: 0). + +--- Defines the structure of a Lua table containing parameters for the FAC task. +--- (Data structure definition for TaskEnRouteFACParams. Not a globally accessible table.) +---@class TaskEnRouteFACParams +---@field frequency number Radio frequency to use for the JTAC communications. +---@field modulation number Radio modulation type for JTAC communications. +---@field callname Callsigns_JTAC JTAC callsign identifier (Axeman, Darknight, etc.). +---@field number number JTAC callsign number. +---@field priority number The priority of the tasking, where lower numbers indicate higher importance (default: 0). + +--- Parameters for the FAC_AttackGroup task +--- (Data structure definition for TaskFACAttackGroupParams. Not a globally accessible table.) +---@class TaskFACAttackGroupParams +---@field groupId number ID of the group that is to be assigned by JTAC +---@field weaponType number Weapon flag type that defines the preferred weapon of choice +---@field designation string Type of designation to be used (NO, WP, IR_POINTER, LASER, AUTO) +---@field datalink boolean Determines whether or not the JTAC will send the 9-line via SADL, enabled by default +---@field frequency number Radio frequency to use for the JTAC +---@field modulation number Radio modulation type +---@field callname Callsigns_JTAC JTAC callsign identifier (Axeman, Darknight, etc.) +---@field number number JTAC callsign number + +--- Union type of all option identifier enumerators used across different AI unit types. +---@version 1.2.0 +---@alias AIOptionId AI.Option.Air.id|AI.Option.Ground.id|AI.Option.Naval.id + +--- Defines the structure of a Lua table detailing configuration options for large-scale formation flying between aircraft groups. +--- (Data structure definition for TaskFollowBigFormationParams. Not a globally accessible table.) +---@class TaskFollowBigFormationParams +---@field groupId number Unique ID of the lead aircraft group to follow. +---@field pos Vec3 A `Vec3` representing the relative position the controlled flight will maintain within the formation in the DCS World coordinate system. +---@field formation FormationType Formation pattern to use when following the group, must match aircraft type (fixed-wing or helicopter). +---@field lastWptIndexFlag boolean Determines whether the AI will terminate the follow task when the lead group reaches a specified waypoint. +---@field lastWptIndex number Waypoint index of the lead group that, when reached, will cause the following aircraft to terminate the task. + +--- Defines the structure of a Lua table detailing configuration options for formation flying between aircraft groups. +--- (Data structure definition for TaskFollowParams. Not a globally accessible table.) +---@class TaskFollowParams +---@field groupId number Unique ID of the group to follow or orbit above if it's a ground unit. +---@field pos Vec3 A `Vec3` representing the relative position the controlled flight will maintain within the formation in the DCS World coordinate system. +---@field formation FormationType Formation pattern to use when following the group, must match aircraft type (fixed-wing or helicopter). +---@field lastWptIndexFlag boolean Determines whether the AI will terminate the follow task when the lead group reaches a specified waypoint. +---@field lastWptIndex number Waypoint index of the lead group that, when reached, will cause the following aircraft to terminate the task. + +--- Defines the structure of a Lua table representing common properties specific to both airplane and helicopter units in the DCS World. Includes all fields from UnitDesc plus the following aircraft-specific fields. +--- (Data structure definition for UnitDescAircraft. Not a globally accessible table.) +---@class UnitDescAircraft +---@field fuelMassMax number A numeric value representing the maximum internal fuel capacity in kilograms. +---@field range number A numeric value representing the maximum operational range in meters at standard cruise settings. +---@field Hmax number A numeric value representing the service ceiling (maximum operational altitude) in meters. +---@field VyMax number A numeric value representing the maximum rate of climb in meters per second. +---@field NyMin number A numeric value representing the minimum safe negative G-load limit. +---@field NyMax number A numeric value representing the maximum safe positive G-load limit. +---@field tankerType Unit.RefuelingSystem An `Unit.RefuelingSystem` enumerator specifying the aerial refueling system installed, if any. + +--- Defines the structure of a Lua table representing a sensor's air target detection capabilities in different hemispheres. +--- (Data structure definition for UnitSensorDetectionDistanceAir. Not a globally accessible table.) +---@class UnitSensorDetectionDistanceAir +---@field upperHemisphere UnitSensorHemisphereDistance A table representing detection distances for targets positioned above the sensor's horizontal plane. +---@field lowerHemisphere UnitSensorHemisphereDistance A table representing detection distances for targets positioned below the sensor's horizontal plane. + +--- Defines the structure of a Lua table representing the destructive component specifications of a weapon's payload. +--- (Data structure definition for WeaponWarheadDetails. Not a globally accessible table.) +---@class WeaponWarheadDetails +---@field _type Weapon.WarheadType A `Weapon.WarheadType` enumerator specifying the primary damage mechanism of the warhead. +---@field mass number A numeric value representing the total mass of the warhead in kilograms. +---@field caliber number A numeric value representing the diameter of the warhead in millimeters. +---@field explosiveMass number A numeric value representing the mass of high explosive material in kilograms, relevant for HE and AP+HE warheads. +---@field shapedExplosiveMass number A numeric value representing the mass of shaped charge explosive material in kilograms, relevant for shaped explosive warheads. +---@field shapedExplosiveArmorThickness number A numeric value representing the maximum armor penetration capability in millimeters of rolled homogeneous armor equivalent. + +--- Parameters for the escort task +--- (Data structure definition for TaskEscortParams. Not a globally accessible table.) +---@class TaskEscortParams +---@field groupId number Unique ID of the group to escort +---@field pos Vec3 Vec3 point defining the relative position the controlled flight will form up on +---@field formation FormationType Formation pattern to use when escorting the group. Use AircraftFormationType for fixed-wing aircraft or HelicopterFormationType for helicopters. +---@field lastWptIndexFlag boolean If true the AI will follow the group until it reaches a specified waypoint (default: true) +---@field lastWptIndex number Identifies the waypoint at which the following group will stop its task (default: -1) +---@field engagementDistMax number Maximum distance of targets from the followed aircraft that the AI will actively engage +---@field targetTypes Attributes[] Array of attribute types which the AI will engage + +--- Defines the structure of a Lua table detailing configuration options for helicopter escort operations above ground forces. +--- (Data structure definition for TaskGroundEscortParams. Not a globally accessible table.) +---@class TaskGroundEscortParams +---@field groupId number Unique ID of the ground group to escort and protect. +---@field engagementDistMax number Maximum distance in meters defining the size/length of the orbit pattern before the helicopter returns to the escorted group. +---@field lastWptIndexFlag boolean Determines whether the helicopter will follow the ground group until it reaches a specified waypoint. +---@field lastWptIndex number Waypoint index at which the escorting helicopter will terminate its escort task. +---@field targetTypes Attributes[] A numerically indexed table of `Attributes` defining which enemy unit types the escorting helicopter will engage. +---@field lastWptIndexFlagChangedManually boolean Indicates whether the lastWptIndexFlag was manually configured rather than using system defaults. + +--- Defines the structure of a Lua table containing parameters for the beacon activation command. +--- (Data structure definition for CommandActivateBeaconParams. Not a globally accessible table.) +---@class CommandActivateBeaconParams +---@field _type BeaconType Beacon type identifier determining its functional characteristics. +---@field system BeaconSystemName Navigation system that will process the beacon signal. +---@field frequency number Broadcast frequency in Hertz for the navigation beacon. +---@field callsign string Morse code identifier transmitted by the beacon for identification. +---@field name string Descriptive name for the beacon shown in the mission editor interface. + +--- Defines the structure of a Lua table representing an attack task that directs a group to engage another group. +--- (Data structure definition for TaskAttackGroup. Not a globally accessible table.) +---@class TaskAttackGroup +---@field id string Task identifier, must be 'AttackGroup'. +---@field params TaskAttackGroupParams A table containing parameters that configure the attack behavior. + +--- Defines the structure of a Lua table representing an attack task that directs a group to engage a specific unit. +--- (Data structure definition for TaskAttackUnit. Not a globally accessible table.) +---@class TaskAttackUnit +---@field id string Task identifier, must be 'AttackUnit'. +---@field params TaskAttackUnitParams A table containing parameters that configure the attack behavior. + +--- Assigns the AI a task to bomb an airbases runway. By default the AI will line up along the length of the runway and drop its payload. +--- (Data structure definition for TaskBombingRunway. Not a globally accessible table.) +---@class TaskBombingRunway +---@field id string Task identifier, must be 'BombingRunway' +---@field params TaskBombingRunwayParams + +--- Defines the structure of a Lua table representing a naval recovery tanker task that directs an aircraft to orbit above a vessel group, providing refueling services. +--- (Data structure definition for TaskRecoveryTanker. Not a globally accessible table.) +---@class TaskRecoveryTanker +---@field id string Task identifier, must be 'RecoveryTanker'. +---@field params TaskRecoveryTankerParams A table containing parameters that configure the recovery tanker behavior. + +--- Defines the structure of a Lua table representing a hold task that commands ground forces to cease movement and maintain their current position. +--- (Data structure definition for TaskHold. Not a globally accessible table.) +---@class TaskHold +---@field id string Task identifier, must be 'hold'. +---@field params TaskHoldParams A table containing parameters for the hold task. + +--- Defines the structure of a Lua table representing an air refueling task directing aircraft to seek and connect with the nearest available tanker. +--- (Data structure definition for TaskRefueling. Not a globally accessible table.) +---@class TaskRefueling +---@field id string Task identifier, must be 'refueling'. +---@field params TaskRefuelingParams A table containing parameters for the refueling task. + +--- Defines the structure of a Lua table representing a wrapper that allows a command or action to be used as a task. +--- (Data structure definition for WrappedAction. Not a globally accessible table.) +---@class WrappedAction +---@field id string Task identifier, must be 'WrappedAction'. +---@field params WrappedActionParams A table containing the action to be wrapped as a task. + +--- Defines the structure of a Lua table representing a composite task that combines multiple tasks to be executed sequentially or in parallel. +--- (Data structure definition for ComboTask. Not a globally accessible table.) +---@class ComboTask +---@field id string Task identifier, must be 'ComboTask'. +---@field params ComboTaskParams A table containing parameters that define the tasks to be combined. + +--- Defines the structure of a Lua table detailing mission execution parameters and route information. +--- (Data structure definition for MissionParams. Not a globally accessible table.) +---@class MissionParams +---@field airborne boolean Indicates whether the aircraft group is already airborne when the mission is assigned. +---@field route MissionRoute A table containing the route waypoints to be followed during the mission. + +--- Defines the structure of a Lua table detailing configuration options for controlled task execution. +--- (Data structure definition for ControlledTaskParams. Not a globally accessible table.) +---@class ControlledTaskParams +---@field task table The task to be executed. +---@field condition ControlledTaskCondition A table specifying the conditions that must be met for the task to start. +---@field stopCondition ControlledTaskStopCondition A table specifying the conditions that will trigger task termination. + +--- Defines the structure of a Lua table representing an en-route task that assigns the aircraft to act as an AWACS for friendly forces. +--- (Data structure definition for TaskEnRouteAWACS. Not a globally accessible table.) +---@class TaskEnRouteAWACS +---@field id string En-route task identifier, must be 'awacs'. +---@field params TaskEnRouteAWACSParams + +--- Defines the structure of a Lua table representing an en-route task that assigns the controlled group to search for and engage a specific unit. The target must be detected for AI to engage it. +--- (Data structure definition for TaskEnRouteEngageUnit. Not a globally accessible table.) +---@class TaskEnRouteEngageUnit +---@field id string En-route task identifier, must be 'engageUnit'. +---@field params TaskEnRouteEngageUnitParams + +--- Defines the structure of a Lua table representing an en-route task that assigns the group to act as an EWR radar for friendly forces. +--- (Data structure definition for TaskEnRouteEWR. Not a globally accessible table.) +---@class TaskEnRouteEWR +---@field id string En-route task identifier, must be 'ewr'. +---@field params TaskEnRouteEWRParams + +--- Defines the structure of a Lua table representing an en-route task that assigns the controlled group to engage targets matching specific parameters. +--- (Data structure definition for TaskEnRouteEngageTargets. Not a globally accessible table.) +---@class TaskEnRouteEngageTargets +---@field id string En-route task identifier, must be 'engageTargets'. +---@field params TaskEnRouteEngageTargetsParams + +--- Defines the structure of a Lua table representing an en-route task that assigns the aircraft to act as an airborne tanker for friendly forces. The aircraft must be a certified tanker aircraft. +--- (Data structure definition for TaskEnRouteTanker. Not a globally accessible table.) +---@class TaskEnRouteTanker +---@field id string En-route task identifier, must be 'tanker'. +---@field params TaskEnRouteTankerParams + +--- Defines the structure of a Lua table representing an en-route task that assigns the controlled group to search for and engage a specific group. The target must be detected for AI to engage it. +--- (Data structure definition for TaskEnRouteEngageGroup. Not a globally accessible table.) +---@class TaskEnRouteEngageGroup +---@field id string En-route task identifier, must be 'engageGroup'. +---@field params TaskEnRouteEngageGroupParams + +--- Defines the structure of a command that broadcasts an audio message over a unit or group's active radio frequency. +--- (Data structure definition for CommandTransmitMessage. Not a globally accessible table.) +---@class CommandTransmitMessage +---@field id string Command identifier that must be 'transmitMessage'. +---@field params CommandTransmitMessageParams + +--- Defines the structure of a command that toggles the Enhanced Position Location Reporting System (EPLRS) data link capabilities for a unit or group. +--- (Data structure definition for CommandEPLRS. Not a globally accessible table.) +---@class CommandEPLRS +---@field id string Command identifier that must be 'EPLRS'. +---@field params CommandEPLRSParams + +--- Defines the structure of a command that changes the active task action within a mission group's task queue. +--- (Data structure definition for CommandSwitchAction. Not a globally accessible table.) +---@class CommandSwitchAction +---@field id string Command identifier that must be 'SwitchAction'. +---@field params CommandSwitchActionParams + +--- Defines the structure of a command that activates an initially inactive group, triggering AI units to follow their designated route. +--- (Data structure definition for CommandStart. Not a globally accessible table.) +---@class CommandStart +---@field id string Command identifier that must be 'start'. +---@field params CommandStartParams + +--- Defines the structure of a command that deactivates any active Instrument Carrier Landing System (ICLS) beacon on a unit or group. +--- (Data structure definition for CommandDeactivateICLS. Not a globally accessible table.) +---@class CommandDeactivateICLS +---@field id string Command identifier that must be 'DeactivateICLS'. +---@field params CommandDeactivateICLSParams + +--- Defines the structure of a command that activates a Link 4 data link system for aircraft carrier operations. +--- (Data structure definition for CommandActivateLink4. Not a globally accessible table.) +---@class CommandActivateLink4 +---@field id string Command identifier that must be 'ActivateLink4'. +---@field params CommandActivateLink4Params + +--- Defines the structure of a command that activates an Automatic Carrier Landing System (ACLS) on an aircraft carrier. +--- (Data structure definition for CommandActivateACLS. Not a globally accessible table.) +---@class CommandActivateACLS +---@field id string Command identifier that must be 'ActivateACLS'. +---@field params CommandActivateACLSParams + +--- Defines the structure of a command that activates an Instrument Carrier Landing System (ICLS) beacon for aircraft carriers. +--- (Data structure definition for CommandActivateICLS. Not a globally accessible table.) +---@class CommandActivateICLS +---@field id string Command identifier that must be 'ActivateICLS'. +---@field params CommandActivateICLSParams + +--- Defines the structure of a command that deactivates any active radio navigation beacon on a unit or group. +--- (Data structure definition for CommandDeactivateBeacon. Not a globally accessible table.) +---@class CommandDeactivateBeacon +---@field id string Command identifier that must be 'DeactivateBeacon'. +---@field params CommandDeactivateBeaconParams + +--- Defines the structure of a command that terminates any active radio message transmission from a unit or group. +--- (Data structure definition for CommandStopTransmission. Not a globally accessible table.) +---@class CommandStopTransmission +---@field id string Command identifier that must be 'stopTransmission'. +---@field params CommandStopTransmissionParams + +--- Defines the structure of a command that toggles aircraft smoke pod emission. +--- (Data structure definition for CommandSmokeOnOff. Not a globally accessible table.) +---@class CommandSmokeOnOff +---@field id string Command identifier that must be 'smoke_on_off'. +---@field params CommandSmokeOnOffParams + +--- Defines the structure of a command that toggles a group's invulnerability to all damage. +--- (Data structure definition for CommandSetImmortal. Not a globally accessible table.) +---@class CommandSetImmortal +---@field id string Command identifier that must be 'setImmortal'. +---@field params CommandSetImmortalParams + +--- Defines the structure of a command that toggles infinite fuel supply for a unit or group. +--- (Data structure definition for CommandSetUnlimitedFuel. Not a globally accessible table.) +---@class CommandSetUnlimitedFuel +---@field id string Command identifier that must be 'setUnlimitedFuel'. +---@field params CommandSetUnlimitedFuelParams + +--- Defines the structure of a command that halts or resumes a ground group's movement along its route. +--- (Data structure definition for CommandStopRoute. Not a globally accessible table.) +---@class CommandStopRoute +---@field id string Command identifier that must be 'StopRoute'. +---@field params CommandStopRouteParams + +--- Defines the structure of a command that deactivates any active Link 4 data link system on a unit or group. +--- (Data structure definition for CommandDeactivateLink4. Not a globally accessible table.) +---@class CommandDeactivateLink4 +---@field id string Command identifier that must be 'DeactivateLink4'. +---@field params CommandDeactivateLink4Params + +--- Defines the structure of a command that toggles a group's visibility to enemy AI sensors. +--- (Data structure definition for CommandSetInvisible. Not a globally accessible table.) +---@class CommandSetInvisible +---@field id string Command identifier that must be 'setInvisible'. +---@field params CommandSetInvisibleParams + +--- Defines the structure of a command that adjusts a ship's cargo loading level, affecting its buoyancy and water line position. +--- (Data structure definition for CommandLoadingShip. Not a globally accessible table.) +---@class CommandLoadingShip +---@field id string Command identifier that must be 'LoadingShip'. +---@field params CommandLoadingShipParams + +--- Defines the structure of a command that deactivates any active Automatic Carrier Landing System (ACLS) on a unit or group. +--- (Data structure definition for CommandDeactivateACLS. Not a globally accessible table.) +---@class CommandDeactivateACLS +---@field id string Command identifier that must be 'DeactivateACLS'. +---@field params CommandDeactivateACLSParams + +--- Defines the structure of a command that changes the radio broadcasting frequency for a specific unit within an AI group. +--- (Data structure definition for CommandSetFrequencyForUnit. Not a globally accessible table.) +---@class CommandSetFrequencyForUnit +---@field id string Command identifier that must be 'SetFrequencyForUnit'. +---@field params CommandSetFrequencyForUnitParams + +--- Defines the structure of a command that changes a group's identification callsign for radio communications. +--- (Data structure definition for CommandSetCallsign. Not a globally accessible table.) +---@class CommandSetCallsign +---@field id string Command identifier that must be 'SetCallsign'. +---@field params CommandSetCallsignParams + +--- Defines the structure of a command that changes the radio broadcasting frequency for an AI group. +--- (Data structure definition for CommandSetFrequency. Not a globally accessible table.) +---@class CommandSetFrequency +---@field id string Command identifier that must be 'SetFrequency'. +---@field params CommandSetFrequencyParams + +--- Defines the structure of a command that changes the active route leg for a group, allowing control of navigation between waypoints. +--- (Data structure definition for CommandSwitchWaypoint. Not a globally accessible table.) +---@class CommandSwitchWaypoint +---@field id string Command identifier that must be 'SwitchWaypoint'. +---@field params CommandSwitchWaypointParams + +--- Defines the structure of a command that executes a Lua script within the context of a group, providing access to the group through the '...' self-reference. +--- (Data structure definition for CommandScript. Not a globally accessible table.) +---@class CommandScript +---@field id string Command identifier that must be 'Script'. +---@field params CommandScriptParams + +--- Represents a numerically indexed table of `Object.Category` enum values. +---@alias ObjectCategoryArray Object.Category[] + +--- Trigger zone used in mission editor and referenced in zone-related events. +--- (Data structure definition for Zone. Not a globally accessible table.) +---@class Zone +---@field id number Unique identifier of the zone. +---@field name string Name of the zone as defined in the mission editor. +---@field position Vec3 3D position of the zone's center. +---@field radius number Radius of the zone in meters. +---@field coalition coalition.side|nil The `coalition.side` value indicating which faction owns or controls the zone, or `nil` if not coalition-specific. + +--- Assigns the nearest world object to the point for AI to attack. +--- (Data structure definition for TaskAttackMapObject. Not a globally accessible table.) +---@class TaskAttackMapObject +---@field id string Task identifier, must be 'AttackMapObject' +---@field params TaskAttackMapObjectParams + +--- Assigns a point on the ground for which the AI will attack. Best used for discriminant carpet bombing of a target or having a GBU hit a specific point on the map. +--- (Data structure definition for TaskBombing. Not a globally accessible table.) +---@class TaskBombing +---@field id string Task identifier, must be 'Bombing' +---@field params TaskBombingParams + +--- Assigns a point on the ground for which the AI will attack. Similar to the bombing task, but with more control over target area. Can be combined with follow big formation task for all participating aircraft to simultaneously bomb a target. +--- (Data structure definition for TaskCarpetBombing. Not a globally accessible table.) +---@class TaskCarpetBombing +---@field id string Task identifier, must be 'CarpetBombing' +---@field params TaskCarpetBombingParams + +--- Defines the structure of a Lua table representing an en-route task that assigns the controlled group to engage targets with specific attributes within a defined zone. +--- (Data structure definition for TaskEnRouteEngageTargetsInZone. Not a globally accessible table.) +---@class TaskEnRouteEngageTargetsInZone +---@field id string En-route task identifier, must be 'engageTargetsInZone'. +---@field params TaskEnRouteEngageTargetsInZoneParams + +--- Assigns a point on the ground for which the AI will shoot at. Most commonly used with artillery to shell a target. +--- (Data structure definition for TaskFireAtPoint. Not a globally accessible table.) +---@class TaskFireAtPoint +---@field id string Task identifier, must be 'fireAtPoint' +---@field params TaskFireAtPointParams + +--- Defines the structure of a Lua table representing a landing task that directs helicopters to touch down at specific coordinates. +--- (Data structure definition for TaskLand. Not a globally accessible table.) +---@class TaskLand +---@field id string Task identifier, must be 'land'. +---@field params TaskLandParams A table containing parameters that configure the landing behavior. + +--- Defines the structure of a Lua table representing an orbit task that directs aircraft to fly various pattern types at specified locations. +--- (Data structure definition for TaskOrbit. Not a globally accessible table.) +---@class TaskOrbit +---@field id string Task identifier, must be 'orbit'. +---@field params TaskOrbitParams A table containing parameters that configure the orbit behavior and pattern. + +--- Defines the structure of a Lua table representing a strafing task that directs AI to perform gun or rocket attacks on a ground point. +--- (Data structure definition for TaskStrafing. Not a globally accessible table.) +---@class TaskStrafing +---@field id string Task identifier, must be 'Strafing'. +---@field params TaskStrafingParams A table containing parameters that configure the strafing attack behavior. + +--- Defines the structure of a Lua table representing a scenery object's properties and physical characteristics in the DCS World. +--- (Data structure definition for SceneryObjectDesc. Not a globally accessible table.) +---@class SceneryObjectDesc +---@field life number A numeric value representing the initial health or integrity level of the scenery object. +---@field box Box3 A box (two Vec3 points) representing the three-dimensional collision boundaries of the scenery object in the DCS World coordinate system. +---@field category Object.Category An `Object.Category` enumerator representing the category of the scenery object. +---@field categoryEx Weapon.Category A `Weapon.Category` enumerator representing the subcategory of the scenery object. + +--- Defines the structure of a Lua table representing a static object's properties and physical characteristics in the DCS World. +--- (Data structure definition for StaticObjectDesc. Not a globally accessible table.) +---@class StaticObjectDesc +---@field life number A numeric value representing the initial health or integrity level of the static object. +---@field box Box3 A box (two Vec3 points) representing the three-dimensional collision boundaries of the static object in the DCS World coordinate system. + +--- Defines the structure of a Lua table representing the basic properties and capabilities common to all unit types in the DCS World. +--- (Data structure definition for UnitDesc. Not a globally accessible table.) +---@class UnitDesc +---@field typeName string A string containing the internal identifier for the unit type used by the DCS World engine. +---@field displayName string A string containing the human-readable name of the unit as shown in the DCS World interface. +---@field category Unit.Category An `Unit.Category` enumerator specifying the basic classification of the unit. +---@field massEmpty number A numeric value representing the unit's empty weight in kilograms. +---@field speedMax number A numeric value representing the unit's maximum speed in meters per second. +---@field life number A numeric value representing the unit's total health or structural integrity. +---@field RCS nil|number A numeric value representing the unit's radar cross-section signature. +---@field box Box3|nil A box (two Vec3 points) representing the unit's physical dimensions in the DCS World coordinate system. +---@field attributes UnitAttributes A table defining special characteristics and capabilities of the unit. +---@field Kmax nil|number A numeric coefficient related to the unit's performance characteristics. +---@field Kab nil|number A numeric coefficient related to afterburner performance for aircraft. + +--- Defines the structure of a Lua table representing a circular area used for spatial trigger conditions in the DCS World. +--- (Data structure definition for TriggerZoneCircular. Not a globally accessible table.) +---@class TriggerZoneCircular +---@field position Position3 A `Position3` representing the center point and orientation of the trigger zone in the DCS World coordinate system. +---@field radius number A numeric value defining the radius of the circular trigger zone in meters. + +--- Defines the structure of a Lua table representing an en-route task that assigns the controlled group to act as a Forward Air Controller or JTAC and engage the specified group as a JTAC target once detected. +--- (Data structure definition for TaskEnRouteFACEngageGroup. Not a globally accessible table.) +---@class TaskEnRouteFACEngageGroup +---@field id string En-route task identifier, must be 'FAC_engageGroup'. +---@field params TaskEnRouteFACEngageGroupParams + +--- Defines the structure of a Lua table representing an en-route task that assigns the controlled group to act as a Forward Air Controller or JTAC. Any detected targets will be assigned as targets to the player via the JTAC radio menu. +--- (Data structure definition for TaskEnRouteFAC. Not a globally accessible table.) +---@class TaskEnRouteFAC +---@field id string En-route task identifier, must be 'fac'. +---@field params TaskEnRouteFACParams + +--- Assigns the controlled group to act as a Forward Air Controller or JTAC in attacking the specified group. This task adds the group to the JTAC radio menu and interacts with a player to destroy the target. +--- (Data structure definition for TaskFACAttackGroup. Not a globally accessible table.) +---@class TaskFACAttackGroup +---@field id string Task identifier, must be 'FAC_AttackGroup' +---@field params TaskFACAttackGroupParams + +--- Defines the structure of a Lua table representing an advanced formation-following task for coordinated bombing missions with multiple aircraft. +--- (Data structure definition for TaskFollowBigFormation. Not a globally accessible table.) +---@class TaskFollowBigFormation +---@field id string Task identifier, must be 'followBigFormation'. +---@field params TaskFollowBigFormationParams A table containing parameters that configure the formation-following behavior. + +--- Defines the structure of a Lua table representing a follow task that directs aircraft to join formation with another group or orbit above ground units. +--- (Data structure definition for TaskFollow. Not a globally accessible table.) +---@class TaskFollow +---@field id string Task identifier, must be 'follow'. +---@field params TaskFollowParams A table containing parameters that configure the follow behavior. + +--- Defines the structure of a Lua table representing a sensor system's capabilities and technical specifications. +--- (Data structure definition for UnitSensor. Not a globally accessible table.) +---@class UnitSensor +---@field _type Unit.SensorType An `Unit.SensorType` enumerator specifying the general category of the sensor system. +---@field typeName string A string containing the specific model name or designation of the sensor. +---@field detectionDistanceAir UnitSensorDetectionDistanceAir|nil A table containing the sensor's detection ranges against aerial targets from different aspects. +---@field detectionDistanceIdle nil|number A numeric value representing the maximum detection distance in meters against idle (non-emitting) targets. +---@field detectionDistanceMaximal nil|number A numeric value representing the maximum absolute detection distance in meters under optimal conditions. +---@field detectionDistanceAfterburner nil|number A numeric value representing the maximum detection distance in meters against targets using afterburner. + +--- Defines the structure of a Lua table representing the specifications and capabilities of a weapon or ammunition type available to a unit. +--- (Data structure definition for UnitAmmoDesc. Not a globally accessible table.) +---@class UnitAmmoDesc +---@field missileCategory Weapon.MissileCategory|nil +---@field rangeMaxAltMax nil|number A numeric value representing the maximum weapon range in meters when fired at maximum altitude. +---@field rangeMin nil|number A numeric value representing the minimum effective range of the weapon in meters. +---@field displayName string A string containing the human-readable name of the weapon as displayed in the DCS World interface. +---@field rangeMaxAltMin nil|number A numeric value representing the maximum weapon range in meters when fired at minimum altitude. +---@field altMax nil|number A numeric value representing the maximum altitude in meters at which the weapon can be effectively used. +---@field RCS Box3|nil A `Box3` representing the radar cross-section characteristics of the weapon. +---@field box Box3|nil A box (two Vec3 points) representing the physical dimensions of the weapon in the DCS World coordinate system. +---@field altMin nil|number A numeric value representing the minimum altitude in meters at which the weapon can be effectively used. +---@field life nil|number A numeric value representing the weapon's health or structural integrity. +---@field fuseDist nil|number A numeric value representing the distance in meters at which the weapon's fuse activates. +---@field category Weapon.Category|nil +---@field guidance Weapon.GuidanceType|nil +---@field warhead WeaponWarheadDetails|nil +---@field typeName string A string containing the internal type identifier for the weapon. +---@field Nmax nil|number A numeric value representing the maximum G-load the weapon can withstand. + +--- Defines the structure of a Lua table representing the basic properties common to all weapon types in the DCS World. +--- (Data structure definition for WeaponDesc. Not a globally accessible table.) +---@class WeaponDesc +---@field life number A numeric value representing the weapon's total health or structural integrity. +---@field box Box3 A box (two Vec3 points) representing the physical dimensions of the weapon in the DCS World coordinate system. +---@field category Weapon.Category A `Weapon.Category` enumerator specifying the fundamental classification of the weapon. +---@field warhead WeaponWarheadDetails A table representing the specifications of the weapon's destructive payload component. + +--- Defines the structure of a Lua table representing the properties and capabilities of air-dropped bomb weapons in the DCS World. +--- (Data structure definition for WeaponDescBomb. Not a globally accessible table.) +---@class WeaponDescBomb +---@field life number A numeric value representing the bomb's total health or structural integrity. +---@field box Box3 A box (two Vec3 points) representing the physical dimensions of the bomb in the DCS World coordinate system. +---@field category Weapon.Category A `Weapon.Category` enumerator specifying the fundamental classification of the bomb. +---@field warhead WeaponWarheadDetails A table representing the specifications of the bomb's destructive payload component. +---@field guidance Weapon.GuidanceType A `Weapon.GuidanceType` enumerator specifying the bomb's targeting and course correction technology, if applicable. +---@field altMin number A numeric value representing the minimum effective release altitude in meters. +---@field altMax number A numeric value representing the maximum effective release altitude in meters. + +--- Defines the structure of a Lua table representing the properties and capabilities of missile weapons in the DCS World. +--- (Data structure definition for WeaponDescMissile. Not a globally accessible table.) +---@class WeaponDescMissile +---@field life number A numeric value representing the missile's total health or structural integrity. +---@field box Box3 A box (two Vec3 points) representing the physical dimensions of the missile in the DCS World coordinate system. +---@field category Weapon.Category A `Weapon.Category` enumerator specifying the fundamental classification of the missile. +---@field warhead WeaponWarheadDetails A table representing the specifications of the missile's destructive payload component. +---@field guidance Weapon.GuidanceType A `Weapon.GuidanceType` enumerator specifying the missile's targeting and course correction technology. +---@field rangeMin number A numeric value representing the minimum effective engagement range in meters. +---@field rangeMaxAltMin number A numeric value representing the maximum engagement range in meters when fired at minimum altitude. +---@field rangeMaxAltMax number A numeric value representing the maximum engagement range in meters when fired at maximum altitude. +---@field altMin number A numeric value representing the minimum effective engagement altitude in meters. +---@field altMax number A numeric value representing the maximum effective engagement altitude in meters. +---@field Nmax number A numeric value representing the maximum G-force the missile can sustain during flight. +---@field fuseDist number A numeric value representing the distance in meters at which the missile's proximity fuse activates. + +--- Defines the structure of a Lua table representing the properties and capabilities of unguided rocket weapons in the DCS World. +--- (Data structure definition for WeaponDescRocket. Not a globally accessible table.) +---@class WeaponDescRocket +---@field life number A numeric value representing the rocket's total health or structural integrity. +---@field box Box3 A box (two Vec3 points) representing the physical dimensions of the rocket in the DCS World coordinate system. +---@field category Weapon.Category A `Weapon.Category` enumerator specifying the fundamental classification of the rocket. +---@field warhead WeaponWarheadDetails A table representing the specifications of the rocket's destructive payload component. +---@field distMin number A numeric value representing the minimum effective firing distance in meters. +---@field distMax number A numeric value representing the maximum effective firing distance in meters. + +--- Controlled aircraft will follow the assigned group along their route in formation and will engage threats within a defined distance from the followed group. +--- (Data structure definition for TaskEscort. Not a globally accessible table.) +---@class TaskEscort +---@field id string Task identifier, must be 'escort' +---@field params TaskEscortParams + +--- Defines the structure of a Lua table representing a ground escort task that directs helicopters to provide aerial protection for ground units. +--- (Data structure definition for TaskGroundEscort. Not a globally accessible table.) +---@class TaskGroundEscort +---@field id string Task identifier, must be 'groundEscort'. +---@field params TaskGroundEscortParams A table containing parameters that configure the ground escort behavior. + +--- Defines the structure of a command that activates a radio navigation beacon on a unit or group. +--- (Data structure definition for CommandActivateBeacon. Not a globally accessible table.) +---@class CommandActivateBeacon +---@field id string Command identifier that must be 'ActivateBeacon'. +---@field params CommandActivateBeaconParams + +--- Defines the structure of a Lua table representing a route-based mission consisting of waypoints assigned to a group. +--- (Data structure definition for Mission. Not a globally accessible table.) +---@class Mission +---@field id string Task identifier, must be 'Mission'. +---@field params MissionParams A table containing parameters that define the mission configuration. + +--- Defines the structure of a Lua table representing a task with start and stop conditions that determine when to execute and terminate the task. +--- (Data structure definition for ControlledTask. Not a globally accessible table.) +---@class ControlledTask +---@field id string Task identifier, must be 'ControlledTask'. +---@field params ControlledTaskParams A table containing parameters that configure the task execution conditions. + +--- Defines the structure of a Lua table representing a target detected by a controller, including its object reference and detection details. +--- (Data structure definition for ControllerDetectedTarget. Not a globally accessible table.) +---@class ControllerDetectedTarget +---@field object Object Reference to the detected target object. +---@field visible boolean Whether the target is currently visible via line of sight. +---@field _type boolean Whether the target's specific type is known to the detector. +---@field distance boolean Whether the distance to the target is known to the detector. + +--- Base structure for all event data. Contains common fields present in every event. +--- (Data structure definition for EventDataBase. Not a globally accessible table.) +---@class EventDataBase +---@field id world.event The event ID that identifies the type of event. +---@field time number Timestamp when the event occurred in mission time. +---@field initiator Object|nil The initiating object (can be a Unit, StaticObject, or other object type depending on event). + +--- Base structure for mark-related events. +--- (Data structure definition for EventDataMarkBase. Not a globally accessible table.) +---@class EventDataMarkBase +---@field id world.event The event ID that identifies the type of event. +---@field time number Timestamp when the event occurred in mission time. +---@field initiator Object|nil The initiating object (can be a Unit, StaticObject, or other object type depending on event). +---@field MarkID number ID of the mark that was added, changed, or removed. +---@field MarkText nil|string Text of the mark. +---@field MarkCoordinate Vec3|nil Coordinate of the mark. +---@field MarkCoalition coalition.side|nil Coalition that owns the mark. + +--- Event data structure for S_EVENT_SHOOTING_START events. Occurs when continuous shooting begins. +--- (Data structure definition for EventDataShootingStart. Not a globally accessible table.) +---@class EventDataShootingStart +---@field id world.event The event ID that identifies the type of event. +---@field time number Timestamp when the event occurred in mission time. +---@field initiator Object|nil The initiating object (can be a Unit, StaticObject, or other object type depending on event). +---@field target Object|nil The target being shot at, if applicable. + +--- Event data structure for S_EVENT_WEAPON_ADD events. Occurs when a weapon is added to a unit. +--- (Data structure definition for EventDataWeaponAdd. Not a globally accessible table.) +---@class EventDataWeaponAdd +---@field id world.event The event ID that identifies the type of event. +---@field time number Timestamp when the event occurred in mission time. +---@field initiator Object|nil The initiating object (can be a Unit, StaticObject, or other object type depending on event). +---@field WeaponName string Name of the weapon that was added. + +--- Represents a numerically indexed table of `Object` instances. +---@alias ObjectArray Object[] + +--- Defines the structure of a Lua table representing a specific weapon or ammunition type and its quantity in a unit's inventory. +--- (Data structure definition for UnitAmmoItem. Not a globally accessible table.) +---@class UnitAmmoItem +---@field count number A numeric value indicating the quantity of this ammunition type available to the unit. +---@field desc UnitAmmoDesc A table representing the specifications and capabilities of this ammunition type. + +--- Represents a Lua table structure defining either a main task or an enroute task that can be assigned to a controller to direct AI behavior. +---@alias ControllerTask TaskAttackGroup|TaskAttackMapObject|TaskAttackUnit|TaskBombing|TaskBombingRunway|TaskCarpetBombing|TaskEnRouteAWACS|TaskEnRouteEWR|TaskEnRouteEngageGroup|TaskEnRouteEngageTargets|TaskEnRouteEngageTargetsInZone|TaskEnRouteEngageUnit|TaskEnRouteFAC|TaskEnRouteFACEngageGroup|TaskEnRouteTanker|TaskEscort|TaskFACAttackGroup|TaskFireAtPoint|TaskFollow|TaskFollowBigFormation|TaskGroundEscort|TaskHold|TaskLand|TaskOrbit|TaskRecoveryTanker|TaskRefueling|TaskStrafing + +--- Represents a Lua table structure defining an immediate command that can be issued to a controller to affect AI behavior without modifying tasks. +---@alias ControllerCommand CommandActivateACLS|CommandActivateBeacon|CommandActivateICLS|CommandActivateLink4|CommandDeactivateACLS|CommandDeactivateBeacon|CommandDeactivateICLS|CommandDeactivateLink4|CommandEPLRS|CommandLoadingShip|CommandScript|CommandSetCallsign|CommandSetFrequency|CommandSetFrequencyForUnit|CommandSetImmortal|CommandSetInvisible|CommandSetUnlimitedFuel|CommandSmokeOnOff|CommandStart|CommandStopRoute|CommandStopTransmission|CommandSwitchAction|CommandSwitchWaypoint|CommandTransmitMessage + +--- Represents a numerically indexed Lua table (sequence) where each element is a table containing information about a detected target. +---@alias ControllerDetectedTargetArray ControllerDetectedTarget[] + +--- Represents a numerically indexed Lua table (sequence) of `Airbase` objects in the DCS World mission. +---@alias AirbaseArray Airbase[] + +--- Event data structure that contains information about an event. The id field identifies which type of event is being handled. +--- (Data structure definition for EventData. Not a globally accessible table.) +---@class EventData +---@field id world.event The event ID that identifies the type of event. +---@field time number Timestamp when the event occurred in mission time. +---@field initiator Object|nil The initiating object (can be a Unit, StaticObject, or other object type depending on event). +---@field target Object|nil The target object involved in the event (present in hit, kill, shooting events). +---@field weapon Weapon|nil The weapon used during the event (present in shot, hit, kill events). +---@field WeaponName nil|string Name of the weapon (present in weapon_add events). +---@field place Airbase|nil The place object (used in landing, takeoff, birth, base_captured events). +---@field subplace nil|world.BirthPlace The specific location within the place (used in birth events). +---@field MarkID nil|number ID of the mark in mark-related events. +---@field MarkText nil|string Text of the mark. +---@field MarkCoordinate Vec3|nil Coordinate of the mark. +---@field MarkCoalition coalition.side|nil Coalition that owns the mark. +---@field Zone Zone|nil The zone object in trigger zone events. +---@field Cargo Cargo|nil The cargo object in cargo-related events. +---@field IniCoalition coalition.side|nil Coalition of the initiator. +---@field TgtCoalition coalition.side|nil Coalition of the target. +---@field IniPlayerName nil|string Name of the player that initiated the event. +---@field TgtPlayerName nil|string Name of the player that was targeted. + +--- Event data structure for S_EVENT_BASE_CAPTURED events. Occurs when a base is captured. +--- (Data structure definition for EventDataBaseCaptured. Not a globally accessible table.) +---@class EventDataBaseCaptured +---@field id world.event The event ID that identifies the type of event. +---@field time number Timestamp when the event occurred in mission time. +---@field initiator Object|nil The initiating object (can be a Unit, StaticObject, or other object type depending on event). +---@field place Airbase The airbase that was captured. + +--- Event data structure for S_EVENT_BIRTH events. Occurs when an object is spawned. +--- (Data structure definition for EventDataBirth. Not a globally accessible table.) +---@class EventDataBirth +---@field id world.event The event ID that identifies the type of event. +---@field time number Timestamp when the event occurred in mission time. +---@field initiator Object|nil The initiating object (can be a Unit, StaticObject, or other object type depending on event). +---@field place Airbase|nil The place where the unit was spawned, if applicable. +---@field subplace nil|world.BirthPlace The specific location type within the place where the unit was spawned. + +--- Comprehensive event data structure containing all possible fields from any event type. +--- (Data structure definition for EventDataGeneric. Not a globally accessible table.) +---@class EventDataGeneric +---@field id world.event The event ID that identifies the type of event. +---@field time number Timestamp when the event occurred in mission time. +---@field initiator Object|nil The initiating object (can be a Unit, StaticObject, or other object type depending on event). +---@field IniObjectCategory Object.Category|nil (UNIT/STATIC/SCENERY) The initiator object category (Object.Category.UNIT or Object.Category.STATIC). +---@field IniDCSUnit StaticObject|Unit|nil (UNIT/STATIC) The initiating DCS Unit or StaticObject. +---@field IniDCSUnitName nil|string (UNIT/STATIC) The initiating Unit name. +---@field IniUnit Unit|nil (UNIT) The initiating MOOSE Unit wrapper of the initiator Unit object. +---@field IniUnitName nil|string (UNIT) The initiating UNIT name (same as IniDCSUnitName). +---@field IniDCSGroup Group|nil (UNIT) The initiating Group. +---@field IniDCSGroupName nil|string (UNIT) The initiating Group name. +---@field IniGroup Group|nil (UNIT) The initiating GROUP object. +---@field IniGroupName nil|string (UNIT) The initiating GROUP name (same as IniDCSGroupName). +---@field IniCategory Unit.Category|nil (UNIT) The category of the initiator. +---@field IniCoalition coalition.side|nil (UNIT) The coalition of the initiator. +---@field IniTypeName nil|string (UNIT) The type name of the initiator. +---@field IniPlayerName nil|string (UNIT) The name of the initiating player in case the Unit is a client or player slot. +---@field IniPlayerUCID nil|string (UNIT) The UCID of the initiating player in case the Unit is a client or player slot. +---@field target Object|nil (UNIT/STATIC) The target object (Unit/StaticObject/other depending on event type). +---@field TgtObjectCategory Object.Category|nil (UNIT/STATIC) The target object category (Object.Category.UNIT or Object.Category.STATIC). +---@field TgtDCSUnit StaticObject|Unit|nil (UNIT/STATIC) The target DCS Unit or StaticObject. +---@field TgtDCSUnitName nil|string (UNIT/STATIC) The target Unit name. +---@field TgtUnit Unit|nil (UNIT) The target Unit object. +---@field TgtUnitName nil|string (UNIT) The target UNIT name (same as TgtDCSUnitName). +---@field TgtDCSGroup Group|nil (UNIT) The target Group. +---@field TgtDCSGroupName nil|string (UNIT) The target Group name. +---@field TgtGroup Group|nil (UNIT) The target GROUP object. +---@field TgtGroupName nil|string (UNIT) The target GROUP name (same as TgtDCSGroupName). +---@field TgtCategory Unit.Category|nil (UNIT) The category of the target. +---@field TgtCoalition coalition.side|nil (UNIT) The coalition of the target. +---@field TgtTypeName nil|string (UNIT) The type name of the target. +---@field TgtPlayerName nil|string (UNIT) The name of the target player in case the Unit is a client or player slot. +---@field TgtPlayerUCID nil|string (UNIT) The UCID of the target player in case the Unit is a client or player slot. +---@field weapon Weapon|nil The weapon used during the event (present in shot, hit, kill events). +---@field WeaponName nil|string Name of the weapon. +---@field WeaponTypeName nil|string Type name of the weapon. +---@field WeaponCategory Weapon.Category|nil Category of the weapon. +---@field WeaponCoalition coalition.side|nil Coalition of the weapon. +---@field WeaponPlayerName nil|string Player name associated with the weapon, if applicable. +---@field WeaponTgtDCSUnit Unit|nil Target unit of the weapon. +---@field WeaponUNIT Unit|nil Sometimes, the weapon is a player unit. +---@field place Airbase|nil The place object (used in landing, takeoff, birth, base_captured events). +---@field PlaceName nil|string The name of the place. +---@field subplace nil|world.BirthPlace The specific location within the place (used in birth events). +---@field MarkID nil|number ID of the mark in mark-related events. +---@field MarkText nil|string Text of the mark. +---@field MarkCoordinate Vec3|nil Coordinate of the mark. +---@field MarkVec3 Vec3|nil Vector 3D position of the mark. +---@field MarkCoalition coalition.side|nil Coalition that owns the mark. +---@field MarkGroupID nil|number Group ID associated with the mark, if applicable. +---@field Cargo Cargo|nil The cargo object in cargo-related events. +---@field CargoName nil|string The name of the cargo. +---@field IniDynamicCargo DynamicCargo|nil The dynamic cargo object in dynamic cargo events. +---@field IniDynamicCargoName nil|string The name of the dynamic cargo. +---@field Zone Zone|nil The zone object in zone-related events. +---@field ZoneName nil|string The name of the zone. + +--- Event data structure for S_EVENT_HIT events. Occurs whenever an object is hit by a weapon. +--- (Data structure definition for EventDataHit. Not a globally accessible table.) +---@class EventDataHit +---@field id world.event The event ID that identifies the type of event. +---@field time number Timestamp when the event occurred in mission time. +---@field initiator Object|nil The initiating object (can be a Unit, StaticObject, or other object type depending on event). +---@field weapon Weapon|nil The weapon that hit the target. May be nil in multiplayer due to desync issues. +---@field target Object The object that was hit. + +--- Event data structure for S_EVENT_KILL events. Occurs when a unit kills another unit. +--- (Data structure definition for EventDataKill. Not a globally accessible table.) +---@class EventDataKill +---@field id world.event The event ID that identifies the type of event. +---@field time number Timestamp when the event occurred in mission time. +---@field initiator Object|nil The initiating object (can be a Unit, StaticObject, or other object type depending on event). +---@field target Object The object that was killed. +---@field weapon Weapon|nil The weapon that caused the kill, if applicable. + +--- Event data structure for S_EVENT_LAND events. Occurs when an aircraft lands. +--- (Data structure definition for EventDataLand. Not a globally accessible table.) +---@class EventDataLand +---@field id world.event The event ID that identifies the type of event. +---@field time number Timestamp when the event occurred in mission time. +---@field initiator Object|nil The initiating object (can be a Unit, StaticObject, or other object type depending on event). +---@field place Airbase|nil The airbase or ship where the landing occurred. + +--- Event data structure for S_EVENT_SHOT events. Occurs whenever any unit fires a weapon. +--- (Data structure definition for EventDataShot. Not a globally accessible table.) +---@class EventDataShot +---@field id world.event The event ID that identifies the type of event. +---@field time number Timestamp when the event occurred in mission time. +---@field initiator Object|nil The initiating object (can be a Unit, StaticObject, or other object type depending on event). +---@field weapon Weapon The weapon that was fired. + +--- Event data structure for S_EVENT_TAKEOFF events. Occurs when an aircraft takes off. +--- (Data structure definition for EventDataTakeoff. Not a globally accessible table.) +---@class EventDataTakeoff +---@field id world.event The event ID that identifies the type of event. +---@field time number Timestamp when the event occurred in mission time. +---@field initiator Object|nil The initiating object (can be a Unit, StaticObject, or other object type depending on event). +---@field place Airbase|nil The airbase or ship from which the takeoff occurred. + +--- Maps event IDs to their respective event data types. Used to document which event types correspond to which event IDs. +--- (Data structure definition for EventTypeMap. Not a globally accessible table.) +---@class EventTypeMap +---@field S_EVENT_INVALID EventDataBase Event data for invalid events (ID 0). +---@field S_EVENT_SHOT EventDataShot Event data for shot events (ID 1). +---@field S_EVENT_HIT EventDataHit Event data for hit events (ID 2). +---@field S_EVENT_TAKEOFF EventDataTakeoff Event data for takeoff events (ID 3). +---@field S_EVENT_LAND EventDataLand Event data for landing events (ID 4). +---@field S_EVENT_CRASH EventDataGeneric Event data for crash events (ID 5). +---@field S_EVENT_EJECTION EventDataGeneric Event data for ejection events (ID 6). +---@field S_EVENT_REFUELING EventDataGeneric Event data for refueling events (ID 7). +---@field S_EVENT_DEAD EventDataGeneric Event data for dead events (ID 8). +---@field S_EVENT_PILOT_DEAD EventDataGeneric Event data for pilot dead events (ID 9). +---@field S_EVENT_BASE_CAPTURED EventDataBaseCaptured Event data for base captured events (ID 10). +---@field S_EVENT_MISSION_START EventDataGeneric Event data for mission start events (ID 11). +---@field S_EVENT_MISSION_END EventDataGeneric Event data for mission end events (ID 12). +---@field S_EVENT_TOOK_CONTROL EventDataGeneric Event data for took control events (ID 13). +---@field S_EVENT_REFUELING_STOP EventDataGeneric Event data for refueling stop events (ID 14). +---@field S_EVENT_BIRTH EventDataBirth Event data for birth/spawn events (ID 15). +---@field S_EVENT_HUMAN_FAILURE EventDataGeneric Event data for human failure events (ID 16). +---@field S_EVENT_DETAILED_FAILURE EventDataGeneric Event data for detailed failure events (ID 17). +---@field S_EVENT_ENGINE_STARTUP EventDataGeneric Event data for engine startup events (ID 18). +---@field S_EVENT_ENGINE_SHUTDOWN EventDataGeneric Event data for engine shutdown events (ID 19). +---@field S_EVENT_PLAYER_ENTER_UNIT EventDataGeneric Event data for player enter unit events (ID 20). +---@field S_EVENT_PLAYER_LEAVE_UNIT EventDataGeneric Event data for player leave unit events (ID 21). +---@field S_EVENT_PLAYER_COMMENT EventDataGeneric Event data for player comment events (ID 22). +---@field S_EVENT_SHOOTING_START EventDataShootingStart Event data for shooting start events (ID 23). +---@field S_EVENT_SHOOTING_END EventDataGeneric Event data for shooting end events (ID 24). +---@field S_EVENT_MARK_ADDED EventDataMarkBase Event data for mark added events (ID 25). +---@field S_EVENT_MARK_CHANGE EventDataMarkBase Event data for mark change events (ID 26). +---@field S_EVENT_MARK_REMOVED EventDataMarkBase Event data for mark removed events (ID 27). +---@field S_EVENT_KILL EventDataKill Event data for kill events (ID 28). +---@field S_EVENT_SCORE EventDataGeneric Event data for score events (ID 29). +---@field S_EVENT_UNIT_LOST EventDataGeneric Event data for unit lost events (ID 30). +---@field S_EVENT_LANDING_AFTER_EJECTION EventDataGeneric Event data for landing after ejection events (ID 31). +---@field S_EVENT_PARATROOPER_LENDING EventDataGeneric Event data for paratrooper landing events (ID 32). +---@field S_EVENT_DISCARD_CHAIR_AFTER_EJECTION EventDataGeneric Event data for discard chair after ejection events (ID 33). +---@field S_EVENT_WEAPON_ADD EventDataWeaponAdd Event data for weapon add events (ID 34). +---@field S_EVENT_TRIGGER_ZONE EventDataGeneric Event data for trigger zone events (ID 35). +---@field S_EVENT_LANDING_QUALITY_MARK EventDataGeneric Event data for landing quality mark events (ID 36). +---@field S_EVENT_BDA EventDataGeneric Event data for battle damage assessment events (ID 37). +---@field S_EVENT_AI_ABORT_MISSION EventDataGeneric Event data for AI abort mission events (ID 38). +---@field S_EVENT_DAYNIGHT EventDataGeneric Event data for day/night transition events (ID 39). +---@field S_EVENT_FLIGHT_TIME EventDataGeneric Event data for flight time events (ID 40). +---@field S_EVENT_PLAYER_SELF_KILL_PILOT EventDataGeneric Event data for player self-kill pilot events (ID 41). +---@field S_EVENT_PLAYER_CAPTURE_AIRFIELD EventDataGeneric Event data for player capture airfield events (ID 42). +---@field S_EVENT_EMERGENCY_LANDING EventDataGeneric Event data for emergency landing events (ID 43). +---@field S_EVENT_UNIT_CREATE_TASK EventDataGeneric Event data for unit create task events (ID 44). +---@field S_EVENT_UNIT_DELETE_TASK EventDataGeneric Event data for unit delete task events (ID 45). +---@field S_EVENT_SIMULATION_START EventDataGeneric Event data for simulation start events (ID 46). +---@field S_EVENT_WEAPON_REARM EventDataGeneric Event data for weapon rearm events (ID 47). +---@field S_EVENT_WEAPON_DROP EventDataGeneric Event data for weapon drop events (ID 48). +---@field S_EVENT_UNIT_TASK_COMPLETE EventDataGeneric Event data for unit task complete events (ID 49). +---@field S_EVENT_UNIT_TASK_STAGE EventDataGeneric Event data for unit task stage events (ID 50). +---@field S_EVENT_MAC_EXTRA_SCORE EventDataGeneric Event data for MAC extra score events (ID 51). +---@field S_EVENT_MISSION_RESTART EventDataGeneric Event data for mission restart events (ID 52). +---@field S_EVENT_MISSION_WINNER EventDataGeneric Event data for mission winner events (ID 53). +---@field S_EVENT_RUNWAY_TAKEOFF EventDataGeneric Event data for runway takeoff events (ID 54). +---@field S_EVENT_RUNWAY_TOUCH EventDataGeneric Event data for runway touch events (ID 55). +---@field S_EVENT_MAC_LMS_RESTART EventDataGeneric Event data for MAC LMS restart events (ID 56). +---@field S_EVENT_SIMULATION_FREEZE EventDataGeneric Event data for simulation freeze events (ID 57). +---@field S_EVENT_SIMULATION_UNFREEZE EventDataGeneric Event data for simulation unfreeze events (ID 58). +---@field S_EVENT_HUMAN_AIRCRAFT_REPAIR_START EventDataGeneric Event data for human aircraft repair start events (ID 59). +---@field S_EVENT_HUMAN_AIRCRAFT_REPAIR_FINISH EventDataGeneric Event data for human aircraft repair finish events (ID 60). +---@field S_EVENT_MAX EventDataBase Maximum event ID marker (ID 61). diff --git a/BuildScript/Mission.php b/BuildScript/Mission.php new file mode 100644 index 0000000..b568212 --- /dev/null +++ b/BuildScript/Mission.php @@ -0,0 +1,133 @@ + diff --git a/BuildScript/Script.php b/BuildScript/Script.php new file mode 100644 index 0000000..ada5159 --- /dev/null +++ b/BuildScript/Script.php @@ -0,0 +1,73 @@ + diff --git a/BuildScript/Warehouses.php b/BuildScript/Warehouses.php new file mode 100644 index 0000000..57ad972 --- /dev/null +++ b/BuildScript/Warehouses.php @@ -0,0 +1,57 @@ + diff --git a/Database/Aircraft/A-10A.lua b/Database/Aircraft/A-10A.lua new file mode 100644 index 0000000..84d632c --- /dev/null +++ b/Database/Aircraft/A-10A.lua @@ -0,0 +1,23 @@ +Library.aircraft["A-10A"] = { + altitude = DCSEx.converter.feetToMeters(20000), + liveries = { + [coalition.side.BLUE] = { "104th FS Maryland ANG, Baltimore (MD)", "118th FS Bradley ANGB, Connecticut (CT)", "118th FS Bradley ANGB, Connecticut (CT) N621", "172nd FS Battle Creek ANGB, Michigan (BC)", "184th FS Arkansas ANG, Fort Smith (FS)", "190th FS Boise ANGB, Idaho (ID)", "23rd TFW England AFB (EL)", "25th FS Osan AB, Korea (OS)", "354th FS Davis Monthan AFB, Arizona (DM)", "355th FS Eielson AFB, Alaska (AK)", "357th FS Davis Monthan AFB, Arizona (DM)", "358th FS Davis Monthan AFB, Arizona (DM)", "422nd TES Nellis AFB, Nevada (OT)", "47th FS Barksdale AFB, Louisiana (BD)", "66th WS Nellis AFB, Nevada (WA)", "74th FS Moody AFB, Georgia (FT)", "81st FS Spangdahlem AB, Germany (SP) 1", "81st FS Spangdahlem AB, Germany (SP) 2", "A-10 Grey" }, + [coalition.side.RED] = { "Fictional Russian Air Force 1", "Fictional Russian Air Force 2", "Algerian AF Fictional Desert", "Algerian AF Fictional Grey" }, + }, + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 240,flare = 120,fuel = 5029,ammo_type = 1,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{DB434044-F5D0-4F1F-9BA9-B73027E18DD3}" }, + [2] = { CLSID = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}" }, + [3] = { CLSID = "{69DC8AE7-8F77-427B-B8AA-B19D3F478B66}" }, + [4] = { CLSID = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}" }, + [5] = { CLSID = "{AB8B8299-F1CC-4359-89B5-2172E0CF4A5A}" }, + [7] = { CLSID = "{AB8B8299-F1CC-4359-89B5-2172E0CF4A5A}" }, + [8] = { CLSID = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}" }, + [9] = { CLSID = "{69DC8AE7-8F77-427B-B8AA-B19D3F478B66}" }, + [10] = { CLSID = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}" }, + [11] = { CLSID = "{6D21ECEA-F85B-4E8D-9D51-31DC9B8AA4EF}" }, + } + } +} diff --git a/Database/Aircraft/A-10C.lua b/Database/Aircraft/A-10C.lua new file mode 100644 index 0000000..c505a68 --- /dev/null +++ b/Database/Aircraft/A-10C.lua @@ -0,0 +1,22 @@ +Library.aircraft["A-10C"] = { + altitude = DCSEx.converter.feetToMeters(20000), + liveries = { + [coalition.side.BLUE] = { "104th FS Maryland ANG, Baltimore (MD)", "118th FS Bradley ANGB, Connecticut (CT)", "118th FS Bradley ANGB, Connecticut (CT) N621", "172nd FS Battle Creek ANGB, Michigan (BC)", "184th FS Arkansas ANG, Fort Smith (FS)", "190th FS Boise ANGB, Idaho (ID)", "23rd TFW England AFB (EL)", "25th FS Osan AB, Korea (OS)", "354th FS Davis Monthan AFB, Arizona (DM)", "355th FS Eielson AFB, Alaska (AK)", "357th FS Davis Monthan AFB, Arizona (DM)", "358th FS Davis Monthan AFB, Arizona (DM)", "422nd TES Nellis AFB, Nevada (OT)", "47th FS Barksdale AFB, Louisiana (BD)", "66th WS Nellis AFB, Nevada (WA)", "74th FS Moody AFB, Georgia (FT)", "81st FS Spangdahlem AB, Germany (SP) 1", "81st FS Spangdahlem AB, Germany (SP) 2", "A-10 Grey" }, + [coalition.side.RED] = { "Fictional Russian Air Force 1", "Fictional Russian Air Force 2", "Algerian AF Fictional Desert", "Algerian AF Fictional Grey" }, + }, + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 240,flare = 120,fuel = 5029,ammo_type = 1,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "ALQ_184" }, + [3] = { CLSID = "{E6A6262A-CA08-4B3D-B030-E1A993B98452}" }, + [4] = { CLSID = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}" }, + [5] = { CLSID = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}" }, + [7] = { CLSID = "{GBU-38}" }, + [8] = { CLSID = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}" }, + [9] = { CLSID = "{E6A6262A-CA08-4B3D-B030-E1A993B98453}" }, + [10] = { CLSID = "{A111396E-D3E8-4b9c-8AC9-2432489304D5}" }, + [11] = { CLSID = "{DB434044-F5D0-4F1F-9BA9-B73027E18DD3}" }, + } + } +} diff --git a/Database/Aircraft/A-10C_2.lua b/Database/Aircraft/A-10C_2.lua new file mode 100644 index 0000000..7ca663f --- /dev/null +++ b/Database/Aircraft/A-10C_2.lua @@ -0,0 +1,22 @@ +Library.aircraft["A-10C_2"] = { + altitude = DCSEx.converter.feetToMeters(20000), + liveries = { + [coalition.side.BLUE] = { "104th FS Maryland ANG, Baltimore (MD)", "118th FS Bradley ANGB, Connecticut (CT)", "118th FS Bradley ANGB, Connecticut (CT) N621", "172nd FS Battle Creek ANGB, Michigan (BC)", "184th FS Arkansas ANG, Fort Smith (FS)", "190th FS Boise ANGB, Idaho (ID)", "23rd TFW England AFB (EL)", "25th FS Osan AB, Korea (OS)", "354th FS Davis Monthan AFB, Arizona (DM)", "355th FS Eielson AFB, Alaska (AK)", "357th FS Davis Monthan AFB, Arizona (DM)", "358th FS Davis Monthan AFB, Arizona (DM)", "422nd TES Nellis AFB, Nevada (OT)", "47th FS Barksdale AFB, Louisiana (BD)", "66th WS Nellis AFB, Nevada (WA)", "74th FS Moody AFB, Georgia (FT)", "81st FS Spangdahlem AB, Germany (SP) 1", "81st FS Spangdahlem AB, Germany (SP) 2", "A-10 Grey" }, + [coalition.side.RED] = { "Fictional Russian Air Force 1", "Fictional Russian Air Force 2", "Algerian AF Fictional Desert", "Algerian AF Fictional Grey" }, + }, + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 240,flare = 120,fuel = 5029,ammo_type = 1,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "ALQ_184" }, + [3] = { CLSID = "{E6A6262A-CA08-4B3D-B030-E1A993B98452}" }, + [4] = { CLSID = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}" }, + [5] = { CLSID = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}" }, + [7] = { CLSID = "{GBU-38}" }, + [8] = { CLSID = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}" }, + [9] = { CLSID = "{E6A6262A-CA08-4B3D-B030-E1A993B98453}" }, + [10] = { CLSID = "{A111396E-D3E8-4b9c-8AC9-2432489304D5}" }, + [11] = { CLSID = "{DB434044-F5D0-4F1F-9BA9-B73027E18DD3}" }, + } + } +} diff --git a/Database/Aircraft/A-50.lua b/Database/Aircraft/A-50.lua new file mode 100644 index 0000000..1267dac --- /dev/null +++ b/Database/Aircraft/A-50.lua @@ -0,0 +1,6 @@ +Library.aircraft["A-50"] = { + altitude = DCSEx.converter.feetToMeters(32000), + options = { "eplrs" }, + payload = { chaff = 192, flare = 192, fuel = 70000 }, + speed = DCSEx.converter.knotsToMps(300), +} diff --git a/Database/Aircraft/AH-1W.lua b/Database/Aircraft/AH-1W.lua new file mode 100644 index 0000000..2e15a7e --- /dev/null +++ b/Database/Aircraft/AH-1W.lua @@ -0,0 +1,13 @@ +Library.aircraft["AH-1W"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 30,flare = 30,fuel = 1250 }, + pylons = { + default = { + [1] = { CLSID = "{3EA17AB0-A805-4D9E-8732-4CE00CB00F17}" }, + [2] = { CLSID = "[M260_HYDRA}" }, + [3] = { CLSID = "[M260_HYDRA}" }, + [4] = { CLSID = "{3EA17AB0-A805-4D9E-8732-4CE00CB00F17}" }, + } + } +} diff --git a/Database/Aircraft/AH-64A.lua b/Database/Aircraft/AH-64A.lua new file mode 100644 index 0000000..929bb52 --- /dev/null +++ b/Database/Aircraft/AH-64A.lua @@ -0,0 +1,13 @@ +Library.aircraft["AH-64A"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 30,flare = 30,fuel = 1157 }, + pylons = { + default = { + [1] = { CLSID = "{88D18A5E-99C8-4B04-B40B-1C02F2018B6E}" }, + [2] = { CLSID = "{3DFB7321-AB0E-11d7-9897-000476191836}" }, + [3] = { CLSID = "{3DFB7321-AB0E-11d7-9897-000476191836}" }, + [4] = { CLSID = "{88D18A5E-99C8-4B04-B40B-1C02F2018B6E}" }, + } + } +} diff --git a/Database/Aircraft/AH-64D.lua b/Database/Aircraft/AH-64D.lua new file mode 100644 index 0000000..fc6fa74 --- /dev/null +++ b/Database/Aircraft/AH-64D.lua @@ -0,0 +1,13 @@ +Library.aircraft["AH-64D"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 30,flare = 30,fuel = 1157 }, + pylons = { + default = { + [1] = { CLSID = "{88D18A5E-99C8-4B04-B40B-1C02F2018B6E}" }, + [2] = { CLSID = "{3DFB7321-AB0E-11d7-9897-000476191836}" }, + [3] = { CLSID = "{3DFB7321-AB0E-11d7-9897-000476191836}" }, + [4] = { CLSID = "{88D18A5E-99C8-4B04-B40B-1C02F2018B6E}" }, + } + } +} diff --git a/Database/Aircraft/AJS37.lua b/Database/Aircraft/AJS37.lua new file mode 100644 index 0000000..fa8c3a0 --- /dev/null +++ b/Database/Aircraft/AJS37.lua @@ -0,0 +1,25 @@ +Library.aircraft["AJS37"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 105,flare = 36,fuel = 4476,gun = 100 }, + properties = { Rb04GroupTarget = 3, WeapSafeHeight = 1, Rb04VinkelHopp = 0, MissionGeneratorSetting = 0 }, + pylons = { + default = { + [2] = { CLSID = "{Robot74}" }, + [3] = { CLSID = "{RB75T}" }, + [4] = { CLSID = "{VIGGEN_X-TANK}" }, + [5] = { CLSID = "{RB75T}" }, + [6] = { CLSID = "{Robot74}" }, + } + } + -- Payload.Decade2000.Task.AirToAir.pylon02={Robot74} + -- Payload.Decade2000.Task.AirToAir.pylon03={Robot05} + -- Payload.Decade2000.Task.AirToAir.pylon04={VIGGEN_X-TANK} + -- Payload.Decade2000.Task.AirToAir.pylon05={Robot05} + -- Payload.Decade2000.Task.AirToAir.pylon06={Robot74} + -- Payload.Decade2000.Task.Antiship.pylon02={RB75T} + -- Payload.Decade2000.Task.Antiship.pylon03={RB75T} + -- Payload.Decade2000.Task.Antiship.pylon04={VIGGEN_X-TANK} + -- Payload.Decade2000.Task.Antiship.pylon05={RB75T} + -- Payload.Decade2000.Task.Antiship.pylon06={RB75T} +} diff --git a/Database/Aircraft/AV8BNA.lua b/Database/Aircraft/AV8BNA.lua new file mode 100644 index 0000000..cf04484 --- /dev/null +++ b/Database/Aircraft/AV8BNA.lua @@ -0,0 +1,29 @@ +Library.aircraft["AV8BNA"] = { + altitude = DCSEx.converter.feetToMeters(20000), + liveries = { + [coalition.side.BLUE] = { "VMA-211", "VMA-211D", "VMA-214", "VMA-214D", "VMA-223D", "VMA-231-1", "VMA-231-2", "VMA-231D", "VMA-311", "VMA-311D", "VMA-513", "VMA-513D", "VMA-542", "VMAT-203", "VMAT-203S" }, + [coalition.side.RED] = { "default" }, + }, + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 60,flare = 120,fuel = 3519.423,gun = 100 }, + properties = { EWDispenserTBL = 2, WpBullseye = 0, EWDispenserBR = 2, AAR_Zone3 = 0, AAR_Zone2 = 0, EWDispenserTFR = 1, AAR_Zone1 = 0, ClockTime = 1, RocketBurst = 1, LaserCode100 = 6, LaserCode1 = 8, EWDispenserTFL = 1, EWDispenserBL = 2, EWDispenserTBR = 2, LaserCode10 = 8, MountNVG = false }, + pylons = { + default = { + [1] = { CLSID = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}" }, + [2] = { CLSID = "LAU_117_AGM_65G" }, + [3] = { CLSID = "LAU_117_AGM_65G" }, + [4] = { CLSID = "{GAU_12_Equalizer}" }, + [6] = { CLSID = "LAU_117_AGM_65G" }, + [7] = { CLSID = "LAU_117_AGM_65G" }, + [8] = { CLSID = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}" }, + -- Payload.Decade2000.Task.sead.pylon01={AGM_122_SIDEARM} + -- Payload.Decade2000.Task.sead.pylon02=LAU_117_AGM_65G + -- Payload.Decade2000.Task.sead.pylon03=LAU_117_AGM_65G + -- Payload.Decade2000.Task.sead.pylon04={GAU_12_Equalizer} + -- Payload.Decade2000.Task.sead.pylon05={ALQ_164_RF_Jammer} + -- Payload.Decade2000.Task.sead.pylon06=LAU_117_AGM_65G + -- Payload.Decade2000.Task.sead.pylon07=LAU_117_AGM_65G + -- Payload.Decade2000.Task.sead.pylon08={AGM_122_SIDEARM} + } + } +} diff --git a/Database/Aircraft/An-26B.lua b/Database/Aircraft/An-26B.lua new file mode 100644 index 0000000..7b7c3c9 --- /dev/null +++ b/Database/Aircraft/An-26B.lua @@ -0,0 +1,5 @@ +Library.aircraft["An-26B"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 384,flare = 384,fuel = 5500 }, +} diff --git a/Database/Aircraft/An-30M.lua b/Database/Aircraft/An-30M.lua new file mode 100644 index 0000000..e022bdf --- /dev/null +++ b/Database/Aircraft/An-30M.lua @@ -0,0 +1,5 @@ +Library.aircraft["An-30M"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 192,flare = 192,fuel = 8300 }, +} diff --git a/Database/Aircraft/B-1B.lua b/Database/Aircraft/B-1B.lua new file mode 100644 index 0000000..9f4b059 --- /dev/null +++ b/Database/Aircraft/B-1B.lua @@ -0,0 +1,12 @@ +Library.aircraft["B-1B"] = { + altitude = DCSEx.converter.feetToMeters(30000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 60,flare = 30,fuel = 88450,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "GBU-38*16" }, + [2] = { CLSID = "GBU-38*16" }, + [3] = { CLSID = "GBU-38*16" }, + } + } +} diff --git a/Database/Aircraft/B-52H.lua b/Database/Aircraft/B-52H.lua new file mode 100644 index 0000000..64cce8e --- /dev/null +++ b/Database/Aircraft/B-52H.lua @@ -0,0 +1,12 @@ +Library.aircraft["B-52H"] = { + altitude = DCSEx.converter.feetToMeters(30000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 1125,flare = 192,fuel = 141135,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{585D626E-7F42-4073-AB70-41E728C333E2}" }, + [2] = { CLSID = "{6C47D097-83FF-4FB2-9496-EAB36DDF0B05}" }, + [3] = { CLSID = "{585D626E-7F42-4073-AB70-41E728C333E2}" }, + } + } +} diff --git a/Database/Aircraft/Bf-109K-4.lua b/Database/Aircraft/Bf-109K-4.lua new file mode 100644 index 0000000..f45c018 --- /dev/null +++ b/Database/Aircraft/Bf-109K-4.lua @@ -0,0 +1,10 @@ +-- Library.aircraft["Bf-109K-4"] = { +-- altitude = DCSEx.converter.feetToMeters(20000), +-- speed = DCSEx.converter.knotsToMps(225), +-- payload = { chaff = 0,flare = 0,fuel = 296,ammo_type = 1 }, +-- properties = { MW50TankContents = 1, Flare_Gun = 1 }, +-- [Payload] +-- Payload.Decade2000.Task.Antiship.Pylon01=SC_501_SC500 +-- Payload.Decade2000.Task.AirToGround.Pylon01=SC_501_SC250 +-- } +-- } diff --git a/Database/Aircraft/C-101CC.lua b/Database/Aircraft/C-101CC.lua new file mode 100644 index 0000000..3b35176 --- /dev/null +++ b/Database/Aircraft/C-101CC.lua @@ -0,0 +1,17 @@ +Library.aircraft["C-101CC"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 0,flare = 0,fuel = 1881.5 }, + properties = { SoloFlight = false, MountIFRHood = false, CameraRecorder = false, SightSunFilter = false, NetCrewControlPriority = 1, NS430allow = 1 }, + pylons = { + default = { + [1] = { CLSID = "{9BFD8C90-F7AE-4e90-833B-BFD0CED0E536}" }, + [2] = { CLSID = "{BR_250}" }, + [3] = { CLSID = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}" }, + [4] = { CLSID = "{C-101-DEFA553}" }, + [5] = { CLSID = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}" }, + [6] = { CLSID = "{BR_250}" }, + [7] = { CLSID = "{9BFD8C90-F7AE-4e90-833B-BFD0CED0E536}" }, + } + } +} diff --git a/Database/Aircraft/C-130.lua b/Database/Aircraft/C-130.lua new file mode 100644 index 0000000..40689f8 --- /dev/null +++ b/Database/Aircraft/C-130.lua @@ -0,0 +1,5 @@ +Library.aircraft["C-130"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 120,flare = 60,fuel = 20830 }, +} diff --git a/Database/Aircraft/C-17A.lua b/Database/Aircraft/C-17A.lua new file mode 100644 index 0000000..579c1d3 --- /dev/null +++ b/Database/Aircraft/C-17A.lua @@ -0,0 +1,5 @@ +Library.aircraft["C-17A"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 120,flare = 60,fuel = 132405 }, +} diff --git a/Database/Aircraft/CH-47D.lua b/Database/Aircraft/CH-47D.lua new file mode 100644 index 0000000..6a6ea60 --- /dev/null +++ b/Database/Aircraft/CH-47D.lua @@ -0,0 +1,5 @@ +Library.aircraft["CH-47D"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 120,flare = 120,fuel = 3600 }, +} diff --git a/Database/Aircraft/CH-53E.lua b/Database/Aircraft/CH-53E.lua new file mode 100644 index 0000000..f115633 --- /dev/null +++ b/Database/Aircraft/CH-53E.lua @@ -0,0 +1,5 @@ +Library.aircraft["CH-53E"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 60,flare = 60,fuel = 1908 }, +} diff --git a/Database/Aircraft/E-2C.lua b/Database/Aircraft/E-2C.lua new file mode 100644 index 0000000..b75f656 --- /dev/null +++ b/Database/Aircraft/E-2C.lua @@ -0,0 +1,6 @@ +Library.aircraft["E-2C"] = { + altitude = DCSEx.converter.feetToMeters(32000), + options = { "eplrs" }, + payload = { chaff = 120, flare = 60, fuel = 5624 }, + speed = DCSEx.converter.knotsToMps(240), +} diff --git a/Database/Aircraft/E-3A.lua b/Database/Aircraft/E-3A.lua new file mode 100644 index 0000000..f347758 --- /dev/null +++ b/Database/Aircraft/E-3A.lua @@ -0,0 +1,6 @@ +Library.aircraft["E-3A"] = { + altitude = DCSEx.converter.feetToMeters(32000), + options = { "eplrs" }, + payload = { chaff = 120, flare = 60, fuel = 65000 }, + speed = DCSEx.converter.knotsToMps(300), +} diff --git a/Database/Aircraft/F-117A.lua b/Database/Aircraft/F-117A.lua new file mode 100644 index 0000000..9dd4cf2 --- /dev/null +++ b/Database/Aircraft/F-117A.lua @@ -0,0 +1,11 @@ +Library.aircraft["F-117A"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 0,flare = 0,fuel = 3840 }, + pylons = { + default = { + [1] = { CLSID = "{EF0A9419-01D6-473B-99A3-BEBDB923B14D}" }, + [2] = { CLSID = "{EF0A9419-01D6-473B-99A3-BEBDB923B14D}" }, + } + } +} diff --git a/Database/Aircraft/F-14A-135-GR.lua b/Database/Aircraft/F-14A-135-GR.lua new file mode 100644 index 0000000..1c0ab3d --- /dev/null +++ b/Database/Aircraft/F-14A-135-GR.lua @@ -0,0 +1,24 @@ +Library.aircraft["F-14A-135-GR"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 140,flare = 60,fuel = 7348 }, + properties = { LGB100 = 6, M61BURST = 0, IlsChannel = 1, LGB1 = 8, KY28Key = 1, TacanBand = 0, TacanChannel = 0, LGB1000 = 1, LGB10 = 8, INSAlignmentStored = true, UseLAU138 = true, ALE39Loadout = 0 }, + pylons = { + default = { + [1] = { CLSID = "{LAU-138 wtip - AIM-9M}" }, + [2] = { CLSID = "{SHOULDER AIM_54A_Mk47 L}" }, + [3] = { CLSID = "{F14-300gal}" }, + [4] = { CLSID = "{AIM_54A_Mk47}" }, + [5] = { CLSID = "{AIM_54A_Mk47}" }, + [6] = { CLSID = "{AIM_54A_Mk47}" }, + [7] = { CLSID = "{AIM_54A_Mk47}" }, + [8] = { CLSID = "{F14-300gal}" }, + [9] = { CLSID = "{SHOULDER AIM_54A_Mk47 R}" }, + [10] = { CLSID = "{LAU-138 wtip - AIM-9M}" }, + -- Payload.Decade2000.Task.AirToGround.Pylon04={BRU-32 MK-84} + -- Payload.Decade2000.Task.AirToGround.Pylon05={BRU-32 MK-84} + -- Payload.Decade2000.Task.AirToGround.Pylon06={BRU-32 MK-84} + -- Payload.Decade2000.Task.AirToGround.Pylon07={BRU-32 MK-84} + } + } +} diff --git a/Database/Aircraft/F-14A.lua b/Database/Aircraft/F-14A.lua new file mode 100644 index 0000000..0f92cfe --- /dev/null +++ b/Database/Aircraft/F-14A.lua @@ -0,0 +1,24 @@ +Library.aircraft["F-14A"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 30,flare = 15,fuel = 7348 }, + pylons = { + default = { + [1] = { CLSID = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}" }, + [2] = { CLSID = "{7575BA0B-7294-4844-857B-031A144B2595}" }, + [4] = { CLSID = "{7575BA0B-7294-4844-857B-031A144B2595}" }, + [5] = { CLSID = "{7575BA0B-7294-4844-857B-031A144B2595}" }, + [8] = { CLSID = "{7575BA0B-7294-4844-857B-031A144B2595}" }, + [9] = { CLSID = "{7575BA0B-7294-4844-857B-031A144B2595}" }, + [11] = { CLSID = "{7575BA0B-7294-4844-857B-031A144B2595}" }, + [12] = { CLSID = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}" }, + -- Payload.Decade2000.Task.AirToGround.Pylon01={9BFD8C90-F7AE-4e90-833B-BFD0CED0E536} + -- Payload.Decade2000.Task.AirToGround.Pylon02={BCE4E030-38E9-423E-98ED-24BE3DA87C32} + -- Payload.Decade2000.Task.AirToGround.Pylon03={BCE4E030-38E9-423E-98ED-24BE3DA87C32} + -- Payload.Decade2000.Task.AirToGround.Pylon04={0395076D-2F77-4420-9D33-087A4398130B} + -- Payload.Decade2000.Task.AirToGround.Pylon05={BCE4E030-38E9-423E-98ED-24BE3DA87C32} + -- Payload.Decade2000.Task.AirToGround.Pylon06={BCE4E030-38E9-423E-98ED-24BE3DA87C32} + -- Payload.Decade2000.Task.AirToGround.Pylon07={9BFD8C90-F7AE-4e90-833B-BFD0CED0E536} + } + } +} diff --git a/Database/Aircraft/F-14B.lua b/Database/Aircraft/F-14B.lua new file mode 100644 index 0000000..cfbefe4 --- /dev/null +++ b/Database/Aircraft/F-14B.lua @@ -0,0 +1,24 @@ +Library.aircraft["F-14B"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 140,flare = 60,fuel = 7348 }, + properties = { LGB100 = 6, M61BURST = 0, IlsChannel = 1, LGB1 = 8, KY28Key = 1, TacanBand = 0, TacanChannel = 0, LGB1000 = 1, LGB10 = 8, INSAlignmentStored = true, UseLAU138 = true, ALE39Loadout = 0 }, + pylons = { + default = { + [1] = { CLSID = "{LAU-138 wtip - AIM-9M}" }, + [2] = { CLSID = "{SHOULDER AIM_54A_Mk47 L}" }, + [3] = { CLSID = "{F14-300gal}" }, + [4] = { CLSID = "{AIM_54A_Mk47}" }, + [5] = { CLSID = "{AIM_54A_Mk47}" }, + [6] = { CLSID = "{AIM_54A_Mk47}" }, + [7] = { CLSID = "{AIM_54A_Mk47}" }, + [8] = { CLSID = "{F14-300gal}" }, + [9] = { CLSID = "{SHOULDER AIM_54A_Mk47 R}" }, + [10] = { CLSID = "{LAU-138 wtip - AIM-9M}" }, + -- Payload.Decade2000.Task.AirToGround.Pylon04={BRU-32 MK-84} + -- Payload.Decade2000.Task.AirToGround.Pylon05={BRU-32 MK-84} + -- Payload.Decade2000.Task.AirToGround.Pylon06={BRU-32 MK-84} + -- Payload.Decade2000.Task.AirToGround.Pylon07={BRU-32 MK-84} + } + } +} diff --git a/Database/Aircraft/F-15C.lua b/Database/Aircraft/F-15C.lua new file mode 100644 index 0000000..bdc7e19 --- /dev/null +++ b/Database/Aircraft/F-15C.lua @@ -0,0 +1,18 @@ +Library.aircraft["F-15C"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 120,flare = 60,fuel = 6103 }, + pylons = { + default = { + [1] = { CLSID = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}" }, + [3] = { CLSID = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}" }, + [4] = { CLSID = "{C8E06185-7CD6-4C90-959F-044679E90751}" }, + [5] = { CLSID = "{C8E06185-7CD6-4C90-959F-044679E90751}" }, + [6] = { CLSID = "{E1F29B21-F291-4589-9FD8-3272EEC69506}" }, + [7] = { CLSID = "{C8E06185-7CD6-4C90-959F-044679E90751}" }, + [8] = { CLSID = "{C8E06185-7CD6-4C90-959F-044679E90751}" }, + [9] = { CLSID = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}" }, + [11] = { CLSID = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}" }, + } + } +} diff --git a/Database/Aircraft/F-15E.lua b/Database/Aircraft/F-15E.lua new file mode 100644 index 0000000..d25ef3f --- /dev/null +++ b/Database/Aircraft/F-15E.lua @@ -0,0 +1,24 @@ +Library.aircraft["F-15E"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 120,flare = 60,fuel = 10246 }, + pylons = { + default = { + [1] = { CLSID = "{C8E06185-7CD6-4C90-959F-044679E90751}" }, + [2] = { CLSID = "{9BCC2A2B-5708-4860-B1F1-053A18442067}" }, + [3] = { CLSID = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}" }, + [4] = { CLSID = "{GBU-38}" }, + [6] = { CLSID = "{GBU-38}" }, + [7] = { CLSID = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}" }, + [9] = { CLSID = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}" }, + [10] = { CLSID = "{E1F29B21-F291-4589-9FD8-3272EEC69506}" }, + [11] = { CLSID = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}" }, + [13] = { CLSID = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}" }, + [14] = { CLSID = "{GBU-38}" }, + [16] = { CLSID = "{GBU-38}" }, + [17] = { CLSID = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}" }, + [18] = { CLSID = "{9BCC2A2B-5708-4860-B1F1-053A18442067}" }, + [19] = { CLSID = "{C8E06185-7CD6-4C90-959F-044679E90751}" }, + } + } +} diff --git a/Database/Aircraft/F-16A.lua b/Database/Aircraft/F-16A.lua new file mode 100644 index 0000000..46a6526 --- /dev/null +++ b/Database/Aircraft/F-16A.lua @@ -0,0 +1,38 @@ +Library.aircraft["F-16A"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { ammo_type = 5,chaff = 60,flare = 60,fuel = 3249,gun = 100 }, + properties = { LaserCode1 = 8, LaserCode100 = 6, LAU3ROF = 0, LaserCode10 = 8 }, + pylons = { + default = { + [1] = { CLSID = "{C8E06185-7CD6-4C90-959F-044679E90751}" }, + [2] = { CLSID = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}" }, + [3] = { CLSID = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}" }, + [4] = { CLSID = "{F376DBEE-4CAE-41BA-ADD9-B2910AC95DEC}" }, + [5] = { CLSID = "{CAAC1CFD-6745-416B-AFA4-CB57414856D0}" }, + [6] = { CLSID = "{6D21ECEA-F85B-4E8D-9D51-31DC9B8AA4EF}" }, + [7] = { CLSID = "{F376DBEE-4CAE-41BA-ADD9-B2910AC95DEC}" }, + [8] = { CLSID = "{DB769D48-67D7-42ED-A2BE-108D566C8B1E}" }, + [9] = { CLSID = "{6CEB49FC-DED8-4DED-B053-E1F033FF72D3}" }, + [10] = { CLSID = "{C8E06185-7CD6-4C90-959F-044679E90751}" }, + -- Payload.Decade2000.Task.AirToAir.Pylon01={C8E06185-7CD6-4C90-959F-044679E90751} + -- Payload.Decade2000.Task.AirToAir.Pylon02={C8E06185-7CD6-4C90-959F-044679E90751} + -- Payload.Decade2000.Task.AirToAir.Pylon03={C8E06185-7CD6-4C90-959F-044679E90751} + -- Payload.Decade2000.Task.AirToAir.Pylon04={F376DBEE-4CAE-41BA-ADD9-B2910AC95DEC} + -- Payload.Decade2000.Task.AirToAir.Pylon06={6D21ECEA-F85B-4E8D-9D51-31DC9B8AA4EF} + -- Payload.Decade2000.Task.AirToAir.Pylon07={F376DBEE-4CAE-41BA-ADD9-B2910AC95DEC} + -- Payload.Decade2000.Task.AirToAir.Pylon08={C8E06185-7CD6-4C90-959F-044679E90751} + -- Payload.Decade2000.Task.AirToAir.Pylon09={C8E06185-7CD6-4C90-959F-044679E90751} + -- Payload.Decade2000.Task.AirToAir.Pylon10={C8E06185-7CD6-4C90-959F-044679E90751} + -- Payload.Decade2000.Task.SEAD.Pylon01={C8E06185-7CD6-4C90-959F-044679E90751} + -- Payload.Decade2000.Task.SEAD.Pylon02={6CEB49FC-DED8-4DED-B053-E1F033FF72D3} + -- Payload.Decade2000.Task.SEAD.Pylon03={B06DD79A-F21E-4EB9-BD9D-AB3844618C93} + -- Payload.Decade2000.Task.SEAD.Pylon04={F376DBEE-4CAE-41BA-ADD9-B2910AC95DEC} + -- Payload.Decade2000.Task.SEAD.Pylon06={6D21ECEA-F85B-4E8D-9D51-31DC9B8AA4EF} + -- Payload.Decade2000.Task.SEAD.Pylon07={F376DBEE-4CAE-41BA-ADD9-B2910AC95DEC} + -- Payload.Decade2000.Task.SEAD.Pylon08={B06DD79A-F21E-4EB9-BD9D-AB3844618C93} + -- Payload.Decade2000.Task.SEAD.Pylon09={6CEB49FC-DED8-4DED-B053-E1F033FF72D3} + -- Payload.Decade2000.Task.SEAD.Pylon10={C8E06185-7CD6-4C90-959F-044679E90751} + } + } +} diff --git a/Database/Aircraft/F-16C_50.lua b/Database/Aircraft/F-16C_50.lua new file mode 100644 index 0000000..1d3eb6c --- /dev/null +++ b/Database/Aircraft/F-16C_50.lua @@ -0,0 +1,31 @@ +Library.aircraft["F-16C_50"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { ammo_type = 5,chaff = 60,flare = 60,fuel = 3249,gun = 100 }, + properties = { LaserCode1 = 8, LaserCode100 = 6, LAU3ROF = 0, LaserCode10 = 8, HelmetMountedDevice = 1 }, + pylons = { + default = { + [1] = { CLSID = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}" }, + [2] = { CLSID = "{5CE2FF2A-645A-4197-B48D-8720AC69394F}" }, + [3] = { CLSID = "{TER_9A_2L*GBU-12}" }, + [4] = { CLSID = "{F376DBEE-4CAE-41BA-ADD9-B2910AC95DEC}" }, + [5] = { CLSID = "" }, + [6] = { CLSID = "{F376DBEE-4CAE-41BA-ADD9-B2910AC95DEC}" }, + [7] = { CLSID = "{TER_9A_2R*GBU-12}" }, + [8] = { CLSID = "{5CE2FF2A-645A-4197-B48D-8720AC69394F}" }, + [9] = { CLSID = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}" }, + [11] = { CLSID = "{A111396E-D3E8-4b9c-8AC9-2432489304D5}" }, + }, + cap = { + [1] = { CLSID = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}" }, + [2] = { CLSID = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}" }, + [3] = { CLSID = "{5CE2FF2A-645A-4197-B48D-8720AC69394F}" }, + [4] = { CLSID = "{F376DBEE-4CAE-41BA-ADD9-B2910AC95DEC}" }, + [5] = { CLSID = "" }, + [6] = { CLSID = "{F376DBEE-4CAE-41BA-ADD9-B2910AC95DEC}" }, + [7] = { CLSID = "{5CE2FF2A-645A-4197-B48D-8720AC69394F}" }, + [8] = { CLSID = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}" }, + [9] = { CLSID = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}" }, + }, + } +} diff --git a/Database/Aircraft/F-5E-3.lua b/Database/Aircraft/F-5E-3.lua new file mode 100644 index 0000000..60578c2 --- /dev/null +++ b/Database/Aircraft/F-5E-3.lua @@ -0,0 +1,23 @@ +Library.aircraft["F-5E-3"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 30,flare = 15,fuel = 2046,ammo_type = 2,gun = 100, }, + properties = { LAU68ROF = 0,ChaffSalvo = 0,ChaffSalvoInt = 0,LAU3ROF = 0,ChaffBurstInt = 0,LaserCode100 = 6,LaserCode1 = 8,FlareBurstInt = 0,FlareBurst = 0,LaserCode10 = 8,ChaffBurst = 0 }, + pylons = { + default = { + [1] = { CLSID = "{AIM-9P5}" }, + [3] = { CLSID = "{0395076D-2F77-4420-9D33-087A4398130B}" }, + [4] = { CLSID = "{0395076D-2F77-4420-9D33-087A4398130B}" }, + [5] = { CLSID = "{0395076D-2F77-4420-9D33-087A4398130B}" }, + [7] = { CLSID = "{AIM-9P5}" }, + -- pylons = { + -- [1] = { CLSID = "{9BFD8C90-F7AE-4e90-833B-BFD0CED0E536}" }, + -- [2] = { CLSID = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}" }, + -- [3] = { CLSID = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}" }, + -- [4] = { CLSID = "{0395076D-2F77-4420-9D33-087A4398130B}" }, + -- [5] = { CLSID = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}" }, + -- [6] = { CLSID = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}" }, + -- [7] = { CLSID = "{9BFD8C90-F7AE-4e90-833B-BFD0CED0E536}" }, + } + } +} diff --git a/Database/Aircraft/F-5E.lua b/Database/Aircraft/F-5E.lua new file mode 100644 index 0000000..f9436b7 --- /dev/null +++ b/Database/Aircraft/F-5E.lua @@ -0,0 +1,22 @@ +Library.aircraft["F-5E"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 30,flare = 15,fuel = 2046 }, + pylons = { + default = { + [1] = { CLSID = "{AIM-9P5}" }, + [3] = { CLSID = "{0395076D-2F77-4420-9D33-087A4398130B}" }, + [4] = { CLSID = "{0395076D-2F77-4420-9D33-087A4398130B}" }, + [5] = { CLSID = "{0395076D-2F77-4420-9D33-087A4398130B}" }, + [7] = { CLSID = "{AIM-9P5}" }, + -- pylons = { + -- [1] = { CLSID = "{9BFD8C90-F7AE-4e90-833B-BFD0CED0E536}" }, + -- [2] = { CLSID = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}" }, + -- [3] = { CLSID = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}" }, + -- [4] = { CLSID = "{0395076D-2F77-4420-9D33-087A4398130B}" }, + -- [5] = { CLSID = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}" }, + -- [6] = { CLSID = "{BCE4E030-38E9-423E-98ED-24BE3DA87C32}" }, + -- [7] = { CLSID = "{9BFD8C90-F7AE-4e90-833B-BFD0CED0E536}" }, + } + } +} diff --git a/Database/Aircraft/F-86F Sabre.lua b/Database/Aircraft/F-86F Sabre.lua new file mode 100644 index 0000000..3824842 --- /dev/null +++ b/Database/Aircraft/F-86F Sabre.lua @@ -0,0 +1,17 @@ +Library.aircraft["F-86F Sabre"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 0,flare = 0,fuel = 1282,gun = 100 }, + pylons = { + default = { + [4] = { CLSID = "{PTB_120_F86F35}" }, + [5] = { CLSID = "{GAR-8}" }, + [6] = { CLSID = "{GAR-8}" }, + [7] = { CLSID = "{PTB_120_F86F35}" }, + -- Payload.Decade2000.Task.AirToGround.Pylon01={PTB_200_F86F35} + -- Payload.Decade2000.Task.AirToGround.Pylon04={F86ANM64} + -- Payload.Decade2000.Task.AirToGround.Pylon07={F86ANM64} + -- Payload.Decade2000.Task.AirToGround.Pylon10={PTB_200_F86F35} + } + } +} diff --git a/Database/Aircraft/FA-18C_hornet.lua b/Database/Aircraft/FA-18C_hornet.lua new file mode 100644 index 0000000..2ecf9ef --- /dev/null +++ b/Database/Aircraft/FA-18C_hornet.lua @@ -0,0 +1,30 @@ +Library.aircraft["FA-18C_hornet"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 60,flare = 30,fuel = 4900,gun = 100 }, + properties = { OuterBoard = 0, InnerBoard = 0, HelmetMountedDevice = 1 }, + pylons = { + default = { + [1] = { CLSID = "{5CE2FF2A-645A-4197-B48D-8720AC69394F}" }, + [2] = { CLSID = "LAU_117_AGM_65F" }, + [3] = { CLSID = "{BRU33_2X_GBU-12}" }, + [4] = { CLSID = "{AAQ-28_LEFT}" }, + [5] = { CLSID = "{FPU_8A_FUEL_TANK}" }, + [6] = { CLSID = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}" }, + [7] = { CLSID = "{BRU33_2X_GBU-12}" }, + [8] = { CLSID = "{F16A4DE0-116C-4A71-97F0-2CF85B0313EC}" }, + [9] = { CLSID = "{5CE2FF2A-645A-4197-B48D-8720AC69394F}" }, + }, + cap = { + [1] = { CLSID = "{5CE2FF2A-645A-4197-B48D-8720AC69394F}" }, + [2] = { CLSID = "LAU-115_2*LAU-127_AIM-120C" }, + [3] = { CLSID = "{FPU_8A_FUEL_TANK}" }, + [4] = { CLSID = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}" }, + [5] = { CLSID = "{FPU_8A_FUEL_TANK}" }, + [6] = { CLSID = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}" }, + [7] = { CLSID = "{FPU_8A_FUEL_TANK}" }, + [8] = { CLSID = "LAU-115_2*LAU-127_AIM-120C" }, + [9] = { CLSID = "{5CE2FF2A-645A-4197-B48D-8720AC69394F}" }, + }, + } +} diff --git a/Database/Aircraft/I-16.lua b/Database/Aircraft/I-16.lua new file mode 100644 index 0000000..90b99d2 --- /dev/null +++ b/Database/Aircraft/I-16.lua @@ -0,0 +1,14 @@ +Library.aircraft["I-16"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(225), + payload = { chaff = 0,flare = 0,fuel = 191,gun = 100 }, + properties = { landingTorch = false }, + -- Payload.Decade2000.Task.AirToGround.Pylon01=I16_RS_82 + -- Payload.Decade2000.Task.AirToGround.Pylon02=I16_RS_82 + -- Payload.Decade2000.Task.AirToGround.Pylon03=I16_RS_82 + -- Payload.Decade2000.Task.AirToGround.Pylon04=I16_FAB_100SV + -- Payload.Decade2000.Task.AirToGround.Pylon05=I16_FAB_100SV + -- Payload.Decade2000.Task.AirToGround.Pylon06=I16_RS_82 + -- Payload.Decade2000.Task.AirToGround.Pylon07=I16_RS_82 + -- Payload.Decade2000.Task.AirToGround.Pylon08=I16_RS_82 +} diff --git a/Database/Aircraft/IL-76MD.lua b/Database/Aircraft/IL-76MD.lua new file mode 100644 index 0000000..55fe4b0 --- /dev/null +++ b/Database/Aircraft/IL-76MD.lua @@ -0,0 +1,5 @@ +Library.aircraft["IL-76MD"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 96,flare = 96,fuel = 80000 }, +} diff --git a/Database/Aircraft/IL-78M.lua b/Database/Aircraft/IL-78M.lua new file mode 100644 index 0000000..b06cb92 --- /dev/null +++ b/Database/Aircraft/IL-78M.lua @@ -0,0 +1,5 @@ +Library.aircraft["IL-78M"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 96,flare = 96,fuel = 90000 }, +} diff --git a/Database/Aircraft/J-11A.lua b/Database/Aircraft/J-11A.lua new file mode 100644 index 0000000..a41e2ce --- /dev/null +++ b/Database/Aircraft/J-11A.lua @@ -0,0 +1,49 @@ +Library.aircraft["J-11A"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 96,flare = 96,fuel = 9400,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{RKL609_L}" }, + [2] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [3] = { CLSID = "{B79C379A-9E87-4E50-A1EE-7F7E29C2E87A}" }, + [4] = { CLSID = "{E8069896-8435-4B90-95C0-01A03AE6E400}" }, + [5] = { CLSID = "{E8069896-8435-4B90-95C0-01A03AE6E400}" }, + [6] = { CLSID = "{E8069896-8435-4B90-95C0-01A03AE6E400}" }, + [7] = { CLSID = "{E8069896-8435-4B90-95C0-01A03AE6E400}" }, + [8] = { CLSID = "{B79C379A-9E87-4E50-A1EE-7F7E29C2E87A}" }, + [9] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [10] = { CLSID = "{RKL609_R}" }, + -- Payload.Decade2000.Task.AirToGround.Pylon01={RKL609_L} + -- Payload.Decade2000.Task.AirToGround.Pylon02={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AirToGround.Pylon03={FAB_500_DUAL_L} + -- Payload.Decade2000.Task.AirToGround.Pylon04={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon05={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon06={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon07={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon08={FAB_500_DUAL_R} + -- Payload.Decade2000.Task.AirToGround.Pylon09={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AirToGround.Pylon10={RKL609_R} + -- Payload.Decade2000.Task.AntiShip.Pylon01={RKL609_L} + -- Payload.Decade2000.Task.AntiShip.Pylon02={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AntiShip.Pylon03={FAB_500_DUAL_L} + -- Payload.Decade2000.Task.AntiShip.Pylon04={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AntiShip.Pylon05={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AntiShip.Pylon06={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AntiShip.Pylon07={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AntiShip.Pylon08={FAB_500_DUAL_R} + -- Payload.Decade2000.Task.AntiShip.Pylon09={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AntiShip.Pylon10={RKL609_R} + -- Payload.Decade2000.Task.SEAD.Pylon01={RKL609_L} + -- Payload.Decade2000.Task.SEAD.Pylon02={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.SEAD.Pylon03={FAB_500_DUAL_L} + -- Payload.Decade2000.Task.SEAD.Pylon04={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.SEAD.Pylon05={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.SEAD.Pylon06={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.SEAD.Pylon07={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.SEAD.Pylon08={FAB_500_DUAL_R} + -- Payload.Decade2000.Task.SEAD.Pylon09={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.SEAD.Pylon10={RKL609_R} + } + } +} diff --git a/Database/Aircraft/JF-17.lua b/Database/Aircraft/JF-17.lua new file mode 100644 index 0000000..a228ab6 --- /dev/null +++ b/Database/Aircraft/JF-17.lua @@ -0,0 +1,38 @@ +Library.aircraft["JF-17"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 36,flare = 32,fuel = 2325,gun = 100 }, + properties = { AARProbe = false,LaserCode100 = 6,LaserCode10 = 8,LaserCode1 = 8 }, + pylons = { + default = { + [1] = { CLSID = "DIS_PL-5EII" }, + [2] = { CLSID = "DIS_GBU_12_DUAL" }, + [3] = { CLSID = "DIS_LS_6_500" }, + [4] = { CLSID = "DIS_WMD7" }, + [5] = { CLSID = "DIS_LS_6_500" }, + [6] = { CLSID = "DIS_GBU_12_DUAL" }, + [7] = { CLSID = "DIS_PL-5EII" }, + -- Payload.Decade2000.Task.SEAD.Pylon01=DIS_PL-5EII + -- Payload.Decade2000.Task.SEAD.Pylon02=DIS_LD-10_DUAL_L + -- Payload.Decade2000.Task.SEAD.Pylon03=DIS_TANK1100 + -- Payload.Decade2000.Task.SEAD.Pylon04=DIS_SPJ_POD + -- Payload.Decade2000.Task.SEAD.Pylon05=DIS_TANK1100 + -- Payload.Decade2000.Task.SEAD.Pylon06=DIS_LD-10_DUAL_R + -- Payload.Decade2000.Task.SEAD.Pylon07=DIS_PL-5EII + -- Payload.Decade2000.Task.Antiship.Pylon01=DIS_PL-5EII + -- Payload.Decade2000.Task.Antiship.Pylon02=DIS_C-701IR + -- Payload.Decade2000.Task.Antiship.Pylon03=DIS_C-802AK + -- Payload.Decade2000.Task.Antiship.Pylon04=DIS_TANK800 + -- Payload.Decade2000.Task.Antiship.Pylon05=DIS_C-802AK + -- Payload.Decade2000.Task.Antiship.Pylon06=DIS_C-701IR + -- Payload.Decade2000.Task.Antiship.Pylon07=DIS_PL-5EII + -- Payload.Decade2000.Task.AirToAir.Pylon01=DIS_PL-5EII + -- Payload.Decade2000.Task.AirToAir.Pylon02=DIS_SD-10 + -- Payload.Decade2000.Task.AirToAir.Pylon03=DIS_TANK1100 + -- Payload.Decade2000.Task.AirToAir.Pylon04=DIS_TANK800 + -- Payload.Decade2000.Task.AirToAir.Pylon05=DIS_TANK1100 + -- Payload.Decade2000.Task.AirToAir.Pylon06=DIS_SD-10 + -- Payload.Decade2000.Task.AirToAir.Pylon07=DIS_PL-5EII + } + } +} diff --git a/Database/Aircraft/KC-135.lua b/Database/Aircraft/KC-135.lua new file mode 100644 index 0000000..25819c2 --- /dev/null +++ b/Database/Aircraft/KC-135.lua @@ -0,0 +1,5 @@ +Library.aircraft["KC-135"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 0,flare = 0,fuel = 90700 }, +} diff --git a/Database/Aircraft/KC135MPRS.lua b/Database/Aircraft/KC135MPRS.lua new file mode 100644 index 0000000..27c5eff --- /dev/null +++ b/Database/Aircraft/KC135MPRS.lua @@ -0,0 +1,5 @@ +Library.aircraft["KC135MPRS"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 120,flare = 60,fuel = 90700,gun = 100 }, +} diff --git a/Database/Aircraft/Ka-27.lua b/Database/Aircraft/Ka-27.lua new file mode 100644 index 0000000..6579727 --- /dev/null +++ b/Database/Aircraft/Ka-27.lua @@ -0,0 +1,5 @@ +Library.aircraft["Ka-27"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 0,flare = 0,fuel = 2616,gun = 100 }, +} diff --git a/Database/Aircraft/Ka-50.lua b/Database/Aircraft/Ka-50.lua new file mode 100644 index 0000000..0068622 --- /dev/null +++ b/Database/Aircraft/Ka-50.lua @@ -0,0 +1,13 @@ +Library.aircraft["Ka-50"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 0,flare = 128,fuel = 1450,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{A6FD14D3-6D30-4C85-88A7-8D17BEE120E2}" }, + [2] = { CLSID = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}" }, + [3] = { CLSID = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}" }, + [4] = { CLSID = "{A6FD14D3-6D30-4C85-88A7-8D17BEE120E2}" }, + } + } +} diff --git a/Database/Aircraft/L-39C.lua b/Database/Aircraft/L-39C.lua new file mode 100644 index 0000000..6f6851d --- /dev/null +++ b/Database/Aircraft/L-39C.lua @@ -0,0 +1,17 @@ +Library.aircraft["L-39C"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 0,flare = 0,fuel = 823.2,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{UB-16-57UMP}" }, + [2] = { CLSID = "{FB3CE165-BF07-4979-887C-92B87F13276B}" }, + [4] = { CLSID = "{FB3CE165-BF07-4979-887C-92B87F13276B}" }, + [5] = { CLSID = "{UB-16-57UMP}" }, + -- Payload.Decade2000.Task.AirToAir.Pylon01={APU-60-1_R_60M} + -- Payload.Decade2000.Task.AirToAir.Pylon02={PK-3} + -- Payload.Decade2000.Task.AirToAir.Pylon04={PK-3} + -- Payload.Decade2000.Task.AirToAir.Pylon05={APU-60-1_R_60M} + } + } +} diff --git a/Database/Aircraft/M-2000C.lua b/Database/Aircraft/M-2000C.lua new file mode 100644 index 0000000..01dc283 --- /dev/null +++ b/Database/Aircraft/M-2000C.lua @@ -0,0 +1,42 @@ +Library.aircraft["M-2000C"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 112,flare = 16,fuel = 3165,gun = 100 }, + properties = { ForceINSRules = false, LaserCode100 = 6, NoDDMSensor = false, LaserCode1 = 8, WpBullseye = 0, LoadNVGCase = false, RocketBurst = 6, LaserCode10 = 8, GunBurst = 1 }, + pylons = { + default = { + [1] = { CLSID = "{MMagicII}" }, + [2] = { CLSID = "{Matra_S530D}" }, + [5] = { CLSID = "{M2KC_RPL_522}" }, + [8] = { CLSID = "{Matra_S530D}" }, + [9] = { CLSID = "{MMagicII}" }, + -- Payload.Decade2000.Task.AirToGround.Pylon01={MMagicII} + -- Payload.Decade2000.Task.AirToGround.Pylon02={M2KC_RAFAUT_MK82} + -- Payload.Decade2000.Task.AirToGround.Pylon03={BCE4E030-38E9-423E-98ED-24BE3DA87C32} + -- Payload.Decade2000.Task.AirToGround.Pylon04={BCE4E030-38E9-423E-98ED-24BE3DA87C32} + -- Payload.Decade2000.Task.AirToGround.Pylon05={M2KC_RPL_522} + -- Payload.Decade2000.Task.AirToGround.Pylon06={BCE4E030-38E9-423E-98ED-24BE3DA87C32} + -- Payload.Decade2000.Task.AirToGround.Pylon07={BCE4E030-38E9-423E-98ED-24BE3DA87C32} + -- Payload.Decade2000.Task.AirToGround.Pylon08={M2KC_RAFAUT_MK82} + -- Payload.Decade2000.Task.AirToGround.Pylon09={MMagicII} + -- Payload.Decade2000.Task.AntiShip.Pylon01={MMagicII} + -- Payload.Decade2000.Task.AntiShip.Pylon02={M2KC_RAFAUT_MK82} + -- Payload.Decade2000.Task.AntiShip.Pylon03={BCE4E030-38E9-423E-98ED-24BE3DA87C32} + -- Payload.Decade2000.Task.AntiShip.Pylon04={BCE4E030-38E9-423E-98ED-24BE3DA87C32} + -- Payload.Decade2000.Task.AntiShip.Pylon05={M2KC_RPL_522} + -- Payload.Decade2000.Task.AntiShip.Pylon06={BCE4E030-38E9-423E-98ED-24BE3DA87C32} + -- Payload.Decade2000.Task.AntiShip.Pylon07={BCE4E030-38E9-423E-98ED-24BE3DA87C32} + -- Payload.Decade2000.Task.AntiShip.Pylon08={M2KC_RAFAUT_MK82} + -- Payload.Decade2000.Task.AntiShip.Pylon09={MMagicII} + -- Payload.Decade2000.Task.SEAD.Pylon01={MMagicII} + -- Payload.Decade2000.Task.SEAD.Pylon02={M2KC_RAFAUT_MK82} + -- Payload.Decade2000.Task.SEAD.Pylon03={BCE4E030-38E9-423E-98ED-24BE3DA87C32} + -- Payload.Decade2000.Task.SEAD.Pylon04={BCE4E030-38E9-423E-98ED-24BE3DA87C32} + -- Payload.Decade2000.Task.SEAD.Pylon05={M2KC_RPL_522} + -- Payload.Decade2000.Task.SEAD.Pylon06={BCE4E030-38E9-423E-98ED-24BE3DA87C32} + -- Payload.Decade2000.Task.SEAD.Pylon07={BCE4E030-38E9-423E-98ED-24BE3DA87C32} + -- Payload.Decade2000.Task.SEAD.Pylon08={M2KC_RAFAUT_MK82} + -- Payload.Decade2000.Task.SEAD.Pylon09={MMagicII} + } + } +} diff --git a/Database/Aircraft/Mi-24V.lua b/Database/Aircraft/Mi-24V.lua new file mode 100644 index 0000000..b4add81 --- /dev/null +++ b/Database/Aircraft/Mi-24V.lua @@ -0,0 +1,13 @@ +Library.aircraft["Mi-24V"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 0,flare = 192,fuel = 1704 }, + pylons = { + default = { + [1] = { CLSID = "{B919B0F4-7C25-455E-9A02-CEA51DB895E3}" }, + [3] = { CLSID = "{B_8V20A_CM}" }, + [4] = { CLSID = "{B_8V20A_CM}" }, + [6] = { CLSID = "{B919B0F4-7C25-455E-9A02-CEA51DB895E3}" }, + } + } +} diff --git a/Database/Aircraft/Mi-26.lua b/Database/Aircraft/Mi-26.lua new file mode 100644 index 0000000..abaeeb5 --- /dev/null +++ b/Database/Aircraft/Mi-26.lua @@ -0,0 +1,5 @@ +Library.aircraft["Mi-26"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 0,flare = 192,fuel = 9600,gun = 100 }, +} diff --git a/Database/Aircraft/Mi-28N.lua b/Database/Aircraft/Mi-28N.lua new file mode 100644 index 0000000..0a62cb6 --- /dev/null +++ b/Database/Aircraft/Mi-28N.lua @@ -0,0 +1,13 @@ +Library.aircraft["Mi-28N"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 0,flare = 128,fuel = 1500 }, + pylons = { + default = { + [1] = { CLSID = "{57232979-8B0F-4db7-8D9A-55197E06B0F5}" }, + [2] = { CLSID = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}" }, + [3] = { CLSID = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}" }, + [4] = { CLSID = "{57232979-8B0F-4db7-8D9A-55197E06B0F5}" }, + } + } +} diff --git a/Database/Aircraft/Mi-8MT.lua b/Database/Aircraft/Mi-8MT.lua new file mode 100644 index 0000000..cb6fef5 --- /dev/null +++ b/Database/Aircraft/Mi-8MT.lua @@ -0,0 +1,16 @@ +Library.aircraft["Mi-8MT"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 0,flare = 128,fuel = 1929,gun = 100 }, + properties = { LeftEngineResource = 90, RightEngineResource = 90, NetCrewControlPriority = 1, ExhaustScreen = true, CargoHalfdoor = true, GunnersAISkill = 90, AdditionalArmor = true, NS430allow = true }, + pylons = { + default = { + [1] = { CLSID = "{GUV_VOG}" }, + [2] = { CLSID = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}" }, + [3] = { CLSID = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}" }, + [4] = { CLSID = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}" }, + [5] = { CLSID = "{6A4B9E69-64FE-439a-9163-3A87FB6A4D81}" }, + [6] = { CLSID = "{GUV_VOG}" }, + } + } +} diff --git a/Database/Aircraft/MiG-15bis.lua b/Database/Aircraft/MiG-15bis.lua new file mode 100644 index 0000000..4bc9b80 --- /dev/null +++ b/Database/Aircraft/MiG-15bis.lua @@ -0,0 +1,13 @@ +Library.aircraft["MiG-15bis"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 0,flare = 0,fuel = 1172,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{PTB600_MIG15}" }, + [2] = { CLSID = "{PTB600_MIG15}" }, + -- Payload.Decade2000.Task.AirToGround.Pylon01={FAB_50} + -- Payload.Decade2000.Task.AirToGround.Pylon02={FAB_50} + } + } +} diff --git a/Database/Aircraft/MiG-19P.lua b/Database/Aircraft/MiG-19P.lua new file mode 100644 index 0000000..a49c8fc --- /dev/null +++ b/Database/Aircraft/MiG-19P.lua @@ -0,0 +1,18 @@ +Library.aircraft["MiG-19P"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 0,flare = 0,fuel = 1800,gun = 100 }, + properties = { MissileToneVolume = 5,ADF_Selected_Frequency = 1,MountSIRENA = false,ADF_NEAR_Frequency = 303,ADF_FAR_Frequency = 625,NAV_Initial_Hdg = 0 }, + pylons = { + default = { + [1] = { CLSID = "{K-13A}" }, + [2] = { CLSID = "{PTB760_MIG19}" }, + [5] = { CLSID = "{PTB760_MIG19}" }, + [6] = { CLSID = "{K-13A}" }, + -- Payload.Decade2000.Task.AirToGround.Pylon02={3C612111-C7AD-476E-8A8E-2485812F4E5C} + -- Payload.Decade2000.Task.AirToGround.Pylon03={ORO57K_S5M_HEFRAG} + -- Payload.Decade2000.Task.AirToGround.Pylon04={ORO57K_S5M_HEFRAG} + -- Payload.Decade2000.Task.AirToGround.Pylon05={3C612111-C7AD-476E-8A8E-2485812F4E5C} + } + } +} diff --git a/Database/Aircraft/MiG-21Bis.lua b/Database/Aircraft/MiG-21Bis.lua new file mode 100644 index 0000000..9867483 --- /dev/null +++ b/Database/Aircraft/MiG-21Bis.lua @@ -0,0 +1,21 @@ +Library.aircraft["MiG-21Bis"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 18,flare = 40,fuel = 2280,ammo_type = 1,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{R-60 2L}" }, + [2] = { CLSID = "{R-3R}" }, + [3] = { CLSID = "{PTB_800_MIG21}" }, + [4] = { CLSID = "{R-3R}" }, + [5] = { CLSID = "{R-60 2R}" }, + [6] = { CLSID = "{ASO-2}" }, + -- Payload.Decade2000.Task.AirToGround.Pylon01={3C612111-C7AD-476E-8A8E-2485812F4E5C} + -- Payload.Decade2000.Task.AirToGround.Pylon02={UB-32_S5M} + -- Payload.Decade2000.Task.AirToGround.Pylon03={PTB_800_MIG21} + -- Payload.Decade2000.Task.AirToGround.Pylon04={UB-32_S5M} + -- Payload.Decade2000.Task.AirToGround.Pylon05={3C612111-C7AD-476E-8A8E-2485812F4E5C} + -- Payload.Decade2000.Task.AirToGround.Pylon06={ASO-2} + } + } +} diff --git a/Database/Aircraft/MiG-23MLD.lua b/Database/Aircraft/MiG-23MLD.lua new file mode 100644 index 0000000..efb5bbd --- /dev/null +++ b/Database/Aircraft/MiG-23MLD.lua @@ -0,0 +1,19 @@ +Library.aircraft["MiG-23MLD"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 60,flare = 60,fuel = 3800,gun = 100 }, + pylons = { + default = { + [2] = { CLSID = "{CCF898C9-5BC7-49A4-9D1E-C3ED3D5166A1}" }, + [3] = { CLSID = "{B0DBC591-0F52-4F7D-AD7B-51E67725FB81}" }, + [4] = { CLSID = "{A5BAEAB7-6FAF-4236-AF72-0FD900F493F9}" }, + [5] = { CLSID = "{275A2855-4A79-4B2D-B082-91EA2ADF4691}" }, + [6] = { CLSID = "{CCF898C9-5BC7-49A4-9D1E-C3ED3D5166A1}" }, + -- Payload.Decade2000.Task.AirToGround.Pylon02={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon03={682A481F-0CB5-4693-A382-D00DD4A156D7} + -- Payload.Decade2000.Task.AirToGround.Pylon04={A5BAEAB7-6FAF-4236-AF72-0FD900F493F9} + -- Payload.Decade2000.Task.AirToGround.Pylon05={682A481F-0CB5-4693-A382-D00DD4A156D7} + -- Payload.Decade2000.Task.AirToGround.Pylon06={37DCC01E-9E02-432F-B61D-10C166CA2798} + } + } +} diff --git a/Database/Aircraft/MiG-25PD.lua b/Database/Aircraft/MiG-25PD.lua new file mode 100644 index 0000000..a4909d0 --- /dev/null +++ b/Database/Aircraft/MiG-25PD.lua @@ -0,0 +1,13 @@ +Library.aircraft["MiG-25PD"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 64,flare = 64,fuel = 15245,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{682A481F-0CB5-4693-A382-D00DD4A156D7}" }, + [2] = { CLSID = "{4EDBA993-2E34-444C-95FB-549300BF7CAF}" }, + [3] = { CLSID = "{4EDBA993-2E34-444C-95FB-549300BF7CAF}" }, + [4] = { CLSID = "{682A481F-0CB5-4693-A382-D00DD4A156D7}" }, + } + } +} diff --git a/Database/Aircraft/MiG-25RBT.lua b/Database/Aircraft/MiG-25RBT.lua new file mode 100644 index 0000000..6c51eac --- /dev/null +++ b/Database/Aircraft/MiG-25RBT.lua @@ -0,0 +1,13 @@ +Library.aircraft["MiG-25RBT"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 64,flare = 64,fuel = 15245,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{682A481F-0CB5-4693-A382-D00DD4A156D7}" }, + [2] = { CLSID = "{37DCC01E-9E02-432F-B61D-10C166CA2798}" }, + [3] = { CLSID = "{37DCC01E-9E02-432F-B61D-10C166CA2798}" }, + [4] = { CLSID = "{682A481F-0CB5-4693-A382-D00DD4A156D7}" }, + } + } +} diff --git a/Database/Aircraft/MiG-27K.lua b/Database/Aircraft/MiG-27K.lua new file mode 100644 index 0000000..a6a6c21 --- /dev/null +++ b/Database/Aircraft/MiG-27K.lua @@ -0,0 +1,16 @@ +Library.aircraft["MiG-27K"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 60,flare = 60,fuel = 4500,gun = 100 }, + pylons = { + default = { + [2] = { CLSID = "{E659C4BE-2CD8-4472-8C08-3F28ACB61A8A}" }, + [3] = { CLSID = "{682A481F-0CB5-4693-A382-D00DD4A156D7}" }, + [4] = { CLSID = "{3C612111-C7AD-476E-8A8E-2485812F4E5C}" }, + [5] = { CLSID = "{A5BAEAB7-6FAF-4236-AF72-0FD900F493F9}" }, + [6] = { CLSID = "{3C612111-C7AD-476E-8A8E-2485812F4E5C}" }, + [7] = { CLSID = "{682A481F-0CB5-4693-A382-D00DD4A156D7}" }, + [8] = { CLSID = "{E659C4BE-2CD8-4472-8C08-3F28ACB61A8A}" }, + } + } +} diff --git a/Database/Aircraft/MiG-29A.lua b/Database/Aircraft/MiG-29A.lua new file mode 100644 index 0000000..23e4808 --- /dev/null +++ b/Database/Aircraft/MiG-29A.lua @@ -0,0 +1,37 @@ +Library.aircraft["MiG-29A"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 30,flare = 30,fuel = 3376,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [2] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [3] = { CLSID = "{9B25D316-0434-4954-868F-D51DB1A38DF0}" }, + [4] = { CLSID = "{2BEC576B-CDF5-4B7F-961F-B0FA4312B841}" }, + [5] = { CLSID = "{9B25D316-0434-4954-868F-D51DB1A38DF0}" }, + [6] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [7] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + -- Payload.Decade2000.Task.AirToGround.Pylon01={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AirToGround.Pylon02={3858707D-F5D5-4bbb-BDD8-ABB0530EBC7C} + -- Payload.Decade2000.Task.AirToGround.Pylon03={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon04={2BEC576B-CDF5-4B7F-961F-B0FA4312B841} + -- Payload.Decade2000.Task.AirToGround.Pylon05={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon06={3858707D-F5D5-4bbb-BDD8-ABB0530EBC7C} + -- Payload.Decade2000.Task.AirToGround.Pylon07={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AntiShip.Pylon01={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AntiShip.Pylon02={3858707D-F5D5-4bbb-BDD8-ABB0530EBC7C} + -- Payload.Decade2000.Task.AntiShip.Pylon03={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AntiShip.Pylon04={2BEC576B-CDF5-4B7F-961F-B0FA4312B841} + -- Payload.Decade2000.Task.AntiShip.Pylon05={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AntiShip.Pylon06={3858707D-F5D5-4bbb-BDD8-ABB0530EBC7C} + -- Payload.Decade2000.Task.AntiShip.Pylon07={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.SEAD.Pylon01={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.SEAD.Pylon02={3858707D-F5D5-4bbb-BDD8-ABB0530EBC7C} + -- Payload.Decade2000.Task.SEAD.Pylon03={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.SEAD.Pylon04={2BEC576B-CDF5-4B7F-961F-B0FA4312B841} + -- Payload.Decade2000.Task.SEAD.Pylon05={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.SEAD.Pylon06={3858707D-F5D5-4bbb-BDD8-ABB0530EBC7C} + -- Payload.Decade2000.Task.SEAD.Pylon07={FBC29BFE-3D24-4C64-B81D-941239D12249} + } + } +} diff --git a/Database/Aircraft/MiG-29S.lua b/Database/Aircraft/MiG-29S.lua new file mode 100644 index 0000000..137a42f --- /dev/null +++ b/Database/Aircraft/MiG-29S.lua @@ -0,0 +1,37 @@ +Library.aircraft["MiG-29S"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 30,flare = 30,fuel = 3493,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [2] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [3] = { CLSID = "{9B25D316-0434-4954-868F-D51DB1A38DF0}" }, + [4] = { CLSID = "{2BEC576B-CDF5-4B7F-961F-B0FA4312B841}" }, + [5] = { CLSID = "{9B25D316-0434-4954-868F-D51DB1A38DF0}" }, + [6] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [7] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + -- Payload.Decade2000.Task.AirToGround.Pylon01={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AirToGround.Pylon02={3858707D-F5D5-4bbb-BDD8-ABB0530EBC7C} + -- Payload.Decade2000.Task.AirToGround.Pylon03={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon04={2BEC576B-CDF5-4B7F-961F-B0FA4312B841} + -- Payload.Decade2000.Task.AirToGround.Pylon05={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon06={3858707D-F5D5-4bbb-BDD8-ABB0530EBC7C} + -- Payload.Decade2000.Task.AirToGround.Pylon07={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AntiShip.Pylon01={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AntiShip.Pylon02={3858707D-F5D5-4bbb-BDD8-ABB0530EBC7C} + -- Payload.Decade2000.Task.AntiShip.Pylon03={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AntiShip.Pylon04={2BEC576B-CDF5-4B7F-961F-B0FA4312B841} + -- Payload.Decade2000.Task.AntiShip.Pylon05={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AntiShip.Pylon06={3858707D-F5D5-4bbb-BDD8-ABB0530EBC7C} + -- Payload.Decade2000.Task.AntiShip.Pylon07={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.SEAD.Pylon01={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.SEAD.Pylon02={3858707D-F5D5-4bbb-BDD8-ABB0530EBC7C} + -- Payload.Decade2000.Task.SEAD.Pylon03={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.SEAD.Pylon04={2BEC576B-CDF5-4B7F-961F-B0FA4312B841} + -- Payload.Decade2000.Task.SEAD.Pylon05={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.SEAD.Pylon06={3858707D-F5D5-4bbb-BDD8-ABB0530EBC7C} + -- Payload.Decade2000.Task.SEAD.Pylon07={FBC29BFE-3D24-4C64-B81D-941239D12249} + } + } +} diff --git a/Database/Aircraft/MiG-31.lua b/Database/Aircraft/MiG-31.lua new file mode 100644 index 0000000..f1215ed --- /dev/null +++ b/Database/Aircraft/MiG-31.lua @@ -0,0 +1,15 @@ +Library.aircraft["MiG-31"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 0,flare = 0,fuel = 15500,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{4EDBA993-2E34-444C-95FB-549300BF7CAF}" }, + [2] = { CLSID = "{F1243568-8EF0-49D4-9CB5-4DA90D92BC1D}" }, + [3] = { CLSID = "{F1243568-8EF0-49D4-9CB5-4DA90D92BC1D}" }, + [4] = { CLSID = "{F1243568-8EF0-49D4-9CB5-4DA90D92BC1D}" }, + [5] = { CLSID = "{F1243568-8EF0-49D4-9CB5-4DA90D92BC1D}" }, + [6] = { CLSID = "{5F26DBC2-FB43-4153-92DE-6BBCE26CB0FF}" }, + } + } +} diff --git a/Database/Aircraft/OH-58D.lua b/Database/Aircraft/OH-58D.lua new file mode 100644 index 0000000..d57aee4 --- /dev/null +++ b/Database/Aircraft/OH-58D.lua @@ -0,0 +1,11 @@ +Library.aircraft["OH-58D"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 30,flare = 30,fuel = 454 }, + pylons = { + default = { + [1] = { CLSID = "{AGM114x2_OH_58}" }, + [2] = { CLSID = "{M260_HYDRA}" }, + } + } +} diff --git a/Database/Aircraft/P-47D-30.lua b/Database/Aircraft/P-47D-30.lua new file mode 100644 index 0000000..e8a6087 --- /dev/null +++ b/Database/Aircraft/P-47D-30.lua @@ -0,0 +1,15 @@ +Library.aircraft["P-47D-30"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(225), + payload = { chaff = 0,flare = 0,fuel = 557.76,gun = 100,ammo_type = 1 }, + properties = { WaterTankContents = 1 }, + pylons = { + default = { + [2] = { CLSID = "{US_150GAL_FUEL_TANK}" }, + [3] = { CLSID = "{US_150GAL_FUEL_TANK}" }, + -- Payload.Decade2000.Task.AirToGround.Pylon01={US_110GAL_FUEL_TANK} + -- Payload.Decade2000.Task.AirToGround.Pylon02={AN-M64} + -- Payload.Decade2000.Task.AirToGround.Pylon03={AN-M64} + } + } +} diff --git a/Database/Aircraft/P-47D-30bl1.lua b/Database/Aircraft/P-47D-30bl1.lua new file mode 100644 index 0000000..6334902 --- /dev/null +++ b/Database/Aircraft/P-47D-30bl1.lua @@ -0,0 +1,9 @@ +Library.aircraft["P-47D-30bl1"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(225), + payload = { chaff = 0,flare = 0,fuel = 557.76,gun = 100,ammo_type = 1 }, + properties = { WaterTankContents = 1 }, + -- Payload.Decade2000.Task.AirToGround.Pylon01={US_110GAL_FUEL_TANK} + -- Payload.Decade2000.Task.AirToGround.Pylon02={AN-M64} + -- Payload.Decade2000.Task.AirToGround.Pylon03={AN-M64} +} diff --git a/Database/Aircraft/P-47D-40.lua b/Database/Aircraft/P-47D-40.lua new file mode 100644 index 0000000..466fb78 --- /dev/null +++ b/Database/Aircraft/P-47D-40.lua @@ -0,0 +1,15 @@ +Library.aircraft["P-47D-40"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(225), + payload = { chaff = 0,flare = 0,fuel = 557.76,gun = 100,ammo_type = 1 }, + properties = { WaterTankContents = 1 }, + pylons = { + default = { + [2] = { CLSID = "{US_150GAL_FUEL_TANK}" }, + [3] = { CLSID = "{US_150GAL_FUEL_TANK}" }, + -- Payload.Decade2000.Task.AirToGround.Pylon01={US_110GAL_FUEL_TANK} + -- Payload.Decade2000.Task.AirToGround.Pylon02={AN-M64} + -- Payload.Decade2000.Task.AirToGround.Pylon03={AN-M64} + } + } +} diff --git a/Database/Aircraft/P-51D-30-NA.lua b/Database/Aircraft/P-51D-30-NA.lua new file mode 100644 index 0000000..ef741cc --- /dev/null +++ b/Database/Aircraft/P-51D-30-NA.lua @@ -0,0 +1,7 @@ +Library.aircraft["P-51D-30-NA"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 0,flare = 0,fuel = 497.76,gun = 100 }, + -- Payload.Decade2000.Task.AirToGround.Pylon04={AN-M64} + -- Payload.Decade2000.Task.AirToGround.Pylon07={AN-M64} +} diff --git a/Database/Aircraft/P-51D.lua b/Database/Aircraft/P-51D.lua new file mode 100644 index 0000000..abaef30 --- /dev/null +++ b/Database/Aircraft/P-51D.lua @@ -0,0 +1,7 @@ +Library.aircraft["P-51D"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 0,flare = 0,fuel = 497.76,gun = 100 }, + -- Payload.Decade2000.Task.AirToGround.Pylon04={AN-M64} + -- Payload.Decade2000.Task.AirToGround.Pylon07={AN-M64} +} diff --git a/Database/Aircraft/SA342L.lua b/Database/Aircraft/SA342L.lua new file mode 100644 index 0000000..b2f7894 --- /dev/null +++ b/Database/Aircraft/SA342L.lua @@ -0,0 +1,13 @@ +Library.aircraft["SA342L"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 0,flare = 32,fuel = 416.33,gun = 100 }, + properties = { NS430allow = true }, + pylons = { + default = { + [2] = { CLSID = "{LAU_SNEB68G}" }, + [5] = { CLSID = "{FAS}" }, + [6] = { CLSID = "{IR_Deflector}" }, + } + } +} diff --git a/Database/Aircraft/SA342M.lua b/Database/Aircraft/SA342M.lua new file mode 100644 index 0000000..86744d7 --- /dev/null +++ b/Database/Aircraft/SA342M.lua @@ -0,0 +1,16 @@ +Library.aircraft["SA342M"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 0,flare = 32,fuel = 416.33,gun = 100 }, + properties = { NS430allow = true }, + pylons = { + default = { + [1] = { CLSID = "{HOT3D}" }, + [2] = { CLSID = "{HOT3D}" }, + [3] = { CLSID = "{HOT3D}" }, + [4] = { CLSID = "{HOT3D}" }, + [5] = { CLSID = "{FAS}" }, + [6] = { CLSID = "{IR_Deflector}" }, + } + } +} diff --git a/Database/Aircraft/SA342Minigun.lua b/Database/Aircraft/SA342Minigun.lua new file mode 100644 index 0000000..b47e8a3 --- /dev/null +++ b/Database/Aircraft/SA342Minigun.lua @@ -0,0 +1,12 @@ +Library.aircraft["SA342Minigun"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 0,flare = 32,fuel = 416.33,gun = 100 }, + properties = { NS430allow = true }, + pylons = { + default = { + [5] = { CLSID = "{FAS}" }, + [6] = { CLSID = "{IR_Deflector}" }, + } + } +} diff --git a/Database/Aircraft/SA342Mistral.lua b/Database/Aircraft/SA342Mistral.lua new file mode 100644 index 0000000..e4168c4 --- /dev/null +++ b/Database/Aircraft/SA342Mistral.lua @@ -0,0 +1,16 @@ +Library.aircraft["SA342Mistral"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 0,flare = 32,fuel = 416.33,gun = 100 }, + properties = { NS430allow = true }, + pylons = { + default = { + [1] = { CLSID = "{MBDA_MistralD}" }, + [2] = { CLSID = "{MBDA_MistralG}" }, + [3] = { CLSID = "{MBDA_MistralD}" }, + [4] = { CLSID = "{MBDA_MistralG}" }, + [5] = { CLSID = "{FAS}" }, + [6] = { CLSID = "{IR_Deflector}" }, + } + } +} diff --git a/Database/Aircraft/SH-60B.lua b/Database/Aircraft/SH-60B.lua new file mode 100644 index 0000000..eed5761 --- /dev/null +++ b/Database/Aircraft/SH-60B.lua @@ -0,0 +1,10 @@ +Library.aircraft["SH-60B"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 30,flare = 30,fuel = 1100 }, + pylons = { + default = { + [1] = { CLSID = "{7B8DCEB4-820B-4015-9B48-1028A4195692}" }, + } + } +} diff --git a/Database/Aircraft/SpitfireLFMkIX.lua b/Database/Aircraft/SpitfireLFMkIX.lua new file mode 100644 index 0000000..55d96db --- /dev/null +++ b/Database/Aircraft/SpitfireLFMkIX.lua @@ -0,0 +1,5 @@ +Library.aircraft["SpitfireLFMkIX"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(225), + payload = { chaff = 0,flare = 0,fuel = 247,ammo_type = 1,gun = 100 }, +} diff --git a/Database/Aircraft/SpitfireLFMkIXCW.lua b/Database/Aircraft/SpitfireLFMkIXCW.lua new file mode 100644 index 0000000..c95bdce --- /dev/null +++ b/Database/Aircraft/SpitfireLFMkIXCW.lua @@ -0,0 +1,5 @@ +Library.aircraft["SpitfireLFMkIXCW"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(225), + payload = { chaff = 0,flare = 0,fuel = 247,ammo_type = 1,gun = 100 }, +} diff --git a/Database/Aircraft/Su-17M4.lua b/Database/Aircraft/Su-17M4.lua new file mode 100644 index 0000000..d6a465c --- /dev/null +++ b/Database/Aircraft/Su-17M4.lua @@ -0,0 +1,17 @@ +Library.aircraft["Su-17M4"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 96,flare = 96,fuel = 11700,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{637334E4-AB5A-47C0-83A6-51B7F1DF3CD5}" }, + [2] = { CLSID = "{682A481F-0CB5-4693-A382-D00DD4A156D7}" }, + [3] = { CLSID = "{637334E4-AB5A-47C0-83A6-51B7F1DF3CD5}" }, + [4] = { CLSID = "{E659C4BE-2CD8-4472-8C08-3F28ACB61A8A}" }, + [5] = { CLSID = "{E659C4BE-2CD8-4472-8C08-3F28ACB61A8A}" }, + [6] = { CLSID = "{637334E4-AB5A-47C0-83A6-51B7F1DF3CD5}" }, + [7] = { CLSID = "{682A481F-0CB5-4693-A382-D00DD4A156D7}" }, + [8] = { CLSID = "{637334E4-AB5A-47C0-83A6-51B7F1DF3CD5}" }, + } + } +} diff --git a/Database/Aircraft/Su-24M.lua b/Database/Aircraft/Su-24M.lua new file mode 100644 index 0000000..d004edb --- /dev/null +++ b/Database/Aircraft/Su-24M.lua @@ -0,0 +1,15 @@ +Library.aircraft["Su-24M"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 96,flare = 96,fuel = 11700,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{B0DBC591-0F52-4F7D-AD7B-51E67725FB81}" }, + [2] = { CLSID = "{BA565F89-2373-4A84-9502-A0E017D3A44A}" }, + [3] = { CLSID = "{BA565F89-2373-4A84-9502-A0E017D3A44A}" }, + [6] = { CLSID = "{BA565F89-2373-4A84-9502-A0E017D3A44A}" }, + [7] = { CLSID = "{BA565F89-2373-4A84-9502-A0E017D3A44A}" }, + [8] = { CLSID = "{275A2855-4A79-4B2D-B082-91EA2ADF4691}" }, + } + } +} diff --git a/Database/Aircraft/Su-25.lua b/Database/Aircraft/Su-25.lua new file mode 100644 index 0000000..b40dfdf --- /dev/null +++ b/Database/Aircraft/Su-25.lua @@ -0,0 +1,19 @@ +Library.aircraft["Su-25"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 128,flare = 128,fuel = 2835,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{682A481F-0CB5-4693-A382-D00DD4A156D7}" }, + [2] = { CLSID = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}" }, + [3] = { CLSID = "{3C612111-C7AD-476E-8A8E-2485812F4E5C}" }, + [4] = { CLSID = "{3C612111-C7AD-476E-8A8E-2485812F4E5C}" }, + [5] = { CLSID = "{E92CBFE5-C153-11d8-9897-000476191836}" }, + [6] = { CLSID = "{E92CBFE5-C153-11d8-9897-000476191836}" }, + [7] = { CLSID = "{3C612111-C7AD-476E-8A8E-2485812F4E5C}" }, + [8] = { CLSID = "{3C612111-C7AD-476E-8A8E-2485812F4E5C}" }, + [9] = { CLSID = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}" }, + [10] = { CLSID = "{682A481F-0CB5-4693-A382-D00DD4A156D7}" }, + } + } +} diff --git a/Database/Aircraft/Su-25T.lua b/Database/Aircraft/Su-25T.lua new file mode 100644 index 0000000..4114346 --- /dev/null +++ b/Database/Aircraft/Su-25T.lua @@ -0,0 +1,31 @@ +Library.aircraft["Su-25T"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 128,flare = 128,fuel = 3790,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{44EE8698-89F9-48EE-AF36-5FD31896A82D}" }, + [2] = { CLSID = "{CBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [3] = { CLSID = "{0180F983-C14A-11d8-9897-000476191836}" }, + [4] = { CLSID = "{F789E86A-EE2E-4E6B-B81E-D5E5F903B6ED}" }, + [5] = { CLSID = "{E92CBFE5-C153-11d8-9897-000476191836}" }, + [6] = { CLSID = "{B1EF6B0E-3D91-4047-A7A5-A99E7D8B4A8B}" }, + [7] = { CLSID = "{E92CBFE5-C153-11d8-9897-000476191836}" }, + [8] = { CLSID = "{F789E86A-EE2E-4E6B-B81E-D5E5F903B6ED}" }, + [9] = { CLSID = "{0180F983-C14A-11d8-9897-000476191836}" }, + [10] = { CLSID = "{CBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [11] = { CLSID = "{44EE8698-89F9-48EE-AF36-5FD31896A82C}" }, + -- Payload.Decade2000.Task.SEAD.Pylon01={44EE8698-89F9-48EE-AF36-5FD31896A82D} + -- Payload.Decade2000.Task.SEAD.Pylon02={CBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.SEAD.Pylon03={79D73885-0801-45a9-917F-C90FE1CE3DFC} + -- Payload.Decade2000.Task.SEAD.Pylon04={79D73885-0801-45a9-917F-C90FE1CE3DFC} + -- Payload.Decade2000.Task.SEAD.Pylon05={B5CA9846-776E-4230-B4FD-8BCC9BFB1676} + -- Payload.Decade2000.Task.SEAD.Pylon06={0519A264-0AB6-11d6-9193-00A0249B6F00} + -- Payload.Decade2000.Task.SEAD.Pylon07={B5CA9846-776E-4230-B4FD-8BCC9BFB1676} + -- Payload.Decade2000.Task.SEAD.Pylon08={79D73885-0801-45a9-917F-C90FE1CE3DFC} + -- Payload.Decade2000.Task.SEAD.Pylon09={79D73885-0801-45a9-917F-C90FE1CE3DFC} + -- Payload.Decade2000.Task.SEAD.Pylon10={CBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.SEAD.Pylon11={44EE8698-89F9-48EE-AF36-5FD31896A82C} + } + } +} diff --git a/Database/Aircraft/Su-27.lua b/Database/Aircraft/Su-27.lua new file mode 100644 index 0000000..135b30c --- /dev/null +++ b/Database/Aircraft/Su-27.lua @@ -0,0 +1,49 @@ +Library.aircraft["Su-27"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 96,flare = 96,fuel = 9400,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [2] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [3] = { CLSID = "{B79C379A-9E87-4E50-A1EE-7F7E29C2E87A}" }, + [4] = { CLSID = "{E8069896-8435-4B90-95C0-01A03AE6E400}" }, + [5] = { CLSID = "{E8069896-8435-4B90-95C0-01A03AE6E400}" }, + [6] = { CLSID = "{E8069896-8435-4B90-95C0-01A03AE6E400}" }, + [7] = { CLSID = "{E8069896-8435-4B90-95C0-01A03AE6E400}" }, + [8] = { CLSID = "{B79C379A-9E87-4E50-A1EE-7F7E29C2E87A}" }, + [9] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [10] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + -- Payload.Decade2000.Task.AirToGround.Pylon01={44EE8698-89F9-48EE-AF36-5FD31896A82F} + -- Payload.Decade2000.Task.AirToGround.Pylon02={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AirToGround.Pylon03={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon04={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon05={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon06={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon07={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon08={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon09={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AirToGround.Pylon10={44EE8698-89F9-48EE-AF36-5FD31896A82A} + -- Payload.Decade2000.Task.AntiShip.Pylon01={44EE8698-89F9-48EE-AF36-5FD31896A82F} + -- Payload.Decade2000.Task.AntiShip.Pylon02={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AntiShip.Pylon03={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AntiShip.Pylon04={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AntiShip.Pylon05={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AntiShip.Pylon06={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AntiShip.Pylon07={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AntiShip.Pylon08={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AntiShip.Pylon09={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AntiShip.Pylon10={44EE8698-89F9-48EE-AF36-5FD31896A82A} + -- Payload.Decade2000.Task.SEAD.Pylon01={44EE8698-89F9-48EE-AF36-5FD31896A82F} + -- Payload.Decade2000.Task.SEAD.Pylon02={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.SEAD.Pylon03={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.SEAD.Pylon04={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.SEAD.Pylon05={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.SEAD.Pylon06={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.SEAD.Pylon07={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.SEAD.Pylon08={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.SEAD.Pylon09={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.SEAD.Pylon10={44EE8698-89F9-48EE-AF36-5FD31896A82A} + } + } +} diff --git a/Database/Aircraft/Su-30.lua b/Database/Aircraft/Su-30.lua new file mode 100644 index 0000000..9b501c2 --- /dev/null +++ b/Database/Aircraft/Su-30.lua @@ -0,0 +1,19 @@ +Library.aircraft["Su-30"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 96,flare = 96,fuel = 9400,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{44EE8698-89F9-48EE-AF36-5FD31896A82F}" }, + [2] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [3] = { CLSID = "{B4C01D60-A8A3-4237-BD72-CA7655BC0FE9}" }, + [4] = { CLSID = "{B4C01D60-A8A3-4237-BD72-CA7655BC0FE9}" }, + [5] = { CLSID = "{B4C01D60-A8A3-4237-BD72-CA7655BC0FE9}" }, + [6] = { CLSID = "{B4C01D60-A8A3-4237-BD72-CA7655BC0FE9}" }, + [7] = { CLSID = "{B4C01D60-A8A3-4237-BD72-CA7655BC0FE9}" }, + [8] = { CLSID = "{B4C01D60-A8A3-4237-BD72-CA7655BC0FE9}" }, + [9] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [10] = { CLSID = "{44EE8698-89F9-48EE-AF36-5FD31896A82A}" }, + } + } +} diff --git a/Database/Aircraft/Su-33.lua b/Database/Aircraft/Su-33.lua new file mode 100644 index 0000000..794127f --- /dev/null +++ b/Database/Aircraft/Su-33.lua @@ -0,0 +1,57 @@ +Library.aircraft["Su-33"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 48,flare = 48,fuel = 9500,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [2] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [3] = { CLSID = "{9B25D316-0434-4954-868F-D51DB1A38DF0}" }, + [4] = { CLSID = "{E8069896-8435-4B90-95C0-01A03AE6E400}" }, + [5] = { CLSID = "{E8069896-8435-4B90-95C0-01A03AE6E400}" }, + [6] = { CLSID = "{E8069896-8435-4B90-95C0-01A03AE6E400}" }, + [7] = { CLSID = "{E8069896-8435-4B90-95C0-01A03AE6E400}" }, + [8] = { CLSID = "{E8069896-8435-4B90-95C0-01A03AE6E400}" }, + [9] = { CLSID = "{E8069896-8435-4B90-95C0-01A03AE6E400}" }, + [10] = { CLSID = "{9B25D316-0434-4954-868F-D51DB1A38DF0}" }, + [11] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [12] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + -- Payload.Decade2000.Task.AirToGround.Pylon01={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AirToGround.Pylon02={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AirToGround.Pylon03={A0648264-4BC0-4EE8-A543-D119F6BA4257} + -- Payload.Decade2000.Task.AirToGround.Pylon04={A0648264-4BC0-4EE8-A543-D119F6BA4257} + -- Payload.Decade2000.Task.AirToGround.Pylon05={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon06={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon07={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon08={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AirToGround.Pylon09={A0648264-4BC0-4EE8-A543-D119F6BA4257} + -- Payload.Decade2000.Task.AirToGround.Pylon10={A0648264-4BC0-4EE8-A543-D119F6BA4257} + -- Payload.Decade2000.Task.AirToGround.Pylon11={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AirToGround.Pylon12={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AntiShip.Pylon01={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AntiShip.Pylon02={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AntiShip.Pylon03={A0648264-4BC0-4EE8-A543-D119F6BA4257} + -- Payload.Decade2000.Task.AntiShip.Pylon04={A0648264-4BC0-4EE8-A543-D119F6BA4257} + -- Payload.Decade2000.Task.AntiShip.Pylon05={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AntiShip.Pylon06={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AntiShip.Pylon07={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AntiShip.Pylon08={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.AntiShip.Pylon09={A0648264-4BC0-4EE8-A543-D119F6BA4257} + -- Payload.Decade2000.Task.AntiShip.Pylon10={A0648264-4BC0-4EE8-A543-D119F6BA4257} + -- Payload.Decade2000.Task.AntiShip.Pylon11={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.AntiShip.Pylon12={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.SEAD.Pylon01={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.SEAD.Pylon02={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.SEAD.Pylon03={A0648264-4BC0-4EE8-A543-D119F6BA4257} + -- Payload.Decade2000.Task.SEAD.Pylon04={A0648264-4BC0-4EE8-A543-D119F6BA4257} + -- Payload.Decade2000.Task.SEAD.Pylon05={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.SEAD.Pylon06={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.SEAD.Pylon07={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.SEAD.Pylon08={37DCC01E-9E02-432F-B61D-10C166CA2798} + -- Payload.Decade2000.Task.SEAD.Pylon09={A0648264-4BC0-4EE8-A543-D119F6BA4257} + -- Payload.Decade2000.Task.SEAD.Pylon10={A0648264-4BC0-4EE8-A543-D119F6BA4257} + -- Payload.Decade2000.Task.SEAD.Pylon11={FBC29BFE-3D24-4C64-B81D-941239D12249} + -- Payload.Decade2000.Task.SEAD.Pylon12={FBC29BFE-3D24-4C64-B81D-941239D12249} + } + } +} diff --git a/Database/Aircraft/Su-34.lua b/Database/Aircraft/Su-34.lua new file mode 100644 index 0000000..efaba7a --- /dev/null +++ b/Database/Aircraft/Su-34.lua @@ -0,0 +1,21 @@ +Library.aircraft["Su-34"] = { + altitude = DCSEx.converter.feetToMeters(20000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 64,flare = 64,fuel = 9800,gun = 100 }, + pylons = { + default = { + [1] = { CLSID = "{44EE8698-89F9-48EE-AF36-5FD31896A82F}" }, + [2] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [3] = { CLSID = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}" }, + [4] = { CLSID = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}" }, + [5] = { CLSID = "{3C612111-C7AD-476E-8A8E-2485812F4E5C}" }, + [6] = { CLSID = "{3C612111-C7AD-476E-8A8E-2485812F4E5C}" }, + [7] = { CLSID = "{3C612111-C7AD-476E-8A8E-2485812F4E5C}" }, + [8] = { CLSID = "{3C612111-C7AD-476E-8A8E-2485812F4E5C}" }, + [9] = { CLSID = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}" }, + [10] = { CLSID = "{FC56DF80-9B09-44C5-8976-DCFAFF219062}" }, + [11] = { CLSID = "{FBC29BFE-3D24-4C64-B81D-941239D12249}" }, + [12] = { CLSID = "{44EE8698-89F9-48EE-AF36-5FD31896A82A}" }, + } + } +} diff --git a/Database/Aircraft/Tu-160.lua b/Database/Aircraft/Tu-160.lua new file mode 100644 index 0000000..42a7afe --- /dev/null +++ b/Database/Aircraft/Tu-160.lua @@ -0,0 +1,11 @@ +Library.aircraft["Tu-160"] = { + altitude = DCSEx.converter.feetToMeters(30000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 72,flare = 72,fuel = 157000 }, + pylons = { + default = { + [1] = { CLSID = "{0290F5DE-014A-4BB1-9843-D717749B1DED}" }, + [2] = { CLSID = "{0290F5DE-014A-4BB1-9843-D717749B1DED}" }, + } + } +} diff --git a/Database/Aircraft/Tu-22M3.lua b/Database/Aircraft/Tu-22M3.lua new file mode 100644 index 0000000..dc48875 --- /dev/null +++ b/Database/Aircraft/Tu-22M3.lua @@ -0,0 +1,10 @@ +Library.aircraft["Tu-22M3"] = { + altitude = DCSEx.converter.feetToMeters(30000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 48,flare = 48,fuel = 50000 }, + pylons = { + default = { + [3] = { CLSID = "{AD5E5863-08FC-4283-B92C-162E2B2BD3FF}" }, + } + } +} diff --git a/Database/Aircraft/Tu-95MS.lua b/Database/Aircraft/Tu-95MS.lua new file mode 100644 index 0000000..ae11301 --- /dev/null +++ b/Database/Aircraft/Tu-95MS.lua @@ -0,0 +1,10 @@ +Library.aircraft["Tu-95MS"] = { + altitude = DCSEx.converter.feetToMeters(30000), + speed = DCSEx.converter.knotsToMps(300), + payload = { chaff = 48,flare = 48,fuel = 87000 }, + pylons = { + default = { + [1] = { CLSID = "{0290F5DE-014A-4BB1-9843-D717749B1DED}" }, + } + } +} diff --git a/Database/Aircraft/UH-1H.lua b/Database/Aircraft/UH-1H.lua new file mode 100644 index 0000000..03c6843 --- /dev/null +++ b/Database/Aircraft/UH-1H.lua @@ -0,0 +1,14 @@ +Library.aircraft["UH-1H"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 0,flare = 60,fuel = 631 }, + properties = { SoloFlight = false, ExhaustScreen = true, GunnersAISkill = 90, NetCrewControlPriority = 0, EngineResource = 90 }, + pylons = { + default = { + [1] = { CLSID = "{M134_L}" }, + [2] = { CLSID = "{XM158_MK5}" }, + [5] = { CLSID = "{XM158_MK5}" }, + [6] = { CLSID = "{M134_R}" }, + } + } +} diff --git a/Database/Aircraft/UH-60A.lua b/Database/Aircraft/UH-60A.lua new file mode 100644 index 0000000..488214d --- /dev/null +++ b/Database/Aircraft/UH-60A.lua @@ -0,0 +1,5 @@ +Library.aircraft["UH-60A"] = { + altitude = DCSEx.converter.feetToMeters(1500), + speed = DCSEx.converter.knotsToMps(90), + payload = { chaff = 30,flare = 30,fuel = 1100 }, +} diff --git a/Database/Factions/China.lua b/Database/Factions/China.lua new file mode 100644 index 0000000..90b667b --- /dev/null +++ b/Database/Factions/China.lua @@ -0,0 +1,50 @@ +-- do +-- Library.factions.tables["China"] = {} +-- Library.factions.tables["China"].theaters = {} +-- Library.factions.tables["China"].timePeriods = {} + +-- Library.factions.tables["China"].units = {} + +-- Library.factions.tables["China"].units[DCSEx.enums.timePeriod.WORLD_WAR_2] = {} +-- Library.factions.tables["China"].units[DCSEx.enums.timePeriod.KOREA_WAR] = {} +-- Library.factions.tables["China"].units[DCSEx.enums.timePeriod.VIETNAM_WAR] = {} +-- Library.factions.tables["China"].units[DCSEx.enums.timePeriod.COLD_WAR] = {} + +-- Library.factions.tables["China"].units[DCSEx.enums.timePeriod.MODERN] = { +-- [DCSEx.enums.unitFamily.AIRDEFENSE_AAA_MOBILE] = { "HL_ZU-23", "tt_ZU-23" }, +-- [DCSEx.enums.unitFamily.AIRDEFENSE_AAA_STATIC] = { "ZU-23 Closed Insurgent", "ZU-23 Insurgent" }, +-- [DCSEx.enums.unitFamily.AIRDEFENSE_MANPADS] = { "Igla manpad INS", "SA-18 Igla-S manpad" }, +-- [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_LONG] = { "*SA-2" }, +-- [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_MEDIUM] = { "*SA-2", "*SA-3" }, +-- [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT] = { "Osa 9A33 ln", "Tor 9A331" }, +-- [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT_IR] = { "Strela-1 9P31" }, + +-- [DCSEx.enums.unitFamily.GROUND_APC] = { "HL_DSHK", "HL_KORD", "tt_DSHK", "tt_KORD", "BMD-1", "BMP-1", "Boman", "BRDM-2", "BTR_D", "BTR-80" }, +-- [DCSEx.enums.unitFamily.GROUND_ARTILLERY] = { "Grad-URAL", "SAU 2-C9", "SAU Akatsia", "SAU Gvozdika", "SAU Msta", "Smerch", "SpGH_Dana", "Uragan_BM-27" }, +-- [DCSEx.enums.unitFamily.GROUND_INFANTRY] = { "Infantry AK Ins" }, +-- [DCSEx.enums.unitFamily.GROUND_MBT] = { "ZTZ96B" }, +-- [DCSEx.enums.unitFamily.GROUND_SS_MISSILE] = { "Scud_B" }, +-- [DCSEx.enums.unitFamily.GROUND_UNARMED] = { "Land_Rover_101_FC", "Land_Rover_109_S3", "Ural-375", "Ural-4320 APA-5D", "Ural-4320T" }, + +-- [DCSEx.enums.unitFamily.HELICOPTER_ATTACK] = { "Mi-24V", "Mi-28N" }, +-- [DCSEx.enums.unitFamily.HELICOPTER_TRANSPORT] = { "Mi-8MT", "Mi-26" }, +-- [DCSEx.enums.unitFamily.PLANE_AWACS] = { "A-50" }, + +-- [DCSEx.enums.unitFamily.PLANE_BOMBER] = { "Tu-22M3" }, +-- [DCSEx.enums.unitFamily.PLANE_FIGHTER] = { "MiG-21Bis" }, +-- [DCSEx.enums.unitFamily.PLANE_TANKER] = { "IL-78M" }, +-- [DCSEx.enums.unitFamily.PLANE_TRANSPORT] = { "An-26B" }, +-- [DCSEx.enums.unitFamily.PLANE_UAV] = { "WingLoong-I" }, + +-- [DCSEx.enums.unitFamily.SHIP_CARGO] = { "Dry-cargo ship-1", "Dry-cargo ship-2", "ELNYA", "Ship_Tilde_Supply" }, +-- [DCSEx.enums.unitFamily.SHIP_CARRIER] = { "KUZNECOW" }, +-- [DCSEx.enums.unitFamily.SHIP_CRUISER] = { "MOSCOW", "PIOTR" }, +-- [DCSEx.enums.unitFamily.SHIP_FRIGATE] = { "leander-gun-andromeda", "leander-gun-ariadne", "leander-gun-condell", "leander-gun-lynch" }, +-- [DCSEx.enums.unitFamily.SHIP_LIGHT] = { "speedboat" }, +-- [DCSEx.enums.unitFamily.SHIP_MISSILE_BOAT] = { "ALBATROS", "BDK-775", "MOLNIYA" }, +-- [DCSEx.enums.unitFamily.SHIP_SUBMARINE] = { "KILO", "SOM" }, + + -- -- [DCSEx.enums.unitFamily.STATIC_STRUCTURE] = { "af_hq", ".Command Center", "Building01_PBR", "Building02_PBR", "Building03_PBR", "Building04_PBR", "Building05_PBR", "Bunker", "Chemical tank A", "Comms tower M", "FARP Fuel Depot", "outpost", "Sandbox", "Workshop A" }, + -- [DCSEx.enums.unitFamily.STATIC_STRUCTURE] = { "af_hq", ".Command Center", "Building01_PBR", "Building02_PBR", "Building03_PBR", "Building04_PBR", "Building05_PBR", "Chemical tank A", "Comms tower M", "FARP Fuel Depot", "outpost", "Workshop A" }, +-- } +-- end diff --git a/Database/Factions/Insurgents.lua b/Database/Factions/Insurgents.lua new file mode 100644 index 0000000..90dd4e7 --- /dev/null +++ b/Database/Factions/Insurgents.lua @@ -0,0 +1,51 @@ +do + Library.factions.tables["Insurgents"] = {} + Library.factions.tables["Insurgents"].theaters = {} + Library.factions.tables["Insurgents"].timePeriods = {} + + Library.factions.tables["Insurgents"].units = {} + + Library.factions.tables["Insurgents"].units[DCSEx.enums.timePeriod.WORLD_WAR_2] = {} + Library.factions.tables["Insurgents"].units[DCSEx.enums.timePeriod.KOREA_WAR] = {} + Library.factions.tables["Insurgents"].units[DCSEx.enums.timePeriod.VIETNAM_WAR] = {} + Library.factions.tables["Insurgents"].units[DCSEx.enums.timePeriod.COLD_WAR] = {} + + Library.factions.tables["Insurgents"].units[DCSEx.enums.timePeriod.MODERN] = { + [DCSEx.enums.unitFamily.AIRDEFENSE_AAA_MOBILE] = { "HL_ZU-23", "tt_ZU-23" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_AAA_STATIC] = { "ZU-23 Closed Insurgent", "ZU-23 Insurgent" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_MANPADS] = { "Igla manpad INS", "SA-18 Igla-S manpad" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_LONG] = { "*SA-2" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_MEDIUM] = { "*SA-2", "*SA-3" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT] = { "Osa 9A33 ln", "Tor 9A331" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT_IR] = { "Strela-1 9P31" }, + + [DCSEx.enums.unitFamily.GROUND_APC] = { "HL_DSHK", "HL_KORD", "tt_DSHK", "tt_KORD", "BMD-1", "BMP-1", "Boman", "BRDM-2", "BTR_D", "BTR-80" }, + [DCSEx.enums.unitFamily.GROUND_ARTILLERY] = { "Grad-URAL", "SAU 2-C9", "SAU Akatsia", "SAU Gvozdika", "SAU Msta", "Smerch", "SpGH_Dana", "Uragan_BM-27" }, + [DCSEx.enums.unitFamily.GROUND_INFANTRY] = { "Infantry AK Ins" }, + [DCSEx.enums.unitFamily.GROUND_MBT] = { "T-55", "T-72B" }, + [DCSEx.enums.unitFamily.GROUND_SS_MISSILE] = { "Scud_B" }, + [DCSEx.enums.unitFamily.GROUND_UNARMED] = { "Land_Rover_101_FC", "Land_Rover_109_S3", "Ural-375", "Ural-4320 APA-5D", "Ural-4320T" }, + + [DCSEx.enums.unitFamily.HELICOPTER_ATTACK] = { "Mi-24V", "Mi-28N" }, + [DCSEx.enums.unitFamily.HELICOPTER_TRANSPORT] = { "Mi-8MT", "Mi-26" }, + + [DCSEx.enums.unitFamily.PLANE_ATTACK] = { "Su-25" }, + [DCSEx.enums.unitFamily.PLANE_AWACS] = { "A-50" }, + [DCSEx.enums.unitFamily.PLANE_BOMBER] = { "Tu-22M3" }, + [DCSEx.enums.unitFamily.PLANE_FIGHTER] = { "MiG-21Bis" }, + [DCSEx.enums.unitFamily.PLANE_TANKER] = { "IL-78M" }, + [DCSEx.enums.unitFamily.PLANE_TRANSPORT] = { "An-26B" }, + [DCSEx.enums.unitFamily.PLANE_UAV] = { "WingLoong-I" }, + + [DCSEx.enums.unitFamily.SHIP_CARGO] = { "Dry-cargo ship-1", "Dry-cargo ship-2", "ELNYA", "Ship_Tilde_Supply" }, + [DCSEx.enums.unitFamily.SHIP_CARRIER] = { "KUZNECOW" }, + [DCSEx.enums.unitFamily.SHIP_CRUISER] = { "MOSCOW", "PIOTR" }, + [DCSEx.enums.unitFamily.SHIP_FRIGATE] = { "leander-gun-andromeda", "leander-gun-ariadne", "leander-gun-condell", "leander-gun-lynch" }, + [DCSEx.enums.unitFamily.SHIP_LIGHT] = { "speedboat" }, + [DCSEx.enums.unitFamily.SHIP_MISSILE_BOAT] = { "ALBATROS", "BDK-775", "MOLNIYA" }, + [DCSEx.enums.unitFamily.SHIP_SUBMARINE] = { "KILO", "SOM" }, + + -- [DCSEx.enums.unitFamily.STATIC_STRUCTURE] = { "af_hq", ".Command Center", "Building01_PBR", "Building02_PBR", "Building03_PBR", "Building04_PBR", "Building05_PBR", "Bunker", "Chemical tank A", "Comms tower M", "FARP Fuel Depot", "outpost", "Sandbox", "Workshop A" }, + [DCSEx.enums.unitFamily.STATIC_STRUCTURE] = { "af_hq", ".Command Center", "Building01_PBR", "Building02_PBR", "Building03_PBR", "Building04_PBR", "Building05_PBR", "Chemical tank A", "Comms tower M", "FARP Fuel Depot", "outpost", "Workshop A" }, + } +end diff --git a/Database/Factions/MiddleEast-Iran.lua b/Database/Factions/MiddleEast-Iran.lua new file mode 100644 index 0000000..9219361 --- /dev/null +++ b/Database/Factions/MiddleEast-Iran.lua @@ -0,0 +1,74 @@ +do + local factionName = "Middle-eastern state" + if env.mission.theatre == "PersianGulf" then + factionName = "Saudi Arabia" + elseif env.mission.theatre == "Syria" then + factionName = "Syria" + Library.factions.defaults[coalition.side.RED] = "Syria" + end + + Library.factions.tables[factionName] = {} + Library.factions.tables[factionName].theaters = {"PersianGulf"," Syria"} + Library.factions.tables[factionName].timePeriods = {} + + Library.factions.tables[factionName].units = {} + + Library.factions.tables[factionName].units[DCSEx.enums.timePeriod.WORLD_WAR_2] = {} + Library.factions.tables[factionName].units[DCSEx.enums.timePeriod.KOREA_WAR] = {} + Library.factions.tables[factionName].units[DCSEx.enums.timePeriod.VIETNAM_WAR] = {} + Library.factions.tables[factionName].units[DCSEx.enums.timePeriod.COLD_WAR] = {} + + Library.factions.tables[factionName].units[DCSEx.enums.timePeriod.MODERN] = { + [DCSEx.enums.unitFamily.AIRDEFENSE_AAA_MOBILE] = { "Ural-375 ZU-23", "ZSU_57_2", "ZSU-23-4 Shilka" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_AAA_STATIC] = { "ZU-23 Emplacement Closed", "ZU-23 Emplacement" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_MANPADS] = { "SA-18 Igla manpad", "SA-18 Igla-S manpad" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_LONG] = { "*SA-2", "*SA-10" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_MEDIUM] = { "*SA-3", "*SA-6", "*SA-11" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT] = { "2S6 Tunguska", "Osa 9A33 ln", "Tor 9A331" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT_IR] = { "Strela-1 9P31", "Strela-10M3" }, + + [DCSEx.enums.unitFamily.GROUND_APC] = { "BMD-1", "BMP-1", "BMP-2", "BMP-3", "Boman", "BRDM-2", "BTR_D", "BTR-80", "BTR-82A", "Grad_FDDM", "MTLB" }, + [DCSEx.enums.unitFamily.GROUND_ARTILLERY] = { "Grad-URAL", "SAU 2-C9", "SAU Akatsia", "SAU Gvozdika", "SAU Msta", "Smerch", "SpGH_Dana", "Uragan_BM-27" }, + [DCSEx.enums.unitFamily.GROUND_INFANTRY] = { "Infantry AK", "Infantry AK ver2", "Infantry AK ver3", "Paratrooper AKS-74", "Paratrooper RPG-16", "Soldier AK" }, + [DCSEx.enums.unitFamily.GROUND_MBT] = { "T-55", "T-72B", "T-80UD", "T-90" }, + [DCSEx.enums.unitFamily.GROUND_SS_MISSILE] = { "Scud_B" }, + [DCSEx.enums.unitFamily.GROUND_UNARMED] = { "Ural-375", "Ural-4320 APA-5D", "Ural-4320T" }, + + [DCSEx.enums.unitFamily.HELICOPTER_ATTACK] = { "Ka-50", "Mi-24V", "Mi-28N" }, + [DCSEx.enums.unitFamily.HELICOPTER_TRANSPORT] = { "Ka-27", "Mi-8MT", "Mi-26" }, + + [DCSEx.enums.unitFamily.PLANE_ATTACK] = { "Su-25" }, + [DCSEx.enums.unitFamily.PLANE_AWACS] = { "A-50" }, + [DCSEx.enums.unitFamily.PLANE_BOMBER] = { "Tu-22M3", "Tu-95MS", "Tu-160" }, + [DCSEx.enums.unitFamily.PLANE_FIGHTER] = { "MiG-21Bis" }, + [DCSEx.enums.unitFamily.PLANE_TANKER] = { "IL-78M" }, + [DCSEx.enums.unitFamily.PLANE_TRANSPORT] = { "An-26B", "An-30M", "IL-76MD" }, + [DCSEx.enums.unitFamily.PLANE_UAV] = { "WingLoong-I" }, + + [DCSEx.enums.unitFamily.HELICOPTER_ATTACK] = { "Ka-50", "Mi-24V", "Mi-28N" }, + + [DCSEx.enums.unitFamily.SHIP_CARGO] = { "Dry-cargo ship-1", "Dry-cargo ship-2", "ELNYA", "Ship_Tilde_Supply" }, + [DCSEx.enums.unitFamily.SHIP_CARRIER] = { "CV_1143_5", "KUZNECOW" }, + [DCSEx.enums.unitFamily.SHIP_CRUISER] = { "MOSCOW", "PIOTR" }, + [DCSEx.enums.unitFamily.SHIP_FRIGATE] = { "NEUSTRASH", "REZKY" }, + [DCSEx.enums.unitFamily.SHIP_LIGHT] = { "speedboat" }, + [DCSEx.enums.unitFamily.SHIP_MISSILE_BOAT] = { "ALBATROS", "BDK-775", "MOLNIYA" }, + [DCSEx.enums.unitFamily.SHIP_SUBMARINE] = { "IMPROVED_KILO", "KILO", "SOM" }, + + -- [DCSEx.enums.unitFamily.STATIC_STRUCTURE] = { "af_hq", ".Command Center", "Building01_PBR", "Building02_PBR", "Building03_PBR", "Building04_PBR", "Building05_PBR", "Bunker", "Chemical tank A", "Comms tower M", "FARP Fuel Depot", "outpost", "Sandbox", "Workshop A" }, + [DCSEx.enums.unitFamily.STATIC_STRUCTURE] = { "af_hq", ".Command Center", "Building01_PBR", "Building02_PBR", "Building03_PBR", "Building04_PBR", "Building05_PBR", "Chemical tank A", "Comms tower M", "FARP Fuel Depot", "outpost", "Workshop A" }, + } + + ------------------------------------------------------------------------ + -- Create Iran faction from generic "Middle-Eastern state" coalition + ------------------------------------------------------------------------ + Library.factions.tables["Iran"] = DCSEx.table.deepCopy(Library.factions.tables[factionName]) + Library.factions.tables["Iran"].theaters = {"PersianGulf"} + + if env.mission.theatre == "PersianGulf" then + Library.factions.defaults[coalition.side.RED] = "Iran" + end + + -- Iran have F-14 too + table.insert(Library.factions.tables["Iran"].units[DCSEx.enums.timePeriod.MODERN][DCSEx.enums.unitFamily.PLANE_FIGHTER], "F-14A-135-GR") +end diff --git a/Database/Factions/Russia.lua b/Database/Factions/Russia.lua new file mode 100644 index 0000000..0eed915 --- /dev/null +++ b/Database/Factions/Russia.lua @@ -0,0 +1,61 @@ +do + local factionName = "Russia" + -- if env.mission.date.Year < 1990 then + -- factionName = "USSR" + -- end + + Library.factions.defaults[coalition.side.RED] = factionName + + Library.factions.tables[factionName] = {} + Library.factions.tables[factionName].theaters = {} + Library.factions.tables[factionName].timePeriods = {} + + Library.factions.tables[factionName].units = {} + + Library.factions.tables[factionName].units[DCSEx.enums.timePeriod.WORLD_WAR_2] = {} + Library.factions.tables[factionName].units[DCSEx.enums.timePeriod.KOREA_WAR] = {} + Library.factions.tables[factionName].units[DCSEx.enums.timePeriod.VIETNAM_WAR] = {} + Library.factions.tables[factionName].units[DCSEx.enums.timePeriod.COLD_WAR] = {} + + Library.factions.tables[factionName].units[DCSEx.enums.timePeriod.MODERN] = { + [DCSEx.enums.unitFamily.AIRDEFENSE_AAA_MOBILE] = { "Ural-375 ZU-23", "ZSU_57_2", "ZSU-23-4 Shilka" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_AAA_STATIC] = { "ZU-23 Emplacement Closed", "ZU-23 Emplacement" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_MANPADS] = { "SA-18 Igla manpad", "SA-18 Igla-S manpad" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_LONG] = { "*SA-2", "*SA-10" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_LONG] = { "*SA-10" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_MEDIUM] = { "*SA-3", "*SA-6", "*SA-11" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT] = { "2S6 Tunguska", "Osa 9A33 ln", "Tor 9A331" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT_IR] = { "Strela-1 9P31", "Strela-10M3" }, + + [DCSEx.enums.unitFamily.GROUND_APC] = { "BMD-1", "BMP-1", "BMP-2", "BMP-3", "Boman", "BRDM-2", "BTR_D", "BTR-80", "BTR-82A", "Grad_FDDM", "MTLB" }, + [DCSEx.enums.unitFamily.GROUND_ARTILLERY] = { "Grad-URAL", "SAU 2-C9", "SAU Akatsia", "SAU Gvozdika", "SAU Msta", "Smerch", "SpGH_Dana", "Uragan_BM-27" }, + [DCSEx.enums.unitFamily.GROUND_INFANTRY] = { "Infantry AK", "Infantry AK ver2", "Infantry AK ver3", "Paratrooper AKS-74", "Paratrooper RPG-16", "Soldier AK" }, + [DCSEx.enums.unitFamily.GROUND_MBT] = { "T-55", "T-72B", "T-80UD", "T-90" }, + [DCSEx.enums.unitFamily.GROUND_SS_MISSILE] = { "Scud_B" }, + [DCSEx.enums.unitFamily.GROUND_UNARMED] = { "Ural-375", "Ural-4320 APA-5D", "Ural-4320T" }, + + [DCSEx.enums.unitFamily.HELICOPTER_ATTACK] = { "Ka-50", "Mi-24V", "Mi-28N" }, + [DCSEx.enums.unitFamily.HELICOPTER_TRANSPORT] = { "Ka-27", "Mi-8MT", "Mi-26" }, + + [DCSEx.enums.unitFamily.PLANE_ATTACK] = { "Su-25" }, + [DCSEx.enums.unitFamily.PLANE_AWACS] = { "A-50" }, + [DCSEx.enums.unitFamily.PLANE_BOMBER] = { "Tu-22M3", "Tu-95MS", "Tu-160" }, + [DCSEx.enums.unitFamily.PLANE_FIGHTER] = { "MiG-29", "Su-27", "Su-33" }, + [DCSEx.enums.unitFamily.PLANE_TANKER] = { "IL-78M" }, + [DCSEx.enums.unitFamily.PLANE_TRANSPORT] = { "An-26B", "An-30M", "IL-76MD" }, + [DCSEx.enums.unitFamily.PLANE_UAV] = { "WingLoong-I" }, + + [DCSEx.enums.unitFamily.HELICOPTER_ATTACK] = { "Ka-50", "Mi-24V", "Mi-28N" }, + + [DCSEx.enums.unitFamily.SHIP_CARGO] = { "Dry-cargo ship-1", "Dry-cargo ship-2", "ELNYA", "Ship_Tilde_Supply" }, + [DCSEx.enums.unitFamily.SHIP_CARRIER] = { "CV_1143_5", "KUZNECOW" }, + [DCSEx.enums.unitFamily.SHIP_CRUISER] = { "MOSCOW", "PIOTR" }, + [DCSEx.enums.unitFamily.SHIP_FRIGATE] = { "NEUSTRASH", "REZKY" }, + [DCSEx.enums.unitFamily.SHIP_LIGHT] = { "speedboat" }, + [DCSEx.enums.unitFamily.SHIP_MISSILE_BOAT] = { "ALBATROS", "BDK-775", "MOLNIYA" }, + [DCSEx.enums.unitFamily.SHIP_SUBMARINE] = { "IMPROVED_KILO", "KILO", "SOM" }, + + -- [DCSEx.enums.unitFamily.STATIC_STRUCTURE] = { "af_hq", ".Command Center", "Building01_PBR", "Building02_PBR", "Building03_PBR", "Building04_PBR", "Building05_PBR", "Bunker", "Chemical tank A", "Comms tower M", "FARP Fuel Depot", "outpost", "Sandbox", "Workshop A" }, + [DCSEx.enums.unitFamily.STATIC_STRUCTURE] = { "af_hq", ".Command Center", "Building01_PBR", "Building02_PBR", "Building03_PBR", "Building04_PBR", "Building05_PBR", "Chemical tank A", "Comms tower M", "FARP Fuel Depot", "outpost", "Workshop A" }, + } +end diff --git a/Database/Factions/USA.lua b/Database/Factions/USA.lua new file mode 100644 index 0000000..1f6ed88 --- /dev/null +++ b/Database/Factions/USA.lua @@ -0,0 +1,53 @@ +Library.factions.defaults[coalition.side.BLUE] = "USA" + +do + Library.factions.tables["USA"] = {} + Library.factions.tables["USA"].theaters = {} + Library.factions.tables["USA"].timePeriods = {} + + Library.factions.tables["USA"].units = {} + + Library.factions.tables["USA"].units[DCSEx.enums.timePeriod.WORLD_WAR_2] = {} + Library.factions.tables["USA"].units[DCSEx.enums.timePeriod.KOREA_WAR] = {} + Library.factions.tables["USA"].units[DCSEx.enums.timePeriod.VIETNAM_WAR] = {} + Library.factions.tables["USA"].units[DCSEx.enums.timePeriod.COLD_WAR] = {} + + Library.factions.tables["USA"].units[DCSEx.enums.timePeriod.MODERN] = { + [DCSEx.enums.unitFamily.AIRDEFENSE_AAA_MOBILE] = { "Gepard", "Vulcan" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_AAA_STATIC] = { "Gepard", "Vulcan" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_MANPADS] = { "Soldier stinger" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_LONG] = { "*Patriot" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_MEDIUM] = { "*HAWK", "*NASAMS" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT] = { "rapier_fsa", "Roland ADS" }, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT_IR] = { "M6 Linebacker", "M48 Chaparral", "M1097 Avenger" }, + + [DCSEx.enums.unitFamily.GROUND_APC] = { "AAV7", "Cobra", "LAV-25", "M-2 Bradley", "M-113", "M1045 HMMWV TOW", "M1126 Stryker ICV", "M1128 Stryker MGS", "Marder", "MCV-80", "MLRS FDDM", "TPZ" }, + [DCSEx.enums.unitFamily.GROUND_ARTILLERY] = { "M-109", "MLRS" }, + [DCSEx.enums.unitFamily.GROUND_INFANTRY] = { "Soldier M4 GRG", "Soldier M4", "Soldier M249", "Soldier RPG" }, + [DCSEx.enums.unitFamily.GROUND_MBT] = { "Challenger2", "Leclerc", "Leopard-2", "Leopard1A3", "M-1 Abrams", "Merkava_Mk4" }, + [DCSEx.enums.unitFamily.GROUND_SS_MISSILE] = { "Scud_B" }, + [DCSEx.enums.unitFamily.GROUND_UNARMED] = { "Land_Rover_101_FC", "Land_Rover_109_S3", "M 818" }, + + [DCSEx.enums.unitFamily.HELICOPTER_ATTACK] = { "AH-1W", "AH-64D", "OH-58D", "SA342L", "SA342M", "SA342Minigun", "SA342Mistral" }, + [DCSEx.enums.unitFamily.HELICOPTER_TRANSPORT] = { "CH-47D", "CH-53E", "SH-60B", "UH-60A" }, + + [DCSEx.enums.unitFamily.PLANE_ATTACK] = { "A-10C_2" }, + [DCSEx.enums.unitFamily.PLANE_AWACS] = { "E-2C", "E-3A" }, + [DCSEx.enums.unitFamily.PLANE_BOMBER] = { "B-1B Lancer", "B-52H" }, + [DCSEx.enums.unitFamily.PLANE_FIGHTER] = { "F-16C_50", "FA-18C_hornet" }, + [DCSEx.enums.unitFamily.PLANE_TANKER] = { "KC-135", "KC135MPRS" }, + [DCSEx.enums.unitFamily.PLANE_TRANSPORT] = { "C-17A", "C-130" }, + [DCSEx.enums.unitFamily.PLANE_UAV] = { "RQ-1A Predator" }, + + [DCSEx.enums.unitFamily.SHIP_CARGO] = { "Dry-cargo ship-1", "Dry-cargo ship-2", "ELNYA", "Ship_Tilde_Supply" }, + [DCSEx.enums.unitFamily.SHIP_CARRIER] = { "CVN_71", "CVN_72", "CVN_73", "CVN_75", "hms_invincible", "LHA_Tarawa", "Stennis" }, + [DCSEx.enums.unitFamily.SHIP_CRUISER] = { "TICONDEROG" }, + [DCSEx.enums.unitFamily.SHIP_FRIGATE] = { "PERRY", "USS_Arleigh_Burke_IIa" }, + [DCSEx.enums.unitFamily.SHIP_LIGHT] = { "speedboat" }, + [DCSEx.enums.unitFamily.SHIP_MISSILE_BOAT] = { "CastleClass_01", "La_Combattante_II" }, + [DCSEx.enums.unitFamily.SHIP_SUBMARINE] = { "santafe" }, + + -- [DCSEx.enums.unitFamily.STATIC_STRUCTURE] = { "af_hq", ".Command Center", "Building01_PBR", "Building02_PBR", "Building03_PBR", "Building04_PBR", "Building05_PBR", "Bunker", "Chemical tank A", "Comms tower M", "FARP Fuel Depot", "outpost", "Sandbox", "Workshop A" }, + [DCSEx.enums.unitFamily.STATIC_STRUCTURE] = { "af_hq", ".Command Center", "Building01_PBR", "Building02_PBR", "Building03_PBR", "Building04_PBR", "Building05_PBR", "Chemical tank A", "Comms tower M", "FARP Fuel Depot", "outpost", "Workshop A" }, + } +end \ No newline at end of file diff --git a/Database/Factions/ZZZ_CreateAnyFaction.lua b/Database/Factions/ZZZ_CreateAnyFaction.lua new file mode 100644 index 0000000..cebf357 --- /dev/null +++ b/Database/Factions/ZZZ_CreateAnyFaction.lua @@ -0,0 +1,30 @@ +-- The "Any" faction has access to all units from all other factions +do + Library.factions.tables["Any"] = {} + Library.factions.tables["Any"].theaters = {} + Library.factions.tables["Any"].timePeriods = {} + + Library.factions.tables["Any"].units = {} + + local function copyUnitTableToAny(factionID, timePeriod) + Library.factions.tables["Any"].units[timePeriod] = {} + + for k,_ in pairs(Library.factions.tables[factionID].units[timePeriod]) do + Library.factions.tables["Any"].units[timePeriod][k] = Library.factions.tables["Any"].units[timePeriod][k] or {} + + for _,u in ipairs(Library.factions.tables[factionID].units[timePeriod][k]) do + if not DCSEx.table.contains(Library.factions.tables["Any"].units[timePeriod][k], u) then + table.insert(Library.factions.tables["Any"].units[timePeriod][k], u) + end + end + end + end + + for k,_ in pairs(Library.factions.tables) do + if k ~= "Any" then + for _,tp in pairs(DCSEx.enums.timePeriod) do + copyUnitTableToAny(k, tp) + end + end + end +end diff --git a/Database/Factions/ZZZ_SetupDefaults.lua b/Database/Factions/ZZZ_SetupDefaults.lua new file mode 100644 index 0000000..87b7f4a --- /dev/null +++ b/Database/Factions/ZZZ_SetupDefaults.lua @@ -0,0 +1,9 @@ +-- do +-- for k,_ in pairs(Library.factions.tables) do +-- for _,tp in pairs(DCSEx.enums.timePeriod) do +-- if not Library.factions.tables[k].units[tp] or #Library.factions.tables[k].units[tp] == 0 then +-- +-- end +-- end +-- end +-- end \ No newline at end of file diff --git a/Database/Tasks/AntiShipCargo.lua b/Database/Tasks/AntiShipCargo.lua new file mode 100644 index 0000000..0d1325e --- /dev/null +++ b/Database/Tasks/AntiShipCargo.lua @@ -0,0 +1,26 @@ +Library.tasks.antishipCargo = { + taskFamily = DCSEx.enums.taskFamily.ANTISHIP, + description = + { + briefing = { + "Enemy cargo ships are delivering weapons and supplies to frontline units. Sinking them will disrupt their logistics and slow their advance.", + "Cargo ships are moving fuel, ammo, and enemy reinforcements. Take them out to choke the enemy's front-line operations.", + "Intel confirms that enemy cargo ships are carrying weapons and reinforcements. They should be destroyed immediately.", + "Reconnaissance confirms that enemy cargo vessels are transporting materiel vital to the enemy's continued resistance. Interdiction is required.", + "Logistical interdiction remains a top priority. A group of enemy cargo ships must be destroyed to limit their strategic reach." + }, + short = "Sink enemy cargo ships", + }, + conditions = { + difficultyMinimum = 0, + eras = {}, + }, + completionEvent = DCSEx.enums.taskEvent.DESTROY, + flags = { }, + minimumDistance = DCSEx.converter.nmToMeters(10.0), + safeRadius = 0, + surfaceType = land.SurfaceType.WATER, + targetCount = { 2, 2 }, + targetFamilies = { DCSEx.enums.unitFamily.SHIP_CARGO }, + waypointInaccuracy = DCSEx.converter.nmToMeters(5.0) +} diff --git a/Database/Tasks/AntiShipCorvette.lua b/Database/Tasks/AntiShipCorvette.lua new file mode 100644 index 0000000..623cc67 --- /dev/null +++ b/Database/Tasks/AntiShipCorvette.lua @@ -0,0 +1,26 @@ +Library.tasks.antiShipCorvette = { + taskFamily = DCSEx.enums.taskFamily.ANTISHIP, + description = + { + briefing = { + "An enemy missile boat was detected within strike range of our troops. Neutralize it before it can launch.", + "An enemy missile boat has entered our waters. You are authorized to engage and neutralize.", + "An enemy corvette is operating within weapons release range of our task force. Its engagement is necessary to preempt an attack.", + "An hostile craft possesses the capability to strike high-value naval assets. Its destruction is imperative to ensure fleet security.", + "The target is a missile-equipped enemy vessel posing a direct threat to allied maritime operations. It must be destroyed at once." + }, + short = "Sink enemy missile boat", + }, + conditions = { + difficultyMinimum = 0, + eras = {}, + }, + completionEvent = DCSEx.enums.taskEvent.DESTROY, + flags = { }, + minimumDistance = DCSEx.converter.nmToMeters(10.0), + safeRadius = 0, + surfaceType = land.SurfaceType.WATER, + targetCount = { 1, 1 }, + targetFamilies = { DCSEx.enums.unitFamily.SHIP_MISSILE_BOAT }, + waypointInaccuracy = DCSEx.converter.nmToMeters(5.0) +} diff --git a/Database/Tasks/GroundAttackAPC.lua b/Database/Tasks/GroundAttackAPC.lua new file mode 100644 index 0000000..d5a6eed --- /dev/null +++ b/Database/Tasks/GroundAttackAPC.lua @@ -0,0 +1,26 @@ +Library.tasks.groundAttackAPC = { + taskFamily = DCSEx.enums.taskFamily.GROUND_ATTACK, + description = + { + briefing = { + "Enemy APCs are moving to reinforce frontline positions—neutralizing them now will cripple their mobility and delay their counterattack.", + "Taking out these APCs disrupts enemy troop deployments and buys our ground forces critical time to consolidate gains.", + "Enemy armored personnel carriers are transporting infantry toward our exposed flank—engage immediately to prevent a breakthrough.", + "Our troops are under pressure—eliminating those APCs will ease the fight and reduce incoming fire on their position.", + "Recon confirms high-value targets in the APC convoy, including command elements—destroying them now will cause significant disruption." + }, + short = "Destroy enemy APCs", + }, + conditions = { + difficultyMinimum = 0, + eras = {}, + }, + completionEvent = DCSEx.enums.taskEvent.DESTROY, + flags = { DCSEx.enums.taskFlag.ALLOW_JTAC }, + minimumDistance = DCSEx.converter.nmToMeters(10.0), + safeRadius = 100, + surfaceType = land.SurfaceType.LAND, + targetCount = { 3, 4 }, + targetFamilies = { DCSEx.enums.unitFamily.GROUND_APC }, + waypointInaccuracy = DCSEx.converter.nmToMeters(2.5) +} diff --git a/Database/Tasks/GroundAttackArmorConvoy.lua b/Database/Tasks/GroundAttackArmorConvoy.lua new file mode 100644 index 0000000..7f691c3 --- /dev/null +++ b/Database/Tasks/GroundAttackArmorConvoy.lua @@ -0,0 +1,26 @@ +Library.tasks.groundAttackArmorConvoy = { + taskFamily = DCSEx.enums.taskFamily.GROUND_ATTACK, + description = + { + briefing = { + "An enemy armor column is moving toward our forward positions. Destroying it now will prevent a breakthrough and protect our ground forces.", + "An enemy armor column is advancing rapidly toward our main supply route. If we don't stop them here, they'll cut off our logistics.", + "You are to neutralize a key enemy armored unit before it reaches the battlefield. Hitting them now gives us the upper hand in the coming engagement.", + "An armor column is heading toward a populated area. Eliminate the threat before it reaches the city and endangers civilians.", + "This enemy armor column is the primary threat to our flank. Striking them now will stopp them from rolling through our defenses.", + }, + short = "Destroy enemy armor column", + }, + conditions = { + difficultyMinimum = 0, + eras = {}, + }, + completionEvent = DCSEx.enums.taskEvent.DESTROY, + flags = { DCSEx.enums.taskFlag.ALLOW_JTAC, DCSEx.enums.taskFlag.MOVING, DCSEx.enums.taskFlag.ON_ROADS }, + minimumDistance = DCSEx.converter.nmToMeters(10.0), + safeRadius = 100, + surfaceType = land.SurfaceType.LAND, + targetCount = { 3, 4 }, + targetFamilies = { DCSEx.enums.unitFamily.GROUND_APC, DCSEx.enums.unitFamily.GROUND_APC, DCSEx.enums.unitFamily.GROUND_MBT }, + waypointInaccuracy = DCSEx.converter.nmToMeters(2.5) +} diff --git a/Database/Tasks/GroundAttackArtillery.lua b/Database/Tasks/GroundAttackArtillery.lua new file mode 100644 index 0000000..5e50a49 --- /dev/null +++ b/Database/Tasks/GroundAttackArtillery.lua @@ -0,0 +1,26 @@ +Library.tasks.groundAttackArtillery = { + taskFamily = DCSEx.enums.taskFamily.GROUND_ATTACK, + description = + { + briefing = { + "Enemy artillery is shelling our forward positions and delaying our advance. Take them out to clear the way for ground troops.", + "Enemy artillery is hammering our lines and must be silenced immediately. Hit them hard—our soldiers are counting on it.", + "A group of artillery batteries is a key part of the enemy's defensive network. Neutralizing it will open a gap in their lines.", + "Our forces are pinned down by enemy artillery and taking losses. Eliminating the artillery threat will save lives and keep the momentum on our side.", + "High command has prioritized the destruction of enemy artillery to enable a broader offensive. This objective is critical." + }, + short = "Destroy enemy artillery", + }, + conditions = { + difficultyMinimum = 0, + eras = {}, + }, + completionEvent = DCSEx.enums.taskEvent.DESTROY, + flags = { DCSEx.enums.taskFlag.ALLOW_JTAC }, + minimumDistance = DCSEx.converter.nmToMeters(10.0), + safeRadius = 100, + surfaceType = land.SurfaceType.LAND, + targetCount = { 3, 4 }, + targetFamilies = { DCSEx.enums.unitFamily.GROUND_ARTILLERY }, + waypointInaccuracy = DCSEx.converter.nmToMeters(2.5) +} diff --git a/Database/Tasks/GroundAttackMBT.lua b/Database/Tasks/GroundAttackMBT.lua new file mode 100644 index 0000000..179f33a --- /dev/null +++ b/Database/Tasks/GroundAttackMBT.lua @@ -0,0 +1,26 @@ +Library.tasks.groundAttackMBT = { + taskFamily = DCSEx.enums.taskFamily.GROUND_ATTACK, + description = + { + briefing = { + "Enemy armor is advancing on our front lines. Neutralizing these tanks is critical to halting their momentum.", + "Intel confirms enemy tanks are threatening a key supply route. Strike now to preserve our logistics corridor.", + "Enemy tanks are the last major obstacle before our ground forces can break through. Hit them hard and clear the way.", + "An enemy armor column was spotted nearby. Eliminate it before they regroup and launch a counterattack.", + "Enemy tanks are guarding a location vital to the enemy's retreat. Destroy them to trap their forces and cut off escape." + }, + short = "Destroy enemy MBTs", + }, + conditions = { + difficultyMinimum = 0, + eras = {}, + }, + completionEvent = DCSEx.enums.taskEvent.DESTROY, + flags = { DCSEx.enums.taskFlag.ALLOW_JTAC }, + minimumDistance = DCSEx.converter.nmToMeters(10.0), + safeRadius = 100, + surfaceType = land.SurfaceType.LAND, + targetCount = { 2, 4 }, + targetFamilies = { DCSEx.enums.unitFamily.GROUND_MBT }, + waypointInaccuracy = DCSEx.converter.nmToMeters(2.5) +} diff --git a/Database/Tasks/GroundAttackSSMissiles.lua b/Database/Tasks/GroundAttackSSMissiles.lua new file mode 100644 index 0000000..dae5a59 --- /dev/null +++ b/Database/Tasks/GroundAttackSSMissiles.lua @@ -0,0 +1,26 @@ +Library.tasks.groundAttackSSMissiles = { + taskFamily = DCSEx.enums.taskFamily.GROUND_ATTACK, + description = + { + briefing = { + "Enemy missile launchers are targeting civilian and military infrastructure. Taking them out will cripple their long-range strike capability.", + "Mobile TEL units are prepping for launch - this is a time-critical strike to neutralize the threat before missiles are airborne.", + "Enemy missiles have already caused casualties among allied forces. Eliminating the launchers is essential for force protection.", + "Destroying enemy missile launchers will degrade enemy morale and disrupt their command structure - it's a high-value tactical win.", + "Missile launchers are being used to strike nearby towns. Your precision strike today will prevent further suffering and save lives.", + }, + short = "Destroy enemy SS missiles", + }, + conditions = { + difficultyMinimum = 0, + eras = {}, + }, + completionEvent = DCSEx.enums.taskEvent.DESTROY, + flags = { DCSEx.enums.taskFlag.ALLOW_JTAC }, + minimumDistance = DCSEx.converter.nmToMeters(10.0), + safeRadius = 100, + surfaceType = land.SurfaceType.LAND, + targetCount = { 3, 4 }, + targetFamilies = { DCSEx.enums.unitFamily.GROUND_SS_MISSILE }, + waypointInaccuracy = DCSEx.converter.nmToMeters(2.5) +} diff --git a/Database/Tasks/GroundAttackTrucksConvoy.lua b/Database/Tasks/GroundAttackTrucksConvoy.lua new file mode 100644 index 0000000..e3e48f3 --- /dev/null +++ b/Database/Tasks/GroundAttackTrucksConvoy.lua @@ -0,0 +1,26 @@ +Library.tasks.groundAttackTrucksConvoy = { + taskFamily = DCSEx.enums.taskFamily.GROUND_ATTACK, + description = + { + briefing = { + "Intelligence confirms that an enemy convoy is transporting essential logistical supplies. Engaging this target is critical to degrading their operational capabilities.", + "The designated convoy represents a significant threat to our frontline forces by sustaining enemy combat effectiveness; its neutralization is a priority.", + "An enemy convoy is facilitating the movement of vital materiel. A successful strike will disrupt their supply chain and impede their operational tempo.", + "The targeted convoy plays a pivotal role in enemy logistics. Effectively striking this formation will significantly impair their combat readiness.", + "A convoy of enemy trucks is integral to enemy sustainment efforts. Your strike will contribute significantly to operational success." + }, + short = "Destroy enemy truck convoy", + }, + conditions = { + difficultyMinimum = 0, + eras = {}, + }, + completionEvent = DCSEx.enums.taskEvent.DESTROY, + flags = { DCSEx.enums.taskFlag.ALLOW_JTAC, DCSEx.enums.taskFlag.MOVING, DCSEx.enums.taskFlag.ON_ROADS }, + minimumDistance = DCSEx.converter.nmToMeters(10.0), + safeRadius = 100, + surfaceType = land.SurfaceType.LAND, + targetCount = { 3, 4 }, + targetFamilies = { DCSEx.enums.unitFamily.GROUND_UNARMED }, + waypointInaccuracy = DCSEx.converter.nmToMeters(2.5) +} diff --git a/Database/Tasks/InterceptionAttack.lua b/Database/Tasks/InterceptionAttack.lua new file mode 100644 index 0000000..458f7d0 --- /dev/null +++ b/Database/Tasks/InterceptionAttack.lua @@ -0,0 +1,26 @@ +Library.tasks.interceptionTransport = { + taskFamily = DCSEx.enums.taskFamily.INTERCEPTION, + description = + { + briefing = { + "Enemy strike planes are closing on our ground forces. Intercept them before they tear our lines apart.", + "Hostile attack aircraft are en route to hit our armor columns. Engage and destroy before they reach the battlefield.", + "Enemy attack wings have crossed into our airspace, targeting supply convoys. Stop them or our front will collapse.", + "Strike aircraft are inbound to launch precision attacks on our positions. Your mission: intercept and neutralize immediately.", + "Enemy attack planes are preparing a low-level assault on our troops. Cut them off before they can deliver their payload." + }, + short = "Intercept enemy attack aircraft", + }, + conditions = { + difficultyMinimum = 0, + eras = {}, + }, + completionEvent = DCSEx.enums.taskEvent.DESTROY, + flags = { }, + minimumDistance = DCSEx.converter.nmToMeters(10.0), + safeRadius = 100, + surfaceType = nil, + targetCount = { 2, 2 }, + targetFamilies = { DCSEx.enums.unitFamily.PLANE_ATTACK }, + waypointInaccuracy = DCSEx.converter.nmToMeters(6.0) +} diff --git a/Database/Tasks/InterceptionBomber.lua b/Database/Tasks/InterceptionBomber.lua new file mode 100644 index 0000000..97ec12a --- /dev/null +++ b/Database/Tasks/InterceptionBomber.lua @@ -0,0 +1,26 @@ +Library.tasks.interceptionBomber = { + taskFamily = DCSEx.enums.taskFamily.INTERCEPTION, + description = + { + briefing = { + "Enemy bomber inbound. If it reaches its target, thousands of civilians are at risk. You're the last line to stop it before it crosses the border.", + "Intel confirms a hostile bomber on approach to critical infrastructure. Your mission: intercept and neutralize before it gets within strike range.", + "An enemy bomber is carrying high-yield ordnance toward a populated zone. Failure to intercept means catastrophic loss—scramble now.", + "Hostile bomber detected on vector to our forward base. Engage immediately; do not allow it to release payload.", + "Enemy bomber is on a direct course for our command center. Intercept at all costs—success ensures the survival of our forces." + }, + short = "Intercept enemy bomber", + }, + conditions = { + difficultyMinimum = 0, + eras = {}, + }, + completionEvent = DCSEx.enums.taskEvent.DESTROY, + flags = { }, + minimumDistance = DCSEx.converter.nmToMeters(10.0), + safeRadius = 100, + surfaceType = nil, + targetCount = { 1, 1 }, + targetFamilies = { DCSEx.enums.unitFamily.PLANE_BOMBER }, + waypointInaccuracy = DCSEx.converter.nmToMeters(8.0) +} diff --git a/Database/Tasks/InterceptionTransport.lua b/Database/Tasks/InterceptionTransport.lua new file mode 100644 index 0000000..fd686d3 --- /dev/null +++ b/Database/Tasks/InterceptionTransport.lua @@ -0,0 +1,26 @@ +Library.tasks.interceptionTransport = { + taskFamily = DCSEx.enums.taskFamily.INTERCEPTION, + description = + { + briefing = { + "Enemy transport is inbound with troops and supplies. Stop it before it reinforces their front line.", + "Hostile cargo aircraft is carrying critical equipment to enemy forces. Intercept and destroy before delivery.", + "Enemy transport plane detected. Intel suggests it's moving high-value assets. Do not let it reach its destination.", + "Transport aircraft is ferrying reinforcements across the border. Interception is the only way to halt their buildup.", + "Enemy logistics bird is airborne. Taking it out cripples their supply chain. Engage immediately." + }, + short = "Intercept enemy transport aircraft", + }, + conditions = { + difficultyMinimum = 0, + eras = {}, + }, + completionEvent = DCSEx.enums.taskEvent.DESTROY, + flags = { }, + minimumDistance = DCSEx.converter.nmToMeters(10.0), + safeRadius = 100, + surfaceType = nil, + targetCount = { 1, 1 }, + targetFamilies = { DCSEx.enums.unitFamily.PLANE_TRANSPORT }, + waypointInaccuracy = DCSEx.converter.nmToMeters(8.0) +} diff --git a/Database/Tasks/SEADLong.lua b/Database/Tasks/SEADLong.lua new file mode 100644 index 0000000..1cb0f6c --- /dev/null +++ b/Database/Tasks/SEADLong.lua @@ -0,0 +1,29 @@ +Library.tasks.seadLong = { + taskFamily = DCSEx.enums.taskFamily.SEAD, + description = + { + briefing = { + "Engagement of this enemy SAM site is essential to mitigate threats to our aircraft and maintain air superiority in the operational theater.", + "Neutralizing this hostile surface-to-air missile installation will significantly reduce risks to our flight operations and support ongoing ground maneuvers.", + "The elimination of this SAM site is imperative to ensure safe ingress and egress corridors for our air assets during the mission.", + "Suppressing this SAM site is a strategic priority to safeguard our air operations and maintain operational momentum.", + "This SAM installation represents a critical threat vector that must be neutralized to protect both aircrew and mission integrity.", + "Striking this enemy SAM site will preserve the freedom of maneuver necessary for successful execution of subsequent air operations.", + "Destroying this SAM site is vital to maintaining the tactical advantage and ensuring force protection throughout the engagement area.", + "The removal of this hostile SAM site is a prerequisite for sustained air dominance and mission accomplishment." + }, + short = "Destroy enemy SAM site", + }, + conditions = { + difficultyMinimum = 0, + eras = {}, + }, + completionEvent = DCSEx.enums.taskEvent.DESTROY, + flags = { DCSEx.enums.taskFlag.DESTROY_TRACK_RADARS_ONLY }, + minimumDistance = DCSEx.converter.nmToMeters(80.0), + safeRadius = 250, + surfaceType = land.SurfaceType.LAND, + targetCount = { 1, 1 }, + targetFamilies = { DCSEx.enums.unitFamily.AIRDEFENSE_SAM_LONG }, + waypointInaccuracy = 0.0 +} diff --git a/Database/Tasks/SEADMedium.lua b/Database/Tasks/SEADMedium.lua new file mode 100644 index 0000000..5462ec0 --- /dev/null +++ b/Database/Tasks/SEADMedium.lua @@ -0,0 +1,29 @@ +Library.tasks.seadMedium = { + taskFamily = DCSEx.enums.taskFamily.SEAD, + description = + { + briefing = { + "Engagement of this enemy SAM site is essential to mitigate threats to our aircraft and maintain air superiority in the operational theater.", + "Neutralizing this hostile surface-to-air missile installation will significantly reduce risks to our flight operations and support ongoing ground maneuvers.", + "The elimination of this SAM site is imperative to ensure safe ingress and egress corridors for our air assets during the mission.", + "Suppressing this SAM site is a strategic priority to safeguard our air operations and maintain operational momentum.", + "This SAM installation represents a critical threat vector that must be neutralized to protect both aircrew and mission integrity.", + "Striking this enemy SAM site will preserve the freedom of maneuver necessary for successful execution of subsequent air operations.", + "Destroying this SAM site is vital to maintaining the tactical advantage and ensuring force protection throughout the engagement area.", + "The removal of this hostile SAM site is a prerequisite for sustained air dominance and mission accomplishment." + }, + short = "Destroy enemy SAM site", + }, + conditions = { + difficultyMinimum = 0, + eras = {}, + }, + completionEvent = DCSEx.enums.taskEvent.DESTROY, + flags = { DCSEx.enums.taskFlag.DESTROY_TRACK_RADARS_ONLY }, + minimumDistance = DCSEx.converter.nmToMeters(40.0), + safeRadius = 250, + surfaceType = land.SurfaceType.LAND, + targetCount = { 1, 1 }, + targetFamilies = { DCSEx.enums.unitFamily.AIRDEFENSE_SAM_MEDIUM }, + waypointInaccuracy = 0.0 +} diff --git a/Database/Tasks/StrikeScenery.lua b/Database/Tasks/StrikeScenery.lua new file mode 100644 index 0000000..f1ae8ca --- /dev/null +++ b/Database/Tasks/StrikeScenery.lua @@ -0,0 +1,26 @@ +Library.tasks.strikeScenery = { + taskFamily = DCSEx.enums.taskFamily.STRIKE, + description = + { + briefing = { + "A civilian building is being used as an enemy command post. Take it out before they coordinate another strike.", + "Intel confirms hostile forces are operating from inside a civilian structure. Neutralize it to stop further attacks.", + "A civilian building has been repurposed as a weapons cache. Destroy it before those arms hit the front lines.", + "Enemy anti-air control is housed in a civilian facility. Eliminate it to open the skies for our forces.", + "Recon shows a civilian structure is shielding enemy leadership. Bringing it down cripples their chain of command." + }, + short = "Destroy occupied building", + }, + conditions = { + difficultyMinimum = 0, + eras = {}, + }, + completionEvent = DCSEx.enums.taskEvent.DESTROY, + flags = { DCSEx.enums.taskFlag.ALLOW_JTAC, DCSEx.enums.taskFlag.SCENERY_TARGET }, + minimumDistance = DCSEx.converter.nmToMeters(10.0), + safeRadius = 100, + surfaceType = land.SurfaceType.LAND, + targetCount = { 1, 1 }, + targetFamilies = { DCSEx.enums.unitFamily.STATIC_SCENERY }, + waypointInaccuracy = 0.0 +} diff --git a/Database/Tasks/StrikeStructure.lua b/Database/Tasks/StrikeStructure.lua new file mode 100644 index 0000000..492eb1e --- /dev/null +++ b/Database/Tasks/StrikeStructure.lua @@ -0,0 +1,28 @@ +Library.tasks.strikeStructure = { + taskFamily = DCSEx.enums.taskFamily.STRIKE, + description = + { + briefing = { + "Target structure is critical to enemy operations. Destroy it to cripple their plans.", + "That installation is a key asset. Eliminating it will shift the battle in our favor.", + "High-value structure confirmed in the target zone. Neutralize it before the enemy can react.", + "The objective is essential to enemy logistics. Take it out immediately.", + "Command wants that site erased. Its loss will deal a severe blow to enemy capability.", + "Structure identified as a primary objective. Destroy it and deny enemy use.", + "This is a priority strike target. Removing it will disrupt enemy momentum." + }, + short = "Destroy enemy structure", + }, + conditions = { + difficultyMinimum = 0, + eras = {}, + }, + completionEvent = DCSEx.enums.taskEvent.DESTROY, + flags = { DCSEx.enums.taskFlag.ALLOW_JTAC }, + minimumDistance = DCSEx.converter.nmToMeters(10.0), + safeRadius = 100, + surfaceType = land.SurfaceType.LAND, + targetCount = { 1, 1 }, + targetFamilies = { DCSEx.enums.unitFamily.STATIC_STRUCTURE }, + waypointInaccuracy = 0.0 +} diff --git a/Database/Templates/SAM HAWK.lua b/Database/Templates/SAM HAWK.lua new file mode 100644 index 0000000..07ca938 --- /dev/null +++ b/Database/Templates/SAM HAWK.lua @@ -0,0 +1,68 @@ +Library.groupTemplates["*HAWK"] = { + { + dx = 32.279602998366, + dy = -40.996889924631, + name = "Hawk sr", + heading = 0 + }, + { + dx = 0, + dy = 0, + name = "Hawk pcp", + heading = 0 + }, + { + dx = -34.076534003809, + dy = 41.931256096927, + name = "Hawk cwar", + heading = 0 + }, + { + dx = -73.507952767894, + dy = -58.130136438762, + name = "Hawk tr", + heading = 3.8397243543875 + }, + { + dx = -53.646780668367, + dy = -135.33353399258, + name = "Hawk ln", + heading = 4.7123889803847 + }, + { + dx = -137.08263885655, + dy = -113.8479052746, + name = "Hawk ln", + heading = 3.8397243543875 + }, + { + dx = -143.06468370894, + dy = -28.566666166706, + name = "Hawk ln", + heading = 2.6179938779915 + }, + { + dx = 71.695053443613, + dy = 58.889218891971, + name = "Hawk tr", + heading = 0.69813170079773 + }, + { + dx = 142.58862828931, + dy = 26.52486901707, + name = "Hawk ln", + heading = 5.7595865315813 + }, + { + dx = 139.59213643564, + dy = 111.20097058237, + name = "Hawk ln", + heading = 0.69813170079773 + }, + { + dx = 51.990893861743, + dy = 137.17585691391, + name = "Hawk ln", + heading = 1.5707963267949 + } +} diff --git a/Database/Templates/SAM NASAMS.lua b/Database/Templates/SAM NASAMS.lua new file mode 100644 index 0000000..ffe1b94 --- /dev/null +++ b/Database/Templates/SAM NASAMS.lua @@ -0,0 +1,44 @@ +Library.groupTemplates["*NASAMS"] = { + { + dx = 0, + dy = 0, + name = "NASAMS_Command_Post", + heading = 6.2765352930821 + }, + { + dx = -14.042657040991, + dy = 54.532318175887, + name = "NASAMS_LN_B", + heading = 2.3561944901923 + }, + { + dx = 51.255698199675, + dy = -5.1489742484409, + name = "NASAMS_LN_B", + heading = 6.2765352930821 + }, + { + dx = 29.255535502103, + dy = 55.936583879986, + name = "NASAMS_LN_C", + heading = 1.5707963267949 + }, + { + dx = 21.063985561486, + dy = 15.680967029068, + name = "NASAMS_Radar_MPQ64F1", + heading = 6.2765352930821 + }, + { + dx = -18.499094251951, + dy = -14.299637664692, + name = "M 818", + heading = 1.5707963267949 + }, + { + dx = -7.042956299847, + dy = -18.311686379602, + name = "M 818", + heading = 1.5707963267949 + } +} \ No newline at end of file diff --git a/Database/Templates/SAM Patriot.lua b/Database/Templates/SAM Patriot.lua new file mode 100644 index 0000000..b9e3f15 --- /dev/null +++ b/Database/Templates/SAM Patriot.lua @@ -0,0 +1,122 @@ +Library.groupTemplates["*Patriot"] = { + { + dx = 0, + dy = 0, + name = "Patriot str", + heading = 0 + }, + { + dx = -19.60004352557, + dy = 9.2644465620397, + name = "Patriot EPP", + heading = 0 + }, + { + dx = -35.248201985043, + dy = -0.8707412587828, + name = "Patriot ECS", + heading = 0 + }, + { + dx = -52.779034586449, + dy = -20.804649357509, + name = "Patriot AMG", + heading = 4.7123889803847 + }, + { + dx = -35.511640445024, + dy = 16.23725082296, + name = "Patriot cp", + heading = 0 + }, + { + dx = 136.71587389408, + dy = -115.36026572896, + name = "Patriot ln", + heading = 5.9341194567807 + }, + { + dx = 233.22284679177, + dy = -169.50440608741, + name = "Patriot ln", + heading = 5.9341194567807 + }, + { + dx = 302.6260036147, + dy = -119.57223135463, + name = "Patriot ln", + heading = 0 + }, + { + dx = 173.84730488189, + dy = -29.940192911235, + name = "Patriot ln", + heading = 0 + }, + { + dx = 287.4638047662, + dy = 8.9459295797569, + name = "Patriot ln", + heading = 0 + }, + { + dx = 101.78340133674, + dy = 47.897296695373, + name = "Patriot ln", + heading = 0.3490658503988 + }, + { + dx = 187.95200156058, + dy = 104.06103102239, + name = "Patriot ln", + heading = 0.3490658503988 + }, + { + dx = -31.67915746152, + dy = 171.21775733319, + name = "M1097 Avenger", + heading = 5.9341194567807 + }, + { + dx = 46.357670822879, + dy = -267.34404672193, + name = "M1097 Avenger", + heading = 0 + }, + { + dx = -68.129344288536, + dy = -146.06991632772, + name = "Hummer", + heading = 5.9341194567807 + }, + { + dx = -81.188374167279, + dy = -130.70164152705, + name = "Hummer", + heading = 5.9341194567807 + }, + { + dx = -95.083442423507, + dy = -114.72675693111, + name = "M978 HEMTT Tanker", + heading = 5.9341194567807 + }, + { + dx = -55.049316178425, + dy = -162.07189504143, + name = "M 818", + heading = 5.9341194567807 + }, + { + dx = 139.89969071567, + dy = -62.41809110227, + name = "M 818", + heading = 5.9341194567807 + }, + { + dx = -26.639011004663, + dy = 25.619801495515, + name = "M 818", + heading = 1.5707963267949 + } +} \ No newline at end of file diff --git a/Database/Templates/SAM Rapier.lua b/Database/Templates/SAM Rapier.lua new file mode 100644 index 0000000..313bead --- /dev/null +++ b/Database/Templates/SAM Rapier.lua @@ -0,0 +1,68 @@ +Library.groupTemplates["*Rapier"] = { + { + dx = 0, + dy = 0, + name = "rapier_fsa_blindfire_radar", + heading = 0.013157135472537 + }, + { + dx = -14.178539628003, + dy = 10.201632172, + name = "rapier_fsa_optical_tracker_unit", + heading = 3.1590459461097 + }, + { + dx = -66.224154607, + dy = 1.2103631389982, + name = "rapier_fsa_launcher", + heading = 0.017453292519943 + }, + { + dx = -50.662342819996, + dy = 58.961975773003, + name = "rapier_fsa_launcher", + heading = 5.7595865315813 + }, + { + dx = -54.812159296009, + dy = -56.368340474997, + name = "rapier_fsa_launcher", + heading = 0.80285145591739 + }, + { + dx = 49.050959151995, + dy = 45.906522844001, + name = "Land_Rover_101_FC", + heading = 4.1364303272266 + }, + { + dx = 36.182838894005, + dy = 58.774643101002, + name = "Land_Rover_109_S3", + heading = 4.1364303272266 + }, + { + dx = 32.038867964002, + dy = 61.828095365003, + name = "Land_Rover_109_S3", + heading = 4.1364303272266 + }, + { + dx = 22.442303705, + dy = 69.897933492997, + name = "Land_Rover_109_S3", + heading = 4.1364303272266 + }, + { + dx = 52.758722615996, + dy = 42.634966846002, + name = "Land_Rover_101_FC", + heading = 4.1364303272266 + }, + { + dx = 39.890602357991, + dy = 55.066879637001, + name = "Land_Rover_101_FC", + heading = 4.1364303272266 + } +} diff --git a/Database/Templates/SAM SA-10.lua b/Database/Templates/SAM SA-10.lua new file mode 100644 index 0000000..5a5ce8e --- /dev/null +++ b/Database/Templates/SAM SA-10.lua @@ -0,0 +1,110 @@ +Library.groupTemplates["*SA-10"] = { + { + dx = 0.69314285699511, + dy = 127.97571428004, + name = "S-300PS 40B6MD sr", + heading = 0 + }, + { + dx = -22.516027817794, + dy = 246.55467524601, + name = "S-300PS 64H6E sr", + heading = 3.1415926535898 + }, + { + dx = 0, + dy = 0, + name = "S-300PS 40B6M tr", + heading = 4.7123889803847 + }, + { + dx = 23.579234991223, + dy = 246.55467524601, + name = "S-300PS 54K6 cp", + heading = 3.1415926535898 + }, + { + dx = 83.349983285123, + dy = -1.3806866992963, + name = "S-300PS 5P85C ln", + heading = 3.1415926535898 + }, + { + dx = 82.498640577192, + dy = 16.104647497996, + name = "S-300PS 5P85D ln", + heading = 3.3161255787892 + }, + { + dx = 82.547616217693, + dy = -18.227276489837, + name = "S-300PS 5P85D ln", + heading = 2.9670597283904 + }, + { + dx = -82.640406328603, + dy = -0.41562629467808, + name = "S-300PS 5P85C ln", + heading = 0 + }, + { + dx = -81.939684967569, + dy = 17.115632734494, + name = "S-300PS 5P85D ln", + heading = 6.1086523819802 + }, + { + dx = -81.939684967569, + dy = -17.99454369233, + name = "S-300PS 5P85D ln", + heading = 0.17453292519943 + }, + { + dx = -9.0858776818495, + dy = 187.67713509151, + name = "generator_5i57", + heading = 1.5707963267949 + }, + { + dx = 0.83760223048739, + dy = 187.51811292395, + name = "generator_5i57", + heading = 1.5707963267949 + }, + { + dx = -59.823818980018, + dy = 168.63468487991, + name = "ATZ-5", + heading = 0 + }, + { + dx = -59.823818980018, + dy = 179.2654343833, + name = "ATZ-5", + heading = 0 + }, + { + dx = 20.947679329896, + dy = -62.811427216162, + name = "GAZ-66", + heading = 1.5707963267949 + }, + { + dx = 66.751355714747, + dy = 151.35592090525, + name = "ATZ-60_Maz", + heading = 3.9269908169872 + }, + { + dx = 59.63926918729, + dy = 158.46800743265, + name = "ATZ-60_Maz", + heading = 3.9269908169872 + }, + { + dx = -16.327227612433, + dy = -62.472874663305, + name = "KAMAZ Truck", + heading = 1.5707963267949 + } +} \ No newline at end of file diff --git a/Database/Templates/SAM SA-11.lua b/Database/Templates/SAM SA-11.lua new file mode 100644 index 0000000..b8853a4 --- /dev/null +++ b/Database/Templates/SAM SA-11.lua @@ -0,0 +1,74 @@ +Library.groupTemplates["*SA-11"] = { + { + dx = 0, + dy = 0, + name = "SA-11 Buk SR 9S18M1", + heading = 0 + }, + { + dx = 2.6563550499995, + dy = 100.877549925, + name = "SA-11 Buk LN 9A310M1", + heading = 4.7123889803847 + }, + { + dx = -3.9905785699957, + dy = -102.252741427, + name = "SA-11 Buk LN 9A310M1", + heading = 1.553343034275 + }, + { + dx = -105.82160159, + dy = 4.8958284879991, + name = "SA-11 Buk LN 9A310M1", + heading = 0.034906585039887 + }, + { + dx = 99.169831179999, + dy = -3.3463691979996, + name = "SA-11 Buk LN 9A310M1", + heading = 3.1415926535898 + }, + { + dx = 129.64229952999, + dy = -104.569064854, + name = "ATZ-10", + heading = 2.3212879051525 + }, + { + dx = 120.44134814, + dy = -112.000602515, + name = "ATZ-10", + heading = 2.4958208303519 + }, + { + dx = 27.63662248, + dy = 21.425097783998, + name = "ZiL-131 APA-80", + heading = 1.5882496193148 + }, + { + dx = 42.172491459991, + dy = -20.017069267, + name = "Ural-4320-31", + heading = 0.05235987755983 + }, + { + dx = 42.172491459991, + dy = -28.818736966998, + name = "Ural-4320-31", + heading = 0.034906585039887 + }, + { + dx = 22.603219309996, + dy = 21.425097783998, + name = "Ural-375 PBU", + heading = 1.5882496193148 + }, + { + dx = -13.972843719996, + dy = -17.667666831003, + name = "SA-11 Buk CC 9S470M1", + heading = 1.553343034275 + } +} \ No newline at end of file diff --git a/Database/Templates/SAM SA-12.lua b/Database/Templates/SAM SA-12.lua new file mode 100644 index 0000000..a05c3dc --- /dev/null +++ b/Database/Templates/SAM SA-12.lua @@ -0,0 +1,116 @@ +Library.groupTemplates["*SA-12"] = { + { + dx = -2.7678518560006, + dy = 65.945864259964, + name = "S-300V 9S19 sr", + heading = 0 + }, + { + dx = 30.036823454, + dy = 0.38328663993161, + name = "S-300V 9S15 sr", + heading = 0 + }, + { + dx = 0, + dy = 0, + name = "S-300V 9S457 cp", + heading = 0 + }, + { + dx = -4.7906990489992, + dy = 131.80077842996, + name = "S-300V 9S32 tr", + heading = 0 + }, + { + dx = -25.033639470001, + dy = 87.880609649932, + name = "S-300V 9A83 ln", + heading = 0 + }, + { + dx = -24.414217594, + dy = 73.324195559951, + name = "S-300V 9A83 ln", + heading = 0 + }, + { + dx = -27.062572916, + dy = 115.16459542001, + name = "S-300V 9A83 ln", + heading = 0 + }, + { + dx = -27.415392823001, + dy = 131.74713105999, + name = "S-300V 9A83 ln", + heading = 0 + }, + { + dx = 56.555745099, + dy = 43.894974149996, + name = "S-300V 9A83 ln", + heading = 0 + }, + { + dx = 56.202925191001, + dy = 62.947249140008, + name = "S-300V 9A83 ln", + heading = 0 + }, + { + dx = 54.791645562, + dy = 83.763623669976, + name = "S-300V 9A83 ln", + heading = 0 + }, + { + dx = 46.323967789, + dy = 115.87023522996, + name = "S-300V 9A83 ln", + heading = 0 + }, + { + dx = 45.971147881, + dy = 136.33378985, + name = "S-300V 9A83 ln", + heading = 0 + }, + { + dx = 43.854228438, + dy = 155.03324492997, + name = "S-300V 9A83 ln", + heading = 0 + }, + { + dx = -19.300534956999, + dy = 185.37575695, + name = "S-300V 9A83 ln", + heading = 0 + }, + { + dx = 1.8686594770006, + dy = 184.67011713993, + name = "S-300V 9A83 ln", + heading = 0 + }, + { + dx = 26.918872890999, + dy = 183.96447732998, + name = "S-300V 9A83 ln", + heading = 0 + }, + { + dx = -27.768212731, + dy = 148.68248660001, + name = "S-300V 9A83 ln", + heading = 0 + }, + { + dx = -24.685397394, + dy = 57.669850319973, + name = "S-300V 9A83 ln", + heading = 0 + } +} \ No newline at end of file diff --git a/Database/Templates/SAM SA-17.lua b/Database/Templates/SAM SA-17.lua new file mode 100644 index 0000000..157cbfd --- /dev/null +++ b/Database/Templates/SAM SA-17.lua @@ -0,0 +1,38 @@ +Library.groupTemplates["*SA-17"] = { + { + dx = 0, + dy = 0, + name = "SA-11 Buk SR 9S18M1", + heading = 0 + }, + { + dx = -2.65635505, + dy = -100.87754992, + name = "SA-17 Buk M1-2 LN 9A310M1-2", + heading = 4.7123889803847 + }, + { + dx = 3.9905785699998, + dy = 102.25274143007, + name = "SA-17 Buk M1-2 LN 9A310M1-2", + heading = 1.553343034275 + }, + { + dx = 105.82160159, + dy = -4.8958284899127, + name = "SA-17 Buk M1-2 LN 9A310M1-2", + heading = 0.034906585039887 + }, + { + dx = -99.16983118, + dy = 3.3463692000369, + name = "SA-17 Buk M1-2 LN 9A310M1-2", + heading = 3.1415926535898 + }, + { + dx = 13.97284372, + dy = 17.667666830006, + name = "SA-11 Buk CC 9S470M1", + heading = 1.553343034275 + } +} \ No newline at end of file diff --git a/Database/Templates/SAM SA-2.lua b/Database/Templates/SAM SA-2.lua new file mode 100644 index 0000000..4503c87 --- /dev/null +++ b/Database/Templates/SAM SA-2.lua @@ -0,0 +1,92 @@ +Library.groupTemplates["*SA-2"] = { + { + dx = -61.142498229994, + dy = 165.65584994, + name = "p-19 s-125 sr", + heading = 2.2165681500328 + }, + { + dx = 0, + dy = 0, + name = "SNR_75V", + heading = 0.0038885041518015 + }, + { + dx = 79.099061770001, + dy = -47.096697396999, + name = "S_75M_Volhov", + heading = 5.4803338512622 + }, + { + dx = 0.21820687000582, + dy = 106.800532487, + name = "S_75M_Volhov", + heading = 1.535889741755 + }, + { + dx = -1.8276942699886, + dy = -98.244225791997, + name = "S_75M_Volhov", + heading = 4.6774823953448 + }, + { + dx = -85.254996139993, + dy = -46.869375048002, + name = "S_75M_Volhov", + heading = 3.8048177693476 + }, + { + dx = -87.755541969993, + dy = 57.926227576005, + name = "S_75M_Volhov", + heading = 2.3561944901923 + }, + { + dx = 80.690318210007, + dy = 54.289070001003, + name = "S_75M_Volhov", + heading = 0.92502450355699 + }, + { + dx = 29.956672100001, + dy = 44.636165102005, + name = "ZIL-131 KUNG", + heading = 4.1713369122664 + }, + { + dx = 25.126949860001, + dy = 47.889142120999, + name = "ZIL-131 KUNG", + heading = 4.1713369122664 + }, + { + dx = -49.941045745189, + dy = 164.38476072691, + name = "Ural-4320 APA-5D", + heading = 0.68067840827779 + }, + { + dx = -118.28563314999, + dy = -171.162132111, + name = "ATMZ-5", + heading = 0.87266462599716 + }, + { + dx = -137.35942972999, + dy = -151.259040031, + name = "ATMZ-5", + heading = 1.0297442586767 + }, + { + dx = -168.04336334999, + dy = -84.915399762001, + name = "Ural-4320T", + heading = 5.4279739737024 + }, + { + dx = -151.45745328999, + dy = -70.817376204999, + name = "Ural-4320T", + heading = 5.3407075111026 + } +} diff --git a/Database/Templates/SAM SA-20B.lua b/Database/Templates/SAM SA-20B.lua new file mode 100644 index 0000000..89f06fc --- /dev/null +++ b/Database/Templates/SAM SA-20B.lua @@ -0,0 +1,116 @@ +Library.groupTemplates["*SA-20B"] = { + { + dx = 54.7553751285, + dy = 2.4498900099425, + name = "S-300PMU2 64H6E2 sr", + heading = 0 + }, + { + dx = 21.342219296499, + dy = 115.70961456001, + name = "S-300PMU1 40B6MD sr", + heading = 0 + }, + { + dx = 0, + dy = 0, + name = "S-300PMU2 54K6E2 cp", + heading = 0 + }, + { + dx = -26.435087682701, + dy = 298.86609738995, + name = "S-300PMU2 5P85SE2 ln", + heading = 0 + }, + { + dx = -27.534276526401, + dy = 263.14245996997, + name = "S-300PMU2 5P85SE2 ln", + heading = 0 + }, + { + dx = 1.044633408299, + dy = 343.93283998, + name = "S-300PMU2 5P85SE2 ln", + heading = 0 + }, + { + dx = 23.578004703, + dy = 343.38324555999, + name = "S-300PMU2 5P85SE2 ln", + heading = 0 + }, + { + dx = 74.6902859324, + dy = 295.01893644, + name = "S-300PMU2 5P85SE2 ln", + heading = 0 + }, + { + dx = 78.537446885099, + dy = 198.29031820002, + name = "S-300PMU2 5P85SE2 ln", + heading = 0 + }, + { + dx = 80.186230150599, + dy = 171.36019152997, + name = "S-300PMU2 5P85SE2 ln", + heading = 0 + }, + { + dx = 76.3390691978, + dy = 143.88047044002, + name = "S-300PMU2 5P85SE2 ln", + heading = 0 + }, + { + dx = 73.0415026669, + dy = 245.55543847999, + name = "S-300PMU2 5P85SE2 ln", + heading = 0 + }, + { + dx = 73.0415026669, + dy = 274.13434840995, + name = "S-300PMU2 5P85SE2 ln", + heading = 0 + }, + { + dx = 49.4089425286, + dy = 342.83365113998, + name = "S-300PMU2 5P85SE2 ln", + heading = 0 + }, + { + dx = -28.083870948201, + dy = 235.11314446002, + name = "S-300PMU2 5P85SE2 ln", + heading = 0 + }, + { + dx = -26.435087682701, + dy = 204.88545126002, + name = "S-300PMU2 5P85SE2 ln", + heading = 0 + }, + { + dx = -25.885493260901, + dy = 178.50491900998, + name = "S-300PMU2 5P85SE2 ln", + heading = 0 + }, + { + dx = -25.335898839101, + dy = 149.37641466002, + name = "S-300PMU2 5P85SE2 ln", + heading = 0 + }, + { + dx = 18.035892917, + dy = 247.96266972995, + name = "S-300PMU2 92H6E tr", + heading = 0 + } +} \ No newline at end of file diff --git a/Database/Templates/SAM SA-23.lua b/Database/Templates/SAM SA-23.lua new file mode 100644 index 0000000..b4a5e17 --- /dev/null +++ b/Database/Templates/SAM SA-23.lua @@ -0,0 +1,116 @@ +Library.groupTemplates["*SAM SA-23"] = { + { + dx = -1.2347728040004, + dy = 110.10662005993, + name = "S-300VM 9S19M2 sr", + heading = 0 + }, + { + dx = 70.4840989479, + dy = -0.34044244000688, + name = "S-300VM 9S15M2 sr", + heading = 0 + }, + { + dx = 0, + dy = 0, + name = "S-300VM 9S457ME cp", + heading = 0 + }, + { + dx = -5.5379051088003, + dy = 263.58500561002, + name = "S-300VM 9S32ME tr", + heading = 0 + }, + { + dx = -80.1369902307, + dy = 214.60380041995, + name = "S-300VM 9A83ME ln", + heading = 0 + }, + { + dx = -54.833154493501, + dy = 353.77489698003, + name = "S-300VM 9A83ME ln", + heading = 0 + }, + { + dx = -23.5045959615, + dy = 354.97984153999, + name = "S-300VM 9A83ME ln", + heading = 0 + }, + { + dx = 16.8610467625, + dy = 355.58231382002, + name = "S-300VM 9A83ME ln", + heading = 0 + }, + { + dx = 61.776132385099, + dy = 286.38841469004, + name = "S-300VM 9A83ME ln", + heading = 0 + }, + { + dx = 81.403816244499, + dy = 71.364295950043, + name = "S-300VM 9A83ME ln", + heading = 0 + }, + { + dx = 80.825083776699, + dy = 104.35204660997, + name = "S-300VM 9A83ME ln", + heading = 0 + }, + { + dx = 79.6676188412, + dy = 136.76106480998, + name = "S-300VM 9A83ME ln", + heading = 0 + }, + { + dx = 68.1271059684, + dy = 206.20737319998, + name = "S-300VM 9A83ME ln", + heading = 0 + }, + { + dx = 62.570004083, + dy = 245.10708640004, + name = "S-300VM 9A83ME ln", + heading = 0 + }, + { + dx = -88.5716021431, + dy = 277.26091749, + name = "S-300VM 9A83ME ln", + heading = 0 + }, + { + dx = -84.354296186901, + dy = 247.13730350998, + name = "S-300VM 9A83ME ln", + heading = 0 + }, + { + dx = -78.9320456717, + dy = 75.432703869999, + name = "S-300VM 9A83ME ln", + heading = 0 + }, + { + dx = -79.534517951201, + dy = 106.15879012004, + name = "S-300VM 9A83ME ln", + heading = 0 + }, + { + dx = -81.643089840001, + dy = 131.03149978002, + name = "S-300VM 9A83ME ln", + heading = 0 + } +} \ No newline at end of file diff --git a/Database/Templates/SAM SA-3.lua b/Database/Templates/SAM SA-3.lua new file mode 100644 index 0000000..4550650 --- /dev/null +++ b/Database/Templates/SAM SA-3.lua @@ -0,0 +1,74 @@ +Library.groupTemplates["*SA-3"] = { + { + dx = 33.753619990006, + dy = -73.286112753005, + name = "p-19 s-125 sr", + heading = 6.2641478001644 + }, + { + dx = 0, + dy = 0, + name = "snr s-125 tr", + heading = 6.2641478001644 + }, + { + dx = -54.395544479994, + dy = 20.411364487998, + name = "5p73 s-125 ln", + heading = 3.1590459461097 + }, + { + dx = -55.65154957, + dy = -15.115636602, + name = "5p73 s-125 ln", + heading = 3.1590459461097 + }, + { + dx = -26.404573919994, + dy = 38.533723629997, + name = "5p73 s-125 ln", + heading = 3.1590459461097 + }, + { + dx = -28.378296209994, + dy = -36.108864519003, + name = "5p73 s-125 ln", + heading = 3.1590459461097 + }, + { + dx = -0.19719014999282, + dy = 16.417675958997, + name = "ZIL-131 KUNG", + heading = 1.5707963267949 + }, + { + dx = 6.6801895600074, + dy = 13.694317328001, + name = "ZiL-131 APA-80", + heading = 3.1241393610699 + }, + { + dx = 73.148097120007, + dy = 24.177887045, + name = "GAZ-66", + heading = 1.6057029118348 + }, + { + dx = 67.422903450002, + dy = 24.220744209997, + name = "GAZ-66", + heading = 1.6406094968747 + }, + { + dx = 62.727714700013, + dy = 24.526952171, + name = "GAZ-66", + heading = 1.5707963267949 + }, + { + dx = 39.803062310006, + dy = -66.502451588, + name = "ZiL-131 APA-80", + heading = 1.6406094968747 + } +} \ No newline at end of file diff --git a/Database/Templates/SAM SA-5.lua b/Database/Templates/SAM SA-5.lua new file mode 100644 index 0000000..51a316e --- /dev/null +++ b/Database/Templates/SAM SA-5.lua @@ -0,0 +1,74 @@ +Library.groupTemplates["*SA-5"] = { + { + dx = -21.700644880009, + dy = 46.873392940033, + name = "p-19 s-125 sr", + heading = 0 + }, + { + dx = 0, + dy = 0, + name = "RPC_5N62V", + heading = 0 + }, + { + dx = -22.568670669978, + dy = -47.741418740014, + name = "RLS_19J6", + heading = 0 + }, + { + dx = -53.291560299986, + dy = -96.214918539976, + name = "S-200_Launcher", + heading = 0 + }, + { + dx = -50.615134440013, + dy = 96.785124419956, + name = "S-200_Launcher", + heading = 0 + }, + { + dx = 37.9898636, + dy = -128.44787637005, + name = "S-200_Launcher", + heading = 0 + }, + { + dx = 42.272167960007, + dy = 132.77268947999, + name = "S-200_Launcher", + heading = 0 + }, + { + dx = 138.76056035003, + dy = -94.454724150011, + name = "S-200_Launcher", + heading = 0 + }, + { + dx = 136.79007635004, + dy = 94.711739629973, + name = "S-200_Launcher", + heading = 0 + }, + { + dx = 46.961911459395, + dy = -62.291868640343, + name = "Ural-375", + heading = 5.4628805587423 + }, + { + dx = 46.463913819985, + dy = 64.697529679979, + name = "Ural-375", + heading = 0.78539816339745 + }, + { + dx = 137.66849846969, + dy = 0.55932146019768, + name = "2S6 Tunguska", + heading = 5.8643062867009 + } +} \ No newline at end of file diff --git a/Database/Templates/SAM SA-6.lua b/Database/Templates/SAM SA-6.lua new file mode 100644 index 0000000..1502f20 --- /dev/null +++ b/Database/Templates/SAM SA-6.lua @@ -0,0 +1,68 @@ +Library.groupTemplates["*SA-6"] = { + { + dx = 0, + dy = 0, + name = "Kub 1S91 str", + heading = 0 + }, + { + dx = 2.6563550499995, + dy = 100.877549925, + name = "Kub 2P25 ln", + heading = 4.7123889803847 + }, + { + dx = -3.9905785699957, + dy = -102.252741427, + name = "Kub 2P25 ln", + heading = 1.553343034275 + }, + { + dx = -105.82160159, + dy = 4.8958284879991, + name = "Kub 2P25 ln", + heading = 0.034906585039887 + }, + { + dx = 99.169831179999, + dy = -3.3463691979996, + name = "Kub 2P25 ln", + heading = 3.1415926535898 + }, + { + dx = 129.64229952999, + dy = -104.569064854, + name = "ATZ-10", + heading = 2.3212879051525 + }, + { + dx = 120.44134814, + dy = -112.000602515, + name = "ATZ-10", + heading = 2.4958208303519 + }, + { + dx = 25.605996860002, + dy = 20.019633917, + name = "ZiL-131 APA-80", + heading = 1.5882496193148 + }, + { + dx = 42.172491459991, + dy = -20.017069267, + name = "Ural-4320-31", + heading = 0.05235987755983 + }, + { + dx = 42.172491459991, + dy = -28.818736966998, + name = "Ural-4320-31", + heading = 0.034906585039887 + }, + { + dx = 22.038305519993, + dy = 20.113878987999, + name = "Ural-375 PBU", + heading = 1.5882496193148 + } +} \ No newline at end of file diff --git a/Make.bat b/Make.bat new file mode 100644 index 0000000..23675ae --- /dev/null +++ b/Make.bat @@ -0,0 +1,25 @@ +@echo off +cls + +@REM ------------------------------------------- +@REM DELETE PREVIOUSLY GENERATED MISSIONS +@REM ------------------------------------------- +if exist *.miz del *.miz + +@REM ------------------------------------------- +@REM CREATE SCENARIOS +@REM ------------------------------------------- +c:\php\php Make.php +echo. + +@REM ------------------------------------------- +@REM COPY OUTPUT MIZ FILES TO DCS'S MISSIONS DIRECTORY +@REM ------------------------------------------- +if not exist "%userprofile%\Saved Games\DCS\Missions\" goto EOF +if not exist *.miz goto EOF +echo Copying output MIZ files to %userprofile%\Saved Games\DCS\Missions... +copy /y *.miz "%userprofile%\Saved Games\DCS\Missions" +echo DONE +echo. + +:EOF diff --git a/Make.php b/Make.php new file mode 100644 index 0000000..0e25b0b --- /dev/null +++ b/Make.php @@ -0,0 +1,69 @@ +open($filename, ZipArchive::CREATE) !== true) + return false; + + $zip->addFromString("mission", createMissionTable($theaterJson)); + $zip->addFile("./Miz/Options.lua", "options"); + $zip->addFromString("theatre", $theaterJson["dcsID"]); + $zip->addFromString("warehouses", createWarehousesTable($theaterJson)); + $zip->addFromString("l10n/DEFAULT/dictionary", "dictionary = { }\n"); + $zip->addFromString("l10n/DEFAULT/Script.lua", createScript($theaterJson, $debugMode)); + + $resourceTable = "mapResource =\n{\n\t[\"Script.lua\"] = \"Script.lua\",\n"; + $mediaFiles = scandir("./Media"); + foreach ($mediaFiles as $mediaFile) { + if (!is_file("./Media/$mediaFile")) continue; + $zip->addFile("./Media/$mediaFile", "l10n/DEFAULT/$mediaFile"); + $resourceTable .= "\t[\"$mediaFile\"] = \"$mediaFile\",\n"; + } + $resourceTable .= "}\n"; + $zip->addFromString("l10n/DEFAULT/mapResource", $resourceTable); + + $zip->close(); + echo " Done!\n\n"; + return true; +} + +$theaterFiles = scandir("./Theaters"); +foreach ($theaterFiles as $theaterFile) +{ + if (($theaterFile === ".") || ($theaterFile === "..")) continue; + if (!str_ends_with(strtolower($theaterFile), ".json")) continue; + + $theaterJson = json_decode(file_get_contents("./Theaters/$theaterFile"), true); + if ($theaterJson == null) + { + echo "WARNING: Failed to open scenario \"".$theaterFile."\"...\n"; + continue; + } + + echo "Building scenario \"".$theaterFile."\"...\n"; + for ($i = 0; $i <= 1; $i++) + { + packMiz($theaterJson, $i == 1); + } + echo "\n"; +} + +?> diff --git a/Media/Pic-Medal1.png b/Media/Pic-Medal1.png new file mode 100644 index 0000000..c1c5171 Binary files /dev/null and b/Media/Pic-Medal1.png differ diff --git a/Media/Pic-Medal2.png b/Media/Pic-Medal2.png new file mode 100644 index 0000000..8557d2a Binary files /dev/null and b/Media/Pic-Medal2.png differ diff --git a/Media/Pic-Medal3.png b/Media/Pic-Medal3.png new file mode 100644 index 0000000..543d1a9 Binary files /dev/null and b/Media/Pic-Medal3.png differ diff --git a/Media/Pic-Medal4.png b/Media/Pic-Medal4.png new file mode 100644 index 0000000..1480598 Binary files /dev/null and b/Media/Pic-Medal4.png differ diff --git a/Media/Pic-Medal5.png b/Media/Pic-Medal5.png new file mode 100644 index 0000000..b82e416 Binary files /dev/null and b/Media/Pic-Medal5.png differ diff --git a/Media/Pic-Medal6.png b/Media/Pic-Medal6.png new file mode 100644 index 0000000..729e9ad Binary files /dev/null and b/Media/Pic-Medal6.png differ diff --git a/Media/Pic-Medal7.png b/Media/Pic-Medal7.png new file mode 100644 index 0000000..d31d748 Binary files /dev/null and b/Media/Pic-Medal7.png differ diff --git a/Media/Pic-MedalBox.png b/Media/Pic-MedalBox.png new file mode 100644 index 0000000..74eb995 Binary files /dev/null and b/Media/Pic-MedalBox.png differ diff --git a/Media/Pic-MedalW.png b/Media/Pic-MedalW.png new file mode 100644 index 0000000..b85cdea Binary files /dev/null and b/Media/Pic-MedalW.png differ diff --git a/Media/Pic-MissionAborted.png b/Media/Pic-MissionAborted.png new file mode 100644 index 0000000..208ec30 Binary files /dev/null and b/Media/Pic-MissionAborted.png differ diff --git a/Media/Pic-MissionComplete.png b/Media/Pic-MissionComplete.png new file mode 100644 index 0000000..acf8333 Binary files /dev/null and b/Media/Pic-MissionComplete.png differ diff --git a/Media/Pic-MissionFailed.png b/Media/Pic-MissionFailed.png new file mode 100644 index 0000000..dfa8db1 Binary files /dev/null and b/Media/Pic-MissionFailed.png differ diff --git a/Media/Pic-MissionStart.png b/Media/Pic-MissionStart.png new file mode 100644 index 0000000..4189ba0 Binary files /dev/null and b/Media/Pic-MissionStart.png differ diff --git a/Media/Pic-ObjectiveComplete.png b/Media/Pic-ObjectiveComplete.png new file mode 100644 index 0000000..06a1146 Binary files /dev/null and b/Media/Pic-ObjectiveComplete.png differ diff --git a/Media/Pic-Rank1.png b/Media/Pic-Rank1.png new file mode 100644 index 0000000..57f08e1 Binary files /dev/null and b/Media/Pic-Rank1.png differ diff --git a/Media/Pic-Rank2.png b/Media/Pic-Rank2.png new file mode 100644 index 0000000..f865f5e Binary files /dev/null and b/Media/Pic-Rank2.png differ diff --git a/Media/Pic-Rank3.png b/Media/Pic-Rank3.png new file mode 100644 index 0000000..0491419 Binary files /dev/null and b/Media/Pic-Rank3.png differ diff --git a/Media/Pic-Rank4.png b/Media/Pic-Rank4.png new file mode 100644 index 0000000..e9230c5 Binary files /dev/null and b/Media/Pic-Rank4.png differ diff --git a/Media/Pic-Rank5.png b/Media/Pic-Rank5.png new file mode 100644 index 0000000..d0cea9b Binary files /dev/null and b/Media/Pic-Rank5.png differ diff --git a/Media/Pic-Rank6.png b/Media/Pic-Rank6.png new file mode 100644 index 0000000..9d6b810 Binary files /dev/null and b/Media/Pic-Rank6.png differ diff --git a/Media/Pic-Ribbon1.png b/Media/Pic-Ribbon1.png new file mode 100644 index 0000000..8ba99f7 Binary files /dev/null and b/Media/Pic-Ribbon1.png differ diff --git a/Media/Pic-Ribbon10.png b/Media/Pic-Ribbon10.png new file mode 100644 index 0000000..86b8444 Binary files /dev/null and b/Media/Pic-Ribbon10.png differ diff --git a/Media/Pic-Ribbon11.png b/Media/Pic-Ribbon11.png new file mode 100644 index 0000000..bc1ab68 Binary files /dev/null and b/Media/Pic-Ribbon11.png differ diff --git a/Media/Pic-Ribbon12.png b/Media/Pic-Ribbon12.png new file mode 100644 index 0000000..3a5df1a Binary files /dev/null and b/Media/Pic-Ribbon12.png differ diff --git a/Media/Pic-Ribbon13.png b/Media/Pic-Ribbon13.png new file mode 100644 index 0000000..6c8d8e4 Binary files /dev/null and b/Media/Pic-Ribbon13.png differ diff --git a/Media/Pic-Ribbon14.png b/Media/Pic-Ribbon14.png new file mode 100644 index 0000000..05a0f59 Binary files /dev/null and b/Media/Pic-Ribbon14.png differ diff --git a/Media/Pic-Ribbon15.png b/Media/Pic-Ribbon15.png new file mode 100644 index 0000000..23aa96e Binary files /dev/null and b/Media/Pic-Ribbon15.png differ diff --git a/Media/Pic-Ribbon16.png b/Media/Pic-Ribbon16.png new file mode 100644 index 0000000..ac6873b Binary files /dev/null and b/Media/Pic-Ribbon16.png differ diff --git a/Media/Pic-Ribbon17.png b/Media/Pic-Ribbon17.png new file mode 100644 index 0000000..0f1f97a Binary files /dev/null and b/Media/Pic-Ribbon17.png differ diff --git a/Media/Pic-Ribbon18.png b/Media/Pic-Ribbon18.png new file mode 100644 index 0000000..6260c12 Binary files /dev/null and b/Media/Pic-Ribbon18.png differ diff --git a/Media/Pic-Ribbon19.png b/Media/Pic-Ribbon19.png new file mode 100644 index 0000000..7e7ebe5 Binary files /dev/null and b/Media/Pic-Ribbon19.png differ diff --git a/Media/Pic-Ribbon2.png b/Media/Pic-Ribbon2.png new file mode 100644 index 0000000..dfe816f Binary files /dev/null and b/Media/Pic-Ribbon2.png differ diff --git a/Media/Pic-Ribbon20.png b/Media/Pic-Ribbon20.png new file mode 100644 index 0000000..3c06c32 Binary files /dev/null and b/Media/Pic-Ribbon20.png differ diff --git a/Media/Pic-Ribbon21.png b/Media/Pic-Ribbon21.png new file mode 100644 index 0000000..2fcfa37 Binary files /dev/null and b/Media/Pic-Ribbon21.png differ diff --git a/Media/Pic-Ribbon22.png b/Media/Pic-Ribbon22.png new file mode 100644 index 0000000..9889104 Binary files /dev/null and b/Media/Pic-Ribbon22.png differ diff --git a/Media/Pic-Ribbon23.png b/Media/Pic-Ribbon23.png new file mode 100644 index 0000000..de9e4e7 Binary files /dev/null and b/Media/Pic-Ribbon23.png differ diff --git a/Media/Pic-Ribbon24.png b/Media/Pic-Ribbon24.png new file mode 100644 index 0000000..5096f98 Binary files /dev/null and b/Media/Pic-Ribbon24.png differ diff --git a/Media/Pic-Ribbon25.png b/Media/Pic-Ribbon25.png new file mode 100644 index 0000000..118217e Binary files /dev/null and b/Media/Pic-Ribbon25.png differ diff --git a/Media/Pic-Ribbon26.png b/Media/Pic-Ribbon26.png new file mode 100644 index 0000000..43588f3 Binary files /dev/null and b/Media/Pic-Ribbon26.png differ diff --git a/Media/Pic-Ribbon27.png b/Media/Pic-Ribbon27.png new file mode 100644 index 0000000..a1664e3 Binary files /dev/null and b/Media/Pic-Ribbon27.png differ diff --git a/Media/Pic-Ribbon28.png b/Media/Pic-Ribbon28.png new file mode 100644 index 0000000..fb91405 Binary files /dev/null and b/Media/Pic-Ribbon28.png differ diff --git a/Media/Pic-Ribbon29.png b/Media/Pic-Ribbon29.png new file mode 100644 index 0000000..ecdba31 Binary files /dev/null and b/Media/Pic-Ribbon29.png differ diff --git a/Media/Pic-Ribbon3.png b/Media/Pic-Ribbon3.png new file mode 100644 index 0000000..f685ca6 Binary files /dev/null and b/Media/Pic-Ribbon3.png differ diff --git a/Media/Pic-Ribbon30.png b/Media/Pic-Ribbon30.png new file mode 100644 index 0000000..fd2d93f Binary files /dev/null and b/Media/Pic-Ribbon30.png differ diff --git a/Media/Pic-Ribbon31.png b/Media/Pic-Ribbon31.png new file mode 100644 index 0000000..5964862 Binary files /dev/null and b/Media/Pic-Ribbon31.png differ diff --git a/Media/Pic-Ribbon32.png b/Media/Pic-Ribbon32.png new file mode 100644 index 0000000..9de67cf Binary files /dev/null and b/Media/Pic-Ribbon32.png differ diff --git a/Media/Pic-Ribbon33.png b/Media/Pic-Ribbon33.png new file mode 100644 index 0000000..102de7c Binary files /dev/null and b/Media/Pic-Ribbon33.png differ diff --git a/Media/Pic-Ribbon34.png b/Media/Pic-Ribbon34.png new file mode 100644 index 0000000..ec0c51f Binary files /dev/null and b/Media/Pic-Ribbon34.png differ diff --git a/Media/Pic-Ribbon35.png b/Media/Pic-Ribbon35.png new file mode 100644 index 0000000..a6e80ac Binary files /dev/null and b/Media/Pic-Ribbon35.png differ diff --git a/Media/Pic-Ribbon36.png b/Media/Pic-Ribbon36.png new file mode 100644 index 0000000..4489776 Binary files /dev/null and b/Media/Pic-Ribbon36.png differ diff --git a/Media/Pic-Ribbon37.png b/Media/Pic-Ribbon37.png new file mode 100644 index 0000000..d14d905 Binary files /dev/null and b/Media/Pic-Ribbon37.png differ diff --git a/Media/Pic-Ribbon38.png b/Media/Pic-Ribbon38.png new file mode 100644 index 0000000..6dac881 Binary files /dev/null and b/Media/Pic-Ribbon38.png differ diff --git a/Media/Pic-Ribbon39.png b/Media/Pic-Ribbon39.png new file mode 100644 index 0000000..b9e9c21 Binary files /dev/null and b/Media/Pic-Ribbon39.png differ diff --git a/Media/Pic-Ribbon4.png b/Media/Pic-Ribbon4.png new file mode 100644 index 0000000..100178b Binary files /dev/null and b/Media/Pic-Ribbon4.png differ diff --git a/Media/Pic-Ribbon40.png b/Media/Pic-Ribbon40.png new file mode 100644 index 0000000..478cc3e Binary files /dev/null and b/Media/Pic-Ribbon40.png differ diff --git a/Media/Pic-Ribbon5.png b/Media/Pic-Ribbon5.png new file mode 100644 index 0000000..0918250 Binary files /dev/null and b/Media/Pic-Ribbon5.png differ diff --git a/Media/Pic-Ribbon6.png b/Media/Pic-Ribbon6.png new file mode 100644 index 0000000..138964d Binary files /dev/null and b/Media/Pic-Ribbon6.png differ diff --git a/Media/Pic-Ribbon7.png b/Media/Pic-Ribbon7.png new file mode 100644 index 0000000..b4a72fd Binary files /dev/null and b/Media/Pic-Ribbon7.png differ diff --git a/Media/Pic-Ribbon8.png b/Media/Pic-Ribbon8.png new file mode 100644 index 0000000..ae47150 Binary files /dev/null and b/Media/Pic-Ribbon8.png differ diff --git a/Media/Pic-Ribbon9.png b/Media/Pic-Ribbon9.png new file mode 100644 index 0000000..fddb0b8 Binary files /dev/null and b/Media/Pic-Ribbon9.png differ diff --git a/Media/Radio-0.ogg b/Media/Radio-0.ogg new file mode 100644 index 0000000..461d5c8 Binary files /dev/null and b/Media/Radio-0.ogg differ diff --git a/Media/Radio-atcSafeLanding1.ogg b/Media/Radio-atcSafeLanding1.ogg new file mode 100644 index 0000000..cb99786 Binary files /dev/null and b/Media/Radio-atcSafeLanding1.ogg differ diff --git a/Media/Radio-atcSafeLanding2.ogg b/Media/Radio-atcSafeLanding2.ogg new file mode 100644 index 0000000..8985347 Binary files /dev/null and b/Media/Radio-atcSafeLanding2.ogg differ diff --git a/Media/Radio-atcSafeLanding3.ogg b/Media/Radio-atcSafeLanding3.ogg new file mode 100644 index 0000000..164c9ed Binary files /dev/null and b/Media/Radio-atcSafeLanding3.ogg differ diff --git a/Media/Radio-atcSafeLanding4.ogg b/Media/Radio-atcSafeLanding4.ogg new file mode 100644 index 0000000..63993d6 Binary files /dev/null and b/Media/Radio-atcSafeLanding4.ogg differ diff --git a/Media/Radio-atcSafeLanding5.ogg b/Media/Radio-atcSafeLanding5.ogg new file mode 100644 index 0000000..ccc1c2c Binary files /dev/null and b/Media/Radio-atcSafeLanding5.ogg differ diff --git a/Media/Radio-atcSafeLandingPlayer1.ogg b/Media/Radio-atcSafeLandingPlayer1.ogg new file mode 100644 index 0000000..435b886 Binary files /dev/null and b/Media/Radio-atcSafeLandingPlayer1.ogg differ diff --git a/Media/Radio-atcSafeLandingPlayer2.ogg b/Media/Radio-atcSafeLandingPlayer2.ogg new file mode 100644 index 0000000..b07bc24 Binary files /dev/null and b/Media/Radio-atcSafeLandingPlayer2.ogg differ diff --git a/Media/Radio-atcSafeLandingPlayer3.ogg b/Media/Radio-atcSafeLandingPlayer3.ogg new file mode 100644 index 0000000..9d4bd26 Binary files /dev/null and b/Media/Radio-atcSafeLandingPlayer3.ogg differ diff --git a/Media/Radio-atcSafeLandingPlayer4.ogg b/Media/Radio-atcSafeLandingPlayer4.ogg new file mode 100644 index 0000000..476658d Binary files /dev/null and b/Media/Radio-atcSafeLandingPlayer4.ogg differ diff --git a/Media/Radio-atcSafeLandingPlayer5.ogg b/Media/Radio-atcSafeLandingPlayer5.ogg new file mode 100644 index 0000000..361355b Binary files /dev/null and b/Media/Radio-atcSafeLandingPlayer5.ogg differ diff --git a/Media/Radio-awacsPicture1.ogg b/Media/Radio-awacsPicture1.ogg new file mode 100644 index 0000000..b9cf01d Binary files /dev/null and b/Media/Radio-awacsPicture1.ogg differ diff --git a/Media/Radio-awacsPicture2.ogg b/Media/Radio-awacsPicture2.ogg new file mode 100644 index 0000000..9a57666 Binary files /dev/null and b/Media/Radio-awacsPicture2.ogg differ diff --git a/Media/Radio-awacsPicture3.ogg b/Media/Radio-awacsPicture3.ogg new file mode 100644 index 0000000..d5e97f6 Binary files /dev/null and b/Media/Radio-awacsPicture3.ogg differ diff --git a/Media/Radio-awacsPicture4.ogg b/Media/Radio-awacsPicture4.ogg new file mode 100644 index 0000000..ba08863 Binary files /dev/null and b/Media/Radio-awacsPicture4.ogg differ diff --git a/Media/Radio-awacsPicture5.ogg b/Media/Radio-awacsPicture5.ogg new file mode 100644 index 0000000..2040b58 Binary files /dev/null and b/Media/Radio-awacsPicture5.ogg differ diff --git a/Media/Radio-awacsPictureClear1.ogg b/Media/Radio-awacsPictureClear1.ogg new file mode 100644 index 0000000..1841143 Binary files /dev/null and b/Media/Radio-awacsPictureClear1.ogg differ diff --git a/Media/Radio-awacsPictureClear2.ogg b/Media/Radio-awacsPictureClear2.ogg new file mode 100644 index 0000000..82eba33 Binary files /dev/null and b/Media/Radio-awacsPictureClear2.ogg differ diff --git a/Media/Radio-awacsPictureClear3.ogg b/Media/Radio-awacsPictureClear3.ogg new file mode 100644 index 0000000..272bf53 Binary files /dev/null and b/Media/Radio-awacsPictureClear3.ogg differ diff --git a/Media/Radio-awacsPictureClear4.ogg b/Media/Radio-awacsPictureClear4.ogg new file mode 100644 index 0000000..bf01977 Binary files /dev/null and b/Media/Radio-awacsPictureClear4.ogg differ diff --git a/Media/Radio-commandBlueOnBlue1.ogg b/Media/Radio-commandBlueOnBlue1.ogg new file mode 100644 index 0000000..0ec27ab Binary files /dev/null and b/Media/Radio-commandBlueOnBlue1.ogg differ diff --git a/Media/Radio-commandBlueOnBlue2.ogg b/Media/Radio-commandBlueOnBlue2.ogg new file mode 100644 index 0000000..a234e26 Binary files /dev/null and b/Media/Radio-commandBlueOnBlue2.ogg differ diff --git a/Media/Radio-commandBlueOnBlue3.ogg b/Media/Radio-commandBlueOnBlue3.ogg new file mode 100644 index 0000000..917d69d Binary files /dev/null and b/Media/Radio-commandBlueOnBlue3.ogg differ diff --git a/Media/Radio-commandBlueOnBlue4.ogg b/Media/Radio-commandBlueOnBlue4.ogg new file mode 100644 index 0000000..0b1b16c Binary files /dev/null and b/Media/Radio-commandBlueOnBlue4.ogg differ diff --git a/Media/Radio-commandBlueOnBlue5.ogg b/Media/Radio-commandBlueOnBlue5.ogg new file mode 100644 index 0000000..4311884 Binary files /dev/null and b/Media/Radio-commandBlueOnBlue5.ogg differ diff --git a/Media/Radio-commandFriendlyDown1.ogg b/Media/Radio-commandFriendlyDown1.ogg new file mode 100644 index 0000000..4bc0614 Binary files /dev/null and b/Media/Radio-commandFriendlyDown1.ogg differ diff --git a/Media/Radio-commandFriendlyDown2.ogg b/Media/Radio-commandFriendlyDown2.ogg new file mode 100644 index 0000000..39ef8e4 Binary files /dev/null and b/Media/Radio-commandFriendlyDown2.ogg differ diff --git a/Media/Radio-commandFriendlyDown3.ogg b/Media/Radio-commandFriendlyDown3.ogg new file mode 100644 index 0000000..cdddb19 Binary files /dev/null and b/Media/Radio-commandFriendlyDown3.ogg differ diff --git a/Media/Radio-commandFriendlyDown4.ogg b/Media/Radio-commandFriendlyDown4.ogg new file mode 100644 index 0000000..53cee28 Binary files /dev/null and b/Media/Radio-commandFriendlyDown4.ogg differ diff --git a/Media/Radio-commandFriendlyDown5.ogg b/Media/Radio-commandFriendlyDown5.ogg new file mode 100644 index 0000000..5cfcf28 Binary files /dev/null and b/Media/Radio-commandFriendlyDown5.ogg differ diff --git a/Media/Radio-commandFriendlyPilotOnGround.ogg b/Media/Radio-commandFriendlyPilotOnGround.ogg new file mode 100644 index 0000000..1736514 Binary files /dev/null and b/Media/Radio-commandFriendlyPilotOnGround.ogg differ diff --git a/Media/Radio-commandMissionComplete1.ogg b/Media/Radio-commandMissionComplete1.ogg new file mode 100644 index 0000000..a0d8786 Binary files /dev/null and b/Media/Radio-commandMissionComplete1.ogg differ diff --git a/Media/Radio-commandMissionComplete2.ogg b/Media/Radio-commandMissionComplete2.ogg new file mode 100644 index 0000000..4161be3 Binary files /dev/null and b/Media/Radio-commandMissionComplete2.ogg differ diff --git a/Media/Radio-commandMissionComplete3.ogg b/Media/Radio-commandMissionComplete3.ogg new file mode 100644 index 0000000..ff38ff6 Binary files /dev/null and b/Media/Radio-commandMissionComplete3.ogg differ diff --git a/Media/Radio-commandMissionComplete4.ogg b/Media/Radio-commandMissionComplete4.ogg new file mode 100644 index 0000000..16d62be Binary files /dev/null and b/Media/Radio-commandMissionComplete4.ogg differ diff --git a/Media/Radio-commandMissionComplete5.ogg b/Media/Radio-commandMissionComplete5.ogg new file mode 100644 index 0000000..9d4a369 Binary files /dev/null and b/Media/Radio-commandMissionComplete5.ogg differ diff --git a/Media/Radio-commandNewEnemyAircraft1.ogg b/Media/Radio-commandNewEnemyAircraft1.ogg new file mode 100644 index 0000000..40d26a7 Binary files /dev/null and b/Media/Radio-commandNewEnemyAircraft1.ogg differ diff --git a/Media/Radio-commandNewEnemyAircraft2.ogg b/Media/Radio-commandNewEnemyAircraft2.ogg new file mode 100644 index 0000000..268d6ad Binary files /dev/null and b/Media/Radio-commandNewEnemyAircraft2.ogg differ diff --git a/Media/Radio-commandNewEnemyAircraft3.ogg b/Media/Radio-commandNewEnemyAircraft3.ogg new file mode 100644 index 0000000..9cb9e0c Binary files /dev/null and b/Media/Radio-commandNewEnemyAircraft3.ogg differ diff --git a/Media/Radio-commandNewEnemyAircraft4.ogg b/Media/Radio-commandNewEnemyAircraft4.ogg new file mode 100644 index 0000000..4a08232 Binary files /dev/null and b/Media/Radio-commandNewEnemyAircraft4.ogg differ diff --git a/Media/Radio-commandNewEnemyAircraft5.ogg b/Media/Radio-commandNewEnemyAircraft5.ogg new file mode 100644 index 0000000..4603e22 Binary files /dev/null and b/Media/Radio-commandNewEnemyAircraft5.ogg differ diff --git a/Media/Radio-commandObjectiveComplete1.ogg b/Media/Radio-commandObjectiveComplete1.ogg new file mode 100644 index 0000000..5c6192d Binary files /dev/null and b/Media/Radio-commandObjectiveComplete1.ogg differ diff --git a/Media/Radio-commandObjectiveComplete2.ogg b/Media/Radio-commandObjectiveComplete2.ogg new file mode 100644 index 0000000..d7b8fe5 Binary files /dev/null and b/Media/Radio-commandObjectiveComplete2.ogg differ diff --git a/Media/Radio-commandObjectiveComplete3.ogg b/Media/Radio-commandObjectiveComplete3.ogg new file mode 100644 index 0000000..9d89942 Binary files /dev/null and b/Media/Radio-commandObjectiveComplete3.ogg differ diff --git a/Media/Radio-commandObjectiveComplete4.ogg b/Media/Radio-commandObjectiveComplete4.ogg new file mode 100644 index 0000000..90bde76 Binary files /dev/null and b/Media/Radio-commandObjectiveComplete4.ogg differ diff --git a/Media/Radio-commandObjectiveComplete5.ogg b/Media/Radio-commandObjectiveComplete5.ogg new file mode 100644 index 0000000..e9a8922 Binary files /dev/null and b/Media/Radio-commandObjectiveComplete5.ogg differ diff --git a/Media/Radio-commandObjectiveComplete6.ogg b/Media/Radio-commandObjectiveComplete6.ogg new file mode 100644 index 0000000..5d97664 Binary files /dev/null and b/Media/Radio-commandObjectiveComplete6.ogg differ diff --git a/Media/Radio-commandObjectiveCoordinates.ogg b/Media/Radio-commandObjectiveCoordinates.ogg new file mode 100644 index 0000000..76b5756 Binary files /dev/null and b/Media/Radio-commandObjectiveCoordinates.ogg differ diff --git a/Media/Radio-commandObjectiveCoordinatesPrecise.ogg b/Media/Radio-commandObjectiveCoordinatesPrecise.ogg new file mode 100644 index 0000000..419e02e Binary files /dev/null and b/Media/Radio-commandObjectiveCoordinatesPrecise.ogg differ diff --git a/Media/Radio-commandObjectivesManyLeft1.ogg b/Media/Radio-commandObjectivesManyLeft1.ogg new file mode 100644 index 0000000..5cce476 Binary files /dev/null and b/Media/Radio-commandObjectivesManyLeft1.ogg differ diff --git a/Media/Radio-commandObjectivesManyLeft2.ogg b/Media/Radio-commandObjectivesManyLeft2.ogg new file mode 100644 index 0000000..bb20fef Binary files /dev/null and b/Media/Radio-commandObjectivesManyLeft2.ogg differ diff --git a/Media/Radio-commandObjectivesManyLeft3.ogg b/Media/Radio-commandObjectivesManyLeft3.ogg new file mode 100644 index 0000000..0454d90 Binary files /dev/null and b/Media/Radio-commandObjectivesManyLeft3.ogg differ diff --git a/Media/Radio-commandObjectivesManyLeft4.ogg b/Media/Radio-commandObjectivesManyLeft4.ogg new file mode 100644 index 0000000..958972e Binary files /dev/null and b/Media/Radio-commandObjectivesManyLeft4.ogg differ diff --git a/Media/Radio-commandObjectivesManyLeft5.ogg b/Media/Radio-commandObjectivesManyLeft5.ogg new file mode 100644 index 0000000..28c461c Binary files /dev/null and b/Media/Radio-commandObjectivesManyLeft5.ogg differ diff --git a/Media/Radio-commandObjectivesManyLeft6.ogg b/Media/Radio-commandObjectivesManyLeft6.ogg new file mode 100644 index 0000000..236026c Binary files /dev/null and b/Media/Radio-commandObjectivesManyLeft6.ogg differ diff --git a/Media/Radio-commandObjectivesOneLeft1.ogg b/Media/Radio-commandObjectivesOneLeft1.ogg new file mode 100644 index 0000000..395c341 Binary files /dev/null and b/Media/Radio-commandObjectivesOneLeft1.ogg differ diff --git a/Media/Radio-commandObjectivesOneLeft2.ogg b/Media/Radio-commandObjectivesOneLeft2.ogg new file mode 100644 index 0000000..dc9b0c2 Binary files /dev/null and b/Media/Radio-commandObjectivesOneLeft2.ogg differ diff --git a/Media/Radio-commandObjectivesOneLeft3.ogg b/Media/Radio-commandObjectivesOneLeft3.ogg new file mode 100644 index 0000000..8729dfe Binary files /dev/null and b/Media/Radio-commandObjectivesOneLeft3.ogg differ diff --git a/Media/Radio-commandObjectivesOneLeft4.ogg b/Media/Radio-commandObjectivesOneLeft4.ogg new file mode 100644 index 0000000..71f972d Binary files /dev/null and b/Media/Radio-commandObjectivesOneLeft4.ogg differ diff --git a/Media/Radio-commandObjectivesOneLeft5.ogg b/Media/Radio-commandObjectivesOneLeft5.ogg new file mode 100644 index 0000000..ab2cd8b Binary files /dev/null and b/Media/Radio-commandObjectivesOneLeft5.ogg differ diff --git a/Media/Radio-commandObjectivesOneLeft6.ogg b/Media/Radio-commandObjectivesOneLeft6.ogg new file mode 100644 index 0000000..0d9089a Binary files /dev/null and b/Media/Radio-commandObjectivesOneLeft6.ogg differ diff --git a/Media/Radio-commandObjectivesOneLeft7.ogg b/Media/Radio-commandObjectivesOneLeft7.ogg new file mode 100644 index 0000000..4d4e26f Binary files /dev/null and b/Media/Radio-commandObjectivesOneLeft7.ogg differ diff --git a/Media/Radio-jtacSmokeAlreadyOut1.ogg b/Media/Radio-jtacSmokeAlreadyOut1.ogg new file mode 100644 index 0000000..e286550 Binary files /dev/null and b/Media/Radio-jtacSmokeAlreadyOut1.ogg differ diff --git a/Media/Radio-jtacSmokeAlreadyOut2.ogg b/Media/Radio-jtacSmokeAlreadyOut2.ogg new file mode 100644 index 0000000..e78d8cc Binary files /dev/null and b/Media/Radio-jtacSmokeAlreadyOut2.ogg differ diff --git a/Media/Radio-jtacSmokeAlreadyOut3.ogg b/Media/Radio-jtacSmokeAlreadyOut3.ogg new file mode 100644 index 0000000..deba837 Binary files /dev/null and b/Media/Radio-jtacSmokeAlreadyOut3.ogg differ diff --git a/Media/Radio-jtacSmokeAlreadyOut4.ogg b/Media/Radio-jtacSmokeAlreadyOut4.ogg new file mode 100644 index 0000000..7bc3c38 Binary files /dev/null and b/Media/Radio-jtacSmokeAlreadyOut4.ogg differ diff --git a/Media/Radio-jtacSmokeAlreadyOut5.ogg b/Media/Radio-jtacSmokeAlreadyOut5.ogg new file mode 100644 index 0000000..397d277 Binary files /dev/null and b/Media/Radio-jtacSmokeAlreadyOut5.ogg differ diff --git a/Media/Radio-jtacSmokeNoTarget1.ogg b/Media/Radio-jtacSmokeNoTarget1.ogg new file mode 100644 index 0000000..888d4b5 Binary files /dev/null and b/Media/Radio-jtacSmokeNoTarget1.ogg differ diff --git a/Media/Radio-jtacSmokeNoTarget2.ogg b/Media/Radio-jtacSmokeNoTarget2.ogg new file mode 100644 index 0000000..05caf0c Binary files /dev/null and b/Media/Radio-jtacSmokeNoTarget2.ogg differ diff --git a/Media/Radio-jtacSmokeNoTarget3.ogg b/Media/Radio-jtacSmokeNoTarget3.ogg new file mode 100644 index 0000000..b39e925 Binary files /dev/null and b/Media/Radio-jtacSmokeNoTarget3.ogg differ diff --git a/Media/Radio-jtacSmokeNoTarget4.ogg b/Media/Radio-jtacSmokeNoTarget4.ogg new file mode 100644 index 0000000..2e6c952 Binary files /dev/null and b/Media/Radio-jtacSmokeNoTarget4.ogg differ diff --git a/Media/Radio-jtacSmokeNoTarget5.ogg b/Media/Radio-jtacSmokeNoTarget5.ogg new file mode 100644 index 0000000..53483a6 Binary files /dev/null and b/Media/Radio-jtacSmokeNoTarget5.ogg differ diff --git a/Media/Radio-jtacSmokeOK1.ogg b/Media/Radio-jtacSmokeOK1.ogg new file mode 100644 index 0000000..6ccc72b Binary files /dev/null and b/Media/Radio-jtacSmokeOK1.ogg differ diff --git a/Media/Radio-jtacSmokeOK2.ogg b/Media/Radio-jtacSmokeOK2.ogg new file mode 100644 index 0000000..d058151 Binary files /dev/null and b/Media/Radio-jtacSmokeOK2.ogg differ diff --git a/Media/Radio-jtacSmokeOK3.ogg b/Media/Radio-jtacSmokeOK3.ogg new file mode 100644 index 0000000..bf53047 Binary files /dev/null and b/Media/Radio-jtacSmokeOK3.ogg differ diff --git a/Media/Radio-jtacSmokeOK4.ogg b/Media/Radio-jtacSmokeOK4.ogg new file mode 100644 index 0000000..89d66bb Binary files /dev/null and b/Media/Radio-jtacSmokeOK4.ogg differ diff --git a/Media/Radio-jtacSmokeOK5.ogg b/Media/Radio-jtacSmokeOK5.ogg new file mode 100644 index 0000000..f0ca282 Binary files /dev/null and b/Media/Radio-jtacSmokeOK5.ogg differ diff --git a/Media/Radio-pilotEjecting1.ogg b/Media/Radio-pilotEjecting1.ogg new file mode 100644 index 0000000..6852d29 Binary files /dev/null and b/Media/Radio-pilotEjecting1.ogg differ diff --git a/Media/Radio-pilotEjecting2.ogg b/Media/Radio-pilotEjecting2.ogg new file mode 100644 index 0000000..5a8f582 Binary files /dev/null and b/Media/Radio-pilotEjecting2.ogg differ diff --git a/Media/Radio-pilotEjecting3.ogg b/Media/Radio-pilotEjecting3.ogg new file mode 100644 index 0000000..fbc3eff Binary files /dev/null and b/Media/Radio-pilotEjecting3.ogg differ diff --git a/Media/Radio-pilotEjecting4.ogg b/Media/Radio-pilotEjecting4.ogg new file mode 100644 index 0000000..d6f7aa1 Binary files /dev/null and b/Media/Radio-pilotEjecting4.ogg differ diff --git a/Media/Radio-pilotEjecting5.ogg b/Media/Radio-pilotEjecting5.ogg new file mode 100644 index 0000000..9d01793 Binary files /dev/null and b/Media/Radio-pilotEjecting5.ogg differ diff --git a/Media/Radio-pilotImHit1.ogg b/Media/Radio-pilotImHit1.ogg new file mode 100644 index 0000000..19bc164 Binary files /dev/null and b/Media/Radio-pilotImHit1.ogg differ diff --git a/Media/Radio-pilotImHit2.ogg b/Media/Radio-pilotImHit2.ogg new file mode 100644 index 0000000..cb17184 Binary files /dev/null and b/Media/Radio-pilotImHit2.ogg differ diff --git a/Media/Radio-pilotImHit3.ogg b/Media/Radio-pilotImHit3.ogg new file mode 100644 index 0000000..3e784d0 Binary files /dev/null and b/Media/Radio-pilotImHit3.ogg differ diff --git a/Media/Radio-pilotImHit4.ogg b/Media/Radio-pilotImHit4.ogg new file mode 100644 index 0000000..f3d9552 Binary files /dev/null and b/Media/Radio-pilotImHit4.ogg differ diff --git a/Media/Radio-pilotImHit5.ogg b/Media/Radio-pilotImHit5.ogg new file mode 100644 index 0000000..4ae9d49 Binary files /dev/null and b/Media/Radio-pilotImHit5.ogg differ diff --git a/Media/Radio-pilotKillAir1.ogg b/Media/Radio-pilotKillAir1.ogg new file mode 100644 index 0000000..ef66252 Binary files /dev/null and b/Media/Radio-pilotKillAir1.ogg differ diff --git a/Media/Radio-pilotKillAir2.ogg b/Media/Radio-pilotKillAir2.ogg new file mode 100644 index 0000000..e11b3d3 Binary files /dev/null and b/Media/Radio-pilotKillAir2.ogg differ diff --git a/Media/Radio-pilotKillAir3.ogg b/Media/Radio-pilotKillAir3.ogg new file mode 100644 index 0000000..5b469ed Binary files /dev/null and b/Media/Radio-pilotKillAir3.ogg differ diff --git a/Media/Radio-pilotKillAir4.ogg b/Media/Radio-pilotKillAir4.ogg new file mode 100644 index 0000000..c4ebb74 Binary files /dev/null and b/Media/Radio-pilotKillAir4.ogg differ diff --git a/Media/Radio-pilotKillAir5.ogg b/Media/Radio-pilotKillAir5.ogg new file mode 100644 index 0000000..a4b229e Binary files /dev/null and b/Media/Radio-pilotKillAir5.ogg differ diff --git a/Media/Radio-pilotKillAir6.ogg b/Media/Radio-pilotKillAir6.ogg new file mode 100644 index 0000000..81a6b58 Binary files /dev/null and b/Media/Radio-pilotKillAir6.ogg differ diff --git a/Media/Radio-pilotKillAir7.ogg b/Media/Radio-pilotKillAir7.ogg new file mode 100644 index 0000000..6fd52bd Binary files /dev/null and b/Media/Radio-pilotKillAir7.ogg differ diff --git a/Media/Radio-pilotKillAir8.ogg b/Media/Radio-pilotKillAir8.ogg new file mode 100644 index 0000000..51857c1 Binary files /dev/null and b/Media/Radio-pilotKillAir8.ogg differ diff --git a/Media/Radio-pilotKillGround1.ogg b/Media/Radio-pilotKillGround1.ogg new file mode 100644 index 0000000..e8a936b Binary files /dev/null and b/Media/Radio-pilotKillGround1.ogg differ diff --git a/Media/Radio-pilotKillGround2.ogg b/Media/Radio-pilotKillGround2.ogg new file mode 100644 index 0000000..cc88e81 Binary files /dev/null and b/Media/Radio-pilotKillGround2.ogg differ diff --git a/Media/Radio-pilotKillGround3.ogg b/Media/Radio-pilotKillGround3.ogg new file mode 100644 index 0000000..8ec01da Binary files /dev/null and b/Media/Radio-pilotKillGround3.ogg differ diff --git a/Media/Radio-pilotKillGround4.ogg b/Media/Radio-pilotKillGround4.ogg new file mode 100644 index 0000000..ee610a4 Binary files /dev/null and b/Media/Radio-pilotKillGround4.ogg differ diff --git a/Media/Radio-pilotKillGround5.ogg b/Media/Radio-pilotKillGround5.ogg new file mode 100644 index 0000000..c93637e Binary files /dev/null and b/Media/Radio-pilotKillGround5.ogg differ diff --git a/Media/Radio-pilotKillGround6.ogg b/Media/Radio-pilotKillGround6.ogg new file mode 100644 index 0000000..d7e4da6 Binary files /dev/null and b/Media/Radio-pilotKillGround6.ogg differ diff --git a/Media/Radio-pilotKillGround7.ogg b/Media/Radio-pilotKillGround7.ogg new file mode 100644 index 0000000..2e7f8f5 Binary files /dev/null and b/Media/Radio-pilotKillGround7.ogg differ diff --git a/Media/Radio-pilotKillGround8.ogg b/Media/Radio-pilotKillGround8.ogg new file mode 100644 index 0000000..b7244be Binary files /dev/null and b/Media/Radio-pilotKillGround8.ogg differ diff --git a/Media/Radio-pilotKillGround9.ogg b/Media/Radio-pilotKillGround9.ogg new file mode 100644 index 0000000..9cf117e Binary files /dev/null and b/Media/Radio-pilotKillGround9.ogg differ diff --git a/Media/Radio-pilotKillShip1.ogg b/Media/Radio-pilotKillShip1.ogg new file mode 100644 index 0000000..cb6299f Binary files /dev/null and b/Media/Radio-pilotKillShip1.ogg differ diff --git a/Media/Radio-pilotKillShip2.ogg b/Media/Radio-pilotKillShip2.ogg new file mode 100644 index 0000000..43b5199 Binary files /dev/null and b/Media/Radio-pilotKillShip2.ogg differ diff --git a/Media/Radio-pilotKillShip3.ogg b/Media/Radio-pilotKillShip3.ogg new file mode 100644 index 0000000..da6d693 Binary files /dev/null and b/Media/Radio-pilotKillShip3.ogg differ diff --git a/Media/Radio-pilotKillShip4.ogg b/Media/Radio-pilotKillShip4.ogg new file mode 100644 index 0000000..47546a9 Binary files /dev/null and b/Media/Radio-pilotKillShip4.ogg differ diff --git a/Media/Radio-pilotKillShip5.ogg b/Media/Radio-pilotKillShip5.ogg new file mode 100644 index 0000000..d52a630 Binary files /dev/null and b/Media/Radio-pilotKillShip5.ogg differ diff --git a/Media/Radio-pilotKillShip6.ogg b/Media/Radio-pilotKillShip6.ogg new file mode 100644 index 0000000..165951e Binary files /dev/null and b/Media/Radio-pilotKillShip6.ogg differ diff --git a/Media/Radio-pilotKillShip7.ogg b/Media/Radio-pilotKillShip7.ogg new file mode 100644 index 0000000..1bacd9e Binary files /dev/null and b/Media/Radio-pilotKillShip7.ogg differ diff --git a/Media/Radio-pilotKillStrike1.ogg b/Media/Radio-pilotKillStrike1.ogg new file mode 100644 index 0000000..324275b Binary files /dev/null and b/Media/Radio-pilotKillStrike1.ogg differ diff --git a/Media/Radio-pilotKillStrike2.ogg b/Media/Radio-pilotKillStrike2.ogg new file mode 100644 index 0000000..7e332dc Binary files /dev/null and b/Media/Radio-pilotKillStrike2.ogg differ diff --git a/Media/Radio-pilotKillStrike3.ogg b/Media/Radio-pilotKillStrike3.ogg new file mode 100644 index 0000000..277f854 Binary files /dev/null and b/Media/Radio-pilotKillStrike3.ogg differ diff --git a/Media/Radio-pilotKillStrike4.ogg b/Media/Radio-pilotKillStrike4.ogg new file mode 100644 index 0000000..7ac6673 Binary files /dev/null and b/Media/Radio-pilotKillStrike4.ogg differ diff --git a/Media/Radio-pilotKillStrike5.ogg b/Media/Radio-pilotKillStrike5.ogg new file mode 100644 index 0000000..9615107 Binary files /dev/null and b/Media/Radio-pilotKillStrike5.ogg differ diff --git a/Media/Radio-pilotKillStrike6.ogg b/Media/Radio-pilotKillStrike6.ogg new file mode 100644 index 0000000..1d6d250 Binary files /dev/null and b/Media/Radio-pilotKillStrike6.ogg differ diff --git a/Media/Radio-pilotKillStrike7.ogg b/Media/Radio-pilotKillStrike7.ogg new file mode 100644 index 0000000..d403b15 Binary files /dev/null and b/Media/Radio-pilotKillStrike7.ogg differ diff --git a/Media/Radio-pilotKillStrike8.ogg b/Media/Radio-pilotKillStrike8.ogg new file mode 100644 index 0000000..2ae1b79 Binary files /dev/null and b/Media/Radio-pilotKillStrike8.ogg differ diff --git a/Media/Radio-pilotLaunchBruiser.ogg b/Media/Radio-pilotLaunchBruiser.ogg new file mode 100644 index 0000000..66e577c Binary files /dev/null and b/Media/Radio-pilotLaunchBruiser.ogg differ diff --git a/Media/Radio-pilotLaunchFox1.ogg b/Media/Radio-pilotLaunchFox1.ogg new file mode 100644 index 0000000..8902869 Binary files /dev/null and b/Media/Radio-pilotLaunchFox1.ogg differ diff --git a/Media/Radio-pilotLaunchFox2.ogg b/Media/Radio-pilotLaunchFox2.ogg new file mode 100644 index 0000000..0fbe632 Binary files /dev/null and b/Media/Radio-pilotLaunchFox2.ogg differ diff --git a/Media/Radio-pilotLaunchFox3.ogg b/Media/Radio-pilotLaunchFox3.ogg new file mode 100644 index 0000000..4b11334 Binary files /dev/null and b/Media/Radio-pilotLaunchFox3.ogg differ diff --git a/Media/Radio-pilotLaunchGuns1.ogg b/Media/Radio-pilotLaunchGuns1.ogg new file mode 100644 index 0000000..87c1bae Binary files /dev/null and b/Media/Radio-pilotLaunchGuns1.ogg differ diff --git a/Media/Radio-pilotLaunchGuns2.ogg b/Media/Radio-pilotLaunchGuns2.ogg new file mode 100644 index 0000000..5b756ae Binary files /dev/null and b/Media/Radio-pilotLaunchGuns2.ogg differ diff --git a/Media/Radio-pilotLaunchMagnum.ogg b/Media/Radio-pilotLaunchMagnum.ogg new file mode 100644 index 0000000..2fe566b Binary files /dev/null and b/Media/Radio-pilotLaunchMagnum.ogg differ diff --git a/Media/Radio-pilotLaunchMissile.ogg b/Media/Radio-pilotLaunchMissile.ogg new file mode 100644 index 0000000..b885013 Binary files /dev/null and b/Media/Radio-pilotLaunchMissile.ogg differ diff --git a/Media/Radio-pilotLaunchPickle1.ogg b/Media/Radio-pilotLaunchPickle1.ogg new file mode 100644 index 0000000..72f758e Binary files /dev/null and b/Media/Radio-pilotLaunchPickle1.ogg differ diff --git a/Media/Radio-pilotLaunchPickle2.ogg b/Media/Radio-pilotLaunchPickle2.ogg new file mode 100644 index 0000000..e2c8bd8 Binary files /dev/null and b/Media/Radio-pilotLaunchPickle2.ogg differ diff --git a/Media/Radio-pilotLaunchRifle.ogg b/Media/Radio-pilotLaunchRifle.ogg new file mode 100644 index 0000000..ec8b263 Binary files /dev/null and b/Media/Radio-pilotLaunchRifle.ogg differ diff --git a/Media/Radio-pilotLaunchRocket.ogg b/Media/Radio-pilotLaunchRocket.ogg new file mode 100644 index 0000000..e2090f1 Binary files /dev/null and b/Media/Radio-pilotLaunchRocket.ogg differ diff --git a/Media/Radio-pilotNewFriendlyAircraft1.ogg b/Media/Radio-pilotNewFriendlyAircraft1.ogg new file mode 100644 index 0000000..de1f66e Binary files /dev/null and b/Media/Radio-pilotNewFriendlyAircraft1.ogg differ diff --git a/Media/Radio-pilotNewFriendlyAircraft2.ogg b/Media/Radio-pilotNewFriendlyAircraft2.ogg new file mode 100644 index 0000000..a5cc31a Binary files /dev/null and b/Media/Radio-pilotNewFriendlyAircraft2.ogg differ diff --git a/Media/Radio-pilotNewFriendlyAircraft3.ogg b/Media/Radio-pilotNewFriendlyAircraft3.ogg new file mode 100644 index 0000000..68196f8 Binary files /dev/null and b/Media/Radio-pilotNewFriendlyAircraft3.ogg differ diff --git a/Media/Radio-pilotNewFriendlyAircraft4.ogg b/Media/Radio-pilotNewFriendlyAircraft4.ogg new file mode 100644 index 0000000..253840c Binary files /dev/null and b/Media/Radio-pilotNewFriendlyAircraft4.ogg differ diff --git a/Media/Radio-pilotNewFriendlyAircraft5.ogg b/Media/Radio-pilotNewFriendlyAircraft5.ogg new file mode 100644 index 0000000..fa226a7 Binary files /dev/null and b/Media/Radio-pilotNewFriendlyAircraft5.ogg differ diff --git a/Media/Radio-pilotWarningAAA1.ogg b/Media/Radio-pilotWarningAAA1.ogg new file mode 100644 index 0000000..64ef850 Binary files /dev/null and b/Media/Radio-pilotWarningAAA1.ogg differ diff --git a/Media/Radio-pilotWarningAAA2.ogg b/Media/Radio-pilotWarningAAA2.ogg new file mode 100644 index 0000000..3e16dab Binary files /dev/null and b/Media/Radio-pilotWarningAAA2.ogg differ diff --git a/Media/Radio-pilotWarningAAA3.ogg b/Media/Radio-pilotWarningAAA3.ogg new file mode 100644 index 0000000..af0a161 Binary files /dev/null and b/Media/Radio-pilotWarningAAA3.ogg differ diff --git a/Media/Radio-pilotWarningAAA4.ogg b/Media/Radio-pilotWarningAAA4.ogg new file mode 100644 index 0000000..c14b42d Binary files /dev/null and b/Media/Radio-pilotWarningAAA4.ogg differ diff --git a/Media/Radio-pilotWarningAAA5.ogg b/Media/Radio-pilotWarningAAA5.ogg new file mode 100644 index 0000000..35b0cd4 Binary files /dev/null and b/Media/Radio-pilotWarningAAA5.ogg differ diff --git a/Media/Radio-pilotWarningMANPADS1.ogg b/Media/Radio-pilotWarningMANPADS1.ogg new file mode 100644 index 0000000..3a76775 Binary files /dev/null and b/Media/Radio-pilotWarningMANPADS1.ogg differ diff --git a/Media/Radio-pilotWarningMANPADS2.ogg b/Media/Radio-pilotWarningMANPADS2.ogg new file mode 100644 index 0000000..18cfa1b Binary files /dev/null and b/Media/Radio-pilotWarningMANPADS2.ogg differ diff --git a/Media/Radio-pilotWarningMANPADS3.ogg b/Media/Radio-pilotWarningMANPADS3.ogg new file mode 100644 index 0000000..aa2749f Binary files /dev/null and b/Media/Radio-pilotWarningMANPADS3.ogg differ diff --git a/Media/Radio-pilotWarningMANPADS4.ogg b/Media/Radio-pilotWarningMANPADS4.ogg new file mode 100644 index 0000000..a4d0041 Binary files /dev/null and b/Media/Radio-pilotWarningMANPADS4.ogg differ diff --git a/Media/Radio-pilotWarningMANPADS5.ogg b/Media/Radio-pilotWarningMANPADS5.ogg new file mode 100644 index 0000000..573aaed Binary files /dev/null and b/Media/Radio-pilotWarningMANPADS5.ogg differ diff --git a/Media/Radio-pilotWarningSAMLaunch1.ogg b/Media/Radio-pilotWarningSAMLaunch1.ogg new file mode 100644 index 0000000..f86ca5a Binary files /dev/null and b/Media/Radio-pilotWarningSAMLaunch1.ogg differ diff --git a/Media/Radio-pilotWarningSAMLaunch2.ogg b/Media/Radio-pilotWarningSAMLaunch2.ogg new file mode 100644 index 0000000..18be052 Binary files /dev/null and b/Media/Radio-pilotWarningSAMLaunch2.ogg differ diff --git a/Media/Radio-pilotWarningSAMLaunch3.ogg b/Media/Radio-pilotWarningSAMLaunch3.ogg new file mode 100644 index 0000000..daea8d8 Binary files /dev/null and b/Media/Radio-pilotWarningSAMLaunch3.ogg differ diff --git a/Media/Radio-pilotWarningSAMLaunch4.ogg b/Media/Radio-pilotWarningSAMLaunch4.ogg new file mode 100644 index 0000000..5247484 Binary files /dev/null and b/Media/Radio-pilotWarningSAMLaunch4.ogg differ diff --git a/Media/Radio-pilotWarningSAMLaunch5.ogg b/Media/Radio-pilotWarningSAMLaunch5.ogg new file mode 100644 index 0000000..728637f Binary files /dev/null and b/Media/Radio-pilotWarningSAMLaunch5.ogg differ diff --git a/Media/Radio-playerAwacsBogeyDope1.ogg b/Media/Radio-playerAwacsBogeyDope1.ogg new file mode 100644 index 0000000..deab61f Binary files /dev/null and b/Media/Radio-playerAwacsBogeyDope1.ogg differ diff --git a/Media/Radio-playerAwacsBogeyDope2.ogg b/Media/Radio-playerAwacsBogeyDope2.ogg new file mode 100644 index 0000000..ec118f2 Binary files /dev/null and b/Media/Radio-playerAwacsBogeyDope2.ogg differ diff --git a/Media/Radio-playerAwacsPicture1.ogg b/Media/Radio-playerAwacsPicture1.ogg new file mode 100644 index 0000000..3cfac7a Binary files /dev/null and b/Media/Radio-playerAwacsPicture1.ogg differ diff --git a/Media/Radio-playerAwacsPicture2.ogg b/Media/Radio-playerAwacsPicture2.ogg new file mode 100644 index 0000000..6b2247f Binary files /dev/null and b/Media/Radio-playerAwacsPicture2.ogg differ diff --git a/Media/Radio-playerAwacsPicture3.ogg b/Media/Radio-playerAwacsPicture3.ogg new file mode 100644 index 0000000..9bc37ed Binary files /dev/null and b/Media/Radio-playerAwacsPicture3.ogg differ diff --git a/Media/Radio-playerAwacsPicture4.ogg b/Media/Radio-playerAwacsPicture4.ogg new file mode 100644 index 0000000..77520d2 Binary files /dev/null and b/Media/Radio-playerAwacsPicture4.ogg differ diff --git a/Media/Radio-playerAwacsPicture5.ogg b/Media/Radio-playerAwacsPicture5.ogg new file mode 100644 index 0000000..f6c418a Binary files /dev/null and b/Media/Radio-playerAwacsPicture5.ogg differ diff --git a/Media/Radio-playerCommandMissionStatus1.ogg b/Media/Radio-playerCommandMissionStatus1.ogg new file mode 100644 index 0000000..f765303 Binary files /dev/null and b/Media/Radio-playerCommandMissionStatus1.ogg differ diff --git a/Media/Radio-playerCommandMissionStatus2.ogg b/Media/Radio-playerCommandMissionStatus2.ogg new file mode 100644 index 0000000..a34be72 Binary files /dev/null and b/Media/Radio-playerCommandMissionStatus2.ogg differ diff --git a/Media/Radio-playerCommandMissionStatus3.ogg b/Media/Radio-playerCommandMissionStatus3.ogg new file mode 100644 index 0000000..4500da7 Binary files /dev/null and b/Media/Radio-playerCommandMissionStatus3.ogg differ diff --git a/Media/Radio-playerCommandMissionStatus4.ogg b/Media/Radio-playerCommandMissionStatus4.ogg new file mode 100644 index 0000000..15a78ba Binary files /dev/null and b/Media/Radio-playerCommandMissionStatus4.ogg differ diff --git a/Media/Radio-playerCommandMissionStatus5.ogg b/Media/Radio-playerCommandMissionStatus5.ogg new file mode 100644 index 0000000..1c2cb1a Binary files /dev/null and b/Media/Radio-playerCommandMissionStatus5.ogg differ diff --git a/Media/Radio-playerCommandMissionStatus6.ogg b/Media/Radio-playerCommandMissionStatus6.ogg new file mode 100644 index 0000000..69479c7 Binary files /dev/null and b/Media/Radio-playerCommandMissionStatus6.ogg differ diff --git a/Media/Radio-playerCommandRequireObjectives1.ogg b/Media/Radio-playerCommandRequireObjectives1.ogg new file mode 100644 index 0000000..440300f Binary files /dev/null and b/Media/Radio-playerCommandRequireObjectives1.ogg differ diff --git a/Media/Radio-playerCommandRequireObjectives2.ogg b/Media/Radio-playerCommandRequireObjectives2.ogg new file mode 100644 index 0000000..37b3691 Binary files /dev/null and b/Media/Radio-playerCommandRequireObjectives2.ogg differ diff --git a/Media/Radio-playerCommandRequireObjectives3.ogg b/Media/Radio-playerCommandRequireObjectives3.ogg new file mode 100644 index 0000000..44ccb15 Binary files /dev/null and b/Media/Radio-playerCommandRequireObjectives3.ogg differ diff --git a/Media/Radio-playerCommandRequireObjectives4.ogg b/Media/Radio-playerCommandRequireObjectives4.ogg new file mode 100644 index 0000000..da28096 Binary files /dev/null and b/Media/Radio-playerCommandRequireObjectives4.ogg differ diff --git a/Media/Radio-playerCommandRequireObjectives5.ogg b/Media/Radio-playerCommandRequireObjectives5.ogg new file mode 100644 index 0000000..76806f8 Binary files /dev/null and b/Media/Radio-playerCommandRequireObjectives5.ogg differ diff --git a/Media/Radio-playerJTACSmoke1.ogg b/Media/Radio-playerJTACSmoke1.ogg new file mode 100644 index 0000000..71657c9 Binary files /dev/null and b/Media/Radio-playerJTACSmoke1.ogg differ diff --git a/Media/Radio-playerJTACSmoke2.ogg b/Media/Radio-playerJTACSmoke2.ogg new file mode 100644 index 0000000..9409b2f Binary files /dev/null and b/Media/Radio-playerJTACSmoke2.ogg differ diff --git a/Media/Radio-playerJTACSmoke3.ogg b/Media/Radio-playerJTACSmoke3.ogg new file mode 100644 index 0000000..1748c3b Binary files /dev/null and b/Media/Radio-playerJTACSmoke3.ogg differ diff --git a/Media/Radio-playerJTACSmoke4.ogg b/Media/Radio-playerJTACSmoke4.ogg new file mode 100644 index 0000000..6b9d1f5 Binary files /dev/null and b/Media/Radio-playerJTACSmoke4.ogg differ diff --git a/Media/Radio-playerJTACSmoke5.ogg b/Media/Radio-playerJTACSmoke5.ogg new file mode 100644 index 0000000..646322c Binary files /dev/null and b/Media/Radio-playerJTACSmoke5.ogg differ diff --git a/Media/UI-Career.ogg b/Media/UI-Career.ogg new file mode 100644 index 0000000..c257874 Binary files /dev/null and b/Media/UI-Career.ogg differ diff --git a/Media/UI-Done.ogg b/Media/UI-Done.ogg new file mode 100644 index 0000000..f2e56fc Binary files /dev/null and b/Media/UI-Done.ogg differ diff --git a/Media/UI-Error.ogg b/Media/UI-Error.ogg new file mode 100644 index 0000000..07bf61f Binary files /dev/null and b/Media/UI-Error.ogg differ diff --git a/Media/UI-MissionEnd.ogg b/Media/UI-MissionEnd.ogg new file mode 100644 index 0000000..cfaa092 Binary files /dev/null and b/Media/UI-MissionEnd.ogg differ diff --git a/Media/UI-MissionStart.ogg b/Media/UI-MissionStart.ogg new file mode 100644 index 0000000..f964fa6 Binary files /dev/null and b/Media/UI-MissionStart.ogg differ diff --git a/Media/UI-Ok.ogg b/Media/UI-Ok.ogg new file mode 100644 index 0000000..9085cb0 Binary files /dev/null and b/Media/UI-Ok.ogg differ diff --git a/Miz/Mission.lua b/Miz/Mission.lua new file mode 100644 index 0000000..009a327 --- /dev/null +++ b/Miz/Mission.lua @@ -0,0 +1,459 @@ +mission = +{ + ["groundControl"] = + { + ["passwords"] = + { + ["artillery_commander"] = {}, + ["instructor"] = {}, + ["observer"] = {}, + ["forward_observer"] = {}, + }, + ["roles"] = + { + ["artillery_commander"] = + { + ["neutrals"] = 0, + ["blue"] = 0, + ["red"] = 0, + }, + ["instructor"] = + { + ["neutrals"] = 0, + ["blue"] = 0, + ["red"] = 0, + }, + ["observer"] = + { + ["neutrals"] = 0, + ["blue"] = 0, + ["red"] = 0, + }, + ["forward_observer"] = + { + ["neutrals"] = 0, + ["blue"] = 0, + ["red"] = 0, + }, + }, + ["isPilotControlVehicles"] = false, + }, + ["requiredModules"] = {}, + ["date"] = + { + ["Year"] = __DATE_YEAR__, + ["Day"] = __DATE_DAY__, + ["Month"] = __DATE_MONTH__, + }, + ["trig"] = + { + ["actions"] = + { + [1] = "a_do_script_file(\"Script.lua\"); mission.trig.events[\"mission start\"][1]=nil;", + }, + ["events"] = + { + ["mission start"] = + { + [1] = "if mission.trig.conditions[1]() then mission.trig.actions[1]() end", + }, + }, + ["custom"] = {}, + ["func"] = {}, + ["flag"] = + { + [1] = true, + }, + ["conditions"] = + { + [1] = "return(true)", + }, + ["customStartup"] = {}, + ["funcStartup"] = {}, + }, + ["maxDictId"] = 5, + ["result"] = + { + ["offline"] = + { + ["conditions"] = {}, + ["actions"] = {}, + ["func"] = {}, + }, + ["total"] = 0, + ["blue"] = + { + ["conditions"] = {}, + ["actions"] = {}, + ["func"] = {}, + }, + ["red"] = + { + ["conditions"] = {}, + ["actions"] = {}, + ["func"] = {}, + }, + }, + ["pictureFileNameN"] = {}, + ["descriptionNeutralsTask"] = "DictKey_descriptionNeutralsTask_4", + ["pictureFileNameServer"] = {}, + ["weather"] = + { + ["wind"] = + { + ["at8000"] = + { + ["speed"] = 0, + ["dir"] = 0, + }, + ["atGround"] = + { + ["speed"] = 0, + ["dir"] = 0, + }, + ["at2000"] = + { + ["speed"] = 0, + ["dir"] = 0, + }, + }, + ["enable_fog"] = false, + ["season"] = + { + ["temperature"] = __WEATHER_TEMPERATURE__, + }, + ["qnh"] = 760, + ["cyclones"] = {}, + ["dust_density"] = 0, + ["enable_dust"] = false, + ["clouds"] = + { + ["thickness"] = 200, + ["density"] = 0, + ["preset"] = "Preset2", + ["base"] = 2500, + ["iprecptns"] = 0, + }, + ["atmosphere_type"] = 0, + ["groundTurbulence"] = 0, + ["halo"] = + { + ["preset"] = "auto", + }, + ["type_weather"] = 0, + ["modifiedTime"] = false, + ["name"] = "Winter, clean sky", + ["fog"] = + { + ["visibility"] = 0, + ["thickness"] = 0, + }, + ["visibility"] = + { + ["distance"] = 80000, + }, + }, + ["theatre"] = "__THEATER__", + ["triggers"] = + { + ["zones"] = + { + __ZONES__ + }, + }, + ["map"] = + { + ["centerY"] = __MAP_CENTER_Y__, + ["zoom"] = __MAP_ZOOM__, + ["centerX"] = __MAP_CENTER_X__, + }, + ["coalitions"] = + { + ["blue"] = + { + [1] = 80, + }, + ["neutrals"] = + { + [1] = 18, + [2] = 91, + [3] = 70, + [4] = 83, + [5] = 21, + [6] = 23, + [7] = 65, + [8] = 24, + [9] = 11, + [10] = 86, + [11] = 64, + [12] = 25, + [13] = 8, + [14] = 63, + [15] = 27, + [16] = 28, + [17] = 76, + [18] = 84, + [19] = 26, + [20] = 13, + [21] = 90, + [22] = 29, + [23] = 62, + [24] = 30, + [25] = 5, + [26] = 78, + [27] = 16, + [28] = 6, + [29] = 87, + [30] = 31, + [31] = 61, + [32] = 32, + [33] = 33, + [34] = 60, + [35] = 17, + [36] = 34, + [37] = 35, + [38] = 15, + [39] = 69, + [40] = 20, + [41] = 36, + [42] = 59, + [43] = 37, + [44] = 71, + [45] = 79, + [46] = 58, + [47] = 57, + [48] = 56, + [49] = 55, + [50] = 92, + [51] = 88, + [52] = 38, + [53] = 12, + [54] = 73, + [55] = 39, + [56] = 89, + [57] = 54, + [58] = 40, + [59] = 77, + [60] = 72, + [61] = 41, + [62] = 0, + [63] = 42, + [64] = 43, + [65] = 44, + [66] = 85, + [67] = 75, + [68] = 45, + [69] = 19, + [70] = 9, + [71] = 53, + [72] = 46, + [73] = 22, + [74] = 47, + [75] = 52, + [76] = 10, + [77] = 66, + [78] = 51, + [79] = 3, + [80] = 4, + [81] = 1, + [82] = 74, + [83] = 82, + [84] = 2, + [85] = 7, + [86] = 68, + [87] = 50, + [88] = 49, + [89] = 48, + [90] = 67, + }, + ["red"] = + { + [1] = 81, + }, + }, + ["descriptionText"] = "__MISSION_DESCRIPTION__", + ["pictureFileNameR"] = {}, + ["descriptionBlueTask"] = "__MISSION_BRIEFING__", + ["goals"] = {}, + ["descriptionRedTask"] = "__MISSION_BRIEFING__", + ["pictureFileNameB"] = {}, + ["coalition"] = + { + ["blue"] = + { + ["bullseye"] = + { + ["y"] = __BULLSEYE_BLUE_Y__, + ["x"] = __BULLSEYE_BLUE_X__, + }, + ["nav_points"] = {}, + ["name"] = "blue", + ["country"] = + { + [1] = + { + ["id"] = 80, + ["name"] = "CJTF Blue", + ["plane"] = + { + ["group"] = + { + [1] = + { + ["dynSpawnTemplate"] = false, + ["modulation"] = 0, + ["tasks"] = {}, + ["task"] = "Nothing", + ["uncontrolled"] = false, + ["taskSelected"] = true, + ["route"] = + { + ["points"] = + { + [1] = + { + ["alt"] = 22, + ["action"] = "From Runway", + ["alt_type"] = "BARO", + ["speed"] = 138.88888888889, + ["task"] = + { + ["id"] = "ComboTask", + ["params"] = + { + ["tasks"] = + { + [1] = + { + ["enabled"] = true, + ["auto"] = true, + ["id"] = "WrappedAction", + ["number"] = 1, + ["params"] = + { + ["action"] = + { + ["id"] = "Option", + ["params"] = + { + ["value"] = true, + ["name"] = 35, + }, + }, + }, + }, + }, + }, + }, + ["type"] = "TakeOff", + ["ETA"] = 0, + ["ETA_locked"] = true, + ["y"] = __PLAYER_Y__, + ["x"] = __PLAYER_X__, + ["speed_locked"] = true, + ["formation_template"] = "", + ["airdromeId"] = __PLAYER_AIRDROME_ID__, + }, + }, + }, + ["groupId"] = 1, + ["hidden"] = false, + ["units"] = + { + [1] = + { + ["alt"] = 22, + ["alt_type"] = "BARO", + ["livery_id"] = "default", + ["skill"] = "Player", + ["speed"] = 138.88888888889, + ["type"] = "Su-25T", + ["unitId"] = 1, + ["psi"] = 0, + ["onboard_num"] = "010", + ["y"] = -177.707709, + ["x"] = -219.726892, + ["name"] = "Player-1", + ["payload"] = + { + ["pylons"] = {}, + ["fuel"] = "3790", + ["flare"] = 128, + ["chaff"] = 128, + ["gun"] = 100, + }, + ["heading"] = 0, + ["callsign"] = + { + [1] = 2, + [2] = 1, + [3] = 1, + ["name"] = "Springfield11", + }, + }, + }, + ["y"] = __PLAYER_Y__, + ["x"] = __PLAYER_X__, + ["name"] = "Player", + ["communication"] = true, + ["start_time"] = 0, + ["frequency"] = 124, + }, + }, + }, + }, + }, + }, + ["neutrals"] = + { + ["bullseye"] = + { + ["y"] = __MAP_CENTER_Y__, + ["x"] = __MAP_CENTER_X__, + }, + ["nav_points"] = {}, + ["name"] = "neutrals", + ["country"] = {}, + }, + ["red"] = + { + ["bullseye"] = + { + ["y"] = __BULLSEYE_RED_Y__, + ["x"] = __BULLSEYE_RED_X__, + }, + ["nav_points"] = {}, + ["name"] = "red", + ["country"] = {}, + }, + }, + ["sortie"] = "DictKey_sortie_5", + ["version"] = 23, + ["trigrules"] = + { + [1] = + { + ["rules"] = {}, + ["comment"] = "RunMissionScript", + ["eventlist"] = "mission start", + ["actions"] = + { + [1] = + { + ["predicate"] = "a_do_script_file", + ["file"] = "Script.lua", + }, + }, + ["predicate"] = "triggerOnce", + }, + }, + ["currentKey"] = 38, + ["failures"] = {}, + ["forcedOptions"] = + { + ["userMarks"] = true, + ["RBDAI"] = false, + ["unrestrictedSATNAV"] = true, + }, + ["start_time"] = 28800, +} diff --git a/Miz/Options.lua b/Miz/Options.lua new file mode 100644 index 0000000..4431a33 --- /dev/null +++ b/Miz/Options.lua @@ -0,0 +1,460 @@ +options = +{ + ["miscellaneous"] = + { + ["chat_window_at_start"] = true, + ["TrackIR_external_views"] = true, + ["f11_free_camera"] = true, + ["f10_awacs"] = true, + ["Coordinate_Display"] = "Lat Long", + ["accidental_failures"] = false, + ["autologin"] = true, + ["show_pilot_body"] = false, + ["collect_stat"] = false, + ["synchronize_controls"] = false, + ["backup"] = false, + ["headmove"] = false, + ["f5_nearest_ac"] = true, + ["F2_view_effects"] = 1, + ["launcher"] = false, + ["backupTime"] = 5, + ["allow_server_screenshots"] = false, + ["force_feedback_enabled"] = true, + }, -- end of ["miscellaneous"] + ["difficulty"] = + { + ["fuel"] = false, + ["spottingDot"] = 2, + ["miniHUD"] = false, + ["optionsView"] = "optview_all", + ["setGlobal"] = true, + ["avionicsLanguage"] = "native", + ["cockpitVisualRM"] = false, + ["map"] = true, + ["gWarmUp"] = false, + ["spectatorExternalViews"] = true, + ["userSnapView"] = true, + ["iconsTheme"] = "nato", + ["weapons"] = false, + ["padlock"] = false, + ["birds"] = 0, + ["permitCrash"] = true, + ["immortal"] = false, + ["easyCommunication"] = true, + ["wakeTurbulence"] = false, + ["easyFlight"] = false, + ["hideStick"] = false, + ["radio"] = false, + ["geffect"] = "realistic", + ["unrestrictedSATNAV"] = true, + ["reports"] = true, + ["tips"] = true, + ["cockpitStatusBarAllowed"] = false, + ["autoTrimmer"] = false, + ["externalViews"] = true, + ["RBDAI"] = true, + ["controlsIndicator"] = true, + ["units"] = "imperial", + ["userMarks"] = true, + ["labels"] = 0, + }, -- end of ["difficulty"] + ["VR"] = + { + ["handOffsetZ"] = 0, + ["enableHMDMask"] = true, + ["box_mouse_cursor"] = true, + ["handOffsetY"] = 0, + ["mirror_crop"] = false, + ["pointer_use_type"] = 0, + ["mirror_use_DCS_resolution"] = false, + ["openxr_eyeGaze"] = false, + ["hand_controllers_use_throttle"] = true, + ["hand_controllers_joint_throttle"] = true, + ["use_mouse"] = true, + ["openxr_quadView"] = false, + ["custom_IPD_enable"] = false, + ["hand_controllers_type"] = 0, + ["hand_controllers_debug_draw"] = false, + ["handOffsetX"] = 0, + ["prefer_built_in_audio"] = true, + ["pixel_density"] = 1, + ["hand_controllers_use_stick"] = true, + ["msaaMaskSize"] = 0.42, + ["enable"] = false, + ["interaction_with_grip_only"] = false, + ["bloom"] = true, + ["mirror_source"] = 0, + ["custom_IPD"] = 63.5, + ["hand_controllers"] = true, + }, -- end of ["VR"] + ["graphics"] = + { + ["volumetricLights"] = 1, + ["AA"] = "DLAA", + ["forestDetailsFactor"] = 1, + ["rainDroplets"] = 0, + ["LensEffects"] = 0, + ["heatBlr"] = 1, + ["anisotropy"] = 1, + ["water"] = 2, + ["motionBlurAmount"] = 1, + ["scaleGui"] = 1, + ["visibRange"] = "High", + ["aspect"] = 1.7777777777778, + ["lights"] = 2, + ["shadows"] = 4, + ["MSAA"] = 1, + ["canopyReflections"] = 1, + ["clutterMaxDistance"] = 1000, + ["textures"] = 2, + ["DLSS_PerfQuality"] = 2, + ["outputGamma"] = 2.2, + ["forestDistanceFactor"] = 1, + ["BlurFlatShadows"] = 1, + ["cockpitGI"] = 1, + ["terrainTextures"] = "max", + ["box_mouse_cursor"] = false, + ["multiMonitorSetup"] = "1camera", + ["messagesFontScale"] = 2, + ["defaultFOV"] = 78, + ["shadowTree"] = false, + ["chimneySmokeDensity"] = 5, + ["maxFPS"] = 60, + ["SSS"] = 1, + ["fullScreen"] = true, + ["useDeferredShading"] = 1, + ["effects"] = 3, + ["Scaling"] = 0.66, + ["DOF"] = 0, + ["clouds"] = 2, + ["sceneryDetailsFactor"] = 1, + ["Upscaling"] = "DLSS", + ["flatTerrainShadows"] = 0, + ["LODmult"] = 1, + ["secondaryShadows"] = 0, + ["ColorGradingLUT"] = 0, + ["SSLR"] = 1, + ["width"] = 2560, + ["SSAO"] = 1, + ["Sharpening"] = 0, + ["sync"] = true, + ["preloadRadius"] = 60000, + ["height"] = 1440, + ["motionBlur"] = 0, + ["ScreenshotExt"] = "jpg", + ["civTraffic"] = "high", + }, -- end of ["graphics"] + ["plugins"] = + { + ["Su-25T"] = + { + ["CPLocalList"] = "default", + }, -- end of ["Su-25T"] + ["UltraLeap"] = + { + ["handOffsetY"] = 0, + ["enable"] = false, + ["handUseThrottle"] = false, + ["handOffsetZ"] = 0, + ["handUseStick"] = false, + ["handOffsetX"] = 0, + ["set_debug"] = false, + ["mouseClickSrc"] = 0, + }, -- end of ["UltraLeap"] + ["F-15ESE"] = + { + ["RAND_RDR_CHANNEL"] = false, + ["RADAR_SAR_SCINTILLATION"] = true, + ["RADAR_MAX_CORES"] = 4, + ["LINEAR_STICK"] = false, + ["CPLocalList"] = "default", + ["RADALT_ADVANCED"] = false, + ["MONITOR_EYEPOINT_OFFSET"] = 0, + ["THROTTLE_CO_DETENT"] = 10, + ["VR_EYEPOINT_OFFSET"] = 8, + ["FF_TRIM_OFFSET"] = true, + ["THROTTLE_AB_DETENT"] = 75, + ["GHOST_CONTROLS"] = false, + ["FF_PITCH_GAIN"] = 65, + ["FF_ROLL_GAIN"] = 65, + ["LOW_END_TEXT"] = false, + }, -- end of ["F-15ESE"] + ["F/A-18C"] = + { + ["abDetent"] = 0, + ["canopyReflections"] = 0, + ["hmdEye"] = 1, + ["CPLocalList"] = "Dark", + ["F18RealisticTDC"] = false, + ["mfdReflections"] = 0, + }, -- end of ["F/A-18C"] + ["Tacview"] = + { + ["tacviewRealTimeTelemetryPassword"] = "", + ["tacviewSinglePlayerFlights"] = 2, + ["tacviewPlaybackDelay"] = 600, + ["tacviewMultiplayerFlightsAsClient"] = 2, + ["tacviewModuleEnabled"] = true, + ["tacviewDebugMode"] = 0, + ["tacviewRemoteControlPort"] = "42675", + ["tacviewFlightDataRecordingEnabled"] = true, + ["tacviewMultiplayerFlightsAsHost"] = 2, + ["tacviewTerrainExport"] = 0, + ["tacviewAutoDiscardFlights"] = 10, + ["tacviewRemoteControlEnabled"] = false, + ["tacviewBookmarkShortcut"] = 0, + ["tacviewRealTimeTelemetryPort"] = "42674", + ["tacviewRealTimeTelemetryEnabled"] = true, + ["tacviewRemoteControlPassword"] = "", + ["tacviewExportPath"] = "", + }, -- end of ["Tacview"] + ["AV8BNA"] = + { + ["INS_Alignment"] = 2, + ["INS_GYROHasNAV"] = false, + ["CPLocalList"] = "default", + ["MPCD_GAIN"] = 40, + ["USE_REAL_TDC"] = false, + ["FF_TRIM_OFFSET"] = true, + ["MPCD_CONS"] = 70, + ["USE_MPCD_EXPORT"] = false, + ["MPCD_EXPORT"] = false, + ["FF_ROLL_GAIN"] = 65, + ["FF_PITCH_GAIN"] = 65, + }, -- end of ["AV8BNA"] + ["MiG-29"] = + { + ["CPLocalList_MiG-29S"] = "default", + ["CPLocalList_MiG-29A"] = "default", + ["CPLocalList_MiG-29G"] = "default", + }, -- end of ["MiG-29"] + ["VRFree"] = + { + ["handUseThrottle"] = false, + ["handUseStick"] = false, + ["set_debug"] = false, + ["enable"] = false, + ["mouseClickSrc"] = 0, + }, -- end of ["VRFree"] + ["TF-51D"] = + { + ["assistance"] = 100, + ["CPLocalList"] = "default", + ["autoRudder"] = false, + }, -- end of ["TF-51D"] + ["M-2000C"] = + { + ["FUEL_DETOT_AUTO"] = false, + ["CPLocalList"] = "default", + ["THROTTLE_CO_DETENT"] = 15, + ["RADIO_PTTMENU"] = true, + ["REAL_FREQ_REP"] = true, + ["EZ_LAUNCH_ALERT"] = false, + ["REFUEL_HACK"] = true, + ["PITCH_LIMIT_MODE"] = 1, + ["RAND_RDR_CHANNEL"] = false, + ["VTB_FOV_HEIGHT"] = 150, + ["RADAR_CLUTTERQMC"] = true, + ["VTB_FOV_SIZE"] = 130, + ["VTB_FOV_ENABLE"] = true, + ["THROTTLE_AB_DETENT"] = 90, + ["ENGINE_LOG"] = false, + ["UNI_ALIGNED"] = true, + ["UNI_NODRIFT"] = false, + ["VTB_ENLARGE"] = 0, + ["COCKPIT_WEAR"] = 50, + ["MW_KNOBS_SPEED"] = 0, + ["PITCH_ELASTIC_LIMIT"] = 80, + ["RADAR_MAPOBJECTS"] = true, + ["PCN_DIGIT_MSG"] = true, + }, -- end of ["M-2000C"] + ["A-10C_2"] = + { + ["GUNPAC_Runway"] = 0.1, + ["hmdEye"] = 1, + ["CPLocalList"] = "Dark", + ["throttleSlewMaverick"] = 5, + ["GUNPAC_Hot"] = 0.1, + ["defaultGunMode"] = 0, + ["throttleSlewTGP"] = 5, + ["GUNPAC_Cold"] = 0.1, + ["GUNPAC_Air"] = 0.2, + }, -- end of ["A-10C_2"] + ["Ka-50"] = + { + ["Ka50TrimmingMethod"] = 0, + ["Ka50RudderTrimmer"] = false, + ["hmdEye"] = 1, + ["CPLocalList"] = "default", + ["helmetCircleDisplacement"] = 11, + }, -- end of ["Ka-50"] + ["F-15C"] = + { + ["CPLocalList"] = "default", + }, -- end of ["F-15C"] + ["CaptoGlove"] = + { + ["fingerPinkyBentRight"] = "0", + ["armBending"] = 60, + ["shoulderJointX_Right"] = -15, + ["fingerPinkyStraightRight"] = "1000", + ["mouseClickSrc"] = 0, + ["shoulderJointZ_Left"] = 23, + ["trackerHMDnameRight"] = "DISABLE", + ["set_attach"] = false, + ["fingerMiddleBentLeft"] = "0", + ["gloveRotationPitch_Left"] = 0, + ["fingerMiddleStraightRight"] = "1000", + ["yawOffsetGlove_Left"] = 0, + ["yawOffsetShoulder_Right"] = 0, + ["fingerThumbStraightLeft"] = "1000", + ["gloveOffsetY_Right"] = 0, + ["gloveOffsetX_Left"] = 0, + ["shoulderJointY_Left"] = -23, + ["gloveRotationYaw_Right"] = 0, + ["trackerHMDnameLeft"] = "DISABLE", + ["set_symmetrically"] = false, + ["fingerIndexStraightRight"] = "1000", + ["pitchOffsetGlove_Right"] = 0, + ["fingerIndexBentRight"] = "0", + ["gloveOffsetZ_Right"] = 0, + ["shoulderJointZ_Right"] = 23, + ["useHMDControl"] = false, + ["pitchOffsetShoulder_Left"] = 0, + ["fingerMiddleStraightLeft"] = "1000", + ["pitchOffsetShoulder_Right"] = 0, + ["fingerRingStraightLeft"] = "1000", + ["fingerThumbBentRight"] = "0", + ["shoulderJointX_Left"] = -15, + ["enable"] = false, + ["gloveOffsetY_Left"] = 0, + ["fingerMiddleBentRight"] = "0", + ["fingerRingBentLeft"] = "0", + ["forearmLength_Right"] = 30, + ["fingerThumbStraightRight"] = "1000", + ["gloveOffsetZ_Left"] = 0, + ["shoulderLength_Right"] = 40, + ["fingerPinkyStraightLeft"] = "1000", + ["gloveRotationYaw_Left"] = 0, + ["gloveRotationPitch_Right"] = 0, + ["forearmLength_Left"] = 30, + ["set_debug"] = false, + ["useBending"] = false, + ["fingerRingBentRight"] = "0", + ["shoulderJointY_Right"] = -23, + ["gloveRotationRoll_Left"] = 0, + ["fingerIndexBentLeft"] = "0", + ["shoulderLength_Left"] = 40, + ["yawOffsetShoulder_Left"] = 0, + ["fingerRingStraightRight"] = "1000", + ["fingerIndexStraightLeft"] = "1000", + ["yawOffsetGlove_Right"] = 0, + ["gloveOffsetX_Right"] = 0, + ["pitchOffsetGlove_Left"] = 0, + ["fingerThumbBentLeft"] = "0", + ["gloveRotationRoll_Right"] = 0, + ["fingerPinkyBentLeft"] = "0", + }, -- end of ["CaptoGlove"] + ["A-10A"] = + { + ["CPLocalList"] = "default", + }, -- end of ["A-10A"] + ["Supercarrier"] = + { + ["Use_native_ATC_text"] = false, + ["enable_FLOLS_overlay"] = true, + }, -- end of ["Supercarrier"] + ["Su-25"] = + { + ["CPLocalList"] = "default", + }, -- end of ["Su-25"] + ["A-10C"] = + { + ["throttleSlewTGP"] = 5, + ["GUNPAC_Air"] = 0.2, + ["GUNPAC_Cold"] = 0.1, + ["GUNPAC_Runway"] = 0.1, + ["CPLocalList"] = "default", + ["throttleSlewMaverick"] = 5, + ["GUNPAC_Hot"] = 0.1, + }, -- end of ["A-10C"] + ["Su-33"] = + { + ["CPLocalList"] = "default", + }, -- end of ["Su-33"] + ["Su-27"] = + { + ["CPLocalList"] = "default", + }, -- end of ["Su-27"] + ["F-16C"] = + { + ["hmdEye"] = 1, + ["CPLocalList"] = "default", + ["rollForceStickGradient"] = 1, + ["abDetent"] = 0, + ["canopyReflections"] = 0, + ["pitchDeadzoneValue"] = 4.375, + ["rollDeadzoneValue"] = 5.9, + ["mfdReflections"] = 0, + ["canopyTint"] = 1, + ["pitchForceStickGradient"] = 1, + }, -- end of ["F-16C"] + ["AH-64D_BLK_II"] = + { + ["AH64HmdEye"] = 1, + ["CPLocalList"] = "default", + ["AH64BalaclavaCPG"] = true, + ["AH64PedalsTrimmingMethod"] = 0, + ["AH64ManTrkRampUpSpeed"] = 5, + ["AH64CockpitShake"] = 50, + ["AH64AutoHandover"] = true, + ["AH64CyclicTrimmingMethod"] = 0, + ["AH64IhadssMonocleVisible"] = true, + ["AH64BalaclavaPLT"] = true, + ["AH64VRSNotificationAudio"] = true, + ["AH64VRSNotificationVisual"] = false, + ["AH64AIIFFColorScheme"] = 0, + ["AH64TriggerGuardEnable"] = false, + ["AH64LockoutDetent"] = 1, + ["AH64AIInterfaceColorScheme"] = 0, + ["AH64AIColorScheme"] = 0, + }, -- end of ["AH-64D_BLK_II"] + }, -- end of ["plugins"] + ["format"] = 1, + ["sound"] = + { + ["main_output"] = "", + ["FakeAfterburner"] = false, + ["voiceChatInVolume"] = 50, + ["volume"] = 69, + ["headphones_on_external_views"] = true, + ["voiceChatInSensitivity"] = -50, + ["subtitles"] = true, + ["world"] = 100, + ["hear_in_helmet"] = true, + ["music"] = 0, + ["headphones"] = 100, + ["main_layout"] = "", + ["hp_output"] = "", + ["cockpit"] = 100, + ["voice_chat_output"] = "", + ["voice_chat"] = true, + ["radioSpeech"] = true, + ["GBreathEffect"] = true, + ["switches"] = 100, + ["play_audio_while_minimized"] = false, + ["use_turn_servers"] = true, + ["microphone_use"] = 2, + ["voice_chat_input"] = "", + ["gui"] = 100, + }, -- end of ["sound"] + ["views"] = + { + ["cockpit"] = + { + ["mirrors"] = false, + ["reflections"] = true, + ["avionics"] = 3, + }, -- end of ["cockpit"] + }, -- end of ["views"] +} -- end of options diff --git a/README.md b/README.md index f2caab6..66ea6c6 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,174 @@ -# the-universal-mission-for-dcs-world -A dynamic and universal SP/PvE mission for Eagle Dynamic's DCS World +# The Universal Mission for DCS World + +**Current version: open beta 0.1.250722** (see the "version history" section at the end of this file for a list of the latest changes) + +**This is a BETA version, there may be bugs and there WILL be unbalanced stuff.** + +The Universal Mission for DCS World is an attempt to create a fully dynamic single-player/PvE mission giving access to the whole content of DCS World in a structure similar to the one found in old "simulators", like the early Microprose games (think F-117 or the Strike Eagle serie). +These game had both fun and clear objectives, endless replayability and a career system that made sure that something was at stake: crash and die, and you'll lose all these hard-earned medals. + +As the original creator of [Briefing Room](https://github.com/DCS-BR-Tools/briefing-room-for-dcs) (now maintained by the talented John Harvey), I've always wanted to create an easy-to-use, enticing and fun mission generator for DCS, capable of creating CPU-light missions without requiring an external program. +I think with The Universal Mission is, finally, the proper way to approach this problem. The current version is still an early beta but most core features are already working. I hope you'll like it. + +### Features + +- Can generate any kind of mission: ground attack, interception, strike, airbase attack, CAS, CAP, and more +- Completely dynamic, no two missions are ever the same +- Entirely self-contained inside a .miz file, no need for any external program +- More than 170 fully voiced radio messages (with more coming) for immersive and realistic coms +- Supports both single-player and small-scale PvE on closed servers +- Persistent single player career mode, with awards and promotions. Dying won't reset your progress, but you have to come back to base alive for your kills and completed objectives to be saved to your profile, so watch out for SAMs on your way home +- Uses advanced DCS World scripting functionalities (like the brand new Disposition singleton and net.dostring_in hacks) to achieve effects seldom seen in other scripts, such as graphic overlays and random but realistic placement of units in cities and forests without the use of handmade spawn points +- Various little details to make the DCS world more alive, like crew running away from destroyed vehicles + +[![Screen shot of the mission settings menu](./docs/mission-settings-tn.jpg)](./docs/mission-settings.jpg) +[![Screen shot of the target zone system](./docs/target-zones-tn.jpg)](./docs/target-zones.jpg) +[![Screen shot of the career mode's medal case display](./docs/career-mode-tn.jpg)](./docs/career-mode.jpg) + +### Limitations of current beta version + +**Please read the "planned development" section below for more information.** + +- The current version supports only modern (post-Cold War) units and Caucasus, Persian Gulf and Syria theaters + - Kola, Marianas and Germany support will come soon, others will follow later +- Not all mission types are supported yet +- Career progress may be lost because of future updates, don't get too attached to it + +## How to use/play The Universal Mission? + +### First setup + +- Download the latest release from this GitHub page. +- Copy the .miz files for your theater(s) of choice into your **[Saved Games]\DCS World\Missions directory** +- _**(Optional but strongly recommended)**_ Unsanitize the Lua IO module. You don't have to do this, but the persistent career system won't work if you don't. To do it, open the file **[DCS World installation directory]\Scripts\MissionScripting.lua** with a text editor and comment or remove the line "sanitizeModule('io')". Make sure you restart DCS World once you've modified the file. + - Please note: should you want to backup, delete or transfer it, career progress is saved in **[DCS World installation directory]\TheUniversalMission.sav** + +### Customizing the mission to your taste + +- _**(Optional but you'll probably want to do it)**_ Open the .miz file in the DCS World mission editor and change the player unit to pick your desired aircraft. The default player unit is a Su-25T (as it is the only free DCS airplane equipped with weapons) and you probably won't want to stick with it. + +Please refer to the "Advanced stuff you may want to try" section to learn all the ways you can customize The Universal Mission. + +### Starting the mission + +- Launch the mission from the mission editor or the "Mission" selection in the main menu +- You are now on the ramp or runway. Open the communication menu and navigate to the F10/Other menu. From there, you can view and change mission settings. They include: + - Who belong to the blue and red coalitions + - The type of mission + - The number and location of targets (you can use the F10 map to see where the available target zones are located). Be aware that targets of antiship strikes will always be spawned in open seas, which can be quite far if you picked a landlocked target zone + - The amount of enemy air force and air defense. The higher these settings, the more XP you'll recieve upon completion of a single-player mission +- When you're ready, pick the "Begin mission" option, wait a few seconds (precaching all the game assets can take some time, especially if you have a slow CPU), you're ready to go! +- Use the F10 mission and check the F10 map for additional information about the mission. Don't forget to come back to base alive, all awarded XP and completed objectives will only be saved to your pilot profile once you've landed + +### Playing + +### Advanced stuff you may want to try + +The Universal Mission is designed to be easily editable to suit your preferences. Here are a few things you could do after opening the .miz file in DCS World's mission editor. + +#### Player aircraft + +- Change the player aircraft starting condition (runway, parking or parking hot). Air starts are not recommended as all players must be on the ground to begin a new mission +- Move it to another airbase, change its coalition (make sure blue players are spawned on an airbase located in a REDFOR zone are red players are spawned on an airbase located in a REDFOR zone) + - You may also add an aircraft carrier or a FARP for the player to take off from +- Change its default loadout if you plan to play a specific kind of mission and don't want to lose time asking the ground crew to rearm your aircraft (e.g. if you know you want to play SEAD missions, you may as well stock up on AGM-88s) +- Change the skill level from "Player" to "Client" and add other aircraft to create a multiplayer mission to play with your friends. Keep in mind that the persistent career/player stats system will be disabled in multiplayer missions and that all player aircraft must belong to the same coalition (TUM does not support PvP) + +#### Zones + +- All zones whose names starts with BLUFOR or REDFOR decide the territory (and airbases) controlled by the blue and red coalitions + - Be aware that any change to the airbases coalitions will be superseded by the BLUFOR and REFOR zones +- All zones whose names starts with WATER are seas, used to spawn ships +- Zones with a name not starting with BLUFOR, REDFOR or WATER are target zones. These are zones where objectives can be spawned, who can be selected in the "objective location" setting of the intermission F10 menu + - Change, add or remove zones to create new possible target areas. A maximum of 10 target areas can be created, so they fit the F10 menu + +#### A few notes regarding multiplayer + +While The Universal Mission supports multiplayer and is perfectely suitable (and fun!) for playing with friends on a private server, it is **absolutely not suited for public servers** as missions settings can be edited by anyone at any time using the F10 menu. +Please also note that PvP is not supported at the moment and that the mission will not launch if both coalitions have player slots. + +#### Other parameters + +- _(Not yet implemented in this version)_ By changing the year in mission time parameters, the time period will be changed accordingly and the proper factions and AI units will be spawned during the mission. Time periods are: + - 1945 and before: World War 2 + - 1946-1959: Korea War + - 1960-1974: Vietnam War + - 1975-1989: Late Cold War + - 1990-now: Modern +- _(Not yet implemented in this version)_ Changing the weather to make it more cloudy or windy, or setting the mission to nighttime, will make the mission more difficult but also award more points. + +## Planned development + +### Planned for next update (ASAP, before I leave for vacations :) + +- Support for Kola, Marianas and Germany theaters + +### VERY high priority + +- New objectives: helicopter (drop/pickup units...), CAP, CAS, OCA (airbase attack) +- New menu for enemy contacts report, allowing both to quiery AWACS reports and to ask other AI pilots about what they see in the air and on the ground + +### High priority + +- Additional and better radio messages + - More "flavor" radio messages ("fence in" when player approaches the AO, etc) so the world will feel more alive +- All new AI wingman system +- Better balancing of the player career awards and promotions +- Better use of context for "ambient" radio messages (should only warn of a SAM launch if an AI pilot is there to witness it, etc) +- Friendly air defenses +- Laser designation of targets by JTAC +- Support for all missing DCS World theaters +- Support for more factions and five different time periods (World War 2, Korea war, Vietnam war, late Cold war, Modern) + +### Medium priority + +- Advanced options such as "use metric units" or "use bullseye rather than player position for coordinates transmission" (the later awarding more XP when enabled, because of the need for higher situational awareness) +- GitHub page +- Improved score multiplier taking into account various aspects of mission difficulty (weather, nighttime ops...) +- Modded units support (other than player-controlled aircraft, those are already supported: just add them to the mission) +- Spawning of tankers for long-range missions +- (maybe) Text (not voiceover) localization, if there's enough popular demand + +### Low priority + +- Female voice option for the player +- New ATC system. Won't be realistic/BMS-like but a LOT better than the vanilla DCS one +- (maybe) AI CSAR helos to pick up stranded/ejected pilots +- (maybe) PvP support + +## Misc + +- **Released under the GNU GPL 3.0 licence** +- AI use/disclosure + - [ChatGPT](https://chatgpt.com/): used to generate first draft of radio messages + - [ElevenLabs](https://elevenlabs.io/fr): used to generate radio messages voiceover + +## How to build the miz files + +_(this is only for developpers, end users should just download the latest release)_ + +I use a PHP script to "build" the .miz files as PHP is a pretty handy tool capable both of handling all requirement replacements and pack the miz files. + +If you want to build the .miz files yourself, you'll need to: + +- [Download PHP](https://www.php.net/) and install it +- Add the PHP directory it to your system PATH so the batch file will find it +- In the PHP directory, rename php.ini-production to php.ini, edit it and uncomment or delete the "extension=mbstring" line to enable the MBString extension that my script uses. +- Then run Make.bat in the source directory (or, alternatively, open the source directory in VSCode and press Ctrl+Alt+B) +- .Miz files will be generated in the project root directory, and a copy will be sent to your DCS World "missions" directory (if it exists) + +## Want to contribute to the project? + +The core script is quite simple and small, I probably won't need too much help with it (of course you're free to suggest improvements and submit pull requests). + +**What I'll need help with most is:** + +- Feedback, feedback, feedback. Especially regarding bug (don't forget to write down the various script line numbers mentioned in the error message!) mission balancing and career progression. + - You can contact me through GitHub or send me a mail at akaagarmail@gmail.com +- Help with the Lua tables in the Database\Factions and Database\Aircraft directories. Adding missing aircraft, payload for various aircraft and unit lists for various time periods would be really helpful. +- Complete the library of "unit human-readable names" in Script\Library\ObjectNames.lua +- You can also [buy me a coffee](https://buymeacoffee.com/akaagar)! + +## Version history + +- **0.1.250722** (07/22/2025): Initial public release diff --git a/Script/DCS extensions/Converter.lua b/Script/DCS extensions/Converter.lua new file mode 100644 index 0000000..305d7c8 --- /dev/null +++ b/Script/DCS extensions/Converter.lua @@ -0,0 +1,146 @@ +-- ==================================================================================== +-- (DCS LUA ADD-ON) CONVERTER - UNITS CONVERSION FUNCTIONS +-- +-- DCSEx.converter.celsiusToFahrenheit(t) +-- DCSEx.converter.degreesToRadians(degrees) +-- DCSEx.converter.fahrenheitToCelsius(fahrenheit) +-- DCSEx.converter.feetToMeters(feet) +-- DCSEx.converter.kelvinToCelsius(t) +-- DCSEx.converter.kelvinToFahrenheit(t) +-- DCSEx.converter.kmphToMps(kmph) +-- DCSEx.converter.knotsToMps(knots) +-- DCSEx.converter.metersToFeet(meters) +-- DCSEx.converter.metersToNM(meters) +-- DCSEx.converter.mpsToKmph(mps) +-- DCSEx.converter.mpsToKnots(mps) +-- DCSEx.converter.nmToMeters(nm) +-- DCSEx.converter.radiansToDegrees(radians) +-- ==================================================================================== + +DCSEx.converter = {} + +------------------------------------- +-- Converts Celsius degrees to Fahrenheit +-- @param t Temperature in Celsius degrees +-- @return Temperature in Fahrenheit degrees +------------------------------------- +function DCSEx.converter.celsiusToFahrenheit(t) + return t * (9 / 5) + 32 +end + +------------------------------------- +-- Converts angle in degrees to radians. +-- @param degrees Angle in degrees +-- @return Angle in radians +------------------------------------- +function DCSEx.converter.degreesToRadians(degrees) + return degrees * math.pi / 180 +end + +------------------------------------- +-- Converts Fahrenheit degrees to Celsius +-- @param fahrenheit Temperature in Fahrenheit degrees +-- @return Temperature in Celsius degrees +------------------------------------- +function DCSEx.converter.fahrenheitToCelsius(fahrenheit) + return (fahrenheit - 32) * (5 / 9) +end + +------------------------------------- +-- Converts feet to meters. +-- @param feet Distance in feet +-- @return Distance in meters +------------------------------------- +function DCSEx.converter.feetToMeters(feet) + return feet * 0.3048 +end + +------------------------------------- +-- Converts Kelvin degrees to Celsius +-- @param kelvin Temperature in Kelvin degrees +-- @return Temperature in Celsius degrees +------------------------------------- +function DCSEx.converter.kelvinToCelsius(t) + return t - 273.15 +end + +------------------------------------- +-- Converts Kelvin degrees to Fahrenheit +-- @param kelvin Temperature in Kelvin degrees +-- @return Temperature in Fahrenheit degrees +------------------------------------- +function DCSEx.converter.kelvinToFahrenheit(t) + return DCSEx.converter.celsiusToFahrenheit(DCSEx.converter.kelvinToCelsius(t)) +end + +------------------------------------- +-- Converts kilometers per hour to meters per second. +-- @param kmph speed in km/h +-- @return speed in m/s +------------------------------------- +function DCSEx.converter.kmphToMps(kmph) + return kmph / 3.6 +end + +------------------------------------- +-- Converts knots to meters per second. +-- @param knots speed in knots +-- @return speed in m/s +------------------------------------- +function DCSEx.converter.knotsToMps(knots) + return knots * 1852 / 3600 +end + +------------------------------------- +-- Converts meters to feet. +-- @param meters distance in meters +-- @return distance in feet +------------------------------------- +function DCSEx.converter.metersToFeet(meters) + return meters / 0.3048 +end + +------------------------------------- +-- Converts meters to nautical miles. +-- @param meters distance in meters +-- @return distance in nautical miles +------------------------------------- +function DCSEx.converter.metersToNM(meters) + return meters / 1852 +end + +------------------------------------- +-- Converts meters per second to kilometers per hour. +-- @param mps speed in m/s +-- @return speed in km/h +------------------------------------- +function DCSEx.converter.mpsToKmph(mps) + return mps * 3.6 +end + +------------------------------------- +-- Converts meters per second to knots. +-- @param mps speed in m/s +-- @return speed in knots +------------------------------------- +function DCSEx.converter.mpsToKnots(mps) + return mps * 3600 / 1852 +end + +------------------------------------- +-- Converts nautical miles to meters. +-- @param nm distance in nautical miles +-- @return distance in meters +------------------------------------- +function DCSEx.converter.nmToMeters(nm) + return nm * 1852 +end + +------------------------------------- +-- Converts angle in radians to degrees. +-- @param degrees Angle in radians +-- @return Angle in degrees +------------------------------------- +function DCSEx.converter.radiansToDegrees(radians) + return radians * 180 / math.pi +end diff --git a/Script/DCS extensions/DCS.lua b/Script/DCS extensions/DCS.lua new file mode 100644 index 0000000..3c589bb --- /dev/null +++ b/Script/DCS extensions/DCS.lua @@ -0,0 +1,374 @@ +-- ==================================================================================== +-- DCSTOOLS - FUNCTIONS LINKED TO DCS WORLD RULES AND TABLES +-- ==================================================================================== +-- DCSEx.dcs.getBRAA(point, refPoint, showAltitude, metricSystem) +-- DCSEx.dcs.getCJTFForCoalition(coalitionID) +-- DCSEx.dcs.getCoalitionAsString(coalitionID) +-- DCSEx.dcs.getCoalitionColor(coalitionID, alpha) +-- DCSEx.dcs.getGroupCenterPoint(group) +-- DCSEx.dcs.getGroupIDAsNumber(group) +-- DCSEx.dcs.getNearestObject(refPoint, objectTable) +-- DCSEx.dcs.getNearestObjects(refPoint, objectTable, maxCount) +-- DCSEx.dcs.getNearestPoints(refPoint, pointsTable, maxCount) +-- DCSEx.dcs.getOppositeCoalition(coalitionID) +-- DCSEx.dcs.getPlayerUnitsInGroup(group) +-- DCSEx.dcs.getPlayerUnitsInGroupByID(groupID) +-- DCSEx.dcs.getRadioModulationName(modulationID) +-- DCSEx.dcs.getObjectIDAsNumber(obj) +-- DCSEx.dcs.getUnitTypeFromFamily(unitFamily) +-- DCSEx.dcs.getUnitFamilyForDecade(unitFamily, decade) -- TODO: remove? +-- ==================================================================================== + +DCSEx.dcs = { } + +-- TODO: add description and update file header +function DCSEx.dcs.doNothing() +end + +------------------------------------- +-- Gets a BRAA (bearing, range, altitude, aspect) string about a point +-- Format is "[bearing to unit] for [distance] at [altitude]" +-- @param unit A unit +-- @param refPoint2 Reference point for the bearing and distance +-- @param showAltitude Should altitude be displayed? +-- @param metricSystem Should metric system units be used? +-- @return BRAA, as a string +------------------------------------- +function DCSEx.dcs.getBRAA(point, refPoint, showAltitude, metricSystem) + showAltitude = showAltitude or false + metricSystem = metricSystem or false + + if not point.z then point = DCSEx.math.vec2ToVec3(point, "land") end + if not refPoint.z then refPoint = DCSEx.math.vec2ToVec3(refPoint, "land") end + + local braa = tostring(DCSEx.math.getBearing(point, refPoint)) + local distance = DCSEx.math.getDistance2D(point, refPoint) + if metricSystem then + distance = distance / 1000.0 + else + distance = DCSEx.converter.metersToNM(distance) + end + + braa = braa .. " for " .. tostring(math.ceil(distance)) + + if showAltitude then + local altitude = point.y + if metricSystem then + altitude = math.floor(point.y / 100) * 100 + else + altitude = math.floor(DCSEx.converter.metersToFeet(point.y) / 1000) * 1000 + end + braa = braa .. " at " .. tostring(altitude) + end + + return braa +end + +------------------------------------- +-- Returns the CJTF country for a given coalition +-- @param A coalition ID +-- @return A country ID (country.id.CJTF_BLUE or country.id.CJTF_RED) +------------------------------------- +function DCSEx.dcs.getCJTFForCoalition(coalitionID) + if coalitionID == coalition.side.RED then return country.id.CJTF_RED end + + return country.id.CJTF_BLUE +end + +------------------------------------- +-- Returns the name of a coalition, as a string ("blue", "red" or "neutral") +-- @param A coalition ID +-- @return A string +------------------------------------- +function DCSEx.dcs.getCoalitionAsString(coalitionID) + if coalitionID == coalition.side.NEUTRAL then return "neutral" end + if coalitionID == coalition.side.RED then return "red" end + if coalitionID == coalition.side.BLUE then return "blue" end + return nil +end + +------------------------------------- +-- Returns the RGBA color table for the given coalition, accoding to NATO symbology colors +-- @param coalitionID A coalition side +-- @param alpha (optional) Alpha. Default is 1 +-- @return A RGBA color table +------------------------------------- +function DCSEx.dcs.getCoalitionColor(coalitionID, alpha) + alpha = alpha or 1.0 + + if coalitionID == coalition.side.RED then return {0.97, 0.52, 0.51, alpha} + elseif coalitionID == coalition.side.BLUE then return {0.52, 0.87, 0.99, alpha} + else + return {0.5, 0.5, 0.5, alpha} + end +end + +-- TODO: description +function DCSEx.dcs.getFirstUnitCallsign(group) + if not group then return nil end + + local units = group:getUnits() + if not units then return nil end + + local unit0 = units[1] + if not unit0 then return nil end + return unit0:getCallsign() +end + +------------------------------------- +-- Returns a vec3 point at the center of all units of a group +-- @param group A group object +-- @return A vec3 +------------------------------------- +function DCSEx.dcs.getGroupCenterPoint(group) + if not group then return nil end + local units = group:getUnits() + if not units or #units == 0 then return nil end + + local centerPoint = { x = 0, y = 0, z = 0} + for _,u in pairs(units) do + local uPoint = u:getPoint() + centerPoint.x = centerPoint.x + uPoint.x + centerPoint.y = centerPoint.y + uPoint.y + centerPoint.z = centerPoint.z + uPoint.z + end + + centerPoint.x = centerPoint.x / #units + centerPoint.y = centerPoint.y / #units + centerPoint.z = centerPoint.z / #units + + return centerPoint +end + +------------------------------------- +-- Returns the ID of a group as a number (here to fix a bug where sometimes ID is returned as a string) +-- @param group A group table +-- @return An ID (as an number) or nil if group is nil or has no ID +------------------------------------- +function DCSEx.dcs.getGroupIDAsNumber(group) + if not group then + return nil + end + + return tonumber(group:getID()) +end + +------------------------------------- +-- Returns the object nearest (in a 2D plane) from a given point +-- @param refPoint A reference point, as a vec2 or vec3 +-- @param objectTable A table of DCS objects +-- @return The object nearest from the point, or nil if no object was found +------------------------------------- +function DCSEx.dcs.getNearestObject(refPoint, objectTable) + local nearestObjects = DCSEx.dcs.getNearestObjects(refPoint, objectTable, 1) + if #nearestObjects == 0 then return nil end + return nearestObjects[1] +end + +------------------------------------- +-- Returns the nearest objects (in a 2D plane) from a given point +-- @param refPoint A reference point, as a vec2 or vec3 +-- @param objectTable A table of DCS objects +-- @param maxCount (optional) Maximum number of objects to return +-- @return A table of objects, sorted by distance +------------------------------------- +function DCSEx.dcs.getNearestObjects(refPoint, objectTable, maxCount) + if not refPoint then return DCSEx.table.deepCopy(objectTable) end + + refPoint = DCSEx.math.vec3ToVec2(refPoint) + local sortedObjects = {} + local sortedKeys = {} + + for i,o in pairs(objectTable) do + local distance = DCSEx.math.getDistance2D(refPoint, o:getPoint()) + sortedObjects[distance] = i + table.insert(sortedKeys, distance) + end + + table.sort(sortedKeys) + + local sortedList = {} + for _,v in ipairs(sortedKeys) do + if maxCount and #sortedList >= maxCount then break end + table.insert(sortedList, objectTable[sortedObjects[v]]) + end + + return sortedList +end + +------------------------------------- +-- Returns the nearest points (in a 2D plane) from a given point +-- @param refPoint A reference point, as a vec2 or vec3 +-- @param objectTable A table of points (vec2 or vec3) +-- @param maxCount (optional) Maximum number of points to return +-- @return A table of points, sorted by distance +------------------------------------- +function DCSEx.dcs.getNearestPoints(refPoint, pointsTable, maxCount) + if not refPoint then return DCSEx.table.deepCopy(pointsTable) end + + refPoint = DCSEx.math.vec3ToVec2(refPoint) + local sortedPoints = {} + local sortedKeys = {} + + for i,pt in pairs(pointsTable) do + local distance = DCSEx.math.getDistance2D(refPoint, pt) + sortedPoints[distance] = i + table.insert(sortedKeys, distance) + end + + table.sort(sortedKeys) + + local sortedList = {} + for _,v in ipairs(sortedKeys) do + if maxCount and #sortedList >= maxCount then break end + table.insert(sortedList, pointsTable[sortedPoints[v]]) + end + + return sortedList +end + +------------------------------------- +-- Returns the coalition opposed to the provided coalition (coalition.side.NEUTRAL still returns NEUTRAL) +-- @param group A coalition +-- @return Another coalition +------------------------------------- +function DCSEx.dcs.getOppositeCoalition(coalitionID) + if coalitionID == coalition.side.RED then return coalition.side.BLUE end + if coalitionID == coalition.side.BLUE then return coalition.side.RED end + return coalition.side.NEUTRAL +end + +------------------------------------- +-- Returns all player-controlled units in a group +-- @param group A group object +-- @return A table of unit objects +------------------------------------- +function DCSEx.dcs.getPlayerUnitsInGroup(group) + if not group then return nil end + local units = group:getUnits() + if not units then return { } end + + local players = {} + for _,u in pairs(units) do + if u:getPlayerName() then + table.insert(players, u) + end + end + + return players +end + +------------------------------------- +-- Returns all player-controlled units in the group with the given ID +-- @param groupID A group ID +-- @return A table of unit objects +------------------------------------- +function DCSEx.dcs.getPlayerUnitsInGroupByID(groupID) + return DCSEx.dcs.getPlayerUnitsInGroup(DCSEx.world.getGroupByID(groupID)) +end + +------------------------------------- +-- Returns a radio modulation type as a string +-- @param modulationID A modulation ID (from radio.modulation enum) +-- @return A string +------------------------------------- +function DCSEx.dcs.getRadioModulationName(modulationID) + if modulationID == radio.modulation.FM then return "FM" end + return "AM" +end + +------------------------------------- +-- Returns a remplacement unit family for given family if it's not available in this decade (e.g. SAMs in the 1940s). Else returns the original family. +-- @param unitFamily An unit family +-- @param decade (optional) A decade, or the current decade from env.mission.date.Year +-- @return An unit family +------------------------------------- +function DCSEx.dcs.getUnitFamilyForDecade(unitFamily, decade) + -- TODO + -- decade = decade or envMission.getDecade() + + -- if decade < 1990 then + -- if unitFamily == DCSEx.enums.unitFamily.UAVs then + -- unitFamily = DCSEx.enums.unitFamily.AttackHelicopters + -- end + -- end + + -- if decade < 1970 then + -- if unitFamily == DCSEx.enums.unitFamily.AWACS then + -- unitFamily = DCSEx.enums.unitFamily.Transports + -- elseif unitFamily == DCSEx.enums.unitFamily.SAMShort then + -- unitFamily = DCSEx.enums.unitFamily.MobileAAA + -- elseif unitFamily == DCSEx.enums.unitFamily.SAMShortIR then + -- unitFamily = DCSEx.enums.unitFamily.MobileAAA + -- end + -- end + + -- if decade < 1960 then + -- if unitFamily == DCSEx.enums.unitFamily.AttackHelicopters then + -- unitFamily = DCSEx.enums.unitFamily.Fighters + -- elseif unitFamily == DCSEx.enums.unitFamily.MANPADS then + -- unitFamily = DCSEx.enums.unitFamily.Infantry + -- elseif unitFamily == DCSEx.enums.unitFamily.SAMLong then + -- unitFamily = DCSEx.enums.unitFamily.StaticAAA + -- elseif unitFamily == DCSEx.enums.unitFamily.SAMMedium then + -- unitFamily = DCSEx.enums.unitFamily.StaticAAA + -- elseif unitFamily == DCSEx.enums.unitFamily.SSMissiles then + -- unitFamily = DCSEx.enums.unitFamily.Artillery + -- elseif unitFamily == DCSEx.enums.unitFamily.TransportHelicopters then + -- unitFamily = DCSEx.enums.unitFamily.Transports + -- end + -- end + + -- if decade < 1950 then + -- if unitFamily == DCSEx.enums.unitFamily.MobileAAA then + -- unitFamily = DCSEx.enums.unitFamily.APC + -- elseif unitFamily == DCSEx.enums.unitFamily.Tankers then + -- unitFamily = DCSEx.enums.unitFamily.Transports + -- end + -- end + + return unitFamily +end + +-- TODO: description +function DCSEx.dcs.getUnitTypeFromFamily(unitFamily) + return math.floor(unitFamily / 100) +end + +------------------------------------- +-- Returns the ID of an object as a number (here to fix a bug where sometimes ID is returned as a string) +-- @param obj An object (unit, static object...) +-- @return An ID (as an number) or nil if unit is nil or has no ID +------------------------------------- +function DCSEx.dcs.getObjectIDAsNumber(obj) + if not obj then return nil end + return tonumber(obj:getID()) +end + +-- TODO: description & file header +function DCSEx.dcs.loadMission(fileName) + net.dostring_in("mission", string.format("a_load_mission(\"%s\")", fileName)) +end + +-- TODO: description & file header +-- function DCSEx.dcs.isMultiplayer() +-- if #net.get_player_list() > 0 then return true end +-- if dcs and dcs.isServer() == true then return true end +-- return false +-- end + +-- TODO: a_end_mission + +-- TODO: description & file header +function DCSEx.dcs.outPicture(fileName, durationSeconds, clearView, startDelay, horizontalAlign, verticalAlign, size, sizeUnits) + clearView = clearView or false + startDelay = startDelay or 0 + horizontalAlign = horizontalAlign or 1 + verticalAlign = verticalAlign or 1 + size = size or 100 + sizeUnits = sizeUnits or 0 + + if clearView then clearView = "true" else clearView = "false" end + + net.dostring_in("mission", string.format("a_out_picture(getValueResourceByKey(\"%s\"), %d, %s, %d, \"%d\", \"%d\", %d, \"%d\")", fileName, durationSeconds, clearView, startDelay, horizontalAlign, verticalAlign, size, sizeUnits)) +end diff --git a/Script/DCS extensions/Enums.lua b/Script/DCS extensions/Enums.lua new file mode 100644 index 0000000..08adcf6 --- /dev/null +++ b/Script/DCS extensions/Enums.lua @@ -0,0 +1,123 @@ +DCSEx.enums = {} + +------------------------------------- +-- Line types for map markers. The enum is missing from DCS +------------------------------------- +DCSEx.enums.lineType = { + NO_LINE = 0, + SOLID = 1, + DASHED = 2, + DOTTED = 3, + DOT_DASH = 4, + LONG_DASH = 5, + TWO_DASH = 6, +} + +------------------------------------- +-- Event to check to see if a task/objective is complete +------------------------------------- +DCSEx.enums.spawnPointType = { + LAND_LARGE = 1, + LAND_MEDIUM = 2, + LAND_SMALL = 3, + SEA = 4, +} + +------------------------------------- +-- Event to check to see if a task/objective is complete +------------------------------------- +DCSEx.enums.taskEvent = { + DESTROY = 1, + DESTROY_SCENERY = 2, + LAND = 3, +} + +------------------------------------- +-- Families of mission tasks +------------------------------------- +DCSEx.enums.taskFamily = { + ANTISHIP = 1, + -- CAP = 2, -- TODO + -- CAS = 3, -- TODO + GROUND_ATTACK = 2, -- 4 + -- HELICOPTER = XXX, -- 5 + -- HELO_HUNT = XXX, -- 6 + INTERCEPTION = 3, -- 7 + -- OCA = XXX, -- 8 + SEAD = 4, --9 + STRIKE = 5, -- 10 +} + +------------------------------------- +-- Special events for tasks +------------------------------------- +DCSEx.enums.taskFlag = { + ALLOW_JTAC = 1, + DESTROY_TRACK_RADARS_ONLY = 2, + MOVING = 3, + ON_ROADS = 4, + SCENERY_TARGET = 5 +} + +------------------------------------- +-- Enumerates the various time periods during which DCS World missions can take place +------------------------------------- +DCSEx.enums.timePeriod = { + WORLD_WAR_2 = 1, + KOREA_WAR = 2, + VIETNAM_WAR = 3, + COLD_WAR = 4, + MODERN = 5 +} + +------------------------------------- +-- Type of unit families. Hundreds digit must match the value in the Unit.Category enum +------------------------------------- +DCSEx.enums.unitFamily = { + AIRDEFENSE_MANPADS = 251, + AIRDEFENSE_AAA_MOBILE = 252, + AIRDEFENSE_AAA_STATIC = 253, + AIRDEFENSE_SAM_LONG = 254, + AIRDEFENSE_SAM_MEDIUM = 255, + AIRDEFENSE_SAM_SHORT = 256, + AIRDEFENSE_SAM_SHORT_IR = 257, + + PLANE_ATTACK = 001, + PLANE_AWACS = 002, + PLANE_BOMBER = 003, + PLANE_FIGHTER = 004, + PLANE_INTERCEPTOR = 005, + PLANE_TANKER = 006, + PLANE_TRANSPORT = 007, + PLANE_UAV = 008, + + GROUND_APC = 201, + GROUND_ARTILLERY = 202, + GROUND_INFANTRY = 203, + GROUND_MBT = 204, + GROUND_SS_MISSILE = 205, + GROUND_UNARMED = 206, + + HELICOPTER_ATTACK = 101, + HELICOPTER_TRANSPORT = 102, + + SHIP_CARGO = 301, + SHIP_CARRIER = 302, + SHIP_CRUISER = 303, + SHIP_FRIGATE = 304, + SHIP_LIGHT = 305, + SHIP_MISSILE_BOAT = 306, + SHIP_SUBMARINE = 307, + + STATIC_SCENERY = 401, + STATIC_STRUCTURE = 402 +} + +DCSEx.enums.victoryCondition = { + DESTROY = 1, + DESTROY_NO_AIR_DEFENSE = 2, + DESTROY_SCENERY = 3, + DESTROY_TRACK_RADARS_ONLY = 4, -- for SEAD tasks + + LAND_NEAR = 5, +} diff --git a/Script/DCS extensions/EnvMission.lua b/Script/DCS extensions/EnvMission.lua new file mode 100644 index 0000000..916d7f5 --- /dev/null +++ b/Script/DCS extensions/EnvMission.lua @@ -0,0 +1,131 @@ +-- ==================================================================================== +-- (DCS LUA ADD-ON) ENVMISSION - FUNCTIONS RELATED TO THE ENV.MISSION TABLE +-- +-- DCSEx.envMission.getDecade(yearOffset) +-- DCSEx.envMission.getDistanceToNearestPlayerSpawnPoint(point) +-- DCSEx.envMission.getGroup(groupID) +-- DCSEx.envMission.getGroups(sideID) +-- DCSEx.envMission.getPlayerGroups(coalitionId) +-- ==================================================================================== + +DCSEx.envMission = {} + +------------------------------------- +-- Returns the decade during which the mission takes place (1940 to 2010) +-- @param yearOffset An offset to apply to the actual year +-- @return The decade, as a number +------------------------------------- +function DCSEx.envMission.getDecade(yearOffset) + return DCSEx.math.clamp(math.floor((env.mission.date.Year + (yearOffset or 0)) / 10) * 10, 1940, 2010) +end + +------------------------------------- +-- Returns the distance to the nearest player spawn point +-- @param coalition Coalition the players belong to +-- @param point A vec3 or vec2 +-- @return The distance, in meters, to the nearest player spawn point, or nil if no player spawn points are present +------------------------------------- +function DCSEx.envMission.getDistanceToNearestPlayerSpawnPoint(coalition, point) + point = DCSEx.math.vec3ToVec2(point) + + local playerSlots = DCSEx.envMission.getPlayerGroups(coalition) + if #playerSlots == 0 then return nil end + + local nearestPlayer = 999999999999999999999 + for _,p in pairs(playerSlots) do + local dist = DCSEx.math.getDistance2D(point, p) + if dist < nearestPlayer then nearestPlayer = dist end + end + return nearestPlayer +end + +------------------------------------- +-- Gets information about a group +-- @param groupID Group ID +-- @return Missiondata group table or nil if ID doesn't exist +------------------------------------- +function DCSEx.envMission.getGroup(groupID) + local allGroups = DCSEx.envMission.getGroups() + + for _, g in ipairs(allGroups) do + if g.groupId == groupID then + return g + end + end + + return nil +end + +------------------------------------- +-- Gets all unit groups +-- @param sideID Coalition ID (coalition.side.*), or nil to return unit groups from all coalitions +-- @return Table of missiondata group tables +------------------------------------- +function DCSEx.envMission.getGroups(sideID) + -- Group data are located in coalition[COAL_ID_AS_STRING].country[N][UNIT_CATEGORY_AS_STRING].group[] + -- (e.g. coalition["blue"].country[1]["plane"].group[1]) + + local validCoalitions = { [0] = "neutrals", [1] = "red", [2] = "blue"} -- by default, search all coalitions + if sideID == coalition.side.BLUE then + validCoalitions = { [2] = "blue"} + elseif sideID == coalition.side.RED then + validCoalitions = { [1] = "red" } + elseif sideID == coalition.side.NEUTRAL then + validCoalitions = { [0] = "neutrals" } + end + + local groups = {} + + for coalID, coalName in pairs(validCoalitions) do -- In each coalition... + if env.mission.coalition[coalName] then -- (if coalition exists) + for _, countryData in pairs(env.mission.coalition[coalName].country) do -- search each country + for _, uType in pairs({"helicopter", "plane", "ship", "static", "vehicle"}) do -- for each unit category + if countryData[uType] then -- (if unit category exists) + for _,g in pairs(countryData[uType].group) do -- for each group + local groupInfo = DCSEx.table.deepCopy(g) + groupInfo.coalition = coalID -- store coalition side for later reference + table.insert(groups, groupInfo) + end + end + end + end + end + end + + return groups +end + +------------------------------------- +-- Gets all player groups +-- @param coalitionId Coalition ID (coalition.side.*), or nil to return unit groups from all coalitions +-- @return Table of missiondata group tables +------------------------------------- +function DCSEx.envMission.getPlayerGroups(coalitionId) + local allGroups = DCSEx.envMission.getGroups(coalitionId) + + local playerGroups = {} + + for _, g in ipairs(allGroups) do + local isPlayerGroup = false + + for _, u in pairs(g.units) do + if u.skill == "Player" or u.skill == "Client" then + isPlayerGroup = true + end + end + + if isPlayerGroup then + table.insert(playerGroups, DCSEx.table.deepCopy(g)) + end + end + + return playerGroups +end + +-- TODO: description & file header +function DCSEx.envMission.setBriefing(side, text, picture) + text = text or "" + text = text:gsub("\n", "\\n") + picture = picture or "" + net.dostring_in("mission", string.format("a_set_briefing(\"%s\", getValueResourceByKey(\"%s\"), \"%s\")", DCSEx.dcs.getCoalitionAsString(side):lower(), picture, text)) +end diff --git a/Script/DCS extensions/IO.lua b/Script/DCS extensions/IO.lua new file mode 100644 index 0000000..a0597ae --- /dev/null +++ b/Script/DCS extensions/IO.lua @@ -0,0 +1,106 @@ +-- ==================================================================================== +-- DCSEx.IO - HANDLES READING/WRITING FILES +-- ==================================================================================== +-- DCSEx.io.canReadAndWrite() +-- DCSEx.io.load(fileName) +-- DCSEx.io.save(fileName, values) +-- ==================================================================================== + +DCSEx.io = {} + +do + ------------------------------------- + -- Returns true if the IO table has been unsanitized (allowing IO operations) + -- and false if it hasn't been + -- + -- @return A boolean + ------------------------------------- + function DCSEx.io.canReadAndWrite() + return io ~= nil + end + + ------------------------------------- + -- Loads a table from a text file + -- + -- @param fileName Name of the file to read + -- @param obfuscate Should the file contents be obfuscated? + -- @return A table, or nil if something went wrong + ------------------------------------- + -- function DCSEx.io.load(fileName, obfuscate) + -- obfuscate = obfuscate or false -- TODO: obfuscation + + -- -- IO table is sanitized, cannot read/write to disk + -- if not DCSEx.io.canReadAndWrite() then return nil end + + -- local saveFile = io.open(fileName, "r") + -- if not saveFile then return nil end + + -- local values = {} + -- local rawText = saveFile:read("*all") + -- for k, v in string.gmatch(rawText, "(%w+)=(%w+)") do + -- local numval = tonumber(v) + -- if numval then + -- values[k] = tonumber(v) + -- else + -- values[k] = v + -- -- trigger.action.outText("GET value \""..k.."\" AT \""..tostring(v).."\"", 1) + -- end + -- end + -- saveFile:close() + + -- return values + -- end + + function DCSEx.io.load(fileName) + -- IO table is sanitized, cannot read/write to disk + if not DCSEx.io.canReadAndWrite() then return nil end + + local saveFile = io.open(fileName, "r") + if not saveFile then return nil end + local str = saveFile:read("*all") + saveFile:close() + + return str + end + + ------------------------------------- + -- Saves a table to a text file + -- + -- @param fileName Name of the file to write to + -- @param values Key/value table containing the values to save + -- @param obfuscate Should the file contents be obfuscated? + -- @return True if everything went right, false otherwise + ------------------------------------- + -- function DCSEx.io.save(fileName, values, obfuscate) + -- obfuscate = obfuscate or false -- TODO: obfuscation + + -- -- IO table is sanitized, cannot read/write to disk + -- if not DCSEx.io.canReadAndWrite() then return false end + + -- -- No values or not a table + -- if values == nil then return false end + -- if type(values) ~= "table" then return false end + + -- local saveFile = io.open(fileName, "w") + -- if not saveFile then return false end + + -- for k,v in pairs(values) do + -- saveFile:write(k.."="..tostring(v).."\n") + -- -- trigger.action.outText("SET value \""..k.."\" TO \""..tostring(v).."\"", 1) + -- end + -- saveFile:close() + + -- return true + -- end + function DCSEx.io.save(fileName, str) + -- IO table is sanitized, cannot read/write to disk + if not DCSEx.io.canReadAndWrite() then return false end + + local saveFile = io.open(fileName, "w") + if not saveFile then return false end + saveFile:write(str) + saveFile:close() + + return true + end +end diff --git a/Script/DCS extensions/Math.lua b/Script/DCS extensions/Math.lua new file mode 100644 index 0000000..cc7a893 --- /dev/null +++ b/Script/DCS extensions/Math.lua @@ -0,0 +1,351 @@ +-- ==================================================================================== +-- (DCS LUA ADD-ON) MATH - EXTENSION TO THE "MATH" TABLE +-- +-- (Constant) DCSEx.math.TWO_PI +-- DCSEx.math.addVec(vecA, vecB) +-- DCSEx.math.clamp(val, min, max) +-- DCSEx.math.getBearing(point, refPoint) +-- DCSEx.math.getDistance2D(vec2a, vec2b) +-- DCSEx.math.getDistance3D(vec3a, vec3b) +-- DCSEx.math.getRelativeHeading(point, refObject) +-- DCSEx.math.getVec2FromAngle(angle) +-- DCSEx.math.isPointInsideCircle(center, radius, vec2) +-- DCSEx.math.isPointInsidePolygon(polygon, vec2) +-- DCSEx.math.isSamePoint(pointA, pointB) +-- DCSEx.math.lerp(val0, val1, t) +-- DCSEx.math.multVec2(vec2, mult) +-- DCSEx.math.normalizeVec2(vec2) +-- DCSEx.math.randomBoolean() +-- DCSEx.math.randomFloat(min, max) +-- DCSEx.math.randomPointAtDistance(point, distance) +-- DCSEx.math.randomPointInCircle(center, radius, minRadius, surfaceType) +-- DCSEx.math.randomSign() +-- DCSEx.math.toBoolean(val) +-- DCSEx.math.vec2ToVec3(vec2, y) +-- DCSEx.math.vec3ToVec2(vec3) +-- ==================================================================================== + +DCSEx.math = {} + +------------------------------------- +-- Constants +------------------------------------- +DCSEx.math.TWO_PI = math.pi * 2 + +------------------------------------- +-- Returns the sum of two vec2 or vec3 +-- @param vecA A vector +-- @param vecB Another vector +-- @return The sum of both vectors +------------------------------------- +function DCSEx.math.addVec(vecA, vecB) + if vecA.z then + return { x = vecA.x + vecB.x, y = vecA.y + vecB.y, z = vecA.z + vecB.z } + end + + return { x = vecA.x + vecB.x, y = vecA.y + vecB.y } +end + +------------------------------------- +-- Clamp a number value between min and max +-- @param value The value to clamp +-- @param min Minimum allowed value +-- @param max Maximum allowed value +-- @return Clamped value +------------------------------------- +function DCSEx.math.clamp(val, min, max) + return math.max(min, math.min(max, val)) +end + +------------------------------------- +-- Gets the bearing between two vectors, in degrees +-- @param point A vec2/vec3 +-- @param refPoint Vec2/vec3 to use as a reference point +-- @return Bearing, as a number, in degrees +------------------------------------- +function DCSEx.math.getBearing(point, refPoint) + refPoint = refPoint or {x = 0, y = 0} + if point.z then point = DCSEx.math.vec3ToVec2(point) end -- Point is a vec3, convert it + if refPoint.z then refPoint = DCSEx.math.vec3ToVec2(refPoint) end -- Point is a vec3, convert it + + local bearing = math.atan2(point.y - refPoint.y, point.x - refPoint.x) + + if bearing < 0 then + bearing = bearing + DCSEx.math.TWO_PI + end + + return math.floor(bearing * (180 / math.pi)) +end + +------------------------------------- +-- Returns the pythagorean distance between two 2D points or the length of a single vector +-- @param vec2a A 2D point +-- @param vec2b (optional) Another 2D point +-- @return Distance between the points +------------------------------------- +function DCSEx.math.getDistance2D(vec2a, vec2b) + vec2b = vec2b or { x = 0, y = 0 } + if vec2a.z then vec2a = DCSEx.math.vec3ToVec2(vec2a) end + if vec2b.z then vec2b = DCSEx.math.vec3ToVec2(vec2b) end + + return math.sqrt((vec2a.x - vec2b.x) ^ 2 + (vec2a.y - vec2b.y) ^ 2) +end + +------------------------------------- +-- Returns the pythagorean distance between two 3D points or the length of a single vector +-- @param vec3a A 3D point +-- @param vec3b (optional) Another 3D point +-- @return Distance between the points +------------------------------------- +function DCSEx.math.getDistance3D(vec3a, vec3b) + vec3b = vec3b or { x = 0, y = 0, z = 0 } + + return math.sqrt((vec3a.x - vec3b.x) ^ 2 + (vec3a.y - vec3b.y) ^ 2 + (vec3a.z - vec3b.z) ^ 2) +end + +------------------------------------- +-- Returns the relative heading difference between refObject and a given point +-- @param point The point for which to check the relative heading +-- @param refObject The reference object against which relative heading should be measured +-- @param format (optional) Return format. Possible formats are "clock" (1 o'clock...) or "cardinal" (NNW...) +-- @return A number in degrees, or a string if a special format was provided +------------------------------------- +function DCSEx.math.getRelativeHeading(point, refObject, format) + if not point then return nil end + if not refObject then return nil end + + local unitpos = refObject:getPosition() + local bearing = DCSEx.math.getBearing(point, unitpos:getPoint()) + + local unitBearing = math.atan2(unitpos.x.z, unitpos.x.x) + if unitBearing < 0 then unitBearing = unitBearing + DCSEx.math.TWO_PI end + unitBearing = math.floor(unitBearing * (180 / math.pi)) + + local relBearing = bearing - unitBearing + if relBearing < 0 then relBearing = relBearing + 360 end + + if format == "oclock" then + local oClock = math.floor(relBearing / 30) + if oClock <= 0 then oClock = 12 end + + return tostring(oClock).." o'clock" + elseif format == "cardinal" then + return "TODO" -- TODO: cardinal + end + + return math.floor(relBearing) +end + +------------------------------------- +-- Returns an normalized vec2 from an angle/bearing in radians +-- @param unit Angle/bearing in radians +-- @return A normalized vec2 +------------------------------------- +function DCSEx.math.getVec2FromAngle(angle) + return {x = math.cos(angle), y = math.sin(angle)} +end + +------------------------------------- +-- Is a point inside a circle? +-- @param center The center of the circle, as a vec2 +-- @param radius The radius of the circle +-- @param vec2 A vec2 +-- @return True if vec2 is inside the circle, false otherwise +------------------------------------- +function DCSEx.math.isPointInsideCircle(center, radius, vec2) + return (vec2.x - center.x) ^ 2 + (vec2.y - center.y) ^ 2 < radius ^ 2 +end + +------------------------------------- +-- Is a point inside a polygon? +-- @param vec2[] A polygon, as a table of vec2 +-- @param vec2 A vec2 +-- @return True if vec2 is inside the polygon, false otherwise +------------------------------------- +function DCSEx.math.isPointInsidePolygon(polygon, vec2) + if not polygon or not vec2 then return false end + + local oddNodes = false + local j = #polygon + for i = 1, #polygon do + if (polygon[i].y < vec2.y and polygon[j].y >= vec2.y or polygon[j].y < vec2.y and polygon[i].y >= vec2.y) then + if + (polygon[i].x + (vec2.y - polygon[i].y) / (polygon[j].y - polygon[i].y) * (polygon[j].x - polygon[i].x) < + vec2.x) + then + oddNodes = not oddNodes + end + end + j = i + end + + return oddNodes +end + +------------------------------------- +-- Compares two 2D or 3D points +-- @param pointA a Point2 or Point3 +-- @param pointB another Point2 or Point3 +-- @return True if points are the same, false otherwise +------------------------------------- +function DCSEx.math.isSamePoint(pointA, pointB) + if pointA.x ~= pointB.x then return false end + if pointA.y ~= pointB.y then return false end + if pointA.z or pointB.z then + if pointA.z ~= pointB.z then return false end + end + + return true +end + +------------------------------------- +-- Linearly interpolates between two numbers +-- @param val0 Value vers l=0 +-- @param val1 Value vers l=1 +-- @param t Interpolation between 0 and 1 +-- @return A number value +------------------------------------- +function DCSEx.math.lerp(val0, val1, t) + return val0 + t * (val1 - val0); +end + +------------------------------------- +-- Multiplies both the x and y components of a vec2 by a floating-point value +-- @param vec2 A vec2 +-- @param mult A floating-point value +-- @return A vec2 +------------------------------------- +function DCSEx.math.multVec2(vec2, mult) + return {x = vec2.x * mult, y = vec2.y * mult} +end + +------------------------------------- +-- Returns an normalized vec2 +-- @param unit A vec2 +-- @return A normalized vec2 +------------------------------------- +function DCSEx.math.normalizeVec2(vec2) + local length = DCSEx.math.getDistance2D(vec2) + return {x = vec2.x / length, y = vec2.y / length} +end + +------------------------------------- +-- Returns a random boolean +-- @return A boolean +------------------------------------- +function DCSEx.math.randomBoolean() + return DCSEx.math.random(1, 2) == 1 +end + +------------------------------------- +-- Returns a random floating-point number between min and max +-- @param min Minimum floating-point value +-- @param max Maximum floating-point value +-- @return A number +------------------------------------- +function DCSEx.math.randomFloat(min, max) + if min >= max then + return min + end + + return min + math.random() * (max - min) +end + +------------------------------------- +-- Returns a random vec2 at a given distance of another vec2 +-- @param point Reference point +-- @param distance Distance from the reference point +-- @return A vec2 +------------------------------------- +function DCSEx.math.randomPointAtDistance(point, distance) + local angle = math.random() * math.pi * 2.0 + + local x = point.x + math.cos(angle) * distance + local y = point.y + math.sin(angle) * distance + return { x = x, y = y } +end + +------------------------------------- +-- Returns a random vec2 in circle of a given center and radius +-- @param center Center of the circle as a vec2 +-- @param radius Radius of the circle +-- @param minRadius (optional) Minimum inner radius circle in which points should not be spawned +-- @param surfaceType (optional) If not nil, point must be of given surface type +-- @return A vec2 or nil if no point was found +------------------------------------- +function DCSEx.math.randomPointInCircle(center, radius, minRadius, surfaceType) + local minRadius = minRadius or 0 + local dist = minRadius + math.random() * (radius - minRadius) + local angle = math.random() * math.pi * 2.0 + + local point = nil + for i=1,36 do + local x = center.x + math.cos(angle) * dist + local y = center.y + math.sin(angle) * dist + + point = { x = x, y = y } + + if not surfaceType then return point end + if land.getSurfaceType(point) == surfaceType then return point end + angle = angle + 0.174533 -- Increment angle by 10° (in radians) + end + + return nil +end + +------------------------------------- +-- Returns a random sign as a number, -1 or 1 +-- @return -1 50% of the time, 1 50% of the time +------------------------------------- +function DCSEx.math.randomSign() + if math.random() < .5 then + return -1 + end + return 1 +end + +------------------------------------- +-- Converts a value to a boolean +-- @param val Value to convert +-- @return A boolean, or nil if val was nil +------------------------------------- +function DCSEx.math.toBoolean(val) + if val == nil then return nil end + if not val then return false end + if val == 0 then return false end + if val:lower() == "false" or val:lower() == "no" or val:lower() == "off" then return false end + + return true +end + +------------------------------------- +-- Converts a vec2 to a vec3 +-- @param vec2 A vec2 +-- @param y (Optional) A value for the vec3's y component or "land" to use land height +-- @return A vec3 where v3.x=v2.x, v3.y=y and v3.z=v2.y +------------------------------------- +function DCSEx.math.vec2ToVec3(vec2, y) + -- Value was already a vec3 + if vec2.z then + return {x = vec2.x, y = vec2.y, z = vec2.z} + end + + y = y or 0 + if y == "land" then y = land.getHeight(vec2) end + + return {x = vec2.x, y = y, z = vec2.y} +end + +------------------------------------- +-- Converts a vec3 to a vec2 +-- @param vec3 A vec3 +-- @return A vec2 where v2.x=v3.x and v2.y=v3.z +------------------------------------- +function DCSEx.math.vec3ToVec2(vec3) + -- Value was already a vec2 + if not vec3.z then + return {x = vec3.x, y = vec3.y} + end + + return {x = vec3.x, y = vec3.z} +end diff --git a/Script/DCS extensions/String.lua b/Script/DCS extensions/String.lua new file mode 100644 index 0000000..ec38f6e --- /dev/null +++ b/Script/DCS extensions/String.lua @@ -0,0 +1,87 @@ +-- ==================================================================================== +-- (DCS LUA ADD-ON) STRING - EXTENSION TO THE "STRING" TABLE +-- +-- DCSEx.string.firstToUpper(str) +-- DCSEx.string.getReadingTime(message) +-- DCSEx.string.split(str, separator) +-- DCSEx.string.startsWith(haystack, needle) +-- DCSEx.string.trim(str) +-- ==================================================================================== + +DCSEx.string = {} + +------------------------------------- +-- Uppercases the fist letter of a string +-- @param str A string +-- @return A string, with the first letter cast to upper case +------------------------------------- +function DCSEx.string.firstToUpper(str) + return (str:gsub("^%l", DCSEx.string.upper)) +end + +------------------------------------- +-- Estimates the time (in seconds) required to read a string +-- @param message A text message +-- @return A duration in seconds +------------------------------------- +function DCSEx.string.getReadingTime(message) + message = message or "" + message = tostring(message) + + return DCSEx.math.clamp(#message / 8.7, 3.0, 15.0) -- 10.7 letters per second, minimum length 3 seconds, max length 15 seconds +end + +-- TODO: description, update file header +function DCSEx.string.join(table, separator) + local joinedString = "" + + for i=1,#table do + joinedString = joinedString..tostring(table[i]) + if i < #table then + joinedString = joinedString..separator + end + end + + return joinedString +end + +-- TODO: description, file header +-- Code from https://stackoverflow.com/questions/10989788/format-integer-in-lua +function DCSEx.string.toStringThousandsSeparator(number) + local i, j, minus, int, fraction = tostring(number):find('([-]?)(%d+)([.]?%d*)') + int = int:reverse():gsub("(%d%d%d)", "%1,") + return minus .. int:reverse():gsub("^,", "") .. fraction +end + +------------------------------------- +-- Splits a string +-- @param str The string to split +-- @param separator The string to split +-- @return A table of split strings +------------------------------------- +function DCSEx.string.split(str, separator) + separator = separator or "%s" + + local t = {} + for s in DCSEx.string.gmatch(str, "([^"..separator.."]+)") do table.insert(t, s) end + return t +end + +------------------------------------- +-- Does a string starts with the given substring? +-- @param haystack The string +-- @param needle The substring to look for +-- @return True if it starts with the substring, false otherwise +------------------------------------- +function DCSEx.string.startsWith(haystack, needle) + return haystack:sub(1, #needle) == needle +end + +------------------------------------- +-- Trims a string +-- @param str A string +-- @return A string +------------------------------------- +function DCSEx.string.trim(str) + return (str:gsub("^%s*(.-)%s*$", "%1")) +end diff --git a/Script/DCS extensions/Table.lua b/Script/DCS extensions/Table.lua new file mode 100644 index 0000000..5469e95 --- /dev/null +++ b/Script/DCS extensions/Table.lua @@ -0,0 +1,222 @@ +-- ==================================================================================== +-- (DCS LUA ADD-ON) TABLE - EXTENSION TO THE "TABLE" TABLE +-- +-- DCSEx.table.contains(t, val) +-- DCSEx.table.containsKey(t, k) +-- DCSEx.table.containsAll(t, values) +-- DCSEx.table.containsAny(t, values) +-- DCSEx.table.countNonNils(t) +-- DCSEx.table.deepCopy(orig) +-- DCSEx.table.dump(t) +-- DCSEx.table.getKeys(t) +-- DCSEx.table.getKeyFromValue(t, val) +-- DCSEx.table.getRandom(t) +-- DCSEx.table.getRandomIndex(t) +-- DCSEx.table.shuffle(t) +-- ==================================================================================== + +DCSEx.table = {} + +------------------------------------- +-- Returns true if table t contains value val +-- @param t A table +-- @param val A value +-- @return True if the table contains the value, false otherwise +------------------------------------- +function DCSEx.table.contains(t, val) + if not t then return false end + + for _,v in pairs(t) do + if v == val then return true end + end + + return false +end + +------------------------------------- +-- Returns true if table t contains key k +-- @param t A table +-- @param k A key +-- @return True if the table contains the key, false otherwise +------------------------------------- +function DCSEx.table.containsKey(t, k) + if not t then return false end + if t[k] then return true end + + return false +end + +------------------------------------- +-- Returns true if table t contains all values in table values +-- @param t A table +-- @param values A table of values +-- @return True if the table contains all values, false otherwise +------------------------------------- +function DCSEx.table.containsAll(t, values) + if not t then return false end + + for _,v in pairs(values) do + if not DCSEx.table.contains(t, v) then + return false + end + end + + return true +end + +------------------------------------- +-- Returns true if table t contains at least one value in table values +-- @param t A table +-- @param values A table of values +-- @return True if the table contains at least one value, false otherwise +------------------------------------- +function DCSEx.table.containsAny(t, values) + if not t then return false end + + for _,v in pairs(values) do + if DCSEx.table.contains(t, v) then + return true + end + end + + return false +end + +function DCSEx.table.containsAllKeys(t, keys) + if not t then return false end + + for _,k in pairs(keys) do + if not DCSEx.table.containsKey(t, k) then + return false + end + end + + return true +end + +function DCSEx.table.containsAnyKeys(t, keys) + if not t then return false end + + for _,k in pairs(keys) do + if DCSEx.table.containsKey(t, k) then + return true + end + end + + return false +end + +------------------------------------- +-- Returns the number of non-nils elements in a table +-- @param t A table +-- @return A number +------------------------------------- +function DCSEx.table.countNonNils(t) + local count = 0 + for _,v in pairs(t) do + if v ~= nil then count = count + 1 end + end + + return count +end + +------------------------------------- +-- Returns a deep copy of the table, doesn't work with recursive tables (code from http://lua-users.org/wiki/CopyTable) +-- @param orig A table +-- @return A deep copied clone of the table +------------------------------------- +function DCSEx.table.deepCopy(orig) + -- Not a table + if type(orig) ~= "table" then + return orig + end + + local copy = {} + for orig_key, orig_value in next, orig, nil do + copy[DCSEx.table.deepCopy(orig_key)] = DCSEx.table.deepCopy(orig_value) + end + setmetatable(copy, DCSEx.table.deepCopy(getmetatable(orig))) + + return copy +end + +------------------------------------- +-- Dumps the content of a table as a string +-- @param orig A table +-- @return A string representaton of the table +------------------------------------- +function DCSEx.table.dump(t) + if type(t) == 'table' then + local s = '{ ' + for k,v in pairs(t) do + if type(k) ~= 'number' then k = '"'..k..'"' end + s = s .. '['..k..'] = ' .. DCSEx.table.dump(v) .. ',' + end + return s .. '} ' + else + return tostring(t) + end + end + +------------------------------------- +-- Returns the key associated to a value in a table, or nil if not found +-- @param t A table +-- @param val A value +-- @return The key associated to this value in the table, or nil +------------------------------------- +function DCSEx.table.getKeyFromValue(t, val) + for k, v in pairs(t) do + if v == val then return k end + end + return nil +end + +------------------------------------- +-- Returns all the keys in an associative table +-- @param t A table +-- @return An array of keys +------------------------------------- +function DCSEx.table.getKeys(t) + local keys = {} + local n = 1 + + for k,_ in pairs(t) do + keys[n] = k + n = n + 1 + end + + table.sort(keys) + return keys +end + +------------------------------------- +-- Returns a random value from a numerically-indexed table +-- @param t A table +-- @return A random element from the table +------------------------------------- +function DCSEx.table.getRandom(t) + return t[math.random(#t)] +end + +------------------------------------- +-- Returns a random index from a numerically-indexed table +-- @param t A table +-- @return A random index from the table +------------------------------------- +function DCSEx.table.getRandomIndex(t) + return math.random(#t) +end + +------------------------------------- +-- Randomly shuffles a numerically-indexed table +-- @param t A table +-- @return A table with shuffled values +------------------------------------- +function DCSEx.table.shuffle(t) + local len, random = #t, math.random + for i = len, 2, -1 do + local j = random(1, i) + t[i], t[j] = t[j], t[i] + end + return t +end diff --git a/Script/DCS extensions/UnitCallsignMaker.lua b/Script/DCS extensions/UnitCallsignMaker.lua new file mode 100644 index 0000000..d00e5b7 --- /dev/null +++ b/Script/DCS extensions/UnitCallsignMaker.lua @@ -0,0 +1,213 @@ +DCSEx.unitCallsignMaker = {} + +do + local CALLSIGN_TYPE = { + DEFAULT = 1, + A10 = 2, + AH64 = 3, + AWACS = 4, + B1B = 5, + B52 = 6, + F15E = 7, + F16 = 8, + F18 = 9, + JTAC = 10, + TANKER = 11, + TRANSPORT = 12, + } + + local CALLSIGNS = { + [CALLSIGN_TYPE.DEFAULT] = { + [1] = "Enfield", + [2] = "Springfield", + [3] = "Uzi", + [4] = "Colt", + [5] = "Dodge", + [6] = "Ford", + [7] = "Chevy", + [8] = "Pontiac", + }, + [CALLSIGN_TYPE.A10] = { + [9] = "Hawg", + [10] = "Boar", + [11] = "Pig", + [12] = "Tusk", + }, + [CALLSIGN_TYPE.AH64] = { + [9] = "Army Air", + [10] = "Apache", + [11] = "Crow", + [12] = "Sioux", + [13] = "Gatling", + [14] = "Gunslinger", + [15] = "Hammerhead", + [16] = "Bootleg", + [17] = "Palehorse", + [18] = "Carnivor", + [19] = "Saber", + }, + [CALLSIGN_TYPE.AWACS] = { + [1] = "Overlord", + [2] = "Magic", + [3] = "Wizard", + [4] = "Focus", + [5] = "Darkstar", + }, + [CALLSIGN_TYPE.B1B] = { + [9] = "Bone", + [10] = "Dark", + [11] = "Vader", + }, + [CALLSIGN_TYPE.B52] = { + [9] = "Buff", + [10] = "Dump", + [11] = "Kenworth", + }, + [CALLSIGN_TYPE.F15E] = { + [9] = "Dude", + [10] = "Thud", + [11] = "Gunny", + [12] = "Trek", + [13] = "Sniper", + [14] = "Sled", + [15] = "Best", + [16] = "Jazz", + [17] = "Rage", + [18] = "Tahoe", + }, + [CALLSIGN_TYPE.F16] = { + [9] = "Viper", + [10] = "Venom", + [11] = "Lobo", + [12] = "Cowboy", + [13] = "Python", + [14] = "Rattler", + [15] = "Panther", + [16] = "Wolf", + [17] = "Weasel", + [18] = "Wild", + [19] = "Ninja", + [20] = "Jedi", + }, + [CALLSIGN_TYPE.F18] = { + [9] = "Hornet", + [10] = "Squid", + [11] = "Ragin", + [12] = "Roman", + [13] = "Sting", + [14] = "Jury", + [15] = "Joker", + [16] = "Ram", + [17] = "Hawk", + [18] = "Devil", + [19] = "Check", + [20] = "Snake", + }, + [CALLSIGN_TYPE.JTAC] = { + [1] = "Axeman", + [2] = "Darknight", + [3] = "Warrior", + [4] = "Pointer", + [5] = "Eyeball", + [6] = "Moonbeam", + [7] = "Whiplash", + [8] = "Finger", + [9] = "Pinpoint", + [10] = "Ferret", + [11] = "Shaba", + [12] = "Playboy", + [13] = "Hammer", + [14] = "Jaguar", + [15] = "Deathstar", + [16] = "Anvil", + [17] = "Firefly", + [18] = "Mantis", + [19] = "Badger" + }, + [CALLSIGN_TYPE.TANKER] = { + [1] = "Texaco", + [2] = "Arco", + [3] = "Shell", + }, + [CALLSIGN_TYPE.TRANSPORT] = { + [9] = "Heavy", + [10] = "Trash", + [11] = "Cargo", + [12] = "Ascot", + }, + } + + local currentCallsigns = {} + for _,i in pairs(CALLSIGN_TYPE) do + currentCallsigns[i] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 } + end + + local function getCallsignTypeByUnitType(unitType) + if not unitType then return CALLSIGN_TYPE.DEFAULT end + + -- TODO: complete + if unitType == "AH-64A" or unitType == "AH-64D" or unitType == "AH-64D_BLK_II" then return CALLSIGN_TYPE.A10 end + if unitType == "A-10A" or unitType == "A-10C" or unitType == "A-10C_2" then return CALLSIGN_TYPE.A10 end + if unitType == "F-15E" or unitType == "F-15ESE" then return CALLSIGN_TYPE.F15E end + if unitType == "F-16A" or unitType == "F-16A MLU" or unitType == "F-16C bl.50" or unitType == "F-16C bl.52d" or unitType == "F-16C_50" then return CALLSIGN_TYPE.F16 end + if unitType == "FA-18C" or unitType == "FA-18C_hornet" then return CALLSIGN_TYPE.F18 end + -- if unitType == "A-50" or unitType == "E-2C" or unitType == "E-3A" then return CALLSIGN_TYPE.AWACS end + + local unitDesc = Unit.getDescByName(unitType) + if not unitDesc then return CALLSIGN_TYPE.DEFAULT end + + if unitDesc.attributes["AWACS"] then return CALLSIGN_TYPE.AWACS end + if unitDesc.attributes["Tankers"] then return CALLSIGN_TYPE.TANKER end + + return CALLSIGN_TYPE.DEFAULT + end + + local function incrementCallsign(callsignName, callsignNumber) + if not callsignName then return end + callsignNumber = math.max(1, callsignNumber or 1) + + for k,_ in pairs(CALLSIGNS) do + for i,_ in pairs(CALLSIGNS[k]) do + if CALLSIGNS[k][i]:lower() == callsignName:lower() then + currentCallsigns[k][i] = math.max(currentCallsigns[k][i], callsignNumber + 1) + if (currentCallsigns[k][i] > 9) then currentCallsigns[k][i] = 1 end -- More than 9? Loop back to 1. + -- TUM.log("Callsign "..CALLSIGNS[k][i].." set to "..tostring(callsignNumber)) + return + end + end + end + end + + function DCSEx.unitCallsignMaker.getCallsign(unitType) + local csType = getCallsignTypeByUnitType(unitType) + + local csNameIndex = DCSEx.table.getRandom(DCSEx.table.getKeys(CALLSIGNS[csType])) + if not currentCallsigns[csType][csNameIndex] then currentCallsigns[csType][csNameIndex] = 1 end + local csNumber = currentCallsigns[csType][csNameIndex] + + currentCallsigns[csType][csNameIndex] = currentCallsigns[csType][csNameIndex] + 1 + if currentCallsigns[csType][csNameIndex] > 9 then currentCallsigns[csType][csNameIndex] = 1 end + + local callsignTable = { + [1] = csNameIndex, + [2] = csNumber, + -- [3] = 1, + ["name"] = CALLSIGNS[csType][csNameIndex]..tostring(csNumber), + } + + return callsignTable + end + + do + local missionGroups = DCSEx.envMission.getGroups() + for _,g in ipairs(missionGroups) do + if g.units and g.units[1] then + local unit = g.units[1] + if unit.callsign and unit.callsign[1] and unit.callsign.name then + local callsignName = unit.callsign.name:sub(1, #unit.callsign.name - 2) + incrementCallsign(callsignName, unit.callsign[2]) + end + end + end + end +end diff --git a/Script/DCS extensions/UnitGroupMaker.lua b/Script/DCS extensions/UnitGroupMaker.lua new file mode 100644 index 0000000..2f78528 --- /dev/null +++ b/Script/DCS extensions/UnitGroupMaker.lua @@ -0,0 +1,453 @@ +-- ==================================================================================== +-- DCSEX.UNITGROUMAKER - CREATES AND ADDS GROUPS TO THE GAME WORLD +-- +-- (local) createGroupTable(groupID, groupCategory, options) +-- (local) getDefaultUnitSpread(groupCategory) +-- (local) getNextGroupID() +-- (local) getNextUnitID() +-- (local) setAircraftTaskAwacs(groupTable) +-- (local) setAircraftTaskOrbit(groupTable, options) +-- (local) setCommand(groupTable, actionID, actionValue) +-- (local) setOption(groupTable, optionID, optionValue) +-- DCSEx.unitGroupMaker.create(coalitionID, groupCategory, vec2, unitTypes, options) +-- DCSEx.unitGroupMaker.initialize() +-- ==================================================================================== + +DCSEx.unitGroupMaker = {} + +do + local nextGroupID = 1 -- ID of the next generated group + local nextUnitID = 1 -- ID of the next generated unit + + local function createGroupTable(groupID, groupCategory, options) + local groupTable = { + groupId = groupID, + hidden = options.hidden, + name = options.name, + route = {points = {}}, + start_time = 0, + taskSelected = true, + uncontrollable = true, + visible = false, + units = {}, + } + + if groupCategory == Group.Category.GROUND then + groupTable.task = "Ground Nothing" + groupTable.tasks = {} + elseif groupCategory == Group.Category.AIRPLANE or groupCategory == Group.Category.HELICOPTER then + groupTable.uncontrolled = options.uncontrolled or false + end + + return groupTable + end + + local function getDefaultUnitSpread(groupCategory) + if groupCategory == Group.Category.AIRPLANE or groupCategory == Group.Category.HELICOPTER then + return 150 -- TODO: improve + elseif groupCategory == Group.Category.SHIP then + return math.random(250, 400) + else + return math.random(20, 30) + end + end + + -- Returns the next available group ID and increment the number + local function getNextGroupID() + nextGroupID = nextGroupID + 1 + return nextGroupID - 1 + end + + -- Returns the next available unit ID and increment the number + local function getNextUnitID() + nextUnitID = nextUnitID + 1 + return nextUnitID - 1 + end + + local function setAircraftTaskAwacs(groupTable) + groupTable.frequency = 251000000 + groupTable.task = "AWACS" + + table.insert(groupTable.route.points[1].task.params.tasks, + { + ["enabled"] = true, + ["auto"] = true, + ["id"] = "AWACS", + ["number"] = #groupTable.route.points[1].task.params.tasks + 1, + ["params"] = { }, + }) + + return groupTable + end + + local function setAircraftTaskCAP(groupTable) + groupTable.task = "CAP" + + table.insert(groupTable.route.points[1].task.params.tasks, + { + ["enabled"] = true, + ["auto"] = true, + ["id"] = "EngageTargets", + ["number"] = #groupTable.route.points[1].task.params.tasks + 1, + ["params"] = { + maxDist = DCSEx.converter.nmToMeters(60), + maxDistEnabled = false, + -- targetTypes = { "Planes", "Helicopters" }, + targetTypes = { "Fighters", "Interceptors", "Multirole fighters" }, + priority = 0 + }, + }) + + return groupTable + end + + local function setAircraftTaskOrbit(groupTable, options) + -- TODO: oval orbit + table.insert(groupTable.route.points[#groupTable.route.points].task.params.tasks, + { + ["enabled"] = true, + ["auto"] = false, + ["id"] = "Orbit", + ["number"] = #groupTable.route.points[#groupTable.route.points].task.params.tasks + 1, + ["params"] = + { + ["altitude"] = options.altitude, + ["pattern"] = "Circle", + ["speed"] = options.speed, + }, + }) + + return groupTable + end + + local function setCommand(groupTable, actionID, actionValue) + table.insert( + groupTable.route.points[1].task.params.tasks, + { + ["auto"] = false, + ["enabled"] = true, + ["id"] = "WrappedAction", + ["number"] = #groupTable.route.points[1].task.params.tasks + 1, + ["params"] = { ["action"] = { ["id"] = actionID, ["params"] = { ["value"] = actionValue, }, }, }, + }) + end + + local function setOption(groupTable, optionID, optionValue) + table.insert( + groupTable.route.points[1].task.params.tasks, + { + ["auto"] = false, + ["enabled"] = true, + ["id"] = "WrappedAction", + ["number"] = #groupTable.route.points[1].task.params.tasks + 1, + ["params"] = { ["action"] = { ["id"] = "Option", ["params"] = { ["name"] = optionID, ["value"] = optionValue, }, }, }, + }) + end + + function DCSEx.unitGroupMaker.createStatic(side, point2, typeName, shapeName, heading, dead) + heading = heading or DCSEx.converter.degreesToRadians(math.random(0, 359)) + dead = dead or false + local unitID = getNextUnitID() + + local staticObj = { + ["heading"] = 0, + ["groupId"] = getNextGroupID(), + ["shape_name"] = shapeName, + ["type"] = typeName, + ["unitId"] = unitID, + ["rate"] = 100, + ["name"] = "Static #"..tostring(unitID), + ["category"] = "Fortifications", + ["y"] = point2.y, + ["x"] = point2.x, + ["dead"] = dead, + } + + coalition.addStaticObject(DCSEx.dcs.getCJTFForCoalition(side), staticObj) + + return unitID + end + + function DCSEx.unitGroupMaker.create(coalitionID, groupCategory, vec2, unitTypes, options) + if not unitTypes or #unitTypes == 0 then return nil end -- No unit types provided + if type(unitTypes) == "string" then unitTypes = { unitTypes } end -- Single unit type provided, make it a table of size 1 + + local aircraftDB = nil -- Aircraft entry in the DB for airplane/helicopter units + local destVec2 = nil -- Destination point (for moving units) + local groupID = getNextGroupID() -- Get a new unique ID for the group + local isAirUnit = false + + local hidden = false + if not TUM.DEBUG_MODE and coalitionID ~= TUM.settings.getPlayerCoalition() then + hidden = true + end + + -- Setup options + options = options or {} + --options.heading = nil + options.altitude = options.altitude or 0 + options.altitudeType = options.altitudeType or "BARO" + options.hidden = hidden + options.isMoving = false + options.livery = options.livery or "default" + options.name = DCSEx.unitNamesMaker.getName(groupID, unitTypes) + options.pointAction = "Turning Point" + options.skill = options.skill or "Average" + options.speed = 5.5555556 + options.spreadDistance = options.spreadDistance or getDefaultUnitSpread(groupCategory) + options.spreadOffset = options.spreadOffset or 0 + + -- Movement point (for units with a second WP) + if options.moveTo then + options.isMoving = true + destVec2 = DCSEx.table.deepCopy(options.moveTo) + elseif options.moveBy then + options.isMoving = true + local angle = DCSEx.converter.degreesToRadians(math.random(0, 359)) + destVec2 = { x = vec2.x + math.cos(angle) * options.moveBy, y = vec2.y + math.sin(angle) * options.moveBy } + end + + -- Category specific options + if groupCategory == Group.Category.GROUND then + options.pointAction = "Off Road" + + local desc = Unit.getDescByName(unitTypes[1]) + if desc and desc.attributes and desc.attributes.Infantry then options.speed = 1.66667 end + + -- Check position and formation for moving ground units + if options.isMoving then + if options.onRoad then + options.pointAction = "On Road" + vec2 = DCSEx.world.getClosestPointOnRoadsVec2(vec2) + destVec2 = DCSEx.world.getClosestPointOnRoadsVec2(destVec2) + else + options.pointAction = options.formation or DCSEx.table.getRandom({"Rank", "Cone", "Vee", "Diamond", "EchelonL", "EchelonR"}) + end + end + + options.livery = "default" -- TODO: getSeasonalLivery() + elseif groupCategory == Group.Category.AIRPLANE or groupCategory == Group.Category.HELICOPTER then + isAirUnit = true + + -- Plane/helicopter groups always use a single unit type + for i=1,#unitTypes do + unitTypes[i] = unitTypes[1] + end + + -- Get unit info from aircraft database + aircraftDB = Library.aircraft[unitTypes[1]] + if not aircraftDB then return nil end -- Unit wasn't found in the database, abort group creation + + -- Pick a random livery if available + -- if aircraftDB.liveries and aircraftDB.liveries[coalitionID] then + -- options.livery = table.getRandom(aircraftDB.liveries[coalitionID]) + -- end + + options.altitude = aircraftDB.altitude or DCSEx.converter.feetToMeters(15000) + options.altitudeType = "BARO" + options.speed = DCSEx.math.randomFloat(0.9, 1.1) * (aircraftDB.speed or 250) + end + + -- First unit of the group is a template, use a group template instead of enumerating the types in unitTypes + local groupTemplate = nil + if Library.groupTemplates[unitTypes[1]] then + groupTemplate = DCSEx.table.deepCopy(Library.groupTemplates[unitTypes[1]]) + unitTypes = {} + for i=1,#groupTemplate do + table.insert(unitTypes, groupTemplate[i].name) + end + end + + -- Create group table + local groupTable = createGroupTable(groupID, groupCategory, options) + groupTable.x = vec2.x + groupTable.y = vec2.y + + -- Initial waypoint + table.insert( + groupTable.route.points, + { + action = options.pointAction, + alt = options.altitude, + alt_type = options.altitudeType, + ETA = 0.0, + ETA_locked = false, + formation_template = "", + name = "WP1", + speed = options.speed, + speed_locked = true, + task = {id = "ComboTask", params = {tasks = {}}}, + type = "Turning Point", + x = vec2.x, + y = vec2.y + } + ) + + if options.takeOff and (groupCategory == Group.Category.AIRPLANE or groupCategory == Group.Category.HELICOPTER) then + groupTable.route.points[1].alt = 250 + groupTable.route.points[1].alt_type = "RADIO" + end + + if destVec2 then -- There's a destination, add a second waypoint + local destPoint = { + action = options.pointAction, + alt_type = options.altitudeType, + alt = options.altitude, + ETA = 0.0, + ETA_locked = false, + formation_template = "", + name = "WP2", + speed = options.speed, + task = { + id = "ComboTask", + params = { tasks = { } } + }, + type = "Turning Point", + x = destVec2.x, + y = destVec2.y, + speed_locked = true + } + + -- Ground/ship groups loop between their waypoints + if groupCategory == Group.Category.GROUND or groupCategory == Group.Category.SHIP then + if not options.noLoop then + table.insert( + destPoint.task.params.tasks, + { + enabled = true, + auto = false, + id = "GoToWaypoint", + number = 1, + params = { + fromWaypointIndex = 2, + nWaypointIndx = 1 + } + }) + end + end + + table.insert(groupTable.route.points, destPoint) + end + + -- Add various options/commands + if options.disableWeapons then setOption(groupTable, AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD) end -- Values are from the AI.Option.Ground tables, but they're the same for all (ROE=0, WEAPON_HOLD=4) + if options.immortal then setCommand(groupTable, "SetImmortal", true) end + if options.invisible then setCommand(groupTable, "SetInvisible", true) end + if options.silenced then setOption(groupTable, AI.Option.Air.id.SILENCE, true) end + if options.unlimitedFuel then setCommand(groupTable, "SetUnlimitedFuel", true) end + + local groupCallsign = nil + + if isAirUnit then + if options.taskAwacs then setAircraftTaskAwacs(groupTable) end + if options.taskCAP then setAircraftTaskCAP(groupTable) end + setAircraftTaskOrbit(groupTable, options) + groupCallsign = DCSEx.unitCallsignMaker.getCallsign(unitTypes[1]) + groupTable.name = groupCallsign.name + end + + local unitsID = {} + + ----------------------- + -- Create units + ----------------------- + for i=1,#unitTypes do + local unitID = getNextUnitID() + table.insert(unitsID, unitID) + + local unitHeading, unitOffset + + if groupTemplate then -- Use offset and heading from the group + unitHeading = groupTemplate[i].heading + unitOffset = { x = groupTemplate[i].dx, y = groupTemplate[i].dy } + else -- Create offset and heading + unitHeading = options.heading or DCSEx.math.randomFloat(0, DCSEx.math.TWO_PI) + unitOffset = + DCSEx.math.multVec2( + DCSEx.math.getVec2FromAngle(unitHeading), + options.spreadDistance * (options.spreadOffset + i - 1) + ) + end + + local unitType = unitTypes[i] + + local unitTable = { + coldAtStart = false, + heading = unitHeading, + livery = options.livery, + name = options.name.." #"..tostring(i), + playerCanDrive = false, + skill = options.skill, + transportable = {randomTransportable = false}, + type = unitType, + unitId = unitID, + x = vec2.x + unitOffset.x, + y = vec2.y + unitOffset.y + } + + if isAirUnit and aircraftDB then + unitTable.hardpoint_racks = true + unitTable.psi = 1.7703702498393 + + unitTable.callsign = DCSEx.table.deepCopy(groupCallsign) + unitTable.callsign.name = unitTable.callsign.name..tostring(i) + unitTable.callsign[3] = i + unitTable.name = unitTable.callsign.name + + -- Special properties for unit + if aircraftDB.properties then + unitTable.AddPropAircraft = DCSEx.table.deepCopy(aircraftDB.properties) + end + + -- Common payload (fuel, gun ammo, etc) + if aircraftDB.payload then + unitTable.payload = DCSEx.table.deepCopy(aircraftDB.payload) + end + + if aircraftDB.pylons then + -- TODO: pylons according to task + if options.taskAttack and aircraftDB.pylons.attack then + unitTable.payload.pylons = DCSEx.table.deepCopy(aircraftDB.pylons.attack) + elseif options.taskCAP and aircraftDB.pylons.cap then + unitTable.payload.pylons = DCSEx.table.deepCopy(aircraftDB.pylons.cap) + elseif options.taskSEAD and aircraftDB.pylons.sead then + unitTable.payload.pylons = DCSEx.table.deepCopy(aircraftDB.pylons.sead) + elseif options.taskStrike and aircraftDB.pylons.strike then + unitTable.payload.pylons = DCSEx.table.deepCopy(aircraftDB.pylons.strike) + else + unitTable.payload.pylons = DCSEx.table.deepCopy(aircraftDB.pylons.default) + end + end + end + + table.insert(groupTable.units, unitTable) + end + + coalition.addGroup(DCSEx.dcs.getCJTFForCoalition(coalitionID), groupCategory, groupTable) + + return { + callsign = groupCallsign, + groupID = groupID, + unitsID = unitsID, + unitTypeNames = DCSEx.table.deepCopy(unitTypes) -- Not always the same as the unitTypes input parameter, because of group templates + } + end + + ------------------------------------------------------ + -- INITIALIZE THE GROUP MAKER + -- Look for maximum groupID and unitID already in use to make sure we don't use an existing ID when spawning units + ------------------------------------------------------ + local groupData = DCSEx.envMission.getGroups() + + for _,g in ipairs(groupData) do + if g.groupId >= nextGroupID then + nextGroupID = g.groupId + 1 + end + + for __,u in ipairs(g.units) do + if u.unitId >= nextUnitID then + nextUnitID = u.unitId + 1 + end + end + end +end diff --git a/Script/DCS extensions/UnitNamesMaker.lua b/Script/DCS extensions/UnitNamesMaker.lua new file mode 100644 index 0000000..e351556 --- /dev/null +++ b/Script/DCS extensions/UnitNamesMaker.lua @@ -0,0 +1,92 @@ +DCSEx.unitNamesMaker = {} + +do + local function getSectionNameSuffixAir() + return DCSEx.table.getRandom({"squadron", "wing"}) + end + + local function getSectionNameSuffixGround() + return DCSEx.table.getRandom({"bataillon", "brigade", "corps", "division", "regiment"}) + end + + local function getSectionNameSuffixNaval() + return DCSEx.table.getRandom({"fleet", "force"}) + end + + local function getDefaultSectionName() + return "unit "..getSectionNameSuffixGround() + end + + local function getNameByDesc(unitDesc) + if not unitDesc then return getDefaultSectionName() end + + if unitDesc.attributes["UAVs"] then + return DCSEx.table.getRandom({"unmanned"}).." "..getSectionNameSuffixAir() + end + if unitDesc.attributes["Bombers"] then + return DCSEx.table.getRandom({"bomber"}).." "..getSectionNameSuffixAir() + end + if unitDesc.attributes["Fighters"] or unitDesc.attributes["Interceptors"] or unitDesc.attributes["Battle airplanes"] then + return DCSEx.table.getRandom({"fighter", "fighting"}).." "..getSectionNameSuffixAir() + end + if unitDesc.attributes["Planes"] then + return DCSEx.table.getRandom({"air", "cargo", "transport"}).." "..getSectionNameSuffixAir() + end + + if unitDesc.attributes["Attack helicopters"] then + return DCSEx.table.getRandom({"assault", "rotary"}).." "..getSectionNameSuffixAir() + end + if unitDesc.attributes["Helicopters"] then + return DCSEx.table.getRandom({"tactical"}).." "..getSectionNameSuffixAir() + end + + if unitDesc.attributes["Aircraft Carriers"] then + return DCSEx.table.getRandom({"carrier"}).." "..getSectionNameSuffixNaval() + end + if unitDesc.attributes["Unarmed ships"] then + return DCSEx.table.getRandom({"cargo", "logistical"}).." "..getSectionNameSuffixNaval() + end + if unitDesc.attributes["Ships"] then + return DCSEx.table.getRandom({"expeditionary", "assault"}).." "..getSectionNameSuffixNaval() + end + + if unitDesc.attributes["MANPADS AUX"] or unitDesc.attributes["MANPADS"] or unitDesc.attributes["Armed Air Defence"] or unitDesc.attributes["SAM elements"] or unitDesc.attributes["SAM related"] or unitDesc.attributes["SAM"] then + return DCSEx.table.getRandom({"air defense"}).." "..getSectionNameSuffixGround() + end + + if unitDesc.attributes["Tanks"] or unitDesc.attributes["HeavyArmoredUnits"] or unitDesc.attributes["LightArmoredUnits"] then + return DCSEx.table.getRandom({"armor", "heavy"}).." "..getSectionNameSuffixGround() + end + if unitDesc.attributes["LightArmoredUnits"] then + return DCSEx.table.getRandom({"armored", "mechanized"}).." "..getSectionNameSuffixGround() + end + if unitDesc.attributes["Ground vehicles"] then + return DCSEx.table.getRandom({"engineer","logistical","support","tactical"}).." "..getSectionNameSuffixGround() + end + if unitDesc.attributes["Infantry"] then + return DCSEx.table.getRandom({"infantry"}).." "..getSectionNameSuffixGround() + end + + return getDefaultSectionName() + end + + function DCSEx.unitNamesMaker.getName(groupID, unitTypes) + local nameNumber = tostring(groupID) + + if (nameNumber:sub(-1) == "1") then nameNumber = nameNumber.."st" + elseif (nameNumber:sub(-1) == "2") then nameNumber = nameNumber.."nd" + elseif (nameNumber:sub(-1) == "3") then nameNumber = nameNumber.."rd" + else nameNumber = nameNumber.."th" + end + + local nameSection = getDefaultSectionName() + if unitTypes and #unitTypes > 0 then + local desc = Unit.getDescByName(unitTypes[1]) + if desc then + nameSection = getNameByDesc(desc) + end + end + + return nameNumber.." "..nameSection + end +end diff --git a/Script/DCS extensions/World.lua b/Script/DCS extensions/World.lua new file mode 100644 index 0000000..4ff3c71 --- /dev/null +++ b/Script/DCS extensions/World.lua @@ -0,0 +1,497 @@ +-- ==================================================================================== +-- WORLDTOOLS - FUNCTIONS RELATED TO THE GAME WORLD +-- ==================================================================================== +-- DCSEx.world.collidesWithScenery(vec2, radius) +-- DCSEx.world.findSpawnPoint(vec2, minRadius, maxRadius, surfaceType, radiusWithoutScenery) +-- DCSEx.world.getAllPlayers() +-- DCSEx.world.getAllSceneryBuildings(minHealth) +-- DCSEx.world.getAllUnits(unitCategory) +-- DCSEx.world.getClosestPointOnRoadsVec2(vec2) +-- DCSEx.world.getCoordinatesAsString(point) +-- DCSEx.world.getCurrentMarkerID() +-- DCSEx.world.getGroupByID(groupID) +-- DCSEx.world.getNextMarkerID() +-- DCSEx.world.getSceneriesInZone(center, radius, minHealth) +-- DCSEx.world.getTerrainHeightDiff(coord, searchRadius) +-- DCSEx.world.getUnitByID(unitID) +-- DCSEx.world.isGroupAlive(g, unitsMustBeInAir) +-- DCSEx.world.setUnitLifePercent(unitID, life) +-- ==================================================================================== + +DCSEx.world = { } + +do + -- TODO: get max marker already in use from envMission + local nextMarkerId = 1 -- Next map marker ID + + function DCSEx.world.collidesWithScenery(vec2, radius) + local foundOne = false + radius = radius or 8 + + local volS = { + id = world.VolumeType.SPHERE, + params = { + point = DCSEx.math.vec2ToVec3(vec2, "land"), + radius = radius + } + } + + local ifFound = function(foundItem, val) + foundOne = true + return true + end + + world.searchObjects(Object.Category.SCENERY, volS, ifFound) + + return foundOne + end + + -- function DCSEx.world.findSpawnPoint(vec2, minRadius, maxRadius, surfaceType, radiusWithoutScenery, territorySide, expandSearch) + -- expandSearch = expandSearch or true + + -- for _=0,16 do + -- for _=0,16 do + -- local spawnPoint = nil + + -- spawnPoint = DCSEx.math.randomPointInCircle( + -- vec2, + -- DCSEx.converter.nmToMeters(maxRadius), + -- DCSEx.converter.nmToMeters(minRadius), + -- surfaceType) + + -- if spawnPoint and radiusWithoutScenery then + -- if DCSEx.world.collidesWithScenery(spawnPoint, radiusWithoutScenery) then + -- spawnPoint = nil + -- end + -- end + + -- if spawnPoint and territorySide then + -- if scramble.territories.getOwner(spawnPoint) ~= territorySide then + -- spawnPoint = nil + -- end + -- end + + -- if spawnPoint then return spawnPoint end + -- end + + -- if not expandSearch then return nil end + + -- minRadius = minRadius * 0.9 + -- maxRadius = maxRadius * 1.2 + -- end + + -- return nil + -- end + + + ------------------------------------- + -- Returns a table of all player-controlled units currently in the game + -- @return A table of unit objects + ------------------------------------- + function DCSEx.world.getAllPlayers() + local players = {} + + for coalitionID=0,2 do + local coalPlayers = coalition.getPlayers(coalitionID) + for _,p in pairs(coalPlayers) do + table.insert(players, p) + end + end + + return players + end + + ------------------------------------- + -- Returns a table of all map scenery buildings. + -- This function is rather CPU-consuming, better run it once on mission start and store the result in table. + -- @param minHealth Minimum health a building must have to be included in the table + -- @return A table of scenery objects + ------------------------------------- + function DCSEx.world.getAllSceneryBuildings(minHealth) + minHealth = minHealth or 0 + local sceneries = {} + + local scenerySearchvolume = { + id = world.VolumeType.SPHERE, + params = { point = { x = env.mission.map.centerX, y = 0, z = env.mission.map.centerY }, radius = 10000000 } + } + + local function ifSceneryFound(foundScenery, val) + local desc = foundScenery:getDesc() + if desc and desc.attributes and desc.attributes.Buildings then + if desc.life >= minHealth then + table.insert(sceneries, foundScenery) + end + end + return true + end + + world.searchObjects(Object.Category.SCENERY, scenerySearchvolume, ifSceneryFound) + + return sceneries + end + + ------------------------------------- + -- Returns all units belonging to a given category + -- @param coalitionID Coalition ID (coalition.side.XXX) or nil to search all coalitions + -- @param unitCategory An unit category (Group.Category.XXX) + -- @return A table of unit tables + ------------------------------------- + function DCSEx.world.getAllUnits(coalitionID, unitCategory) + local units = {} + + local searchedCoalitions = { 0, 1, 2 } + if coalitionID then searchedCoalitions = { coalitionID } end + + for _,c in pairs(searchedCoalitions) do -- Enumerate coalitions + for _, g in pairs(coalition.getGroups(c, unitCategory)) do + for __, u in pairs(g:getUnits()) do + table.insert(units, u) + end + end + end + + return units + end + + ------------------------------------- + -- Returns the closest point to roads as a vec2 + -- @param vec2 Coordinates to look for + -- @return A vec2 with the closest point on roads + ------------------------------------- + function DCSEx.world.getClosestPointOnRoadsVec2(vec2) + local roadX, roadY = land.getClosestPointOnRoads("roads", vec2.x, vec2.y) + return {x = roadX, y = roadY} + end + + ------------------------------------- + -- Returns the LL/MGRS coordinates of a point, as a string + -- Based on code by Bushmanni - https://forums.eagle.ru/showthread.php?t=99480 + -- @param point The point, as a vec2 or vec3 + -- @param hideElevation (optional) Show elevation NOT be displayed? Default: false + -- @return A string + ------------------------------------- + function DCSEx.world.getCoordinatesAsString(point, hideElevation) + hideElevation = hideElevation or false + + if not point.z then point = DCSEx.math.vec2ToVec3(point, "land") end + local cooString = "" + + local LLposN, LLposE, alt = coord.LOtoLL(point) + local LLposfixN, LLposdegN = math.modf(LLposN) + LLposdegN = LLposdegN * 60 + local LLposdegN2, LLposdegN3 = math.modf(LLposdegN) + local LLposdegN3Decimal = LLposdegN3 * 1000 + LLposdegN3 = LLposdegN3 * 60 + + local LLposfixE, LLposdegE = math.modf(LLposE) + LLposdegE = LLposdegE * 60 + local LLposdegE2, LLposdegE3 = math.modf(LLposdegE) + local LLposdegE3Decimal = LLposdegE3 * 1000 + LLposdegE3 = LLposdegE3 * 60 + + local LLns = "N" + if LLposfixN < 0 then + LLns = "S" + end + local LLew = "E" + if LLposfixE < 0 then + LLew = "W" + end + + local LLposNstring = LLns .. " " .. string.format("%.2i°%.2i'%.2i''", LLposfixN, LLposdegN2, LLposdegN3) + local LLposEstring = LLew .. " " .. string.format("%.3i°%.2i'%.2i''", LLposfixE, LLposdegE2, LLposdegE3) + cooString = "L/L: " .. LLposNstring .. " " .. LLposEstring + + local LLposNstring = LLns .. " " .. string.format("%.2i°%.2i.%.3i", LLposfixN, LLposdegN2, LLposdegN3Decimal) + local LLposEstring = LLew .. " " .. string.format("%.3i°%.2i.%.3i", LLposfixE, LLposdegE2, LLposdegE3Decimal) + cooString = cooString .. "\nL/L: " .. LLposNstring .. " " .. LLposEstring + + local mgrs = coord.LLtoMGRS(LLposN, LLposE) + local mgrsString = + mgrs.MGRSDigraph .. " " .. mgrs.UTMZone .. " " .. tostring(mgrs.Easting) .. " " .. tostring(mgrs.Northing) + cooString = cooString .. "\nMGRS: " .. mgrsString + + if not hideElevation then + cooString = cooString .. "\nELEVATION: " .. math.floor(alt * 3.281) .. "ft / " .. math.floor(alt) .. " meters" + end + + return cooString + end + + ------------------------------------- + -- Returns the last map marker ID generated by DCSEx.world.getNextMarkerID(), if any + -- @return A numeric ID, or nil + ------------------------------------- + function DCSEx.world.getCurrentMarkerID() + if nextMarkerId == 1 then return nil end + return nextMarkerId - 1 + end + + ------------------------------------- + -- Searches and return a group by its ID + -- @param groupID ID of the group + -- @return A group table, or nil if no group with this ID was found + ------------------------------------- + function DCSEx.world.getGroupByID(groupID) + for coalitionID = 1, 2 do + for _, grp in pairs(coalition.getGroups(coalitionID)) do + if DCSEx.dcs.getGroupIDAsNumber(grp) == groupID then + return grp + end + end + end + + return nil + end + + -- TODO: description + function DCSEx.world.getMarkerByText(text, coalition) + if not text then return nil end + text = text:lower() + local markers = DCSEx.world.getMarkPanels() + + for _,m in pairs(markers) do + local markerText = m.text or "" + markerText = markerText:lower() + if markerText == text then + if coalition == nil or m.coalition == coalition then + return m + end + end + end + + return nil + end + + ------------------------------------- + -- Returns a new, unique, map marker ID + -- @return A numeric ID + ------------------------------------- + function DCSEx.world.getNextMarkerID() + nextMarkerId = nextMarkerId + 1 + return nextMarkerId - 1 + end + + -- TODO: description, file header + function DCSEx.world.getPlayersInAir(side) + local players = {} + if side then + players = coalition.getPlayers(side) + else + players = DCSEx.world.getAllPlayers() + end + + local playersInAir = {} + for _,p in ipairs(players) do + if p:inAir() then + table.insert(playersInAir, p) + end + end + + return playersInAir + end + + -- TODO: description, file header + function DCSEx.world.getSpawnPoint(zone, surfaceType, safeRadius) + safeRadius = safeRadius or 100 + + -- Only two surface types are really useful to us: land and water + if surfaceType == land.SurfaceType.SHALLOW_WATER then + surfaceType = land.SurfaceType.WATER + elseif surfaceType == land.SurfaceType.ROAD or surfaceType == land.SurfaceType.RUNWAY then + surfaceType = land.SurfaceType.LAND + end + + local loopsLeft = 512 + + while loopsLeft > 0 do + local basePoint = DCSEx.zones.getRandomPointInside(zone) + + if not surfaceType then -- Nil surfaceType means: any point is fine + return basePoint + end + + if surfaceType == land.SurfaceType.WATER then + if basePoint and land.getSurfaceType(basePoint) == land.SurfaceType.WATER then + return basePoint + end + else + if safeRadius <= 0 then + return basePoint + end + + basePoint = DCSEx.math.vec2ToVec3(basePoint, "land") + + local spawnPoints = Disposition.getSimpleZones(basePoint, math.max(1852, safeRadius * 5), safeRadius, 1) + if #spawnPoints > 0 and land.getSurfaceType(spawnPoints[1]) == land.SurfaceType.LAND then + return spawnPoints[1] + end + end + + loopsLeft = loopsLeft - 1 + end + + return nil + end + + -- TODO: description + function DCSEx.world.getSceneriesInZone(center, radius, minHealth) + minHealth = minHealth or 0 + local sceneries = {} + + local scenerySearchvolume = { + id = world.VolumeType.SPHERE, + params = { point = { x = center.x, y = 0, z = center.y }, radius = radius } + } + + local function ifSceneryFound(foundScenery, val) + local desc = foundScenery:getDesc() + if desc and desc.attributes and desc.attributes.Buildings then + if desc.life >= minHealth then + table.insert(sceneries, foundScenery) + end + end + return true + end + + world.searchObjects(Object.Category.SCENERY, scenerySearchvolume, ifSceneryFound) + + return sceneries + end + + -- TODO: description + function DCSEx.world.getTerrainHeightDiff(coord, searchRadius) + local samples = {} + searchRadius = searchRadius or 5 + + samples[#samples + 1] = land.getHeight(coord) + for i = 0, 360, 30 do + samples[#samples + 1] = land.getHeight({x = (coord.x + (math.sin(math.rad(i))*searchRadius)), y = (coord.y + (math.cos(math.rad(i))*searchRadius))}) + if searchRadius >= 20 then -- if search radius is sorta large, take a sample halfway between center and outer edge + samples[#samples + 1] = land.getHeight({x = (coord.x + (math.sin(math.rad(i))*(searchRadius/2))), y = (coord.y + (math.cos(math.rad(i))*(searchRadius/2)))}) + end + end + local tMax, tMin = 0, 1000000 + for _, height in pairs(samples) do + if height > tMax then + tMax = height + end + if height < tMin then + tMin = height + end + end + + return tMax - tMin + end + + function DCSEx.world.getUnitsCenter(units) + if not units or #units == 0 then return { x = 0, y = 0 } end + + local center = { x = 0, y = 0 } + for _,u in pairs(units) do + local uPt2 = DCSEx.math.vec3ToVec2(u:getPoint()) + center.x = center.x + uPt2.x + center.y = center.y + uPt2.y + end + + center.x = center.x / #units + center.y = center.y / #units + + return center + end + + ------------------------------------- + -- Searches and return an unit by its ID + -- @param unitID ID of the unit + -- @return An unit, or nil if no unit with this ID was found + ------------------------------------- + function DCSEx.world.getUnitByID(unitID) + for coalitionID = 1, 2 do + for _, g in pairs(coalition.getGroups(coalitionID)) do + local units = g:getUnits() + for _, u in pairs(units) do + if DCSEx.dcs.getObjectIDAsNumber(u) == unitID then + return u + end + end + end + end + + return nil + end + + ------------------------------------- + -- Searches and return a coalition static object by its ID + -- @param unitID ID of the static object + -- @return An static object, or nil if no unit with this ID was found + ------------------------------------- + function DCSEx.world.getStaticObjectByID(staticID) + for coalitionID = 1,2 do + for _,s in pairs(coalition.getStaticObjects(coalitionID)) do + if DCSEx.dcs.getObjectIDAsNumber(s) == staticID then + return s + end + end + end + + return nil + end + + -- TODO: description + function DCSEx.world.isGroupAlive(g, unitsMustBeInAir) + if not g then return false end + if not g:isExist() then return false end + if g:getSize() == 0 then return false end + + unitsMustBeInAir = unitsMustBeInAir or false + + local atLeastOneActiveUnit = false + local units = g:getUnits() + if not units or #units == 0 then return false end + for _,u in pairs(units) do + if u:isExist() and u:getLife() > 0 then + if u:inAir() or not unitsMustBeInAir then + atLeastOneActiveUnit = true + end + end + end + + return atLeastOneActiveUnit + end + + -- TODO: description & file header + function DCSEx.world.setUnitLifePercent(unitID, life) + net.dostring_in("mission", string.format("a_unit_set_life_percentage(%d, %f)", unitID, life)) + end + + -- TODO: description & file header + function DCSEx.world.explodeUnit(unitID, amount) + net.dostring_in("mission", string.format("a_explosion_unit(%d, %f)", unitID, amount)) + end + + function DCSEx.world.destroyGroupByID(groupID) + if not groupID then return end + local g = DCSEx.world.getGroupByID(groupID) + if g then g:destroy() end + end + + -- function DCSEx.world.destroySceneryInZone(zone, destructionPercent) + -- destructionPercent = destructionPercent or 0 + -- net.dostring_in("mission", string.format("a_scenery_destruction_zone(%d, %f)", zone.zoneId, destructionPercent)) + -- end + + -- function DCSEx.world.highlightUnit(unitID, enabled) + -- if enabled then + -- enabled = "true" + -- else + -- enabled = "false" + -- end + + -- net.dostring_in("mission", string.format("a_unit_highlight(%d, %s)", unitID, enabled)) + -- end + + -- function DCSEx.world.shellingZone(zone, tnt, shellsCount) + -- net.dostring_in("mission", string.format("a_shelling_zone(%d, %f, %d)", zone.zoneId, tnt, shellsCount)) + -- end +end diff --git a/Script/DCS extensions/Zones.lua b/Script/DCS extensions/Zones.lua new file mode 100644 index 0000000..177d733 --- /dev/null +++ b/Script/DCS extensions/Zones.lua @@ -0,0 +1,305 @@ +-- ==================================================================================== +-- ZONETOOLS - FUNCTIONS RELATED TO MAP TRIGGER ZONES +-- ==================================================================================== +-- DCSEx.zones.drawOnMap(zoneTable, lineColor, fillColor, lineType, drawName, readonly) +-- DCSEx.zones.getAll() +-- DCSEx.zones.getByName(name) +-- DCSEx.zones.getCenter(zoneTable) +-- DCSEx.zones.getProperty(zoneTable, propertyName) +-- DCSEx.zones.getPropertyBoolean(zoneTable, propertyName, defaultValue) +-- DCSEx.zones.getPropertyFloat(zoneTable, propertyName, defaultValue, min, max) +-- DCSEx.zones.getPropertyInt(zoneTable, propertyName, defaultValue, min, max) +-- DCSEx.zones.getPropertyParse(zoneTable, propertyName, stringTable, valueTable, defaultValue) +-- DCSEx.zones.getPropertyTable(zoneTable, propertyName) +-- DCSEx.zones.getRadius(zoneTable, useMaxForQuads) +-- DCSEx.zones.getSurfaceArea(zoneTable) +-- DCSEx.zones.isPointInside(zoneTable, point) +-- ==================================================================================== + +DCSEx.zones = { } + +-- TODO: function description +function DCSEx.zones.drawOnMap(zoneTable, lineColor, fillColor, lineType, drawName, readOnly) + drawName = drawName or false + readOnly = readOnly or true + + if not zoneTable then return end + local markerID = DCSEx.world.getNextMarkerID() + + -- Draw shapes on the F10 map + if zoneTable.type == 2 then -- Zone is a quad + trigger.action.quadToAll( + -1, + markerID, + DCSEx.math.vec2ToVec3(zoneTable.verticies[1]), + DCSEx.math.vec2ToVec3(zoneTable.verticies[2]), + DCSEx.math.vec2ToVec3(zoneTable.verticies[3]), + DCSEx.math.vec2ToVec3(zoneTable.verticies[4]), + lineColor, + fillColor, + lineType, + readOnly + ) + else -- Zone is a circle + trigger.action.circleToAll( + -1, + markerID, + DCSEx.math.vec2ToVec3(zoneTable), + zoneTable.radius, + lineColor, + fillColor, + lineType, + readOnly + ) + end + + if drawName then + local markerIDText = DCSEx.world.getNextMarkerID() + trigger.action.textToAll(-1, markerIDText, DCSEx.math.vec2ToVec3(zoneTable), { 1, 1, 1, 1 }, { 0, 0, 0, .5 }, 18, readOnly, zoneTable.name) + return { markerID, markerIDText } + end + + return markerID +end + +------------------------------------- +-- Returns all trigger zones +-- @return Table of zones +------------------------------------- +function DCSEx.zones.getAll() + if not env.mission.triggers then + return {} + end + if not env.mission.triggers.zones then + return {} + end + + return DCSEx.table.deepCopy(env.mission.triggers.zones) +end + +------------------------------------- +-- Finds and return a trigger zone by a certain name +-- @param name Case-insensitive name of the zone +-- @return Zone table or nil if no zone with this name was found +------------------------------------- +function DCSEx.zones.getByName(name) + if not name then return nil end + if not env.mission.triggers then return nil end + if not env.mission.triggers.zones then return nil end + + name = name:lower() + + for _, z in pairs(env.mission.triggers.zones) do + if z.name:lower() == name then + return DCSEx.table.deepCopy(z) + end + end + + return nil +end + +------------------------------------- +-- Returns the center of a zone +-- @param zoneTable The zone table, returned by TMMissionData.getZones() or TMMissionData.getZoneByName(name) +-- @return A vec2 +------------------------------------- +function DCSEx.zones.getCenter(zoneTable) + if not zoneTable then return nil end + + local x = zoneTable.x or 0 + local y = zoneTable.y or 0 + + if zoneTable.type == 2 then -- Zone is a quad + x = (zoneTable.verticies[1].x + zoneTable.verticies[2].x + zoneTable.verticies[3].x + zoneTable.verticies[4].x) / 4 + y = (zoneTable.verticies[1].y + zoneTable.verticies[2].y + zoneTable.verticies[3].y + zoneTable.verticies[4].y) / 4 + end + + return { x = x, y = y } +end + +------------------------------------- +-- Returns the value of the property of a trigger zone, as a string +-- @param zoneTable The zone table, returned by DCSEx.zones.getAll() or DCSEx.zones.getByName(name) +-- @param propertyName Case-insensitive name of the property +-- @return The value of the property or nil if it doesn't exist +------------------------------------- +function DCSEx.zones.getProperty(zoneTable, propertyName, defaultValue) + if not propertyName then return defaultValue end + if not zoneTable then return defaultValue end + if not zoneTable.properties then return defaultValue end + + propertyName = propertyName:lower() + + for _, p in pairs(zoneTable.properties) do + if p.key:lower() == propertyName then + return (p.value or defaultValue):lower() + end + end + + return defaultValue +end + +------------------------------------- +-- Returns the value of the property of a trigger zone, parsed against a case-insensitive table of strings +-- @param zoneTable The zone table, returned by DCSEx.zones.getAll() or DCSEx.zones.getByName(name) +-- @param propertyName Case-insensitive name of the property +-- @param defaultValue Default value to return if no match was found +-- @return A boolean +------------------------------------- +function DCSEx.zones.getPropertyBoolean(zoneTable, propertyName, defaultValue) + return DCSEx.zones.getPropertyParse( + zoneTable, + propertyName, + {"true", "yes", "1", "on", "enabled", "false", "no", "0", "off", "disabled"}, + {true, true, true, true, true, false, false, false, false, false}, + defaultValue) +end + +------------------------------------- +-- Returns the value of the property of a trigger zone, as a float +-- @param zoneTable The zone table, returned by DCSEx.zones.getAll() or DCSEx.zones.getByName(name) +-- @param propertyName Case-insensitive name of the property +-- @param defaultValue Default value to return if no match was found +-- @param min Minimum value +-- @param max Maximum value +-- @return A float +------------------------------------- +function DCSEx.zones.getPropertyFloat(zoneTable, propertyName, defaultValue, min, max) + local value = tonumber(DCSEx.zones.getProperty(zoneTable, propertyName)) + if not value then return defaultValue end + if min then value = math.max(min, value) end + if max then value = math.min(max, value) end + return value +end + +------------------------------------- +-- Returns the value of the property of a trigger zone, as an integer +-- @param zoneTable The zone table, returned by DCSEx.zones.getAll() or DCSEx.zones.getByName(name) +-- @param propertyName Case-insensitive name of the property +-- @param defaultValue Default value to return if no match was found +-- @param min Minimum value +-- @param max Maximum value +-- @return An integer +------------------------------------- +function DCSEx.zones.getPropertyInt(zoneTable, propertyName, defaultValue, min, max) + local value = DCSEx.zones.getPropertyFloat(zoneTable, propertyName, defaultValue, min, max) + if not value then return nil end + return math.floor(value) +end + +------------------------------------- +-- Gets the value of a property of a trigger zone and parse it according to two correspondance tables +-- @param zoneTable The zone table, returned by DCSEx.zones.getAll() or DCSEx.zones.getByName(name) +-- @param propertyName Case-insensitive name of the property +-- @param stringTable A table of strings +-- @param valueTable A values, matching the strings table's indices +-- @param defaultValue Default value to return if no match was found +-- @return A value +------------------------------------- +function DCSEx.zones.getPropertyParse(zoneTable, propertyName, stringTable, valueTable, defaultValue) + local value = DCSEx.zones.getProperty(zoneTable, propertyName) or "" + value = value:lower() + + for i,_ in ipairs(stringTable) do + if value == stringTable[i]:lower() then + return valueTable[i] + end + end + + return defaultValue +end + +------------------------------------- +-- Returns the value of the property of a trigger zone, as a table of comma-separated lowercase strings +-- @param zoneTable The zone table, returned by DCSEx.zones.getAll() or DCSEx.zones.getByName(name) +-- @param propertyName Case-insensitive name of the property +-- @return An table +------------------------------------- +function DCSEx.zones.getPropertyTable(zoneTable, propertyName) + local value = DCSEx.zones.getProperty(zoneTable, propertyName) + if not value then return {} end + return string.split(value:lower(), ",") +end + +------------------------------------- +-- Returns the radius of a zone, in meter +-- @param zoneTable The zone table, returned by DCSEx.zones.getAll() or DCSEx.zones.getByName(name) +-- @param useMaxForQuads If true, return largest distance between the center and a vertex. If false (default value), returns the mean distance. Only used if the zone is a quad. +-- @return An table +------------------------------------- +function DCSEx.zones.getRadius(zoneTable, useMaxForQuads) + if not zoneTable then return 0 end + useMaxForQuads = useMaxForQuads or false + + local radius = 0 + + if zoneTable.type == 2 then -- Zone is a quad + for _,v in ipairs(zoneTable.verticies) do + if useMaxForQuads then + radius = math.max(radius, DCSEx.math.getDistance2D(zoneTable, v)) + else + radius = radius + DCSEx.math.getDistance2D(zoneTable, v) + end + end + + if #zoneTable.verticies > 0 and not useMaxForQuads then + radius = radius / #zoneTable.verticies + end + else + radius = zoneTable.radius + end + + return radius +end + +-- TODO: description + file header +function DCSEx.zones.getRandomPointInside(zoneTable, surfaceType) + local radius = DCSEx.zones.getRadius(zoneTable) + + for _=1,64 do + local point = DCSEx.math.randomPointInCircle(zoneTable, radius, surfaceType) + + if zoneTable.type == 2 then + if not DCSEx.zones.isPointInside(zoneTable, point) then point = nil end + end + + if point then return point end + end + + return nil +end + +------------------------------------- +-- Returns the surface area of a zone +-- @param zoneTable The zone table, returned by TMMissionData.getZones() or TMMissionData.getZoneByName(name) +-- @return A number, in squared meters +------------------------------------- +function DCSEx.zones.getSurfaceArea(zoneTable) + if not zoneTable then return 0 end + + if zoneTable.type == 2 then -- Zone is a quad + if not zoneTable.verticies then return 0 end + local area = zoneTable.verticies[1].x * zoneTable.verticies[2].y + zoneTable.verticies[2].x * zoneTable.verticies[3].y + zoneTable.verticies[3].x * zoneTable.verticies[4].y + zoneTable.verticies[4].x * zoneTable.verticies[1].y - zoneTable.verticies[2].x * zoneTable.verticies[1].y - zoneTable.verticies[3].x * zoneTable.verticies[2].y - zoneTable.verticies[4].x * zoneTable.verticies[3].y - zoneTable.verticies[1].x * zoneTable.verticies[4].y + return math.abs(area) / 2 + else -- Zone is a circle + if not zoneTable.radius then return 0 end + return (zoneTable.radius ^ 2) * math.pi + end +end + +------------------------------------- +-- Returns true if a point is inside a zone +-- @param zoneTable The zone table, returned by TMMissionData.getZones() or TMMissionData.getZoneByName(name) +-- @param point A point, as a vec3 or vec2 +-- @return True if the point is inside the zone, false otherwise +------------------------------------- +function DCSEx.zones.isPointInside(zoneTable, point) + if not point then return false end + if point.z then point = DCSEx.math.vec3ToVec2(point) end -- Point was a vec3, convert to vec2 + + if zoneTable.type == 2 then -- Zone is a quad + return DCSEx.math.isPointInsidePolygon(zoneTable.verticies, point) + else -- Zone is a circle + return DCSEx.math.isPointInsideCircle({x = zoneTable.x, y = zoneTable.y}, zoneTable.radius, point) + end +end diff --git a/Script/Library/Addons.lua b/Script/Library/Addons.lua new file mode 100644 index 0000000..2c6155b --- /dev/null +++ b/Script/Library/Addons.lua @@ -0,0 +1 @@ +Library.addOns = { } diff --git a/Script/Library/Aircraft.lua b/Script/Library/Aircraft.lua new file mode 100644 index 0000000..2b63c5b --- /dev/null +++ b/Script/Library/Aircraft.lua @@ -0,0 +1,11 @@ +-------------------------------------------------------- +-- INFORMATION ABOUT AIRCRAFT CAPABILITES AND PAYLOAD -- +-------------------------------------------------------- + +-- REGEX +-- .*Pylon(..)=(.*) +-- [(\1)] = { CLSID = "(\2)" }, + +Library.aircraft = {} + +--{{AIRCRAFT}}-- diff --git a/Script/Library/Factions.lua b/Script/Library/Factions.lua new file mode 100644 index 0000000..14286c2 --- /dev/null +++ b/Script/Library/Factions.lua @@ -0,0 +1,70 @@ +Library.factions = {} +Library.factions.defaults = { "USA", "Russia" } +Library.factions.tables = {} + +--{{FACTIONS}}-- + +-- If default red faction is "Russia" and "Russia" is named "USSR" in this time period, change its name in the default +if Library.factions.tables["USSR"] and Library.factions.defaults[coalition.side.RED] == "Russia" then + Library.factions.defaults[coalition.side.RED] = "USSR" +end + +function Library.factions.getUnits(faction, unitFamilies, count, singleType) + singleType = singleType or false + local timePeriod = TUM.settings.getValue(TUM.settings.id.TIME_PERIOD) + + if not unitFamilies then return nil end + if not Library.factions.tables[faction] then return nil end + if not Library.factions.tables[faction].units then return nil end + if not Library.factions.tables[faction].units[timePeriod] then return nil end + + if type(unitFamilies) == "number" then unitFamilies = { unitFamilies } end + count = math.max(1, count or 1) + + local validUnits = {} + for _,f in ipairs(unitFamilies) do + local unitList = Library.factions.tables[faction].units[timePeriod][f] + + if unitList and #unitList > 0 then + for __,u in ipairs(unitList) do + if not DCSEx.table.contains(validUnits, u) then + table.insert(validUnits, u) + end + end + end + end + if #validUnits == 0 then return nil end + + local selectedUnits = {} + if singleType then + local unitType = DCSEx.table.getRandom(validUnits) + for _=1,count do + table.insert(selectedUnits, unitType) + end + else + for _=1,count do + table.insert(selectedUnits, DCSEx.table.getRandom(validUnits)) + end + end + + return selectedUnits +end + +do + -- local function mergeFactions() + -- end + + -- local function setDefaultUnitsIfMissing(unitTypes, defaults) + -- if not unitTypes or #unitTypes == 0 then + -- return defaults + -- end + + -- return unitTypes + -- end + + -- for k,_ in pairs(Library.factions) do + + + -- if not Library.factions[k][DCSEx.enums.unitFamily.SHIP_CARGO] = { "Dry-cargo ship-1", "Dry-cargo ship-2", "ELNYA", "Ship_Tilde_Supply" }, + -- end +end diff --git a/Script/Library/GroupTemplates.lua b/Script/Library/GroupTemplates.lua new file mode 100644 index 0000000..e86ad27 --- /dev/null +++ b/Script/Library/GroupTemplates.lua @@ -0,0 +1,3 @@ +Library.groupTemplates = { } + +--{{TEMPLATES}}-- diff --git a/Script/Library/ObjectNames.lua b/Script/Library/ObjectNames.lua new file mode 100644 index 0000000..65b6dfe --- /dev/null +++ b/Script/Library/ObjectNames.lua @@ -0,0 +1,29 @@ +Library.objectNames = {} + +do + local namesTable = { + ["F-16C_50"] = "F-16C", + ["FA-18C_hornet"] = "F/A-18C", + ["speedboat"] = "speedboat" + } + + function Library.objectNames.get(obj) + if not obj then return "nothing" end + + -- First, try to find a custom name in the names table + local typeName = obj:getTypeName() + if typeName and namesTable[typeName] then + return namesTable[typeName] + end + + -- Else, try to find a display name in the description + local desc = obj:getDesc() + if desc and desc.DisplayName then + return desc.DisplayName + end + + -- If nothing else was found, return the internal typename + if not typeName then return "unknown" end + return typeName + end +end \ No newline at end of file diff --git a/Script/Library/ObjectiveNames.lua b/Script/Library/ObjectiveNames.lua new file mode 100644 index 0000000..06821a7 --- /dev/null +++ b/Script/Library/ObjectiveNames.lua @@ -0,0 +1,14 @@ +Library.objectiveNames = {} + +do + local nextIndex = 0 + + -- Shuffle table order so the same names won't always come first + local names = DCSEx.table.shuffle({ "Abenaki", "Abide", "Ablow", "Abnormal", "Abrum", "Abstain", "Abyss", "Accountant", "Accura", "Aces", "Acker", "Acme", "Acrobat", "Active", "Acton", "Adin", "Adios", "Adobe", "Adorn", "Aegis", "Aero", "Aeromet", "Afton", "Agar", "Agile", "Agio", "Agony", "Ahab", "Airevac", "Airgun", "Airport", "Akela", "Aksarben", "Akula", "Alabama", "Alamo", "Albert", "Alder", "Alfa", "Alga", "Alger", "Algum", "Alkyd", "Alley", "Alleycat", "Allied", "Alloy", "Almighty", "Almug", "Alouette", "Alta", "Alva", "Ambush", "Ame", "Amino", "Amiri", "Ammo", "Amor", "Ampex", "Amt", "Amway", "Anderson", "Andrews", "Andy", "Angel", "Angle", "Angry", "Animal", "Anita", "Anker", "Ankle", "Antar", "Antic", "Antler", "Anvis", "Anzac", "Apache", "Ape", "Apex", "April", "Aprt", "Arby", "Archer", "Architect", "Arctic", "Area", "Argus", "Aria", "Aris", "Ark", "Armor", "Army", "Arnold", "Arris", "Arrow", "Arter", "Artly", "Ascot", "Ash", "Asher", "Aspen", "Assault", "Assoc", "Astor", "Astra", "Astro", "Atari", "Atilla", "Atlas", "Atoka", "Atomic", "Attack", "Auggie", "Aussie", "Austin", "Auto", "Avalanche", "Avalon", "Avenging", "Avro", "Awoke", "Axe", "Axis", "Babs", "Backspin", "Backstop", "Backy", "Bacon", "Bader", "Baf", "Baffin", "Baffle", "Bahrain", "Baja", "Balky", "Ball", "Bam", "Bama", "Bambo", "Bandage", "Bandit", "Bandsaw", "Bandwagon", "Bane", "Bang", "Banger", "Bangor", "Banjo", "Bank", "Banks", "Banshee", "Banter", "Banyan", "Banzi", "Barb", "Barba", "Barbaric", "Bard", "Bark", "Barley", "Barn", "Barney", "Baron", "Barracuda", "Barranquilla", "Barrel", "Barrier", "Bart", "Basco", "Based", "Basher", "Bastard", "Bat", "Bat", "Batman", "Baton", "Bats", "Batter", "Battle", "Battlecat", "Battlestar", "Bay", "Bayhi", "Bayside", "Baywatch", "Bazoo", "Beach", "Beachball", "Beak", "Beamer", "Bear", "Bears", "Beast", "Beaver", "Beavis", "Bec", "Beef", "Beehive", "Beeliner", "Beer", "Beermat", "Beetle", "Bema", "Bender", "Bengal", "Berry", "Berta", "Berth", "Best", "Beta", "Beth", "Betsy", "Bicep", "Biddy", "Big", "Bigfoot", "Bigget", "Biker", "Bilk", "Billfish", "Billo", "Biloxi", "Binal", "Binder", "Birch", "Bird", "Birth", "Bison", "Bixby", "Black", "Blackbird", "Blackheart", "Blackjack", "Blacksheep", "Blacksmith", "Blade", "Blademan", "Blaster", "Blay", "Blaze", "Blazer", "Bleed", "Blob", "Bloke", "Blond", "Bloodhound", "Blower", "Blown", "Blue", "Blues", "Blurb", "Bluto", "Boat", "Bobby", "Bobcat", "Bode", "Bogey", "Bogue", "Bola", "Bolar", "Bold", "Boles", "Bolling", "Bolt", "Boma", "Bomar", "Bomber", "Bona", "Bone", "Bones", "Bonnie", "Bonno", "Bonsi", "Book", "Bookie", "Bookshelf", "Boom", "Boomer", "Boot", "Borage", "Borer", "Borg", "Boron", "Bosco", "Bosen", "Bosox", "Boxcar", "Boxer", "Bozz", "Brady", "Brag", "Brandywine", "Brass", "Brat", "Brave", "Bravo", "Bray", "Brem", "Brevet", "Brew", "Brewer", "Brice", "Brick", "Brickston", "Brickwall", "Brickyard", "Brigg", "Brigham", "Briney", "Bristol", "Broadway", "Brochure", "Brock", "Broke", "Bronco", "Bronk", "Broody", "Brook", "Broom", "Brs", "Brutis", "Bucanero", "Buck", "Buckeye", "Buckshot", "Bud", "Buddha", "Buddy", "Budman", "Buff", "Buffoon", "Buggy", "Bugsy", "Build", "Bulb", "Bull", "Bulldog", "Bullet", "Bullfrog", "Bullit", "Bullseye", "Bully", "Bumpy", "Bunk", "Burger", "Burglar", "Burma", "Burner", "Burning", "Burr", "Bursa", "Burt", "Busch", "Bushmaster", "Bust", "Buster", "Butch", "Butler", "Buzz", "Buzzard", "Buzzsaw", "Buzzy", "Bylaw", "Cabal", "Cabin", "Cacti", "Cactus", "Caddo", "Caddy", "Cade", "Cadon", "Caesar", "Caeser", "Cafe", "Cagey", "Cajun", "Call", "Calumet", "Calvin", "Cam", "Camay", "Camel", "Camelback", "Camelot", "Camera", "Campo", "Camslant", "Camspac", "Cando", "Candor", "Canforce", "Canine", "Cannon", "Canoe", "Canuck", "Canvas", "Canyon", "Cap", "Cape", "Capital", "Capitole", "Capon", "Capra", "Caprock", "Caps", "Capstone", "Carbon", "Carbonate", "Cardfile", "Caribbean", "Carly", "Carmen", "Carny", "Carp", "Carpet", "Carr", "Carson", "Carty", "Case", "Casey", "Cash", "Casino", "Casio", "Casket", "Cass", "Castro", "Cat", "Catbird", "Catfish", "Catman", "Cats", "Cauldron", "Cave", "Cecil", "Cedar", "Cee", "Cef", "Ceman", "Cfe", "Cgwb", "Chain", "Chalice", "Challenger", "Champ", "Chapps", "Chaps", "Charger", "Charlie", "Charter", "Chartroom", "Chaser", "Chastise", "Check", "Checker", "Checkerboard", "Checkmate", "Cheetah", "Cheetahs", "Chemo", "Chena", "Cherokee", "Chesda", "Chestaid", "Chesty", "Cheta", "Cheth", "Chico", "Chief", "Chiefs", "Chili", "Chill", "Chime", "Chippy", "Chisel", "Chisum", "Chock", "Chocks", "Chosen", "Chris", "Chrome", "Chuck", "Chug", "Chuggs", "Chump", "Church", "Cindy", "Circus", "Cisco", "City", "Clam", "Clan", "Claw", "Clean", "Clearwater", "Cleat", "Cletus", "Climax", "Clog", "Clop", "Closeout", "Clover", "Clown", "Club", "Clubleaf", "Cluck", "Clue", "Cluny", "Cnatra", "Coach", "Coast", "Coastguard", "Cobalt", "Cobb", "Cobby", "Cobra", "Cochise", "Cock", "Coco", "Cod", "Coda", "Coder", "Cody", "Coffee", "Coffin", "Coho", "Coil", "Coke", "Colby", "Cold", "Coma", "Combat", "Combo", "Comer", "Comfy", "Commando", "Comnmand", "Comseron", "Conan", "Conda", "Condo", "Condor", "Cone", "Conform", "Congo", "Conic", "Convict", "Convoy", "Cool", "Coop", "Coors", "Cope", "Copper", "Copyright", "Coral", "Cord", "Cornhusker", "Corona", "Corso", "Corvette", "Cosmic", "Cosmo", "Coso", "Costly", "Cotam", "Cougar", "Count", "Courage", "Courageous", "Covert", "Covet", "Cowboy", "Coyote", "Craft", "Crate", "Craze", "Crazy", "Crazyhorse", "Creole", "Crest", "Cricjet", "Crisco", "Crisp", "Crooked", "Crossbow", "Crow", "Crown", "Crud", "Crumb", "Crumpet", "Crystal", "Cub", "Cuban", "Cube", "Cuda", "Cuervo", "Cufmy", "Cujo", "Culdrose", "Cult", "Cummins", "Cuppy", "Cure", "Cushion", "Cushy", "Custer", "Cutlass", "Cutthroat", "Cutty", "Cyborg", "Cyclone", "Cylon", "Cynic", "Czech", "Dabba", "Dactyl", "Dae", "Dagda", "Dagger", "Dago", "Dagon", "Dagrat", "Daily", "Dakota", "Dallas", "Dally", "Dame", "Damon", "Danda", "Dander", "Danno", "Dantic", "Dapper", "Dare", "Dark", "Dart", "Darth", "Datsun", "Datum", "Davey", "Dawg", "Dazzle", "Dea", "Deadeye", "Deadly", "Dealer", "Death", "Deck", "Decoy", "Dee", "Deecee", "Deep", "Deer", "Deerlodge", "Defender", "Defiant", "Deft", "Defy", "Delco", "Deliverance", "Delta", "Delve", "Demo", "Demonstration", "Demus", "Denali", "Deployments", "Derby", "Derma", "Derrick", "Desert", "Destroyer", "Detone", "Detroit", "Deuce", "Devil", "Devo", "Devon", "Dewey", "Diamond", "Diamondback", "Dice", "Dicey", "Dido", "Dill", "Dillon", "Dime", "Dinar", "Dino", "Dipper", "Dirca", "Dirk", "Dirt", "Dirty", "Disco", "Ditto", "Ditty", "Dive", "Dixie", "Dobby", "Dog", "Doggy", "Dollar", "Domino", "Dong", "Donna", "Doom", "Doubleshot", "Doud", "Doughboy", "Down", "Downfall", "Downy", "Dozer", "Draco", "Drag", "Dragnet", "Dragon", "Dragonfire", "Dragonfish", "Dread", "Dreamland", "Drop", "Dropkick", "Dross", "Drum", "Dryden", "Dryer", "Dubai", "Duce", "Duck", "Dude", "Duece", "Duff", "Duke", "Dunmore", "Dupe", "Duple", "Duster", "Dusty", "Dutch", "Eagle", "Earl", "Earthquake", "Easy", "Eathen", "Ecles", "Eddie", "Edgar", "Edge", "Edgy", "Egyptian", "Elcid", "Elgin", "Elton", "Elvan", "Elvis", "Emerald", "Emergency", "Emmet", "Empire", "Energy", "Enjep", "Entry", "Epic", "Epoxy", "Eskan", "Essex", "Esso", "Ethyl", "Eureka", "Eva", "Evac", "Evade", "Evader", "Even", "Event", "Evil", "Executive", "Exile", "Expert", "Expo", "Express", "Extender", "Exude", "Exult", "Exxon", "Faf", "Fagin", "Fail", "Falco", "Falcon", "Falls", "Fame", "Fang", "Fango", "Fangs", "Fantop", "Farad", "Farmer", "Farout", "Farva", "Fast", "Fatly", "Fault", "Faze", "Fazer", "Felix", "Femur", "Fend", "Fendy", "Fenny", "Fetch", "Feud", "Fever", "Fewer", "Fiber", "Fiddle", "Fido", "Fiend", "Fighting", "Filo", "Finder", "Firebird", "Firebrand", "Firehawk", "Firewood", "Firm", "First", "Fisher", "Fist", "Fitch", "Fixer", "Flag", "Flanker", "Flash", "Fleagle", "Flint", "Flite", "Florida", "Floss", "Flout", "Floyd", "Flug", "Fluky", "Flume", "Flush", "Flyer", "Flynet", "Fmn", "Fnf", "Fny", "Fog", "Foghorn", "Foil", "Folic", "Fong", "Font", "Food", "Foray", "Force", "Forces", "Forks", "Fosdick", "Foss", "Foul", "Found", "Fourney", "Fox", "Foxxy", "Foxy", "Frag", "Frank", "Frate", "Fray", "Freak", "Fred", "Freddy", "Free", "Freebie", "Freedom", "Freemason", "Freight", "Fresca", "Fried", "From", "Frontline", "Frosty", "Fujin", "Full", "Fullcard", "Fungo", "Funny", "Furor", "Furr", "Furry", "Fury", "Fuss", "Fuzzy", "Gadget", "Gaf", "Gage", "Gain", "Gainer", "Galaxy", "Gale", "Gam", "Gamble", "Gambler", "Gamma", "Gander", "Gangster", "Gannetprestwick", "Gantsec", "Garby", "Garth", "Garuda", "Gaslight", "Gaspipe", "Gasser", "Gassy", "Gate", "Gator", "Gators", "Gaucho", "Gawky", "Geld", "Gelt", "Genus", "George", "Georgia", "Geronimo", "Getup", "Geyser", "Ghost", "Giant", "Gilly", "Ginger", "Ginzu", "Gipper", "Gitmo", "Glass", "Glida", "Global", "Glory", "Gnarly", "Goal", "Goat", "Goatee", "Gobo", "Golan", "Gold", "Golden", "Golf", "Golly", "Gomer", "Gondola", "Gonzo", "Goodwrench", "Goody", "Goofy", "Goose", "Gopher", "Goral", "Gordo", "Gordon", "Gourd", "Goya", "Graceland", "Grady", "Grand", "Grant", "Grasslands", "Gray", "Greasy", "Green", "Greg", "Gremlin", "Griffin", "Grits", "Gritty", "Grizzly", "Groat", "Grouch", "Ground", "Grout", "Growl", "Grown", "Grub", "Gruman", "Grumman", "Grump", "Grumpy", "Grunt", "Grup", "Gucci", "Guda", "Guilder", "Guiness", "Gull", "Gumby", "Gun", "Gundog", "Gunfighter", "Gunman", "Gunner", "Gunny", "Gunrunner", "Gunsmoke", "Gunstar", "Guntrain", "Guss", "Guts", "Gypsy", "Habitat", "Haf", "Haggar", "Hail", "Haiti", "Hake", "Hal", "Hale", "Halibut", "Hall", "Hambone", "Hammer", "Hampshire", "Hangover", "Hank", "Hanscom", "Happy", "Harass", "Hard", "Hardball", "Hardy", "Harley", "Harm", "Harmo", "Hart", "Hassle", "Hat", "Hatch", "Hatman", "Haven", "Havoc", "Hawk", "Hawkeye", "Hawks", "Hazard", "Haze", "Hazzard", "Head", "Headcap", "Heat", "Hefty", "Heinz", "Helix", "Hell", "Hellion", "Hempy", "Hen", "Hendo", "Henry", "Herc", "Herky", "Herma", "Hershey", "Hewit", "Hewy", "Hexad", "High", "Hike", "Hilda", "Hill", "Hind", "Hippo", "Hippy", "Hiram", "Hire", "Hitman", "Hoax", "Hobby", "Hobie", "Hobo", "Hocus", "Hog", "Hogger", "Hoist", "Hoku", "Hole", "Homeplate", "Homer", "Honk", "Honor", "Hook", "Hooper", "Hoosier", "Hooter", "Hoover", "Hope", "Hops", "Hornet", "Horse", "Horseman", "Hoser", "Hoss", "Hot", "Hotdog", "Hotlips", "Hound", "Howdy", "Howel", "Howl", "Hudson", "Hue", "Huge", "Huggy", "Hugo", "Hula", "Hulk", "Hummer", "Hungry", "Hunt", "Hunter", "Huntress", "Hura", "Hurc", "Hurds", "Hurky", "Hurly", "Huron", "Hurricane", "Husk", "Husker", "Husky", "Hustler", "Hydra", "Hydramatic", "Hydro", "Hymn", "Hyper", "Hypo", "Iceman", "Icer", "Idaho", "Idler", "Idol", "Igloo", "Iguana", "Ilka", "Image", "Impact", "Impy", "Indian", "Indy", "Info", "Instigator", "Intac", "Into", "Invader", "Ionic", "Ions", "Iota", "Iowa", "Iraqouis", "Irate", "Irish", "Iron", "Ironcrow", "Ironrod", "Irony", "Island", "Islands", "Ivan", "Ivanhoe", "Ivory", "Izod", "Jabba", "Jackal", "Jackdaw", "Jackpot", "Jaded", "Jagger", "Jaguar", "Jakal", "Jake", "Jalopy", "Jama", "Jambo", "Jamie", "Jammer", "Janus", "Jape", "Jarem", "Jason", "Jasper", "Java", "Jaws", "Jax", "Jaygo", "Jazz", "Jeckyl", "Jedi", "Jeep", "Jefferson", "Jena", "Jenny", "Jersey", "Jesse", "Jest", "Jester", "Jet", "Jethro", "Jiggs", "Jigsaw", "Jimbo", "Jinx", "Jive", "Jln", "Jockey", "Jody", "John", "Jojo", "Joke", "Joker", "Jolly", "Jonas", "Jordanian", "Josa", "Jose", "Joshua", "Joss", "Joyce", "Jstars", "Juba", "Judge", "Judgement", "Jug", "Juggle", "Juggs", "Juice", "Juicy", "Juliett", "Jumbo", "Jump", "Jungle", "Junior", "Junk", "Junker", "Jury", "Just", "Justice", "Juvat", "Kahona", "Kale", "Kang", "Kanza", "Kaput", "Karma", "Kate", "Kato", "Katt", "Kava", "Kayak", "Kaycee", "Kayo", "Keep", "Kelp", "Kelt", "Kemp", "Kennard", "Ketch", "Kevlar", "Keys", "Keystone", "Kharma", "Kick", "Killer", "Kin", "Kinky", "Kiowa", "Kirby", "Kirt", "Kiska", "Kite", "Kitsap", "Kitty", "Kittyhawk", "Kiwi", "Kleenex", "Knife", "Knight", "Knighthawk", "Knightrider", "Knot", "Known", "Koala", "Kokomo", "Kong", "Koo", "Krupa", "Kuwaiti", "Lactose", "Ladon", "Lag", "Laity", "Laker", "Lame", "Lance", "Lancer", "Land", "Lando", "Landslide", "Lang", "Larado", "Largo", "Lark", "Larma", "Larsen", "Larva", "Launches", "Lava", "Leaf", "Lee", "Legal", "Lehi", "Leica", "Lemay", "Lenna", "Leopard", "Leroy", "Lester", "Lethal", "Levi", "Lexus", "Libby", "Liberty", "Lido", "Life", "Lifter", "Light", "Lightning", "Lily", "Lima", "Lime", "Lined", "Liner", "Lion", "Lippo", "Lips", "Lisa", "Lite", "Litz", "Live", "Lloyd", "Lobby", "Lobo", "Lolly", "Loma", "Lone", "Loner", "Long", "Longhorn", "Lonny", "Look", "Lookout", "Loon", "Loop", "Loosefoot", "Loper", "Lotto", "Loud", "Louie", "Lout", "Lubo", "Lucid", "Lucky", "Lugo", "Lulu", "Lump", "Lumper", "Luna", "Lunar", "Lundy", "Lusk", "Lusty", "Luzon", "Lyneham", "Lynx", "Mac", "Mace", "Mach", "Macho", "Mack", "Mad", "Madras", "Maf", "Maffs", "Mafia", "Magas", "Magnet", "Magni", "Magnum", "Magoo", "Magpie", "Mai", "Mailtruck", "Maine", "Mainiac", "Mainsail", "Majan", "Makin", "Mako", "Mala", "Malan", "Male", "Malibu", "Mallard", "Malty", "Mamba", "Mango", "Maniac", "Manta", "Maple", "Maplecat", "Marathon", "Marcotte", "Marine", "Mariner", "Mark", "Marlin", "Mars", "Marshall", "Marta", "Mash", "Mast", "Mastadon", "Matador", "Matcom", "Math", "Matra", "Matt", "Maul", "Maverick", "Maxie", "May", "Mayo", "Mazda", "Mcasca", "Mcguire", "Meat", "Medellin", "Medevac", "Medusa", "Mega", "Melba", "Meld", "Mellow", "Melon", "Melrose", "Mercury", "Merl", "Mesquite", "Metal", "Meteor", "Metman", "Mexican", "Mexico", "Miami", "Mic", "Micah", "Micro", "Midas", "Middle", "Mig", "Mike", "Miler", "Milestone", "Milky", "Mill", "Miller", "Mime", "Mind", "Mine", "Ming", "Mingo", "Miniature", "Mint", "Minty", "Minuteman", "Misfit", "Missionary", "Mississippi", "Misty", "Mite", "Mobile", "Moccasin", "Mock", "Moffett", "Mogul", "Mohawk", "Moho", "Mojo", "Molly", "Molson", "Monarch", "Money", "Mongoose", "Monster", "Monty", "Monu", "Mooch", "Moody", "Mookie", "Moola", "Moon", "Moonlight", "Moor", "Moose", "Mop", "Mope", "Moped", "More", "Morning", "Mosel", "Motel", "Mother", "Motif", "Motley", "Motown", "Mould", "Mouse", "Mover", "Moxie", "Mozam", "Mua", "Mud", "Mudbug", "Muddy", "Muff", "Mugger", "Muif", "Mull", "Mullett", "Mung", "Murk", "Murky", "Muroc", "Museum", "Mushroom", "Music", "Musket", "Mussel", "Mustang", "Mystic", "Nabob", "Nacho", "Naf", "Nail", "Nalo", "Nancy", "Nanno", "Napper", "Nappy", "Narly", "Narragansett", "Nasa", "Nascar", "Nasty", "Nat", "Nathe", "National", "Nato", "Natro", "Navel", "Navy", "Nawt", "Nears", "Neptune", "Nest", "Nevin", "Newly", "Newport", "Newsreel", "Nickel", "Nigerian", "Nighthawk", "Nightmare", "Nightstaker", "Nightstalker", "Nightwatch", "Nimrod", "Ninja", "Niobe", "Niter", "Nitro", "Noah", "Noble", "Nodak", "Node", "Nomad", "Nomex", "Noose", "North", "Northern", "Norwegian", "Noster", "Note", "Nova", "Novar", "Now", "Nucar", "Nuke", "Numa", "Oak", "Oaky", "Occult", "Ocean", "Oceanside", "Odin", "Ogre", "Ohio", "Oiler", "Oilgate", "Oilwell", "Okie", "Okra", "Old", "Olds", "Olive", "Olmis", "Olson", "Olympic", "Omaha", "Omar", "Omega", "Omen", "Omni", "Ontario", "Onyx", "Opbat", "Opec", "Open", "Operations", "Opus", "Orbit", "Orca", "Orkin", "Osage", "Otis", "Outdo", "Outgun", "Outlaw", "Outrider", "Ouzo", "Overwork", "Ox", "Oxen", "Ozark", "Pacaf", "Paccaf", "Pacer", "Pack", "Packer", "Packers", "Pacom", "Pagan", "Pageboy", "Pago", "Pagus", "Pale", "Palm", "Palmetto", "Pan", "Panama", "Pansy", "Panton", "Pants", "Paola", "Para", "Paradise", "Pard", "Pardo", "Parrot", "Parry", "Part", "Party", "Pash", "Pasha", "Password", "Pat", "Patch", "Pathfinder", "Patio", "Patlo", "Patsy", "Patty", "Patuxent", "Pave", "Pax", "Payday", "Peace", "Peach", "Peak", "Pearl", "Pecos", "Pedro", "Peewee", "Pela", "Pelican", "Pelt", "Pence", "Penguin", "Penn", "Penny", "Pepsi", "Perco", "Percy", "Perk", "Perm", "Perma", "Pesky", "Pete", "Petit", "Petro", "Phantom", "Phaser", "Philander", "Phobia", "Phoenix", "Phony", "Photon", "Pickup", "Pike", "Pikeside", "Pilgrim", "Pilot", "Pinball", "Ping", "Pinion", "Pinto", "Pirate", "Pistol", "Piston", "Pit", "Pitbull", "Pitman", "Pitstop", "Pitt", "Pitts", "Pity", "Pixie", "Pizza", "Plantation", "Plasma", "Plate", "Plead", "Plf", "Plod", "Plow", "Plug", "Plush", "Pluto", "Poison", "Poke", "Poker", "Polar", "Pole", "Polecat", "Polet", "Polly", "Polo", "Pook", "Poppy", "Poppya", "Pork", "Port", "Posey", "Possum", "Potus", "Pounce", "Power", "Predator", "Presidential", "Pride", "Prime", "Primetime", "Primo", "Pro", "Prody", "Prong", "Props", "Prose", "Proton", "Proud", "Provo", "Prowl", "Psycho", "Public", "Pucallpa", "Puerto", "Puff", "Puget", "Pull", "Pulsar", "Puma", "Pure", "Push", "Pusher", "Putt", "Pyote", "Pyramid", "Pyrex", "Python", "Quail", "Quake", "Quarterback", "Quarterdeck", "Quartet", "Quasar", "Quebec", "Queen", "Quest", "Quick", "Quid", "Quote", "Rabble", "Rac", "Race", "Racer", "Rack", "Rafair", "Rage", "Ragin", "Rags", "Ragu", "Raid", "Raider", "Rail", "Rain", "Rainbow", "Rainy", "Raleigh", "Ralph", "Ram", "Rama", "Rambo", "Rampage", "Rampart", "Ramrod", "Ranch", "Rancho", "Rancid", "Randy", "Ranger", "Rank", "Rapid", "Raptor", "Rasca", "Rascal", "Raspberry", "Raster", "Rat", "Ratch", "Rats", "Rattler", "Rattlesnake", "Ratty", "Ravage", "Rave", "Raven", "Rawhide", "Rawly", "Raygun", "Raymond", "Razor", "Razorback", "Razz", "Razzy", "Reach", "Ready", "Reaper", "Rebel", "Recap", "Recon", "Red", "Redeye", "Redhook", "Redstone", "Reef", "Reg", "Reheat", "Relax", "Remit", "Remus", "Renegade", "Reno", "Repo", "Rescue", "Researcher", "Resist", "Retail", "Retriever", "Retro", "Revere", "Rex", "Reynard", "Rhino", "Rhody", "Ribby", "Rican", "Rick", "Rico", "Ridge", "Rifle", "Riley", "Rim", "Ringo", "Ringy", "Rio", "Ripley", "Ripper", "Ripsaw", "Risco", "Riser", "Risk", "Risky", "Ritz", "Ritzy", "Rival", "Rivet", "Riyal", "Roach", "Road", "Roadstead", "Robins", "Rocco", "Rock", "Rocket", "Rockfish", "Rocky", "Rodan", "Rodd", "Rodeo", "Rogue", "Rolex", "Roll", "Roller", "Roman", "Ronin", "Roof", "Rook", "Rooster", "Rope", "Roper", "Roscoe", "Rose", "Rouge", "Rough", "Roust", "Roving", "Rowdy", "Roxanne", "Royal", "Rubbens", "Rubber", "Ruby", "Ruckus", "Rude", "Rudy", "Ruin", "Rule", "Ruler", "Rumble", "Rummy", "Runny", "Rupee", "Rushmore", "Russo", "Rust", "Rustle", "Rusty", "Ryan", "Ryder", "Rye", "Saber", "Sabre", "Safe", "Saga", "Sageunay", "Saggy", "Said", "Sail", "Saint", "Salty", "Salvage", "Sam", "Sambo", "Sammy", "Samp", "San", "Sand", "Sandie", "Sandman", "Sandy", "Santa", "Sapper", "Sapphire", "Satan", "Satel", "Saucer", "Saucy", "Saudi", "Saul", "Savage", "Save", "Savor", "Savoy", "Savvy", "Sawbuck", "Saxon", "Scalp", "Scam", "Scandal", "Scarf", "Scarface", "Scars", "Scary", "Scat", "Scatter", "Scent", "Schooner", "Scoff", "Scoot", "Score", "Scorpion", "Scorpon", "Scout", "Scream", "Screamer", "Screw", "Scron", "Scrow", "Scuba", "Sea", "Seabee", "Seagull", "Seahawk", "Seat", "Selma", "Semi", "Seminole", "Senate", "Senegal", "Sentry", "Service", "Setup", "Shack", "Shade", "Shado", "Shadow", "Shady", "Shag", "Shaka", "Shake", "Shaku", "Shall", "Sham", "Shamrock", "Shamu", "Shane", "Shank", "Shape", "Sharjah", "Shark", "Shawl", "Sheena", "Sheep", "Shep", "Shifty", "Ship", "Shoba", "Shocker", "Shoe", "Shogun", "Shooter", "Shotgun", "Showbiz", "Shrimp", "Shuck", "Shunt", "Shut", "Shutr", "Shy", "Side", "Silent", "Silky", "Silva", "Silver", "Simba", "Sinner", "Site", "Sitka", "Six", "Skagit", "Skate", "Skater", "Skeeter", "Skidoo", "Skier", "Skim", "Skinner", "Skip", "Skull", "Skyhawk", "Skyking", "Skylark", "Skywatch", "Slacker", "Slag", "Slam", "Slang", "Slash", "Slayer", "Sled", "Sleek", "Slew", "Slick", "Slider", "Slingshot", "Slip", "Slog", "Slope", "Slot", "Sloth", "Sluff", "Slug", "Slugger", "Sluggo", "Slump", "Smart", "Smash", "Smasher", "Smog", "Smoke", "Smoky", "Smyth", "Snag", "Snake", "Snap", "Snapper", "Snapshot", "Snarl", "Sneaky", "Snick", "Sniff", "Snip", "Sniper", "Snoop", "Snoopy", "Snow", "Snowbird", "Sock", "Socks", "Soda", "Solex", "Sombrero", "Song", "Sonic", "Sooner", "Sooty", "Soupy", "Sour", "Sourdough", "South", "Southeast", "Space", "Spad", "Spade", "Spar", "Sparky", "Sparrow", "Sparta", "Spartan", "Spear", "Spectre", "Speed", "Speedo", "Sperm", "Spica", "Spice", "Spider", "Spike", "Spin", "Spirit", "Spit", "Spitfire", "Spock", "Spook", "Spooky", "Sport", "Spot", "Spotted", "Sprint", "Spruce", "Spry", "Spud", "Spun", "Spunk", "Spur", "Spurt", "Spy", "Stags", "Stake", "Stalk", "Stallion", "Starbird", "Starburst", "Starfish", "Stargate", "Start", "Startrek", "Stash", "Station", "Stave", "Stealth", "Steel", "Step", "Stetson", "Stick", "Stiff", "Stine", "Sting", "Stinger", "Stint", "Stipe", "Stitch", "Stob", "Stole", "Stomp", "Stone", "Stonewall", "Stork", "Storm", "Stormy", "Stout", "Straight", "Strat", "Stray", "Strike", "Stroke", "Struck", "Strut", "Stud", "Stump", "Sui", "Suitcase", "Sulphur", "Summit", "Sumo", "Sun", "Sundance", "Sundog", "Sunflower", "Sunliner", "Sunny", "Sunshine", "Sunspot", "Super", "Surf", "Sustic", "Swam", "Swamp", "Swap", "Swat", "Sweat", "Swede", "Swedic", "Sweet", "Swift", "Swig", "Swine", "Swing", "Swiss", "Switch", "Sword", "Swordfish", "Tabor", "Tacit", "Tack", "Tacky", "Taco", "Taffy", "Tajas", "Tak", "Take", "Tally", "Talon", "Tang", "Tangy", "Tank", "Tanker", "Tape", "Taper", "Tar", "Tarbox", "Tartan", "Taste", "Tasty", "Tater", "Tazz", "Teal", "Team", "Tempo", "Tennessee", "Termite", "Terra", "Tester", "Testline", "Teton", "Tex", "Texas", "Texon", "Thinker", "Thorus", "Thread", "Three", "Thud", "Thug", "Thumper", "Thunder", "Thunderbird", "Tica", "Tier", "Tigertail", "Tigre", "Timber", "Time", "Timon", "Tin", "Tina", "Tincup", "Tipple", "Tira", "Tire", "Titan", "Titus", "Toad", "Toby", "Toga", "Togo", "Toil", "Toiler", "Toler", "Toll", "Tomahawk", "Tomcat", "Tommy", "Toner", "Tong", "Tonga", "Tonight", "Tonto", "Tonus", "Toot", "Top", "Topcat", "Topdog", "Topgun", "Tophand", "Tophat", "Torch", "Torco", "Toreador", "Torf", "Toro", "Torque", "Tory", "Total", "Totty", "Toxi", "Toxic", "Track", "Tracker", "Trail", "Train", "Trans", "Transport", "Trap", "Trapper", "Travel", "Traw", "Trek", "Trend", "Trial", "Tribe", "Tricker", "Trident", "Trigger", "Trill", "Trim", "Trist", "Triton", "Troch", "Troit", "Trojan", "Troll", "Tron", "Trooper", "Tropic", "Troubleshooter", "Trout", "Truax", "Truce", "Trucker", "Trudy", "Trump", "Tryst", "Tuff", "Tug", "Tully", "Tulsa", "Tune", "Tuppy", "Turban", "Turbo", "Turf", "Turkish", "Tusker", "Tutty", "Twig", "Tyler", "Tyrol", "Ugh", "Ugly", "Ukn", "Uncle", "Unclouded", "Uniform", "Union", "Universal", "Unsew", "Until", "Upset", "Utah", "Utter", "Vader", "Vague", "Valor", "Valvo", "Vampire", "Vandal", "Vandy", "Vapor", "Vda", "Veda", "Vega", "Vegas", "Veil", "Vein", "Vela", "Velcro", "Velva", "Venom", "Venue", "Venus", "Vermont", "Verse", "Vesty", "Vetch", "Veto", "Vette", "Victory", "View", "Viga", "Viking", "Vinal", "Vine", "Vino", "Violent", "Viper", "Visor", "Vodka", "Void", "Volga", "Volt", "Volve", "Volvo", "Voodoo", "Vulcan", "Wafer", "Wager", "Wagon", "Wake", "Walden", "Wamo", "Wang", "Want", "Wappo", "War", "Warbonnet", "Wardog", "Warfighter", "Warhawk", "Warhorse", "Warlord", "Warm", "Warriors", "Warthog", "Wash", "Wasp", "Waterbug", "Watergate", "Watt", "Waylay", "Wear", "Weasel", "Wee", "Welby", "Wellington", "Wept", "Western", "Wgy", "Whisk", "Whistler", "White", "Wicked", "Widget", "Widow", "Wigan", "Wiki", "Wild", "Wildcat", "Wildwood", "Wiley", "Will", "Willow", "Wimpy", "Winder", "Winter", "Wise", "Wisen", "Witch", "Wizzen", "Wodan", "Wolf", "Wolfpack", "Wolverine", "Wooden", "Woodn", "Woody", "Work", "Worked", "Workhorse", "Worldwide", "Worn", "Worry", "Worth", "Wrangler", "Wrestler", "Wyatt", "Wylie", "Yalta", "Yamaha", "Yard", "Yellow", "Yellowblood", "Yoda", "Yogi", "Yonder", "Yosemite", "Yowl", "Yucca", "Yukla", "Yukon", "Yule", "Zag", "Zama", "Zapper", "Zeke", "Zepel", "Zesty", "Zeta", "Zinger", "Zippy", "Zola", "Zombie", "Zonal", "Zoom", "Zorba", "Zorro" }) + + + function Library.objectiveNames.get() + nextIndex = nextIndex + 1 + return names[nextIndex] + end +end diff --git a/Script/Library/RadioMessages.lua b/Script/Library/RadioMessages.lua new file mode 100644 index 0000000..00cf9d7 --- /dev/null +++ b/Script/Library/RadioMessages.lua @@ -0,0 +1,196 @@ +Library.radioMessages = { + + pilotEjecting = { + "Mayday, mayday! Taking fire, punching out now!", + "Engine's gone, I'm bailing! Mark my chute!", + "Bird's on fire, I'm out, call SAR!", + "Control's dead, ejecting! Watch for the seat!", + "Lost hydraulics, can't hold her! Bailing out!" + }, + pilotImHit = { + "I'm hit!", + "Taking fire!", + "Bird's hurt, trying to hold altitude!", + "Impact on fuselage, took a solid hit!", + "Taking damage, warning lights all over" + }, + pilotKillAir = { + "Splash one $1. Rejoining.", + "Good hit, $1 down! Checking six.", + "$1 down. I say again, splash one!", + "Kill confirmed. $1's smoking.", + "Splash one $1! Got him!", + "Splash confirmed! Repeat, $1 down.", + "$1 smoked, good kill.", + "$1 splashed, no chute seen." + }, -- "$1" should be "bandit" in audio version + pilotKillGround = { + "Splash one $1, repeat, target down. Eyes open for secondaries.", + "$1 neutralized, big boom. Watch your altitude, debris kicking up.", + "$1's a fireball, one less on the ground. Moving to next grid.", + "Jackpot on that $1, burning good. Stay sharp for air defense.", + "Scratch one $1, repeat scratch one.", + "$1's toast, good effect on target. Rolling out.", + "Got the $1, solid hit. Watch for air defense in that sector.", + "Splash $1, good kill.", + "$1's cooked, smoke's up." + }, -- "$1" should be "vehicle" in audio version + pilotKillShip = { + "Splash confirmed! Enemy $1 going under.", + "Direct hit! $1 burning and breaking up.", + "$1's out of the fight! Big secondary explosion, she's listing hard.", + "$1 neutralized, heavy smoke and debris in the water.", + "Splash confirmed! Hull's cracking, $1's going down.", + "$1's gone! Big fireball, multiple secondaries.", + "$1's dead in the water! Zero movement on deck." + }, -- "$1" should be "ship" in audio version + pilotKillStrike = { + "Target's gone, good splash. Structure's burning, over.", + "Direct hit, building's collapsing. No secondary fire yet.", + "Objective neutralized, smoke's up. Moving to egress.", + "Good impact on target, structure is down.", + "Target destroyed, heavy debris in the AO.", + "Target's leveled, no movement inside.", + "Structure is toast, solid hit.", + "Good hit, building's breaking apart." + }, + + pilotLaunchGuns = { "Guns!", "Guns, guns!" }, + pilotLaunchBruiser = "Bruiser!", + pilotLaunchFox1 = "Fox 1!", + pilotLaunchFox2 = "Fox 2!", + pilotLaunchFox3 = "Fox 3!", + pilotLaunchMagnum = "Magnum!", + pilotLaunchMissile = "Missile away!", + pilotLaunchPickle = { "Pickle!", "Bomb away!" }, + pilotLaunchRifle = "Rifle!", + pilotLaunchRocket = "Rockets!", + + pilotWarningAAA = { "Heads up, I'm seeing tracers. AAA fire.", "AAA lighting up, stay high.", "Flight, we've got heavy flak incoming, break!", "AAA tracer fire, they've got our range, don't stay straight!", "AAA's hot under us. Stay fast, don't linger!" }, + pilotWarningMANPADS = { "MANPADS launch! Flare, flare, flare!", "Flight, MANPADS in the air. Dump flares, now!", "Coming from the ground, MANPADS hot!", "Go defensive, MANPADS off your nose! Flare, flare!", "MANPADS just came up from the deck, break hard and pop everything!" }, + pilotWarningSAMLaunch = { "Spike! SAM just launched, break!", "SAM up! Defensive now!", "Launch! SAM, coming fast, pump chaff, go cold!", "SAM in the air, break hard!", "SAM fired, visual smoke! Extend, extend!" }, + + atcSafeLanding = { "Be advised: $1 is wheels down at $2 and clear of runway.", "All aircraft, $1 has landed at $2 and vacated active. Runway is open for next inbound.", "Traffic, $1 is on deck at $2 and heading to parking. Runway clear.", "All flights, $1 just rolled out at $2 and cleared the active.", "Heads up, $1 landed at $2 and moving to the ramp. Runway available for next approach." }, + atcSafeLandingPlayer = { "$1, wheels on deck, welcome back. You may taxi to the parking area.", "$1, good copy on landing. Exit when able, proceed to the parking area.", "$1, touchdown confirmed. Continue to parking.", "$1, welcome home. Clear of runway and taxi to parking area.", "$1, nice landing. Taxi to parking when ready." }, + + awacsPicture = { + "$1, picture's up.$2", + "$1, sending updated picture.$2", + "$1, picture transmitted.$2", + "$1, sending picture, over.$2", + "$1, picture is live.$2", + }, + awacsPictureClear = { "$1, picture clear.", "$1, picture is clean.", "$1, picture clear, nothing airborne.", "$1, picture clean at this time." }, + + commandBlueOnBlue = + { + "Check fire! You hit a friendly!", + "Break, break! You just tagged blue!", + "Abort! You hit a friendly asset!", + "Cease fire, check your IFF!", + "Blue-on-blue, blue-on-blue!", + }, + commandFriendlyDown = { + "All flights, we lost a friendly. $1 is down. Eyes open, hostiles still active in the area.", + "Friendly bird down, I repeat, friendly bird down. $1 went cold. Stay sharp, possible threat still live.", + "All squadrons, heads up. $1 just took a hit and is down hard. Keep it tight.", + "We've lost $1. No chute sighted. Threat's still out there.", + "Friendly down. $1 is out of the fight. Maintain altitude discipline and check six.", + }, + commandFriendlyPilotOnGround = "Ejected pilot is stranded on the ground. Launch CSAR operations immediately.", + commandNewEnemyAircraft = { + "Be advised, enemy fighters airborne from $2, heading unknown.", + "To all flights, enemy fighters just launched from $2, expect contact in your sector.", + "Flights, bogeys off $2, monitor radar and standby.", + "Enemy birds wheels-up from $2, likely pushing toward your AO.", + "Hostile aircraft launch confirmed from $2, maintain alert status." + }, + commandMissionComplete = { + "All flights, mission objectives confirmed complete. You're clear to RTB.", + "All leads, targets neutralized. Good work out there. Proceed to home plate.", + "All squadrons, job's done. Exit the AO on your current route and check in with tower on approach for recovery.", + "All flights, tasking is finished. Return to base, tower will advise final.", + "All squadrons, good job, we're outta here. Break off and RTB." + }, + commandObjectiveComplete = { + "All callsigns, objective $1 complete. Proceed to next objective.", + "All flights, objective $1 is in the bag. Move to next phase as briefed.", + "Flight leads, objective $1 secured. You're clear to proceed with the remainder.", + "Work's done on objective $1. Push on to your next set.", + "All flights, objective $1 wrapped, keep on the good work.", + "All callsigns, objective $1's done. Proceed direct to next waypoint." + }, + commandObjectiveCoordinates = "We have no exact coordinates for objective $1.\nObjective should be located near:\n$2\n\nFly $3 to reach the objective.", + commandObjectiveCoordinatesPrecise = "We have exact coordinates for objective $1.\nObjective coordinates are:\n$2\n\nFly $3 to reach the objective.", + commandObjectivesManyLeft = { + "Stay focused, people. We still have work to do.\n$1", + "Our work is not done yet, we have a lot to do.\n$1", + "Flights, you've got a couple of tasks left, keep moving down the list.\n$1", + "Still a few objectives outstanding, don't break until they're complete.\n$1", + "Alright people, keep your focus, more work ahead before you can head home.\n$1", + "All flights, maintain timeline. You've got more boxes to check before RTB.\n$1" + }, + commandObjectivesOneLeft = { + "All flights, you've got one last task before you're done.\n$1", + "Flights, almost home. One objective remaining, then you're RTB.\n$1", + "All flights, you're down to the final push. Complete this and you're done.\n$1", + "All leads, one more on the board. Finish it and head back to base.\n$1", + "Flights, you're not clear yet. One last objective to wrap up.\n$1", + "Come on people, one last push and we're done.\n$1", + "Just one objective to complete and we're done.\n$1" + }, + + jtacSmokeOK = { + "$1, target marked with $2 smoke, over.", + "$1, $2 smoke out, standby for visual confirmation.", + "$1, target marked, $2 smoke on deck.", + "$1, mark complete, look for $2 smoke north of the road.", + "$1, $2 smoke is up, call visual when you have it." + }, + jtacSmokeNoTarget = { + "$1, no target to mark at this time, over.", + "$1, negative mark, no target.", + "$1, no joy on target, unable to mark.", + "$1, no mark available, stand by for update.", + "$1, negative smoke, target not established." + }, + jtacSmokeAlreadyOut = { + "$1, target already marked, smoke on deck.", + "$1, mark's up, resmoke already in place, over.", + "$1, smoke is already out, advise when visual.", + "$1, you're good, mark is already on the target.", + "$1, target already marked with smoke." + }, + + pilotNewFriendlyAircraft = { + "$1 CAP launched from $2, we're heading toward AO.", + "$1 CAP just wheels-up from $2, we'll will soon be on station.", + "$1 airborne from $2, we're pushing to cover your AO.", + "$1 CAP launched from $2, expect blue air in your vicinity shortly.", + "$1 departed $2, we're setting up CAP near you." + }, + playerAwacsBogeyDope = { "$1, request bogey dope.", "$1, bogey dope." }, + playerAwacsPicture = { "$1, request picture.", "$1, picture when able.", "$1, need a picture.", "$1, request full picture.", "$1, picture update." }, + playerCommandMissionStatus = { + "Command, request update on mission status.", + "Command, request SITREP on current mission status, over.", + "Command, how's the mission picture? Any updates?", + "Command, confirm progress on objectives, over.", + "Command, need an update on situation, what's the status?", + "Command, mission timeline check, are we on schedule?" + }, + playerCommandRequireObjectives = { + "Command, request objective $1 coordinates, over.", + "Command, send me grid for objective $1.", + "Command, need objective $1 location, over.", + "Command, pass coordinates for objective $1.", + "Command, confirm grid on objective $1." + }, + playerJTACSmoke = { + "$1, request smoke on objective $2, over.", + "$1, mark objective $2 with smoke, how copy?", + "$1, need a smoke mark on objective $2, standby for talk-on.", + "$1, can you pop smoke on objective $2? Need visual confirmation.", + "$1, give me a smoke mark near objective $2, confirm when ready.", + }, +} diff --git a/Script/Library/SAMRanges.lua b/Script/Library/SAMRanges.lua new file mode 100644 index 0000000..3060509 --- /dev/null +++ b/Script/Library/SAMRanges.lua @@ -0,0 +1,17 @@ +-- ==================================================================================== +-- ENGAGEMENT RANGES OF VARIOUS SAM SYSTEMS +-- ==================================================================================== + +Library.samRanges = { + ["*HAWK"] = DCSEx.converter.nmToMeters(25), + ["*NASAMS"] = DCSEx.converter.nmToMeters(8), + ["*Patriot"] = DCSEx.converter.nmToMeters(54), + ["*SA-2"] = DCSEx.converter.nmToMeters(23), + ["*SA-3"] = DCSEx.converter.nmToMeters(10), + ["*SA-5"] = DCSEx.converter.nmToMeters(4.5), + ["*SA-6"] = DCSEx.converter.nmToMeters(13), + ["*SA-10"] = DCSEx.converter.nmToMeters(65), + ["*SA-11"] = DCSEx.converter.nmToMeters(20), + + ["Osa 9A33 ln"] = DCSEx.converter.nmToMeters(8), +} diff --git a/Script/Library/Tasks.lua b/Script/Library/Tasks.lua new file mode 100644 index 0000000..5413f93 --- /dev/null +++ b/Script/Library/Tasks.lua @@ -0,0 +1,3 @@ +Library.tasks = {} + +--{{TASKS}}-- diff --git a/Script/Script.lua b/Script/Script.lua new file mode 100644 index 0000000..b5b517e --- /dev/null +++ b/Script/Script.lua @@ -0,0 +1,153 @@ +env.setErrorMessageBoxEnabled(true) -- Enable messageboxes for Lua errors + +DCSEx = {} +Library = {} +TUM = {} + +TUM.VERSION_NUMBER = 1 +TUM.VERSION_STRING = "0.1.250722" + +TUM.DEBUG_MODE = __DEBUG_MODE__ + +TUM.logLevel = { + INFO = 0, + WARNING = 1, + ERROR = 2 +} + +------------------------------------- +-- Prints and logs a debug message +-- @param message The message +-- @param logLevel Is it a warning, error or info messages (as defined in TUM.logLevel). Info messages are not printed out unless debug mode is enabled. +------------------------------------- +function TUM.log(message, logLevel) + logLevel = logLevel or TUM.logLevel.INFO + + if logLevel == TUM.logLevel.ERROR then + trigger.action.outText("ERROR: "..message, 3600) + env.warning("TUM - ERROR: "..message, false) + elseif logLevel == TUM.logLevel.WARNING then + trigger.action.outText("WARNING: "..message, 10) + env.warning("TUM - WARNING: "..message, false) + else + if TUM.DEBUG_MODE then -- Info messages are only printed out if debug mode is enabled + trigger.action.outText(message, 3) + end + + env.info("TUM: "..message, false) + end +end + +--[[DCS EXTENSIONS]]-- + +--[[LIBRARY]]-- + +--[[THE UNIVERSAL MISSION]]-- + +-------------------- +-- Module startup -- +-------------------- + +do + local function startUpMission() + TUM.hasStarted = false + + local coreSettings = { + multiplayer = false + } + + if #DCSEx.envMission.getPlayerGroups() == 0 then + TUM.log("No \"Player\" or \"Client\" aircraft slots have been found. Please fix this problem in the mission editor.", TUM.logLevel.ERROR) + return nil + end + + if world:getPlayer() then + coreSettings.multiplayer = false + + if #DCSEx.envMission.getPlayerGroups() > 1 then + TUM.log("Multiple players slots have been found in addition to the single-player \"Player\" aircraft. Please fix this problem in the mission editor.", TUM.logLevel.ERROR) + return nil + end + else + coreSettings.multiplayer = true + + if #DCSEx.envMission.getPlayerGroups(coalition.side.BLUE) == 0 and #DCSEx.envMission.getPlayerGroups(coalition.side.RED) == 0 then + TUM.log("Neither BLUE nor RED coalitions have player slots. Please make sure one coalition has player slots in the mission editor.", TUM.logLevel.ERROR) + return nil + end + + if #DCSEx.envMission.getPlayerGroups(coalition.side.BLUE) > 0 and #DCSEx.envMission.getPlayerGroups(coalition.side.RED) > 0 then + TUM.log("Both coalitions have player slots. The Universal Mission is a purely singleplayer/PvE experience and does not support PvP. Please make sure only one coalition has player slots in the mission editor.", TUM.logLevel.ERROR) + return nil + end + end + + if not TUM.territories.onStartUp() then return nil end + if not TUM.settings.onStartUp(coreSettings) then return nil end -- Must be called after TUM.territories.onStartUp() + if not TUM.playerCareer.onStartUp() then return nil end + if not TUM.intermission.onStartUp() then return nil end + if not TUM.airForce.onStartUp() then return nil end + + TUM.hasStarted = true + + return coreSettings + end + + if not startUpMission() then + trigger.action.outText("A critical error has happened, cannot start the mission.", 3600) + end +end + +------------------- +-- Event handler -- +------------------- +do + local eventHandler = {} + + function eventHandler:onEvent(event) + if not event then return end -- No event + + TUM.ambientRadio.onEvent(event) -- Must be first so other (more important) radio messages with interrupt the "ambient" ones + TUM.ambientWorld.onEvent(event) + TUM.objectives.onEvent(event) + TUM.playerScore.onEvent(event) + TUM.mission.onEvent(event) + end + + function TUM.onEvent(event) + eventHandler:onEvent(event) + end + + if TUM.hasStarted then + world.addEventHandler(eventHandler) + end +end + +-------------------------------------------- +-- Game clock, called every 10-20 seconds -- +-------------------------------------------- +do + local clockTick = -1 + + function TUM.onClockTick(arg, time) + local nextTickTime = time + math.random(10, 20) + clockTick = clockTick + 1 + + if clockTick % 4 == 0 then + if TUM.playerScore.onClockTick() then return nextTickTime end + if TUM.mission.onClockTick() then return nextTickTime end + elseif clockTick % 4 == 1 then + if TUM.airForce.onClockTick(TUM.settings.getPlayerCoalition()) then return nextTickTime end + elseif clockTick % 4 == 2 then + if TUM.supportAWACS.onClockTick() then return nextTickTime end + else + if TUM.airForce.onClockTick(TUM.settings.getEnemyCoalition()) then return nextTickTime end + end + + return nextTickTime + end + + if TUM.hasStarted then + timer.scheduleFunction(TUM.onClockTick, nil, timer.getTime() + math.random(10, 15)) + end +end diff --git a/Script/The Universal Mission/AirForce.lua b/Script/The Universal Mission/AirForce.lua new file mode 100644 index 0000000..921278d --- /dev/null +++ b/Script/The Universal Mission/AirForce.lua @@ -0,0 +1,195 @@ +-- ==================================================================================== +-- TUM.AIRFORCE - HANDLES THE FRIENDLY AND ENEMY COMBAT AIR PATROL +-- ==================================================================================== +-- ==================================================================================== + +TUM.airForce = {} + +do + local desiredUnitCount = { 4, 4 } -- Desired max number of aircraft in the air at any single time + local fighterGroups = { {}, {} } + + local playerCenter = nil + + local function getSkillLevel(side) + -- Friendly AI is always excellent + if side == TUM.settings.getPlayerCoalition() then return "Excellent" end + + local airForceLevel = TUM.settings.getValue(TUM.settings.id.ENEMY_AIR_FORCE) - 1 + + -- if airForceLevel <= 1 then return "Average" + -- elseif airForceLevel == 2 then return DCSEx.table.getRandom({"Average", "Good"}) + -- elseif airForceLevel == 3 then return DCSEx.table.getRandom({"Good", "High"}) + -- else return DCSEx.table.getRandom({"High", "Excellent"}) + -- end + + if airForceLevel <= 2 then return "Average" + elseif airForceLevel == 3 then return DCSEx.table.getRandom({"Average", "Good"}) + else return DCSEx.table.getRandom({"Good", "High", "Excellent"}) + end + end + + local function randomizeDesiredAircraftCount(side) + local airForceLevel = 0 + + if side == TUM.settings.getPlayerCoalition() then + if TUM.settings.getValue(TUM.settings.id.AI_CAP) == 1 then + airForceLevel = 2 + else + airForceLevel = 0 + end + else + airForceLevel = TUM.settings.getValue(TUM.settings.id.ENEMY_AIR_FORCE) - 1 + end + + if airForceLevel == 0 then + desiredUnitCount[side] = 0 + else + desiredUnitCount[side] = math.random(airForceLevel, math.ceil(airForceLevel * 1.5)) + 1 + end + end + + local function getAirborneUnitCount(side) + local count = 0 + + for _,id in ipairs(fighterGroups[side]) do + local g = DCSEx.world.getGroupByID(id) + if g then + count = count + g:getSize() + end + end + + return count + end + + local function launchNewAircraftGroup(side, airbases) + local groupSize = DCSEx.table.getRandom({ 1, 2, 2, 2, 2, 3, 3, 4 }) + groupSize = math.min(groupSize, desiredUnitCount[side] - getAirborneUnitCount(side)) + if groupSize <= 0 then return false end + + local faction = TUM.settings.getEnemyFaction() + if side == TUM.settings.getPlayerCoalition() then faction = TUM.settings.getPlayerFaction() end + + local units = Library.factions.getUnits(faction, DCSEx.enums.unitFamily.PLANE_FIGHTER, groupSize, true) + if not units or #units == 0 then return false end -- No aircraft found + + local launchAirbase = airbases[DCSEx.math.clamp(math.random(1, math.ceil(math.sqrt(#airbases))), 1, #airbases)] + local originPt = DCSEx.math.vec3ToVec2(launchAirbase:getPoint()) + + local groupInfo = DCSEx.unitGroupMaker.create( + side, Group.Category.AIRPLANE, + originPt, units, + { + moveTo = DCSEx.math.randomPointInCircle(TUM.objectives.getCenter(), TUM.objectives.getRadius(), 0), + silenced = true, + skill = getSkillLevel(side), + takeOff = true, + taskCAP = true, + unlimitedFuel = true + }) + + if not groupInfo then return false end + table.insert(fighterGroups[side], groupInfo.groupID) + + if side == TUM.settings.getPlayerCoalition() then + local newUnit = nil + local newGroup = DCSEx.world.getGroupByID(groupInfo.groupID) + if newGroup then newUnit = newGroup:getUnit(1) end + + local callsign = "FRIENDLY CAP" + local typeName = "Fighter aircraft" + if newUnit then + callsign = newUnit:getCallsign() + typeName = Library.objectNames.get(newUnit) + end + + TUM.radio.playForCoalition(TUM.settings.getPlayerCoalition(), "pilotNewFriendlyAircraft", { typeName, launchAirbase:getName() }, callsign) + else + TUM.radio.playForCoalition(TUM.settings.getPlayerCoalition(), "commandNewEnemyAircraft", { tostring(groupSize), launchAirbase:getName() }, "Command") + end + + return true + end + + local function updateAirForce(side) + if desiredUnitCount[side] <= 0 then return false end -- No airforce + if not TUM.DEBUG_MODE and #DCSEx.world.getPlayersInAir() == 0 then return false end -- No players currently in the air, don't spawn new AI aircraft (except in debug mode) + + local airbases = coalition.getAirbases(side) + if not airbases or #airbases == 0 then return false end -- No airbases found for this coalition, nowhere to takeoff from + + local center = nil + if side == TUM.settings.getPlayerCoalition() then + center = playerCenter + else + center = TUM.objectives.getCenter() + end + airbases = DCSEx.dcs.getNearestObjects(center, airbases) + + local airborneUnitCount = getAirborneUnitCount(side) + + if airborneUnitCount < desiredUnitCount[side] then + if math.random(1, 2 + math.ceil(airborneUnitCount / 2)) == 1 then + if math.random(1, 4) then + randomizeDesiredAircraftCount(side) + end + + return launchNewAircraftGroup(side, airbases) + end + end + + return false + end + + ---------------------------------------------------------- + -- Called on every mission update tick (every 10-20 seconds) + -- @param side The side for which air force must be updated + -- @return True if something was done this tick, false otherwise + ---------------------------------------------------------- + function TUM.airForce.onClockTick(side) + if TUM.mission.getStatus() == TUM.mission.status.NONE then return false end -- Not currenly in a mission + if TUM.objectives.getCount() <= 0 then return false end -- No objectives, nothing to defend for CAP + + return updateAirForce(side) + end + + function TUM.airForce.create() + TUM.airForce.removeAll() + TUM.log("Creating friendly and enemy air forces...") + + for side=1,2 do + randomizeDesiredAircraftCount(side) + end + end + + function TUM.airForce.removeAll() + if #fighterGroups[1] > 0 or #fighterGroups[2] > 0 then + TUM.log("Removing all friendly and enemy air force...") + end + + for side=1,2 do + for _,id in ipairs(fighterGroups[side]) do + DCSEx.world.destroyGroupByID(id) + end + end + + fighterGroups = { {}, {} } + end + + function TUM.airForce.onStartUp() + playerCenter = { x = env.mission.map.centerX, y = env.mission.map.centerY } + + local playerSlots = DCSEx.envMission.getPlayerGroups() + if #playerSlots > 0 then + playerCenter = { x = 0, y = 0 } + for _,p in ipairs(playerSlots) do + playerCenter.x = playerCenter.x + p.x + playerCenter.y = playerCenter.y + p.y + end + playerCenter.x = playerCenter.x / #playerSlots + playerCenter.y = playerCenter.y / #playerSlots + end + + return true + end +end diff --git a/Script/The Universal Mission/AmbientRadio.lua b/Script/The Universal Mission/AmbientRadio.lua new file mode 100644 index 0000000..05bf88e --- /dev/null +++ b/Script/The Universal Mission/AmbientRadio.lua @@ -0,0 +1,447 @@ +-- ==================================================================================== +-- TUM.AMBIENTRADIO - HANDLES AMBIENT CHATTER/RADIO MESSAGES REACTING TO A MISSION EVENT +-- ==================================================================================== +-- (local) doAmbientChatter(stringID, callsign, minimumDelaySinceLastMessage, replacements, centerPoint, maxRadiusInNM) +-- (local) onEventDead(event) +-- (local) onEventEjection(event) +-- (local) onEventHit(event) +-- (local) onEventKill(event) +-- (local) onEventLand(event) +-- (local) onEventLandingAfterEjection(event) +-- (local) onEventPlayerEnterUnit(event) +-- (local) onEventShootingStart(event) +-- (local) onEventShotFriendly(event) +-- (local) onEventShotHostile(event) +-- (local) onEventShot(event) +-- (local) onEventTakeOff(event) +-- TUM.ambientRadio.onEvent(event) +-- ==================================================================================== + +TUM.ambientRadio = {} + +do + local lastAmbientChatter = 0 + + ------------------------------------- + -- Plays an ambient radio message + ------------------------------------- + -- @param messageID ID of the radio message in scrambe.db.radioMessages + -- @param replacements String placeholders ($1, $2...) replacements in the message + -- @param callsign (optional) Callsign of the caller unit + -- @param minimumDelaySinceLastMessage (optional) If the last message happened less than this number of seconds from the current time, don't play the message + -- @param replacements (optional) Table of strings to use as replacement for $1, $2, $3... + -- @param centerPoint (optional) Center point used for max radius measurement + -- @param maxRadiusInNM (optional) Maximum radius (in nm) beyond which units will not recieve the message + ------------------------------------- + local function doAmbientChatter(messageID, replacements, callsign, minimumDelaySinceLastMessage, centerPoint, maxRadiusInNM) + -- Check parameters + callsign = callsign or "FLIGHT" + minimumDelaySinceLastMessage = minimumDelaySinceLastMessage or 1 + if maxRadiusInNM then maxRadiusInNM = DCSEx.converter.nmToMeters(maxRadiusInNM) end + + -- Don't play this message if another message was played too recently + local currentTime = timer.getAbsTime() + if currentTime < lastAmbientChatter + minimumDelaySinceLastMessage then return end + lastAmbientChatter = currentTime + + local players = coalition.getPlayers(TUM.settings.getPlayerCoalition()) + if not players or #players == 0 then return end + + for _,p in pairs(players) do + local tooFar = false + + -- If the message is restricted to a given zone, make sure the player isn't too far + if centerPoint and maxRadiusInNM then + if DCSEx.math.getDistance2D(centerPoint, p:getPoint()) > maxRadiusInNM then + tooFar = true + end + end + + if not tooFar then + TUM.radio.playForUnit(DCSEx.dcs.getObjectIDAsNumber(p), messageID, replacements, callsign) + end + end + end + + ---------------------------------------------- + -- Called when a DEAD event happens + -- + -- @param event Event data + ---------------------------------------------- + local function onEventDead(event) + if not event.initiator then return end -- No initiator + if Object.getCategory(event.initiator) ~= Object.Category.UNIT then return end -- Initiator isn't an unit + if event.initiator:getCoalition() ~= TUM.settings.getPlayerCoalition() then return end -- Not a friendly + + local unitDesc = event.initiator:getDesc() + + if unitDesc.category == Unit.Category.AIRPLANE or unitDesc.category == Unit.Category.HELICOPTER then + doAmbientChatter("commandFriendlyDown", { event.initiator:getCallsign() }, "COMMAND", 1) + end + end + + ---------------------------------------------- + -- Called when an EJECTION event happens + -- + -- @param event Event data + ---------------------------------------------- + local function onEventEjection(event) + if not event.initiator then return end -- No initator + if Object.getCategory(event.initiator) ~= Object.Category.UNIT then return end -- Initiator isn't an unit + if event.initiator:getCoalition() ~= TUM.settings.getPlayerCoalition() then return end -- Initiator isn't a friendly + if event.initiator:getPlayerName() then return end -- No "ejecting!" message for players, so it won't cut the "mission failed" music which is played at the same time + + doAmbientChatter("pilotEjecting", nil, event.initiator:getCallsign(), 1) + end + + ---------------------------------------------- + -- Called when a HIT event happens + -- + -- @param event Event data + ---------------------------------------------- + local function onEventHit(event) + if not event.target then return end -- No target + if Object.getCategory(event.target) ~= Object.Category.UNIT then return end -- Target isn't an unit + if event.target:getCoalition() ~= TUM.settings.getPlayerCoalition() then return end -- Target isn't a friendly + + -- Blue on blue event + if event.initiator then + if Object.getCategory(event.initiator) == Object.Category.UNIT then -- Attacker is an unit + if event.initiator:getCoalition() == TUM.settings.getPlayerCoalition() then -- Attacker is a friendly + doAmbientChatter("commandBlueOnBlue", nil, "COMMAND", 1) + return + end + end + end + + -- Friendly aircraft hit + if event.target:getDesc().category == Unit.Category.AIRPLANE or event.target:getDesc().category == Unit.Category.HELICOPTER then + if not event.initiator:getPlayerName() then -- Players don't radio out when they're hit + doAmbientChatter("pilotImHit", nil, event.target:getCallsign(), 3) + end + end + end + + ---------------------------------------------- + -- Called when a KILL event happens + -- + -- @param event Event data + ---------------------------------------------- + local function onEventKill(event) + if not event.target or not event.initiator then return end -- No event target or initiator + if Object.getCategory(event.initiator) ~= Object.Category.UNIT then return end -- Killer isn't an unit + if event.initiator:getCoalition() ~= TUM.settings.getPlayerCoalition() then return end -- Killer isn't a friendly + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) and event.initiator:getPlayerName() then return end -- No player chatter in MP + + local targetDesc = event.target:getDesc() + + local killerName = nil + if event.initiator:getCallsign() then + killerName = event.initiator:getCallsign() + else + killerName = event.initiator:getName() + end + + local killMessage = "pilotKillGround" + + if Object.getCategory(event.target) == Object.Category.SCENERY then + if TUM.objectives.getSceneryObjectObjective(event.target) then + doAmbientChatter("pilotKillStrike", nil, killerName, 1) + else + return -- Not a scenery target, congratulations you just bombed a random civilian target lol + end + elseif Object.getCategory(event.target) == Object.Category.STATIC then + killMessage = "pilotKillStrike" + doAmbientChatter("pilotKillStrike", nil, killerName, 1) + elseif Object.getCategory(event.target) == Object.Category.UNIT then + local killUnitType = "unit" + + if targetDesc.category == Unit.Category.AIRPLANE then + killMessage = "pilotKillAir" + + if event.target:hasAttribute("AWACS") then + killUnitType = "AWACS" + elseif event.target:hasAttribute("Tankers") then + killUnitType = "tanker" + elseif event.target:hasAttribute("Transports") then + killUnitType = "transport" + elseif event.target:hasAttribute("Bombers") then + killUnitType = "bomber" + elseif event.target:hasAttribute("Multirole fighters") or event.target:hasAttribute("Fighters") then + killUnitType = "fighter" + elseif event.target:hasAttribute("Interceptors") then + killUnitType = "interceptor" + elseif event.target:hasAttribute("UAVs") then + killUnitType = "UAV" + else + killUnitType = "aircraft" + end + elseif targetDesc.category == Unit.Category.HELICOPTER then + killMessage = "pilotKillAir" + if event.target:hasAttribute("Attack helicopters") then + killUnitType = "attack " + elseif event.target:hasAttribute("Transport helicopters") then + killUnitType = "transport " + else + killUnitType = "" + end + killUnitType = killUnitType.." "..DCSEx.table.getRandom({"helicopter", "helo", "chopper"}) + elseif targetDesc.category == Unit.Category.GROUND_UNIT then + if event.target:hasAttribute("Infantry") then return end -- No kill message for infantry (yet?) + killMessage = "pilotKillGround" + + if event.target:hasAttribute("MANPADS") then + killUnitType = "MANPADS" + elseif event.target:hasAttribute("Infantry") then + killUnitType = "infantry" + elseif event.target:hasAttribute("SR SAM") then + killUnitType = "short-range SAM" + elseif event.target:hasAttribute("SAM SR") then + killUnitType = "SAM search radar" + elseif event.target:hasAttribute("SAM TR") then + killUnitType = "SAM tracking radar" + elseif event.target:hasAttribute("SAM LL") then + killUnitType = "SAM launcher" + elseif event.target:hasAttribute("AAA") then + killUnitType = "AAA" + elseif event.target:hasAttribute("Air Defence") then + killUnitType = "air defense" + elseif event.target:hasAttribute("Artillery") then + killUnitType = "artillery" + elseif event.target:hasAttribute("Armored vehicles") then + killUnitType = "armor" + elseif event.target:hasAttribute("Trucks") then + killUnitType = "truck" + else + killUnitType = "vehicle" + end + elseif targetDesc.category == Unit.Category.SHIP then + killMessage = "pilotKillShip" + + if event.target:hasAttribute("Aircraft Carriers") then + killUnitType = "carrier" + elseif event.target:hasAttribute("Heavy armed ships") then + killUnitType = "warship" + elseif event.target:hasAttribute("Light armed ships") then + killUnitType = "armed ship" + elseif event.target:hasAttribute("Unarmed ships") then + killUnitType = "cargo ship" + else + killUnitType = "ship" + end + elseif targetDesc.category == Unit.Category.STRUCTURE then + killMessage = "pilotKillStrike" + end + + killUnitType = Library.objectNames.get(event.target) + doAmbientChatter(killMessage, killUnitType, killerName, 1) + end + end + + ---------------------------------------------- + -- Called when a LAND event happens + -- + -- @param event Event data + ---------------------------------------------- + local function onEventLand(event) + if not event.initiator then return end -- No event initiator + if Object.getCategory(event.initiator) ~= Object.Category.UNIT then return end -- Initiator isn't an unit + if event.initiator:getCoalition() ~= TUM.settings.getPlayerCoalition() then return end -- Not a friendly + + local baseName = "AIRBASE" + if event.place then + baseName = event.place:getName():upper() + end + + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) or not event.initiator:getPlayerName() then + doAmbientChatter("atcSafeLanding", {event.initiator:getCallsign()}, baseName.." ATC", 1) + end + end + + ---------------------------------------------- + -- Called when a LANDING_AFTER_EJECTION event happens + -- + -- @param event Event data + ---------------------------------------------- + local function onEventLandingAfterEjection(event) + if not event.initiator then return end -- No event initiator + if event.initiator:getCoalition() ~= TUM.settings.getPlayerCoalition() then return end -- Not a friendly + + doAmbientChatter("commandFriendlyPilotOnGround", nil, "COMMAND", 1) + end + + ---------------------------------------------- + -- Called when a PLAYER_ENTER_UNIT event happens + -- + -- @param event Event data + ---------------------------------------------- + local function onEventPlayerEnterUnit(event) + if not event.initiator then return end -- No event initiator + + -- TODO + end + + ---------------------------------------------- + -- Called when a SHOOTING_START event happens + -- + -- @param event Event data + ---------------------------------------------- + local function onEventShootingStart(event) + if not event.initiator then return end -- No event initiator + if Object.getCategory(event.initiator) ~= Object.Category.UNIT then return end -- Initiator isn't an unit + + -- Plane or helicopter + if event.initiator:getDesc().category == Unit.Category.AIRPLANE or event.initiator:getDesc().category == Unit.Category.HELICOPTER then + if event.initiator:getCoalition() == TUM.settings.getPlayerCoalition() then + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) and event.initiator:getPlayerName() then return end -- No player chatter in MP + doAmbientChatter("pilotLaunchGuns", nil, event.initiator:getCallsign(), 2) + return + end + end + + -- AAA + if event.initiator:hasAttribute("AAA") and event.initiator:getCoalition() == TUM.settings.getEnemyCoalition() then + doAmbientChatter("pilotWarningAAA", nil, "Flight", 2) + return + end + end + + -------------------------------------------------------------- + -- Called when a SHOT event happens, with a friendly initiator + -- + -- @param event Event data + -------------------------------------------------------------- + local function onEventShotFriendly(event) + if not event.initiator then return end -- No event initiator + if Object.getCategory(event.initiator) ~= Object.Category.UNIT then return end -- Initiator isn't an unit + local unitCategory = event.initiator:getDesc().category + local weaponDesc = event.weapon:getDesc() + + if unitCategory == Unit.Category.AIRPLANE or unitCategory == Unit.Category.HELICOPTER then + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) and event.initiator:getPlayerName() then return end -- No player chatter in MP + + if weaponDesc.category == Weapon.Category.BOMB then + doAmbientChatter("pilotLaunchPickle", nil, event.initiator:getCallsign(), 1) + elseif weaponDesc.category == Weapon.Category.ROCKET then + doAmbientChatter(event.initiator:getCoalition(),"pilotLaunchRocket", nil, event.initiator:getCallsign(), 1) + elseif weaponDesc.category == Weapon.Category.MISSILE then + if weaponDesc.missileCategory == Weapon.MissileCategory.AAM then + if weaponDesc.guidance == Weapon.GuidanceType.IR then + doAmbientChatter("pilotLaunchFox2", nil, event.initiator:getCallsign(), 1) + elseif weaponDesc.guidance == Weapon.GuidanceType.RADAR_ACTIVE then + doAmbientChatter("pilotLaunchFox3", nil, event.initiator:getCallsign(), 1) + elseif weaponDesc.guidance == Weapon.GuidanceType.RADAR_SEMI_ACTIVE then + doAmbientChatter("pilotLaunchFox1", nil, event.initiator:getCallsign(), 1) + else + doAmbientChatter("pilotLaunchMissile", nil, event.initiator:getCallsign(), 1) + end + elseif weaponDesc.missileCategory == Weapon.MissileCategory.ANTI_SHIP or weaponDesc.typeName == "weapons.missiles.AGM_84D" then + doAmbientChatter("pilotLaunchBruiser", nil, event.initiator:getCallsign(), 1) + elseif weaponDesc.guidance == Weapon.GuidanceType.RADAR_PASSIVE then + doAmbientChatter("pilotLaunchMagnum", nil, event.initiator:getCallsign(), 1) + else + doAmbientChatter("pilotLaunchRifle", nil, event.initiator:getCallsign(), 1) + end + end + -- elseif unitCategory == Unit.Category.GROUND_UNIT then + -- if event.initiator:hasAttribute("MANPADS") then + -- -- Do nothing, no message for MANPADS + -- elseif event.initiator:hasAttribute("IR Guided SAM") then + -- -- doAmbientChatter("Friendly SAM engaging", nil, "Air defense HQ", 2) + -- elseif event.initiator:hasAttribute("SAM") then + -- -- doAmbientChatter("Friendly SAM engaging", nil, "Air defense HQ", 2) + -- end + -- elseif unitCategory == Unit.Category.SHIP then + -- TODO + end + end + + --------------------------------------------------------------------- + -- Called when a SHOT event happens, with a hostile or neutral initiator + -- + -- @param event Event data + --------------------------------------------------------------------- + local function onEventShotHostile(event) + if not event.initiator then return end -- No event initiator + if Object.getCategory(event.initiator) ~= Object.Category.UNIT then return end -- Initiator isn't an unit + + if event.initiator:getDesc().category == Unit.Category.AIRPLANE or event.initiator:getDesc().category == Unit.Category.HELICOPTER then + -- if weaponDesc.category == Weapon.Category.MISSILE then + -- if weaponDesc.missileCategory == Weapon.MissileCategory.AAM then + -- doAmbientChatter("Missile!", nil, nil, 2, event.initiator:getPoint(), 8) + -- end + -- end + elseif event.initiator:getDesc().category == Unit.Category.GROUND_UNIT or event.initiator:getDesc().category == Unit.Category.SHIP then + if event.initiator:hasAttribute("MANPADS") then + doAmbientChatter("pilotWarningMANPADS", nil, nil, 2, event.initiator:getPoint(), 8) + elseif event.initiator:hasAttribute("IR Guided SAM") then + doAmbientChatter("pilotWarningSAMLaunch", nil, nil, 2, event.initiator:getPoint(), 12) + elseif event.initiator:hasAttribute("SAM SR") then + doAmbientChatter("pilotWarningSAMLaunch", nil, nil, 2, event.initiator:getPoint(), 12) + elseif event.initiator:hasAttribute("SAM") or event.initiator:hasAttribute("SAM LL") or event.initiator:hasAttribute("SAM CC") or event.initiator:hasAttribute("SAM LR") then + doAmbientChatter("pilotWarningSAMLaunch", nil, nil, 2, event.initiator:getPoint(), 24) + end + end + end + + ----------------------------------- + -- Called when a SHOT event happens + -- + -- @param event Event data + ----------------------------------- + local function onEventShot(event) + if not event.initiator then return end -- No event initiator + if not event.weapon then return end -- No weapon shot, abort + + if event.initiator:getCoalition() == TUM.settings.getPlayerCoalition() then + onEventShotFriendly(event) + else + onEventShotHostile(event) + end + end + + ---------------------------------------------- + -- Called when a TAKEOFF event happens + -- + -- @param event Event data + ---------------------------------------------- + local function onEventTakeOff(event) + if not event.initiator then return end -- No event initiator + if event.initiator:getCoalition() ~= TUM.settings.getPlayerCoalition() then return end -- Not a friendly + + local airbaseName = "airbase" + if event.place then airbaseName = event.place:getName() end + + local callsign = event.initiator:getCallsign() or "aircraft" + + doAmbientChatter("Fly safe, "..callsign.."!", nil, airbaseName) -- TODO: proper message + end + + ------------------------------------- + -- Called when an event is raised + -- @param event The DCS World event + ------------------------------------- + function TUM.ambientRadio.onEvent(event) + if event.id == world.event.S_EVENT_DEAD then + onEventDead(event) + elseif event.id == world.event.S_EVENT_EJECTION then + onEventEjection(event) + elseif event.id == world.event.S_EVENT_HIT then + onEventHit(event) + elseif event.id == world.event.S_EVENT_KILL then + onEventKill(event) + elseif event.id == world.event.S_EVENT_LAND then + onEventLand(event) + elseif event.id == world.event.S_EVENT_LANDING_AFTER_EJECTION then + onEventLandingAfterEjection(event) + -- elseif event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT then + -- onEventPlayerEnterUnit(event) + elseif event.id == world.event.S_EVENT_SHOOTING_START then + onEventShootingStart(event) + elseif event.id == world.event.S_EVENT_SHOT then + onEventShot(event) + -- elseif event.id == world.event.S_EVENT_TAKEOFF then + -- onEventTakeOff(event) + end + end +end diff --git a/Script/The Universal Mission/AmbientWorld.lua b/Script/The Universal Mission/AmbientWorld.lua new file mode 100644 index 0000000..c032942 --- /dev/null +++ b/Script/The Universal Mission/AmbientWorld.lua @@ -0,0 +1,75 @@ +-- ==================================================================================== +-- TUM.AMBIENTWORLD - HANDLES LITTLE DETAILS DESIGNED TO MAKE THE GAME WORLD MORE ALIVE +-- ==================================================================================== +-- ==================================================================================== + +TUM.ambientWorld = {} + +do + local groupIDs = {} + + --------------- + -- CONSTANTS -- + --------------- + local ESCAPING_CREW_ONE_TIME_OUT_OF = 6 -- one time of out this number, crew will flee destroyed vehicles + + local function doSpawnEscapingCrew(point3) + local options = { + disableWeapons = true, + hidden = true, + invisible = true, + moveBy = 250, + spreadDistance = math.random(2, 3), + } + + local unitTypes = Library.factions.getUnits(TUM.settings.getEnemyFaction(), DCSEx.enums.unitFamily.GROUND_INFANTRY, math.random(1, 3)) + if not unitTypes or #unitTypes == 0 then return end + + local groupInfo = DCSEx.unitGroupMaker.create(TUM.settings.getEnemyCoalition(), Group.Category.GROUND, DCSEx.math.vec3ToVec2(point3), unitTypes, options) + if groupInfo then + table.insert(groupIDs, groupInfo.groupID) + end + end + + + -- Called when a unit is destroyed + local function onEventDead(event) + if not event.initiator then return end -- Nothing was hit + + -- TODO: spawn from target scenery buildings and static structures + + if Object.getCategory(event.initiator) ~= Object.Category.UNIT then return end -- Target wasn't an unit + if event.initiator:getDesc().category ~= Unit.Category.GROUND_UNIT then return end -- Wasn't a ground unit + if not event.initiator:hasAttribute("Vehicles") then return end -- Wasn't a vehicle + if event.initiator:getCoalition() ~= TUM.settings.getEnemyCoalition() then return end -- Only spawn escaping crew from enemy vehicles + + if math.random(1, ESCAPING_CREW_ONE_TIME_OUT_OF) ~= 1 then return end -- Do not spawn every time + + TUM.log("Spawning crew escaping from destroyed unit "..event.initiator:getName()) + timer.scheduleFunction( + doSpawnEscapingCrew, + event.initiator:getPoint(), + timer.getTime() + math.random(4, 7) + ) + end + + function TUM.ambientWorld.removeAll() + for _,id in ipairs(groupIDs) do + DCSEx.world.destroyGroupByID(id) + end + + groupIDs = {} + end + + ------------------------------------- + -- Called when an event is raised + -- @param event The DCS World event + ------------------------------------- + function TUM.ambientWorld.onEvent(event) + if not event then return end -- No event + + if event.id == world.event.S_EVENT_DEAD then + onEventDead(event) + end + end +end diff --git a/Script/The Universal Mission/DebugMenu.lua b/Script/The Universal Mission/DebugMenu.lua new file mode 100644 index 0000000..fcc134e --- /dev/null +++ b/Script/The Universal Mission/DebugMenu.lua @@ -0,0 +1,79 @@ +-- ==================================================================================== +-- TUM.DEBUGMENU - HANDLES THE F10 DEBUG MENU +-- ==================================================================================== +-- (local) doMarkersBoom() +-- TUM.debugMenu.onStartUp() +-- ==================================================================================== + +TUM.debugMenu = {} + +do + local function doMarkersAirBoom() + local panels = world.getMarkPanels() + local boomCount = 0 + + for _,p in pairs(panels) do + local nearestPoint = nil + local nearestDistance = 99999999 + + if p.text:lower() == "airboom" then + for _,c in pairs({ Unit.Category.AIRPLANE, Unit.Category.HELICOPTER}) do + for _,u in DCSEx.world.getAllUnits(nil, c) do + local distance = DCSEx.math.getDistance3D(p.pos, u:getPoint()) + if distance < nearestDistance then + nearestDistance = distance + nearestPoint = u:getPoint() + end + end + end + + if nearestPoint then + trigger.action.explosion(nearestPoint, 1024) + boomCount = boomCount + 1 + end + end + end + + TUM.log("Detonated "..tostring(boomCount).. " \"airboom\" marker(s).") + end + + local function doMarkersBoom() + local panels = world.getMarkPanels() + local boomCount = 0 + for _,p in pairs(panels) do + if p.text:lower() == "boom" then + trigger.action.explosion(p.pos, 8192) + boomCount = boomCount + 1 + end + end + + TUM.log("Detonated "..tostring(boomCount).. " \"boom\" marker(s).") + end + + local function doAwardPointsAndObjectives() + TUM.playerScore.award(100, "debug cheat") + TUM.playerScore.awardCompletedObjective() + end + + local function doSimulatePlayerLanding() + -- TUM.playerCareer.awardScore(TUM.playerScore.getScore(), TUM.playerScore.getCompletedObjectives()) + + local event = { + id = world.event.S_EVENT_LAND, + initiator = coalition.getPlayers(TUM.settings.getPlayerCoalition())[1] + } + + TUM.onEvent(event) + end + + function TUM.debugMenu.createMenu() + if not TUM.DEBUG_MODE then return end + + local rootMenu = missionCommands.addSubMenu("[DEBUG]") + missionCommands.addCommand("Detonate \"boom\" map markers", rootMenu, doMarkersBoom, nil) + missionCommands.addCommand("Detonate aircraft near \"airboom\" map markers", rootMenu, doMarkersAirBoom, nil) + missionCommands.addCommand("Award 100 points and 1 objective", rootMenu, doAwardPointsAndObjectives, nil) + missionCommands.addCommand("Simulate player landing", rootMenu, doSimulatePlayerLanding, nil) + missionCommands.addCommand("Reset player stats", rootMenu, TUM.playerCareer.reset, nil) + end +end diff --git a/Script/The Universal Mission/EnemyAirDefense.lua b/Script/The Universal Mission/EnemyAirDefense.lua new file mode 100644 index 0000000..ee88879 --- /dev/null +++ b/Script/The Universal Mission/EnemyAirDefense.lua @@ -0,0 +1,242 @@ +-- ==================================================================================== +-- TUM.ENEMYAIRDEFENSE - HANDLES THE OPFOR AIR DEFENSE +-- ==================================================================================== +-- (local) addSAMCoverage(point2) +-- (local) isPointCoveredBySAM(point2) +-- (local) createEnemyStrategicAirDefense() +-- (local) createEnemyScatteredAirDefense() +-- TUM.enemyAirDefense.onStartUp() +-- ==================================================================================== + +TUM.enemyAirDefense = {} + +do + local AIR_DEFENSE_RANGE = { -- in meters + [DCSEx.enums.unitFamily.AIRDEFENSE_MANPADS] = 750, + [DCSEx.enums.unitFamily.AIRDEFENSE_AAA_STATIC] = 1500, + [DCSEx.enums.unitFamily.AIRDEFENSE_AAA_MOBILE] = 1500, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT_IR] = 6000, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT] = 8000, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_MEDIUM] = 35000, + [DCSEx.enums.unitFamily.AIRDEFENSE_SAM_LONG] = 70000, + } + + local airDefenseGroups = {} -- Stores info about all air defense groups (groupID, point2, range and unitFamily) + + local function getPointDefenseFamily(forceSHORAD) + local airDefenseLevel = TUM.settings.getValue(TUM.settings.id.ENEMY_AIR_DEFENSE) - 1 + + if airDefenseLevel > 1 and forceSHORAD then + return DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT + end + + if airDefenseLevel <= 1 then return DCSEx.table.getRandom({DCSEx.enums.unitFamily.AIRDEFENSE_AAA_MOBILE, DCSEx.enums.unitFamily.AIRDEFENSE_AAA_MOBILE, DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT_IR}) + elseif airDefenseLevel == 2 then return DCSEx.table.getRandom({DCSEx.enums.unitFamily.AIRDEFENSE_AAA_MOBILE, DCSEx.enums.unitFamily.AIRDEFENSE_AAA_MOBILE, DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT_IR, DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT}) + elseif airDefenseLevel == 3 then return DCSEx.table.getRandom({DCSEx.enums.unitFamily.AIRDEFENSE_AAA_MOBILE, DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT_IR, DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT}) + else return DCSEx.table.getRandom({DCSEx.enums.unitFamily.AIRDEFENSE_AAA_MOBILE, DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT_IR, DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT, DCSEx.enums.unitFamily.AIRDEFENSE_SAM_SHORT}) + end + end + + local function getSkillLevel() + local airDefenseLevel = TUM.settings.getValue(TUM.settings.id.ENEMY_AIR_DEFENSE) - 1 + + if airDefenseLevel <= 1 then return "Average" + elseif airDefenseLevel == 2 then return DCSEx.table.getRandom({"Average", "Good", "Good", "High"}) + elseif airDefenseLevel == 3 then return DCSEx.table.getRandom({"High", "Excellent"}) + else return "Excellent" + end + end + + local function getEnemyPointUnitsProtectingPoint(point2) + local count = 0 + + for _,adg in ipairs(airDefenseGroups) do + if DCSEx.math.getDistance2D(adg.point2, point2) < adg.range then + count = count + 1 + end + end + + return count + end + + local function addAirDefenseGroup(side, faction, unitFamily, point) + if not point then return end + + -- Make sure no air defense unit is spawned in range of an allied player + for _,p in ipairs(coalition.getPlayers(TUM.settings.getPlayerCoalition())) do + if DCSEx.math.getDistance2D(point, DCSEx.math.vec3ToVec2(p:getPoint())) < AIR_DEFENSE_RANGE[unitFamily] then + return false + end + end + + local units = Library.factions.getUnits(faction, unitFamily, 1) + if not units or #units == 0 then return false end -- No valid units found + + local groupInfo = DCSEx.unitGroupMaker.create(side, Group.Category.GROUND, point, units, { skill = getSkillLevel() }) + if not groupInfo then return false end -- Failed to create group + + local adGroup = { + groupID = groupInfo.groupID, + point2 = point, + range = AIR_DEFENSE_RANGE[unitFamily], + unitFamily = unitFamily + } + + table.insert(airDefenseGroups, adGroup) + return true + end + + local function createPointAirDefense(airDefenseLevel, side, faction) + -- Add point air defense near all objectives + for i=1,TUM.objectives.getCount() do + local objPoint2 = TUM.objectives.getObjective(i).point2 -- Objective location + local desiredCount = math.random(math.floor(airDefenseLevel / 3), math.ceil(airDefenseLevel / 1.5)) -- Number of desired point air defense groups for this objective + -- local desiredCount = math.random(math.floor(airDefenseLevel / 2), math.ceil(airDefenseLevel / 1.25)) -- Number of desired point air defense groups for this objective + local adCount = desiredCount - getEnemyPointUnitsProtectingPoint(objPoint2) -- Number of point air defense groups already defending this objective + + local realCount = 0 + if adCount > 0 then -- Not enough groups? Create a few to reach the desired number + for j=1,adCount do + local forceSHORAD = false + -- if j == 1 then forceSHORAD = (math.random(1, 2) == 1) end + if j == 1 then forceSHORAD = (math.random(1, 5) <= 2) end + local unitFamily = getPointDefenseFamily(forceSHORAD) + local point = DCSEx.math.randomPointInCircle(objPoint2, AIR_DEFENSE_RANGE[unitFamily], AIR_DEFENSE_RANGE[unitFamily] / 2, land.SurfaceType.LAND) + + if addAirDefenseGroup(side, faction, unitFamily, point) then + realCount = realCount + 1 + else + TUM.log("Failed to add point air defense group near objective "..TUM.objectives.getObjective(i).name..".", TUM.logLevel.WARNING) + end + end + end + TUM.log(string.format("Spawned %d air defense unit(s) near objective %s.", realCount, TUM.objectives.getObjective(i).name)) + end + end + + local function createLocalAirDefense(airDefenseLevel, side, faction, objectivesCenter, objectivesRadius) + -- local count = math.ceil(math.random(2, 3) * math.max(1, math.sqrt(objectivesRadius) / 200) * math.sqrt(airDefenseLevel)) + local count = math.ceil(math.random(2, 3) * math.max(1, math.sqrt(objectivesRadius) / 300) * math.sqrt(airDefenseLevel)) + + if count <= 0 then return end + + local realCount = 0 + for i=1,count do + local forceSHORAD = false + -- if i <= math.max(1, count / 4) then forceSHORAD = true end + if i <= math.max(1, count / 6) then forceSHORAD = true end + local unitFamily = getPointDefenseFamily(forceSHORAD) + local point = DCSEx.math.randomPointInCircle(objectivesCenter, objectivesRadius, 0, land.SurfaceType.LAND) + + if addAirDefenseGroup(side, faction, unitFamily, point) then + realCount = realCount + 1 + else + TUM.log("Failed to add local air defense group.", TUM.logLevel.WARNING) + end + end + + TUM.log(string.format("Spawned %d air defense unit(s) around the objectives.", realCount)) + end + + local function createMANPADs(airDefenseLevel, side, faction, objectivesCenter, objectivesRadius) + local count = math.ceil(math.random(2, 3) * math.max(1, math.sqrt(objectivesRadius) / 120) * math.sqrt(airDefenseLevel)) + + if count <= 0 then return end + + local realCount = 0 + for _=1,count do + local point = DCSEx.math.randomPointInCircle(objectivesCenter, objectivesRadius, 0, land.SurfaceType.LAND) + + if addAirDefenseGroup(side, faction, DCSEx.enums.unitFamily.AIRDEFENSE_MANPADS, point) then + realCount = realCount + 1 + else + TUM.log("Failed to add local MANPADS group.", TUM.logLevel.WARNING) + end + end + + TUM.log(string.format("Spawned %d MANPADS around the objectives.", realCount)) + end + + local function addStrategicSAMSite(side, faction, unitFamily, objectivesCenter, objectivesRadius) + local point = DCSEx.math.randomPointInCircle(objectivesCenter, objectivesRadius) + + if point then + local zoneCenter = TUM.territories.getRandomPointInTerritory(side, land.SurfaceType.LAND) + if not zoneCenter then zoneCenter = TUM.territories.getRandomPointInTerritory(side) end + if not zoneCenter then zoneCenter = DCSEx.table.getRandom(TUM.territories.getTerritoryZones(side)) end + + local vector = { x = zoneCenter.x - point.x, y = zoneCenter.y - point.y } + vector = DCSEx.math.normalizeVec2(vector) + local distance = math.floor(DCSEx.math.getDistance2D(zoneCenter, point) * 2) + local step = math.min(distance / 10, 2500) + + for __=0,distance,step do + point.x = point.x + vector.x * step + point.y = point.y + vector.y * step + + if TUM.territories.getPointOwner(point) == side and land.getSurfaceType(point) == land.SurfaceType.LAND then + local distanceToObjectives = DCSEx.math.getDistance2D(objectivesCenter, point) + + if distanceToObjectives > AIR_DEFENSE_RANGE[unitFamily] * 2 then return false end -- Went too far, no need to spawn a SAM site here, it will never engage the players and only eat up CPU + + if distanceToObjectives > AIR_DEFENSE_RANGE[unitFamily] / 2 then + return addAirDefenseGroup(side, faction, unitFamily, point) + end + end + end + end + + return false + end + + local function createStrategicAirDefense(airDefenseLevel, side, faction, objectivesCenter, objectivesRadius) + if airDefenseLevel <= 1 then return end + + local count = math.random(1, math.ceil(airDefenseLevel / 1.5)) + local realCount = 0 + local rerollsLeft = 5 + for i=1,count do + local unitFamily = DCSEx.table.getRandom({DCSEx.enums.unitFamily.AIRDEFENSE_SAM_MEDIUM, DCSEx.enums.unitFamily.AIRDEFENSE_SAM_LONG}) + + if addStrategicSAMSite(side, faction, unitFamily, objectivesCenter, objectivesRadius) then + realCount = realCount + 1 + elseif rerollsLeft > 0 then -- Small chance to retry if couldn't spawn the SAM site last time + if math.random(1, 3) == 1 then + i = i - 1 + rerollsLeft = rerollsLeft - 1 + end + end + end + TUM.log(string.format("Spawned %d strategic SAM(s) on enemy territory.", realCount)) + end + + function TUM.enemyAirDefense.create() + TUM.enemyAirDefense.removeAll() -- Destroy all pre-existing air defense + TUM.log("Creating enemy air defense...") + + if TUM.objectives.getCount() == 0 then return end -- No objectives, no air defense + local airDefenseLevel = TUM.settings.getValue(TUM.settings.id.ENEMY_AIR_DEFENSE) - 1 + if airDefenseLevel <= 0 then return end -- No surface-to-air defense at all + if TUM.settings.getValue(TUM.settings.id.TASKING) == DCSEx.enums.taskFamily.ANTISHIP then return end -- No ground air defense during antiship strikes + + local side = TUM.settings.getEnemyCoalition() + local faction = TUM.settings.getEnemyFaction() + local objectivesCenter = TUM.objectives.getCenter() + local objectivesRadius = TUM.objectives.getRadius() + + createPointAirDefense(airDefenseLevel, side, faction) -- Must be created before the other layers, else it may think objective sites are already protected and fail to generate point defense + createLocalAirDefense(airDefenseLevel, side, faction, objectivesCenter, objectivesRadius) + createMANPADs(airDefenseLevel, side, faction, objectivesCenter, objectivesRadius) + createStrategicAirDefense(airDefenseLevel, side, faction, objectivesCenter, objectivesRadius) + end + + function TUM.enemyAirDefense.removeAll() + if #airDefenseGroups > 0 then TUM.log("Removing all enemy air defense...") end + + for _,g in ipairs(airDefenseGroups) do + DCSEx.world.destroyGroupByID(g.groupID) + end + + airDefenseGroups = {} + end +end diff --git a/Script/The Universal Mission/Intermission.lua b/Script/The Universal Mission/Intermission.lua new file mode 100644 index 0000000..74dd9c0 --- /dev/null +++ b/Script/The Universal Mission/Intermission.lua @@ -0,0 +1,121 @@ +-- ==================================================================================== +-- TUM.INTERMISSION - HANDLES THE MENU DISPLAYED BETWEEN MISSIONS +-- ==================================================================================== +-- TUM.intermission.createMenu() +-- TUM.intermission.onInitialize() +-- ==================================================================================== + +TUM.intermission = {} + +do + local missionZonesMarkers = {} + + local function doCommandStartMission() + local players = DCSEx.world.getAllPlayers() + + if #players == 0 then + trigger.action.outText("No player slots occupied. At least one client slot must be occupied by a player to start the mission.", 5) + trigger.action.outSound("UI-Error.ogg") + return + end + + if not TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then + for _,p in ipairs(players) do + if p:inAir() then + trigger.action.outText("Cannot start a single player mission while the player is in the air. Please land before starting the mission.", 5) + trigger.action.outSound("UI-Error.ogg") + return + end + end + end + + trigger.action.outText("Generating mission and loading assets, this can take some time...", 5) + + timer.scheduleFunction(TUM.mission.beginMission, false, timer.getTime() + 1) + -- TUM.mission.beginMission() + end + + local function setSetting(args) + if not args.id or not args.value then return end + + TUM.settings.setValue(args.id, args.value, false) + TUM.intermission.createMenu() + end + + local function createSubMenu(id, parentMenu) + local rootMenu = nil + + rootMenu = missionCommands.addSubMenu(TUM.settings.getSettingsName(id)..": "..TUM.settings.getValue(id, true), parentMenu) + for i,v in ipairs(TUM.settings.getPossibleValues(id)) do + local commandText = v + if id == TUM.settings.id.TARGET_LOCATION then + local playerCenter = DCSEx.world.getUnitsCenter(DCSEx.world.getAllPlayers()) + local distance = math.floor(DCSEx.converter.metersToNM(DCSEx.math.getDistance2D(playerCenter, DCSEx.zones.getByName(v)))) + commandText = commandText.."(≈"..tostring(distance).." nm)" + end + + missionCommands.addCommand(commandText, rootMenu, setSetting, { id = id, value = i, redrawMenu = true }) + end + end + + function TUM.intermission.createMissionZonesMarkers() + TUM.intermission.removeMissionZonesMarkers() + + local missionZones = TUM.territories.getMissionZones() + for _,z in ipairs(missionZones) do + local zoneOwner = TUM.territories.getPointOwner(z) + local color = DCSEx.dcs.getCoalitionColor(zoneOwner) + + local ids = DCSEx.zones.drawOnMap(z, { color[1], color[2], color[3], 1 }, { color[1], color[2], color[3], .5 }, DCSEx.enums.lineType.SOLID, true, true) + if ids then + table.insert(missionZonesMarkers, ids[1]) + table.insert(missionZonesMarkers, ids[2]) + end + end + end + + function TUM.intermission.removeMissionZonesMarkers() + for _,id in ipairs(missionZonesMarkers) do + trigger.action.removeMark(id) + end + + missionZonesMarkers = {} + end + + ------------------------------------- + -- Creates the mission briefing menu + ------------------------------------- + function TUM.intermission.createMenu() + missionCommands.removeItem() -- Clear the menu + + local briefingText = "Welcome to The Universal Mission for DCS World, a highly customizable mission available for single-player and PvE.\n\nOpen the communication menu and select the ''F10. Other'' option to access mission settings." + DCSEx.envMission.setBriefing(coalition.side.RED, briefingText) + DCSEx.envMission.setBriefing(coalition.side.BLUE, briefingText) + + TUM.intermission.createMissionZonesMarkers() -- Show the available mission zones on the F10 map + + missionCommands.addCommand("ℹ Display mission settings", nil, TUM.settings.printSettingsSummary, false) + + local settingsMenu = missionCommands.addSubMenu("✎ Change mission settings") + createSubMenu(TUM.settings.id.COALITION_BLUE, settingsMenu) + createSubMenu(TUM.settings.id.COALITION_RED, settingsMenu) + createSubMenu(TUM.settings.id.TASKING, settingsMenu) + createSubMenu(TUM.settings.id.TARGET_LOCATION, settingsMenu) + createSubMenu(TUM.settings.id.TARGET_COUNT, settingsMenu) + createSubMenu(TUM.settings.id.ENEMY_AIR_DEFENSE, settingsMenu) + createSubMenu(TUM.settings.id.ENEMY_AIR_FORCE, settingsMenu) + createSubMenu(TUM.settings.id.AI_CAP, settingsMenu) + TUM.playerCareer.createMenu() + missionCommands.addCommand("➤ Begin mission", nil, doCommandStartMission, nil) + TUM.debugMenu.createMenu() -- Append debug menu to other menus (if debug mode enabled) + end + + ------------------------------------- + -- Called on mission start up + -- @return True if started up properly, false if an error happened + ------------------------------------- + function TUM.intermission.onStartUp() + TUM.intermission.createMenu() -- Create the briefing menu + return true + end +end diff --git a/Script/The Universal Mission/Mission.lua b/Script/The Universal Mission/Mission.lua new file mode 100644 index 0000000..d13cebb --- /dev/null +++ b/Script/The Universal Mission/Mission.lua @@ -0,0 +1,208 @@ +-- ==================================================================================== +-- TUM.MISSION - HANDLES THE MAIN MISSION +-- ==================================================================================== +-- ==================================================================================== + +TUM.mission = {} + +TUM.mission.status = { + NONE = 0, + IN_PROGRESS = 1, + COMPLETED = 2, + FAILED = 3, +} + +TUM.mission.endCause = { -- Why did the mission end? + ABORTED = 1, + COMPLETED = 2, + FAILED = 3 +} + +do + local OBJECTIVES_REMINDER_INTERVAL = 5 + local missionStatus = TUM.mission.status.NONE + local objectivesReminderIntervalLeft = OBJECTIVES_REMINDER_INTERVAL + + function TUM.mission.getStatus() + return missionStatus + end + + local function closeMission(removeAllUnits) + if removeAllUnits then + TUM.airForce.removeAll() + TUM.ambientWorld.removeAll() + TUM.enemyAirDefense.removeAll() + TUM.objectives.removeAll() + end + + missionStatus = TUM.mission.status.NONE + end + + function TUM.mission.checkMissionStatus(silent) + silent = silent or false + if missionStatus ~= TUM.mission.status.IN_PROGRESS then return end + + if TUM.objectives.areAllCompleted() then + missionStatus = TUM.mission.status.COMPLETED + + DCSEx.dcs.outPicture("Pic-MissionComplete.png", 5, true, 0, 1, 1, 25, 1) + trigger.action.outSound("UI-MissionEnd.ogg") + + if not silent then + TUM.radio.playForCoalition(TUM.settings.getPlayerCoalition(), "commandMissionComplete", nil, "Command", true) + end + end + end + + function TUM.mission.beginMission(silent) + silent = silent or false + + closeMission(true) + TUM.intermission.removeMissionZonesMarkers() + + for _=1,TUM.settings.getValue(TUM.settings.id.TARGET_COUNT) do + TUM.objectives.add() + end + + if TUM.objectives.getCount() == 0 then + TUM.log("Couldn't create any objective, mission creation failed.", TUM.logLevel.WARNING) + closeMission(true) + return + end + + TUM.supportAWACS.create() -- Create the AWACS aircraft if it wasn't airborne already + + TUM.enemyAirDefense.create() -- Must be called once objectives have been created + TUM.airForce.create() -- Must be called once objectives have been created + TUM.missionMenu.create() -- Must be called once objectives have been created + + local briefingOrder = DCSEx.table.shuffle({1, 2, 3, 4, 5}) -- Just to make sure the same description is used twice + + local briefingText = "" + for i=1,TUM.objectives.getCount() do + local obj = TUM.objectives.getObjective(i) + briefingText = briefingText.."Objective "..obj.name..":\n" + local descriptions = Library.tasks[obj.taskID].description.briefing + + briefingText = briefingText..descriptions[DCSEx.math.clamp(briefingOrder[i], 1, #descriptions)] + if i < TUM.objectives.getCount() then + briefingText = briefingText.."\n\n" + end + end + + DCSEx.envMission.setBriefing(TUM.settings.getPlayerCoalition(), briefingText) + DCSEx.envMission.setBriefing(TUM.settings.getEnemyCoalition(), "") + + missionStatus = TUM.mission.status.IN_PROGRESS + + if not silent then + DCSEx.dcs.outPicture("Pic-MissionStart.png", 5, true, 0, 1, 1, 25, 1) + trigger.action.outSound("UI-MissionStart.ogg") + end + + trigger.action.outText("MISSION OBJECTIVES:\n"..TUM.mission.getSummaryString(), 10) + + objectivesReminderIntervalLeft = OBJECTIVES_REMINDER_INTERVAL + end + + function TUM.mission.getSummaryString(onlyShowIncomplete, doublePercentage) + onlyShowIncomplete = onlyShowIncomplete or false + if missionStatus == TUM.mission.status.NONE then return "" end + + local missionSummary = "" + for i=1,TUM.objectives.getCount() do + local o = TUM.objectives.getObjective(i) + + if o then + if not o.completed or not onlyShowIncomplete then + missionSummary = missionSummary.."- Objective "..o.name..": "..Library.tasks[o.taskID].description.short + if not o.completed then + missionSummary = missionSummary.." ("..TUM.objectives.getObjectiveProgress(i, doublePercentage)..")" + else + missionSummary = missionSummary.." [DONE!]" + end + + if i < TUM.objectives.getCount() then + missionSummary = missionSummary.."\n" + end + end + end + end + + return missionSummary + end + + function TUM.mission.endMission(endCause) + endCause = endCause or TUM.mission.endCause.ABORTED + + if endCause == TUM.mission.endCause.ABORTED then + DCSEx.dcs.outPicture("Pic-MissionAborted.png", 5, true, 0, 1, 1, 25, 1) + TUM.playerScore.reset(true, "mission aborted") + elseif endCause == TUM.mission.endCause.COMPLETED then + DCSEx.dcs.outPicture("Pic-MissionComplete.png", 5, true, 0, 1, 1, 25, 1) + elseif endCause == TUM.mission.endCause.FAILED then + DCSEx.dcs.outPicture("Pic-MissionFailed.png", 5, true, 0, 1, 1, 25, 1) + end + trigger.action.outSound("UI-MissionEnd.ogg") + + closeMission(true) + end + + ---------------------------------------------------------- + -- Called on every mission update tick (every 15 seconds) + -- @return True if a radio message or other output was triggered, false otherwise + ---------------------------------------------------------- + function TUM.mission.onClockTick() + if TUM.mission.getStatus() == TUM.mission.status.NONE then return false end -- Not currenly in a mission + if TUM.objectives.getCount() <= 0 then return false end -- No objectives + + objectivesReminderIntervalLeft = objectivesReminderIntervalLeft - 1 + if objectivesReminderIntervalLeft > 0 then return false end + + objectivesReminderIntervalLeft = OBJECTIVES_REMINDER_INTERVAL + + TUM.mission.playMissionSummaryRadioMessage(true, false) + return true + end + + ------------------------------------- + -- Called when an event is raised + -- @param event The DCS World event + ------------------------------------- + function TUM.mission.onEvent(event) + if missionStatus == TUM.mission.status.NONE then return end + if not event.initiator then return end + if Object.getCategory(event.initiator) ~= Object.Category.UNIT then return end + if not event.initiator:getPlayerName() then return end + + -- All objectives complete and all players on the ground? Mission is complete + if event.id == world.event.S_EVENT_RUNWAY_TOUCH or event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT or event.id == world.event.S_EVENT_PLAYER_LEAVE_UNIT then + if TUM.objectives.areAllCompleted() and #DCSEx.world.getPlayersInAir(TUM.settings.getPlayerCoalition()) == 0 then + TUM.mission.endMission(TUM.mission.endCause.COMPLETED) + end + end + + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return end + + -- When player dies in single-player, fail the mission + if event.id == world.event.S_EVENT_CRASH or event.id == world.event.S_EVENT_EJECTION or event.id == world.event.S_EVENT_PILOT_DEAD then + TUM.mission.endMission(TUM.mission.endCause.FAILED) + end + end + + function TUM.mission.playMissionSummaryRadioMessage(onlyShowIncomplete, delayed) + onlyShowIncomplete = onlyShowIncomplete or false + delayed = delayed or false + + local incompleteObjectives = TUM.objectives.getCount() - TUM.objectives.getCompletedCount() + + local messageID = "commandMissionComplete" + if incompleteObjectives > 1 then + messageID = "commandObjectivesManyLeft" + elseif incompleteObjectives == 1 then + messageID = "commandObjectivesOneLeft" + end + + TUM.radio.playForCoalition(TUM.settings.getPlayerCoalition(), messageID, { TUM.mission.getSummaryString(onlyShowIncomplete, true) }, "COMMAND", delayed) + end +end \ No newline at end of file diff --git a/Script/The Universal Mission/MissionMenu.lua b/Script/The Universal Mission/MissionMenu.lua new file mode 100644 index 0000000..1c9ccc5 --- /dev/null +++ b/Script/The Universal Mission/MissionMenu.lua @@ -0,0 +1,66 @@ +-- ==================================================================================== +-- TUM.MISSION - HANDLES THE F10 MENU DISPLAYED DURING A MISSION +-- ==================================================================================== +-- ==================================================================================== + +TUM.missionMenu = {} + +do + local function doCommandAbortMission() + TUM.mission.endMission(TUM.mission.endCause.ABORTED) + TUM.intermission.createMenu() + end + + local function doCommandMissionStatus() + TUM.radio.playForCoalition(TUM.settings.getPlayerCoalition(), "playerCommandMissionStatus", nil, "Flight", false) + TUM.mission.playMissionSummaryRadioMessage(false, true) + end + + local function doCommandObjectiveLocation(index) + local obj = TUM.objectives.getObjective(index) + if not obj then return end + + local messageSuffix = "" + if obj.preciseCoordinates then messageSuffix = "Precise" end + + TUM.radio.playForCoalition(TUM.settings.getPlayerCoalition(), "playerCommandRequireObjectives", { obj.name }, "Flight", false) + + local players = coalition.getPlayers(TUM.settings.getPlayerCoalition()) + for _,p in ipairs(players) do + local coordinates = DCSEx.world.getCoordinatesAsString(obj.waypoint3, false) + local braa = DCSEx.dcs.getBRAA(obj.waypoint3, p:getPoint(), false) + TUM.radio.playForUnit(DCSEx.dcs.getObjectIDAsNumber(p), "commandObjectiveCoordinates"..messageSuffix, { obj.name, coordinates, braa }, "Command", true) + end + end + + function TUM.missionMenu.create() + missionCommands.removeItem() -- Clear the menu + missionCommands.addCommand("☱ Mission status", nil, doCommandMissionStatus, nil) + + local objectivesMenuRoot = missionCommands.addSubMenu("Objectives") + for i=1,TUM.objectives.getCount() do + local obj = TUM.objectives.getObjective(i) + if obj then + local objRoot = missionCommands.addSubMenu("Objective "..obj.name.." ("..Library.tasks[obj.taskID].description.short..")", objectivesMenuRoot) + missionCommands.addCommand("Request objective coordinates", objRoot, doCommandObjectiveLocation, i) + TUM.supportJTAC.setupJTACOnObjective(i, objRoot) + end + end + + TUM.supportAWACS.createMenu() + + if not TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then -- If not multiplayer, add "show mission score" command + missionCommands.addCommand("★ Display mission score", nil, TUM.playerScore.showScore, nil) + end + + local abortRoot = missionCommands.addSubMenu("⬣ Abort mission") + if not TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) and DCSEx.io.canReadAndWrite() then + missionCommands.addCommand("✓ Confirm (all xp will be lost!)", abortRoot, doCommandAbortMission, nil) + else + missionCommands.addCommand("✓ Confirm", abortRoot, doCommandAbortMission, nil) + end + missionCommands.addCommand("✕ Cancel", abortRoot, DCSEx.dcs.doNothing, nil) + + TUM.debugMenu.createMenu() -- Append debug menu to other menus (if debug mode enabled) + end +end diff --git a/Script/The Universal Mission/Objectives.lua b/Script/The Universal Mission/Objectives.lua new file mode 100644 index 0000000..fdf1a71 --- /dev/null +++ b/Script/The Universal Mission/Objectives.lua @@ -0,0 +1,206 @@ +-- ==================================================================================== +-- TUM.OBJECTIVES - HANDLES THE MISSION OBJECTIVES +-- ==================================================================================== +-- ==================================================================================== + +TUM.objectives = {} + +do + local objectives = {} + + local function updateObjectiveText(index) + if index < 1 or index > #objectives then return end + + local taskDB = Library.tasks[objectives[index].taskID] + local suffix = "" + if DCSEx.table.contains(taskDB.flags, DCSEx.enums.taskFlag.MOVING) then + suffix = "\n(last position known to intel, target is moving)" + end + + local text = "Objective "..objectives[index].name..":\n"..taskDB.description.short.." ("..TUM.objectives.getObjectiveProgress(index)..")"..suffix + trigger.action.setMarkupText(objectives[index].markerTextID, text) + end + + function TUM.objectives.add() + local objective = TUM.objectivesMaker.create() + + if not objective then + TUM.log("Failed to spawn a group for objective #"..tostring(#objectives + 1)..".", TUM.logLevel.WARNING) + return false + end + + table.insert(objectives, objective) + updateObjectiveText(#objectives) + return true + end + + function TUM.objectives.getCount() + return #objectives + end + + function TUM.objectives.getCompletedCount() + if #objectives == 0 then return 0 end + + local count = 0 + for i=1,#objectives do + if objectives[i].completed then count = count + 1 end + end + + return count + end + + function TUM.objectives.getCenter() + local point2 = { x = 0, y = 0 } + + if #objectives == 0 then return point2 end + + for _,o in ipairs(objectives) do + point2.x = point2.x + o.point2.x + point2.y = point2.y + o.point2.y + end + + point2.x = point2.x / #objectives + point2.y = point2.y / #objectives + + return point2 + end + + function TUM.objectives.getRadius() + if #objectives < 2 then return 10000 end -- Default to a 10km radius if no objectives or a single objective + + local center = TUM.objectives.getCenter() + local radius = 0 + + for _,o in ipairs(objectives) do + local dist = DCSEx.math.getDistance2D(center, o.point2) + if dist > radius then radius = dist end + end + + return radius + end + + function TUM.objectives.getObjective(index) + if index < 1 or index > #objectives then return nil end + return DCSEx.table.deepCopy(objectives[index]) + end + + function TUM.objectives.getObjectiveProgress(index, doublePercentage) + doublePercentage = doublePercentage or false + if index < 1 or index > #objectives then return "" end + + if TUM.DEBUG_MODE then + return tostring(#objectives[index].completedUnitsID).."/"..tostring(math.max(1, #objectives[index].unitsID)) + else + local percentage = 0 + if #objectives[index].unitsID > 0 then + percentage = math.floor((#objectives[index].completedUnitsID / math.max(1, #objectives[index].unitsID)) * 100.0) + end + + if doublePercentage then + return tostring(percentage).."%%" + else + return tostring(percentage).."%" + end + end + end + + function TUM.objectives.removeAll() + TUM.log("Removing all objectives...") + + for _,o in ipairs(objectives) do + if o.groupID then + local g = DCSEx.world.getGroupByID(o.groupID) + if g then g:destroy() end + elseif o.unitsID then -- Some objects (such as static object) do not belong to a group, must be removed one by one + for _,id in ipairs(o.unitsID) do + local u = DCSEx.world.getUnitByID(id) + if u then + u:destroy() + else + local s = DCSEx.world.getStaticObjectByID(id) + if s then s:destroy() end + end + end + end + + trigger.action.removeMark(o.markerID) + trigger.action.removeMark(o.markerTextID) + end + + objectives = {} + end + + function TUM.objectives.areAllCompleted() + if #objectives == 0 then return false end + + return TUM.objectives.getCompletedCount() == TUM.objectives.getCount() + end + + function TUM.objectives.getSceneryObjectObjective(sceneryObject) + if #objectives == 0 then return nil end + if not sceneryObject then return nil end + if Object.getCategory(sceneryObject) ~= Object.Category.SCENERY then return nil end + + for i=1,#objectives do + if DCSEx.math.isSamePoint(sceneryObject:getPoint(), objectives[i].point3) then + return i + end + end + + return nil + end + + local function markObjectiveAsComplete(index) + if index < 1 or index > #objectives then return end -- Out of bounds + if objectives[index].completed then return end -- Objective already completed + + objectives[index].completed = true + TUM.playerScore.awardCompletedObjective() + + if TUM.objectives.areAllCompleted() then + TUM.mission.checkMissionStatus() + else + TUM.radio.playForCoalition(TUM.settings.getPlayerCoalition(), "commandObjectiveComplete", { objectives[index].name }, "Command", true) + end + + DCSEx.dcs.outPicture("Pic-ObjectiveComplete.png", 5, true, 0, 1, 1, 25, 1) + trigger.action.outSound("UI-MissionEnd.ogg") + end + + local function onObjectiveEvent(index, event) + if index < 1 or index > #objectives then return end -- Out of bounds + if objectives[index].completed then return end -- Objective already completed + + if event.id ~= world.event.S_EVENT_DEAD then return end + if not event.initiator then return end + + if objectives[index].isSceneryTarget then + if Object.getCategory(event.initiator) == Object.Category.SCENERY then + if DCSEx.math.isSamePoint(event.initiator:getPoint(), objectives[index].point3) then + -- markObjectiveAsComplete(index) + timer.scheduleFunction(markObjectiveAsComplete, index, timer.getTime() + 3) + end + end + else + if Object.getCategory(event.initiator) == Object.Category.UNIT or Object.getCategory(event.initiator) == Object.Category.STATIC then + local unitID = DCSEx.dcs.getObjectIDAsNumber(event.initiator) + + if DCSEx.table.contains(objectives[index].completedUnitsID, unitID) then return end + if not DCSEx.table.contains(objectives[index].unitsID, unitID) then return end + + table.insert(objectives[index].completedUnitsID, unitID) + if #objectives[index].completedUnitsID == #objectives[index].unitsID then + timer.scheduleFunction(markObjectiveAsComplete, index, timer.getTime() + 3) + end + end + end + + updateObjectiveText(index) + end + + function TUM.objectives.onEvent(event) + for i,_ in ipairs(objectives) do + onObjectiveEvent(i, event) + end + end +end diff --git a/Script/The Universal Mission/ObjectivesMaker.lua b/Script/The Universal Mission/ObjectivesMaker.lua new file mode 100644 index 0000000..64b66e7 --- /dev/null +++ b/Script/The Universal Mission/ObjectivesMaker.lua @@ -0,0 +1,179 @@ +-- ==================================================================================== +-- TUM.OBJECTIVESMAKER - CREATE MISSION OBJECTIVES +-- ==================================================================================== +-- ==================================================================================== + +TUM.objectivesMaker = {} + +do + local function pickRandomTask() + local taskFamily = TUM.settings.getValue(TUM.settings.id.TASKING) + + local validTaskIDs = {} + for k,t in pairs(Library.tasks) do + if t.taskFamily == taskFamily then + table.insert(validTaskIDs, k) + end + end + + if #validTaskIDs == 0 then return nil end + + return DCSEx.table.getRandom(validTaskIDs) + end + + local function pickWaterPoint(nearThisPoint) + local waterZones = TUM.territories.getWaterZones() + if not waterZones or #waterZones == 0 then return nil end -- No "water" zones on this map + + local possiblePoints = {} + + for _=1,24 do + local point = DCSEx.zones.getRandomPointInside(DCSEx.table.getRandom(waterZones), land.SurfaceType.WATER) + if point then + table.insert(possiblePoints, point) + end + end + + if #possiblePoints == 0 then return nil end + + possiblePoints = DCSEx.dcs.getNearestPoints(nearThisPoint, possiblePoints, 1) + + return possiblePoints[1] + end + + function TUM.objectivesMaker.create() + local zone = DCSEx.zones.getByName(TUM.settings.getValue(TUM.settings.id.TARGET_LOCATION, true)) + + local taskID = pickRandomTask() + if not taskID then + TUM.log("Failed to find a valid task.", TUM.logLevel.WARNING) + return nil + end + local objectiveDB = Library.tasks[taskID] + + local spawnPoint = nil + local isSceneryTarget = false + + if DCSEx.table.contains(objectiveDB.flags, DCSEx.enums.taskFlag.SCENERY_TARGET) then + local validSceneries = DCSEx.world.getSceneriesInZone(zone, DCSEx.zones.getRadius(zone), 100) + if not validSceneries or #validSceneries == 0 then + TUM.log("Failed to find a valid scenery object to use as target.", TUM.logLevel.WARNING) + return nil + end + + local pickedScenery = DCSEx.table.getRandom(validSceneries) + spawnPoint = pickedScenery:getPoint() + isSceneryTarget = true + elseif objectiveDB.surfaceType == land.SurfaceType.WATER then + spawnPoint = pickWaterPoint(zone) + if not spawnPoint then + spawnPoint = DCSEx.world.getSpawnPoint(zone, objectiveDB.surfaceType, objectiveDB.safeRadius) + end + else + spawnPoint = DCSEx.world.getSpawnPoint(zone, objectiveDB.surfaceType, objectiveDB.safeRadius) + end + + if not spawnPoint then + TUM.log("Failed to find a spawn point for objective.", TUM.logLevel.WARNING) + return nil + end + + if DCSEx.table.contains(objectiveDB.flags, DCSEx.enums.taskFlag.ON_ROADS) then + spawnPoint = DCSEx.world.getClosestPointOnRoadsVec2(spawnPoint) + end + + local objective = { + completed = false, + completedUnitsID = {}, + isSceneryTarget = isSceneryTarget, + markerID = DCSEx.world.getNextMarkerID(), + markerTextID = DCSEx.world.getNextMarkerID(), + name = Library.objectiveNames.get():upper(), + point2 = DCSEx.table.deepCopy(spawnPoint), + point3 = DCSEx.math.vec2ToVec3(spawnPoint, "land"), + preciseCoordinates = objectiveDB.waypointInaccuracy <= 0, + taskID = taskID, + unitsID = {} + } + + if objectiveDB.waypointInaccuracy <= 0 then -- Exact coordinates are available + objective.waypoint2 = DCSEx.table.deepCopy(objective.point2) + objective.waypoint3 = DCSEx.table.deepCopy(objective.point3) + else -- No exact coordinates available, create the waypoint near the target + objective.waypoint2 = DCSEx.math.randomPointInCircle(objective.point2, objectiveDB.waypointInaccuracy) + objective.waypoint3 = DCSEx.math.vec2ToVec3(objective.waypoint2, "land") + end + + if not DCSEx.table.contains(objectiveDB.flags, DCSEx.enums.taskFlag.SCENERY_TARGET) then + -- Check group options + local groupOptions = {} + if DCSEx.table.contains(objectiveDB.flags, DCSEx.enums.taskFlag.MOVING) then + local destPoint = DCSEx.math.randomPointInCircle(objective.point2, 5000, 2500, land.SurfaceType.LAND) + if destPoint then + groupOptions.isMoving = true + if DCSEx.table.contains(objectiveDB.flags, DCSEx.enums.taskFlag.ON_ROADS) then + groupOptions.onRoad = true + destPoint = DCSEx.world.getClosestPointOnRoadsVec2(destPoint) + end + groupOptions.moveTo = destPoint + end + end + + local units = Library.factions.getUnits(TUM.settings.getEnemyFaction(), objectiveDB.targetFamilies, math.random(objectiveDB.targetCount[1], objectiveDB.targetCount[2])) + + local groupInfo = nil + if objectiveDB.targetFamilies[1] == DCSEx.enums.unitFamily.STATIC_STRUCTURE then + if units and #units >= 1 then + groupInfo = {} + groupInfo.unitsID = { DCSEx.unitGroupMaker.createStatic(TUM.settings.getEnemyCoalition(), objective.point2, units[1], "") } + end + else + groupInfo = DCSEx.unitGroupMaker.create(TUM.settings.getEnemyCoalition(), DCSEx.dcs.getUnitTypeFromFamily(objectiveDB.targetFamilies[1]), objective.point2, units, groupOptions) + end + + if not groupInfo then + TUM.log("Failed to spawn a group for objective.", TUM.logLevel.WARNING) + return nil + end + objective.groupID = groupInfo.groupID + + if DCSEx.table.contains(objectiveDB.flags, DCSEx.enums.taskFlag.DESTROY_TRACK_RADARS_ONLY) then + objective.unitsID = {} + for i=1,#groupInfo.unitTypeNames do + if Unit.getDescByName(groupInfo.unitTypeNames[i]).attributes["SAM TR"] then + table.insert(objective.unitsID, groupInfo.unitsID[i]) + end + end + if #objective.unitsID == 0 then + objective.unitsID = DCSEx.table.deepCopy(groupInfo.unitsID) + end + else + objective.unitsID = DCSEx.table.deepCopy(groupInfo.unitsID) + end + end + + --------------------------------------------------------------------- + -- Create dot marker (accurate WPs) or circle marker (inaccurate WPs) + --------------------------------------------------------------------- + if objectiveDB.waypointInaccuracy <= 0 then + trigger.action.markToAll(objective.markerID, "Objective "..objective.name.."\n\n"..DCSEx.world.getCoordinatesAsString(objective.point3, false), objective.point3, true) + else + local circleRadius = math.max(objectiveDB.waypointInaccuracy, 1000) + trigger.action.circleToAll( + -1, objective.markerID, + objective.waypoint3, circleRadius, + { 1, 1, 1, 1 }, { 1, 0, 0, 0.25 } , 2, true) + end + + --------------------- + -- Create text marker + --------------------- + local textPoint3 = DCSEx.table.deepCopy(objective.waypoint3) + textPoint3.x = textPoint3.x + 224 + textPoint3.z = textPoint3.z + 224 + -- Text marker is created with an empty string, its content will be updated by TUM.MissionObjectives when it's added + trigger.action.textToAll(-1, objective.markerTextID, textPoint3, { 1, 1, 1, 1 }, { 0, 0, 0, .5 }, 12, true, "") + + return objective + end +end diff --git a/Script/The Universal Mission/PlayerCareer.lua b/Script/The Universal Mission/PlayerCareer.lua new file mode 100644 index 0000000..f1519d1 --- /dev/null +++ b/Script/The Universal Mission/PlayerCareer.lua @@ -0,0 +1,308 @@ +-- ==================================================================================== +-- TUM.PLAYERCAREER - HANDLES THE PERSISTENT PILOT CAREER IN SINGLE-PLAYER MISSIONS +-- ==================================================================================== +-- (local const) MAX_RIBBONS +-- (local const) MEDAL_BOX_DISPLAY_TIME +-- (local const) OBJECTIVES_PER_RIBBON +-- (local const) MEDALS +-- (local const) RANKS +-- (local table) careerStats +-- (local) fixIncompleteStats() +-- (local) getHighestMedal() +-- (local) getRibbonCount() +-- TUM.playerCareer.awardScore(score, objectives) +-- TUM.playerCareer.createMenu() +-- TUM.playerCareer.displayMedalBox(printSummary) +-- TUM.playerCareer.getCareerSummary() +-- TUM.playerCareer.load() +-- TUM.playerCareer.onStartUp() +-- TUM.playerCareer.reset() +-- TUM.playerCareer.save() +-- ==================================================================================== + +TUM.playerCareer = {} + +do + local MAX_RIBBONS = 40 -- Maximum number of ribbons + local MEDAL_BOX_DISPLAY_TIME = 15 -- in seconds + local OBJECTIVES_PER_RIBBON = 4 -- How many completed objectives to gain a new ribbon? + + local MEDALS = { + { "Air medal", 200 }, + { "Bronze star", 300 }, + { "Airman's medal", 400 }, + { "Distinguished Flying Cross", 500 }, + { "Silver Star for Valor", 600 }, + { "Air Force Cross", 700 }, + { "Congressional Medal of Honor", 800 }, + } + + local RANKS = { + { "2d Lt.", "Second lieutenant", 0 }, + { "1st Lt.", "First lieutenant", 500 }, + { "Capt.", "Captain", 2000 }, + { "Maj.", "Major", 8000 }, + { "Lt Col.", "Lieutenant colonel", 16000 }, + { "Col.", "Colonel", 32000 }, + } + + local careerStats = {} + + ------------------------------------- + -- Adds missing fields, if any, to the careerStats table + ------------------------------------- + local function fixIncompleteStats() + if not careerStats then careerStats = { } end + if not careerStats.bestSortie then careerStats.bestSortie = 0 end + if not careerStats.completedObjectives then careerStats.completedObjectives = 0 end + if not careerStats.completedSorties then careerStats.completedSorties = 0 end + if not careerStats.medals then careerStats.medals = 0 end + if not careerStats.medalWounded then careerStats.medalWounded = false end + if not careerStats.rank then careerStats.rank = 1 end + if not careerStats.score then careerStats.score = 0 end + careerStats.version = TUM.VERSION_NUMBER + end + + ------------------------------------- + -- Returns the highest medal a player has obtained for scoring a high number of points during a single sortie + -- @return A number (index in the MEDALS table) + ------------------------------------- + local function getHighestMedal() + local medal = 0 + for i=1,#MEDALS do + if careerStats.bestSortie >= MEDALS[i][2] then + medal = i + end + end + + return medal + end + + ------------------------------------- + -- Returns the current number a ribbons a player was awarded for completing objectives + -- @return A number + ------------------------------------- + local function getRibbonCount() + return DCSEx.math.clamp(math.floor(careerStats.completedObjectives / OBJECTIVES_PER_RIBBON), 0, MAX_RIBBONS) + end + + ------------------------------------- + -- Awards career points. Only works in single-player missions + -- @param score Number of career points to award + -- @param objectives Number of completed objectives to award + -- @return True if a ribbon, medal or promotion was awarded, false otherwise + ------------------------------------- + function TUM.playerCareer.awardScore(score, objectives) + if not DCSEx.io.canReadAndWrite() then return false end -- IO disabled, career and scoring disabled + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return false end -- No career in multiplayer + score = math.max(0, math.floor(score or 0)) + + fixIncompleteStats() + + local oldRibbonCount = getRibbonCount() + careerStats.bestSortie = math.max(careerStats.bestSortie, score) + careerStats.score = careerStats.score + score + careerStats.completedObjectives = careerStats.completedObjectives + objectives + TUM.log("Awarded "..tostring(score).." xp and "..tostring(objectives).." completed objectives to player.") + local newRibbonCount = getRibbonCount() + + local somethingWasAwarded = false + + -- Check for promotions + if careerStats.rank < #RANKS and careerStats.score >= RANKS[careerStats.rank + 1][3] then + careerStats.rank = careerStats.rank + 1 + somethingWasAwarded = true + trigger.action.outText("✪ You have been promoted to the rank of "..RANKS[careerStats.rank][2]..".", MEDAL_BOX_DISPLAY_TIME) + end + + -- Check for medals + for i=1,#MEDALS do + if i > careerStats.medals and score >= MEDALS[i][2] then + trigger.action.outText("✪ You have been awarded the "..MEDALS[i][1]..".", MEDAL_BOX_DISPLAY_TIME) + careerStats.medals = i + somethingWasAwarded = true + break + end + end + + -- Check for ribbons + if newRibbonCount > oldRibbonCount then + trigger.action.outText("✪ You have been awarded a battle ribbon.", MEDAL_BOX_DISPLAY_TIME) + somethingWasAwarded = true + end + + TUM.playerCareer.save() + + if somethingWasAwarded then + TUM.playerCareer.displayMedalBox(false) + end + + return somethingWasAwarded + end + + ------------------------------------- + -- Appends the career menu to the F10 menu. Only works in single-player missions + ------------------------------------- + function TUM.playerCareer.createMenu() + if not DCSEx.io.canReadAndWrite() then return end -- IO disabled, career and scoring disabled + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return end -- No career in multiplayer + + missionCommands.addCommand("✪ View pilot career stats", nil, TUM.playerCareer.displayMedalBox, true) + end + + ------------------------------------- + -- Displays the player's medal box and carrer summary. Only works in single-player missions + ------------------------------------- + function TUM.playerCareer.displayMedalBox(printSummary) + if not DCSEx.io.canReadAndWrite() then return end -- IO disabled, career and scoring disabled + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return end -- No career in multiplayer + fixIncompleteStats() + + if printSummary then + trigger.action.outText(TUM.playerCareer.getCareerSummary(), MEDAL_BOX_DISPLAY_TIME, true) + end + + DCSEx.dcs.outPicture("Pic-MedalBox.png", MEDAL_BOX_DISPLAY_TIME, true, 0, 2, 2, 50, 1) + DCSEx.dcs.outPicture("Pic-Rank"..tostring(careerStats.rank)..".png", MEDAL_BOX_DISPLAY_TIME, false, 0, 2, 2, 50, 1) + + local ribbonCount = getRibbonCount() + for i=1,ribbonCount do + DCSEx.dcs.outPicture("Pic-Ribbon"..tostring(i)..".png", MEDAL_BOX_DISPLAY_TIME, false, 0, 2, 2, 50, 1) + end + + for i=1,careerStats.medals do + DCSEx.dcs.outPicture("Pic-Medal"..tostring(i)..".png", MEDAL_BOX_DISPLAY_TIME, false, 0, 2, 2, 50, 1) + end + trigger.action.outSound("UI-Career.ogg") + end + + ------------------------------------- + -- Returns the player career summary as a string. Only works in single-player missions + -- @return A string + ------------------------------------- + function TUM.playerCareer.getCareerSummary() + if not DCSEx.io.canReadAndWrite() then return "" end -- IO disabled, career and scoring disabled + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return "" end -- No career in multiplayer + fixIncompleteStats() + + local playerName = "Player" + local players = DCSEx.world.getAllPlayers() + if #players > 0 then + playerName = players[1]:getPlayerName() + end + + local summary = "" + summary = summary.."CAREER STATS FOR "..RANKS[careerStats.rank][1]:upper().." "..playerName:upper()..":\n" + summary = summary.."=======================\n" + summary = summary.."- Rank: "..RANKS[careerStats.rank][2].."\n" + summary = summary.."- Best sortie XP: "..DCSEx.string.toStringThousandsSeparator(careerStats.bestSortie).."\n" + summary = summary.."- Total career XP: "..DCSEx.string.toStringThousandsSeparator(careerStats.score).."\n" + summary = summary.."- Completed objectives: "..tostring(careerStats.completedObjectives).."\n" + + if careerStats.medals == 0 then + summary = summary.."- Medals: None" + else + summary = summary.."- Medals:" + for i=1,careerStats.medals do + summary = summary.."\n - "..MEDALS[i][1] + end + end + + local ribbonCount = getRibbonCount() + if ribbonCount < MAX_RIBBONS or careerStats.rank < #RANKS or careerStats.medals < #MEDALS then + summary = summary.."\n" + + if careerStats.rank < #RANKS then + summary = summary.."\n- Next promotion: "..DCSEx.string.toStringThousandsSeparator(RANKS[careerStats.rank + 1][3]).." xp" + end + + if ribbonCount < MAX_RIBBONS then + summary = summary.."\n- Next ribbon: "..tostring((ribbonCount + 1) * OBJECTIVES_PER_RIBBON).." objectives" + end + + if careerStats.medals < #MEDALS then + summary = summary.."\n- Next medal: "..DCSEx.string.toStringThousandsSeparator(MEDALS[careerStats.medals + 1][2]).." xp in a single flight" + end + end + + return summary + end + + ------------------------------------- + -- Loads the player career from the disk. Only works in single-player missions + -- @return True if everything worked (or disabled because of MP), false if an error happened + ------------------------------------- + function TUM.playerCareer.load() + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return true end -- No career in multiplayer + if not DCSEx.io.canReadAndWrite() then return false end + + local jsonString = DCSEx.io.load("TheUniversalMission.sav") + if jsonString then + -- TODO: what if Json is malformed? + careerStats = net.json2lua(jsonString) + if not careerStats then + careerStats = {} + fixIncompleteStats() + TUM.log("Failed to load player career data, career data reset.") + else + TUM.log("Player career data loaded successfully.") + end + else + fixIncompleteStats() + return false + end + + fixIncompleteStats() + end + + ------------------------------------- + -- Called on mission start up + -- @return True if started up properly, false if an error happened + ------------------------------------- + function TUM.playerCareer.onStartUp() + fixIncompleteStats() + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return true end -- No career in multiplayer + + if DCSEx.io.canReadAndWrite() then + TUM.log("Lua IO module available, can read and write.") + TUM.playerCareer.load() + else + local msg = "IO module is disabled, CANNOT read and write persistant data. Player progress will NOT be saved.\n" + msg = msg.."To enable the IO module, comment or remove the \"sanitizeModule('io')\" line in \n" + msg = msg.."[DCSWorld installation directory]\\Scripts\\MissionScripting.lua and restart the game." + + TUM.log(msg, TUM.logLevel.WARNING) + end + + return true + end + + ------------------------------------- + -- Resets the player career stats and save them. Only works in single-player missions + ------------------------------------- + function TUM.playerCareer.reset() + if not DCSEx.io.canReadAndWrite() then return end -- IO disabled, career and scoring disabled + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return end -- No career in multiplayer + + careerStats = nil + fixIncompleteStats() + TUM.playerCareer.save() + end + + ------------------------------------- + -- Save the player career to the disk. Only works in single-player missions + -- @return True if everything worked (or disabled), false if an error happened + ------------------------------------- + function TUM.playerCareer.save() + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return true end -- No career in multiplayer + if not DCSEx.io.canReadAndWrite() then return true end -- IO disabled, career and scoring disabled + + fixIncompleteStats() + + if DCSEx.io.save("TheUniversalMission.sav", net.lua2json(careerStats)) then + return true + else + return false + end + end +end diff --git a/Script/The Universal Mission/PlayerScore.lua b/Script/The Universal Mission/PlayerScore.lua new file mode 100644 index 0000000..8e52671 --- /dev/null +++ b/Script/The Universal Mission/PlayerScore.lua @@ -0,0 +1,391 @@ +-- ==================================================================================== +-- TUM.PLAYERSCORE - HANDLES ALL SCORING DURING SINGLE-PLAYER MISSIONS +-- ==================================================================================== +-- (const) SKILL_MULTIPLIER_BONUS +-- (const) SCORE_REMINDER_INTERVAL +-- (local) getKillValue(killedObject) +-- (local) onKillEvent(event) +-- (local) onLandEvent(event) +-- (local) onResetEvent(event) +-- (local) printReminder() +-- TUM.playerScore.award(amount, message, silent) +-- TUM.playerScore.getCompletedObjectives() +-- TUM.playerScore.getScore() +-- TUM.playerScore.getScoreMultiplier(settingID, settingValue) +-- TUM.playerScore.getTotalScoreMultiplier() +-- TUM.playerScore.onClockTick(clockTick) +-- TUM.playerScore.onEvent(event) +-- TUM.playerScore.reset(showMessage, reason) +-- TUM.playerScore.showScore() +-- ==================================================================================== + +TUM.playerScore = {} + +do + local SKILL_MULTIPLIER_BONUS = { + 0, + 0.1, -- 0.05 + 0.2, -- 0.125 + 0.4, -- 0.25 + 0.6 -- 0.5 + } + + local SCORE_REMINDER_INTERVAL = 5 -- in minutes + + local completedObjectives = 0 + local score = 0 + local scoreReminderIntervalLeft = SCORE_REMINDER_INTERVAL + + local function getKillValue(killedObject) + if not killedObject then return 0 end + + if Object.getCategory(killedObject) == Object.Category.BASE then return 60 end + if Object.getCategory(killedObject) == Object.Category.STATIC then return 60 end + if Object.getCategory(killedObject) == Object.Category.SCENERY then + for i=1,TUM.objectives.getCount() do + local obj = TUM.objectives.getObjective(i) + if obj then + if obj.isSceneryTarget then + if DCSEx.math.isSamePoint(killedObject:getPoint(), obj.point3) then + return 60 + end + end + end + end + + return 0 + end + if Object.getCategory(killedObject) ~= Object.Category.UNIT then return 0 end + + local objectDesc = killedObject:getDesc() + if not objectDesc or not objectDesc.attributes then return 10 end -- No description, assume a default value of 10 points + + local groundMultiplier = 1 + if not killedObject:inAir() then groundMultiplier = 0.5 end -- Aircraft killed on the ground are worth less points + + -- Misc + if objectDesc.attributes["Missiles"] then return 10 end + if objectDesc.attributes["UAVs"] then return math.floor(15 * groundMultiplier) end + + -- Fixed wing + if objectDesc.attributes["Fighters"] then return math.floor(40 * groundMultiplier) end + if objectDesc.attributes["Interceptors"] then return math.floor(40 * groundMultiplier) end + if objectDesc.attributes["Interceptors"] then return math.floor(40 * groundMultiplier) end + if objectDesc.attributes["Planes"] then return math.floor(25 * groundMultiplier) end + + -- Rotary wing + if objectDesc.attributes["Attack helicopters"] then return math.floor(30 * groundMultiplier) end + if objectDesc.attributes["Helicopters"] then return math.floor(25 * groundMultiplier) end + + -- Default air + if objectDesc.attributes["Air"] then return math.floor(20 * groundMultiplier) end + + -- Ships + if objectDesc.attributes["Aircraft Carriers"] then return 300 end + if objectDesc.attributes["Cruisers"] then return 250 end + if objectDesc.attributes["Destroyers"] then return 150 end + if objectDesc.attributes["Frigates"] then return 150 end + if objectDesc.attributes["Corvettes"] then return 100 end + if objectDesc.attributes["Heavy armed ships"] then return 75 end + if objectDesc.attributes["Ships"] then return 25 end + + -- Air defense + if objectDesc.attributes["MANPADS AUX"] then return 5 end + if objectDesc.attributes["MANPADS"] then return 10 end + if objectDesc.attributes["SR SAM"] then return 20 end + if objectDesc.attributes["IR Guided SAM"] then return 15 end + if objectDesc.attributes["SAM TR"] then return 25 end + if objectDesc.attributes["SAM SR"] then return 15 end + if objectDesc.attributes["Armed Air Defence"] then return 10 end + if objectDesc.attributes["SAM elements"] then return 5 end + if objectDesc.attributes["SAM related"] then return 5 end + + -- Ground vehicles + if objectDesc.attributes["Modern Tanks"] then return 20 end + if objectDesc.attributes["Tanks"] then return 15 end + if objectDesc.attributes["Modern Tanks"] then return 25 end + if objectDesc.attributes["HeavyArmoredUnits"] then return 20 end + if objectDesc.attributes["LightArmoredUnits"] then return 15 end + if objectDesc.attributes["NonArmoredUnits"] then return 10 end + if objectDesc.attributes["Unarmed vehicles"] then return 10 end + + -- Infantry + if objectDesc.attributes["Infantry"] then return 3 end + + return 10 -- Don't know what this thing is, assume a default value of 10 points + end + + ------------------------------------- + -- Called by TUM.playerScore.onEvent when a KILL event is triggered + -- @param event The DCS World event + ------------------------------------- + local function onKillEvent(event) + if not event.target then return end + if Object.getCategory(event.initiator) ~= Object.Category.UNIT then return end + if not event.initiator:getPlayerName() then return end + + local killValue = getKillValue(event.target) + if killValue <= 0 then return end + + -- Higher reward for higher threat levels + local scoreMultiplier = TUM.playerScore.getTotalScoreMultiplier() + killValue = math.max(1, math.floor(killValue * scoreMultiplier)) + + -- Penalty for destroying friendly or civilian units + local prefix = "" + if Object.getCategory(event.target) == Object.Category.UNIT then + if event.initiator:getCoalition() == event.target:getCoalition() then + prefix = "friendly " + killValue = killValue * -5.0 + elseif event.target:getCoalition() == coalition.side.NEUTRAL then + prefix = "neutral " + killValue = killValue * -2.0 + end + end + + TUM.playerScore.award(killValue, "destroyed "..prefix..Library.objectNames.get(event.target)) + end + + ------------------------------------- + -- Called by TUM.playerScore.onEvent when a LAND event is triggered + -- @param event The DCS World event + ------------------------------------- + local function onLandEvent(event) + if Object.getCategory(event.initiator) ~= Object.Category.UNIT then return end + if not event.initiator:getPlayerName() then return end + + local muteRadioMessage = false + if score > 0 or completedObjectives > 0 then + muteRadioMessage = TUM.playerCareer.awardScore(score, completedObjectives) + end + + -- Single-player landing radio message is handled here instead of in AmbientRadio to avoid + -- "conflicts" with the "awardScore" message if both the medal case and the radio message are + -- triggered at the same time (delaying the radio message wouldn't be a solution as this would + -- interrupt the medal case music very quickly) + if not muteRadioMessage then + local baseName = "AIRBASE" + if event.place then + baseName = event.place:getName():upper() + end + + TUM.radio.playForAll("atcSafeLandingPlayer", {event.initiator:getCallsign(), baseName}, baseName.." ATC") + end + + TUM.playerScore.reset(false) + end + + ------------------------------------- + -- Called by TUM.playerScore.onEvent when any event causing a score reset (crash, ejection, slot change...) is triggered + -- @param event The DCS World event + ------------------------------------- + local function onResetEvent(event) + if not event.initiator:getPlayerName() then return end + + local reason = nil + if event.id == world.event.S_EVENT_CRASH then + reason = "you crashed" + elseif event.id == world.event.S_EVENT_PILOT_DEAD then + reason = "you were killed" + elseif event.id == world.event.S_EVENT_EJECTION then + reason = "you ejected" + elseif event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT then + reason = "you've taken control of a new aircraft" + end + + TUM.playerScore.reset(true, reason) + end + + ------------------------------------- + -- Print a reminder that the player has to land for their current score and completed objectives to be added to their flight profile + -- @return True if a reminded was printed, false if it was not needed (no score to award, etc) + ------------------------------------- + local function printReminder() + if score <= 0 and completedObjectives <= 0 then return false end -- Nothing to remind the player of + + local msg = "" + if score > 0 and completedObjectives > 0 then + msg = string.format("You've been awarded %s point(s) and have completed %d objective(s).", DCSEx.string.toStringThousandsSeparator(score), completedObjectives) + elseif score > 0 then + msg = string.format("You've been awarded %s point(s).", DCSEx.string.toStringThousandsSeparator(score)) + else + msg = string.format("You have completed %d objective(s).", completedObjectives) + end + + trigger.action.outText("REMINDER: "..msg.." They will be awarded to your flight career once you've landed.", 5) + trigger.action.outSound("UI-Ok.ogg") + return true + end + + ------------------------------------- + -- Awards points to the player. Only works in single-player missions + -- @param amount Number of points to award + -- @param message Message to display (why are these points awarded?). If missing, no message will be displayed. + ------------------------------------- + function TUM.playerScore.award(amount, message) + if not DCSEx.io.canReadAndWrite() then return end -- IO disabled, career and scoring disabled + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return end -- No scoring in multiplayer + + score = score + amount + + if message then + TUM.log("Awarded "..DCSEx.string.toStringThousandsSeparator(amount).." points ("..message..").") + end + end + + ------------------------------------- + -- Awards a new completed objective to the player. Only works in single-player missions + ------------------------------------- + function TUM.playerScore.awardCompletedObjective() + if not DCSEx.io.canReadAndWrite() then return end -- IO disabled, career and scoring disabled + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return end -- No scoring in multiplayer + + completedObjectives = completedObjectives + 1 + end + + ------------------------------------- + -- Returns the current number of completed objectives. Only works in single-player missions + -- @return A number + ------------------------------------- + function TUM.playerScore.getCompletedObjectives() + if not DCSEx.io.canReadAndWrite() then return 0 end -- IO disabled, career and scoring disabled + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return 0 end -- No scoring in multiplayer + + return completedObjectives + end + + ------------------------------------- + -- Returns the current player score. Only works in single-player missions + -- @return A number + ------------------------------------- + function TUM.playerScore.getScore() + if not DCSEx.io.canReadAndWrite() then return 0 end -- IO disabled, career and scoring disabled + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return 0 end -- No scoring in multiplayer + + return score + end + + ------------------------------------- + -- Returns the value to add to the global score multiplier according to a given setting + -- @param settingID ID of the setting (from the TUM.settings.id enum) + -- @param settingValue The setting value + -- @return A number (0 if no multiplier) + ------------------------------------- + function TUM.playerScore.getScoreMultiplier(settingID, settingValue) + if not DCSEx.io.canReadAndWrite() then return 0 end -- IO disabled, career and scoring disabled + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return 0 end -- No scoring in multiplayer + + if settingID == TUM.settings.id.ENEMY_AIR_DEFENSE or settingID == TUM.settings.id.ENEMY_AIR_FORCE then + return SKILL_MULTIPLIER_BONUS[settingValue] + end + + return 0 + end + + ------------------------------------- + -- Returns the global score multiplier according the current settings + -- @return A number (1.0 if no multiplier) + ------------------------------------- + function TUM.playerScore.getTotalScoreMultiplier() + if not DCSEx.io.canReadAndWrite() then return 1.0 end -- IO disabled, career and scoring disabled + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return 1.0 end -- No scoring in multiplayer + + local scoreMultiplier = 1.0 + + if TUM.settings.getValue(TUM.settings.id.TASKING) ~= DCSEx.enums.taskFamily.ANTISHIP then -- No ground air defense during antiship strikes + scoreMultiplier = scoreMultiplier +TUM.playerScore.getScoreMultiplier(TUM.settings.id.ENEMY_AIR_DEFENSE, TUM.settings.getValue(TUM.settings.id.ENEMY_AIR_DEFENSE)) + end + scoreMultiplier = scoreMultiplier +TUM.playerScore.getScoreMultiplier(TUM.settings.id.ENEMY_AIR_FORCE, TUM.settings.getValue(TUM.settings.id.ENEMY_AIR_FORCE)) + + return scoreMultiplier + end + + ---------------------------------------------------------- + -- Called on every mission update tick (every 10-20 seconds) + -- @return True if something was done this tick, false otherwise + ---------------------------------------------------------- + function TUM.playerScore.onClockTick() + if not DCSEx.io.canReadAndWrite() then return false end -- IO disabled, career and scoring disabled + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return false end -- No scoring in multiplayer + if score == 0 and completedObjectives == 0 then return false end -- Nothing to remind the player of + + scoreReminderIntervalLeft = scoreReminderIntervalLeft - 1 + if scoreReminderIntervalLeft == 0 then + scoreReminderIntervalLeft = SCORE_REMINDER_INTERVAL + return printReminder() + end + + return false + end + + ------------------------------------- + -- Called when an event is raised + -- @param event The DCS World event + ------------------------------------- + function TUM.playerScore.onEvent(event) + if not event.initiator then return end + if not DCSEx.io.canReadAndWrite() then return end -- IO disabled, career and scoring disabled + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return end -- No scoring in multiplayer + + if event.id == world.event.S_EVENT_KILL then + onKillEvent(event) + return + end + + if event.id == world.event.S_EVENT_LAND then + onLandEvent(event) + return + end + + if event.id == world.event.S_EVENT_CRASH or event.id == world.event.S_EVENT_PILOT_DEAD or event.id == world.event.S_EVENT_PLAYER_ENTER_UNIT or event.id == world.event.S_EVENT_EJECTION then + onResetEvent(event) + return + end + end + + ------------------------------------- + -- Resets the player score to 0 + -- @param showMessage Should a message be displayed (if any points are lost)? + -- @param reason The reason for the point loss, displayed in the message + ------------------------------------- + function TUM.playerScore.reset(showMessage, reason) + if completedObjectives == 0 and score == 0 then return end + + if not DCSEx.io.canReadAndWrite() then return end -- IO disabled, career and scoring disabled + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return end -- No scoring in multiplayer + + if showMessage then + local msg = "" + + if reason then + msg = "Unstowed progress lost because "..reason.."." + else + msg = "Unstowed progress lost." + end + + msg = msg.." You lost "..DCSEx.string.toStringThousandsSeparator(score).." xp and "..tostring(completedObjectives).." completed objective(s)." + trigger.action.outText(msg, 5) + trigger.action.outSound("UI-Error.ogg") + else + TUM.log("Mission score reset.") + end + + completedObjectives = 0 + score = 0 + end + + ------------------------------------- + -- Shows the current mission score + ------------------------------------- + function TUM.playerScore.showScore() + if not DCSEx.io.canReadAndWrite() then return end -- IO disabled, career and scoring disabled + if TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then return end -- No scoring in multiplayer + + local scoreMsg = "CURRENT PROGRESS (will be awarded to your career profile on landing):\n" + scoreMsg = scoreMsg.."XP: "..DCSEx.string.toStringThousandsSeparator(score).."\n" + scoreMsg = scoreMsg.."Completed objectives: "..tostring(completedObjectives) + + trigger.action.outText(scoreMsg, 5) + trigger.action.outSound("UI-Ok.ogg") + end +end diff --git a/Script/The Universal Mission/Radio.lua b/Script/The Universal Mission/Radio.lua new file mode 100644 index 0000000..3e7c2da --- /dev/null +++ b/Script/The Universal Mission/Radio.lua @@ -0,0 +1,160 @@ +-- ==================================================================================== +-- TUM.RADIO - HANDLES FUNCTIONS TO PLAY RADIO MESSAGES +-- ==================================================================================== +-- (const, local) ANSWER_DELAY +-- (local) function doRadioMessage(args, time) +-- TUM.radio.playForAll(messageID, replacements, callsign, delayed, functionToRun, functionParameters) +-- TUM.radio.playForCoalition(coalitionID, messageID, replacements, callsign, delayed, functionToRun, functionParameters) +-- TUM.radio.playForGroup(groupID, messageID, replacements, callsign, delayed, functionToRun, functionParameters) +-- TUM.radio.playForUnit(unitID, messageID, replacements, callsign, delayed, functionToRun, functionParameters) +-- ==================================================================================== + +TUM.radio = {} + +do + -- Min/max time to get a answer to a radio message, in seconds + local ANSWER_DELAY = { 3.0, 5.0 } + + ------------------------------------- + -- Executes a radio message + -- @param args Message parameters + -- @param time Game time at the moment the message is played + ------------------------------------- + local function doRadioMessage(args, time) + if not args or not args.messageID then return nil end + + local callsign = args.callsign + if not callsign then + local unit = DCSEx.world.getUnitByID(args.unitID) + if not unit then + callsign = "Flight" + else + callsign = unit:getCallsign() + end + end + + local message = "" + local oggFile = args.messageID + if type(Library.radioMessages[args.messageID]) == "table" then + local index = DCSEx.table.getRandomIndex(Library.radioMessages[args.messageID]) + oggFile = oggFile..tostring(index) + message = Library.radioMessages[args.messageID][index] + else + message = Library.radioMessages[args.messageID] + end + + if args.replacements then + for i,r in ipairs(args.replacements) do + message = message:gsub("$"..tostring(i), tostring(r)) + end + end + + local duration = DCSEx.string.getReadingTime(message) + + -- Print message + trigger.action.outTextForUnit(args.unitID, callsign:upper()..": "..message, duration, false) + + -- Play sound + trigger.action.outSoundForUnit(args.unitID, "Radio-"..oggFile..".ogg") + + if args.functionToRun then -- a function was provided, run it + args.functionToRun(args.functionParameters) + end + + return nil -- disable scheduling, if any + end + + ------------------------------------- + -- Plays a message to all players in a coalition + -- @param messageID ID of the radio message in scrambe.db.radioMessages + -- @param replacements String placeholders ($1, $2...) replacements in the message + -- @param callsign Name of the person speaking or nil to use unitID's callsign + -- @param delayed Should the message be delayed (used for message answers) + -- @param functionToRun Function to run when the message is played + -- @param functionParameters Parameters for the function to run when the message is played + ------------------------------------- + function TUM.radio.playForAll(messageID, replacements, callsign, delayed, functionToRun, functionParameters) + local players = DCSEx.world.getAllPlayers() + + for _, unit in pairs(players) do + TUM.radio.playForUnit(DCSEx.dcs.getObjectIDAsNumber(unit), messageID, replacements, callsign, delayed, functionToRun, functionParameters) + end + end + + ------------------------------------- + -- Plays a message to all players in a given coalition + -- @param coalitionID ID of the coalition (coalition.side.XXX) + -- @param messageID ID of the radio message in scrambe.db.radioMessages + -- @param replacements String placeholders ($1, $2...) replacements in the message + -- @param callsign Name of the person speaking or nil to use unitID's callsign + -- @param delayed Should the message be delayed (used for message answers) + -- @param functionToRun Function to run when the message is played + -- @param functionParameters Parameters for the function to run when the message is played + ------------------------------------- + function TUM.radio.playForCoalition(coalitionID, messageID, replacements, callsign, delayed, functionToRun, functionParameters) + local players = coalition.getPlayers(coalitionID) + + for _,u in pairs(players) do + TUM.radio.playForUnit(DCSEx.dcs.getObjectIDAsNumber(u), messageID, replacements, callsign, delayed, functionToRun, functionParameters) + end + end + + ------------------------------------- + -- Plays a message to players from a certain group + -- @param groupID ID of the group sending/receiving the message + -- @param messageID ID of the radio message in scrambe.db.radioMessages + -- @param replacements String placeholders ($1, $2...) replacements in the message + -- @param callsign Name of the person speaking or nil to use unitID's callsign + -- @param delayed Should the message be delayed (used for message answers) + -- @param functionToRun Function to run when the message is played + -- @param functionParameters Parameters for the function to run when the message is played + ------------------------------------- + function TUM.radio.playForGroup(groupID, messageID, replacements, callsign, delayed, functionToRun, functionParameters) + local group = DCSEx.world.getGroupByID(groupID) + if not group then return end -- group does not exist + + for _,u in pairs(group:getUnits()) do + TUM.radio.playForUnit(DCSEx.dcs.getObjectIDAsNumber(u), messageID, replacements, callsign, delayed, functionToRun, functionParameters) + end + end + + ------------------------------------- + -- Plays a message for a given unit only + -- @param unitID ID of the unit receiving the message + -- @param messageID ID of the radio message in scrambe.db.radioMessages + -- @param replacements String placeholders ($1, $2...) replacements in the message + -- @param callsign Name of the person speaking or nil to use unitID's callsign + -- @param replacements Table of two tables used for string replacements. E.g. { {"UNIT_NAME", "TIME"}, {"Enfield11", "12:30"}} + -- @param delayed Should the message be delayed (used for message answers) + -- @param functionToRun Function to run when the message is played + -- @param functionParameters Parameters for the function to run when the message is played + ------------------------------------- + function TUM.radio.playForUnit(unitID, messageID, replacements, callsign, delayed, functionToRun, functionParameters) + if not messageID then return end + if not Library.radioMessages[messageID] then return end + delayed = delayed or false + + if replacements and type(replacements) ~= "table" then + replacements = { replacements } + end + + local radioArgs = { + callsign = callsign, + functionToRun = functionToRun, + functionParameters = functionParameters, + messageID = messageID, + replacements = replacements, + unitID = unitID + } + + if delayed then -- message is delayed, schedule it + timer.scheduleFunction( + doRadioMessage, + radioArgs, + timer.getTime() + DCSEx.math.randomFloat(ANSWER_DELAY[1], ANSWER_DELAY[2]) + ) + else -- no delay, play the message at once + doRadioMessage(radioArgs, nil) + end + end +end diff --git a/Script/The Universal Mission/Settings.lua b/Script/The Universal Mission/Settings.lua new file mode 100644 index 0000000..8aae018 --- /dev/null +++ b/Script/The Universal Mission/Settings.lua @@ -0,0 +1,232 @@ +-- ==================================================================================== +-- TUM.SETTINGS - HANDLES THE MISSION SETTINGS +-- ==================================================================================== +-- (enum) TUM.settings.id +-- TUM.settings.getName(id) +-- TUM.settings.getValue(id, returnAsString) +-- TUM.settings.setAllToDefaults() +-- TUM.settings.setValue(id, value, updateMenu) +-- ==================================================================================== + +TUM.settings = {} + +TUM.settings.id = { + AI_CAP = 1, + COALITION_BLUE = 2, + COALITION_RED = 3, + ENEMY_AIR_DEFENSE = 4, + ENEMY_AIR_FORCE = 5, + MULTIPLAYER = 6, + PLAYER_COALITION = 7, + TARGET_COUNT = 8, + TARGET_LOCATION = 9, + TASKING = 10, + TIME_PERIOD = 11, +} + +do + local settings = {} + + local SETTING_NAMES = { + [TUM.settings.id.AI_CAP] = "Friendly AI CAP", + [TUM.settings.id.COALITION_BLUE] = "Blue coalition", + [TUM.settings.id.COALITION_RED] = "Red coalition", + [TUM.settings.id.ENEMY_AIR_DEFENSE] = "Enemy air defense", + [TUM.settings.id.ENEMY_AIR_FORCE] = "Enemy air force", + [TUM.settings.id.MULTIPLAYER] = "Multiplayer", + [TUM.settings.id.PLAYER_COALITION] = "Player coalition", + [TUM.settings.id.TARGET_COUNT] = "Target count", + [TUM.settings.id.TARGET_LOCATION] = "Target location", + [TUM.settings.id.TASKING] = "Mission type", + [TUM.settings.id.TIME_PERIOD] = "Time period", + } + + local SETTING_VALUES = { + [TUM.settings.id.AI_CAP] = { "Enabled", "Disabled" }, + [TUM.settings.id.COALITION_BLUE] = { }, + [TUM.settings.id.COALITION_RED] = { }, + [TUM.settings.id.ENEMY_AIR_DEFENSE] = { "None", "Green", "Regular", "Veteran", "Elite" }, + [TUM.settings.id.ENEMY_AIR_FORCE] = { "None", "Green", "Regular", "Veteran", "Elite" }, + [TUM.settings.id.PLAYER_COALITION] = { "Red", "Blue" }, -- Must match values in the coalition.side enum + [TUM.settings.id.TARGET_COUNT] = { "1", "2", "3", "4" }, + [TUM.settings.id.TARGET_LOCATION] = { }, + [TUM.settings.id.TASKING] = { "Antiship strike", "Ground attack", "Interception", "SEAD", "Strike" }, -- Must match values in the DCSEx.enums.taskFamily enum + [TUM.settings.id.TIME_PERIOD] = { "World War 2", "Korea War", "Vietnam War", "Late Cold War", "Modern" }, -- Must match values in the DCSEx.enums.timePeriod enum + } + + local function getFaction(side) + if side == coalition.side.BLUE then + return TUM.settings.getValue(TUM.settings.id.COALITION_BLUE, true) + else + return TUM.settings.getValue(TUM.settings.id.COALITION_RED, true) + end + end + + local function setAllToDefaults(coreSettings) + settings = { + [TUM.settings.id.AI_CAP] = 1, -- Enabled + [TUM.settings.id.COALITION_BLUE] = 1, + [TUM.settings.id.COALITION_RED] = 2, + [TUM.settings.id.ENEMY_AIR_DEFENSE] = 3, + [TUM.settings.id.ENEMY_AIR_FORCE] = 2, + [TUM.settings.id.MULTIPLAYER] = coreSettings.multiplayer, + [TUM.settings.id.PLAYER_COALITION] = coalition.side.BLUE, + [TUM.settings.id.TARGET_COUNT] = 2, + [TUM.settings.id.TARGET_LOCATION] = 1, + [TUM.settings.id.TASKING] = DCSEx.enums.taskFamily.GROUND_ATTACK, + [TUM.settings.id.TIME_PERIOD] = DCSEx.enums.timePeriod.MODERN + } + + -- TODO: set default time period according to mission year + -- if env.mission.date.Year <= 1945 then + -- settings[TUM.settings.id.TIME_PERIOD] = DCSEx.enums.timePeriod.WORLD_WAR_2 + -- elseif env.mission.date.Year < 1960 then + -- settings[TUM.settings.id.TIME_PERIOD] = DCSEx.enums.timePeriod.KOREA_WAR + -- elseif env.mission.date.Year < 1975 then + -- settings[TUM.settings.id.TIME_PERIOD] = DCSEx.enums.timePeriod.VIETNAM_WAR + -- elseif env.mission.date.Year < 1990 then + -- settings[TUM.settings.id.TIME_PERIOD] = DCSEx.enums.timePeriod.COLD_WAR + -- else + -- settings[TUM.settings.id.TIME_PERIOD] = DCSEx.enums.timePeriod.MODERN + -- end + + for i,id in pairs(SETTING_VALUES[TUM.settings.id.COALITION_BLUE]) do + if id == Library.factions.defaults[coalition.side.BLUE] then settings[TUM.settings.id.COALITION_BLUE] = i end + if id == Library.factions.defaults[coalition.side.RED] then settings[TUM.settings.id.COALITION_RED] = i end + end + + if #DCSEx.envMission.getPlayerGroups(coalition.side.RED) > 0 then + settings[TUM.settings.id.PLAYER_COALITION] = coalition.side.RED + end + end + + function TUM.settings.getSettingsName(id) + return SETTING_NAMES[id] + end + + function TUM.settings.getPossibleValues(id) + return SETTING_VALUES[id]; + end + + function TUM.settings.getValue(id, returnAsString) + returnAsString = returnAsString or false + + if returnAsString then + return SETTING_VALUES[id][settings[id]] + end + + return settings[id] + end + + function TUM.settings.getPlayerCoalition() + return settings[TUM.settings.id.PLAYER_COALITION] + end + + function TUM.settings.getEnemyCoalition() + return DCSEx.dcs.getOppositeCoalition(settings[TUM.settings.id.PLAYER_COALITION]) + end + + function TUM.settings.getPlayerFaction() + return getFaction(TUM.settings.getPlayerCoalition()) + end + + function TUM.settings.getEnemyFaction() + return getFaction(TUM.settings.getEnemyCoalition()) + end + + function TUM.settings.getSettingsSummary() + local showScoreMultiplier = true + if not DCSEx.io.canReadAndWrite() or TUM.settings.getValue(TUM.settings.id.MULTIPLAYER) then + showScoreMultiplier = false + end + + local settingsOrder = { + TUM.settings.id.PLAYER_COALITION, + TUM.settings.id.MULTIPLAYER, + -1, + TUM.settings.id.TIME_PERIOD, + TUM.settings.id.COALITION_BLUE, + TUM.settings.id.COALITION_RED, + -1, + TUM.settings.id.TASKING, + TUM.settings.id.TARGET_LOCATION, + TUM.settings.id.TARGET_COUNT, + -1, + TUM.settings.id.ENEMY_AIR_DEFENSE, + TUM.settings.id.ENEMY_AIR_FORCE, + -1, + TUM.settings.id.AI_CAP, + } + + local summary = "" + for _,v in pairs(settingsOrder) do + if v < 0 then + summary = summary.."\n" + else + summary = summary.."\n"..SETTING_NAMES[v]:upper()..": " + if type(settings[v]) == "boolean" then + if settings[v] then + summary = summary.."Enabled" + else + summary = summary.."Disabled" + end + else + summary = summary..SETTING_VALUES[v][settings[v]] + end + + if showScoreMultiplier then + local settingMultiplier = TUM.playerScore.getScoreMultiplier(v, settings[v]) + + if settingMultiplier > 0.0 then + summary = summary.." (+"..tostring(math.ceil(settingMultiplier * 100)).."% xp)" + end + end + end + end + + if showScoreMultiplier then + summary = summary.."\n\nTotal XP modifier: "..tostring(math.ceil(TUM.playerScore.getTotalScoreMultiplier() * 100)).."%" + end + + return summary + end + + function TUM.settings.printSettingsSummary(clearView) + trigger.action.outText("MISSION SETTINGS\n"..TUM.settings.getSettingsSummary(), 15, clearView or false) + trigger.action.outSound("UI-Ok.ogg") + end + + function TUM.settings.setValue(id, value, silent) + silent = silent or false + + settings[id] = value + + if not silent then + TUM.settings.printSettingsSummary(true) + trigger.action.outSound("UI-Ok.ogg") + end + end + + function TUM.settings.onStartUp(coreSettings) + -- Load mission zones + SETTING_VALUES[TUM.settings.id.TARGET_LOCATION] = {} + local missionZones = TUM.territories.getMissionZones() + for _,m in ipairs(missionZones) do + table.insert(SETTING_VALUES[TUM.settings.id.TARGET_LOCATION], m.name) + end + + -- Load available coalitions + SETTING_VALUES[TUM.settings.id.COALITION_BLUE] = { } + SETTING_VALUES[TUM.settings.id.COALITION_RED] = { } + for k,faction in pairs(Library.factions.tables) do + if not faction.theaters or #faction.theaters == 0 or DCSEx.table.contains(faction.theaters, env.mission.theatre) then + table.insert(SETTING_VALUES[TUM.settings.id.COALITION_BLUE], k) + table.insert(SETTING_VALUES[TUM.settings.id.COALITION_RED], k) + end + end + + setAllToDefaults(coreSettings) + TUM.settings.printSettingsSummary() + return true + end +end diff --git a/Script/The Universal Mission/SupportAWACS.lua b/Script/The Universal Mission/SupportAWACS.lua new file mode 100644 index 0000000..b0689e1 --- /dev/null +++ b/Script/The Universal Mission/SupportAWACS.lua @@ -0,0 +1,122 @@ +-- ==================================================================================== +-- TUM.SUPPORTAWACS - HANDLES THE FRIENDLY AWACS +-- ==================================================================================== +-- ==================================================================================== + +TUM.supportAWACS = {} + +do + local awacsGroupID = nil + local awacsCallsign = "AWACS" + + local function doAwacsPicture(bogeyDope, delayAnswer) + delayAnswer = delayAnswer or false + + if not awacsGroupID then return end + + local awacsGroup = DCSEx.world.getGroupByID(awacsGroupID) + if not awacsGroup then return end + local awacsUnit = awacsGroup:getUnits()[1] + if not awacsGroup then return end + local awacsController = awacsUnit:getController() + if not awacsController then return end + + -- local aircraftGroups = awacsController:getDetectedTargets(Controller.Detection.RADAR) + local detectedUnits = awacsController:getDetectedTargets() + + local detectedAircraft = {} + for _,u in pairs(detectedUnits) do + if u.object and u.distance and Object.getCategory(u.object) == Object.Category.UNIT and u.object:inAir() then + if u.object:getCoalition() ~= TUM.settings.getPlayerCoalition() then + table.insert(detectedAircraft, u.object) + end + end + end + + -- No aircraft on picture + if #detectedAircraft == 0 then + TUM.radio.playForCoalition(TUM.settings.getPlayerCoalition(), "awacsPictureClear", { awacsCallsign }, "Overlord", delayAnswer) + return + end + + for _,p in pairs(coalition.getPlayers(TUM.settings.getPlayerCoalition())) do + local pVec3 = p:getPoint() + + local maxCount = 5 + if bogeyDope then maxCount = 1 end -- TODO: Only report the nearest FIGHTER (not transport, awacs, etc) + local sortedThreats = DCSEx.dcs.getNearestObjects(pVec3, detectedAircraft, maxCount) + + local pictureMsg = "" + for ___,u in pairs(sortedThreats) do + local typeName = Library.objectNames.get(u) + pictureMsg = pictureMsg.."\n- "..typeName..", "..DCSEx.dcs.getBRAA(u:getPoint(), pVec3, true) + end + + TUM.radio.playForUnit(DCSEx.dcs.getObjectIDAsNumber(p), "awacsPicture", { awacsCallsign, pictureMsg }, "Overlord", delayAnswer) + end + end + + local function doCommandPicture() + TUM.radio.playForCoalition(TUM.settings.getPlayerCoalition(), "playerAwacsPicture", { awacsCallsign }, "Flight", false) + doAwacsPicture(false, true) + end + + local function doCommandBogeyDope() + TUM.radio.playForCoalition(TUM.settings.getPlayerCoalition(), "playerAwacsBogeyDope", { awacsCallsign }, "Flight", false) + doAwacsPicture(true, true) + end + + ---------------------------------------------------------- + -- Called on every mission update tick (every 15 seconds) + -- @return True if something was done this tick, false otherwise + ---------------------------------------------------------- + function TUM.supportAWACS.onClockTick() + if not awacsGroupID then return false end -- No awacs aircraft + if TUM.mission.getStatus() == TUM.mission.status.NONE then return false end -- Not in a mission + + doAwacsPicture(false, false) + return true + end + + function TUM.supportAWACS.createMenu() + if not awacsGroupID then return end -- No AWACS + + local rootPath = missionCommands.addSubMenu("✈ Awacs") + missionCommands.addCommand("Bogey dope", rootPath, doCommandBogeyDope, nil) + missionCommands.addCommand("Picture", rootPath, doCommandPicture, nil) + end + + function TUM.supportAWACS.create() + if awacsGroupID then return end -- Already spawned + + local awacsUnits = Library.factions.getUnits(TUM.settings.getPlayerFaction(), DCSEx.enums.unitFamily.PLANE_AWACS, 1) + awacsCallsign = "AWACS" + + if awacsUnits and #awacsUnits > 0 then + local groupInfo = DCSEx.unitGroupMaker.create( + TUM.settings.getPlayerCoalition(), + Group.Category.AIRPLANE, + TUM.territories.getTerritoryCenter(TUM.settings.getPlayerCoalition()), + { DCSEx.table.getRandom(awacsUnits) }, + { + immortal = true, + invisible = true, + silenced = true, + taskAwacs = true, + unlimitedFuel = true + }) + + if groupInfo then + awacsGroupID = groupInfo.groupID + if groupInfo.callsign then + awacsCallsign = groupInfo.callsign.name:sub(1, #groupInfo.callsign.name - 1) + end + TUM.log("Spawned AWACS aircraft") + else + TUM.log("Failed to create AWACS aircraft", TUM.logLevel.WARNING) + end + else + TUM.log("No AWACS aircraft available") + end + end +end diff --git a/Script/The Universal Mission/SupportJTAC.lua b/Script/The Universal Mission/SupportJTAC.lua new file mode 100644 index 0000000..21757c2 --- /dev/null +++ b/Script/The Universal Mission/SupportJTAC.lua @@ -0,0 +1,93 @@ +-- ==================================================================================== +-- TUM.SUPPORTJTAC - HANDLES FRIENDLY JTAC SMOKE MARKERS AND LASING +-- ==================================================================================== +-- ==================================================================================== + +TUM.supportJTAC = {} + +do + local JTAC_CALLSIGNS = { + "Anvil", + "Axeman", + "Badger", + "Darknight", + "Deathstar", + "Eyeball", + "Ferret", + "Finger", + "Firefly", + "Hammer", + "Jaguar", + "Mantis", + "Moonbeam", + "Pinpoint", + "Playboy", + "Pointer", + "Shaba", + "Warrior", + "Whiplash", + } + + local SMOKE_DURATION = 300 -- in seconds + local SMOKE_MARKER_PENALTY = -25 + + local jtacName = {} + local lastSmoke = {} + + local function spawnSmoke(args) + trigger.action.smoke(args.point3, args.smokeColor) + end + + local function doCommandSmoke(index) + local obj = TUM.objectives.getObjective(index) + if not obj then return end + + -- Pick a smoke color + local smokeColor = DCSEx.table.getRandom({ trigger.smokeColor.Red, trigger.smokeColor.Orange }) -- TODO: green or blue smoke when marking friendlies + local smokeColorName = "red" + if smokeColor == trigger.smokeColor.Orange then smokeColorName = "orange" end + + TUM.radio.playForCoalition(TUM.settings.getPlayerCoalition(), "playerJTACSmoke", { jtacName[index], obj.name }, "Flight", false) + + if not lastSmoke[index] then lastSmoke[index] = -3600 end + if lastSmoke[index] + SMOKE_DURATION > timer.getAbsTime() then + TUM.radio.playForCoalition(TUM.settings.getPlayerCoalition(), "jtacSmokeAlreadyOut", { jtacName[index], obj.name }, jtacName[index], true) + return + end + + if obj.completed then + TUM.radio.playForCoalition(TUM.settings.getPlayerCoalition(), "jtacSmokeNoTarget", { jtacName[index] }, jtacName[index], true) + return + end + + if obj.isSceneryTarget then + TUM.radio.playForCoalition(TUM.settings.getPlayerCoalition(), "jtacSmokeOK", { jtacName[index], smokeColorName }, jtacName[index], true, spawnSmoke, { point3 = obj.point3, smokeColor = smokeColor }) + else + for _,id in ipairs(obj.unitsID) do + if not DCSEx.table.contains(obj.completedUnitsID, id) then + local unit = DCSEx.world.getUnitByID(id) + if unit and unit:isActive() and unit:getLife() > 0 then + TUM.radio.playForCoalition(TUM.settings.getPlayerCoalition(), "jtacSmokeOK", { jtacName[index], smokeColorName }, jtacName[index], true, spawnSmoke, { point3 = unit:getPoint(), smokeColor = smokeColor }) + lastSmoke[index] = timer.getAbsTime() + TUM.playerScore.award(SMOKE_MARKER_PENALTY, "called for smoke marker") + return + end + end + end + end + + TUM.radio.playForCoalition(TUM.settings.getPlayerCoalition(), "jtacSmokeNoTarget", { jtacName[index] }, jtacName[index], true) + end + + function TUM.supportJTAC.setupJTACOnObjective(index, menuRoot) + local obj = TUM.objectives.getObjective(index) + if not obj then return end + + jtacName[index] = DCSEx.table.getRandom(JTAC_CALLSIGNS) + lastSmoke[index] = -3600 + local objectiveDB = Library.tasks[obj.taskID] + if not DCSEx.table.contains(objectiveDB.flags, DCSEx.enums.taskFlag.ALLOW_JTAC) then return end -- No JTAC for this objective + + missionCommands.addCommand("Require smoke marker on target ("..tostring(SMOKE_MARKER_PENALTY).."xp)", menuRoot, doCommandSmoke, index) + end +end diff --git a/Script/The Universal Mission/Territories.lua b/Script/The Universal Mission/Territories.lua new file mode 100644 index 0000000..05cae5a --- /dev/null +++ b/Script/The Universal Mission/Territories.lua @@ -0,0 +1,176 @@ +-- ==================================================================================== +-- TUM.TERRITORIES - HANDLES THE MISSION SPECIAL ZONES (COALITION TERRITORIES, WATER AND BATTLE ZONES) +-- ==================================================================================== +-- (local) assignTerritoryToZone(coalitionID, zone) +-- TUM.territories.getCenter(side) +-- TUM.territories.getPointOwner(point) +-- TUM.territories.getRandomPoint(side, surfaceType) +-- TUM.territories.getSurfaceArea(side) +-- TUM.territories.onInitialize() +-- ==================================================================================== + +TUM.territories = {} + +do + local coalitionZones = { {}, {} } + local missionZones = {} + local waterZones = {} + + local function addZoneToCoalition(zone, side) + table.insert(coalitionZones[side], zone) + local airbases = world.getAirbases() + + for _,ab in pairs(airbases) do + local airbasePoint2 = DCSEx.math.vec3ToVec2(ab:getPoint()) + + if DCSEx.zones.isPointInside(zone, airbasePoint2) then + if ab:getDesc().category ~= Airbase.Category.SHIP then -- Ignore ships + ab:setCoalition(side) + end + end + end + end + + function TUM.territories.getMissionZones() + return DCSEx.table.deepCopy(missionZones) + end + + function TUM.territories.getWaterZones() + return DCSEx.table.deepCopy(waterZones) + end + + ------------------------------------- + -- Returns the coalition to which belong a given point on the map. + -- Return coalition.side.NEUTRAL if the point isn't in any coalition territory. + -- @param point A vec2 or vec3 + -- @return A value from the coalition.side enum + ------------------------------------- + function TUM.territories.getPointOwner(point) + for side=1,2 do + for _,z in ipairs(coalitionZones[side]) do + if DCSEx.zones.isPointInside(z, point) then + return side + end + end + end + + return coalition.side.NEUTRAL + end + + function TUM.territories.getTerritoryZones(side) + return DCSEx.table.deepCopy(coalitionZones[side]) + end + + function TUM.territories.getTerritoryCenter(side) + local center = { x = 0, y = 0 } + + if #coalitionZones[side] == 0 then return center end + + for _,z in ipairs(coalitionZones[side]) do + center.x = center.x + z.x + center.y = center.y + z.y + end + + -- TODO: bigger zones should skew the center in their favor + + center.x = center.x / #coalitionZones[side] + center.y = center.y / #coalitionZones[side] + + return center + end + + -- function TUM.territories.getCenter(side) + -- return DCSEx.zones.getCenter(zones[side]) + -- end + + -- function TUM.territories.getSurfaceArea(side) + -- return DCSEx.zones.getSurfaceArea(zones[side]) + -- end + + -- function TUM.territories.getZone(side) + -- return DCSEx.table.deepCopy(zones[side]) + -- end + + function TUM.territories.getRandomPointInTerritory(side, surfaceType) + if #coalitionZones[side] == 0 then return nil end + + local zone = DCSEx.table.getRandom(coalitionZones[side]) -- TODO: bigger zones should be selected more often + + return DCSEx.zones.getRandomPointInside(zone, surfaceType) + end + + ------------------------------------- + -- Called on mission start up + -- @return True if started up properly, false if an error happened + ------------------------------------- + function TUM.territories.onStartUp() + coalitionZones = { {}, {} } + missionZones = {} + waterZones = {} + + -- Disable autocapture for all bases and give them to the neutral coalition + for _,ab in pairs(world.getAirbases()) do + ab:autoCapture(false) + if ab:getDesc().category ~= Airbase.Category.SHIP then -- Ignore ships + ab:setCoalition(coalition.side.NEUTRAL) + end + end + + local zones = DCSEx.zones.getAll() + for _,z in ipairs(zones) do + if DCSEx.string.startsWith(z.name:lower(), "blufor") then + addZoneToCoalition(z, coalition.side.BLUE) + elseif DCSEx.string.startsWith(z.name:lower(), "redfor") then + addZoneToCoalition(z, coalition.side.RED) + elseif DCSEx.string.startsWith(z.name:lower(), "water") then + table.insert(waterZones, z) + else + table.insert(missionZones, z) + end + end + + for side=1,2 do + if #coalitionZones[side] == 0 or #coalition.getAirbases(side) == 0 then + local name = DCSEx.dcs.getCoalitionAsString(side) + local zoneName = "BLUFOR" + if side == 1 then zoneName = "REDFOR" end + + TUM.log("Coalition "..name.." has no territory zones and/or controls no airfields. Please add zone with a name starting with "..zoneName.." in the mission editor and make sure at least one contains an airbase.", TUM.logLevel.ERROR) + return false + end + end + + if #missionZones == 0 then + TUM.log("No mission zones found. Create at least one mission zone in the mission editor.", TUM.logLevel.ERROR) + return false + end + + if #missionZones > 10 then + TUM.log("Too many mission zones, extra zones removed.", TUM.logLevel.WARNING) + while #missionZones > 10 do + table.remove(missionZones, 11) + end + end + + -- zones = {} + -- zones[coalition.side.BLUE] = DCSEx.zones.getByName("BLUFOR") + -- zones[coalition.side.RED] = DCSEx.zones.getByName("REDFOR") + + -- if not zones[coalition.side.BLUE] then + -- TUM.log("BLUFOR zone not found.", TUM.logLevel.ERROR) + -- return false + -- elseif not zones[coalition.side.RED] then + -- TUM.log("REDFOR zone not found.", TUM.logLevel.ERROR) + -- return false + -- end + + -- -- TODO: square kilometers if "metric system" enabled? + -- for side=1,2 do + -- TUM.log( + -- "Coalition "..DCSEx.dcs.getCoalitionAsString(side):upper().."'s territory is ".. + -- DCSEx.string.toStringThousandsSeparator(math.floor(TUM.territories.getSurfaceArea(side) / 2589988.110336)).." squared miles.") + -- end + + return true + end +end diff --git a/Theaters/Caucasus.json b/Theaters/Caucasus.json new file mode 100644 index 0000000..90e1300 --- /dev/null +++ b/Theaters/Caucasus.json @@ -0,0 +1,111 @@ +{ + "displayName": "Caucasus", + + "dcsID": "Caucasus", + + "mapCenter": [-195544.17125104, 567104.46094014], + "mapZoom": 1162321.1721, + + "dateTime": { + "day": 1, + "month": 6, + "year": 2010, + "hour": 12, + "minute": 0 + }, + + "temperature": 20, + + "airbasesIDs": [ + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 + ], + + "bullseye": { + "blue": [-149214.54, 654166.11714286], + "red": [-270504.66208029, 733703.82894114] + }, + + "player": { + "airdromeID": 20, + "coordinates": [-220592.328125, 564391.96875] + }, + + "territories": { + "blue": [ + [ + [-375703.95292654, 598321.08670511], + [-370058.39294777, 966278.76061563], + [-199363.2265308, 963954.11827143], + [-171467.5184004, 445558.87551483] + ] + ], + "red": [ + [ + [-166818.233712, 438584.94848223], + [-197038.5841866, 964286.21003488], + [24134.53027586, 931741.21721608], + [9522.4926837457, 191176.58470665] + ] + ] + }, + + "water": [ + [ + [-376700.22821691, 593671.80201671], + [-289028.00266423, 611604.7572434], + [-18373.215446654, 217743.92578323], + [-34812.459814021, 176857.71554837] + ] + ], + + "targetZones": { + "Beslan": [ + [-175147.14565671, 799751.46262936], + [-166703.83615973, 903949.57755804], + [-51375.904166842, 898960.34921891], + [-83997.781768822, 700158.78924449] + ], + "Gudauta": [ + [-269105.81777443, 598500.41625738], + [-215037.95776597, 674356.81686626], + [-155321.21686111, 523719.9929621], + [-170922.88790833, 469652.13295364] + ], + "Krasnodar": [ + [-106832.96941319, 326629.38553691], + [-45743.111618209, 512436.81651571], + [54683.538881552, 440470.42617563], + [12627.613040497, 186322.11570511] + ], + "Kutaisi": [ + [-273881.04198694, 596033.84611676], + [-237393.71785064, 646917.52943609], + [-282320.28702526, 739501.01176832], + [-363237.75415746, 612415.91001469] + ], + "Mineralnye Vody": [ + [-101304.00818205, 631085.71482831], + [-72437.611949508, 794330.16248821], + [5601.1971757155, 752125.50041029], + [425.15371332823, 589876.44572392] + ], + "Sochi": [ + [-96123.143050964, 382119.2820623], + [-170221.94557815, 463194.2732755], + [-147701.45317085, 498456.86211823], + [-80817.404891958, 409921.39731707] + ], + "Tbilisi": [ + [-372327.21895733, 926566.65406156], + [-298318.79760333, 936139.55478797], + [-296039.71423042, 883050.54221029], + [-333825.30929469, 864068.20535336] + ], + "Tskhinvali": [ + [-298592.15531772, 770083.97260799], + [-297358.401497, 875693.29966117], + [-256644.52541342, 816843.24241308], + [-268735.31285642, 791674.6644705] + ] + } +} diff --git a/Theaters/PersianGulf.json b/Theaters/PersianGulf.json new file mode 100644 index 0000000..de0b260 --- /dev/null +++ b/Theaters/PersianGulf.json @@ -0,0 +1,142 @@ +{ + "displayName": "Persian Gulf", + + "dcsID": "PersianGulf", + + "mapCenter": [118386.33272362, -41217.28531875], + "mapZoom": 1737026.8428411, + + "dateTime": { + "day": 1, + "month": 6, + "year": 2010, + "hour": 12, + "minute": 0 + }, + + "temperature": 20, + + "airbasesIDs": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29 + ], + + "bullseye": { + "blue": [166322.25019013, -44195.045620775], + "red": [-169172.07717003, -57594.966979834] + }, + + "player": { + "airdromeID": 10, + "coordinates": [-219.726892, -177.707709] + }, + + "territories": { + "blue": [ + [ + [-307115.20244152, -247609.54575878], + [-243044.54390661, 107267.25709719], + [33454.560159089, 29200.580921164], + [-62340.405029027, -302660.54848052] + ] + ], + "red": [ + [ + [49941.710284372, -572035.39823009], + [-29.317171471281, -88484.682763893], + [320030.98115796, 223805.30973451], + [395252.22009601, -368938.05309735] + ], + [ + [-55085.147072128, 84593.785575221], + [-146650.77273585, 370437.78221239], + [484583.00108716, 358200.5899705], + [458388.99634675, -167846.60766962] + ] + ] + }, + + "water": [ + [ + [-204699.00571429, -358539.57142857], + [-112209.71714286, -145492.10571429], + [-47856.108767926, -66643.239544395], + [163877.71142857, -475876.72857143] + ], + [ + [-55151.648571428, 15451.557142857], + [-218963.52285714, 71589.334285714], + [-92883.597142857, 206412.02857143], + [-15579.117142857, 38918.988571429] + ], + [ + [-7420.5808003751, -98644.076928495], + [47871.218593147, 71929.786756514], + [82110.513303182, 55064.173125399], + [4464.0777089467, -153907.73899684] + ] + ], + + "targetZones": { + "Abu Dhabi": [ + [-204860.98435502, -222106.10557259], + [-247744.31778359, -141358.86196664], + [-182710.05131177, -81671.372436289], + [-163177.94779087, -178568.06476343] + ], + "Al Ain": [ + [-248792.74376565, -139154.08428967], + [-264772.64606392, -20923.69604369], + [-181322.04517298, -25936.998725498], + [-182993.14606692, -81605.547254743] + ], + "Bandar Abbas": [ + [79775.008137338, -149137.23425317], + [80802.268378164, 93501.634630072], + [159079.49872915, 72134.621620879], + [157230.43029566, -82981.674743934] + ], + "Bandar-e-Jask": [ + [-58172.158403749, 103238.87890696], + [-83573.640846363, 292247.75901759], + [128105.37950876, 82753.812420981], + [94236.736251937, 48612.034944349] + ], + "Dubai": [ + [-151296.20914139, -165626.15303421], + [-171798.65224026, -77327.287663645], + [-89788.8798448, -20096.541590128], + [-68028.618150913, -75566.341630614] + ], + "Fujairah": [ + [-176241.90974867, -23251.723543507], + [-243696.97303153, 81760.198117366], + [-99634.018603114, 16074.29694469], + [-91586.382645962, -20140.064862495] + ], + "Jiroft": [ + [165007.33580356, 6125.0737463145], + [168663.08801595, 346110.02949853], + [508891.61819553, 92577.799040587], + [459092.29155577, -88924.483775809] + ], + "Lar": [ + [35025.034918605, -278211.20943952], + [195878.13226374, -27182.890855455], + [484682.5570425, -258713.86430678], + [346982.5570425, -532895.28023598] + ], + "Liwa": [ + [-290302.06730787, -336600.37066318], + [-293695.27073546, -66994.934689444], + [-198994.04780189, -233570.37568008], + [-195909.31741318, -331047.85596349] + ], + "Qeshm island": [ + [38930.467140658, -29990.631302214], + [99137.421110632, 41009.016231995], + [106449.04588886, -32833.518828454], + [40494.05528009, -120963.0321419] + ] + } +} diff --git a/Theaters/Syria.json b/Theaters/Syria.json new file mode 100644 index 0000000..5d08a0a --- /dev/null +++ b/Theaters/Syria.json @@ -0,0 +1,157 @@ +{ + "displayName": "Syria", + + "dcsID": "Syria", + + "mapCenter": [51819.182300883, -5.8207660913467], + "mapZoom": 1073746.3126844, + + "dateTime": { + "day": 1, + "month": 6, + "year": 2010, + "hour": 12, + "minute": 0 + }, + + "temperature": 20, + + "airbasesIDs": [ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, 52, 53, 54, 55, 57, 58, 59, 60, 63, 64, 65, 66, 67, 68 + ], + + "bullseye": { + "blue": [59008.849557521, 148483.77581121], + "red": [-122607.66961652, -15032.448377581] + }, + + "player": { + "airdromeID": 15, + "coordinates": [147687.484375, 39418.742188] + }, + + "territories": { + "blue": [ + [ + [61504.424778761, -82831.85840708], + [183604.71976401, 134064.89675516], + [281162.24188791, 326725.66371681], + [181764.01179941, -412318.5840708] + ], + + [ + [-374753.69008506, -151027.11089456], + [-378159.2920354, -32519.174041298], + [-190735.49569322, -14514.794973451], + [-216169.41541003, -84131.998300885] + ], + + [ + [-66466.076696166, -326725.66371681], + [-11858.407079646, -146029.49852507], + [85085.545722713, -117805.30973451], + [46737.463126843, -349427.72861357] + ] + ], + "red": [ + [ + [17913.491728614, -113408.46215929], + [-25925.600660767, 415583.25267257], + [263146.71764012, 330296.29111504], + [151556.30064897, 69121.577061947] + ], + [ + [-36707.96460177, 34359.8820059], + [-371410.02949853, 365380.53097345], + [-25970.501474926, 398206.48967552], + [-4495.5752212389, -25769.911504425] + ], + [ + [-252684.36578171, -16566.371681416], + [-302690.26548673, 70253.687315634], + [-198383.48082596, 232849.55752212], + [-74135.693215339, 67185.840707965] + ] + ] + }, + + "water": [ + [ + [-296861.35693215, -335622.41887906], + [-371410.02949853, -152471.97640118], + [127728.61356932, -2761.0619469027], + [165769.91150442, -60129.793510324] + ], + [ + [31398.230088494, -409864.30678466], + [87539.823008849, -115657.81710914], + [158713.86430678, -76696.16519174], + [85392.33038348, -383174.04129794] + ] + ], + + "targetZones": { + "Aleppo": [ + [92448.377581118, -613.56932153399], + [-32719.764011802, 391764.01179941], + [166383.48082596, 396058.99705015], + [181415.92920354, 24542.772861357] + ], + "Cyprus": [ + [-32719.7640118, -321203.53982301], + [-59410.029498526, -159221.23893805], + [86005.899705014, -111056.04719764], + [46123.893805309, -342371.68141593] + ], + "Damascus": [ + [-299622.41887906, -33746.312684366], + [-257592.92035398, 143268.43657817], + [-171079.6460177, 280707.96460177], + [-93463.12684366, 53073.746312684] + ], + "Iraq": [ + [-327233.03834808, 280707.96460177], + [-334289.08554572, 387469.02654867], + [-122300.88495575, 385935.10324484], + [-190100.29498525, 261994.10029499] + ], + "Israel": [ + [-370182.89085546, -143882.0058997], + [-374784.66076696, -58289.085545723], + [-219759.76376893, -12984.78001191], + [-214002.44876894, -82648.446833453] + ], + "Jordan": [ + [-377545.72271386, -34359.8820059], + [-359445.42772861, 263834.80825959], + [-222005.89970501, 261380.53097345], + [-310053.09734513, -21474.926253687] + ], + "Lebanon": [ + [-201144.54277286, -91421.828908555], + [-183044.24778761, -15032.448377581], + [-72294.985250738, 56448.377581121], + [-41923.303834809, -9203.5398230089] + ], + "Palmyra": [ + [-40082.595870209, -14112.09439528], + [-134572.27138643, 331941.00294985], + [-39469.026548676, 394831.85840708], + [95823.008849554, -17793.510324484] + ], + "Turkey east": [ + [189126.84365782, -11351.032448378], + [193728.61356932, 383480.8259587], + [305705.01474926, 382867.25663717], + [306625.36873156, -21168.14159292] + ], + "Turkey west": [ + [141882.0058997, -319056.04719764], + [182991.15044248, -16873.156342183], + [307238.9380531, -24542.772861357], + [289752.21238938, -383787.61061947] + ] + } +} diff --git a/Tools/ExportAirbaseCoordinates.lua b/Tools/ExportAirbaseCoordinates.lua new file mode 100644 index 0000000..2b1c947 --- /dev/null +++ b/Tools/ExportAirbaseCoordinates.lua @@ -0,0 +1,36 @@ +do + local abInfo = {} + + local airbases = world.getAirbases() + + local function extractAirbaseInfo(ab) + if ab:getDesc().category ~= Airbase.Category.SHIP then return nil end + local abDesc = airbases:getDesc() + if not abDesc then return nil end + + local airbase = {} + airbase.id = airbase.getID() + airbase.name = abDesc.displayName + airbase.coordinates = ab:getPoint() + + local parking = airbases:getParking() + if not parking then return nil end + for _,p in ipairs(parking) do + if p.Term_Type == 16 then + airbase.runway = p.vTerminalPos + end + end + + return airbase + end + + for _,ab in ipairs(airbases) do + local airbaseInfo = extractAirbaseInfo(ab) + if airbaseInfo then + table.insert(airbases, airbaseInfo) + end + end + + local jsonAirbases = net.lua2json(airbases) + trigger.action.outText(jsonAirbases, 3600, true) +end \ No newline at end of file diff --git a/docs/career-mode-tn.jpg b/docs/career-mode-tn.jpg new file mode 100644 index 0000000..4416fce Binary files /dev/null and b/docs/career-mode-tn.jpg differ diff --git a/docs/career-mode.jpg b/docs/career-mode.jpg new file mode 100644 index 0000000..b7c7025 Binary files /dev/null and b/docs/career-mode.jpg differ diff --git a/docs/mission-settings-tn.jpg b/docs/mission-settings-tn.jpg new file mode 100644 index 0000000..857ab43 Binary files /dev/null and b/docs/mission-settings-tn.jpg differ diff --git a/docs/mission-settings.jpg b/docs/mission-settings.jpg new file mode 100644 index 0000000..b0a99b0 Binary files /dev/null and b/docs/mission-settings.jpg differ diff --git a/docs/target-zones-tn.jpg b/docs/target-zones-tn.jpg new file mode 100644 index 0000000..e7de659 Binary files /dev/null and b/docs/target-zones-tn.jpg differ diff --git a/docs/target-zones.jpg b/docs/target-zones.jpg new file mode 100644 index 0000000..9cd204d Binary files /dev/null and b/docs/target-zones.jpg differ