mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
Merge branch 'master' into LDT-Debug
# Conflicts: # Moose Development/Moose/Moose.lua # Moose Mission Setup/Moose.lua
This commit is contained in:
commit
c1910646e2
565
Moose Development/Documentation/DCS_ControlAPI.txt
Normal file
565
Moose Development/Documentation/DCS_ControlAPI.txt
Normal file
@ -0,0 +1,565 @@
|
||||
DCS Simulation Control User Scripts
|
||||
====================================
|
||||
|
||||
The behaviour of the DCS can be altered using the *GameGUI.lua scripts.
|
||||
You define the hooks to the DCS events, and then do what you want using the provided API.
|
||||
===================================================================================================
|
||||
|
||||
When loading, DCS searches for Saved Games\DCS\Scripts\*GameGUI.lua files,
|
||||
sorts them by name and then loads into the GUI Lua-state.
|
||||
Each user script is loaded into an isolated environment, so the only
|
||||
thing they share is the state of the simulator.
|
||||
|
||||
Each script defines a set of callbacks to the DCS events and sets them with the call
|
||||
DCS.setUserCallbacks(cb_table)
|
||||
For each callback type the hooks of all user scripts will be called in order of loading.
|
||||
|
||||
For callbacks which are supposed to returning a value, currently there are 3 of them:
|
||||
onPlayerTryConnect
|
||||
onPlayerTrySendChat
|
||||
onPlayerTryChangeSlot
|
||||
returning a value means breaking the hook call chain.
|
||||
Returning nothing (or nil) means continuing the hook chain, which ends with the default allow-all handlers.
|
||||
|
||||
The example user script 'testGameGUI.lua':
|
||||
----------------------------------------------------------------------------------------------
|
||||
local test = {}
|
||||
|
||||
function test.onPlayerTryConnect(ipaddr, name, ucid, playerID)
|
||||
print('onPlayerTryConnect(%s, %s, %s, %d)', ipaddr, name, ucid, playerID)
|
||||
-- if you want to gently intercept the call, allowing other user scripts to get it,
|
||||
-- you better return nothing here
|
||||
return true -- allow the player to connect
|
||||
end
|
||||
|
||||
function test.onSimulationStart()
|
||||
print('Current mission is '..DCS.getMissionName())
|
||||
end
|
||||
|
||||
DCS.setUserCallbacks(test) -- here we set our callbacks
|
||||
----------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
The available API are documented below.
|
||||
The full list of the callbacks is at the end of this document.
|
||||
|
||||
In addition, all standard lua 5.1 libraries are available as well, namely:
|
||||
base api, like print, etc,
|
||||
math.*
|
||||
table.*
|
||||
string.*
|
||||
io.*
|
||||
os.*
|
||||
debug.*
|
||||
|
||||
===================================================================================================
|
||||
|
||||
|
||||
|
||||
Lua File System (lfs) API
|
||||
-------------------------------
|
||||
lfs.currentdir() -> string
|
||||
Returns the path of the DCS install folder
|
||||
|
||||
lfs.writedir() -> string
|
||||
Returns the path of the current 'Saved Games\DCS' folder.
|
||||
|
||||
lfs.tempdir() -> string
|
||||
Returns the pat of the DCS Temp folder (AppData\Local\Temp\DCS).
|
||||
|
||||
lfs.mkdir()
|
||||
lfs.rmdir()
|
||||
lfs.attributes()
|
||||
lfs.dir()
|
||||
lfs.normpath()
|
||||
lfs.realpath()
|
||||
|
||||
|
||||
|
||||
DCS Control API, table 'DCS.*'
|
||||
-------------------------------
|
||||
|
||||
DCS.setPause(bool)
|
||||
Pauses/resumes the simulation. Server-side only.
|
||||
|
||||
DCS.getPause() -> bool
|
||||
true if simulation is paused
|
||||
|
||||
DCS.stopMission()
|
||||
stops current mission
|
||||
|
||||
DCS.exitProcess()
|
||||
Exits the DCS process.
|
||||
|
||||
DCS.isMultiplayer() -> bool
|
||||
True when running in the multiplayer mode.
|
||||
|
||||
DCS.isServer() -> bool
|
||||
True when running as a server or in the single-player mode.
|
||||
|
||||
DCS.getModelTime() -> number
|
||||
returns current DCS simulation time in seconds.
|
||||
|
||||
DCS.getRealTime() -> number
|
||||
returns current DCS real time in seconds relative to the DCS start time.
|
||||
|
||||
DCS.getMissionOptions() -> table
|
||||
Returns the value of 'mission.options'
|
||||
|
||||
DCS.getMissionDescription() -> string
|
||||
translated mission.descriptionText string
|
||||
|
||||
DCS.getAvailableCoalitions() -> table {
|
||||
[coalition_id] = { name = "coalition name", }
|
||||
...
|
||||
}
|
||||
Returns a list of coalitions which have available slots.
|
||||
|
||||
DCS.getAvailableSlots(coalitionID) -> array of {unitId, type, role, callsign, groupName, country}
|
||||
Returns the list of available slots.
|
||||
NOTE: the returned unitID is actually a slotID, which for multi-seat units is 'unitID_seatID'
|
||||
|
||||
DCS.getCurrentMission() -> table with the currently loaded mission
|
||||
NOTE: to get valid mission.options use DCS.getMissionOptions()
|
||||
|
||||
DCS.getMissionName() -> string
|
||||
Returns the name of the current mission
|
||||
|
||||
DCS.getMissionFilename() -> string
|
||||
Returns the file name of the current mission (returns nil when acting as a multiplayer client).
|
||||
|
||||
DCS.getMissionResult(string side) -> integer [0, 100]
|
||||
Gets missin result for either 'red' or 'blue'
|
||||
|
||||
DCS.getUnitProperty(missionId, propertyId) -> string
|
||||
propertyId:
|
||||
DCS.UNIT_RUNTIME_ID, // unique within runtime mission. int
|
||||
DCS.UNIT_MISSION_ID, // unique within mission file. int>0
|
||||
DCS.UNIT_NAME, // unit name, as assigned by mission designer.
|
||||
DCS.UNIT_TYPE, // unit type (Ural, ZU-23, etc)
|
||||
DCS.UNIT_CATEGORY,
|
||||
DCS.UNIT_GROUP_MISSION_ID, // group ID, unique within mission file. int>0
|
||||
DCS.UNIT_GROUPNAME, // group name, as assigned by mission designer.
|
||||
DCS.UNIT_GROUPCATEGORY,
|
||||
DCS.UNIT_CALLSIGN,
|
||||
DCS.UNIT_HIDDEN,// ME hiding
|
||||
DCS.UNIT_COALITION,// "blue", "red" or "unknown"
|
||||
DCS.UNIT_COUNTRY_ID,
|
||||
DCS.UNIT_TASK, //"unit.group.task"
|
||||
DCS.UNIT_PLAYER_NAME, // valid for network "humanable" units
|
||||
DCS.UNIT_ROLE,//"artillery_commander", "instructor", etc
|
||||
DCS.UNIT_INVISIBLE_MAP_ICON,//ME invisible map icon
|
||||
|
||||
DCS.getUnitType(missionId) -> typeId
|
||||
a shortcut for DCS.getUnitProperty(missionId, DCS.UNIT_TYPE)
|
||||
|
||||
DCS.getUnitTypeAttribute(typeId, attr) -> string
|
||||
Returns a value from Database: Objects[typeId][attr],
|
||||
for example DCS.getUnitTypeAttribute("Ural", "DisplayName")
|
||||
|
||||
DCS.writeDebriefing(str)
|
||||
Writes a custom string to the debriefing file
|
||||
|
||||
DCS.setUserCallbacks(cb_table)
|
||||
Hooks the callbacks using the handlers from the provided table.
|
||||
See: "GameGUI scripts" section.
|
||||
|
||||
|
||||
|
||||
Logging API 'log.*'
|
||||
------------------------
|
||||
Logging works as follows:
|
||||
a) each log message is accompanied with 2 attributes: a subsystem, and level.
|
||||
b) after each messages gets into a logger it passes (asynchronously) through
|
||||
a series of output filters which decide where the message will be written to.
|
||||
|
||||
Writing to log is done by:
|
||||
|
||||
log.write(SUBSYSTEM_NAME, LOG_LEVEL, message, ...)
|
||||
if there are any arguments after 'message',
|
||||
the actual string is formed as string.format(message, ...)
|
||||
|
||||
SUBSYSTEM_NAME is a string
|
||||
LOG_LEVEL is one of the values, listed below
|
||||
see log.set_output()
|
||||
|
||||
log.set_output(log_file_name_wo_ext, rule_subsystem_name, rule_level_mask, rule_output_mode)
|
||||
|
||||
the args:
|
||||
log_file_name_wo_ext: resulting log will be written to $WRITE_DIR/Logs/<log_file_name_wo_ext>.log
|
||||
|
||||
rule_subsytem_name: the name of the subsystem whose messages to write or empty string to match all subsystems
|
||||
|
||||
rule_level_mask: a sum of log-level bit flags to match messages
|
||||
valid flags are:
|
||||
log.ALERT
|
||||
log.ERROR
|
||||
log.WARNING
|
||||
log.INFO
|
||||
log.DEBUG
|
||||
log.ALL - includes all of the above
|
||||
log.TRACE - a special level which is excluded from dcs.log file
|
||||
|
||||
rule_output_mode: a sum of output flags:
|
||||
log.MESSAGE
|
||||
log.TIME
|
||||
log.MODULE - this is a 'subsystem', not a dlc
|
||||
log.LEVEL
|
||||
log.FULL - all of the above
|
||||
|
||||
So, in order to save net.trace(msg) messages to a file, you should issue a call:
|
||||
log.set_output('lua-net', 'LuaNET', log.TRACE, log.MESSAGE + log.TIME)
|
||||
|
||||
This will write to a Logs/lua-net.log file
|
||||
|
||||
Or, to save everything lua-network-related:
|
||||
log.set_output('lua-net', 'LuaNET', log.TRACE + log.ALL, log.MESSAGE + log.TIME + log.LEVEL)
|
||||
|
||||
To close the log file, you must use
|
||||
log.set_output('lua-net', '', 0, 0)
|
||||
|
||||
log.* API is available in the 'Saved Games\DCS\Config\autoexec.cfg' file as well so you can control log output in you local machine.
|
||||
|
||||
|
||||
|
||||
Network specific API, available through the table 'net.'
|
||||
----------------------------------------------------------------
|
||||
|
||||
net.log(msg) -- equivalent to log.write('LuaNET', log.INFO, msg)
|
||||
net.trace(msg) -- equivalent to log.write('LuaNET', log.TRACE, msg)
|
||||
|
||||
What is the difference: log() always writes to dcs.log, but may lose messages if the output rate is too high.
|
||||
trace() output never appears in the dcs.log file, it must be explicitly directed to a log file.
|
||||
It never loses messages when there's an active output, but it may block if output rate is higher than writing to the log file.
|
||||
To control logger output you can use $WRITE_DIR/Config/autoexec.cfg file, or call this from your network script
|
||||
(log.* API, see above)
|
||||
|
||||
|
||||
net.dostring_in(state, string) -> string
|
||||
Executes a lua-string in a given internal lua-state and returns a string result
|
||||
Valid state names are:
|
||||
'config': the state in which $INSTALL_DIR/Config/main.cfg is executed, as well as $WRITE_DIR/Config/autoexec.cfg
|
||||
used for configuration settings
|
||||
'mission': holds current mission
|
||||
'export': runs $WRITE_DIR/Scripts/Export.lua and the relevant export API
|
||||
|
||||
net.send_chat(string message, bool all)
|
||||
Send chat message. If not all, then send to my coalition (side) only.
|
||||
|
||||
net.send_chat_to(string message, playerID to)
|
||||
Send direct chat message to a player
|
||||
Server-side only:
|
||||
net.send_chat_to(string message, playerID to[, playerID from])
|
||||
|
||||
net.recv_chat(message[, int from=0])
|
||||
Receive chat message locally[, pretending it was sent by another player].
|
||||
from = 0 means from the system
|
||||
|
||||
net.load_mission(miz_filename)
|
||||
Loads a specified mission, temporarily overriding the server mission list.
|
||||
SERVER ONLY
|
||||
|
||||
net.load_next_mission() -> bool
|
||||
Load the next mission from the server mission list. Returns false if list end is reached
|
||||
SERVER ONLY
|
||||
|
||||
net.get_player_list() -> array of playerID
|
||||
Returns the list of currently connected players
|
||||
|
||||
net.get_my_player_id() -> playerID
|
||||
Returns the playerID of the local player. Currently always 1 for the server.
|
||||
|
||||
net.get_server_id() -> playerID
|
||||
Returns playerID of the server. Currently, always 1.
|
||||
|
||||
net.get_player_info(playerID) -> table
|
||||
Returns a table of all player attributes or nil if playerID is invalid
|
||||
|
||||
net.get_player_info(playerID, attrName) -> value
|
||||
Returns a value of a given attribute for the playerID.
|
||||
|
||||
Currently defined attributes are:
|
||||
'id': playerID
|
||||
'name': player name
|
||||
'side': 0 - spectators, 1 - red, 2 - blue
|
||||
'slot': slotID of the player or ''
|
||||
'ping': ping of the player in ms
|
||||
'ipaddr': IP address of the player, SERVER ONLY
|
||||
'ucid': Unique Client Identifier, SERVER ONLY
|
||||
|
||||
net.kick(id, message)
|
||||
Kick a player.
|
||||
|
||||
net.get_stat(playerID, statID) -> integer
|
||||
Get statistics for player. statIDs are:
|
||||
net.PS_PING (0) - ping (in ms)
|
||||
net.PS_CRASH (1) - number of crashes
|
||||
net.PS_CAR (2) - number of destroyed vehicles
|
||||
net.PS_PLANE (3) - ... planes/helicopters
|
||||
net.PS_SHIP (4) - ... ships
|
||||
net.PS_SCORE (5) - total score
|
||||
net.PS_LAND (6) - number of landings
|
||||
net.PS_EJECT (7) - of ejects
|
||||
|
||||
|
||||
net.get_name(playerID) -> string
|
||||
The same as net.get_player_info(playerID, 'name')
|
||||
FIXME: implement in ServMan_compat.lua ?
|
||||
|
||||
net.get_slot(playerID) -> sideID, slotID
|
||||
The same as:
|
||||
net.get_player_info(playerID, 'side'), net.get_player_info(playerID, 'slot')
|
||||
FIXME: implement in ServMan_compat.lua ?
|
||||
|
||||
|
||||
|
||||
net.set_slot(sideID, slotID)
|
||||
Try to set the local player's slot. Empty slotID ('') puts the player into spectators.
|
||||
|
||||
net.force_player_slot(playerID, sideID, slotID) -> boolean
|
||||
Forces a player to occupy a set slot. Slot '' means no slot (moves player to spectators)
|
||||
SideID: 0 - spectators, 1 - red, 2 - blue
|
||||
|
||||
net.set_name(playerID, name) -- OBSOLETE, works only locally
|
||||
|
||||
|
||||
net.lua2json(value) -> string
|
||||
Convert a Lua value to JSON string
|
||||
|
||||
net.json2lua(json_string) -> value
|
||||
Convert JSON string to a Lua value
|
||||
|
||||
|
||||
LuaExport API 'Export.Lo*'
|
||||
----------------------------------------------------------------
|
||||
See Scripts/Export.lua for the documentation. Note that all export
|
||||
API functions are available here in the Export. namespace, not the global one.
|
||||
In multiplayer the availability of the API on clients depends on the server setting.
|
||||
|
||||
The calls to check export capabilities:
|
||||
Export.LoIsObjectExportAllowed() -- returns the value of server.advanced.allow_object_export
|
||||
Export.LoIsSensorExportAllowed() -- returns the value of server.advanced.allow_sensor_export
|
||||
Export.LoIsOwnshipExportAllowed() -- returns the value of server.advanced.allow_ownship_export
|
||||
|
||||
These calls are only available on clients when LoIsObjectExportAllowed() is true:
|
||||
Export.LoGetObjectById
|
||||
Export.LoGetWorldObjects
|
||||
|
||||
These calls are only available on clients when LoIsSensorExportAllowed() is true:
|
||||
Export.LoGetTWSInfo
|
||||
Export.LoGetTargetInformation
|
||||
Export.LoGetLockedTargetInformation
|
||||
Export.LoGetF15_TWS_Contacts
|
||||
Export.LoGetSightingSystemInfo
|
||||
Export.LoGetWingTargets
|
||||
|
||||
These calls are only available on clients when LoIsOwnshipExportAllowed() is true:
|
||||
Export.LoGetPlayerPlaneId
|
||||
Export.LoGetIndicatedAirSpeed
|
||||
Export.LoGetAngleOfAttack
|
||||
Export.LoGetAngleOfSideSlip
|
||||
Export.LoGetAccelerationUnits
|
||||
Export.LoGetVerticalVelocity
|
||||
Export.LoGetADIPitchBankYaw
|
||||
Export.LoGetTrueAirSpeed
|
||||
Export.LoGetAltitudeAboveSeaLevel
|
||||
Export.LoGetAltitudeAboveGroundLevel
|
||||
Export.LoGetMachNumber
|
||||
Export.LoGetRadarAltimeter
|
||||
Export.LoGetMagneticYaw
|
||||
Export.LoGetGlideDeviation
|
||||
Export.LoGetSideDeviation
|
||||
Export.LoGetSlipBallPosition
|
||||
Export.LoGetBasicAtmospherePressure
|
||||
Export.LoGetControlPanel_HSI
|
||||
Export.LoGetEngineInfo
|
||||
Export.LoGetSelfData
|
||||
Export.LoGetCameraPosition
|
||||
Export.LoSetCameraPosition
|
||||
Export.LoSetCommand
|
||||
Export.LoGetMCPState
|
||||
Export.LoGetRoute
|
||||
Export.LoGetNavigationInfo
|
||||
Export.LoGetPayloadInfo
|
||||
Export.LoGetWingInfo
|
||||
Export.LoGetMechInfo
|
||||
Export.LoGetRadioBeaconsStatus
|
||||
Export.LoGetVectorVelocity
|
||||
Export.LoGetVectorWindVelocity
|
||||
Export.LoGetSnares
|
||||
Export.LoGetAngularVelocity
|
||||
Export.LoGetHeightWithObjects
|
||||
Export.LoGetFMData
|
||||
|
||||
These functions are always available:
|
||||
Export.LoGetPilotName
|
||||
Export.LoGetAltitude
|
||||
Export.LoGetNameByType
|
||||
Export.LoGeoCoordinatesToLoCoordinates
|
||||
Export.LoCoordinatesToGeoCoordinates
|
||||
Export.LoGetVersionInfo
|
||||
Export.LoGetWindAtPoint
|
||||
Export.LoGetModelTime
|
||||
Export.LoGetMissionStartTime
|
||||
|
||||
These are not available in the *GameGUI state:
|
||||
-- Export.LoSetSharedTexture
|
||||
-- Export.LoRemoveSharedTexture
|
||||
-- Export.LoUpdateSharedTexture
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------
|
||||
--- The Callbacks.
|
||||
-------------------------------------------------------------------------------------------
|
||||
|
||||
function onMissionLoadBegin()
|
||||
end
|
||||
|
||||
function onMissionLoadProgress(progress, message)
|
||||
end
|
||||
|
||||
function onMissionLoadEnd()
|
||||
end
|
||||
|
||||
|
||||
function onSimulationStart()
|
||||
end
|
||||
|
||||
function onSimulationStop()
|
||||
end
|
||||
|
||||
function onSimulationFrame()
|
||||
end
|
||||
|
||||
function onSimulationPause()
|
||||
end
|
||||
|
||||
function onSimulationResume()
|
||||
end
|
||||
|
||||
|
||||
function onGameEvent(eventName,arg1,arg2,arg3,arg4)
|
||||
--"friendly_fire", playerID, weaponName, victimPlayerID
|
||||
--"mission_end", winner, msg
|
||||
--"kill", killerPlayerID, killerUnitType, killerSide, victimPlayerID, victimUnitType, victimSide, weaponName
|
||||
--"self_kill", playerID
|
||||
--"change_slot", playerID, slotID, prevSide
|
||||
--"connect", playerID, name
|
||||
--"disconnect", playerID, name, playerSide, reason_code
|
||||
--"crash", playerID, unit_missionID
|
||||
--"eject", playerID, unit_missionID
|
||||
--"takeoff", playerID, unit_missionID, airdromeName
|
||||
--"landing", playerID, unit_missionID, airdromeName
|
||||
--"pilot_death", playerID, unit_missionID
|
||||
end
|
||||
|
||||
function onNetConnect(localPlayerID)
|
||||
end
|
||||
|
||||
function onNetMissionChanged(newMissionName)
|
||||
end
|
||||
|
||||
function onNetDisconnect(reason_msg, err_code)
|
||||
end
|
||||
|
||||
-- disconnect reason codes:
|
||||
net.ERR_INVALID_ADDRESS
|
||||
net.ERR_CONNECT_FAILED
|
||||
net.ERR_WRONG_VERSION
|
||||
net.ERR_PROTOCOL_ERROR
|
||||
net.ERR_TAINTED_CLIENT
|
||||
net.ERR_INVALID_PASSWORD
|
||||
net.ERR_BANNED
|
||||
net.ERR_BAD_CALLSIGN
|
||||
|
||||
net.ERR_TIMEOUT
|
||||
net.ERR_KICKED
|
||||
|
||||
|
||||
function onPlayerConnect(id)
|
||||
end
|
||||
|
||||
function onPlayerDisconnect(id, err_code)
|
||||
-- this is never called for local playerID
|
||||
end
|
||||
|
||||
function onPlayerStart(id)
|
||||
-- a player entered the simulation
|
||||
-- this is never called for local playerID
|
||||
end
|
||||
|
||||
function onPlayerStop(id)
|
||||
-- a player left the simulation (happens right before a disconnect, if player exited by desire)
|
||||
-- this is never called for local playerID
|
||||
end
|
||||
|
||||
function onPlayerChangeSlot(id)
|
||||
-- a player successfully changed the slot
|
||||
-- this will also come as onGameEvent('change_slot', playerID, slotID),
|
||||
-- if allowed by server.advanced.event_Connect setting
|
||||
end
|
||||
|
||||
|
||||
--- These 3 functions are different from the rest:
|
||||
--- 1. they are called directly from the network code, so try to make them as fast as possible
|
||||
--- 2. they return a result
|
||||
-- The code shows the default implementations.
|
||||
|
||||
function onPlayerTryConnect(addr, name, ucid, playerID) --> true | false, "disconnect reason"
|
||||
return true
|
||||
end
|
||||
|
||||
function onPlayerTrySendChat(playerID, msg, all) -- -> filteredMessage | "" - empty string drops the message
|
||||
return msg
|
||||
end
|
||||
|
||||
function onPlayerTryChangeSlot(playerID, side, slotID) -- -> true | false
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- GUI callbacks
|
||||
function onChatMessage(message, from)
|
||||
-- this one may be useful for chat archiving
|
||||
end
|
||||
|
||||
function onShowRadioMenu(a_h)
|
||||
end
|
||||
|
||||
function onShowPool()
|
||||
end
|
||||
|
||||
function onShowGameMenu()
|
||||
end
|
||||
|
||||
function onShowBriefing()
|
||||
end
|
||||
|
||||
function onShowChatAll()
|
||||
end
|
||||
|
||||
function onShowChatTeam()
|
||||
end
|
||||
|
||||
function onShowChatRead()
|
||||
end
|
||||
|
||||
function onShowMessage(a_text, a_duration)
|
||||
end
|
||||
|
||||
function onTriggerMessage(message, duration, clearView)
|
||||
end
|
||||
|
||||
function onRadioMessage(message, duration)
|
||||
end
|
||||
|
||||
function onRadioCommand(command_message)
|
||||
end
|
||||
|
||||
===================================================================================================
|
||||
|
||||
Happy hacking!
|
||||
|
||||
Sincerely,
|
||||
dsb at eagle dot ru
|
||||
190
Moose Development/Documentation/Scoring/Scoring Sample.csv
Normal file
190
Moose Development/Documentation/Scoring/Scoring Sample.csv
Normal file
@ -0,0 +1,190 @@
|
||||
Game;Date;Time;Source Player Name;Target Player Name;Event;Source Coalition;Source Category;Source Type;Source Unit;Target Coalition;Target Category;Target Type;Target Name;Nr;Score
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:19:37;PL8;;DESTROY_SCORE;Red;Plane;Su-25T;Pilot #058;Blue;Vehicle;Hawk cwar;Unit #114;1;5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:39:25;PL12;;HIT_SCORE;Red;Plane;AJS37;Pilot #110;Blue;Vehicle;Soldier M249;Unit #453;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:14:30;PL1;PL2;DESTROY_PENALTY;Blue;Plane;F-15C;Pilot #069;Blue;Plane;F-15C;Pilot #071;1;-15
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:40:14;PL3;PL4;HIT_SCORE;Blue;Plane;F-15C;Pilot #018;Red;Plane;AJS37;Pilot #112;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:25:22;PL5;;HIT_PENALTY;Red;Helicopter;Ka-50;MEDEVAC RED #13;Red;Vehicle;Infantry AK;Wounded Pilot #1143;1;-10
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:18:34;PL6;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;M-2 Bradley;Unit #007;1;3
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:21:35;PL7;PL2;HIT_SCORE;Red;Plane;M-2000C;Pilot #037;Blue;Plane;F-15C;Pilot #071;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:46:42;PL8;;HIT_SCORE;Red;Plane;Su-25T;Pilot #058;Blue;Vehicle;Strela-1 9P31;Unit #429;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:30:09;PL9;PL10;DESTROY_SCORE;Blue;Plane;F-15C;Pilot #017;Red;Plane;Su-27;Pilot #039;1;5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:22:46;PL11;;HIT_SCORE;Red;;;;;Scenery;Infantry AK;Wounded Pilot #1129;1;0
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:11:46;PL6;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;MLRS;Unit #011;1;2
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:53:10;PL11;;HIT_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;MLRS;Unit #510;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:10:54;PL12;;HIT_SCORE;Red;;;;;Scenery;UKRYTIE;280468;1;0
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:09:28;PL13;PL9;HIT_SCORE;Red;Plane;Su-27;Pilot #086;Blue;Plane;F-15C;Pilot #017;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:52:59;PL13;PL14;HIT_SCORE;Red;Plane;Su-27;Pilot #086;Blue;Plane;M-2000C;Pilot #030;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:39:27;PL12;;DESTROY_SCORE;Red;Plane;AJS37;Pilot #110;Blue;Vehicle;Soldier stinger;Unit #454;1;4
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:53:21;PL11;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;MLRS;Unit #510;1;2
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:51:37;PL15;PL9;HIT_SCORE;Red;Plane;Su-27;Pilot #041;Blue;Plane;F-15C;Pilot #017;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:22:05;PL16;PL2;DESTROY_SCORE;Red;Plane;Su-27;Pilot #044;Blue;Plane;F-15C;Pilot #071;1;3
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:22:28;PL6;PL3;HIT_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Plane;F-15C;Pilot #018;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:38:21;PL17;PL18;HIT_SCORE;Blue;Plane;M-2000C;Pilot #029;Red;Plane;Su-25T;Pilot #059;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:14:34;PL7;PL17;DESTROY_SCORE;Red;Plane;M-2000C;Pilot #037;Blue;Plane;M-2000C;Pilot #029;1;2,5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:21:22;PL19;PL20;HIT_SCORE;Blue;Plane;F-15C;Pilot #024;Red;Plane;A-10C;Pilot #057;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:24:21;PL5;PL21;HIT_PENALTY;Red;Helicopter;Mi-8MT;MEDEVAC RED #10;Red;Helicopter;SA342Mistral;helicargo4;1;-10
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:21:36;PL6;PL19;HIT_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Plane;F-15C;Pilot #024;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:21:51;PL6;PL19;DESTROY_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Plane;F-15C;Pilot #024;1;6
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:21:14;PL19;PL20;HIT_SCORE;Blue;Plane;F-15C;Pilot #024;Red;Plane;A-10C;Pilot #057;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:13:59;PL2;;HIT_SCORE;Blue;;;;;Scenery;F-15C;Pilot #069;1;0
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:48:55;PL11;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;M-1 Abrams;Unit #514;1;3
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:22:48;PL6;PL3;DESTROY_SCORE;Red;;;;Blue;Plane;F-15C;Pilot #018;1;6
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:18:19;PL6;;HIT_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;M-2 Bradley;Unit #007;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:25:19;PL5;;HIT_PENALTY;Red;Helicopter;Ka-50;MEDEVAC RED #13;Red;Vehicle;Infantry AK;Wounded Pilot #1143;1;-10
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:22:12;PL9;PL20;HIT_SCORE;Blue;Plane;F-15C;Pilot #017;Red;Plane;A-10C;Pilot #057;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:51:35;PL15;PL9;HIT_SCORE;Red;Plane;Su-27;Pilot #041;Blue;Plane;F-15C;Pilot #017;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:19:18;PL6;;HIT_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;M-113;Unit #006;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:54:22;PL1;PL13;HIT_SCORE;Blue;Plane;F-15C;Pilot #069;Red;Plane;Su-27;Pilot #086;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:37:18;PL11;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;Osa 9A33 ln;Unit #218;1;5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:21:14;PL16;PL1;DESTROY_SCORE;Red;Plane;Su-27;Pilot #044;Blue;Plane;F-15C;Pilot #069;1;5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:55:00;PL11;;HIT_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;M1128 Stryker MGS;Unit #010;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:22:13;PL9;PL20;HIT_SCORE;Blue;Plane;F-15C;Pilot #017;Red;Plane;A-10C;Pilot #057;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:19:55;PL2;PL18;HIT_SCORE;Blue;Plane;F-15C;Pilot #071;Red;Plane;Su-25T;Pilot #059;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:14:34;PL16;PL17;DESTROY_SCORE;Red;Plane;Su-27;Pilot #044;Blue;Plane;M-2000C;Pilot #029;1;2,5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:46:42;PL11;;HIT_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;Soldier M4;Unit #470;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:11:46;PL6;;HIT_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;MLRS;Unit #011;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:21:20;PL19;PL20;HIT_SCORE;Blue;Plane;F-15C;Pilot #024;Red;Plane;A-10C;Pilot #057;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:10:01;PL6;PL3;HIT_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Plane;F-15C;Pilot #018;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:38:22;PL18;PL17;DESTROY_SCORE;Red;Plane;Su-25T;Pilot #059;Blue;Plane;M-2000C;Pilot #029;1;5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:46:25;PL6;;HIT_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;M-2 Bradley;Unit #007;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:19:53;PL24;PL18;HIT_SCORE;Blue;Plane;F-15C;Pilot #033;Red;Plane;Su-25T;Pilot #059;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:51:55;PL15;PL9;DESTROY_SCORE;Red;Plane;Su-27;Pilot #041;Blue;Plane;F-15C;Pilot #017;1;5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:48:11;PL2;PL10;DESTROY_SCORE;Blue;Plane;F-15C;Pilot #071;Red;Plane;Su-27;Pilot #039;1;5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:39:25;PL12;;DESTROY_SCORE;Red;Plane;AJS37;Pilot #110;Blue;Vehicle;Soldier M249;X33;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:13:13;PL6;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;M-1 Abrams;Unit #009;1;3
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:17:07;PL6;;HIT_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;M-2 Bradley;Unit #007;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:18:29;PL19;PL18;HIT_SCORE;Blue;Plane;F-15C;Pilot #024;Red;Plane;Su-25T;Pilot #059;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:22:05;PL16;PL2;HIT_SCORE;Red;Plane;Su-27;Pilot #044;Blue;Plane;F-15C;Pilot #071;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:22:26;PL19;PL20;DESTROY_SCORE;Blue;Plane;F-15C;Pilot #024;Red;Plane;A-10C;Pilot #057;1;2
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:55:13;PL6;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;M1128 Stryker MGS;Unit #010;1;1,5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:55:54;PL11;;HIT_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;AAV7;Unit #005;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:21:26;PL6;PL19;HIT_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Plane;F-15C;Pilot #024;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:09:27;PL3;PL13;HIT_SCORE;Blue;Plane;F-15C;Pilot #018;Red;Plane;Su-27;Pilot #086;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:57:05;PL15;PL1;DESTROY_SCORE;Red;Plane;Su-27;Pilot #041;Blue;Plane;F-15C;Pilot #069;1;5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:53:23;PL11;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;MLRS;Unit #511;1;2
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:47:44;PL16;PL26;HIT_SCORE;Red;Plane;Su-27;Pilot #044;Blue;Plane;F-15C;Pilot #068;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:40:04;PL19;PL12;HIT_SCORE;Blue;Plane;F-15C;Pilot #024;Red;Plane;AJS37;Pilot #110;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:41:24;PL20;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #057;Blue;Vehicle;Soldier M4;X36;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:07:56;PL6;;HIT_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;M-109;Unit #512;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:41:24;PL20;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #057;Blue;Vehicle;Soldier M249;Unit #310;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:39:25;PL12;;HIT_SCORE;Red;Plane;AJS37;Pilot #110;Blue;Vehicle;Soldier M4;Unit #450;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:46:45;PL11;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;Soldier M4;Unit #469;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:54:26;PL1;PL13;DESTROY_SCORE;Blue;Plane;F-15C;Pilot #069;Red;Plane;Su-27;Pilot #086;1;5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:13:29;PL4;;DESTROY_SCORE;Red;Plane;AJS37;Pilot #112;Blue;Structure;Warehouse;AMMO SENAKI;1;50
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:56:32;PL11;;HIT_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;M-109;Unit #014;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:55:49;PL16;PL2;HIT_SCORE;Red;Plane;Su-27;Pilot #044;Blue;Plane;F-15C;Pilot #071;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:17:00;PL6;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;Vulcan;Unit #004;1;4
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:47:55;PL10;PL19;HIT_SCORE;Red;Plane;Su-27;Pilot #039;Blue;Plane;F-15C;Pilot #024;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:29:20;PL9;PL10;HIT_SCORE;Blue;Plane;F-15C;Pilot #017;Red;Plane;Su-27;Pilot #039;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:46:45;PL11;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;Soldier M4;Unit #470;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:54:58;PL11;;HIT_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;AAV7;Unit #005;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:48:15;PL11;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;Vulcan;Unit #503;1;4
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:22:05;PL7;PL2;DESTROY_SCORE;Red;Plane;M-2000C;Pilot #037;Blue;Plane;F-15C;Pilot #071;1;3
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:19:55;PL24;PL18;HIT_SCORE;Blue;Plane;F-15C;Pilot #033;Red;Plane;Su-25T;Pilot #059;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:45:32;PL7;;DESTROY_SCORE;Red;Plane;M-2000C;Pilot #037;Blue;Structure;Windsock;logistic10;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:41:22;PL20;;HIT_SCORE;Red;Plane;A-10C;Pilot #057;Blue;Vehicle;Soldier M4;Unit #308;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:47:30;;;CAPTURE KOBULETI;Red;;;;NA;NA;NA;NA;1;50
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:55:13;PL11;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;M1128 Stryker MGS;Unit #010;1;1,5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:20:05;PL17;PL18;DESTROY_SCORE;Blue;;;;Red;Plane;Su-25T;Pilot #059;1;1,67
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:22:42;PL23;PL22;HIT_SCORE;Red;Plane;M-2000C;Pilot #038;Blue;Plane;F-15C;Pilot #070;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:46:42;PL11;;HIT_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;Soldier M4;Unit #469;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:37:08;PL18;;DESTROY_SCORE;Red;Plane;Su-25T;Pilot #059;Blue;Vehicle;Hawk sr;Unit #118;1;5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:40:12;PL19;PL12;DESTROY_SCORE;Blue;Plane;F-15C;Pilot #024;Red;Plane;AJS37;Pilot #110;1;4
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:50:48;PL20;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #057;Blue;Vehicle;Vulcan;Unit #095;1;4
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:45:31;PL7;;HIT_SCORE;Red;Plane;M-2000C;Pilot #037;Blue;Vehicle;outpost;outpost1;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:25:15;PL5;;HIT_PENALTY;Red;Helicopter;Ka-50;MEDEVAC RED #13;Red;Vehicle;Infantry AK;Wounded Pilot #1143;1;-10
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:13:29;PL4;;HIT_SCORE;Red;;;;;Scenery;Warehouse;AMMO SENAKI;1;0
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:20:05;PL2;PL18;DESTROY_SCORE;Blue;Plane;F-15C;Pilot #071;Red;Plane;Su-25T;Pilot #059;1;1,67
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:45:32;PL7;;HIT_SCORE;Red;;;;;Scenery;Windsock;logistic10;1;0
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:39:25;PL12;;HIT_SCORE;Red;Plane;AJS37;Pilot #110;Blue;Vehicle;Soldier M249;X33;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:52:37;PL11;;HIT_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;Vulcan;Unit #508;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:47:58;PL2;PL10;HIT_SCORE;Blue;Plane;F-15C;Pilot #071;Red;Plane;Su-27;Pilot #039;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:56:35;PL15;PL1;HIT_SCORE;Red;Plane;Su-27;Pilot #041;Blue;Plane;F-15C;Pilot #069;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:41:25;PL20;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #057;Blue;Vehicle;Soldier M4;Unit #308;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:32:57;PL9;PL25;DESTROY_SCORE;Blue;Plane;F-15C;Pilot #017;Red;Plane;Su-27;Pilot #043;1;5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:18:23;PL6;;HIT_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;M-2 Bradley;Unit #007;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:21:14;PL16;PL1;HIT_SCORE;Red;Plane;Su-27;Pilot #044;Blue;Plane;F-15C;Pilot #069;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:22:47;PL9;PL6;DESTROY_SCORE;Blue;Plane;F-15C;Pilot #017;Red;Plane;A-10C;Pilot #056;1;2
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:23:16;PL5;PL5;HIT_PENALTY;Red;Helicopter;Mi-8MT;MEDEVAC RED #10;Red;Helicopter;Mi-8MT;MEDEVAC RED #10;1;-10
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:21:03;PL19;PL6;HIT_SCORE;Blue;Plane;F-15C;Pilot #024;Red;Plane;A-10C;Pilot #056;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:46:24;PL6;;HIT_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;M-2 Bradley;Unit #007;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:21:17;PL11;PL27;HIT_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Plane;F-15C;Pilot #025;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:18:22;PL6;;HIT_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;M-2 Bradley;Unit #007;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:52:37;PL11;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;Vulcan;Unit #508;1;4
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:19:30;PL6;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;M-113;Unit #006;1;2
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:41:22;PL20;;HIT_SCORE;Red;Plane;A-10C;Pilot #057;Blue;Vehicle;Soldier M249;Unit #310;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:22:46;PL9;PL11;DESTROY_SCORE;Blue;Plane;F-15C;Pilot #017;Red;Plane;A-10C;Pilot #054;1;4
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:36:57;PL18;;HIT_SCORE;Red;Plane;Su-25T;Pilot #059;Blue;Vehicle;Hawk sr;Unit #118;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:41:22;PL20;;HIT_SCORE;Red;Plane;A-10C;Pilot #057;Blue;Vehicle;Soldier M249;Unit #311;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:22:26;PL11;;HIT_SCORE;Red;;;;;Scenery;KAZARMA2;270090725;1;0
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:37:07;PL11;;HIT_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;Osa 9A33 ln;Unit #218;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:18:46;;;CAPTURE KUTAISI;Red;;;;NA;NA;NA;NA;1;50
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:33:20;PL9;PL15;HIT_SCORE;Blue;Plane;F-15C;Pilot #017;Red;Plane;Su-27;Pilot #041;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:10:10;PL6;PL3;DESTROY_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Plane;F-15C;Pilot #018;1;6
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:13:23;PL6;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;M-2 Bradley;Unit #008;1;3
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:56:08;PL11;PL26;DESTROY_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Plane;F-15C;Pilot #025;1;6
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:14:23;PL7;PL17;HIT_SCORE;Red;Plane;M-2000C;Pilot #037;Blue;Plane;M-2000C;Pilot #029;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:22:47;PL19;PL6;DESTROY_SCORE;Blue;Plane;F-15C;Pilot #024;Red;Plane;A-10C;Pilot #056;1;2
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:50:37;PL20;;HIT_SCORE;Red;Plane;A-10C;Pilot #057;Blue;Vehicle;Vulcan;Unit #015;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:22:26;PL9;PL20;DESTROY_SCORE;Blue;Plane;F-15C;Pilot #017;Red;Plane;A-10C;Pilot #057;1;2
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:57:05;PL2;PL1;DESTROY_PENALTY;Blue;Plane;F-15C;Pilot #071;Blue;Plane;F-15C;Pilot #069;1;-15
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:56:44;PL11;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;M-109;Unit #014;1;2
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:22:36;PL9;PL6;HIT_SCORE;Blue;Plane;F-15C;Pilot #017;Red;Plane;A-10C;Pilot #056;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:53:12;PL11;;HIT_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;MLRS;Unit #511;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:47:56;PL20;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #057;Blue;Vehicle;M-109;Unit #013;1;2
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:22:37;PL9;PL6;HIT_SCORE;Blue;Plane;F-15C;Pilot #017;Red;Plane;A-10C;Pilot #056;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:48:04;PL11;;HIT_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;M-113;Unit #505;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:13:23;PL6;;HIT_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;M-2 Bradley;Unit #008;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:46:42;PL11;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;Vulcan;Unit #515;1;4
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:38:22;PL18;;HIT_SCORE;Red;;;;;Scenery;M-2000C;Pilot #029;1;0
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:09:28;PL13;PL9;DESTROY_SCORE;Red;Plane;Su-27;Pilot #086;Blue;Plane;F-15C;Pilot #017;1;5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:13:29;PL4;;DESTROY_SCORE;Red;Plane;AJS37;Pilot #112;Blue;Structure;Warehouse;AMMO SENAKI;1;50
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:48:49;PL28;;HIT_SCORE;;;;;;Scenery;Ural-4320T;Unit #150;1;0
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:40:22;PL3;PL4;DESTROY_SCORE;Blue;Plane;F-15C;Pilot #018;Red;Plane;AJS37;Pilot #112;1;4
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:39:25;PL12;;DESTROY_SCORE;Red;Plane;AJS37;Pilot #110;Blue;Vehicle;Soldier M4;Unit #450;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:14:24;PL16;PL17;HIT_SCORE;Red;Plane;Su-27;Pilot #044;Blue;Plane;M-2000C;Pilot #029;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:48:04;PL11;;HIT_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;Vulcan;Unit #503;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:00:00;;;CAPTURE GUDAUTA;Blue;;;;NA;NA;NA;NA;1;50
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:39:25;PL12;;HIT_SCORE;Red;Plane;AJS37;Pilot #110;Blue;Vehicle;Soldier stinger;Unit #454;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:13:13;PL6;;HIT_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;M-1 Abrams;Unit #009;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:46:42;PL8;;DESTROY_SCORE;Red;Plane;Su-25T;Pilot #058;Blue;Vehicle;Strela-1 9P31;Unit #429;1;4
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:56:06;PL11;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;AAV7;Unit #005;1;2
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:48:55;PL11;;HIT_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;M-1 Abrams;Unit #514;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:53:06;PL20;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #057;Blue;Vehicle;MLRS;Unit #012;1;2
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:13:59;PL1;;HIT_SCORE;Blue;;;;;Scenery;F-15C;Pilot #071;1;0
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:25:17;PL5;;HIT_PENALTY;Red;Helicopter;Ka-50;MEDEVAC RED #13;Red;Vehicle;Infantry AK;Wounded Pilot #1143;1;-10
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:39:25;PL12;;HIT_SCORE;Red;Plane;AJS37;Pilot #110;Blue;Vehicle;Soldier M4;Unit #451;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:25:20;PL5;;HIT_PENALTY;Red;Helicopter;Ka-50;MEDEVAC RED #13;Red;Vehicle;Infantry AK;Wounded Pilot #1143;1;-10
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:53:14;PL13;PL14;DESTROY_SCORE;Red;Plane;Su-27;Pilot #086;Blue;Plane;M-2000C;Pilot #030;1;5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:50:48;PL20;;HIT_SCORE;Red;Plane;A-10C;Pilot #057;Blue;Vehicle;Vulcan;Unit #095;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:45:31;PL7;;DESTROY_SCORE;Red;Plane;M-2000C;Pilot #037;Blue;Vehicle;outpost;outpost1;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:39:25;PL12;;HIT_SCORE;Red;Plane;AJS37;Pilot #110;Blue;Vehicle;outpost;outpost2;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:19:37;PL8;;HIT_SCORE;Red;Plane;Su-25T;Pilot #058;Blue;Vehicle;Hawk cwar;Unit #114;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:18:36;PL19;PL18;DESTROY_SCORE;Blue;Plane;F-15C;Pilot #024;Red;Plane;Su-25T;Pilot #059;1;4
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:07:56;PL6;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;M-109;Unit #512;1;2
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:39:25;;;SEALS 4;Blue;;;;NA;NA;NA;NA;1;50
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:39:19;PL12;;HIT_SCORE;Red;;;;;Scenery;BLK_LIGHT_POLE;269633408;1;0
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:41:24;PL20;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #057;Blue;Vehicle;Soldier M249;Unit #311;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:20:05;PL24;PL18;DESTROY_SCORE;Blue;Plane;F-15C;Pilot #033;Red;Plane;Su-25T;Pilot #059;1;1,67
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:46:42;PL11;;HIT_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;Vulcan;Unit #515;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:09:37;PL3;PL13;DESTROY_SCORE;Blue;Plane;F-15C;Pilot #018;Red;Plane;Su-27;Pilot #086;1;5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:48:04;PL11;;HIT_SCORE;Red;;;;;Scenery;BLK_LIGHT_POLE;269963637;1;0
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:41:24;;;SEALS 1;Blue;;;;NA;NA;NA;NA;1;50
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:11:14;PL4;;HIT_SCORE;Red;;;;;Scenery;UKRYTIE;280476;1;0
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:53:06;PL20;;HIT_SCORE;Red;Plane;A-10C;Pilot #057;Blue;Vehicle;MLRS;Unit #012;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:32:54;PL9;PL25;HIT_SCORE;Blue;Plane;F-15C;Pilot #017;Red;Plane;Su-27;Pilot #043;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:55:01;PL11;;HIT_SCORE;Red;Plane;A-10C;Pilot #054;Blue;Vehicle;M1128 Stryker MGS;Unit #010;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:39:25;PL12;;DESTROY_SCORE;Red;Plane;AJS37;Pilot #110;Blue;Vehicle;Soldier M4;Unit #451;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:47:56;PL20;;HIT_SCORE;Red;Plane;A-10C;Pilot #057;Blue;Vehicle;M-109;Unit #013;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:18:25;PL19;PL18;HIT_SCORE;Blue;Plane;F-15C;Pilot #024;Red;Plane;Su-25T;Pilot #059;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:41:22;PL20;;HIT_SCORE;Red;Plane;A-10C;Pilot #057;Blue;Vehicle;Soldier M4;X36;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:18:24;PL19;PL18;HIT_SCORE;Blue;Plane;F-15C;Pilot #024;Red;Plane;Su-25T;Pilot #059;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:21:17;PL19;PL20;HIT_SCORE;Blue;Plane;F-15C;Pilot #024;Red;Plane;A-10C;Pilot #057;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:23:03;PL23;PL22;DESTROY_SCORE;Red;Plane;M-2000C;Pilot #038;Blue;Plane;F-15C;Pilot #070;1;6
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:17:00;PL6;;HIT_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;Vulcan;Unit #004;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:45:40;PL6;;HIT_SCORE;Red;Plane;A-10C;Pilot #056;Blue;Vehicle;M1128 Stryker MGS;Unit #010;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:09:18;PL13;;HIT_SCORE;Red;;;;;Scenery;KORPUS_B1;269962308;1;0
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:33:24;PL9;PL15;DESTROY_SCORE;Blue;Plane;F-15C;Pilot #017;Red;Plane;Su-27;Pilot #041;1;5
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;1:39:27;PL12;;DESTROY_SCORE;Red;Plane;AJS37;Pilot #110;Blue;Vehicle;Soldier M249;Unit #453;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:21:53;PL9;PL11;HIT_SCORE;Blue;Plane;F-15C;Pilot #017;Red;Plane;A-10C;Pilot #054;1;1
|
||||
TAW_CSARz_PALACE_3.6136;7/05/2017;12:50:37;PL20;;DESTROY_SCORE;Red;Plane;A-10C;Pilot #057;Blue;Vehicle;Vulcan;Unit #015;1;4
|
||||
|
BIN
Moose Development/Documentation/Scoring/Scoring Sample.xlsx
Normal file
BIN
Moose Development/Documentation/Scoring/Scoring Sample.xlsx
Normal file
Binary file not shown.
@ -3,7 +3,7 @@
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.ui.externaltools.launchGroup"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/luarocks/lua5.1.exe}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/Generate_Moose.bat}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value=""Moose_Create.lua" "D" "${current_date}" "${workspace_loc:/Moose_Framework//Moose Development/Moose}" "${workspace_loc:/Moose_Framework/Moose Mission Setup}""/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Moose Mission Setup}"/>
|
||||
</launchConfiguration>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.ui.externaltools.launchGroup"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/luarocks/lua5.1.exe}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/Utils/Generate_Moose.bat}"/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value=""Moose_Create.lua" "S" "${current_date}" "${workspace_loc:/Moose_Framework//Moose Development/Moose}" "${workspace_loc:/Moose_Framework/Moose Mission Setup}""/>
|
||||
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework/Moose Mission Setup}"/>
|
||||
</launchConfiguration>
|
||||
|
||||
@ -70,8 +70,9 @@ function AI_A2A:New( AIGroup )
|
||||
|
||||
self:SetControllable( AIGroup )
|
||||
|
||||
self:ManageFuel( .2, 60 )
|
||||
self:ManageDamage( 0.4 )
|
||||
self:SetFuelThreshold( .2, 60 )
|
||||
self:SetDamageThreshold( 0.4 )
|
||||
self:SetDisengageRadius( 70000 )
|
||||
|
||||
self:SetStartState( "Stopped" )
|
||||
|
||||
@ -219,8 +220,37 @@ function AI_A2A:New( AIGroup )
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
|
||||
self:AddTransition( "Patrolling", "Refuel", "Refuelling" )
|
||||
|
||||
--- Refuel Handler OnBefore for AI_A2A
|
||||
-- @function [parent=#AI_A2A] OnBeforeRefuel
|
||||
-- @param #AI_A2A self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Refuel Handler OnAfter for AI_A2A
|
||||
-- @function [parent=#AI_A2A] OnAfterRefuel
|
||||
-- @param #AI_A2A self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Refuel Trigger for AI_A2A
|
||||
-- @function [parent=#AI_A2A] Refuel
|
||||
-- @param #AI_A2A self
|
||||
|
||||
--- Refuel Asynchronous Trigger for AI_A2A
|
||||
-- @function [parent=#AI_A2A] __Refuel
|
||||
-- @param #AI_A2A self
|
||||
-- @param #number Delay
|
||||
|
||||
self:AddTransition( "*", "Takeoff", "Airborne" )
|
||||
self:AddTransition( "*", "Return", "Returning" )
|
||||
self:AddTransition( "*", "Hold", "Holding" )
|
||||
self:AddTransition( "*", "Home", "Home" )
|
||||
self:AddTransition( "*", "LostControl", "LostControl" )
|
||||
self:AddTransition( "*", "Fuel", "Fuel" )
|
||||
@ -234,6 +264,13 @@ function AI_A2A:New( AIGroup )
|
||||
return self
|
||||
end
|
||||
|
||||
--- @param Wrapper.Group#GROUP self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function GROUP:OnEventTakeoff( EventData, Fsm )
|
||||
Fsm:Takeoff()
|
||||
self:UnHandleEvent( EVENTS.Takeoff )
|
||||
end
|
||||
|
||||
function AI_A2A:SetDispatcher( Dispatcher )
|
||||
self.Dispatcher = Dispatcher
|
||||
end
|
||||
@ -294,8 +331,27 @@ function AI_A2A:SetHomeAirbase( HomeAirbase )
|
||||
self.HomeAirbase = HomeAirbase
|
||||
end
|
||||
|
||||
--- Sets to refuel at the given tanker.
|
||||
-- @param #AI_A2A self
|
||||
-- @param Wrapper.Group#GROUP TankerName The group name of the tanker as defined within the Mission Editor or spawned.
|
||||
-- @return #AI_A2A self
|
||||
function AI_A2A:SetTanker( TankerName )
|
||||
self:F2( { TankerName } )
|
||||
|
||||
self.TankerName = TankerName
|
||||
end
|
||||
|
||||
|
||||
--- Sets the disengage range, that when engaging a target beyond the specified range, the engagement will be cancelled and the plane will RTB.
|
||||
-- @param #AI_A2A self
|
||||
-- @param #number DisengageRadius The disengage range.
|
||||
-- @return #AI_A2A self
|
||||
function AI_A2A:SetDisengageRadius( DisengageRadius )
|
||||
self:F2( { DisengageRadius } )
|
||||
|
||||
self.DisengageRadius = DisengageRadius
|
||||
end
|
||||
|
||||
--- Set the status checking off.
|
||||
-- @param #AI_A2A self
|
||||
-- @return #AI_A2A self
|
||||
@ -311,13 +367,13 @@ end
|
||||
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_A2A.
|
||||
-- Once the time is finished, the old AI will return to the base.
|
||||
-- @param #AI_A2A self
|
||||
-- @param #number PatrolFuelTresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
|
||||
-- @param #number PatrolFuelThresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
|
||||
-- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
|
||||
-- @return #AI_A2A self
|
||||
function AI_A2A:ManageFuel( PatrolFuelTresholdPercentage, PatrolOutOfFuelOrbitTime )
|
||||
function AI_A2A:SetFuelThreshold( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime )
|
||||
|
||||
self.PatrolManageFuel = true
|
||||
self.PatrolFuelTresholdPercentage = PatrolFuelTresholdPercentage
|
||||
self.PatrolFuelThresholdPercentage = PatrolFuelThresholdPercentage
|
||||
self.PatrolOutOfFuelOrbitTime = PatrolOutOfFuelOrbitTime
|
||||
|
||||
self.Controllable:OptionRTBBingoFuel( false )
|
||||
@ -332,12 +388,12 @@ end
|
||||
-- Note that for groups, the average damage of the complete group will be calculated.
|
||||
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25.
|
||||
-- @param #AI_A2A self
|
||||
-- @param #number PatrolDamageTreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged.
|
||||
-- @param #number PatrolDamageThreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged.
|
||||
-- @return #AI_A2A self
|
||||
function AI_A2A:ManageDamage( PatrolDamageTreshold )
|
||||
function AI_A2A:SetDamageThreshold( PatrolDamageThreshold )
|
||||
|
||||
self.PatrolManageDamage = true
|
||||
self.PatrolDamageTreshold = PatrolDamageTreshold
|
||||
self.PatrolDamageThreshold = PatrolDamageThreshold
|
||||
|
||||
return self
|
||||
end
|
||||
@ -372,45 +428,79 @@ end
|
||||
|
||||
--- @param #AI_A2A self
|
||||
function AI_A2A:onafterStatus()
|
||||
self:F()
|
||||
|
||||
self:F( " Checking Status" )
|
||||
|
||||
if self.Controllable and self.Controllable:IsAlive() then
|
||||
|
||||
local RTB = false
|
||||
|
||||
local Fuel = self.Controllable:GetUnit(1):GetFuel()
|
||||
self:F({Fuel=Fuel})
|
||||
if Fuel < self.PatrolFuelTresholdPercentage then
|
||||
self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... RTB!" )
|
||||
local OldAIControllable = self.Controllable
|
||||
local AIControllableTemplate = self.Controllable:GetTemplate()
|
||||
local DistanceFromHomeBase = self.HomeAirbase:GetCoordinate():Get2DDistance( self.Controllable:GetCoordinate() )
|
||||
|
||||
if not self:Is( "Holding" ) and not self:Is( "Returning" ) then
|
||||
local DistanceFromHomeBase = self.HomeAirbase:GetCoordinate():Get2DDistance( self.Controllable:GetCoordinate() )
|
||||
self:F({DistanceFromHomeBase=DistanceFromHomeBase})
|
||||
|
||||
local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
|
||||
local TimedOrbitTask = OldAIControllable:TaskControlled( OrbitTask, OldAIControllable:TaskCondition(nil,nil,nil,nil,self.PatrolOutOfFuelOrbitTime,nil ) )
|
||||
OldAIControllable:SetTask( TimedOrbitTask, 10 )
|
||||
if DistanceFromHomeBase > self.DisengageRadius then
|
||||
self:E( self.Controllable:GetName() .. " is too far from home base, RTB!" )
|
||||
self:Hold( 300 )
|
||||
RTB = false
|
||||
end
|
||||
end
|
||||
|
||||
if self:Is( "Fuel" ) or self:Is( "Damaged" ) or self:Is( "LostControl" ) then
|
||||
if DistanceFromHomeBase < 5000 then
|
||||
self:E( self.Controllable:GetName() .. " is too far from home base, RTB!" )
|
||||
self:Home( "Destroy" )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
self:Fuel()
|
||||
RTB = true
|
||||
else
|
||||
if not self:Is( "Fuel" ) and not self:Is( "Home" ) then
|
||||
local Fuel = self.Controllable:GetFuel()
|
||||
self:F({Fuel=Fuel})
|
||||
if Fuel < self.PatrolFuelThresholdPercentage then
|
||||
if self.TankerName then
|
||||
self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... Refuelling at Tanker!" )
|
||||
self:Refuel()
|
||||
else
|
||||
self:E( self.Controllable:GetName() .. " is out of fuel: " .. Fuel .. " ... RTB!" )
|
||||
local OldAIControllable = self.Controllable
|
||||
|
||||
local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
|
||||
local TimedOrbitTask = OldAIControllable:TaskControlled( OrbitTask, OldAIControllable:TaskCondition(nil,nil,nil,nil,self.PatrolOutOfFuelOrbitTime,nil ) )
|
||||
OldAIControllable:SetTask( TimedOrbitTask, 10 )
|
||||
|
||||
self:Fuel()
|
||||
RTB = true
|
||||
end
|
||||
else
|
||||
end
|
||||
end
|
||||
|
||||
-- TODO: Check GROUP damage function.
|
||||
local Damage = self.Controllable:GetLife()
|
||||
local InitialLife = self.Controllable:GetLife0()
|
||||
self:F( { Damage = Damage, InitialLife = InitialLife, DamageTreshold = self.PatrolDamageTreshold } )
|
||||
if ( Damage / InitialLife ) < self.PatrolDamageTreshold then
|
||||
self:F( { Damage = Damage, InitialLife = InitialLife, DamageThreshold = self.PatrolDamageThreshold } )
|
||||
if ( Damage / InitialLife ) < self.PatrolDamageThreshold then
|
||||
self:E( self.Controllable:GetName() .. " is damaged: " .. Damage .. " ... RTB!" )
|
||||
self:Damaged()
|
||||
RTB = true
|
||||
self:SetStatusOff()
|
||||
end
|
||||
|
||||
-- Check if planes went RTB and are out of control.
|
||||
if self.Controllable:HasTask() == false then
|
||||
if not self:Is( "Started" ) and
|
||||
not self:Is( "Stopped" ) then
|
||||
not self:Is( "Stopped" ) and
|
||||
not self:Is( "Home" ) then
|
||||
if self.IdleCount >= 2 then
|
||||
self:E( self.Controllable:GetName() .. " control lost! " )
|
||||
self:LostControl()
|
||||
if Damage ~= InitialLife then
|
||||
self:Damaged()
|
||||
else
|
||||
self:E( self.Controllable:GetName() .. " control lost! " )
|
||||
self:LostControl()
|
||||
end
|
||||
else
|
||||
self.IdleCount = self.IdleCount + 1
|
||||
end
|
||||
@ -418,7 +508,7 @@ function AI_A2A:onafterStatus()
|
||||
else
|
||||
self.IdleCount = 0
|
||||
end
|
||||
|
||||
|
||||
if RTB == true then
|
||||
self:__RTB( 0.5 )
|
||||
end
|
||||
@ -429,13 +519,28 @@ end
|
||||
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_A2A.RTBRoute( AIGroup )
|
||||
function AI_A2A.RTBRoute( AIGroup, Fsm )
|
||||
|
||||
AIGroup:E( { "RTBRoute:", AIGroup:GetName() } )
|
||||
local _AI_A2A = AIGroup:GetState( AIGroup, "AI_A2A" ) -- #AI_A2A
|
||||
_AI_A2A:__RTB( 0.5 )
|
||||
AIGroup:F( { "AI_A2A.RTBRoute:", AIGroup:GetName() } )
|
||||
|
||||
if AIGroup:IsAlive() then
|
||||
Fsm:__RTB( 0.5 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_A2A.RTBHold( AIGroup, Fsm )
|
||||
|
||||
AIGroup:F( { "AI_A2A.RTBHold:", AIGroup:GetName() } )
|
||||
if AIGroup:IsAlive() then
|
||||
Fsm:__RTB( 0.5 )
|
||||
Fsm:Return()
|
||||
local Task = AIGroup:TaskOrbitCircle( 4000, 400 )
|
||||
AIGroup:SetTask( Task )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_A2A self
|
||||
@ -448,8 +553,6 @@ function AI_A2A:onafterRTB( AIGroup, From, Event, To )
|
||||
|
||||
self:E( "Group " .. AIGroup:GetName() .. " ... RTB! ( " .. self:GetState() .. " )" )
|
||||
|
||||
self.CheckStatus = false
|
||||
|
||||
self:ClearTargetDistance()
|
||||
AIGroup:ClearTasks()
|
||||
|
||||
@ -466,11 +569,12 @@ function AI_A2A:onafterRTB( AIGroup, From, Event, To )
|
||||
|
||||
local ToAirbaseCoord = CurrentCoord:Translate( 5000, ToAirbaseAngle )
|
||||
if Distance < 5000 then
|
||||
self:E( "RTB and near the airbase!" )
|
||||
self:Home()
|
||||
return
|
||||
end
|
||||
--- Create a route point of type air.
|
||||
local ToPatrolRoutePoint = ToAirbaseCoord:RoutePointAir(
|
||||
local ToRTBRoutePoint = ToAirbaseCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@ -481,7 +585,8 @@ function AI_A2A:onafterRTB( AIGroup, From, Event, To )
|
||||
self:F( { Angle = ToAirbaseAngle, ToTargetSpeed = ToTargetSpeed } )
|
||||
self:T2( { self.MinSpeed, self.MaxSpeed, ToTargetSpeed } )
|
||||
|
||||
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
|
||||
EngageRoute[#EngageRoute+1] = ToRTBRoutePoint
|
||||
EngageRoute[#EngageRoute+1] = ToRTBRoutePoint
|
||||
|
||||
AIGroup:OptionROEHoldFire()
|
||||
AIGroup:OptionROTEvadeFire()
|
||||
@ -490,13 +595,11 @@ function AI_A2A:onafterRTB( AIGroup, From, Event, To )
|
||||
AIGroup:WayPointInitialize( EngageRoute )
|
||||
|
||||
local Tasks = {}
|
||||
Tasks[#Tasks+1] = AIGroup:TaskFunction( 1, 1, "AI_A2A.RTBRoute" )
|
||||
EngageRoute[1].task = AIGroup:TaskCombo( Tasks )
|
||||
|
||||
AIGroup:SetState( AIGroup, "AI_A2A", self )
|
||||
Tasks[#Tasks+1] = AIGroup:TaskFunction( "AI_A2A.RTBRoute", self )
|
||||
EngageRoute[#EngageRoute].task = AIGroup:TaskCombo( Tasks )
|
||||
|
||||
--- NOW ROUTE THE GROUP!
|
||||
AIGroup:WayPointExecute( 1, 0 )
|
||||
AIGroup:Route( EngageRoute, 0.5 )
|
||||
|
||||
end
|
||||
|
||||
@ -513,6 +616,89 @@ function AI_A2A:onafterHome( AIGroup, From, Event, To )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @param #AI_A2A self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_A2A:onafterHold( AIGroup, From, Event, To, HoldTime )
|
||||
self:F( { AIGroup, From, Event, To } )
|
||||
|
||||
self:E( "Group " .. self.Controllable:GetName() .. " ... Holding! ( " .. self:GetState() .. " )" )
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
local OrbitTask = AIGroup:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
|
||||
local TimedOrbitTask = AIGroup:TaskControlled( OrbitTask, AIGroup:TaskCondition( nil, nil, nil, nil, HoldTime , nil ) )
|
||||
|
||||
local RTBTask = AIGroup:TaskFunction( "AI_A2A.RTBHold", self )
|
||||
|
||||
local OrbitHoldTask = AIGroup:TaskOrbitCircle( 4000, self.PatrolMinSpeed )
|
||||
|
||||
--AIGroup:SetState( AIGroup, "AI_A2A", self )
|
||||
|
||||
AIGroup:SetTask( AIGroup:TaskCombo( { TimedOrbitTask, RTBTask, OrbitHoldTask } ), 1 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_A2A.Resume( AIGroup, Fsm )
|
||||
|
||||
AIGroup:F( { "AI_A2A.Resume:", AIGroup:GetName() } )
|
||||
if AIGroup:IsAlive() then
|
||||
Fsm:__RTB( 0.5 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #AI_A2A self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_A2A:onafterRefuel( AIGroup, From, Event, To )
|
||||
self:F( { AIGroup, From, Event, To } )
|
||||
|
||||
self:E( "Group " .. self.Controllable:GetName() .. " ... Refuelling! ( " .. self:GetState() .. " )" )
|
||||
|
||||
if AIGroup and AIGroup:IsAlive() then
|
||||
local Tanker = GROUP:FindByName( self.TankerName )
|
||||
if Tanker:IsAlive() and Tanker:IsAirPlane() then
|
||||
|
||||
local RefuelRoute = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local CurrentCoord = AIGroup:GetCoordinate()
|
||||
local ToRefuelCoord = Tanker:GetCoordinate()
|
||||
local ToRefuelSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToRefuelRoutePoint = ToRefuelCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
ToRefuelSpeed,
|
||||
true
|
||||
)
|
||||
|
||||
self:F( { ToRefuelSpeed = ToRefuelSpeed } )
|
||||
|
||||
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
|
||||
RefuelRoute[#RefuelRoute+1] = ToRefuelRoutePoint
|
||||
|
||||
AIGroup:OptionROEHoldFire()
|
||||
AIGroup:OptionROTEvadeFire()
|
||||
|
||||
local Tasks = {}
|
||||
Tasks[#Tasks+1] = AIGroup:TaskRefueling()
|
||||
Tasks[#Tasks+1] = AIGroup:TaskFunction( self:GetClassName() .. ".Resume", self )
|
||||
RefuelRoute[#RefuelRoute].task = AIGroup:TaskCombo( Tasks )
|
||||
|
||||
AIGroup:Route( RefuelRoute, 0.5 )
|
||||
else
|
||||
self:RTB()
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- AI CAP classes makes AI Controllables execute a Combat Air Patrol.
|
||||
-- AI CAP classes makes AI Groups execute a Combat Air Patrol.
|
||||
--
|
||||
-- There are the following types of CAP classes defined:
|
||||
--
|
||||
@ -34,7 +34,7 @@
|
||||
|
||||
--- # AI_A2A_CAP class, extends @{AI_CAP#AI_PATROL_ZONE}
|
||||
--
|
||||
-- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}
|
||||
-- The AI_A2A_CAP class implements the core functions to patrol a @{Zone} by an AI @{Group} or @{Group}
|
||||
-- and automatically engage any airborne enemies that are within a certain range or within a certain zone.
|
||||
--
|
||||
-- 
|
||||
@ -120,20 +120,20 @@ AI_A2A_CAP = {
|
||||
|
||||
--- Creates a new AI_A2A_CAP object
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
-- @param Wrapper.Group#GROUP AICap
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
|
||||
-- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h.
|
||||
-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h.
|
||||
-- @param Dcs.DCSTypes#Speed EngageMinSpeed The minimum speed of the @{Controllable} in km/h when engaging a target.
|
||||
-- @param Dcs.DCSTypes#Speed EngageMaxSpeed The maximum speed of the @{Controllable} in km/h when engaging a target.
|
||||
-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Group} in km/h.
|
||||
-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Group} in km/h.
|
||||
-- @param Dcs.DCSTypes#Speed EngageMinSpeed The minimum speed of the @{Group} in km/h when engaging a target.
|
||||
-- @param Dcs.DCSTypes#Speed EngageMaxSpeed The maximum speed of the @{Group} in km/h when engaging a target.
|
||||
-- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_A2A_CAP
|
||||
function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, PatrolAltType )
|
||||
function AI_A2A_CAP:New( AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, EngageMinSpeed, EngageMaxSpeed, PatrolAltType )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_A2A_CAP
|
||||
local self = BASE:Inherit( self, AI_A2A_PATROL:New( AICap, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType ) ) -- #AI_A2A_CAP
|
||||
|
||||
self.Accomplished = false
|
||||
self.Engaging = false
|
||||
@ -141,12 +141,12 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
|
||||
self.EngageMinSpeed = EngageMinSpeed
|
||||
self.EngageMaxSpeed = EngageMaxSpeed
|
||||
|
||||
self:AddTransition( { "Patrolling", "Engaging", "Returning" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP.
|
||||
self:AddTransition( { "Patrolling", "Engaging", "Returning", "Airborne" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_CAP.
|
||||
|
||||
--- OnBefore Transition Handler for Event Engage.
|
||||
-- @function [parent=#AI_A2A_CAP] OnBeforeEngage
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -155,7 +155,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
|
||||
--- OnAfter Transition Handler for Event Engage.
|
||||
-- @function [parent=#AI_A2A_CAP] OnAfterEngage
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -172,7 +172,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
|
||||
--- OnLeave Transition Handler for State Engaging.
|
||||
-- @function [parent=#AI_A2A_CAP] OnLeaveEngaging
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -181,7 +181,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
|
||||
--- OnEnter Transition Handler for State Engaging.
|
||||
-- @function [parent=#AI_A2A_CAP] OnEnterEngaging
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -191,7 +191,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
|
||||
--- OnBefore Transition Handler for Event Fired.
|
||||
-- @function [parent=#AI_A2A_CAP] OnBeforeFired
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -200,7 +200,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
|
||||
--- OnAfter Transition Handler for Event Fired.
|
||||
-- @function [parent=#AI_A2A_CAP] OnAfterFired
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -219,7 +219,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
|
||||
--- OnBefore Transition Handler for Event Destroy.
|
||||
-- @function [parent=#AI_A2A_CAP] OnBeforeDestroy
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -228,7 +228,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
|
||||
--- OnAfter Transition Handler for Event Destroy.
|
||||
-- @function [parent=#AI_A2A_CAP] OnAfterDestroy
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -248,7 +248,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
|
||||
--- OnBefore Transition Handler for Event Abort.
|
||||
-- @function [parent=#AI_A2A_CAP] OnBeforeAbort
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -257,7 +257,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
|
||||
--- OnAfter Transition Handler for Event Abort.
|
||||
-- @function [parent=#AI_A2A_CAP] OnAfterAbort
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -276,7 +276,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
|
||||
--- OnBefore Transition Handler for Event Accomplish.
|
||||
-- @function [parent=#AI_A2A_CAP] OnBeforeAccomplish
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -285,7 +285,7 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
|
||||
--- OnAfter Transition Handler for Event Accomplish.
|
||||
-- @function [parent=#AI_A2A_CAP] OnAfterAccomplish
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -302,6 +302,17 @@ function AI_A2A_CAP:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeiling
|
||||
return self
|
||||
end
|
||||
|
||||
--- onafter State Transition for Event Patrol.
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AICap The AI Group managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_CAP:onafterStart( AICap, From, Event, To )
|
||||
|
||||
AICap:HandleEvent( EVENTS.Takeoff, nil, self )
|
||||
|
||||
end
|
||||
|
||||
--- Set the Engage Zone which defines where the AI will engage bogies.
|
||||
-- @param #AI_A2A_CAP self
|
||||
@ -333,33 +344,36 @@ end
|
||||
|
||||
--- onafter State Transition for Event Patrol.
|
||||
-- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE AIGroup The AI Group managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AICap The AI Group managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_CAP:onafterPatrol( AIGroup, From, Event, To )
|
||||
function AI_A2A_CAP:onafterPatrol( AICap, From, Event, To )
|
||||
|
||||
-- Call the parent Start event handler
|
||||
self:GetParent(self).onafterPatrol( self, AIGroup, From, Event, To )
|
||||
self:GetParent(self).onafterPatrol( self, AICap, From, Event, To )
|
||||
self:HandleEvent( EVENTS.Dead )
|
||||
|
||||
end
|
||||
|
||||
-- todo: need to fix this global function
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIGroup
|
||||
function AI_A2A_CAP.AttackRoute( AIGroup )
|
||||
--- @param Wrapper.Group#GROUP AICap
|
||||
function AI_A2A_CAP.AttackRoute( AICap, Fsm )
|
||||
|
||||
local EngageZone = AIGroup:GetState( AIGroup, "AI_A2A_CAP" ) -- AI.AI_Cap#AI_A2A_CAP
|
||||
EngageZone:__Engage( 0.5 )
|
||||
AICap:F( { "AI_A2A_CAP.AttackRoute:", AICap:GetName() } )
|
||||
|
||||
if AICap:IsAlive() then
|
||||
Fsm:__Engage( 0.5 )
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE AIGroup The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_CAP:onbeforeEngage( AIGroup, From, Event, To )
|
||||
function AI_A2A_CAP:onbeforeEngage( AICap, From, Event, To )
|
||||
|
||||
if self.Accomplished == true then
|
||||
return false
|
||||
@ -367,43 +381,43 @@ function AI_A2A_CAP:onbeforeEngage( AIGroup, From, Event, To )
|
||||
end
|
||||
|
||||
--- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE AIGroup The AI Group managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AICap The AI Group managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_CAP:onafterAbort( AIGroup, From, Event, To )
|
||||
AIGroup:ClearTasks()
|
||||
function AI_A2A_CAP:onafterAbort( AICap, From, Event, To )
|
||||
AICap:ClearTasks()
|
||||
self:__Route( 0.5 )
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE AIGroup The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AICap The AICap Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_CAP:onafterEngage( AIGroup, From, Event, To, AttackSetUnit )
|
||||
function AI_A2A_CAP:onafterEngage( AICap, From, Event, To, AttackSetUnit )
|
||||
|
||||
self:F( { AIGroup, From, Event, To, AttackSetUnit} )
|
||||
self:F( { AICap, From, Event, To, AttackSetUnit} )
|
||||
|
||||
self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT
|
||||
|
||||
local FirstAttackUnit = self.AttackSetUnit:GetFirst()
|
||||
local FirstAttackUnit = self.AttackSetUnit:GetFirst() -- Wrapper.Unit#UNIT
|
||||
|
||||
if FirstAttackUnit then
|
||||
if FirstAttackUnit and FirstAttackUnit:IsAlive() then -- If there is no attacker anymore, stop the engagement.
|
||||
|
||||
if AIGroup:IsAlive() then
|
||||
if AICap:IsAlive() then
|
||||
|
||||
local EngageRoute = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
local CurrentCoord = AIGroup:GetCoordinate()
|
||||
local CurrentCoord = AICap:GetCoordinate()
|
||||
local ToTargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
|
||||
local ToTargetSpeed = math.random( self.EngageMinSpeed, self.EngageMaxSpeed )
|
||||
local ToInterceptAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToPatrolRoutePoint = CurrentCoord:Translate( 5000, ToInterceptAngle ):RoutePointAir(
|
||||
local ToPatrolRoutePoint = CurrentCoord:Translate( 5000, ToInterceptAngle ):WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@ -414,40 +428,31 @@ function AI_A2A_CAP:onafterEngage( AIGroup, From, Event, To, AttackSetUnit )
|
||||
self:F( { Angle = ToInterceptAngle, ToTargetSpeed = ToTargetSpeed } )
|
||||
self:T2( { self.MinSpeed, self.MaxSpeed, ToTargetSpeed } )
|
||||
|
||||
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
|
||||
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
|
||||
|
||||
AIGroup:OptionROEOpenFire()
|
||||
AIGroup:OptionROTPassiveDefense()
|
||||
|
||||
local AttackTasks = {}
|
||||
|
||||
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
|
||||
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
|
||||
self:T( { "Attacking Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
|
||||
if AttackUnit:IsAlive() and AttackUnit:IsAir() then
|
||||
AttackTasks[#AttackTasks+1] = AIGroup:TaskAttackUnit( AttackUnit )
|
||||
AttackTasks[#AttackTasks+1] = AICap:TaskAttackUnit( AttackUnit )
|
||||
end
|
||||
end
|
||||
|
||||
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
|
||||
self.Controllable:WayPointInitialize( EngageRoute )
|
||||
|
||||
|
||||
if #AttackTasks == 0 then
|
||||
self:E("No targets found -> Going back to Patrolling")
|
||||
self:__Abort( 0.5 )
|
||||
else
|
||||
AttackTasks[#AttackTasks+1] = AIGroup:TaskFunction( 1, #AttackTasks, "AI_A2A_CAP.AttackRoute" )
|
||||
AttackTasks[#AttackTasks+1] = AIGroup:TaskOrbitCircle( 4000, self.PatrolMinSpeed )
|
||||
|
||||
EngageRoute[1].task = AIGroup:TaskCombo( AttackTasks )
|
||||
|
||||
--- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ...
|
||||
AIGroup:SetState( AIGroup, "AI_A2A_CAP", self )
|
||||
AICap:OptionROEOpenFire()
|
||||
AICap:OptionROTEvadeFire()
|
||||
|
||||
AttackTasks[#AttackTasks+1] = AICap:TaskFunction( "AI_A2A_CAP.AttackRoute", self )
|
||||
EngageRoute[#EngageRoute].task = AICap:TaskCombo( AttackTasks )
|
||||
end
|
||||
|
||||
--- NOW ROUTE THE GROUP!
|
||||
AIGroup:WayPointExecute( 1, 0 )
|
||||
AICap:Route( EngageRoute, 0.5 )
|
||||
end
|
||||
else
|
||||
self:E("No targets found -> Going back to Patrolling")
|
||||
@ -456,22 +461,22 @@ function AI_A2A_CAP:onafterEngage( AIGroup, From, Event, To, AttackSetUnit )
|
||||
end
|
||||
|
||||
--- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_CAP:onafterAccomplish( Controllable, From, Event, To )
|
||||
function AI_A2A_CAP:onafterAccomplish( AICap, From, Event, To )
|
||||
self.Accomplished = true
|
||||
self:SetDetectionOff()
|
||||
end
|
||||
|
||||
--- @param #AI_A2A_CAP self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AICap The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_A2A_CAP:onafterDestroy( Controllable, From, Event, To, EventData )
|
||||
function AI_A2A_CAP:onafterDestroy( AICap, From, Event, To, EventData )
|
||||
|
||||
if EventData.IniUnit then
|
||||
self.AttackUnits[EventData.IniUnit] = nil
|
||||
@ -489,3 +494,15 @@ function AI_A2A_CAP:OnEventDead( EventData )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- @param Wrapper.Group#GROUP AICap
|
||||
function AI_A2A_CAP.Resume( AICap )
|
||||
|
||||
AICap:F( { "AI_A2A_CAP.Resume:", AICap:GetName() } )
|
||||
if AICap:IsAlive() then
|
||||
local _AI_A2A = AICap:GetState( AICap, "AI_A2A" ) -- #AI_A2A
|
||||
_AI_A2A:__Reset( 1 )
|
||||
_AI_A2A:__Route( 5 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -117,12 +117,12 @@ AI_A2A_GCI = {
|
||||
|
||||
--- Creates a new AI_A2A_GCI object
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
-- @param Wrapper.Group#GROUP AIIntercept
|
||||
-- @return #AI_A2A_GCI
|
||||
function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
|
||||
function AI_A2A_GCI:New( AIIntercept, EngageMinSpeed, EngageMaxSpeed )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, AI_A2A:New( AIGroup ) ) -- #AI_A2A_GCI
|
||||
local self = BASE:Inherit( self, AI_A2A:New( AIIntercept ) ) -- #AI_A2A_GCI
|
||||
|
||||
self.Accomplished = false
|
||||
self.Engaging = false
|
||||
@ -134,12 +134,12 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
|
||||
|
||||
self.PatrolAltType = "RADIO"
|
||||
|
||||
self:AddTransition( { "Started", "Engaging", "Returning" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI.
|
||||
self:AddTransition( { "Started", "Engaging", "Returning", "Airborne" }, "Engage", "Engaging" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_GCI.
|
||||
|
||||
--- OnBefore Transition Handler for Event Engage.
|
||||
-- @function [parent=#AI_A2A_GCI] OnBeforeEngage
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -148,7 +148,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
|
||||
--- OnAfter Transition Handler for Event Engage.
|
||||
-- @function [parent=#AI_A2A_GCI] OnAfterEngage
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -165,7 +165,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
|
||||
--- OnLeave Transition Handler for State Engaging.
|
||||
-- @function [parent=#AI_A2A_GCI] OnLeaveEngaging
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -174,7 +174,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
|
||||
--- OnEnter Transition Handler for State Engaging.
|
||||
-- @function [parent=#AI_A2A_GCI] OnEnterEngaging
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -184,7 +184,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
|
||||
--- OnBefore Transition Handler for Event Fired.
|
||||
-- @function [parent=#AI_A2A_GCI] OnBeforeFired
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -193,7 +193,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
|
||||
--- OnAfter Transition Handler for Event Fired.
|
||||
-- @function [parent=#AI_A2A_GCI] OnAfterFired
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -212,7 +212,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
|
||||
--- OnBefore Transition Handler for Event Destroy.
|
||||
-- @function [parent=#AI_A2A_GCI] OnBeforeDestroy
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -221,7 +221,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
|
||||
--- OnAfter Transition Handler for Event Destroy.
|
||||
-- @function [parent=#AI_A2A_GCI] OnAfterDestroy
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -241,7 +241,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
|
||||
--- OnBefore Transition Handler for Event Abort.
|
||||
-- @function [parent=#AI_A2A_GCI] OnBeforeAbort
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -250,7 +250,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
|
||||
--- OnAfter Transition Handler for Event Abort.
|
||||
-- @function [parent=#AI_A2A_GCI] OnAfterAbort
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -269,7 +269,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
|
||||
--- OnBefore Transition Handler for Event Accomplish.
|
||||
-- @function [parent=#AI_A2A_GCI] OnBeforeAccomplish
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -278,7 +278,7 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
|
||||
--- OnAfter Transition Handler for Event Accomplish.
|
||||
-- @function [parent=#AI_A2A_GCI] OnAfterAccomplish
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -295,14 +295,27 @@ function AI_A2A_GCI:New( AIGroup, EngageMinSpeed, EngageMaxSpeed )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- onafter State Transition for Event Patrol.
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AI Group managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The AI Group managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_GCI:onafterEngage( AIGroup, From, Event, To )
|
||||
function AI_A2A_GCI:onafterStart( AIIntercept, From, Event, To )
|
||||
|
||||
AIIntercept:HandleEvent( EVENTS.Takeoff, nil, self )
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- onafter State Transition for Event Patrol.
|
||||
-- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The AI Group managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_GCI:onafterEngage( AIIntercept, From, Event, To )
|
||||
|
||||
self:HandleEvent( EVENTS.Dead )
|
||||
|
||||
@ -311,19 +324,24 @@ end
|
||||
-- todo: need to fix this global function
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIControllable
|
||||
function AI_A2A_GCI.InterceptRoute( AIControllable )
|
||||
function AI_A2A_GCI.InterceptRoute( AIIntercept, Fsm )
|
||||
|
||||
AIControllable:T( "NewEngageRoute" )
|
||||
local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cap#AI_A2A_GCI
|
||||
EngageZone:__Engage( 0.5 )
|
||||
AIIntercept:F( { "AI_A2A_GCI.InterceptRoute:", AIIntercept:GetName() } )
|
||||
|
||||
if AIIntercept:IsAlive() then
|
||||
Fsm:__Engage( 0.5 )
|
||||
|
||||
--local Task = AIIntercept:TaskOrbitCircle( 4000, 400 )
|
||||
--AIIntercept:SetTask( Task )
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_GCI:onbeforeEngage( AIGroup, From, Event, To )
|
||||
function AI_A2A_GCI:onbeforeEngage( AIIntercept, From, Event, To )
|
||||
|
||||
if self.Accomplished == true then
|
||||
return false
|
||||
@ -331,39 +349,41 @@ function AI_A2A_GCI:onbeforeEngage( AIGroup, From, Event, To )
|
||||
end
|
||||
|
||||
--- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AI Group managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The AI Group managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_GCI:onafterAbort( AIGroup, From, Event, To )
|
||||
AIGroup:ClearTasks()
|
||||
function AI_A2A_GCI:onafterAbort( AIIntercept, From, Event, To )
|
||||
AIIntercept:ClearTasks()
|
||||
self:Return()
|
||||
self:__RTB( 0.5 )
|
||||
end
|
||||
|
||||
|
||||
--- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The GroupGroup managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_GCI:onafterEngage( AIGroup, From, Event, To, AttackSetUnit )
|
||||
function AI_A2A_GCI:onafterEngage( AIIntercept, From, Event, To, AttackSetUnit )
|
||||
|
||||
self:F( { AIGroup, From, Event, To, AttackSetUnit} )
|
||||
self:F( { AIIntercept, From, Event, To, AttackSetUnit} )
|
||||
|
||||
self.AttackSetUnit = AttackSetUnit or self.AttackSetUnit -- Core.Set#SET_UNIT
|
||||
|
||||
local FirstAttackUnit = self.AttackSetUnit:GetFirst()
|
||||
|
||||
if FirstAttackUnit then
|
||||
if FirstAttackUnit and FirstAttackUnit:IsAlive() then
|
||||
|
||||
if AIGroup:IsAlive() then
|
||||
if AIIntercept:IsAlive() then
|
||||
|
||||
local EngageRoute = {}
|
||||
|
||||
local CurrentCoord = AIIntercept:GetCoordinate()
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local CurrentCoord = AIGroup:GetCoordinate()
|
||||
local CurrentCoord = AIIntercept:GetCoordinate()
|
||||
|
||||
local ToTargetCoord = self.AttackSetUnit:GetFirst():GetCoordinate()
|
||||
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
|
||||
@ -372,7 +392,7 @@ function AI_A2A_GCI:onafterEngage( AIGroup, From, Event, To, AttackSetUnit )
|
||||
local ToInterceptAngle = CurrentCoord:GetAngleDegrees( CurrentCoord:GetDirectionVec3( ToTargetCoord ) )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToPatrolRoutePoint = CurrentCoord:Translate( 5000, ToInterceptAngle ):RoutePointAir(
|
||||
local ToPatrolRoutePoint = CurrentCoord:Translate( 15000, ToInterceptAngle ):WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@ -381,42 +401,34 @@ function AI_A2A_GCI:onafterEngage( AIGroup, From, Event, To, AttackSetUnit )
|
||||
)
|
||||
|
||||
self:F( { Angle = ToInterceptAngle, ToTargetSpeed = ToTargetSpeed } )
|
||||
self:T2( { self.EngageMinSpeed, self.EngageMaxSpeed, ToTargetSpeed } )
|
||||
self:F( { self.EngageMinSpeed, self.EngageMaxSpeed, ToTargetSpeed } )
|
||||
|
||||
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
|
||||
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
|
||||
|
||||
AIGroup:OptionROEOpenFire()
|
||||
AIGroup:OptionROTPassiveDefense()
|
||||
|
||||
local AttackTasks = {}
|
||||
|
||||
for AttackUnitID, AttackUnit in pairs( self.AttackSetUnit:GetSet() ) do
|
||||
local AttackUnit = AttackUnit -- Wrapper.Unit#UNIT
|
||||
self:T( { "Intercepting Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
|
||||
if AttackUnit:IsAlive() and AttackUnit:IsAir() then
|
||||
AttackTasks[#AttackTasks+1] = AIGroup:TaskAttackUnit( AttackUnit )
|
||||
self:T( { "Intercepting Unit:", AttackUnit:GetName(), AttackUnit:IsAlive(), AttackUnit:IsAir() } )
|
||||
AttackTasks[#AttackTasks+1] = AIIntercept:TaskAttackUnit( AttackUnit )
|
||||
end
|
||||
end
|
||||
|
||||
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
|
||||
AIGroup:WayPointInitialize( EngageRoute )
|
||||
|
||||
|
||||
|
||||
if #AttackTasks == 0 then
|
||||
self:E("No targets found -> Going RTB")
|
||||
self:Return()
|
||||
self:__RTB( 0.5 )
|
||||
else
|
||||
AttackTasks[#AttackTasks+1] = AIGroup:TaskFunction( 1, #AttackTasks, "AI_A2A_GCI.InterceptRoute" )
|
||||
AttackTasks[#AttackTasks+1] = AIGroup:TaskOrbitCircle( 4000, self.EngageMinSpeed )
|
||||
EngageRoute[1].task = AIGroup:TaskCombo( AttackTasks )
|
||||
|
||||
--- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ...
|
||||
AIGroup:SetState( AIGroup, "EngageZone", self )
|
||||
AIIntercept:OptionROEOpenFire()
|
||||
AIIntercept:OptionROTEvadeFire()
|
||||
|
||||
AttackTasks[#AttackTasks+1] = AIIntercept:TaskFunction( "AI_A2A_GCI.InterceptRoute", self )
|
||||
EngageRoute[#EngageRoute].task = AIIntercept:TaskCombo( AttackTasks )
|
||||
end
|
||||
|
||||
--- NOW ROUTE THE GROUP!
|
||||
AIGroup:WayPointExecute( 1, 0 )
|
||||
AIIntercept:Route( EngageRoute, 0.5 )
|
||||
|
||||
end
|
||||
else
|
||||
@ -427,22 +439,22 @@ function AI_A2A_GCI:onafterEngage( AIGroup, From, Event, To, AttackSetUnit )
|
||||
end
|
||||
|
||||
--- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_GCI:onafterAccomplish( AIGroup, From, Event, To )
|
||||
function AI_A2A_GCI:onafterAccomplish( AIIntercept, From, Event, To )
|
||||
self.Accomplished = true
|
||||
self:SetDetectionOff()
|
||||
end
|
||||
|
||||
--- @param #AI_A2A_GCI self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AIGroup Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIIntercept The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function AI_A2A_GCI:onafterDestroy( AIGroup, From, Event, To, EventData )
|
||||
function AI_A2A_GCI:onafterDestroy( AIIntercept, From, Event, To, EventData )
|
||||
|
||||
if EventData.IniUnit then
|
||||
self.AttackUnits[EventData.IniUnit] = nil
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- AI PATROL classes makes AI Controllables execute an Patrol.
|
||||
-- AI PATROL classes makes AI Groups execute an Patrol.
|
||||
--
|
||||
-- There are the following types of PATROL classes defined:
|
||||
--
|
||||
@ -44,7 +44,7 @@
|
||||
|
||||
--- # AI_A2A_PATROL class, extends @{Fsm#FSM_CONTROLLABLE}
|
||||
--
|
||||
-- The AI_A2A_PATROL class implements the core functions to patrol a @{Zone} by an AI @{Controllable} or @{Group}.
|
||||
-- The AI_A2A_PATROL class implements the core functions to patrol a @{Zone} by an AI @{Group} or @{Group}.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@ -120,7 +120,7 @@
|
||||
-- * @{#AI_A2A_PATROL.SetDetectionOn}(): Set the detection on. The AI will detect for targets.
|
||||
-- * @{#AI_A2A_PATROL.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased.
|
||||
--
|
||||
-- The detection frequency can be set with @{#AI_A2A_PATROL.SetDetectionInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection.
|
||||
-- The detection frequency can be set with @{#AI_A2A_PATROL.SetRefreshTimeInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection.
|
||||
-- Use the method @{#AI_A2A_PATROL.GetDetectedUnits}() to obtain a list of the @{Unit}s detected by the AI.
|
||||
--
|
||||
-- The detection can be filtered to potential targets in a specific zone.
|
||||
@ -139,7 +139,7 @@
|
||||
--
|
||||
-- ## 7. Manage "damage" behaviour of the AI in the AI_A2A_PATROL
|
||||
--
|
||||
-- When the AI is damaged, it is required that a new AIControllable is started. However, damage cannon be foreseen early on.
|
||||
-- When the AI is damaged, it is required that a new Patrol is started. However, damage cannon be foreseen early on.
|
||||
-- Therefore, when the damage treshold is reached, the AI will return immediately to the home base (RTB).
|
||||
-- Use the method @{#AI_A2A_PATROL.ManageDamage}() to have this proces in place.
|
||||
--
|
||||
@ -152,23 +152,23 @@ AI_A2A_PATROL = {
|
||||
|
||||
--- Creates a new AI_A2A_PATROL object
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIGroup
|
||||
-- @param Wrapper.Group#GROUP AIPatrol
|
||||
-- @param Core.Zone#ZONE_BASE PatrolZone The @{Zone} where the patrol needs to be executed.
|
||||
-- @param Dcs.DCSTypes#Altitude PatrolFloorAltitude The lowest altitude in meters where to execute the patrol.
|
||||
-- @param Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h.
|
||||
-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h.
|
||||
-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Group} in km/h.
|
||||
-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Group} in km/h.
|
||||
-- @param Dcs.DCSTypes#AltitudeType PatrolAltType The altitude type ("RADIO"=="AGL", "BARO"=="ASL"). Defaults to RADIO
|
||||
-- @return #AI_A2A_PATROL self
|
||||
-- @usage
|
||||
-- -- Define a new AI_A2A_PATROL Object. This PatrolArea will patrol an AIControllable within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h.
|
||||
-- -- Define a new AI_A2A_PATROL Object. This PatrolArea will patrol a Group within PatrolZone between 3000 and 6000 meters, with a variying speed between 600 and 900 km/h.
|
||||
-- PatrolZone = ZONE:New( 'PatrolZone' )
|
||||
-- PatrolSpawn = SPAWN:New( 'Patrol Group' )
|
||||
-- PatrolArea = AI_A2A_PATROL:New( PatrolZone, 3000, 6000, 600, 900 )
|
||||
function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
function AI_A2A_PATROL:New( AIPatrol, PatrolZone, PatrolFloorAltitude, PatrolCeilingAltitude, PatrolMinSpeed, PatrolMaxSpeed, PatrolAltType )
|
||||
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, AI_A2A:New( AIGroup ) ) -- #AI_A2A_PATROL
|
||||
local self = BASE:Inherit( self, AI_A2A:New( AIPatrol ) ) -- #AI_A2A_PATROL
|
||||
|
||||
self.PatrolZone = PatrolZone
|
||||
self.PatrolFloorAltitude = PatrolFloorAltitude
|
||||
@ -179,12 +179,12 @@ function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeil
|
||||
-- defafult PatrolAltType to "RADIO" if not specified
|
||||
self.PatrolAltType = PatrolAltType or "RADIO"
|
||||
|
||||
self:AddTransition( "Started", "Patrol", "Patrolling" )
|
||||
self:AddTransition( { "Started", "Airborne", "Refuelling" }, "Patrol", "Patrolling" )
|
||||
|
||||
--- OnBefore Transition Handler for Event Patrol.
|
||||
-- @function [parent=#AI_A2A_PATROL] OnBeforePatrol
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -193,7 +193,7 @@ function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeil
|
||||
--- OnAfter Transition Handler for Event Patrol.
|
||||
-- @function [parent=#AI_A2A_PATROL] OnAfterPatrol
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -210,7 +210,7 @@ function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeil
|
||||
--- OnLeave Transition Handler for State Patrolling.
|
||||
-- @function [parent=#AI_A2A_PATROL] OnLeavePatrolling
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -219,7 +219,7 @@ function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeil
|
||||
--- OnEnter Transition Handler for State Patrolling.
|
||||
-- @function [parent=#AI_A2A_PATROL] OnEnterPatrolling
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -229,7 +229,7 @@ function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeil
|
||||
--- OnBefore Transition Handler for Event Route.
|
||||
-- @function [parent=#AI_A2A_PATROL] OnBeforeRoute
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -238,7 +238,7 @@ function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeil
|
||||
--- OnAfter Transition Handler for Event Route.
|
||||
-- @function [parent=#AI_A2A_PATROL] OnAfterRoute
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
@ -252,6 +252,8 @@ function AI_A2A_PATROL:New( AIGroup, PatrolZone, PatrolFloorAltitude, PatrolCeil
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param #number Delay The delay in seconds.
|
||||
|
||||
|
||||
|
||||
self:AddTransition( "*", "Reset", "Patrolling" ) -- FSM_CONTROLLABLE Transition for type #AI_A2A_PATROL.
|
||||
|
||||
return self
|
||||
@ -262,8 +264,8 @@ end
|
||||
|
||||
--- Sets (modifies) the minimum and maximum speed of the patrol.
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h.
|
||||
-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h.
|
||||
-- @param Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Group} in km/h.
|
||||
-- @param Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Group} in km/h.
|
||||
-- @return #AI_A2A_PATROL self
|
||||
function AI_A2A_PATROL:SetSpeed( PatrolMinSpeed, PatrolMaxSpeed )
|
||||
self:F2( { PatrolMinSpeed, PatrolMaxSpeed } )
|
||||
@ -290,18 +292,18 @@ end
|
||||
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings.
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @return #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_PATROL:onafterPatrol( Controllable, From, Event, To )
|
||||
function AI_A2A_PATROL:onafterPatrol( AIPatrol, From, Event, To )
|
||||
self:F2()
|
||||
|
||||
self:ClearTargetDistance()
|
||||
|
||||
self:__Route( 1 )
|
||||
|
||||
self.Controllable:OnReSpawn(
|
||||
AIPatrol:OnReSpawn(
|
||||
function( PatrolGroup )
|
||||
self:E( "ReSpawn" )
|
||||
self:__Reset( 1 )
|
||||
@ -312,23 +314,27 @@ end
|
||||
|
||||
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIGroup
|
||||
-- This statis method is called from the route path within the last task at the last waaypoint of the Controllable.
|
||||
-- Note that this method is required, as triggers the next route when patrolling for the Controllable.
|
||||
function AI_A2A_PATROL.PatrolRoute( AIGroup )
|
||||
--- @param Wrapper.Group#GROUP AIPatrol
|
||||
-- This statis method is called from the route path within the last task at the last waaypoint of the AIPatrol.
|
||||
-- Note that this method is required, as triggers the next route when patrolling for the AIPatrol.
|
||||
function AI_A2A_PATROL.PatrolRoute( AIPatrol, Fsm )
|
||||
|
||||
local _AI_A2A_Patrol = AIGroup:GetState( AIGroup, "AI_A2A_PATROL" ) -- #AI_A2A_PATROL
|
||||
_AI_A2A_Patrol:Route()
|
||||
AIPatrol:F( { "AI_A2A_PATROL.PatrolRoute:", AIPatrol:GetName() } )
|
||||
|
||||
if AIPatrol:IsAlive() then
|
||||
Fsm:Route()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Defines a new patrol route using the @{Process_PatrolZone} parameters and settings.
|
||||
-- @param #AI_A2A_PATROL self
|
||||
-- @param Wrapper.Group#GROUP AIGroup The AIGroup managed by the FSM.
|
||||
-- @param Wrapper.Group#GROUP AIPatrol The Group managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
-- @param #string Event The Event string.
|
||||
-- @param #string To The To State string.
|
||||
function AI_A2A_PATROL:onafterRoute( AIGroup, From, Event, To )
|
||||
function AI_A2A_PATROL:onafterRoute( AIPatrol, From, Event, To )
|
||||
|
||||
self:F2()
|
||||
|
||||
@ -338,22 +344,22 @@ function AI_A2A_PATROL:onafterRoute( AIGroup, From, Event, To )
|
||||
end
|
||||
|
||||
|
||||
if AIGroup:IsAlive() then
|
||||
if AIPatrol:IsAlive() then
|
||||
|
||||
local PatrolRoute = {}
|
||||
|
||||
--- Calculate the target route point.
|
||||
|
||||
local CurrentCoord = AIGroup:GetCoordinate()
|
||||
local CurrentCoord = AIPatrol:GetCoordinate()
|
||||
|
||||
local ToTargetCoord = self.PatrolZone:GetRandomPointVec2()
|
||||
ToTargetCoord:SetAlt(math.random( self.PatrolFloorAltitude,self.PatrolCeilingAltitude ) )
|
||||
ToTargetCoord:SetAlt( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ) )
|
||||
self:SetTargetDistance( ToTargetCoord ) -- For RTB status check
|
||||
|
||||
local ToTargetSpeed = math.random( self.PatrolMinSpeed, self.PatrolMaxSpeed )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToPatrolRoutePoint = ToTargetCoord:RoutePointAir(
|
||||
local ToPatrolRoutePoint = ToTargetCoord:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@ -361,22 +367,29 @@ function AI_A2A_PATROL:onafterRoute( AIGroup, From, Event, To )
|
||||
true
|
||||
)
|
||||
|
||||
PatrolRoute[#PatrolRoute+1] = ToPatrolRoutePoint
|
||||
PatrolRoute[#PatrolRoute+1] = ToPatrolRoutePoint
|
||||
|
||||
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
|
||||
AIGroup:WayPointInitialize( PatrolRoute )
|
||||
|
||||
local Tasks = {}
|
||||
Tasks[#Tasks+1] = AIGroup:TaskFunction( 1, 1, "AI_A2A_PATROL.PatrolRoute" )
|
||||
Tasks[#Tasks+1] = AIPatrol:TaskFunction( "AI_A2A_PATROL.PatrolRoute", self )
|
||||
PatrolRoute[#PatrolRoute].task = AIPatrol:TaskCombo( Tasks )
|
||||
|
||||
PatrolRoute[1].task = AIGroup:TaskCombo( Tasks )
|
||||
|
||||
--- Do a trick, link the NewPatrolRoute function of the PATROLGROUP object to the AIControllable in a temporary variable ...
|
||||
AIGroup:SetState( AIGroup, "AI_A2A_PATROL", self )
|
||||
AIPatrol:OptionROEReturnFire()
|
||||
AIPatrol:OptionROTEvadeFire()
|
||||
|
||||
--- NOW ROUTE THE GROUP!
|
||||
AIGroup:WayPointExecute( 1, 2 )
|
||||
AIPatrol:Route( PatrolRoute, 0.5 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param Wrapper.Group#GROUP AIPatrol
|
||||
function AI_A2A_PATROL.Resume( AIPatrol )
|
||||
|
||||
AIPatrol:F( { "AI_A2A_PATROL.Resume:", AIPatrol:GetName() } )
|
||||
if AIPatrol:IsAlive() then
|
||||
local _AI_A2A = AIPatrol:GetState( AIPatrol, "AI_A2A" ) -- #AI_A2A
|
||||
_AI_A2A:__Reset( 1 )
|
||||
_AI_A2A:__Route( 5 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -531,7 +531,7 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToEngageZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:RoutePointAir(
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@ -588,7 +588,7 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:RoutePointAir(
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@ -612,7 +612,7 @@ function AI_BAI_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
--- NOW ROUTE THE GROUP!
|
||||
Controllable:WayPointExecute( 1 )
|
||||
|
||||
self:SetDetectionInterval( 2 )
|
||||
self:SetRefreshTimeInterval( 2 )
|
||||
self:SetDetectionActivated()
|
||||
self:__Target( -2 ) -- Start Targetting
|
||||
end
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
|
||||
--- @type AI_BALANCER
|
||||
-- @field Core.Set#SET_CLIENT SetClient
|
||||
-- @field Functional.Spawn#SPAWN SpawnAI
|
||||
-- @field Core.Spawn#SPAWN SpawnAI
|
||||
-- @field Wrapper.Group#GROUP Test
|
||||
-- @extends Core.Fsm#FSM_SET
|
||||
|
||||
@ -106,7 +106,7 @@ AI_BALANCER = {
|
||||
--- Creates a new AI_BALANCER object
|
||||
-- @param #AI_BALANCER self
|
||||
-- @param Core.Set#SET_CLIENT SetClient A SET\_CLIENT object that will contain the CLIENT objects to be monitored if they are alive or not (joined by a player).
|
||||
-- @param Functional.Spawn#SPAWN SpawnAI The default Spawn object to spawn new AI Groups when needed.
|
||||
-- @param Core.Spawn#SPAWN SpawnAI The default Spawn object to spawn new AI Groups when needed.
|
||||
-- @return #AI_BALANCER
|
||||
function AI_BALANCER:New( SetClient, SpawnAI )
|
||||
|
||||
@ -151,22 +151,22 @@ end
|
||||
|
||||
--- Returns the AI to the nearest friendly @{Airbase#AIRBASE}.
|
||||
-- @param #AI_BALANCER self
|
||||
-- @param Dcs.DCSTypes#Distance ReturnTresholdRange If there is an enemy @{Client#CLIENT} within the ReturnTresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}.
|
||||
-- @param Dcs.DCSTypes#Distance ReturnThresholdRange If there is an enemy @{Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}.
|
||||
-- @param Core.Set#SET_AIRBASE ReturnAirbaseSet The SET of @{Set#SET_AIRBASE}s to evaluate where to return to.
|
||||
function AI_BALANCER:ReturnToNearestAirbases( ReturnTresholdRange, ReturnAirbaseSet )
|
||||
function AI_BALANCER:ReturnToNearestAirbases( ReturnThresholdRange, ReturnAirbaseSet )
|
||||
|
||||
self.ToNearestAirbase = true
|
||||
self.ReturnTresholdRange = ReturnTresholdRange
|
||||
self.ReturnThresholdRange = ReturnThresholdRange
|
||||
self.ReturnAirbaseSet = ReturnAirbaseSet
|
||||
end
|
||||
|
||||
--- Returns the AI to the home @{Airbase#AIRBASE}.
|
||||
-- @param #AI_BALANCER self
|
||||
-- @param Dcs.DCSTypes#Distance ReturnTresholdRange If there is an enemy @{Client#CLIENT} within the ReturnTresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}.
|
||||
function AI_BALANCER:ReturnToHomeAirbase( ReturnTresholdRange )
|
||||
-- @param Dcs.DCSTypes#Distance ReturnThresholdRange If there is an enemy @{Client#CLIENT} within the ReturnThresholdRange given in meters, the AI will not return to the nearest @{Airbase#AIRBASE}.
|
||||
function AI_BALANCER:ReturnToHomeAirbase( ReturnThresholdRange )
|
||||
|
||||
self.ToHomeAirbase = true
|
||||
self.ReturnTresholdRange = ReturnTresholdRange
|
||||
self.ReturnThresholdRange = ReturnThresholdRange
|
||||
end
|
||||
|
||||
--- @param #AI_BALANCER self
|
||||
@ -246,12 +246,12 @@ function AI_BALANCER:onenterMonitoring( SetGroup )
|
||||
if self.ToNearestAirbase == false and self.ToHomeAirbase == false then
|
||||
self:Destroy( Client.UnitName, AIGroup )
|
||||
else
|
||||
-- We test if there is no other CLIENT within the self.ReturnTresholdRange of the first unit of the AI group.
|
||||
-- We test if there is no other CLIENT within the self.ReturnThresholdRange of the first unit of the AI group.
|
||||
-- If there is a CLIENT, the AI stays engaged and will not return.
|
||||
-- If there is no CLIENT within the self.ReturnTresholdRange, then the unit will return to the Airbase return method selected.
|
||||
-- If there is no CLIENT within the self.ReturnThresholdRange, then the unit will return to the Airbase return method selected.
|
||||
|
||||
local PlayerInRange = { Value = false }
|
||||
local RangeZone = ZONE_RADIUS:New( 'RangeZone', AIGroup:GetVec2(), self.ReturnTresholdRange )
|
||||
local RangeZone = ZONE_RADIUS:New( 'RangeZone', AIGroup:GetVec2(), self.ReturnThresholdRange )
|
||||
|
||||
self:T2( RangeZone )
|
||||
|
||||
|
||||
@ -358,16 +358,20 @@ function AI_CAP_ZONE:onafterStart( Controllable, From, Event, To )
|
||||
|
||||
end
|
||||
|
||||
-- todo: need to fix this global function
|
||||
|
||||
--- @param Wrapper.Controllable#CONTROLLABLE AIControllable
|
||||
function _NewEngageCapRoute( AIControllable )
|
||||
--- @param AI.AI_CAP#AI_CAP_ZONE
|
||||
-- @param Wrapper.Group#GROUP EngageGroup
|
||||
function AI_CAP_ZONE.EngageRoute( EngageGroup, Fsm )
|
||||
|
||||
AIControllable:T( "NewEngageRoute" )
|
||||
local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cap#AI_CAP_ZONE
|
||||
EngageZone:__Engage( 1 )
|
||||
EngageGroup:F( { "AI_CAP_ZONE.EngageRoute:", EngageGroup:GetName() } )
|
||||
|
||||
if EngageGroup:IsAlive() then
|
||||
Fsm:__Engage( 1 )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @param #AI_CAP_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable The Controllable Object managed by the FSM.
|
||||
-- @param #string From The From State string.
|
||||
@ -402,7 +406,7 @@ function AI_CAP_ZONE:onafterDetected( Controllable, From, Event, To )
|
||||
end
|
||||
|
||||
if Engage == true then
|
||||
self:E( 'Detected -> Engaging' )
|
||||
self:F( 'Detected -> Engaging' )
|
||||
self:__Engage( 1 )
|
||||
end
|
||||
end
|
||||
@ -440,7 +444,7 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
|
||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToEngageZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:RoutePointAir(
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@ -464,7 +468,7 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
|
||||
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToPatrolRoutePoint = ToTargetPointVec3:RoutePointAir(
|
||||
local ToPatrolRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@ -475,7 +479,7 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
|
||||
EngageRoute[#EngageRoute+1] = ToPatrolRoutePoint
|
||||
|
||||
Controllable:OptionROEOpenFire()
|
||||
Controllable:OptionROTPassiveDefense()
|
||||
Controllable:OptionROTEvadeFire()
|
||||
|
||||
local AttackTasks = {}
|
||||
|
||||
@ -485,13 +489,13 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
|
||||
if DetectedUnit:IsAlive() and DetectedUnit:IsAir() then
|
||||
if self.EngageZone then
|
||||
if DetectedUnit:IsInZone( self.EngageZone ) then
|
||||
self:E( {"Within Zone and Engaging ", DetectedUnit } )
|
||||
self:F( {"Within Zone and Engaging ", DetectedUnit } )
|
||||
AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit )
|
||||
end
|
||||
else
|
||||
if self.EngageRange then
|
||||
if DetectedUnit:GetPointVec3():Get2DDistance(Controllable:GetPointVec3() ) <= self.EngageRange then
|
||||
self:E( {"Within Range and Engaging", DetectedUnit } )
|
||||
self:F( {"Within Range and Engaging", DetectedUnit } )
|
||||
AttackTasks[#AttackTasks+1] = Controllable:TaskAttackUnit( DetectedUnit )
|
||||
end
|
||||
else
|
||||
@ -503,28 +507,20 @@ function AI_CAP_ZONE:onafterEngage( Controllable, From, Event, To )
|
||||
end
|
||||
end
|
||||
|
||||
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
|
||||
self.Controllable:WayPointInitialize( EngageRoute )
|
||||
|
||||
|
||||
if #AttackTasks == 0 then
|
||||
self:E("No targets found -> Going back to Patrolling")
|
||||
self:F("No targets found -> Going back to Patrolling")
|
||||
self:__Abort( 1 )
|
||||
self:__Route( 1 )
|
||||
self:SetDetectionActivated()
|
||||
else
|
||||
|
||||
AttackTasks[#AttackTasks+1] = Controllable:TaskFunction( "AI_CAP_ZONE.EngageRoute", self )
|
||||
EngageRoute[1].task = Controllable:TaskCombo( AttackTasks )
|
||||
|
||||
--- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ...
|
||||
self.Controllable:SetState( self.Controllable, "EngageZone", self )
|
||||
|
||||
self.Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageCapRoute" )
|
||||
|
||||
self:SetDetectionDeactivated()
|
||||
end
|
||||
|
||||
--- NOW ROUTE THE GROUP!
|
||||
self.Controllable:WayPointExecute( 1, 2 )
|
||||
Controllable:Route( EngageRoute, 0.5 )
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@ -373,12 +373,15 @@ function AI_CAS_ZONE:onafterStart( Controllable, From, Event, To )
|
||||
self:SetDetectionDeactivated() -- When not engaging, set the detection off.
|
||||
end
|
||||
|
||||
--- @param Wrapper.Controllable#CONTROLLABLE AIControllable
|
||||
function _NewEngageRoute( AIControllable )
|
||||
--- @param AI.AI_CAS#AI_CAS_ZONE
|
||||
-- @param Wrapper.Group#GROUP EngageGroup
|
||||
function AI_CAS_ZONE.EngageRoute( EngageGroup, Fsm )
|
||||
|
||||
AIControllable:T( "NewEngageRoute" )
|
||||
local EngageZone = AIControllable:GetState( AIControllable, "EngageZone" ) -- AI.AI_Cas#AI_CAS_ZONE
|
||||
EngageZone:__Engage( 1, EngageZone.EngageSpeed, EngageZone.EngageAltitude, EngageZone.EngageWeaponExpend, EngageZone.EngageAttackQty, EngageZone.EngageDirection )
|
||||
EngageGroup:F( { "AI_CAS_ZONE.EngageRoute:", EngageGroup:GetName() } )
|
||||
|
||||
if EngageGroup:IsAlive() then
|
||||
Fsm:__Engage( 1, Fsm.EngageSpeed, Fsm.EngageAltitude, Fsm.EngageWeaponExpend, Fsm.EngageAttackQty, Fsm.EngageDirection )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -464,6 +467,9 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
|
||||
if Controllable:IsAlive() then
|
||||
|
||||
Controllable:OptionROEOpenFire()
|
||||
Controllable:OptionROTVertical()
|
||||
|
||||
local EngageRoute = {}
|
||||
|
||||
--- Calculate the current route point.
|
||||
@ -473,7 +479,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToEngageZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:RoutePointAir(
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@ -485,7 +491,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
|
||||
local AttackTasks = {}
|
||||
|
||||
for DetectedUnitID, DetectedUnit in pairs( self.DetectedUnits ) do
|
||||
for DetectedUnit, Detected in pairs( self.DetectedUnits ) do
|
||||
local DetectedUnit = DetectedUnit -- Wrapper.Unit#UNIT
|
||||
self:T( DetectedUnit )
|
||||
if DetectedUnit:IsAlive() then
|
||||
@ -503,7 +509,8 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
end
|
||||
end
|
||||
|
||||
EngageRoute[1].task = Controllable:TaskCombo( AttackTasks )
|
||||
AttackTasks[#AttackTasks+1] = Controllable:TaskFunction( "AI_CAS_ZONE.EngageRoute", self )
|
||||
EngageRoute[#EngageRoute].task = Controllable:TaskCombo( AttackTasks )
|
||||
|
||||
--- Define a random point in the @{Zone}. The AI will fly to that point within the zone.
|
||||
|
||||
@ -515,7 +522,7 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, self.EngageAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:RoutePointAir(
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@ -524,22 +531,10 @@ function AI_CAS_ZONE:onafterEngage( Controllable, From, Event, To,
|
||||
)
|
||||
|
||||
EngageRoute[#EngageRoute+1] = ToTargetRoutePoint
|
||||
|
||||
--- Now we're going to do something special, we're going to call a function from a waypoint action at the AIControllable...
|
||||
Controllable:WayPointInitialize( EngageRoute )
|
||||
|
||||
Controllable:Route( EngageRoute, 0.5 )
|
||||
|
||||
--- Do a trick, link the NewEngageRoute function of the object to the AIControllable in a temporary variable ...
|
||||
Controllable:SetState( Controllable, "EngageZone", self )
|
||||
|
||||
Controllable:WayPointFunction( #EngageRoute, 1, "_NewEngageRoute" )
|
||||
|
||||
--- NOW ROUTE THE GROUP!
|
||||
Controllable:WayPointExecute( 1 )
|
||||
|
||||
Controllable:OptionROEOpenFire()
|
||||
Controllable:OptionROTVertical()
|
||||
|
||||
self:SetDetectionInterval( 2 )
|
||||
self:SetRefreshTimeInterval( 2 )
|
||||
self:SetDetectionActivated()
|
||||
self:__Target( -2 ) -- Start Targetting
|
||||
end
|
||||
|
||||
@ -958,7 +958,7 @@ function AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
|
||||
-- @param Wrapper.Unit#UNIT ClientUnit
|
||||
function( FollowGroup, Formation, ClientUnit, CT1, CV1, CT2, CV2 )
|
||||
|
||||
FollowGroup:OptionROTPassiveDefense()
|
||||
FollowGroup:OptionROTEvadeFire()
|
||||
FollowGroup:OptionROEReturnFire()
|
||||
|
||||
local GroupUnit = FollowGroup:GetUnit( 1 )
|
||||
|
||||
@ -47,7 +47,7 @@
|
||||
-- @field Dcs.DCSTypes#Altitude PatrolCeilingAltitude The highest altitude in meters where to execute the patrol.
|
||||
-- @field Dcs.DCSTypes#Speed PatrolMinSpeed The minimum speed of the @{Controllable} in km/h.
|
||||
-- @field Dcs.DCSTypes#Speed PatrolMaxSpeed The maximum speed of the @{Controllable} in km/h.
|
||||
-- @field Functional.Spawn#SPAWN CoordTest
|
||||
-- @field Core.Spawn#SPAWN CoordTest
|
||||
-- @extends Core.Fsm#FSM_CONTROLLABLE
|
||||
|
||||
--- # AI_PATROL_ZONE class, extends @{Fsm#FSM_CONTROLLABLE}
|
||||
@ -128,7 +128,7 @@
|
||||
-- * @{#AI_PATROL_ZONE.SetDetectionOn}(): Set the detection on. The AI will detect for targets.
|
||||
-- * @{#AI_PATROL_ZONE.SetDetectionOff}(): Set the detection off, the AI will not detect for targets. The existing target list will NOT be erased.
|
||||
--
|
||||
-- The detection frequency can be set with @{#AI_PATROL_ZONE.SetDetectionInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection.
|
||||
-- The detection frequency can be set with @{#AI_PATROL_ZONE.SetRefreshTimeInterval}( seconds ), where the amount of seconds specify how much seconds will be waited before the next detection.
|
||||
-- Use the method @{#AI_PATROL_ZONE.GetDetectedUnits}() to obtain a list of the @{Unit}s detected by the AI.
|
||||
--
|
||||
-- The detection can be filtered to potential targets in a specific zone.
|
||||
@ -187,7 +187,7 @@ function AI_PATROL_ZONE:New( PatrolZone, PatrolFloorAltitude, PatrolCeilingAltit
|
||||
-- defafult PatrolAltType to "RADIO" if not specified
|
||||
self.PatrolAltType = PatrolAltType or "RADIO"
|
||||
|
||||
self:SetDetectionInterval( 30 )
|
||||
self:SetRefreshTimeInterval( 30 )
|
||||
|
||||
self.CheckStatus = true
|
||||
|
||||
@ -544,7 +544,7 @@ end
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param #number Seconds The interval in seconds.
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:SetDetectionInterval( Seconds )
|
||||
function AI_PATROL_ZONE:SetRefreshTimeInterval( Seconds )
|
||||
self:F2()
|
||||
|
||||
if Seconds then
|
||||
@ -591,13 +591,13 @@ end
|
||||
-- When the fuel treshold is reached, the AI will continue for a given time its patrol task in orbit, while a new AIControllable is targetted to the AI_PATROL_ZONE.
|
||||
-- Once the time is finished, the old AI will return to the base.
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param #number PatrolFuelTresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
|
||||
-- @param #number PatrolFuelThresholdPercentage The treshold in percentage (between 0 and 1) when the AIControllable is considered to get out of fuel.
|
||||
-- @param #number PatrolOutOfFuelOrbitTime The amount of seconds the out of fuel AIControllable will orbit before returning to the base.
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:ManageFuel( PatrolFuelTresholdPercentage, PatrolOutOfFuelOrbitTime )
|
||||
function AI_PATROL_ZONE:ManageFuel( PatrolFuelThresholdPercentage, PatrolOutOfFuelOrbitTime )
|
||||
|
||||
self.PatrolManageFuel = true
|
||||
self.PatrolFuelTresholdPercentage = PatrolFuelTresholdPercentage
|
||||
self.PatrolFuelThresholdPercentage = PatrolFuelThresholdPercentage
|
||||
self.PatrolOutOfFuelOrbitTime = PatrolOutOfFuelOrbitTime
|
||||
|
||||
return self
|
||||
@ -610,12 +610,12 @@ end
|
||||
-- Note that for groups, the average damage of the complete group will be calculated.
|
||||
-- So, in a group of 4 airplanes, 2 lost and 2 with damage 0.2, the damage treshold will be 0.25.
|
||||
-- @param #AI_PATROL_ZONE self
|
||||
-- @param #number PatrolDamageTreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged.
|
||||
-- @param #number PatrolDamageThreshold The treshold in percentage (between 0 and 1) when the AI is considered to be damaged.
|
||||
-- @return #AI_PATROL_ZONE self
|
||||
function AI_PATROL_ZONE:ManageDamage( PatrolDamageTreshold )
|
||||
function AI_PATROL_ZONE:ManageDamage( PatrolDamageThreshold )
|
||||
|
||||
self.PatrolManageDamage = true
|
||||
self.PatrolDamageTreshold = PatrolDamageTreshold
|
||||
self.PatrolDamageThreshold = PatrolDamageThreshold
|
||||
|
||||
return self
|
||||
end
|
||||
@ -748,7 +748,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:RoutePointAir(
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TakeOffParking,
|
||||
POINT_VEC3.RoutePointAction.FromParkingArea,
|
||||
@ -763,7 +763,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:RoutePointAir(
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@ -789,7 +789,7 @@ function AI_PATROL_ZONE:onafterRoute( Controllable, From, Event, To )
|
||||
local ToTargetPointVec3 = POINT_VEC3:New( ToTargetVec2.x, ToTargetAltitude, ToTargetVec2.y )
|
||||
|
||||
--- Create a route point of type air.
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:RoutePointAir(
|
||||
local ToTargetRoutePoint = ToTargetPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
@ -831,10 +831,9 @@ function AI_PATROL_ZONE:onafterStatus()
|
||||
local RTB = false
|
||||
|
||||
local Fuel = self.Controllable:GetUnit(1):GetFuel()
|
||||
if Fuel < self.PatrolFuelTresholdPercentage then
|
||||
if Fuel < self.PatrolFuelThresholdPercentage then
|
||||
self:E( self.Controllable:GetName() .. " is out of fuel:" .. Fuel .. ", RTB!" )
|
||||
local OldAIControllable = self.Controllable
|
||||
local AIControllableTemplate = self.Controllable:GetTemplate()
|
||||
|
||||
local OrbitTask = OldAIControllable:TaskOrbitCircle( math.random( self.PatrolFloorAltitude, self.PatrolCeilingAltitude ), self.PatrolMinSpeed )
|
||||
local TimedOrbitTask = OldAIControllable:TaskControlled( OrbitTask, OldAIControllable:TaskCondition(nil,nil,nil,nil,self.PatrolOutOfFuelOrbitTime,nil ) )
|
||||
@ -846,7 +845,7 @@ function AI_PATROL_ZONE:onafterStatus()
|
||||
|
||||
-- TODO: Check GROUP damage function.
|
||||
local Damage = self.Controllable:GetLife()
|
||||
if Damage <= self.PatrolDamageTreshold then
|
||||
if Damage <= self.PatrolDamageThreshold then
|
||||
self:E( self.Controllable:GetName() .. " is damaged:" .. Damage .. ", RTB!" )
|
||||
RTB = true
|
||||
end
|
||||
@ -877,7 +876,7 @@ function AI_PATROL_ZONE:onafterRTB()
|
||||
local CurrentAltitude = self.Controllable:GetUnit(1):GetAltitude()
|
||||
local CurrentPointVec3 = POINT_VEC3:New( CurrentVec2.x, CurrentAltitude, CurrentVec2.y )
|
||||
local ToPatrolZoneSpeed = self.PatrolMaxSpeed
|
||||
local CurrentRoutePoint = CurrentPointVec3:RoutePointAir(
|
||||
local CurrentRoutePoint = CurrentPointVec3:WaypointAir(
|
||||
self.PatrolAltType,
|
||||
POINT_VEC3.RoutePointType.TurningPoint,
|
||||
POINT_VEC3.RoutePointAction.TurningPoint,
|
||||
|
||||
@ -91,7 +91,7 @@ do -- ACT_ACCOUNT
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
@ -107,7 +107,7 @@ do -- ACT_ACCOUNT
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
@ -125,7 +125,7 @@ do -- ACT_ACCOUNT
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
@ -155,7 +155,6 @@ do -- ACT_ACCOUNT_DEADS
|
||||
-- @extends #ACT_ACCOUNT
|
||||
ACT_ACCOUNT_DEADS = {
|
||||
ClassName = "ACT_ACCOUNT_DEADS",
|
||||
TargetSetUnit = nil,
|
||||
}
|
||||
|
||||
|
||||
@ -163,13 +162,10 @@ do -- ACT_ACCOUNT_DEADS
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Set#SET_UNIT TargetSetUnit
|
||||
-- @param #string TaskName
|
||||
function ACT_ACCOUNT_DEADS:New( TargetSetUnit, TaskName )
|
||||
function ACT_ACCOUNT_DEADS:New()
|
||||
-- Inherits from BASE
|
||||
local self = BASE:Inherit( self, ACT_ACCOUNT:New() ) -- #ACT_ACCOUNT_DEADS
|
||||
|
||||
self.TargetSetUnit = TargetSetUnit
|
||||
self.TaskName = TaskName
|
||||
|
||||
self.DisplayInterval = 30
|
||||
self.DisplayCount = 30
|
||||
self.DisplayMessage = true
|
||||
@ -181,38 +177,39 @@ do -- ACT_ACCOUNT_DEADS
|
||||
|
||||
function ACT_ACCOUNT_DEADS:Init( FsmAccount )
|
||||
|
||||
self.TargetSetUnit = FsmAccount.TargetSetUnit
|
||||
self.TaskName = FsmAccount.TaskName
|
||||
self.Task = self:GetTask()
|
||||
self.TaskName = self.Task:GetName()
|
||||
end
|
||||
|
||||
--- Process Events
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ACCOUNT_DEADS:onenterReport( ProcessUnit, Task, From, Event, To )
|
||||
self:E( { ProcessUnit, From, Event, To } )
|
||||
|
||||
self:Message( "Your group with assigned " .. self.TaskName .. " task has " .. self.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed." )
|
||||
local MessageText = "Your group with assigned " .. self.TaskName .. " task has " .. Task.TargetSetUnit:GetUnitTypesText() .. " targets left to be destroyed."
|
||||
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
|
||||
end
|
||||
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Wrapper.Client#CLIENT ProcessClient
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param Tasking.Task#TASK Task
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function ACT_ACCOUNT_DEADS:onafterEvent( ProcessClient, Task, From, Event, To, EventData )
|
||||
self:T( { ProcessClient:GetName(), Task:GetName(), From, Event, To, EventData } )
|
||||
function ACT_ACCOUNT_DEADS:onafterEvent( ProcessUnit, Task, From, Event, To, EventData )
|
||||
self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } )
|
||||
|
||||
if self.TargetSetUnit:FindUnit( EventData.IniUnitName ) then
|
||||
local PlayerName = ProcessClient:GetPlayerName()
|
||||
if Task.TargetSetUnit:FindUnit( EventData.IniUnitName ) then
|
||||
local PlayerName = ProcessUnit:GetPlayerName()
|
||||
local PlayerHit = self.PlayerHits and self.PlayerHits[EventData.IniUnitName]
|
||||
if PlayerHit == PlayerName then
|
||||
self:Player( EventData )
|
||||
@ -224,24 +221,26 @@ do -- ACT_ACCOUNT_DEADS
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Wrapper.Client#CLIENT ProcessClient
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param Tasking.Task#TASK Task
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function ACT_ACCOUNT_DEADS:onenterAccountForPlayer( ProcessClient, Task, From, Event, To, EventData )
|
||||
self:T( { ProcessClient:GetName(), Task:GetName(), From, Event, To, EventData } )
|
||||
function ACT_ACCOUNT_DEADS:onenterAccountForPlayer( ProcessUnit, Task, From, Event, To, EventData )
|
||||
self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } )
|
||||
|
||||
local TaskGroup = ProcessClient:GetGroup()
|
||||
local TaskGroup = ProcessUnit:GetGroup()
|
||||
|
||||
self.TargetSetUnit:Remove( EventData.IniUnitName )
|
||||
self:Message( "You have destroyed a target. Your group assigned with task " .. self.TaskName .. " has " .. self.TargetSetUnit:Count() .. " targets ( " .. self.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." )
|
||||
|
||||
local PlayerName = ProcessClient:GetPlayerName()
|
||||
Task.TargetSetUnit:Remove( EventData.IniUnitName )
|
||||
|
||||
local MessageText = "You have destroyed a target.\nYour group assigned with task " .. self.TaskName .. " has\n" .. Task.TargetSetUnit:Count() .. " targets ( " .. Task.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed."
|
||||
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
|
||||
|
||||
local PlayerName = ProcessUnit:GetPlayerName()
|
||||
Task:AddProgress( PlayerName, "Destroyed " .. EventData.IniTypeName, timer.getTime(), 1 )
|
||||
|
||||
if self.TargetSetUnit:Count() > 0 then
|
||||
if Task.TargetSetUnit:Count() > 0 then
|
||||
self:__More( 1 )
|
||||
else
|
||||
self:__NoMore( 1 )
|
||||
@ -250,20 +249,22 @@ do -- ACT_ACCOUNT_DEADS
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ACCOUNT_DEADS self
|
||||
-- @param Wrapper.Client#CLIENT ProcessClient
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param Tasking.Task#TASK Task
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function ACT_ACCOUNT_DEADS:onenterAccountForOther( ProcessClient, Task, From, Event, To, EventData )
|
||||
self:T( { ProcessClient:GetName(), Task:GetName(), From, Event, To, EventData } )
|
||||
function ACT_ACCOUNT_DEADS:onenterAccountForOther( ProcessUnit, Task, From, Event, To, EventData )
|
||||
self:T( { ProcessUnit:GetName(), Task:GetName(), From, Event, To, EventData } )
|
||||
|
||||
local TaskGroup = ProcessClient:GetGroup()
|
||||
self.TargetSetUnit:Remove( EventData.IniUnitName )
|
||||
self:Message( "One of the task targets has been destroyed. Your group assigned with task " .. self.TaskName .. " has " .. self.TargetSetUnit:Count() .. " targets ( " .. self.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed." )
|
||||
local TaskGroup = ProcessUnit:GetGroup()
|
||||
Task.TargetSetUnit:Remove( EventData.IniUnitName )
|
||||
|
||||
local MessageText = "One of the task targets has been destroyed.\nYour group assigned with task " .. self.TaskName .. " has\n" .. Task.TargetSetUnit:Count() .. " targets ( " .. Task.TargetSetUnit:GetUnitTypesText() .. " ) left to be destroyed."
|
||||
self:GetCommandCenter():MessageTypeToGroup( MessageText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
|
||||
|
||||
if self.TargetSetUnit:Count() > 0 then
|
||||
if Task.TargetSetUnit:Count() > 0 then
|
||||
self:__More( 1 )
|
||||
else
|
||||
self:__NoMore( 1 )
|
||||
|
||||
@ -229,14 +229,14 @@ do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ASSIGN_MENU_ACCEPT:onafterStart( ProcessUnit, From, Event, To )
|
||||
self:E( { ProcessUnit, From, Event, To } )
|
||||
|
||||
self:Message( "Access the radio menu to accept the task. You have 30 seconds or the assignment will be cancelled." )
|
||||
self:GetCommandCenter():MessageTypeToGroup( "Access the radio menu to accept the task. You have 30 seconds or the assignment will be cancelled.", ProcessUnit:GetGroup(), MESSAGE.Type.Information )
|
||||
|
||||
local ProcessGroup = ProcessUnit:GetGroup()
|
||||
|
||||
@ -263,7 +263,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
@ -275,7 +275,7 @@ do -- ACT_ASSIGN_MENU_ACCEPT
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ASSIGN_MENU_ACCEPT self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
|
||||
@ -154,7 +154,7 @@ do -- ACT_ROUTE
|
||||
--- Get the routing text to be displayed.
|
||||
-- The route mode determines the text displayed.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
-- @param Wrapper.Unit#UNIT Controllable
|
||||
-- @return #string
|
||||
function ACT_ROUTE:GetRouteText( Controllable )
|
||||
|
||||
@ -174,6 +174,7 @@ do -- ACT_ROUTE
|
||||
end
|
||||
|
||||
|
||||
local Task = self:GetTask() -- This is to dermine that the coordinates are for a specific task mode (A2A or A2G).
|
||||
local CC = self:GetTask():GetMission():GetCommandCenter()
|
||||
if CC then
|
||||
if CC:IsModeWWII() then
|
||||
@ -198,7 +199,7 @@ do -- ACT_ROUTE
|
||||
RouteText = Coordinate:ToStringFromRP( ShortestReferencePoint, ShortestReferenceName, Controllable )
|
||||
end
|
||||
else
|
||||
RouteText = Coordinate:ToString( Controllable )
|
||||
RouteText = Coordinate:ToString( Controllable, nil, Task )
|
||||
end
|
||||
end
|
||||
|
||||
@ -214,7 +215,7 @@ do -- ACT_ROUTE
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
@ -226,7 +227,7 @@ do -- ACT_ROUTE
|
||||
|
||||
--- Check if the controllable has arrived.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @return #boolean
|
||||
function ACT_ROUTE:onfuncHasArrived( ProcessUnit )
|
||||
return false
|
||||
@ -234,7 +235,7 @@ do -- ACT_ROUTE
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
@ -351,7 +352,7 @@ do -- ACT_ROUTE_POINT
|
||||
|
||||
--- Method override to check if the controllable has arrived.
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @return #boolean
|
||||
function ACT_ROUTE_POINT:onfuncHasArrived( ProcessUnit )
|
||||
|
||||
@ -360,7 +361,7 @@ do -- ACT_ROUTE_POINT
|
||||
|
||||
if Distance <= self.Range then
|
||||
local RouteText = "You have arrived."
|
||||
self:Message( RouteText )
|
||||
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
|
||||
return true
|
||||
end
|
||||
end
|
||||
@ -372,14 +373,15 @@ do -- ACT_ROUTE_POINT
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ROUTE_POINT self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function ACT_ROUTE_POINT:onafterReport( ProcessUnit, From, Event, To )
|
||||
|
||||
local RouteText = self:GetRouteText( ProcessUnit )
|
||||
self:Message( RouteText )
|
||||
|
||||
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Update )
|
||||
end
|
||||
|
||||
end -- ACT_ROUTE_POINT
|
||||
@ -444,13 +446,13 @@ do -- ACT_ROUTE_ZONE
|
||||
|
||||
--- Method override to check if the controllable has arrived.
|
||||
-- @param #ACT_ROUTE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @return #boolean
|
||||
function ACT_ROUTE_ZONE:onfuncHasArrived( ProcessUnit )
|
||||
|
||||
if ProcessUnit:IsInZone( self.Zone ) then
|
||||
local RouteText = "You have arrived within the zone."
|
||||
self:Message( RouteText )
|
||||
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Information )
|
||||
end
|
||||
|
||||
return ProcessUnit:IsInZone( self.Zone )
|
||||
@ -460,7 +462,7 @@ do -- ACT_ROUTE_ZONE
|
||||
|
||||
--- StateMachine callback function
|
||||
-- @param #ACT_ROUTE_ZONE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE ProcessUnit
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @param #string Event
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
@ -468,7 +470,7 @@ do -- ACT_ROUTE_ZONE
|
||||
self:E( { ProcessUnit = ProcessUnit } )
|
||||
|
||||
local RouteText = self:GetRouteText( ProcessUnit )
|
||||
self:Message( RouteText )
|
||||
self:GetCommandCenter():MessageTypeToGroup( RouteText, ProcessUnit:GetGroup(), MESSAGE.Type.Update )
|
||||
end
|
||||
|
||||
end -- ACT_ROUTE_ZONE
|
||||
|
||||
@ -4,10 +4,6 @@
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- The @{#BASE} class is the core root class from where every other class in moose is derived.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **Sven Van de Velde (FlightControl)**
|
||||
-- ### Contributions:
|
||||
--
|
||||
@ -202,7 +198,15 @@ BASE = {
|
||||
ClassID = 0,
|
||||
Events = {},
|
||||
States = {},
|
||||
_ = {},
|
||||
}
|
||||
|
||||
|
||||
--- @field #BASE.__
|
||||
BASE.__ = {}
|
||||
|
||||
--- @field #BASE._
|
||||
BASE._ = {
|
||||
Schedules = {} --- Contains the Schedulers Active
|
||||
}
|
||||
|
||||
--- The Formation Class
|
||||
@ -228,47 +232,19 @@ FORMATION = {
|
||||
-- @return #BASE
|
||||
function BASE:New()
|
||||
local self = routines.utils.deepCopy( self ) -- Create a new self instance
|
||||
local MetaTable = {}
|
||||
setmetatable( self, MetaTable )
|
||||
self.__index = self
|
||||
|
||||
_ClassID = _ClassID + 1
|
||||
self.ClassID = _ClassID
|
||||
|
||||
|
||||
-- This is for "private" methods...
|
||||
-- When a __ is passed to a method as "self", the __index will search for the method on the public method list too!
|
||||
-- if rawget( self, "__" ) then
|
||||
--setmetatable( self, { __index = self.__ } )
|
||||
-- end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function BASE:_Destructor()
|
||||
--self:E("_Destructor")
|
||||
|
||||
--self:EventRemoveAll()
|
||||
end
|
||||
|
||||
|
||||
-- THIS IS WHY WE NEED LUA 5.2 ...
|
||||
function BASE:_SetDestructor()
|
||||
|
||||
-- TODO: Okay, this is really technical...
|
||||
-- When you set a proxy to a table to catch __gc, weak tables don't behave like weak...
|
||||
-- Therefore, I am parking this logic until I've properly discussed all this with the community.
|
||||
|
||||
local proxy = newproxy(true)
|
||||
local proxyMeta = getmetatable(proxy)
|
||||
|
||||
proxyMeta.__gc = function ()
|
||||
env.info("In __gc for " .. self:GetClassNameAndID() )
|
||||
if self._Destructor then
|
||||
self:_Destructor()
|
||||
end
|
||||
end
|
||||
|
||||
-- keep the userdata from newproxy reachable until the object
|
||||
-- table is about to be garbage-collected - then the __gc hook
|
||||
-- will be invoked and the destructor called
|
||||
rawset( self, '__proxy', proxy )
|
||||
|
||||
end
|
||||
|
||||
--- This is the worker method to inherit from a parent class.
|
||||
-- @param #BASE self
|
||||
-- @param Child is the Child class that inherits.
|
||||
@ -276,18 +252,40 @@ end
|
||||
-- @return #BASE Child
|
||||
function BASE:Inherit( Child, Parent )
|
||||
local Child = routines.utils.deepCopy( Child )
|
||||
--local Parent = routines.utils.deepCopy( Parent )
|
||||
--local Parent = Parent
|
||||
|
||||
if Child ~= nil then
|
||||
setmetatable( Child, Parent )
|
||||
Child.__index = Child
|
||||
|
||||
|
||||
-- This is for "private" methods...
|
||||
-- When a __ is passed to a method as "self", the __index will search for the method on the public method list of the same object too!
|
||||
if rawget( Child, "__" ) then
|
||||
setmetatable( Child, { __index = Child.__ } )
|
||||
setmetatable( Child.__, { __index = Parent } )
|
||||
else
|
||||
setmetatable( Child, { __index = Parent } )
|
||||
end
|
||||
|
||||
--Child:_SetDestructor()
|
||||
end
|
||||
--self:T( 'Inherited from ' .. Parent.ClassName )
|
||||
return Child
|
||||
end
|
||||
|
||||
|
||||
local function getParent( Child )
|
||||
local Parent = nil
|
||||
|
||||
if Child.ClassName == 'BASE' then
|
||||
Parent = nil
|
||||
else
|
||||
if rawget( Child, "__" ) then
|
||||
Parent = getmetatable( Child.__ ).__index
|
||||
else
|
||||
Parent = getmetatable( Child ).__index
|
||||
end
|
||||
end
|
||||
return Parent
|
||||
end
|
||||
|
||||
|
||||
--- This is the worker method to retrieve the Parent class.
|
||||
-- Note that the Parent class must be passed to call the parent class method.
|
||||
--
|
||||
@ -297,12 +295,91 @@ end
|
||||
-- @param #BASE self
|
||||
-- @param #BASE Child is the Child class from which the Parent class needs to be retrieved.
|
||||
-- @return #BASE
|
||||
function BASE:GetParent( Child )
|
||||
local Parent = getmetatable( Child )
|
||||
-- env.info('Inherited class of ' .. Child.ClassName .. ' is ' .. Parent.ClassName )
|
||||
return Parent
|
||||
function BASE:GetParent( Child, FromClass )
|
||||
|
||||
|
||||
local Parent
|
||||
-- BASE class has no parent
|
||||
if Child.ClassName == 'BASE' then
|
||||
Parent = nil
|
||||
else
|
||||
|
||||
self:E({FromClass = FromClass})
|
||||
self:E({Child = Child.ClassName})
|
||||
if FromClass then
|
||||
while( Child.ClassName ~= "BASE" and Child.ClassName ~= FromClass.ClassName ) do
|
||||
Child = getParent( Child )
|
||||
self:E({Child.ClassName})
|
||||
end
|
||||
end
|
||||
if Child.ClassName == 'BASE' then
|
||||
Parent = nil
|
||||
else
|
||||
Parent = getParent( Child )
|
||||
end
|
||||
end
|
||||
self:E({Parent.ClassName})
|
||||
return Parent
|
||||
end
|
||||
|
||||
--- This is the worker method to check if an object is an (sub)instance of a class.
|
||||
--
|
||||
-- ### Examples:
|
||||
--
|
||||
-- * ZONE:New( 'some zone' ):IsInstanceOf( ZONE ) will return true
|
||||
-- * ZONE:New( 'some zone' ):IsInstanceOf( 'ZONE' ) will return true
|
||||
-- * ZONE:New( 'some zone' ):IsInstanceOf( 'zone' ) will return true
|
||||
-- * ZONE:New( 'some zone' ):IsInstanceOf( 'BASE' ) will return true
|
||||
--
|
||||
-- * ZONE:New( 'some zone' ):IsInstanceOf( 'GROUP' ) will return false
|
||||
--
|
||||
-- @param #BASE self
|
||||
-- @param ClassName is the name of the class or the class itself to run the check against
|
||||
-- @return #boolean
|
||||
function BASE:IsInstanceOf( ClassName )
|
||||
|
||||
-- Is className NOT a string ?
|
||||
if type( ClassName ) ~= 'string' then
|
||||
|
||||
-- Is className a Moose class ?
|
||||
if type( ClassName ) == 'table' and ClassName.ClassName ~= nil then
|
||||
|
||||
-- Get the name of the Moose class as a string
|
||||
ClassName = ClassName.ClassName
|
||||
|
||||
-- className is neither a string nor a Moose class, throw an error
|
||||
else
|
||||
|
||||
-- I'm not sure if this should take advantage of MOOSE logging function, or throw an error for pcall
|
||||
local err_str = 'className parameter should be a string; parameter received: '..type( ClassName )
|
||||
self:E( err_str )
|
||||
-- error( err_str )
|
||||
return false
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
ClassName = string.upper( ClassName )
|
||||
|
||||
if string.upper( self.ClassName ) == ClassName then
|
||||
return true
|
||||
end
|
||||
|
||||
local Parent = getParent(self)
|
||||
|
||||
while Parent do
|
||||
|
||||
if string.upper( Parent.ClassName ) == ClassName then
|
||||
return true
|
||||
end
|
||||
|
||||
Parent = getParent( Parent )
|
||||
|
||||
end
|
||||
|
||||
return false
|
||||
|
||||
end
|
||||
--- Get the ClassName + ClassID of the class instance.
|
||||
-- The ClassName + ClassID is formatted as '%s#%09d'.
|
||||
-- @param #BASE self
|
||||
@ -383,7 +460,7 @@ do -- Event Handling
|
||||
-- @return #BASE
|
||||
function BASE:UnHandleEvent( Event )
|
||||
|
||||
self:EventDispatcher():Remove( self, Event )
|
||||
self:EventDispatcher():RemoveEvent( self, Event )
|
||||
|
||||
return self
|
||||
end
|
||||
@ -566,6 +643,22 @@ function BASE:CreateEventCrash( EventTime, Initiator )
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
--- Creation of a Takeoff Event.
|
||||
-- @param #BASE self
|
||||
-- @param Dcs.DCSTypes#Time EventTime The time stamp of the event.
|
||||
-- @param Dcs.DCSWrapper.Object#Object Initiator The initiating object of the event.
|
||||
function BASE:CreateEventTakeoff( EventTime, Initiator )
|
||||
self:F( { EventTime, Initiator } )
|
||||
|
||||
local Event = {
|
||||
id = world.event.S_EVENT_TAKEOFF,
|
||||
time = EventTime,
|
||||
initiator = Initiator,
|
||||
}
|
||||
|
||||
world.onEvent( Event )
|
||||
end
|
||||
|
||||
-- TODO: Complete Dcs.DCSTypes#Event structure.
|
||||
--- The main event handling function... This function captures all events generated for the class.
|
||||
-- @param #BASE self
|
||||
@ -596,6 +689,86 @@ function BASE:onEvent(event)
|
||||
end
|
||||
end
|
||||
|
||||
do -- Scheduling
|
||||
|
||||
--- Schedule a new time event. Note that the schedule will only take place if the scheduler is *started*. Even for a single schedule event, the scheduler needs to be started also.
|
||||
-- @param #BASE self
|
||||
-- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called.
|
||||
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
|
||||
-- @param #table ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
|
||||
-- @return #number The ScheduleID of the planned schedule.
|
||||
function BASE:ScheduleOnce( Start, SchedulerFunction, ... )
|
||||
self:F2( { Start } )
|
||||
self:T3( { ... } )
|
||||
|
||||
local ObjectName = "-"
|
||||
ObjectName = self.ClassName .. self.ClassID
|
||||
|
||||
self:F3( { "ScheduleOnce: ", ObjectName, Start } )
|
||||
self.SchedulerObject = self
|
||||
|
||||
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
|
||||
self,
|
||||
SchedulerFunction,
|
||||
{ ... },
|
||||
Start,
|
||||
nil,
|
||||
nil,
|
||||
nil
|
||||
)
|
||||
|
||||
self._.Schedules[#self.Schedules+1] = ScheduleID
|
||||
|
||||
return self._.Schedules
|
||||
end
|
||||
|
||||
--- Schedule a new time event. Note that the schedule will only take place if the scheduler is *started*. Even for a single schedule event, the scheduler needs to be started also.
|
||||
-- @param #BASE self
|
||||
-- @param #number Start Specifies the amount of seconds that will be waited before the scheduling is started, and the event function is called.
|
||||
-- @param #number Repeat Specifies the interval in seconds when the scheduler will call the event function.
|
||||
-- @param #number RandomizeFactor Specifies a randomization factor between 0 and 1 to randomize the Repeat.
|
||||
-- @param #number Stop Specifies the amount of seconds when the scheduler will be stopped.
|
||||
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
|
||||
-- @param #table ... Optional arguments that can be given as part of scheduler. The arguments need to be given as a table { param1, param 2, ... }.
|
||||
-- @return #number The ScheduleID of the planned schedule.
|
||||
function BASE:ScheduleRepeat( Start, Repeat, RandomizeFactor, Stop, SchedulerFunction, ... )
|
||||
self:F2( { Start } )
|
||||
self:T3( { ... } )
|
||||
|
||||
local ObjectName = "-"
|
||||
ObjectName = self.ClassName .. self.ClassID
|
||||
|
||||
self:F3( { "ScheduleRepeat: ", ObjectName, Start, Repeat, RandomizeFactor, Stop } )
|
||||
self.SchedulerObject = self
|
||||
|
||||
local ScheduleID = _SCHEDULEDISPATCHER:AddSchedule(
|
||||
self,
|
||||
SchedulerFunction,
|
||||
{ ... },
|
||||
Start,
|
||||
Repeat,
|
||||
RandomizeFactor,
|
||||
Stop
|
||||
)
|
||||
|
||||
self._.Schedules[SchedulerFunction] = ScheduleID
|
||||
|
||||
return self._.Schedules
|
||||
end
|
||||
|
||||
--- Stops the Schedule.
|
||||
-- @param #BASE self
|
||||
-- @param #function SchedulerFunction The event function to be called when a timer event occurs. The event function needs to accept the parameters specified in SchedulerArguments.
|
||||
function BASE:ScheduleStop( SchedulerFunction )
|
||||
|
||||
self:F3( { "ScheduleStop:" } )
|
||||
|
||||
_SCHEDULEDISPATCHER:Stop( self, self._.Schedules[SchedulerFunction] )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Set a state or property of the Object given a Key and a Value.
|
||||
-- Note that if the Object is destroyed, nillified or garbage collected, then the Values and Keys will also be gone.
|
||||
-- @param #BASE self
|
||||
@ -610,7 +783,6 @@ function BASE:SetState( Object, Key, Value )
|
||||
|
||||
self.States[ClassNameAndID] = self.States[ClassNameAndID] or {}
|
||||
self.States[ClassNameAndID][Key] = Value
|
||||
self:T2( { ClassNameAndID, Key, Value } )
|
||||
|
||||
return self.States[ClassNameAndID][Key]
|
||||
end
|
||||
@ -628,7 +800,6 @@ function BASE:GetState( Object, Key )
|
||||
|
||||
if self.States[ClassNameAndID] then
|
||||
local Value = self.States[ClassNameAndID][Key] or false
|
||||
self:T2( { ClassNameAndID, Key, Value } )
|
||||
return Value
|
||||
end
|
||||
|
||||
@ -899,3 +1070,35 @@ end
|
||||
|
||||
|
||||
|
||||
--- old stuff
|
||||
|
||||
--function BASE:_Destructor()
|
||||
-- --self:E("_Destructor")
|
||||
--
|
||||
-- --self:EventRemoveAll()
|
||||
--end
|
||||
|
||||
|
||||
-- THIS IS WHY WE NEED LUA 5.2 ...
|
||||
--function BASE:_SetDestructor()
|
||||
--
|
||||
-- -- TODO: Okay, this is really technical...
|
||||
-- -- When you set a proxy to a table to catch __gc, weak tables don't behave like weak...
|
||||
-- -- Therefore, I am parking this logic until I've properly discussed all this with the community.
|
||||
--
|
||||
-- local proxy = newproxy(true)
|
||||
-- local proxyMeta = getmetatable(proxy)
|
||||
--
|
||||
-- proxyMeta.__gc = function ()
|
||||
-- env.info("In __gc for " .. self:GetClassNameAndID() )
|
||||
-- if self._Destructor then
|
||||
-- self:_Destructor()
|
||||
-- end
|
||||
-- end
|
||||
--
|
||||
-- -- keep the userdata from newproxy reachable until the object
|
||||
-- -- table is about to be garbage-collected - then the __gc hook
|
||||
-- -- will be invoked and the destructor called
|
||||
-- rawset( self, '__proxy', proxy )
|
||||
--
|
||||
--end
|
||||
File diff suppressed because it is too large
Load Diff
@ -2,8 +2,19 @@
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- 1) @{#DATABASE} class, extends @{Base#BASE}
|
||||
-- ===================================================
|
||||
-- ### Author: **Sven Van de Velde (FlightControl)**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- @module Database
|
||||
|
||||
|
||||
--- @type DATABASE
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- # DATABASE class, extends @{Base#BASE}
|
||||
--
|
||||
-- Mission designers can use the DATABASE class to refer to:
|
||||
--
|
||||
-- * STATICS
|
||||
@ -17,35 +28,10 @@
|
||||
--
|
||||
-- On top, for internal MOOSE administration purposes, the DATBASE administers the Unit and Group TEMPLATES as defined within the Mission Editor.
|
||||
--
|
||||
-- Moose will automatically create one instance of the DATABASE class into the **global** object _DATABASE.
|
||||
-- Moose refers to _DATABASE within the framework extensively, but you can also refer to the _DATABASE object within your missions if required.
|
||||
-- The singleton object **_DATABASE** is automatically created by MOOSE, that administers all objects within the mission.
|
||||
-- Moose refers to **_DATABASE** within the framework extensively, but you can also refer to the _DATABASE object within your missions if required.
|
||||
--
|
||||
-- 1.1) DATABASE iterators
|
||||
-- -----------------------
|
||||
-- You can iterate the database with the available iterator methods.
|
||||
-- The iterator methods will walk the DATABASE set, and call for each element within the set a function that you provide.
|
||||
-- The following iterator methods are currently available within the DATABASE:
|
||||
--
|
||||
-- * @{#DATABASE.ForEachUnit}: Calls a function for each @{UNIT} it finds within the DATABASE.
|
||||
-- * @{#DATABASE.ForEachGroup}: Calls a function for each @{GROUP} it finds within the DATABASE.
|
||||
-- * @{#DATABASE.ForEachPlayer}: Calls a function for each alive player it finds within the DATABASE.
|
||||
-- * @{#DATABASE.ForEachPlayerJoined}: Calls a function for each joined player it finds within the DATABASE.
|
||||
-- * @{#DATABASE.ForEachClient}: Calls a function for each @{CLIENT} it finds within the DATABASE.
|
||||
-- * @{#DATABASE.ForEachClientAlive}: Calls a function for each alive @{CLIENT} it finds within the DATABASE.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
--
|
||||
-- ### Author: **Sven Van de Velde (FlightControl)**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ====
|
||||
-- @module Database
|
||||
|
||||
|
||||
--- DATABASE class
|
||||
-- @type DATABASE
|
||||
-- @extends Core.Base#BASE
|
||||
-- @field #DATABASE
|
||||
DATABASE = {
|
||||
ClassName = "DATABASE",
|
||||
Templates = {
|
||||
@ -70,6 +56,8 @@ DATABASE = {
|
||||
NavPoints = {},
|
||||
PLAYERSETTINGS = {},
|
||||
ZONENAMES = {},
|
||||
HITS = {},
|
||||
DESTROYS = {},
|
||||
}
|
||||
|
||||
local _DATABASECoalition =
|
||||
@ -104,6 +92,7 @@ function DATABASE:New()
|
||||
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
|
||||
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
||||
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
||||
self:HandleEvent( EVENTS.Hit, self.AccountHits )
|
||||
self:HandleEvent( EVENTS.NewCargo )
|
||||
self:HandleEvent( EVENTS.DeleteCargo )
|
||||
|
||||
@ -214,6 +203,16 @@ function DATABASE:FindStatic( StaticName )
|
||||
return StaticFound
|
||||
end
|
||||
|
||||
--- Finds a AIRBASE based on the AirbaseName.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string AirbaseName
|
||||
-- @return Wrapper.Airbase#AIRBASE The found AIRBASE.
|
||||
function DATABASE:FindAirbase( AirbaseName )
|
||||
|
||||
local AirbaseFound = self.AIRBASES[AirbaseName]
|
||||
return AirbaseFound
|
||||
end
|
||||
|
||||
--- Adds a Airbase based on the Airbase Name in the DATABASE.
|
||||
-- @param #DATABASE self
|
||||
-- @param #string AirbaseName The name of the airbase
|
||||
@ -375,7 +374,12 @@ function DATABASE:Spawn( SpawnTemplate )
|
||||
SpawnTemplate.CountryID = SpawnCountryID
|
||||
SpawnTemplate.CategoryID = SpawnCategoryID
|
||||
|
||||
-- Ensure that for the spawned group and its units, there are GROUP and UNIT objects created in the DATABASE.
|
||||
local SpawnGroup = self:AddGroup( SpawnTemplate.name )
|
||||
for UnitID, UnitData in pairs( SpawnTemplate.units ) do
|
||||
self:AddUnit( UnitData.name )
|
||||
end
|
||||
|
||||
return SpawnGroup
|
||||
end
|
||||
|
||||
@ -400,10 +404,13 @@ end
|
||||
--- Private method that registers new Group Templates within the DATABASE Object.
|
||||
-- @param #DATABASE self
|
||||
-- @param #table GroupTemplate
|
||||
-- @param Dcs.DCScoalition#coalition.side CoalitionSide The coalition.side of the object.
|
||||
-- @param Dcs.DCSObject#Object.Category CategoryID The Object.category of the object.
|
||||
-- @param Dcs.DCScountry#country.id CountryID the country.id of the object
|
||||
-- @return #DATABASE self
|
||||
function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionID, CategoryID, CountryID )
|
||||
function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID, GroupName )
|
||||
|
||||
local GroupTemplateName = env.getValueDictByKey(GroupTemplate.name)
|
||||
local GroupTemplateName = GroupName or env.getValueDictByKey( GroupTemplate.name )
|
||||
|
||||
local TraceTable = {}
|
||||
|
||||
@ -418,7 +425,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionID, CategoryID
|
||||
end
|
||||
|
||||
GroupTemplate.CategoryID = CategoryID
|
||||
GroupTemplate.CoalitionID = CoalitionID
|
||||
GroupTemplate.CoalitionID = CoalitionSide
|
||||
GroupTemplate.CountryID = CountryID
|
||||
|
||||
self.Templates.Groups[GroupTemplateName].GroupName = GroupTemplateName
|
||||
@ -427,7 +434,7 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionID, CategoryID
|
||||
self.Templates.Groups[GroupTemplateName].UnitCount = #GroupTemplate.units
|
||||
self.Templates.Groups[GroupTemplateName].Units = GroupTemplate.units
|
||||
self.Templates.Groups[GroupTemplateName].CategoryID = CategoryID
|
||||
self.Templates.Groups[GroupTemplateName].CoalitionID = CoalitionID
|
||||
self.Templates.Groups[GroupTemplateName].CoalitionID = CoalitionSide
|
||||
self.Templates.Groups[GroupTemplateName].CountryID = CountryID
|
||||
|
||||
|
||||
@ -454,13 +461,13 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionID, CategoryID
|
||||
self.Templates.Units[UnitTemplate.name].GroupTemplate = GroupTemplate
|
||||
self.Templates.Units[UnitTemplate.name].GroupId = GroupTemplate.groupId
|
||||
self.Templates.Units[UnitTemplate.name].CategoryID = CategoryID
|
||||
self.Templates.Units[UnitTemplate.name].CoalitionID = CoalitionID
|
||||
self.Templates.Units[UnitTemplate.name].CoalitionID = CoalitionSide
|
||||
self.Templates.Units[UnitTemplate.name].CountryID = CountryID
|
||||
|
||||
if UnitTemplate.skill and (UnitTemplate.skill == "Client" or UnitTemplate.skill == "Player") then
|
||||
self.Templates.ClientsByName[UnitTemplate.name] = UnitTemplate
|
||||
self.Templates.ClientsByName[UnitTemplate.name].CategoryID = CategoryID
|
||||
self.Templates.ClientsByName[UnitTemplate.name].CoalitionID = CoalitionID
|
||||
self.Templates.ClientsByName[UnitTemplate.name].CoalitionID = CoalitionSide
|
||||
self.Templates.ClientsByName[UnitTemplate.name].CountryID = CountryID
|
||||
self.Templates.ClientsByID[UnitTemplate.unitId] = UnitTemplate
|
||||
end
|
||||
@ -504,7 +511,7 @@ function DATABASE:_RegisterStaticTemplate( StaticTemplate, CoalitionID, Category
|
||||
|
||||
|
||||
TraceTable[#TraceTable+1] = "Static"
|
||||
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].GroupName
|
||||
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].StaticName
|
||||
|
||||
TraceTable[#TraceTable+1] = "Coalition"
|
||||
TraceTable[#TraceTable+1] = self.Templates.Statics[StaticTemplateName].CoalitionID
|
||||
@ -631,6 +638,7 @@ end
|
||||
function DATABASE:_RegisterStatics()
|
||||
|
||||
local CoalitionsData = { GroupsRed = coalition.getStaticObjects( coalition.side.RED ), GroupsBlue = coalition.getStaticObjects( coalition.side.BLUE ) }
|
||||
self:E( { Statics = CoalitionsData } )
|
||||
for CoalitionId, CoalitionData in pairs( CoalitionsData ) do
|
||||
for DCSStaticId, DCSStatic in pairs( CoalitionData ) do
|
||||
|
||||
@ -707,6 +715,8 @@ function DATABASE:_EventOnDeadOrCrash( Event )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:AccountDestroys( Event )
|
||||
end
|
||||
|
||||
|
||||
@ -929,7 +939,7 @@ end
|
||||
-- @param #string PlayerName
|
||||
-- @return Core.Settings#SETTINGS
|
||||
function DATABASE:GetPlayerSettings( PlayerName )
|
||||
self:E({PlayerName})
|
||||
self:F2( { PlayerName } )
|
||||
return self.PLAYERSETTINGS[PlayerName]
|
||||
end
|
||||
|
||||
@ -940,7 +950,7 @@ end
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @return Core.Settings#SETTINGS
|
||||
function DATABASE:SetPlayerSettings( PlayerName, Settings )
|
||||
self:E({PlayerName, Settings})
|
||||
self:F2( { PlayerName, Settings } )
|
||||
self.PLAYERSETTINGS[PlayerName] = Settings
|
||||
end
|
||||
|
||||
@ -1038,6 +1048,101 @@ function DATABASE:_RegisterTemplates()
|
||||
return self
|
||||
end
|
||||
|
||||
--- Account the Hits of the Players.
|
||||
-- @param #DATABASE self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function DATABASE:AccountHits( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
if Event.IniPlayerName ~= nil then -- It is a player that is hitting something
|
||||
self:T( "Hitting Something" )
|
||||
|
||||
-- What is he hitting?
|
||||
if Event.TgtCategory then
|
||||
|
||||
-- A target got hit
|
||||
self.HITS[Event.TgtUnitName] = self.HITS[Event.TgtUnitName] or {}
|
||||
local Hit = self.HITS[Event.TgtUnitName]
|
||||
|
||||
Hit.Players = Hit.Players or {}
|
||||
Hit.Players[Event.IniPlayerName] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- It is a weapon initiated by a player, that is hitting something
|
||||
-- This seems to occur only with scenery and static objects.
|
||||
if Event.WeaponPlayerName ~= nil then
|
||||
self:T( "Hitting Scenery" )
|
||||
|
||||
-- What is he hitting?
|
||||
if Event.TgtCategory then
|
||||
|
||||
if Event.IniCoalition then -- A coalition object was hit, probably a static.
|
||||
-- A target got hit
|
||||
self.HITS[Event.TgtUnitName] = self.HITS[Event.TgtUnitName] or {}
|
||||
local Hit = self.HITS[Event.TgtUnitName]
|
||||
|
||||
Hit.Players = Hit.Players or {}
|
||||
Hit.Players[Event.WeaponPlayerName] = true
|
||||
else -- A scenery object was hit.
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Account the destroys.
|
||||
-- @param #DATABASE self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function DATABASE:AccountDestroys( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
local TargetUnit = nil
|
||||
local TargetGroup = nil
|
||||
local TargetUnitName = ""
|
||||
local TargetGroupName = ""
|
||||
local TargetPlayerName = ""
|
||||
local TargetCoalition = nil
|
||||
local TargetCategory = nil
|
||||
local TargetType = nil
|
||||
local TargetUnitCoalition = nil
|
||||
local TargetUnitCategory = nil
|
||||
local TargetUnitType = nil
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
|
||||
TargetUnit = Event.IniUnit
|
||||
TargetUnitName = Event.IniDCSUnitName
|
||||
TargetGroup = Event.IniDCSGroup
|
||||
TargetGroupName = Event.IniDCSGroupName
|
||||
TargetPlayerName = Event.IniPlayerName
|
||||
|
||||
TargetCoalition = Event.IniCoalition
|
||||
--TargetCategory = TargetUnit:getCategory()
|
||||
--TargetCategory = TargetUnit:getDesc().category -- Workaround
|
||||
TargetCategory = Event.IniCategory
|
||||
TargetType = Event.IniTypeName
|
||||
|
||||
TargetUnitType = TargetType
|
||||
|
||||
self:T( { TargetUnitName, TargetGroupName, TargetPlayerName, TargetCoalition, TargetCategory, TargetType } )
|
||||
end
|
||||
|
||||
self:T( "Something got destroyed" )
|
||||
|
||||
local Destroyed = false
|
||||
|
||||
-- What is the player destroying?
|
||||
if self.HITS[Event.IniUnitName] then -- Was there a hit for this unit for this player before registered???
|
||||
|
||||
|
||||
self.DESTROYS[Event.IniUnitName] = self.DESTROYS[Event.IniUnitName] or {}
|
||||
|
||||
self.DESTROYS[Event.IniUnitName] = true
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -450,16 +450,16 @@ end
|
||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
||||
-- @param Dcs.DCSWorld#world.event EventID
|
||||
-- @return #EVENT.Events
|
||||
function EVENT:Remove( EventClass, EventID )
|
||||
function EVENT:RemoveEvent( EventClass, EventID )
|
||||
|
||||
self:E( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } )
|
||||
self:F2( { "Removing subscription for class: ", EventClass:GetClassNameAndID() } )
|
||||
|
||||
local EventPriority = EventClass:GetEventPriority()
|
||||
|
||||
self.EventsDead = self.EventsDead or {}
|
||||
self.EventsDead[EventID] = self.EventsDead[EventID] or {}
|
||||
self.EventsDead[EventID][EventPriority] = self.EventsDead[EventID][EventPriority] or {}
|
||||
self.EventsDead[EventID][EventPriority][EventClass] = self.Events[EventID][EventPriority][EventClass]
|
||||
self.Events = self.Events or {}
|
||||
self.Events[EventID] = self.Events[EventID] or {}
|
||||
self.Events[EventID][EventPriority] = self.Events[EventID][EventPriority] or {}
|
||||
self.Events[EventID][EventPriority][EventClass] = self.Events[EventID][EventPriority][EventClass]
|
||||
|
||||
self.Events[EventID][EventPriority][EventClass] = nil
|
||||
|
||||
@ -561,12 +561,13 @@ end
|
||||
-- @param Core.Base#BASE EventClass The self instance of the class for which the event is.
|
||||
-- @param EventID
|
||||
-- @return #EVENT
|
||||
function EVENT:OnEventForGroup( GroupName, EventFunction, EventClass, EventID )
|
||||
self:F2( GroupName )
|
||||
function EVENT:OnEventForGroup( GroupName, EventFunction, EventClass, EventID, ... )
|
||||
self:E( GroupName )
|
||||
|
||||
local Event = self:Init( EventID, EventClass )
|
||||
Event.EventGroup = true
|
||||
Event.EventFunction = EventFunction
|
||||
Event.Params = arg
|
||||
return self
|
||||
end
|
||||
|
||||
@ -743,10 +744,15 @@ function EVENT:onEvent( Event )
|
||||
|
||||
local EventMeta = _EVENTMETA[Event.id]
|
||||
|
||||
if self and self.Events and self.Events[Event.id] then
|
||||
--self:E( { EventMeta.Text, Event } ) -- Activate the see all incoming events ...
|
||||
|
||||
if self and
|
||||
self.Events and
|
||||
self.Events[Event.id] and
|
||||
( Event.initiator ~= nil or ( Event.initiator == nil and Event.id ~= EVENTS.PlayerLeaveUnit ) ) then
|
||||
|
||||
if Event.initiator then
|
||||
|
||||
|
||||
Event.IniObjectCategory = Event.initiator:getCategory()
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.UNIT then
|
||||
@ -868,9 +874,9 @@ function EVENT:onEvent( Event )
|
||||
-- Okay, we got the event from DCS. Now loop the SORTED self.EventSorted[] table for the received Event.id, and for each EventData registered, check if a function needs to be called.
|
||||
for EventClass, EventData in pairs( self.Events[Event.id][EventPriority] ) do
|
||||
|
||||
if Event.IniObjectCategory ~= Object.Category.STATIC then
|
||||
--self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
|
||||
end
|
||||
--if Event.IniObjectCategory ~= Object.Category.STATIC then
|
||||
-- self:E( { "Evaluating: ", EventClass:GetClassNameAndID() } )
|
||||
--end
|
||||
|
||||
Event.IniGroup = GROUP:FindByName( Event.IniDCSGroupName )
|
||||
Event.TgtGroup = GROUP:FindByName( Event.TgtDCSGroupName )
|
||||
@ -920,7 +926,7 @@ function EVENT:onEvent( Event )
|
||||
end
|
||||
else
|
||||
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
|
||||
self:Remove( EventClass, Event.id )
|
||||
self:RemoveEvent( EventClass, Event.id )
|
||||
end
|
||||
else
|
||||
|
||||
@ -947,7 +953,7 @@ function EVENT:onEvent( Event )
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventData.EventFunction( EventClass, Event )
|
||||
return EventData.EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||
end, ErrorHandler )
|
||||
|
||||
else
|
||||
@ -963,14 +969,14 @@ function EVENT:onEvent( Event )
|
||||
|
||||
local Result, Value = xpcall(
|
||||
function()
|
||||
return EventFunction( EventClass, Event )
|
||||
return EventFunction( EventClass, Event, unpack( EventData.Params ) )
|
||||
end, ErrorHandler )
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
-- The EventClass is not alive anymore, we remove it from the EventHandlers...
|
||||
self:Remove( EventClass, Event.id )
|
||||
--self:RemoveEvent( EventClass, Event.id )
|
||||
end
|
||||
else
|
||||
|
||||
@ -1015,7 +1021,7 @@ function EVENT:onEvent( Event )
|
||||
end
|
||||
end
|
||||
else
|
||||
self:E( { EventMeta.Text, Event } )
|
||||
self:T( { EventMeta.Text, Event } )
|
||||
end
|
||||
|
||||
Event = nil
|
||||
|
||||
@ -418,7 +418,7 @@ do -- FSM
|
||||
-- @param #table ReturnEvents A table indicating for which returned events of the SubFSM which Event must be triggered in the FSM.
|
||||
-- @return Core.Fsm#FSM_PROCESS The SubFSM.
|
||||
function FSM:AddProcess( From, Event, Process, ReturnEvents )
|
||||
self:T( { From, Event, Process, ReturnEvents } )
|
||||
self:T( { From, Event } )
|
||||
|
||||
local Sub = {}
|
||||
Sub.From = From
|
||||
@ -541,7 +541,7 @@ do -- FSM
|
||||
end
|
||||
|
||||
function FSM:_submap( subs, sub, name )
|
||||
self:F( { sub = sub, name = name } )
|
||||
--self:F( { sub = sub, name = name } )
|
||||
subs[sub.From] = subs[sub.From] or {}
|
||||
subs[sub.From][sub.Event] = subs[sub.From][sub.Event] or {}
|
||||
|
||||
@ -844,7 +844,7 @@ do -- FSM_CONTROLLABLE
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE FSMControllable
|
||||
-- @return #FSM_CONTROLLABLE
|
||||
function FSM_CONTROLLABLE:SetControllable( FSMControllable )
|
||||
self:F( FSMControllable )
|
||||
--self:F( FSMControllable:GetName() )
|
||||
self.Controllable = FSMControllable
|
||||
end
|
||||
|
||||
@ -904,7 +904,7 @@ do -- FSM_PROCESS
|
||||
|
||||
local self = BASE:Inherit( self, FSM_CONTROLLABLE:New() ) -- Core.Fsm#FSM_PROCESS
|
||||
|
||||
self:F( Controllable, Task )
|
||||
--self:F( Controllable )
|
||||
|
||||
self:Assign( Controllable, Task )
|
||||
|
||||
@ -960,7 +960,7 @@ do -- FSM_PROCESS
|
||||
|
||||
-- Copy Processes
|
||||
for ProcessID, Process in pairs( self:GetProcesses() ) do
|
||||
self:E( { Process} )
|
||||
--self:E( { Process:GetName() } )
|
||||
local FsmProcess = NewFsm:AddProcess( Process.From, Process.Event, Process.fsm:Copy( Controllable, Task ), Process.ReturnEvents )
|
||||
end
|
||||
|
||||
@ -990,7 +990,6 @@ do -- FSM_PROCESS
|
||||
|
||||
-- Copy Processes
|
||||
for ProcessID, Process in pairs( self:GetProcesses() ) do
|
||||
self:E( { Process} )
|
||||
if Process.fsm then
|
||||
Process.fsm:Remove()
|
||||
Process.fsm = nil
|
||||
@ -1063,7 +1062,7 @@ end
|
||||
-- @param Wrapper.Unit#UNIT ProcessUnit
|
||||
-- @return #FSM_PROCESS self
|
||||
function FSM_PROCESS:Assign( ProcessUnit, Task )
|
||||
self:T( { Task, ProcessUnit } )
|
||||
--self:T( { Task:GetName(), ProcessUnit:GetName() } )
|
||||
|
||||
self:SetControllable( ProcessUnit )
|
||||
self:SetTask( Task )
|
||||
@ -1093,7 +1092,7 @@ end
|
||||
-- @param #string From
|
||||
-- @param #string To
|
||||
function FSM_PROCESS:onstatechange( ProcessUnit, Task, From, Event, To, Dummy )
|
||||
self:T( { ProcessUnit, From, Event, To, Dummy, self:IsTrace() } )
|
||||
self:T( { ProcessUnit:GetName(), From, Event, To, Dummy, self:IsTrace() } )
|
||||
|
||||
if self:IsTrace() then
|
||||
--MESSAGE:New( "@ Process " .. self:GetClassNameAndID() .. " : " .. Event .. " changed to state " .. To, 2 ):ToAll()
|
||||
|
||||
146
Moose Development/Moose/Core/Goal.lua
Normal file
146
Moose Development/Moose/Core/Goal.lua
Normal file
@ -0,0 +1,146 @@
|
||||
--- **Core (WIP)** -- Base class to allow the modeling of processes to achieve Goals.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- GOAL models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- ### Author: **Sven Van de Velde (FlightControl)**
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- @module Goal
|
||||
|
||||
do -- Goal
|
||||
|
||||
--- @type GOAL
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
|
||||
--- # GOAL class, extends @{Fsm#FSM}
|
||||
--
|
||||
-- GOAL models processes that have an objective with a defined achievement. Derived classes implement the ways how the achievements can be realized.
|
||||
--
|
||||
-- ## 1. GOAL constructor
|
||||
--
|
||||
-- * @{#GOAL.New}(): Creates a new GOAL object.
|
||||
--
|
||||
-- ## 2. GOAL is a finite state machine (FSM).
|
||||
--
|
||||
-- ### 2.1 GOAL States
|
||||
--
|
||||
-- * **Pending**: The goal object is in progress.
|
||||
-- * **Achieved**: The goal objective is Achieved.
|
||||
--
|
||||
-- ### 2.2 GOAL Events
|
||||
--
|
||||
-- * **Achieved**: Set the goal objective to Achieved.
|
||||
--
|
||||
-- @field #GOAL
|
||||
GOAL = {
|
||||
ClassName = "GOAL",
|
||||
}
|
||||
|
||||
--- @field #table GOAL.Players
|
||||
GOAL.Players = {}
|
||||
|
||||
--- @field #number GOAL.TotalContributions
|
||||
GOAL.TotalContributions = 0
|
||||
|
||||
--- GOAL Constructor.
|
||||
-- @param #GOAL self
|
||||
-- @return #GOAL
|
||||
function GOAL:New()
|
||||
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #GOAL
|
||||
self:F( {} )
|
||||
|
||||
--- Achieved State for GOAL
|
||||
-- @field GOAL.Achieved
|
||||
|
||||
--- Achieved State Handler OnLeave for GOAL
|
||||
-- @function [parent=#GOAL] OnLeaveAchieved
|
||||
-- @param #GOAL self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Achieved State Handler OnEnter for GOAL
|
||||
-- @function [parent=#GOAL] OnEnterAchieved
|
||||
-- @param #GOAL self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
|
||||
self:SetStartState( "Pending" )
|
||||
self:AddTransition( "*", "Achieved", "Achieved" )
|
||||
|
||||
--- Achieved Handler OnBefore for GOAL
|
||||
-- @function [parent=#GOAL] OnBeforeAchieved
|
||||
-- @param #GOAL self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Achieved Handler OnAfter for GOAL
|
||||
-- @function [parent=#GOAL] OnAfterAchieved
|
||||
-- @param #GOAL self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Achieved Trigger for GOAL
|
||||
-- @function [parent=#GOAL] Achieved
|
||||
-- @param #GOAL self
|
||||
|
||||
--- Achieved Asynchronous Trigger for GOAL
|
||||
-- @function [parent=#GOAL] __Achieved
|
||||
-- @param #GOAL self
|
||||
-- @param #number Delay
|
||||
|
||||
self:SetEventPriority( 5 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- @param #GOAL self
|
||||
-- @param #string PlayerName
|
||||
function GOAL:AddPlayerContribution( PlayerName )
|
||||
self.Players[PlayerName] = self.Players[PlayerName] or 0
|
||||
self.Players[PlayerName] = self.Players[PlayerName] + 1
|
||||
self.TotalContributions = self.TotalContributions + 1
|
||||
end
|
||||
|
||||
|
||||
--- @param #GOAL self
|
||||
-- @param #number Player contribution.
|
||||
function GOAL:GetPlayerContribution( PlayerName )
|
||||
return self.Players[PlayerName] or 0
|
||||
end
|
||||
|
||||
|
||||
--- @param #GOAL self
|
||||
function GOAL:GetPlayerContributions()
|
||||
return self.Players or {}
|
||||
end
|
||||
|
||||
|
||||
--- @param #GOAL self
|
||||
function GOAL:GetTotalContributions()
|
||||
return self.TotalContributions or 0
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @param #GOAL self
|
||||
-- @return #boolean true if the goal is Achieved
|
||||
function GOAL:IsAchieved()
|
||||
return self:Is( "Achieved" )
|
||||
end
|
||||
|
||||
end
|
||||
@ -103,6 +103,15 @@ do -- MENU_BASE
|
||||
return self
|
||||
end
|
||||
|
||||
--- Sets a tag for later selection of menu refresh.
|
||||
-- @param #MENU_BASE self
|
||||
-- @param #string MenuTag A Tag or Key that will filter only menu items set with this key.
|
||||
-- @return #MENU_BASE
|
||||
function MENU_BASE:SetTag( MenuTag )
|
||||
self.MenuTag = MenuTag
|
||||
return self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
do -- MENU_COMMAND_BASE
|
||||
@ -115,6 +124,7 @@ do -- MENU_COMMAND_BASE
|
||||
-- ----------------------------------------------------------
|
||||
-- The MENU_COMMAND_BASE class defines the main MENU class where other MENU COMMAND_
|
||||
-- classes are derived from, in order to set commands.
|
||||
--
|
||||
-- @field #MENU_COMMAND_BASE
|
||||
MENU_COMMAND_BASE = {
|
||||
ClassName = "MENU_COMMAND_BASE",
|
||||
@ -128,15 +138,51 @@ do -- MENU_COMMAND_BASE
|
||||
-- @return #MENU_COMMAND_BASE
|
||||
function MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, CommandMenuArguments )
|
||||
|
||||
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) )
|
||||
local self = BASE:Inherit( self, MENU_BASE:New( MenuText, ParentMenu ) ) -- #MENU_COMMAND_BASE
|
||||
|
||||
-- When a menu function goes into error, DCS displays an obscure menu message.
|
||||
-- This error handler catches the menu error and displays the full call stack.
|
||||
local ErrorHandler = function( errmsg )
|
||||
env.info( "MOOSE error in MENU COMMAND function: " .. errmsg )
|
||||
if debug ~= nil then
|
||||
env.info( debug.traceback() )
|
||||
end
|
||||
return errmsg
|
||||
end
|
||||
|
||||
self.CommandMenuFunction = CommandMenuFunction
|
||||
self.MenuCallHandler = function( CommandMenuArguments )
|
||||
self.CommandMenuFunction( unpack( CommandMenuArguments ) )
|
||||
self:SetCommandMenuFunction( CommandMenuFunction )
|
||||
self:SetCommandMenuArguments( CommandMenuArguments )
|
||||
self.MenuCallHandler = function()
|
||||
local function MenuFunction()
|
||||
return self.CommandMenuFunction( unpack( self.CommandMenuArguments ) )
|
||||
end
|
||||
local Status, Result = xpcall( MenuFunction, ErrorHandler )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- This sets the new command function of a menu,
|
||||
-- so that if a menu is regenerated, or if command function changes,
|
||||
-- that the function set for the menu is loosely coupled with the menu itself!!!
|
||||
-- If the function changes, no new menu needs to be generated if the menu text is the same!!!
|
||||
-- @param #MENU_COMMAND_BASE
|
||||
-- @return #MENU_COMMAND_BASE
|
||||
function MENU_COMMAND_BASE:SetCommandMenuFunction( CommandMenuFunction )
|
||||
self.CommandMenuFunction = CommandMenuFunction
|
||||
return self
|
||||
end
|
||||
|
||||
--- This sets the new command arguments of a menu,
|
||||
-- so that if a menu is regenerated, or if command arguments change,
|
||||
-- that the arguments set for the menu are loosely coupled with the menu itself!!!
|
||||
-- If the arguments change, no new menu needs to be generated if the menu text is the same!!!
|
||||
-- @param #MENU_COMMAND_BASE
|
||||
-- @return #MENU_COMMAND_BASE
|
||||
function MENU_COMMAND_BASE:SetCommandMenuArguments( CommandMenuArguments )
|
||||
self.CommandMenuArguments = CommandMenuArguments
|
||||
return self
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -247,7 +293,7 @@ do -- MENU_MISSION_COMMAND
|
||||
self:T( { MenuText, CommandMenuFunction, arg } )
|
||||
|
||||
|
||||
self.MenuPath = missionCommands.addCommand( MenuText, self.MenuParentPath, self.MenuCallHandler, arg )
|
||||
self.MenuPath = missionCommands.addCommand( MenuText, self.MenuParentPath, self.MenuCallHandler )
|
||||
|
||||
ParentMenu.Menus[self.MenuPath] = self
|
||||
|
||||
@ -420,7 +466,7 @@ do -- MENU_COALITION_COMMAND
|
||||
self:T( { MenuText, CommandMenuFunction, arg } )
|
||||
|
||||
|
||||
self.MenuPath = missionCommands.addCommandForCoalition( self.MenuCoalition, MenuText, self.MenuParentPath, self.MenuCallHandler, arg )
|
||||
self.MenuPath = missionCommands.addCommandForCoalition( self.MenuCoalition, MenuText, self.MenuParentPath, self.MenuCallHandler )
|
||||
|
||||
ParentMenu.Menus[self.MenuPath] = self
|
||||
|
||||
@ -653,7 +699,7 @@ do -- MENU_CLIENT
|
||||
missionCommands.removeItemForGroup( self.MenuClient:GetClientGroupID(), MenuPath[MenuPathID] )
|
||||
end
|
||||
|
||||
self.MenuPath = missionCommands.addCommandForGroup( self.MenuClient:GetClientGroupID(), MenuText, MenuParentPath, self.MenuCallHandler, arg )
|
||||
self.MenuPath = missionCommands.addCommandForGroup( self.MenuClient:GetClientGroupID(), MenuText, MenuParentPath, self.MenuCallHandler )
|
||||
MenuPath[MenuPathID] = self.MenuPath
|
||||
|
||||
if ParentMenu and ParentMenu.Menus then
|
||||
@ -805,13 +851,14 @@ do
|
||||
--- Removes the sub menus recursively of this MENU_GROUP.
|
||||
-- @param #MENU_GROUP self
|
||||
-- @param MenuTime
|
||||
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
||||
-- @return #MENU_GROUP self
|
||||
function MENU_GROUP:RemoveSubMenus( MenuTime )
|
||||
function MENU_GROUP:RemoveSubMenus( MenuTime, MenuTag )
|
||||
--self:F2( { self.MenuPath, MenuTime, self.MenuTime } )
|
||||
|
||||
--self:T( { "Removing Group SubMenus:", self.MenuGroup:GetName(), self.MenuPath } )
|
||||
self:T( { "Removing Group SubMenus:", MenuTime, MenuTag, self.MenuGroup:GetName(), self.MenuPath } )
|
||||
for MenuText, Menu in pairs( self.Menus ) do
|
||||
Menu:Remove( MenuTime )
|
||||
Menu:Remove( MenuTime, MenuTag )
|
||||
end
|
||||
|
||||
end
|
||||
@ -820,28 +867,31 @@ do
|
||||
--- Removes the main menu and sub menus recursively of this MENU_GROUP.
|
||||
-- @param #MENU_GROUP self
|
||||
-- @param MenuTime
|
||||
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
||||
-- @return #nil
|
||||
function MENU_GROUP:Remove( MenuTime )
|
||||
function MENU_GROUP:Remove( MenuTime, MenuTag )
|
||||
--self:F2( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } )
|
||||
|
||||
self:RemoveSubMenus( MenuTime )
|
||||
self:RemoveSubMenus( MenuTime, MenuTag )
|
||||
|
||||
if not MenuTime or self.MenuTime ~= MenuTime then
|
||||
if self.MenuGroup._Menus[self.Path] then
|
||||
self = self.MenuGroup._Menus[self.Path]
|
||||
|
||||
missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath )
|
||||
if self.ParentMenu then
|
||||
self.ParentMenu.Menus[self.MenuText] = nil
|
||||
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1
|
||||
if self.ParentMenu.MenuCount == 0 then
|
||||
if self.MenuRemoveParent == true then
|
||||
self:T2( "Removing Parent Menu " )
|
||||
self.ParentMenu:Remove()
|
||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
||||
if self.MenuGroup._Menus[self.Path] then
|
||||
self = self.MenuGroup._Menus[self.Path]
|
||||
|
||||
missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath )
|
||||
if self.ParentMenu then
|
||||
self.ParentMenu.Menus[self.MenuText] = nil
|
||||
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1
|
||||
if self.ParentMenu.MenuCount == 0 then
|
||||
if self.MenuRemoveParent == true then
|
||||
self:T2( "Removing Parent Menu " )
|
||||
self.ParentMenu:Remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self:T( { "Removing Group Menu:", MenuGroup = self.MenuGroup:GetName(), MenuPath = self.MenuGroup._Menus[self.Path].Path } )
|
||||
self:T( { "Removing Group Menu:", MenuGroup = self.MenuGroup:GetName() } )
|
||||
self.MenuGroup._Menus[self.Path] = nil
|
||||
self = nil
|
||||
end
|
||||
@ -852,7 +902,7 @@ do
|
||||
|
||||
|
||||
--- @type MENU_GROUP_COMMAND
|
||||
-- @extends Core.Menu#MENU_BASE
|
||||
-- @extends Core.Menu#MENU_COMMAND_BASE
|
||||
|
||||
--- # MENU_GROUP_COMMAND class, extends @{Menu#MENU_COMMAND_BASE}
|
||||
--
|
||||
@ -876,32 +926,37 @@ do
|
||||
function MENU_GROUP_COMMAND:New( MenuGroup, MenuText, ParentMenu, CommandMenuFunction, ... )
|
||||
|
||||
MenuGroup._Menus = MenuGroup._Menus or {}
|
||||
local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText
|
||||
local Path = ( ParentMenu and ( table.concat( ParentMenu.MenuPath or {}, "@" ) .. "@" .. MenuText ) ) or MenuText
|
||||
if MenuGroup._Menus[Path] then
|
||||
self = MenuGroup._Menus[Path]
|
||||
self:F2( { "Re-using Group Command Menu:", MenuGroup:GetName(), MenuText } )
|
||||
else
|
||||
self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) )
|
||||
|
||||
--if MenuGroup:IsAlive() then
|
||||
MenuGroup._Menus[Path] = self
|
||||
--end
|
||||
|
||||
self.Path = Path
|
||||
self.MenuGroup = MenuGroup
|
||||
self.MenuGroupID = MenuGroup:GetID()
|
||||
self.MenuText = MenuText
|
||||
self.ParentMenu = ParentMenu
|
||||
|
||||
self:F( { "Adding Group Command Menu:", MenuGroup = MenuGroup:GetName(), MenuText = MenuText, MenuPath = self.MenuParentPath } )
|
||||
self.MenuPath = missionCommands.addCommandForGroup( self.MenuGroupID, MenuText, self.MenuParentPath, self.MenuCallHandler, arg )
|
||||
|
||||
if self.ParentMenu and self.ParentMenu.Menus then
|
||||
self.ParentMenu.Menus[MenuText] = self
|
||||
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1
|
||||
self:F2( { ParentMenu.Menus, MenuText } )
|
||||
end
|
||||
--self:E( { Path=Path } )
|
||||
--self:E( { self.MenuTag, self.MenuTime, "Re-using Group Command Menu:", MenuGroup:GetName(), MenuText } )
|
||||
self:SetCommandMenuFunction( CommandMenuFunction )
|
||||
self:SetCommandMenuArguments( arg )
|
||||
return self
|
||||
end
|
||||
self = BASE:Inherit( self, MENU_COMMAND_BASE:New( MenuText, ParentMenu, CommandMenuFunction, arg ) )
|
||||
|
||||
--if MenuGroup:IsAlive() then
|
||||
MenuGroup._Menus[Path] = self
|
||||
--end
|
||||
|
||||
--self:E({Path=Path})
|
||||
self.Path = Path
|
||||
self.MenuGroup = MenuGroup
|
||||
self.MenuGroupID = MenuGroup:GetID()
|
||||
self.MenuText = MenuText
|
||||
self.ParentMenu = ParentMenu
|
||||
|
||||
self:F( { "Adding Group Command Menu:", MenuGroup = MenuGroup:GetName(), MenuText = MenuText, MenuPath = self.MenuParentPath } )
|
||||
self.MenuPath = missionCommands.addCommandForGroup( self.MenuGroupID, MenuText, self.MenuParentPath, self.MenuCallHandler )
|
||||
|
||||
if self.ParentMenu and self.ParentMenu.Menus then
|
||||
self.ParentMenu.Menus[MenuText] = self
|
||||
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount + 1
|
||||
self:F2( { ParentMenu.Menus, MenuText } )
|
||||
end
|
||||
-- end
|
||||
|
||||
return self
|
||||
end
|
||||
@ -909,28 +964,32 @@ do
|
||||
--- Removes a menu structure for a group.
|
||||
-- @param #MENU_GROUP_COMMAND self
|
||||
-- @param MenuTime
|
||||
-- @param MenuTag A Tag or Key to filter the menus to be refreshed with the Tag set.
|
||||
-- @return #nil
|
||||
function MENU_GROUP_COMMAND:Remove( MenuTime )
|
||||
function MENU_GROUP_COMMAND:Remove( MenuTime, MenuTag )
|
||||
--self:F2( { self.MenuGroupID, self.MenuPath, MenuTime, self.MenuTime } )
|
||||
|
||||
--self:E( { MenuTag = MenuTag, MenuTime = self.MenuTime, Path = self.Path } )
|
||||
if not MenuTime or self.MenuTime ~= MenuTime then
|
||||
if self.MenuGroup._Menus[self.Path] then
|
||||
self = self.MenuGroup._Menus[self.Path]
|
||||
|
||||
missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath )
|
||||
self:T( { "Removing Group Command Menu:", MenuGroup = self.MenuGroup:GetName(), MenuText = self.MenuText, MenuPath = self.Path } )
|
||||
|
||||
self.ParentMenu.Menus[self.MenuText] = nil
|
||||
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1
|
||||
if self.ParentMenu.MenuCount == 0 then
|
||||
if self.MenuRemoveParent == true then
|
||||
self:T2( "Removing Parent Menu " )
|
||||
self.ParentMenu:Remove()
|
||||
if ( not MenuTag ) or ( MenuTag and self.MenuTag and MenuTag == self.MenuTag ) then
|
||||
if self.MenuGroup._Menus[self.Path] then
|
||||
self = self.MenuGroup._Menus[self.Path]
|
||||
|
||||
missionCommands.removeItemForGroup( self.MenuGroupID, self.MenuPath )
|
||||
--self:E( { "Removing Group Command Menu:", MenuGroup = self.MenuGroup:GetName(), MenuText = self.MenuText, MenuPath = self.Path } )
|
||||
|
||||
self.ParentMenu.Menus[self.MenuText] = nil
|
||||
self.ParentMenu.MenuCount = self.ParentMenu.MenuCount - 1
|
||||
if self.ParentMenu.MenuCount == 0 then
|
||||
if self.MenuRemoveParent == true then
|
||||
self:T2( "Removing Parent Menu " )
|
||||
self.ParentMenu:Remove()
|
||||
end
|
||||
end
|
||||
|
||||
self.MenuGroup._Menus[self.Path] = nil
|
||||
self = nil
|
||||
end
|
||||
|
||||
self.MenuGroup._Menus[self.Path] = nil
|
||||
self = nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -53,6 +53,16 @@ MESSAGE = {
|
||||
MessageID = 0,
|
||||
}
|
||||
|
||||
--- Message Types
|
||||
-- @type MESSAGE.Type
|
||||
MESSAGE.Type = {
|
||||
Update = "Update",
|
||||
Information = "Information",
|
||||
Briefing = "Briefing Report",
|
||||
Overview = "Overview Report",
|
||||
Detailed = "Detailed Report"
|
||||
}
|
||||
|
||||
|
||||
--- Creates a new MESSAGE object. Note that these MESSAGE objects are not yet displayed on the display panel. You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
|
||||
-- @param self
|
||||
@ -74,6 +84,9 @@ function MESSAGE:New( MessageText, MessageDuration, MessageCategory )
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( { MessageText, MessageDuration, MessageCategory } )
|
||||
|
||||
|
||||
self.MessageType = nil
|
||||
|
||||
-- When no MessageCategory is given, we don't show it as a title...
|
||||
if MessageCategory and MessageCategory ~= "" then
|
||||
if MessageCategory:sub(-1) ~= "\n" then
|
||||
@ -96,6 +109,37 @@ function MESSAGE:New( MessageText, MessageDuration, MessageCategory )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Creates a new MESSAGE object of a certain type.
|
||||
-- Note that these MESSAGE objects are not yet displayed on the display panel.
|
||||
-- You must use the functions @{ToClient} or @{ToCoalition} or @{ToAll} to send these Messages to the respective recipients.
|
||||
-- The message display times are automatically defined based on the timing settings in the @{Settings} menu.
|
||||
-- @param self
|
||||
-- @param #string MessageText is the text of the Message.
|
||||
-- @param #MESSAGE.Type MessageType The type of the message.
|
||||
-- @return #MESSAGE
|
||||
-- @usage
|
||||
-- MessageAll = MESSAGE:NewType( "To all Players: BLUE has won! Each player of BLUE wins 50 points!", MESSAGE.Type.Information )
|
||||
-- MessageRED = MESSAGE:NewType( "To the RED Players: You receive a penalty because you've killed one of your own units", MESSAGE.Type.Information )
|
||||
-- MessageClient1 = MESSAGE:NewType( "Congratulations, you've just hit a target", MESSAGE.Type.Update )
|
||||
-- MessageClient2 = MESSAGE:NewType( "Congratulations, you've just killed a target", MESSAGE.Type.Update )
|
||||
function MESSAGE:NewType( MessageText, MessageType )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( { MessageText } )
|
||||
|
||||
self.MessageType = MessageType
|
||||
|
||||
self.MessageTime = timer.getTime()
|
||||
self.MessageText = MessageText:gsub("^\n","",1):gsub("\n$","",1)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- Sends a MESSAGE to a Client Group. Note that the Group needs to be defined within the ME with the skillset "Client" or "Player".
|
||||
-- @param #MESSAGE self
|
||||
-- @param Wrapper.Client#CLIENT Client is the Group of the Client.
|
||||
@ -115,14 +159,22 @@ end
|
||||
-- MessageClient2 = MESSAGE:New( "Congratulations, you've just killed a target", "Score", 25, "Score" )
|
||||
-- MessageClient1:ToClient( ClientGroup )
|
||||
-- MessageClient2:ToClient( ClientGroup )
|
||||
function MESSAGE:ToClient( Client )
|
||||
function MESSAGE:ToClient( Client, Settings )
|
||||
self:F( Client )
|
||||
|
||||
if Client and Client:GetClientGroupID() then
|
||||
|
||||
local ClientGroupID = Client:GetClientGroupID()
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
|
||||
if self.MessageType then
|
||||
local Settings = Settings or ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
|
||||
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
||||
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||
end
|
||||
|
||||
if self.MessageDuration ~= 0 then
|
||||
local ClientGroupID = Client:GetClientGroupID()
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outTextForGroup( ClientGroupID, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
@ -132,13 +184,21 @@ end
|
||||
-- @param #MESSAGE self
|
||||
-- @param Wrapper.Group#GROUP Group is the Group.
|
||||
-- @return #MESSAGE
|
||||
function MESSAGE:ToGroup( Group )
|
||||
function MESSAGE:ToGroup( Group, Settings )
|
||||
self:F( Group.GroupName )
|
||||
|
||||
if Group then
|
||||
|
||||
if self.MessageType then
|
||||
local Settings = Settings or ( Group and _DATABASE:GetPlayerSettings( Group:GetPlayerName() ) ) or _SETTINGS -- Core.Settings#SETTINGS
|
||||
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
||||
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||
end
|
||||
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outTextForGroup( Group:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
|
||||
if self.MessageDuration ~= 0 then
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outTextForGroup( Group:GetID(), self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
@ -193,12 +253,20 @@ end
|
||||
-- or
|
||||
-- MessageRED = MESSAGE:New( "To the RED Players: You receive a penalty because you've killed one of your own units", "Penalty", 25, "Score" )
|
||||
-- MessageRED:ToCoalition( coalition.side.RED )
|
||||
function MESSAGE:ToCoalition( CoalitionSide )
|
||||
function MESSAGE:ToCoalition( CoalitionSide, Settings )
|
||||
self:F( CoalitionSide )
|
||||
|
||||
if self.MessageType then
|
||||
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
|
||||
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
||||
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||
end
|
||||
|
||||
if CoalitionSide then
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outTextForCoalition( CoalitionSide, self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
|
||||
if self.MessageDuration ~= 0 then
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outTextForCoalition( CoalitionSide, self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
@ -232,8 +300,16 @@ end
|
||||
function MESSAGE:ToAll()
|
||||
self:F()
|
||||
|
||||
self:ToCoalition( coalition.side.RED )
|
||||
self:ToCoalition( coalition.side.BLUE )
|
||||
if self.MessageType then
|
||||
local Settings = Settings or _SETTINGS -- Core.Settings#SETTINGS
|
||||
self.MessageDuration = Settings:GetMessageTime( self.MessageType )
|
||||
self.MessageCategory = "" -- self.MessageType .. ": "
|
||||
end
|
||||
|
||||
if self.MessageDuration ~= 0 then
|
||||
self:T( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$","") .. " / " .. self.MessageDuration )
|
||||
trigger.action.outText( self.MessageCategory .. self.MessageText:gsub("\n$",""):gsub("\n$",""), self.MessageDuration )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
@ -245,8 +321,7 @@ end
|
||||
function MESSAGE:ToAllIf( Condition )
|
||||
|
||||
if Condition and Condition == true then
|
||||
self:ToCoalition( coalition.side.RED )
|
||||
self:ToCoalition( coalition.side.BLUE )
|
||||
self:ToAll()
|
||||
end
|
||||
|
||||
return self
|
||||
|
||||
@ -54,8 +54,8 @@ do -- COORDINATE
|
||||
--
|
||||
-- A COORDINATE can prepare waypoints for Ground and Air groups to be embedded into a Route.
|
||||
--
|
||||
-- * @{#COORDINATE.RoutePointAir}(): Build an air route point.
|
||||
-- * @{#COORDINATE.RoutePointGround}(): Build a ground route point.
|
||||
-- * @{#COORDINATE.WaypointAir}(): Build an air route point.
|
||||
-- * @{#COORDINATE.WaypointGround}(): Build a ground route point.
|
||||
--
|
||||
-- Route points can be used in the Route methods of the @{Group#GROUP} class.
|
||||
--
|
||||
@ -90,6 +90,18 @@ do -- COORDINATE
|
||||
-- * @{#COORDINATE.IlluminationBomb}(): To illuminate the point.
|
||||
--
|
||||
--
|
||||
-- ## Markings
|
||||
--
|
||||
-- Place markers (text boxes with clarifications for briefings, target locations or any other reference point) on the map for all players, coalitions or specific groups:
|
||||
--
|
||||
-- * @{#COORDINATE.MarkToAll}(): Place a mark to all players.
|
||||
-- * @{#COORDINATE.MarkToCoalition}(): Place a mark to a coalition.
|
||||
-- * @{#COORDINATE.MarkToCoalitionRed}(): Place a mark to the red coalition.
|
||||
-- * @{#COORDINATE.MarkToCoalitionBlue}(): Place a mark to the blue coalition.
|
||||
-- * @{#COORDINATE.MarkToGroup}(): Place a mark to a group (needs to have a client in it or a CA group (CA group is bugged)).
|
||||
-- * @{#COORDINATE.RemoveMark}(): Removes a mark from the map.
|
||||
--
|
||||
--
|
||||
-- ## 3D calculation methods
|
||||
--
|
||||
-- Various calculation methods exist to use or manipulate 3D space. Find below a short description of each method:
|
||||
@ -138,6 +150,31 @@ do -- COORDINATE
|
||||
ClassName = "COORDINATE",
|
||||
}
|
||||
|
||||
--- @field COORDINATE.WaypointAltType
|
||||
COORDINATE.WaypointAltType = {
|
||||
BARO = "BARO",
|
||||
RADIO = "RADIO",
|
||||
}
|
||||
|
||||
--- @field COORDINATE.WaypointAction
|
||||
COORDINATE.WaypointAction = {
|
||||
TurningPoint = "Turning Point",
|
||||
FlyoverPoint = "Fly Over Point",
|
||||
FromParkingArea = "From Parking Area",
|
||||
FromParkingAreaHot = "From Parking Area Hot",
|
||||
FromRunway = "From Runway",
|
||||
Landing = "Landing",
|
||||
}
|
||||
|
||||
--- @field COORDINATE.WaypointType
|
||||
COORDINATE.WaypointType = {
|
||||
TakeOffParking = "TakeOffParking",
|
||||
TakeOffParkingHot = "TakeOffParkingHot",
|
||||
TakeOff = "TakeOffParkingHot",
|
||||
TurningPoint = "Turning Point",
|
||||
Land = "Land",
|
||||
}
|
||||
|
||||
|
||||
--- COORDINATE constructor.
|
||||
-- @param #COORDINATE self
|
||||
@ -279,11 +316,55 @@ do -- COORDINATE
|
||||
|
||||
return RandomVec3
|
||||
end
|
||||
|
||||
--- Return the height of the land at the coordinate.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #number
|
||||
function COORDINATE:GetLandHeight()
|
||||
local Vec2 = { x = self.x, y = self.z }
|
||||
return land.getHeight( Vec2 )
|
||||
end
|
||||
|
||||
|
||||
--- Set the heading of the coordinate, if applicable.
|
||||
-- @param #COORDINATE self
|
||||
function COORDINATE:SetHeading( Heading )
|
||||
self.Heading = Heading
|
||||
end
|
||||
|
||||
|
||||
--- Get the heading of the coordinate, if applicable.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #number or nil
|
||||
function COORDINATE:GetHeading()
|
||||
return self.Heading
|
||||
end
|
||||
|
||||
|
||||
--- Set the velocity of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #string Velocity Velocity in meters per second.
|
||||
function COORDINATE:SetVelocity( Velocity )
|
||||
self.Velocity = Velocity
|
||||
end
|
||||
|
||||
|
||||
--- Return the velocity of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #number Velocity in meters per second.
|
||||
function COORDINATE:GetVelocity()
|
||||
local Velocity = self.Velocity
|
||||
return Velocity or 0
|
||||
end
|
||||
|
||||
|
||||
--- Return velocity text of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #string
|
||||
function COORDINATE:GetMovingText( Settings )
|
||||
|
||||
return self:GetVelocityText( Settings ) .. ", " .. self:GetHeadingText( Settings )
|
||||
end
|
||||
|
||||
|
||||
--- Return a direction vector Vec3 from COORDINATE to the COORDINATE.
|
||||
@ -294,6 +375,7 @@ do -- COORDINATE
|
||||
return { x = TargetCoordinate.x - self.x, y = TargetCoordinate.y - self.y, z = TargetCoordinate.z - self.z }
|
||||
end
|
||||
|
||||
|
||||
--- Get a correction in radians of the real magnetic north of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #number CorrectionRadians The correction in radians.
|
||||
@ -339,6 +421,7 @@ do -- COORDINATE
|
||||
return ( ( TargetVec3.x - SourceVec3.x ) ^ 2 + ( TargetVec3.z - SourceVec3.z ) ^ 2 ) ^ 0.5
|
||||
end
|
||||
|
||||
|
||||
--- Return the 3D distance in meters between the target COORDINATE and the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE TargetCoordinate The target COORDINATE.
|
||||
@ -405,6 +488,38 @@ do -- COORDINATE
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Return the velocity text of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #string Velocity text.
|
||||
function COORDINATE:GetVelocityText( Settings )
|
||||
local Velocity = self:GetVelocity()
|
||||
local Settings = Settings or _SETTINGS
|
||||
if Velocity then
|
||||
if Settings:IsMetric() then
|
||||
return string.format( " moving at %d km/h", UTILS.MpsToKmph( Velocity ) )
|
||||
else
|
||||
return string.format( " moving at %d mi/h", UTILS.MpsToKmph( Velocity ) / 1.852 )
|
||||
end
|
||||
else
|
||||
return " stationary"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Return the heading text of the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @return #string Heading text.
|
||||
function COORDINATE:GetHeadingText( Settings )
|
||||
local Heading = self:GetHeading()
|
||||
if Heading then
|
||||
return string.format( " bearing %3d°", Heading )
|
||||
else
|
||||
return " bearing unknown"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Provides a Bearing / Range string
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number AngleRadians The angle in randians
|
||||
@ -463,25 +578,25 @@ do -- COORDINATE
|
||||
|
||||
--- Build an air type route point.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE.RoutePointAltType AltType The altitude type.
|
||||
-- @param #COORDINATE.RoutePointType Type The route point type.
|
||||
-- @param #COORDINATE.RoutePointAction Action The route point action.
|
||||
-- @param #COORDINATE.WaypointAltType AltType The altitude type.
|
||||
-- @param #COORDINATE.WaypointType Type The route point type.
|
||||
-- @param #COORDINATE.WaypointAction Action The route point action.
|
||||
-- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h.
|
||||
-- @param #boolean SpeedLocked true means the speed is locked.
|
||||
-- @return #table The route point.
|
||||
function COORDINATE:RoutePointAir( AltType, Type, Action, Speed, SpeedLocked )
|
||||
function COORDINATE:WaypointAir( AltType, Type, Action, Speed, SpeedLocked )
|
||||
self:F2( { AltType, Type, Action, Speed, SpeedLocked } )
|
||||
|
||||
local RoutePoint = {}
|
||||
RoutePoint.x = self.x
|
||||
RoutePoint.y = self.z
|
||||
RoutePoint.alt = self.y
|
||||
RoutePoint.alt_type = AltType
|
||||
RoutePoint.alt_type = AltType or "RADIO"
|
||||
|
||||
RoutePoint.type = Type
|
||||
RoutePoint.action = Action
|
||||
RoutePoint.type = Type or nil
|
||||
RoutePoint.action = Action or nil
|
||||
|
||||
RoutePoint.speed = Speed / 3.6
|
||||
RoutePoint.speed = ( Speed and Speed / 3.6 ) or ( 500 / 3.6 )
|
||||
RoutePoint.speed_locked = true
|
||||
|
||||
-- ["task"] =
|
||||
@ -505,12 +620,81 @@ do -- COORDINATE
|
||||
return RoutePoint
|
||||
end
|
||||
|
||||
|
||||
--- Build a Waypoint Air "Turning Point".
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE.WaypointAltType AltType The altitude type.
|
||||
-- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h.
|
||||
-- @return #table The route point.
|
||||
function COORDINATE:WaypointAirTurningPoint( AltType, Speed )
|
||||
return self:WaypointAir( AltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.TurningPoint, Speed )
|
||||
end
|
||||
|
||||
|
||||
--- Build a Waypoint Air "Fly Over Point".
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE.WaypointAltType AltType The altitude type.
|
||||
-- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h.
|
||||
-- @return #table The route point.
|
||||
function COORDINATE:WaypointAirFlyOverPoint( AltType, Speed )
|
||||
return self:WaypointAir( AltType, COORDINATE.WaypointType.TurningPoint, COORDINATE.WaypointAction.FlyoverPoint, Speed )
|
||||
end
|
||||
|
||||
|
||||
--- Build a Waypoint Air "Take Off Parking Hot".
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE.WaypointAltType AltType The altitude type.
|
||||
-- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h.
|
||||
-- @return #table The route point.
|
||||
function COORDINATE:WaypointAirTakeOffParkingHot( AltType, Speed )
|
||||
return self:WaypointAir( AltType, COORDINATE.WaypointType.TakeOffParkingHot, COORDINATE.WaypointAction.FromParkingAreaHot, Speed )
|
||||
end
|
||||
|
||||
|
||||
--- Build a Waypoint Air "Take Off Parking".
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE.WaypointAltType AltType The altitude type.
|
||||
-- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h.
|
||||
-- @return #table The route point.
|
||||
function COORDINATE:WaypointAirTakeOffParking( AltType, Speed )
|
||||
return self:WaypointAir( AltType, COORDINATE.WaypointType.TakeOffParking, COORDINATE.WaypointAction.FromParkingArea, Speed )
|
||||
end
|
||||
|
||||
|
||||
--- Build a Waypoint Air "Take Off Runway".
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE.WaypointAltType AltType The altitude type.
|
||||
-- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h.
|
||||
-- @return #table The route point.
|
||||
function COORDINATE:WaypointAirTakeOffRunway( AltType, Speed )
|
||||
return self:WaypointAir( AltType, COORDINATE.WaypointType.TakeOff, COORDINATE.WaypointAction.FromRunway, Speed )
|
||||
end
|
||||
|
||||
|
||||
--- Build a Waypoint Air "Landing".
|
||||
-- @param #COORDINATE self
|
||||
-- @param Dcs.DCSTypes#Speed Speed Airspeed in km/h.
|
||||
-- @return #table The route point.
|
||||
-- @usage
|
||||
--
|
||||
-- LandingZone = ZONE:New( "LandingZone" )
|
||||
-- LandingCoord = LandingZone:GetCoordinate()
|
||||
-- LandingWaypoint = LandingCoord:WaypointAirLanding( 60 )
|
||||
-- HeliGroup:Route( { LandWaypoint }, 1 ) -- Start landing the helicopter in one second.
|
||||
--
|
||||
function COORDINATE:WaypointAirLanding( Speed )
|
||||
return self:WaypointAir( nil, COORDINATE.WaypointType.Land, COORDINATE.WaypointAction.Landing, Speed )
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Build an ground type route point.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Dcs.DCSTypes#Speed Speed Speed in km/h.
|
||||
-- @param #COORDINATE.RoutePointAction Formation The route point Formation.
|
||||
-- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h.
|
||||
-- @param #string Formation (optional) The route point Formation, which is a text string that specifies exactly the Text in the Type of the route point, like "Vee", "Echelon Right".
|
||||
-- @return #table The route point.
|
||||
function COORDINATE:RoutePointGround( Speed, Formation )
|
||||
function COORDINATE:WaypointGround( Speed, Formation )
|
||||
self:F2( { Formation, Speed } )
|
||||
|
||||
local RoutePoint = {}
|
||||
@ -520,7 +704,7 @@ do -- COORDINATE
|
||||
RoutePoint.action = Formation or ""
|
||||
|
||||
|
||||
RoutePoint.speed = Speed / 3.6
|
||||
RoutePoint.speed = ( Speed or 999 ) / 3.6
|
||||
RoutePoint.speed_locked = true
|
||||
|
||||
-- ["task"] =
|
||||
@ -642,6 +826,88 @@ do -- COORDINATE
|
||||
self:F2( Azimuth )
|
||||
self:Flare( FLARECOLOR.Red, Azimuth )
|
||||
end
|
||||
|
||||
do -- Markings
|
||||
|
||||
--- Mark to All
|
||||
-- @param #COORDINATE self
|
||||
-- @param #string MarkText Free format text that shows the marking clarification.
|
||||
-- @return #number The resulting Mark ID which is a number.
|
||||
-- @usage
|
||||
-- local TargetCoord = TargetGroup:GetCoordinate()
|
||||
-- local MarkID = TargetCoord:MarkToAll( "This is a target for all players" )
|
||||
function COORDINATE:MarkToAll( MarkText )
|
||||
local MarkID = UTILS.GetMarkID()
|
||||
trigger.action.markToAll( MarkID, MarkText, self:GetVec3() )
|
||||
return MarkID
|
||||
end
|
||||
|
||||
--- Mark to Coalition
|
||||
-- @param #COORDINATE self
|
||||
-- @param #string MarkText Free format text that shows the marking clarification.
|
||||
-- @param Coalition
|
||||
-- @return #number The resulting Mark ID which is a number.
|
||||
-- @usage
|
||||
-- local TargetCoord = TargetGroup:GetCoordinate()
|
||||
-- local MarkID = TargetCoord:MarkToCoalition( "This is a target for the red coalition", coalition.side.RED )
|
||||
function COORDINATE:MarkToCoalition( MarkText, Coalition )
|
||||
local MarkID = UTILS.GetMarkID()
|
||||
trigger.action.markToCoalition( MarkID, MarkText, self:GetVec3(), Coalition )
|
||||
return MarkID
|
||||
end
|
||||
|
||||
--- Mark to Red Coalition
|
||||
-- @param #COORDINATE self
|
||||
-- @param #string MarkText Free format text that shows the marking clarification.
|
||||
-- @return #number The resulting Mark ID which is a number.
|
||||
-- @usage
|
||||
-- local TargetCoord = TargetGroup:GetCoordinate()
|
||||
-- local MarkID = TargetCoord:MarkToCoalitionRed( "This is a target for the red coalition" )
|
||||
function COORDINATE:MarkToCoalitionRed( MarkText )
|
||||
return self:MarkToCoalition( MarkText, coalition.side.RED )
|
||||
end
|
||||
|
||||
--- Mark to Blue Coalition
|
||||
-- @param #COORDINATE self
|
||||
-- @param #string MarkText Free format text that shows the marking clarification.
|
||||
-- @return #number The resulting Mark ID which is a number.
|
||||
-- @usage
|
||||
-- local TargetCoord = TargetGroup:GetCoordinate()
|
||||
-- local MarkID = TargetCoord:MarkToCoalitionBlue( "This is a target for the blue coalition" )
|
||||
function COORDINATE:MarkToCoalitionBlue( MarkText )
|
||||
return self:MarkToCoalition( MarkText, coalition.side.BLUE )
|
||||
end
|
||||
|
||||
--- Mark to Group
|
||||
-- @param #COORDINATE self
|
||||
-- @param #string MarkText Free format text that shows the marking clarification.
|
||||
-- @param Wrapper.Group#GROUP MarkGroup The @{Group} that receives the mark.
|
||||
-- @return #number The resulting Mark ID which is a number.
|
||||
-- @usage
|
||||
-- local TargetCoord = TargetGroup:GetCoordinate()
|
||||
-- local MarkGroup = GROUP:FindByName( "AttackGroup" )
|
||||
-- local MarkID = TargetCoord:MarkToGroup( "This is a target for the attack group", AttackGroup )
|
||||
function COORDINATE:MarkToGroup( MarkText, MarkGroup )
|
||||
local MarkID = UTILS.GetMarkID()
|
||||
trigger.action.markToGroup( MarkID, MarkText, self:GetVec3(), MarkGroup:GetID() )
|
||||
return MarkID
|
||||
end
|
||||
|
||||
--- Remove a mark
|
||||
-- @param #COORDINATE self
|
||||
-- @param #number MarkID The ID of the mark to be removed.
|
||||
-- @usage
|
||||
-- local TargetCoord = TargetGroup:GetCoordinate()
|
||||
-- local MarkGroup = GROUP:FindByName( "AttackGroup" )
|
||||
-- local MarkID = TargetCoord:MarkToGroup( "This is a target for the attack group", AttackGroup )
|
||||
-- <<< logic >>>
|
||||
-- RemoveMark( MarkID ) -- The mark is now removed
|
||||
function COORDINATE:RemoveMark( MarkID )
|
||||
trigger.action.removeMark( MarkID )
|
||||
end
|
||||
|
||||
end -- Markings
|
||||
|
||||
|
||||
--- Returns if a Coordinate has Line of Sight (LOS) with the ToCoordinate.
|
||||
-- @param #COORDINATE self
|
||||
@ -662,6 +928,39 @@ do -- COORDINATE
|
||||
end
|
||||
|
||||
|
||||
--- Returns if a Coordinate is in a certain Radius of this Coordinate in 2D plane using the X and Z axis.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE ToCoordinate The coordinate that will be tested if it is in the radius of this coordinate.
|
||||
-- @param #number Radius The radius of the circle on the 2D plane around this coordinate.
|
||||
-- @return #boolean true if in the Radius.
|
||||
function COORDINATE:IsInRadius( Coordinate, Radius )
|
||||
|
||||
local InVec2 = self:GetVec2()
|
||||
local Vec2 = Coordinate:GetVec2()
|
||||
|
||||
local InRadius = UTILS.IsInRadius( InVec2, Vec2, Radius)
|
||||
|
||||
return InRadius
|
||||
end
|
||||
|
||||
|
||||
--- Returns if a Coordinate is in a certain radius of this Coordinate in 3D space using the X, Y and Z axis.
|
||||
-- So Radius defines the radius of the a Sphere in 3D space around this coordinate.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE ToCoordinate The coordinate that will be tested if it is in the radius of this coordinate.
|
||||
-- @param #number Radius The radius of the sphere in the 3D space around this coordinate.
|
||||
-- @return #boolean true if in the Sphere.
|
||||
function COORDINATE:IsInSphere( Coordinate, Radius )
|
||||
|
||||
local InVec3 = self:GetVec3()
|
||||
local Vec3 = Coordinate:GetVec3()
|
||||
|
||||
local InSphere = UTILS.IsInSphere( InVec3, Vec3, Radius)
|
||||
|
||||
return InSphere
|
||||
end
|
||||
|
||||
|
||||
--- Return a BR string from a COORDINATE to the COORDINATE.
|
||||
-- @param #COORDINATE self
|
||||
-- @param #COORDINATE TargetCoordinate The target COORDINATE.
|
||||
@ -725,16 +1024,26 @@ do -- COORDINATE
|
||||
return ""
|
||||
end
|
||||
|
||||
--- Provides a Lat Lon string
|
||||
--- Provides a Lat Lon string in Degree Minute Second format.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) Settings
|
||||
-- @return #string The LL Text
|
||||
function COORDINATE:ToStringLL( Settings ) --R2.1 Fixes issue #424.
|
||||
-- @return #string The LL DMS Text
|
||||
function COORDINATE:ToStringLLDMS( Settings )
|
||||
|
||||
local LL_Accuracy = Settings and Settings.LL_Accuracy or _SETTINGS.LL_Accuracy
|
||||
local LL_DMS = Settings and Settings.LL_DMS or _SETTINGS.LL_DMS
|
||||
local lat, lon = coord.LOtoLL( self:GetVec3() )
|
||||
return "LL, " .. UTILS.tostringLL( lat, lon, LL_Accuracy, LL_DMS )
|
||||
return "LL DMS, " .. UTILS.tostringLL( lat, lon, LL_Accuracy, true )
|
||||
end
|
||||
|
||||
--- Provides a Lat Lon string in Degree Decimal Minute format.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Core.Settings#SETTINGS Settings (optional) Settings
|
||||
-- @return #string The LL DDM Text
|
||||
function COORDINATE:ToStringLLDDM( Settings )
|
||||
|
||||
local LL_Accuracy = Settings and Settings.LL_Accuracy or _SETTINGS.LL_Accuracy
|
||||
local lat, lon = coord.LOtoLL( self:GetVec3() )
|
||||
return "LL DDM, " .. UTILS.tostringLL( lat, lon, LL_Accuracy, false )
|
||||
end
|
||||
|
||||
--- Provides a MGRS string
|
||||
@ -780,44 +1089,124 @@ do -- COORDINATE
|
||||
|
||||
end
|
||||
|
||||
--- Provides a coordinate string of the point, based on the A2G coordinate format system.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @return #string The coordinate Text in the configured coordinate system.
|
||||
function COORDINATE:ToStringA2G( Controllable, Settings ) -- R2.2
|
||||
|
||||
self:F( { Controllable = Controllable and Controllable:GetName() } )
|
||||
|
||||
local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS
|
||||
|
||||
if Settings:IsA2G_BR() then
|
||||
-- If no Controllable is given to calculate the BR from, then MGRS will be used!!!
|
||||
if Controllable then
|
||||
local Coordinate = Controllable:GetCoordinate()
|
||||
return Controllable and self:ToStringBR( Coordinate, Settings ) or self:ToStringMGRS( Settings )
|
||||
else
|
||||
return self:ToStringMGRS( Settings )
|
||||
end
|
||||
end
|
||||
if Settings:IsA2G_LL_DMS() then
|
||||
return self:ToStringLLDMS( Settings )
|
||||
end
|
||||
if Settings:IsA2G_LL_DDM() then
|
||||
return self:ToStringLLDDM( Settings )
|
||||
end
|
||||
if Settings:IsA2G_MGRS() then
|
||||
return self:ToStringMGRS( Settings )
|
||||
end
|
||||
|
||||
return nil
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Provides a coordinate string of the point, based on the A2A coordinate format system.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @return #string The coordinate Text in the configured coordinate system.
|
||||
function COORDINATE:ToStringA2A( Controllable, Settings ) -- R2.2
|
||||
|
||||
self:F( { Controllable = Controllable and Controllable:GetName() } )
|
||||
|
||||
local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS
|
||||
|
||||
if Settings:IsA2A_BRAA() then
|
||||
if Controllable then
|
||||
local Coordinate = Controllable:GetCoordinate()
|
||||
return self:ToStringBRA( Coordinate, Settings )
|
||||
else
|
||||
return self:ToStringMGRS( Settings )
|
||||
end
|
||||
end
|
||||
if Settings:IsA2A_BULLS() then
|
||||
local Coalition = Controllable:GetCoalition()
|
||||
return self:ToStringBULLS( Coalition, Settings )
|
||||
end
|
||||
if Settings:IsA2A_LL_DMS() then
|
||||
return self:ToStringLLDMS( Settings )
|
||||
end
|
||||
if Settings:IsA2A_LL_DDM() then
|
||||
return self:ToStringLLDDM( Settings )
|
||||
end
|
||||
if Settings:IsA2A_MGRS() then
|
||||
return self:ToStringMGRS( Settings )
|
||||
end
|
||||
|
||||
return nil
|
||||
|
||||
end
|
||||
|
||||
--- Provides a coordinate string of the point, based on a coordinate format system:
|
||||
-- * Uses default settings in COORDINATE.
|
||||
-- * Can be overridden if for a GROUP containing x clients, a menu was selected to override the default.
|
||||
-- @param #COORDINATE self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @param Tasking.Task#TASK Task The task for which coordinates need to be calculated.
|
||||
-- @return #string The coordinate Text in the configured coordinate system.
|
||||
function COORDINATE:ToString( Controllable, Settings ) -- R2.2
|
||||
function COORDINATE:ToString( Controllable, Settings, Task ) -- R2.2
|
||||
|
||||
self:E( { Controllable = Controllable } )
|
||||
self:F( { Controllable = Controllable and Controllable:GetName() } )
|
||||
|
||||
local Settings = Settings or ( Controllable and _DATABASE:GetPlayerSettings( Controllable:GetPlayerName() ) ) or _SETTINGS
|
||||
|
||||
local IsAir = Controllable and Controllable:IsAirPlane() or false
|
||||
|
||||
if IsAir then
|
||||
if Settings:IsA2A_BRA() then
|
||||
local Coordinate = Controllable:GetCoordinate()
|
||||
return self:ToStringBRA( Coordinate, Settings )
|
||||
end
|
||||
|
||||
if Settings:IsA2A_BULLS() then
|
||||
local Coalition = Controllable:GetCoalition()
|
||||
return self:ToStringBULLS( Coalition, Settings )
|
||||
local ModeA2A = false
|
||||
|
||||
if Task then
|
||||
if Task:IsInstanceOf( TASK_A2A ) then
|
||||
ModeA2A = true
|
||||
else
|
||||
if Task:IsInstanceOf( TASK_A2G ) then
|
||||
ModeA2A = false
|
||||
else
|
||||
if Task:IsInstanceOf( TASK_CARGO ) then
|
||||
ModeA2A = false
|
||||
else
|
||||
ModeA2A = false
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if Settings:IsA2G_BRA() then
|
||||
local Coordinate = Controllable:GetCoordinate()
|
||||
return Controllable and self:ToStringBR( Coordinate, Settings ) or self:ToStringMGRS( Settings )
|
||||
end
|
||||
if Settings:IsA2G_LL() then
|
||||
return self:ToStringLL( Settings )
|
||||
end
|
||||
if Settings:IsA2G_MGRS() then
|
||||
return self:ToStringMGRS( Settings )
|
||||
local IsAir = Controllable and Controllable:IsAirPlane() or false
|
||||
if IsAir then
|
||||
ModeA2A = true
|
||||
else
|
||||
ModeA2A = false
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if ModeA2A == true then
|
||||
return self:ToStringA2A( Controllable, Settings )
|
||||
else
|
||||
return self:ToStringA2G( Controllable, Settings )
|
||||
end
|
||||
|
||||
return nil
|
||||
|
||||
end
|
||||
|
||||
86
Moose Development/Moose/Core/Report.lua
Normal file
86
Moose Development/Moose/Core/Report.lua
Normal file
@ -0,0 +1,86 @@
|
||||
--- The REPORT class
|
||||
-- @type REPORT
|
||||
-- @extends Core.Base#BASE
|
||||
REPORT = {
|
||||
ClassName = "REPORT",
|
||||
Title = "",
|
||||
}
|
||||
|
||||
--- Create a new REPORT.
|
||||
-- @param #REPORT self
|
||||
-- @param #string Title
|
||||
-- @return #REPORT
|
||||
function REPORT:New( Title )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #REPORT
|
||||
|
||||
self.Report = {}
|
||||
|
||||
self:SetTitle( Title or "" )
|
||||
self:SetIndent( 3 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Has the REPORT Text?
|
||||
-- @param #REPORT self
|
||||
-- @return #boolean
|
||||
function REPORT:HasText() --R2.1
|
||||
|
||||
return #self.Report > 0
|
||||
end
|
||||
|
||||
|
||||
--- Set indent of a REPORT.
|
||||
-- @param #REPORT self
|
||||
-- @param #number Indent
|
||||
-- @return #REPORT
|
||||
function REPORT:SetIndent( Indent ) --R2.1
|
||||
self.Indent = Indent
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Add a new line to a REPORT.
|
||||
-- @param #REPORT self
|
||||
-- @param #string Text
|
||||
-- @return #REPORT
|
||||
function REPORT:Add( Text )
|
||||
self.Report[#self.Report+1] = Text
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a new line to a REPORT.
|
||||
-- @param #REPORT self
|
||||
-- @param #string Text
|
||||
-- @return #REPORT
|
||||
function REPORT:AddIndent( Text ) --R2.1
|
||||
self.Report[#self.Report+1] = string.rep(" ", self.Indent ) .. Text:gsub("\n","\n"..string.rep( " ", self.Indent ) )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Produces the text of the report, taking into account an optional delimeter, which is \n by default.
|
||||
-- @param #REPORT self
|
||||
-- @param #string Delimiter (optional) A delimiter text.
|
||||
-- @return #string The report text.
|
||||
function REPORT:Text( Delimiter )
|
||||
Delimiter = Delimiter or "\n"
|
||||
local ReportText = ( self.Title ~= "" and self.Title .. Delimiter or self.Title ) .. table.concat( self.Report, Delimiter ) or ""
|
||||
return ReportText
|
||||
end
|
||||
|
||||
--- Sets the title of the report.
|
||||
-- @param #REPORT self
|
||||
-- @param #string Title The title of the report.
|
||||
-- @return #REPORT
|
||||
function REPORT:SetTitle( Title )
|
||||
self.Title = Title
|
||||
return self
|
||||
end
|
||||
|
||||
--- Gets the amount of report items contained in the report.
|
||||
-- @param #REPORT self
|
||||
-- @return #number Returns the number of report items contained in the report. 0 is returned if no report items are contained in the report. The title is not counted for.
|
||||
function REPORT:GetCount()
|
||||
return #self.Report
|
||||
end
|
||||
@ -55,6 +55,7 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
self:F2( { Scheduler, ScheduleFunction, ScheduleArguments, Start, Repeat, Randomize, Stop } )
|
||||
|
||||
self.CallID = self.CallID + 1
|
||||
local CallID = self.CallID .. "#" .. ( Scheduler.MasterObject and Scheduler.MasterObject.GetClassNameAndID and Scheduler.MasterObject:GetClassNameAndID() or "" ) or ""
|
||||
|
||||
-- Initialize the ObjectSchedulers array, which is a weakly coupled table.
|
||||
-- If the object used as the key is nil, then the garbage collector will remove the item from the Functions array.
|
||||
@ -65,27 +66,27 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
self.ObjectSchedulers = self.ObjectSchedulers or setmetatable( {}, { __mode = "v" } )
|
||||
|
||||
if Scheduler.MasterObject then
|
||||
self.ObjectSchedulers[self.CallID] = Scheduler
|
||||
self:F3( { CallID = self.CallID, ObjectScheduler = tostring(self.ObjectSchedulers[self.CallID]), MasterObject = tostring(Scheduler.MasterObject) } )
|
||||
self.ObjectSchedulers[CallID] = Scheduler
|
||||
self:F3( { CallID = CallID, ObjectScheduler = tostring(self.ObjectSchedulers[CallID]), MasterObject = tostring(Scheduler.MasterObject) } )
|
||||
else
|
||||
self.PersistentSchedulers[self.CallID] = Scheduler
|
||||
self:F3( { CallID = self.CallID, PersistentScheduler = self.PersistentSchedulers[self.CallID] } )
|
||||
self.PersistentSchedulers[CallID] = Scheduler
|
||||
self:F3( { CallID = CallID, PersistentScheduler = self.PersistentSchedulers[CallID] } )
|
||||
end
|
||||
|
||||
self.Schedule = self.Schedule or setmetatable( {}, { __mode = "k" } )
|
||||
self.Schedule[Scheduler] = self.Schedule[Scheduler] or {}
|
||||
self.Schedule[Scheduler][self.CallID] = {}
|
||||
self.Schedule[Scheduler][self.CallID].Function = ScheduleFunction
|
||||
self.Schedule[Scheduler][self.CallID].Arguments = ScheduleArguments
|
||||
self.Schedule[Scheduler][self.CallID].StartTime = timer.getTime() + ( Start or 0 )
|
||||
self.Schedule[Scheduler][self.CallID].Start = Start + .1
|
||||
self.Schedule[Scheduler][self.CallID].Repeat = Repeat
|
||||
self.Schedule[Scheduler][self.CallID].Randomize = Randomize
|
||||
self.Schedule[Scheduler][self.CallID].Stop = Stop
|
||||
self.Schedule[Scheduler][CallID] = {}
|
||||
self.Schedule[Scheduler][CallID].Function = ScheduleFunction
|
||||
self.Schedule[Scheduler][CallID].Arguments = ScheduleArguments
|
||||
self.Schedule[Scheduler][CallID].StartTime = timer.getTime() + ( Start or 0 )
|
||||
self.Schedule[Scheduler][CallID].Start = Start + .1
|
||||
self.Schedule[Scheduler][CallID].Repeat = Repeat or 0
|
||||
self.Schedule[Scheduler][CallID].Randomize = Randomize or 0
|
||||
self.Schedule[Scheduler][CallID].Stop = Stop
|
||||
|
||||
self:T3( self.Schedule[Scheduler][self.CallID] )
|
||||
self:T3( self.Schedule[Scheduler][CallID] )
|
||||
|
||||
self.Schedule[Scheduler][self.CallID].CallHandler = function( CallID )
|
||||
self.Schedule[Scheduler][CallID].CallHandler = function( CallID )
|
||||
self:F2( CallID )
|
||||
|
||||
local ErrorHandler = function( errmsg )
|
||||
@ -100,11 +101,12 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
if not Scheduler then
|
||||
Scheduler = self.PersistentSchedulers[CallID]
|
||||
end
|
||||
|
||||
|
||||
--self:T3( { Scheduler = Scheduler } )
|
||||
|
||||
if Scheduler then
|
||||
|
||||
local MasterObject = tostring(Scheduler.MasterObject)
|
||||
local Schedule = self.Schedule[Scheduler][CallID]
|
||||
|
||||
--self:T3( { Schedule = Schedule } )
|
||||
@ -133,10 +135,13 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
end
|
||||
|
||||
local CurrentTime = timer.getTime()
|
||||
local StartTime = CurrentTime + Start
|
||||
local StartTime = Schedule.StartTime
|
||||
|
||||
self:F3( { Master = MasterObject, CurrentTime = CurrentTime, StartTime = StartTime, Start = Start, Repeat = Repeat, Randomize = Randomize, Stop = Stop } )
|
||||
|
||||
|
||||
if Status and (( Result == nil ) or ( Result and Result ~= false ) ) then
|
||||
if Repeat ~= 0 and ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) then
|
||||
if Repeat ~= 0 and ( ( Stop == 0 ) or ( Stop ~= 0 and CurrentTime <= StartTime + Stop ) ) then
|
||||
local ScheduleTime =
|
||||
CurrentTime +
|
||||
Repeat +
|
||||
@ -160,9 +165,9 @@ function SCHEDULEDISPATCHER:AddSchedule( Scheduler, ScheduleFunction, ScheduleAr
|
||||
return nil
|
||||
end
|
||||
|
||||
self:Start( Scheduler, self.CallID )
|
||||
self:Start( Scheduler, CallID )
|
||||
|
||||
return self.CallID
|
||||
return CallID
|
||||
end
|
||||
|
||||
function SCHEDULEDISPATCHER:RemoveSchedule( Scheduler, CallID )
|
||||
@ -182,10 +187,11 @@ function SCHEDULEDISPATCHER:Start( Scheduler, CallID )
|
||||
-- Only start when there is no ScheduleID defined!
|
||||
-- This prevents to "Start" the scheduler twice with the same CallID...
|
||||
if not Schedule[CallID].ScheduleID then
|
||||
Schedule[CallID].StartTime = timer.getTime() -- Set the StartTime field to indicate when the scheduler started.
|
||||
Schedule[CallID].ScheduleID = timer.scheduleFunction(
|
||||
Schedule[CallID].CallHandler,
|
||||
CallID,
|
||||
timer.getTime() + Schedule[CallID].Start
|
||||
timer.getTime() + Schedule[CallID].Start
|
||||
)
|
||||
end
|
||||
else
|
||||
|
||||
@ -210,7 +210,8 @@ SCHEDULER = {
|
||||
-- @return #SCHEDULER self.
|
||||
-- @return #number The ScheduleID of the planned schedule.
|
||||
function SCHEDULER:New( SchedulerObject, SchedulerFunction, SchedulerArguments, Start, Repeat, RandomizeFactor, Stop )
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SCHEDULER
|
||||
self:F2( { Start, Repeat, RandomizeFactor, Stop } )
|
||||
|
||||
local ScheduleID = nil
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -55,11 +55,15 @@ do -- SETTINGS
|
||||
if PlayerName == nil then
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SETTINGS
|
||||
self:SetMetric() -- Defaults
|
||||
self:SetA2G_MGRS() -- Defaults
|
||||
self:SetA2A_BRA() -- Defaults
|
||||
self:SetLL_Accuracy( 2 ) -- Defaults
|
||||
self:SetLL_DMS( true ) -- Defaults
|
||||
self:SetA2G_BR() -- Defaults
|
||||
self:SetA2A_BRAA() -- Defaults
|
||||
self:SetLL_Accuracy( 3 ) -- Defaults
|
||||
self:SetMGRS_Accuracy( 5 ) -- Defaults
|
||||
self:SetMessageTime( MESSAGE.Type.Briefing, 180 )
|
||||
self:SetMessageTime( MESSAGE.Type.Detailed, 60 )
|
||||
self:SetMessageTime( MESSAGE.Type.Information, 30 )
|
||||
self:SetMessageTime( MESSAGE.Type.Overview, 60 )
|
||||
self:SetMessageTime( MESSAGE.Type.Update, 15 )
|
||||
return self
|
||||
else
|
||||
local Settings = _DATABASE:GetPlayerSettings( PlayerName )
|
||||
@ -82,7 +86,6 @@ do -- SETTINGS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if metric.
|
||||
function SETTINGS:IsMetric()
|
||||
self:E( {Metric = ( self.Metric ~= nil and self.Metric == true ) or ( self.Metric == nil and _SETTINGS:IsMetric() ) } )
|
||||
return ( self.Metric ~= nil and self.Metric == true ) or ( self.Metric == nil and _SETTINGS:IsMetric() )
|
||||
end
|
||||
|
||||
@ -96,7 +99,6 @@ do -- SETTINGS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if imperial.
|
||||
function SETTINGS:IsImperial()
|
||||
self:E( {Metric = ( self.Metric ~= nil and self.Metric == false ) or ( self.Metric == nil and _SETTINGS:IsMetric() ) } )
|
||||
return ( self.Metric ~= nil and self.Metric == false ) or ( self.Metric == nil and _SETTINGS:IsMetric() )
|
||||
end
|
||||
|
||||
@ -111,25 +113,10 @@ do -- SETTINGS
|
||||
--- Gets the SETTINGS LL accuracy.
|
||||
-- @param #SETTINGS self
|
||||
-- @return #number
|
||||
function SETTINGS:GetLL_Accuracy()
|
||||
return self.LL_Accuracy or _SETTINGS:GetLL_Accuracy()
|
||||
function SETTINGS:GetLL_DDM_Accuracy()
|
||||
return self.LL_DDM_Accuracy or _SETTINGS:GetLL_DDM_Accuracy()
|
||||
end
|
||||
|
||||
--- Sets the SETTINGS LL DMS.
|
||||
-- @param #SETTINGS self
|
||||
-- @param #number LL_DMS
|
||||
-- @return #SETTINGS
|
||||
function SETTINGS:SetLL_DMS( LL_DMS )
|
||||
self.LL_DMS = LL_DMS
|
||||
end
|
||||
|
||||
--- Gets the SETTINGS LL DMS.
|
||||
-- @param #SETTINGS self
|
||||
-- @return #number
|
||||
function SETTINGS:GetLL_DMS()
|
||||
return self.LL_DMS or _SETTINGS:GetLL_DMS()
|
||||
end
|
||||
|
||||
--- Sets the SETTINGS MGRS accuracy.
|
||||
-- @param #SETTINGS self
|
||||
-- @param #number MGRS_Accuracy
|
||||
@ -145,21 +132,50 @@ do -- SETTINGS
|
||||
return self.MGRS_Accuracy or _SETTINGS:GetMGRS_Accuracy()
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Sets A2G LL
|
||||
--- Sets the SETTINGS Message Display Timing of a MessageType
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS
|
||||
function SETTINGS:SetA2G_LL()
|
||||
self.A2GSystem = "LL"
|
||||
-- @param Core.Message#MESSAGE MessageType The type of the message.
|
||||
-- @param #number MessageTime The display time duration in seconds of the MessageType.
|
||||
function SETTINGS:SetMessageTime( MessageType, MessageTime )
|
||||
self.MessageTypeTimings = self.MessageTypeTimings or {}
|
||||
self.MessageTypeTimings[MessageType] = MessageTime
|
||||
end
|
||||
|
||||
|
||||
--- Gets the SETTINGS Message Display Timing of a MessageType
|
||||
-- @param #SETTINGS self
|
||||
-- @param Core.Message#MESSAGE MessageType The type of the message.
|
||||
-- @return #number
|
||||
function SETTINGS:GetMessageTime( MessageType )
|
||||
return ( self.MessageTypeTimings and self.MessageTypeTimings[MessageType] ) or _SETTINGS:GetMessageTime( MessageType )
|
||||
end
|
||||
|
||||
--- Is LL
|
||||
--- Sets A2G LL DMS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if LL
|
||||
function SETTINGS:IsA2G_LL()
|
||||
return ( self.A2GSystem and self.A2GSystem == "LL" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL() )
|
||||
-- @return #SETTINGS
|
||||
function SETTINGS:SetA2G_LL_DMS()
|
||||
self.A2GSystem = "LL DMS"
|
||||
end
|
||||
|
||||
--- Sets A2G LL DDM
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS
|
||||
function SETTINGS:SetA2G_LL_DDM()
|
||||
self.A2GSystem = "LL DDM"
|
||||
end
|
||||
|
||||
--- Is LL DMS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if LL DMS
|
||||
function SETTINGS:IsA2G_LL_DMS()
|
||||
return ( self.A2GSystem and self.A2GSystem == "LL DMS" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL_DMS() )
|
||||
end
|
||||
|
||||
--- Is LL DDM
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if LL DDM
|
||||
function SETTINGS:IsA2G_LL_DDM()
|
||||
return ( self.A2GSystem and self.A2GSystem == "LL DDM" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_LL_DDM() )
|
||||
end
|
||||
|
||||
--- Sets A2G MGRS
|
||||
@ -179,31 +195,30 @@ do -- SETTINGS
|
||||
--- Sets A2G BRA
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS
|
||||
function SETTINGS:SetA2G_BRA()
|
||||
self.A2GSystem = "BRA"
|
||||
function SETTINGS:SetA2G_BR()
|
||||
self.A2GSystem = "BR"
|
||||
end
|
||||
|
||||
--- Is BRA
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if BRA
|
||||
function SETTINGS:IsA2G_BRA()
|
||||
self:E( { BRA = ( self.A2GSystem and self.A2GSystem == "BRA" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_BRA() ) } )
|
||||
return ( self.A2GSystem and self.A2GSystem == "BRA" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_BRA() )
|
||||
function SETTINGS:IsA2G_BR()
|
||||
return ( self.A2GSystem and self.A2GSystem == "BR" ) or ( not self.A2GSystem and _SETTINGS:IsA2G_BR() )
|
||||
end
|
||||
|
||||
--- Sets A2A BRA
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS
|
||||
function SETTINGS:SetA2A_BRA()
|
||||
self.A2ASystem = "BRA"
|
||||
function SETTINGS:SetA2A_BRAA()
|
||||
self.A2ASystem = "BRAA"
|
||||
end
|
||||
|
||||
--- Is BRA
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if BRA
|
||||
function SETTINGS:IsA2A_BRA()
|
||||
self:E( { BRA = ( self.A2ASystem and self.A2ASystem == "BRA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRA() ) } )
|
||||
return ( self.A2ASystem and self.A2ASystem == "BRA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRA() )
|
||||
function SETTINGS:IsA2A_BRAA()
|
||||
self:E( { BRA = ( self.A2ASystem and self.A2ASystem == "BRAA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRAA() ) } )
|
||||
return ( self.A2ASystem and self.A2ASystem == "BRAA" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BRAA() )
|
||||
end
|
||||
|
||||
--- Sets A2A BULLS
|
||||
@ -220,69 +235,179 @@ do -- SETTINGS
|
||||
return ( self.A2ASystem and self.A2ASystem == "BULLS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_BULLS() )
|
||||
end
|
||||
|
||||
--- Sets A2A LL DMS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS
|
||||
function SETTINGS:SetA2A_LL_DMS()
|
||||
self.A2ASystem = "LL DMS"
|
||||
end
|
||||
|
||||
--- Sets A2A LL DDM
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS
|
||||
function SETTINGS:SetA2A_LL_DDM()
|
||||
self.A2ASystem = "LL DDM"
|
||||
end
|
||||
|
||||
--- Is LL DMS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if LL DMS
|
||||
function SETTINGS:IsA2A_LL_DMS()
|
||||
return ( self.A2ASystem and self.A2ASystem == "LL DMS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_LL_DMS() )
|
||||
end
|
||||
|
||||
--- Is LL DDM
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if LL DDM
|
||||
function SETTINGS:IsA2A_LL_DDM()
|
||||
return ( self.A2ASystem and self.A2ASystem == "LL DDM" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_LL_DDM() )
|
||||
end
|
||||
|
||||
--- Sets A2A MGRS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #SETTINGS
|
||||
function SETTINGS:SetA2A_MGRS()
|
||||
self.A2ASystem = "MGRS"
|
||||
end
|
||||
|
||||
--- Is MGRS
|
||||
-- @param #SETTINGS self
|
||||
-- @return #boolean true if MGRS
|
||||
function SETTINGS:IsA2A_MGRS()
|
||||
return ( self.A2ASystem and self.A2ASystem == "MGRS" ) or ( not self.A2ASystem and _SETTINGS:IsA2A_MGRS() )
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
-- @return #SETTINGS
|
||||
function SETTINGS:SetSystemMenu( RootMenu, MenuText )
|
||||
function SETTINGS:SetSystemMenu( MenuGroup, RootMenu )
|
||||
|
||||
MenuText = MenuText or "System Settings"
|
||||
|
||||
if not self.SettingsMenu then
|
||||
self.SettingsMenu = MENU_MISSION:New( MenuText, RootMenu )
|
||||
end
|
||||
|
||||
if self.DefaultMenu then
|
||||
self.DefaultMenu:Remove()
|
||||
self.DefaultMenu = nil
|
||||
end
|
||||
self.DefaultMenu = MENU_MISSION:New( "Default Settings", self.SettingsMenu )
|
||||
local MenuText = "System Settings"
|
||||
|
||||
local A2GCoordinateMenu = MENU_MISSION:New( "A2G Coordinate System", self.DefaultMenu )
|
||||
local MenuTime = timer.getTime()
|
||||
|
||||
local SettingsMenu = MENU_GROUP:New( MenuGroup, MenuText, RootMenu ):SetTime( MenuTime )
|
||||
|
||||
local A2GCoordinateMenu = MENU_GROUP:New( MenuGroup, "A2G Coordinate System", SettingsMenu ):SetTime( MenuTime )
|
||||
|
||||
if self:IsA2G_LL() then
|
||||
MENU_MISSION_COMMAND:New( "Activate BRA", A2GCoordinateMenu, self.A2GMenuSystem, self, "BRA" )
|
||||
MENU_MISSION_COMMAND:New( "Activate MGRS", A2GCoordinateMenu, self.A2GMenuSystem, self, "MGRS" )
|
||||
MENU_MISSION_COMMAND:New( "LL Accuracy 1", A2GCoordinateMenu, self.MenuLL_Accuracy, self, 1 )
|
||||
MENU_MISSION_COMMAND:New( "LL Accuracy 2", A2GCoordinateMenu, self.MenuLL_Accuracy, self, 2 )
|
||||
MENU_MISSION_COMMAND:New( "LL Accuracy 3", A2GCoordinateMenu, self.MenuLL_Accuracy, self, 3 )
|
||||
MENU_MISSION_COMMAND:New( "LL Decimal On", A2GCoordinateMenu, self.MenuLL_DMS, self, true )
|
||||
MENU_MISSION_COMMAND:New( "LL Decimal Off", A2GCoordinateMenu, self.MenuLL_DMS, self, false )
|
||||
|
||||
if not self:IsA2G_LL_DMS() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Min Sec (LL DMS)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
|
||||
if not self:IsA2G_LL_DDM() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if self:IsA2G_LL_DDM() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 1", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 2", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 3", A2GCoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 3 ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if not self:IsA2G_BR() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Bearing, Range (BR)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "BR" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if not self:IsA2G_MGRS() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Military Grid (MGRS)", A2GCoordinateMenu, self.A2GMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if self:IsA2G_MGRS() then
|
||||
MENU_MISSION_COMMAND:New( "Activate BRA", A2GCoordinateMenu, self.A2GMenuSystem, self, "BRA" )
|
||||
MENU_MISSION_COMMAND:New( "Activate LL", A2GCoordinateMenu, self.A2GMenuSystem, self, "LL" )
|
||||
MENU_MISSION_COMMAND:New( "MGRS Accuracy 1", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, 1 )
|
||||
MENU_MISSION_COMMAND:New( "MGRS Accuracy 2", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, 2 )
|
||||
MENU_MISSION_COMMAND:New( "MGRS Accuracy 3", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, 3 )
|
||||
MENU_MISSION_COMMAND:New( "MGRS Accuracy 4", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, 4 )
|
||||
MENU_MISSION_COMMAND:New( "MGRS Accuracy 5", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, 5 )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 1", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 2", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 3", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 3 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 4", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 4 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 5", A2GCoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 5 ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if self:IsA2G_BRA() then
|
||||
MENU_MISSION_COMMAND:New( "Activate MGRS", A2GCoordinateMenu, self.A2GMenuSystem, self, "MGRS" )
|
||||
MENU_MISSION_COMMAND:New( "Activate LL", A2GCoordinateMenu, self.A2GMenuSystem, self, "LL" )
|
||||
local A2ACoordinateMenu = MENU_GROUP:New( MenuGroup, "A2A Coordinate System", SettingsMenu ):SetTime( MenuTime )
|
||||
|
||||
if not self:IsA2A_LL_DMS() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Min Sec (LL DMS)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DMS" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
local A2ACoordinateMenu = MENU_MISSION:New( "A2A Coordinate System", self.DefaultMenu )
|
||||
|
||||
if self:IsA2A_BULLS() then
|
||||
MENU_MISSION_COMMAND:New( "Activate BRA", A2ACoordinateMenu, self.A2AMenuSystem, self, "BRA" )
|
||||
if not self:IsA2A_LL_DDM() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "LL DDM" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if self:IsA2A_BRA() then
|
||||
MENU_MISSION_COMMAND:New( "Activate BULLS", A2ACoordinateMenu, self.A2AMenuSystem, self, "BULLS" )
|
||||
|
||||
if self:IsA2A_LL_DDM() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 1", A2ACoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 2", A2ACoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "LL DDM Accuracy 3", A2ACoordinateMenu, self.MenuLL_DDM_Accuracy, self, MenuGroup, RootMenu, 3 ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if not self:IsA2A_BULLS() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Bullseye (BULLS)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BULLS" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
local MetricsMenu = MENU_MISSION:New( "Measures and Weights System", self.DefaultMenu )
|
||||
if not self:IsA2A_BRAA() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Bearing Range Altitude Aspect (BRAA)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "BRAA" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if not self:IsA2A_MGRS() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Military Grid (MGRS)", A2ACoordinateMenu, self.A2AMenuSystem, self, MenuGroup, RootMenu, "MGRS" ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if self:IsA2A_MGRS() then
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 1", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 1 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 2", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 2 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 3", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 3 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 4", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 4 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "MGRS Accuracy 5", A2ACoordinateMenu, self.MenuMGRS_Accuracy, self, MenuGroup, RootMenu, 5 ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
local MetricsMenu = MENU_GROUP:New( MenuGroup, "Measures and Weights System", SettingsMenu ):SetTime( MenuTime )
|
||||
|
||||
if self:IsMetric() then
|
||||
MENU_MISSION_COMMAND:New( "Activate Imperial", MetricsMenu, self.MenuMWSystem, self, false )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Imperial (Miles,Feet)", MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, false ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
if self:IsImperial() then
|
||||
MENU_MISSION_COMMAND:New( "Activate Metric", MetricsMenu, self.MenuMWSystem, self, true )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Metric (Kilometers,Meters)", MetricsMenu, self.MenuMWSystem, self, MenuGroup, RootMenu, true ):SetTime( MenuTime )
|
||||
end
|
||||
|
||||
local MessagesMenu = MENU_GROUP:New( MenuGroup, "Messages and Reports", SettingsMenu ):SetTime( MenuTime )
|
||||
|
||||
local UpdateMessagesMenu = MENU_GROUP:New( MenuGroup, "Update Messages", MessagesMenu ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "Off", UpdateMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Update, 0 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "5 seconds", UpdateMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Update, 5 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "10 seconds", UpdateMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Update, 10 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "15 seconds", UpdateMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Update, 15 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "30 seconds", UpdateMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Update, 30 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "1 minute", UpdateMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Update, 60 ):SetTime( MenuTime )
|
||||
|
||||
local InformationMessagesMenu = MENU_GROUP:New( MenuGroup, "Information Messages", MessagesMenu ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "5 seconds", InformationMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Information, 5 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "10 seconds", InformationMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Information, 10 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "15 seconds", InformationMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Information, 15 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "30 seconds", InformationMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Information, 30 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "1 minute", InformationMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Information, 60 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", InformationMessagesMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Information, 120 ):SetTime( MenuTime )
|
||||
|
||||
local BriefingReportsMenu = MENU_GROUP:New( MenuGroup, "Briefing Reports", MessagesMenu ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "15 seconds", BriefingReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Briefing, 15 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "30 seconds", BriefingReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Briefing, 30 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "1 minute", BriefingReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Briefing, 60 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", BriefingReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Briefing, 120 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "3 minutes", BriefingReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Briefing, 180 ):SetTime( MenuTime )
|
||||
|
||||
local OverviewReportsMenu = MENU_GROUP:New( MenuGroup, "Overview Reports", MessagesMenu ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "15 seconds", OverviewReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Overview, 15 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "30 seconds", OverviewReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Overview, 30 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "1 minute", OverviewReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Overview, 60 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", OverviewReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Overview, 120 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "3 minutes", OverviewReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.Overview, 180 ):SetTime( MenuTime )
|
||||
|
||||
local DetailedReportsMenu = MENU_GROUP:New( MenuGroup, "Detailed Reports", MessagesMenu ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "15 seconds", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 15 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "30 seconds", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 30 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "1 minute", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 60 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "2 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 120 ):SetTime( MenuTime )
|
||||
MENU_GROUP_COMMAND:New( MenuGroup, "3 minutes", DetailedReportsMenu, self.MenuMessageTimingsSystem, self, MenuGroup, RootMenu, MESSAGE.Type.DetailedReportsMenu, 180 ):SetTime( MenuTime )
|
||||
|
||||
|
||||
SettingsMenu:Remove( MenuTime )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@ -293,35 +418,39 @@ do -- SETTINGS
|
||||
-- @return #SETTINGS
|
||||
function SETTINGS:SetPlayerMenu( PlayerUnit )
|
||||
|
||||
local MenuText = "Player Settings"
|
||||
self.MenuText = MenuText
|
||||
|
||||
local SettingsMenu = _SETTINGS.SettingsMenu
|
||||
|
||||
local PlayerGroup = PlayerUnit:GetGroup()
|
||||
local PlayerName = PlayerUnit:GetPlayerName()
|
||||
local PlayerNames = PlayerGroup:GetPlayerNames()
|
||||
|
||||
local GroupMenu = MENU_GROUP:New( PlayerGroup, MenuText, SettingsMenu )
|
||||
local PlayerMenu = MENU_GROUP:New( PlayerGroup, 'Settings "' .. PlayerName .. '"', GroupMenu )
|
||||
local PlayerMenu = MENU_GROUP:New( PlayerGroup, 'Settings "' .. PlayerName .. '"' )
|
||||
|
||||
self.PlayerMenu = PlayerMenu
|
||||
|
||||
local A2GCoordinateMenu = MENU_GROUP:New( PlayerGroup, "A2G Coordinate System", PlayerMenu )
|
||||
|
||||
if self:IsA2G_LL() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate BRA", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRA" )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate MGRS", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL Accuracy 1", A2GCoordinateMenu, self.MenuGroupLL_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL Accuracy 2", A2GCoordinateMenu, self.MenuGroupLL_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL Accuracy 3", A2GCoordinateMenu, self.MenuGroupLL_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL Decimal On", A2GCoordinateMenu, self.MenuGroupLL_DMSSystem, self, PlayerUnit, PlayerGroup, PlayerName, true )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL Decimal Off", A2GCoordinateMenu, self.MenuGroupLL_DMSSystem, self, PlayerUnit, PlayerGroup, PlayerName, false )
|
||||
if not self:IsA2G_LL_DMS() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Lat/Lon Degree Min Sec (LL DMS)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
|
||||
end
|
||||
|
||||
if not self:IsA2G_LL_DDM() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
|
||||
end
|
||||
|
||||
if self:IsA2G_LL_DDM() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 1", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 2", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 3", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
|
||||
end
|
||||
|
||||
if not self:IsA2G_BR() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Bearing, Range (BR)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "BR" )
|
||||
end
|
||||
|
||||
if not self:IsA2G_MGRS() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
|
||||
end
|
||||
|
||||
if self:IsA2G_MGRS() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate BRA", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRA" )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate LL", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL" )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "MGRS Accuracy 1", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "MGRS Accuracy 2", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "MGRS Accuracy 3", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
|
||||
@ -329,30 +458,93 @@ do -- SETTINGS
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "MGRS Accuracy 5", A2GCoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 5 )
|
||||
end
|
||||
|
||||
if self:IsA2G_BRA() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate MGRS", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate LL", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL" )
|
||||
end
|
||||
|
||||
local A2ACoordinateMenu = MENU_GROUP:New( PlayerGroup, "A2A Coordinate System", PlayerMenu )
|
||||
|
||||
if self:IsA2A_BULLS() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate BRA", A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRA" )
|
||||
|
||||
if not self:IsA2A_LL_DMS() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Lat/Lon Degree Min Sec (LL DMS)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DMS" )
|
||||
end
|
||||
|
||||
if self:IsA2A_BRA() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate BULLS", A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BULLS" )
|
||||
if not self:IsA2A_LL_DDM() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Lat/Lon Degree Dec Min (LL DDM)", A2GCoordinateMenu, self.MenuGroupA2GSystem, self, PlayerUnit, PlayerGroup, PlayerName, "LL DDM" )
|
||||
end
|
||||
|
||||
if self:IsA2A_LL_DDM() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 1", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 2", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "LL DDM Accuracy 3", A2GCoordinateMenu, self.MenuGroupLL_DDM_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
|
||||
end
|
||||
|
||||
if not self:IsA2A_BULLS() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Bullseye (BULLS)", A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BULLS" )
|
||||
end
|
||||
|
||||
if not self:IsA2A_BRAA() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Bearing Range Altitude Aspect (BRAA)", A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "BRAA" )
|
||||
end
|
||||
|
||||
if not self:IsA2A_MGRS() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS)", A2ACoordinateMenu, self.MenuGroupA2ASystem, self, PlayerUnit, PlayerGroup, PlayerName, "MGRS" )
|
||||
end
|
||||
|
||||
if self:IsA2A_MGRS() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 1", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 1 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 2", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 2 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 3", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 3 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 4", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 4 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Military Grid (MGRS) Accuracy 5", A2ACoordinateMenu, self.MenuGroupMGRS_AccuracySystem, self, PlayerUnit, PlayerGroup, PlayerName, 5 )
|
||||
end
|
||||
|
||||
local MetricsMenu = MENU_GROUP:New( PlayerGroup, "Measures and Weights System", PlayerMenu )
|
||||
|
||||
if self:IsMetric() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate Imperial", MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, false )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Imperial (Miles,Feet)", MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, false )
|
||||
end
|
||||
|
||||
if self:IsImperial() then
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Activate Metric", MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, true )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Metric (Kilometers,Meters)", MetricsMenu, self.MenuGroupMWSystem, self, PlayerUnit, PlayerGroup, PlayerName, true )
|
||||
end
|
||||
|
||||
|
||||
local MessagesMenu = MENU_GROUP:New( PlayerGroup, "Messages and Reports", PlayerMenu )
|
||||
|
||||
local UpdateMessagesMenu = MENU_GROUP:New( PlayerGroup, "Update Messages", MessagesMenu )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "Off", UpdateMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Update, 0 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "5 seconds", UpdateMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Update, 5 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "10 seconds", UpdateMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Update, 10 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "15 seconds", UpdateMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Update, 15 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "30 seconds", UpdateMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Update, 30 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "1 minute", UpdateMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Update, 60 )
|
||||
|
||||
local InformationMessagesMenu = MENU_GROUP:New( PlayerGroup, "Information Messages", MessagesMenu )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "5 seconds", InformationMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Information, 5 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "10 seconds", InformationMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Information, 10 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "15 seconds", InformationMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Information, 15 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "30 seconds", InformationMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Information, 30 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "1 minute", InformationMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Information, 60 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "2 minutes", InformationMessagesMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Information, 120 )
|
||||
|
||||
local BriefingReportsMenu = MENU_GROUP:New( PlayerGroup, "Briefing Reports", MessagesMenu )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "15 seconds", BriefingReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Briefing, 15 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "30 seconds", BriefingReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Briefing, 30 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "1 minute", BriefingReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Briefing, 60 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "2 minutes", BriefingReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Briefing, 120 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "3 minutes", BriefingReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Briefing, 180 )
|
||||
|
||||
local OverviewReportsMenu = MENU_GROUP:New( PlayerGroup, "Overview Reports", MessagesMenu )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "15 seconds", OverviewReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Overview, 15 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "30 seconds", OverviewReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Overview, 30 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "1 minute", OverviewReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Overview, 60 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "2 minutes", OverviewReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Overview, 120 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "3 minutes", OverviewReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.Overview, 180 )
|
||||
|
||||
local DetailedReportsMenu = MENU_GROUP:New( PlayerGroup, "Detailed Reports", MessagesMenu )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "15 seconds", DetailedReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.DetailedReportsMenu, 15 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "30 seconds", DetailedReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.DetailedReportsMenu, 30 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "1 minute", DetailedReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.DetailedReportsMenu, 60 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "2 minutes", DetailedReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.DetailedReportsMenu, 120 )
|
||||
MENU_GROUP_COMMAND:New( PlayerGroup, "3 minutes", DetailedReportsMenu, self.MenuGroupMessageTimingsSystem, self, PlayerUnit, PlayerGroup, PlayerName, MESSAGE.Type.DetailedReportsMenu, 180 )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
@ -372,40 +564,44 @@ do -- SETTINGS
|
||||
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:A2GMenuSystem( A2GSystem )
|
||||
function SETTINGS:A2GMenuSystem( MenuGroup, RootMenu, A2GSystem )
|
||||
self.A2GSystem = A2GSystem
|
||||
self:SetSystemMenu()
|
||||
MESSAGE:New( string.format("Settings: Default A2G coordinate system set to %s for all players!", A2GSystem ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:A2AMenuSystem( A2ASystem )
|
||||
function SETTINGS:A2AMenuSystem( MenuGroup, RootMenu, A2ASystem )
|
||||
self.A2ASystem = A2ASystem
|
||||
self:SetSystemMenu()
|
||||
MESSAGE:New( string.format("Settings: Default A2A coordinate system set to %s for all players!", A2ASystem ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuLL_Accuracy( LL_Accuracy )
|
||||
function SETTINGS:MenuLL_DDM_Accuracy( MenuGroup, RootMenu, LL_Accuracy )
|
||||
self.LL_Accuracy = LL_Accuracy
|
||||
self:SetSystemMenu()
|
||||
MESSAGE:New( string.format("Settings: Default LL accuracy set to %s for all players!", LL_Accuracy ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuLL_DMS( LL_DMS )
|
||||
self.LL_DMS = LL_DMS
|
||||
self:SetSystemMenu()
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuMGRS_Accuracy( MGRS_Accuracy )
|
||||
function SETTINGS:MenuMGRS_Accuracy( MenuGroup, RootMenu, MGRS_Accuracy )
|
||||
self.MGRS_Accuracy = MGRS_Accuracy
|
||||
self:SetSystemMenu()
|
||||
MESSAGE:New( string.format("Settings: Default MGRS accuracy set to %s for all players!", MGRS_Accuracy ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuMWSystem( MW )
|
||||
function SETTINGS:MenuMWSystem( MenuGroup, RootMenu, MW )
|
||||
self.Metric = MW
|
||||
MESSAGE:New( string.format("Settings: Default measurement format set to %s for all players!.", MW and "Metric" or "Imperial" ), 5 ):ToAll()
|
||||
self:SetSystemMenu()
|
||||
MESSAGE:New( string.format("Settings: Default measurement format set to %s for all players!", MW and "Metric" or "Imperial" ), 5 ):ToAll()
|
||||
self:SetSystemMenu( MenuGroup, RootMenu )
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuMessageTimingsSystem( MenuGroup, RootMenu, MessageType, MessageTime )
|
||||
self:SetMessageTime( MessageType, MessageTime )
|
||||
MESSAGE:New( string.format( "Settings: Default message time set for %s to %d.", MessageType, MessageTime ), 5 ):ToAll()
|
||||
end
|
||||
|
||||
do
|
||||
@ -413,7 +609,7 @@ do -- SETTINGS
|
||||
function SETTINGS:MenuGroupA2GSystem( PlayerUnit, PlayerGroup, PlayerName, A2GSystem )
|
||||
BASE:E( {self, PlayerUnit:GetName(), A2GSystem} )
|
||||
self.A2GSystem = A2GSystem
|
||||
MESSAGE:New( string.format("Settings: A2G format set to %s for player %s.", A2GSystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
MESSAGE:New( string.format( "Settings: A2G format set to %s for player %s.", A2GSystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
self:RemovePlayerMenu(PlayerUnit)
|
||||
self:SetPlayerMenu(PlayerUnit)
|
||||
end
|
||||
@ -421,42 +617,40 @@ do -- SETTINGS
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupA2ASystem( PlayerUnit, PlayerGroup, PlayerName, A2ASystem )
|
||||
self.A2ASystem = A2ASystem
|
||||
MESSAGE:New( string.format("Settings: A2A format set to %s for player %s.", A2ASystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
MESSAGE:New( string.format( "Settings: A2A format set to %s for player %s.", A2ASystem, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
self:RemovePlayerMenu(PlayerUnit)
|
||||
self:SetPlayerMenu(PlayerUnit)
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupLL_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy )
|
||||
function SETTINGS:MenuGroupLL_DDM_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, LL_Accuracy )
|
||||
self.LL_Accuracy = LL_Accuracy
|
||||
MESSAGE:New( string.format("Settings: A2G LL format accuracy set to %d for player %s.", LL_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
MESSAGE:New( string.format( "Settings: A2G LL format accuracy set to %d for player %s.", LL_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
self:RemovePlayerMenu(PlayerUnit)
|
||||
self:SetPlayerMenu(PlayerUnit)
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupLL_DMSSystem( PlayerUnit, PlayerGroup, PlayerName, LL_DMS )
|
||||
self.LL_DMS = LL_DMS
|
||||
MESSAGE:New( string.format("Settings: A2G LL format mode set to %s for player %s.", LL_DMS and "DMS" or "HMS", PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
self:RemovePlayerMenu(PlayerUnit)
|
||||
self:SetPlayerMenu(PlayerUnit)
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupMGRS_AccuracySystem( PlayerUnit, PlayerGroup, PlayerName, MGRS_Accuracy )
|
||||
self.MGRS_Accuracy = MGRS_Accuracy
|
||||
MESSAGE:New( string.format("Settings: A2G MGRS format accuracy set to %d for player %s.", MGRS_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
MESSAGE:New( string.format( "Settings: A2G MGRS format accuracy set to %d for player %s.", MGRS_Accuracy, PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
self:RemovePlayerMenu(PlayerUnit)
|
||||
self:SetPlayerMenu(PlayerUnit)
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupMWSystem( PlayerUnit, PlayerGroup, PlayerName, MW )
|
||||
self.Metrics = MW
|
||||
MESSAGE:New( string.format("Settings: Measurement format set to %s for player %s.", MW and "Metric" or "Imperial", PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
self.Metric = MW
|
||||
MESSAGE:New( string.format( "Settings: Measurement format set to %s for player %s.", MW and "Metric" or "Imperial", PlayerName ), 5 ):ToGroup( PlayerGroup )
|
||||
self:RemovePlayerMenu(PlayerUnit)
|
||||
self:SetPlayerMenu(PlayerUnit)
|
||||
end
|
||||
|
||||
--- @param #SETTINGS self
|
||||
function SETTINGS:MenuGroupMessageTimingsSystem( PlayerUnit, PlayerGroup, PlayerName, MessageType, MessageTime )
|
||||
self:SetMessageTime( MessageType, MessageTime )
|
||||
MESSAGE:New( string.format( "Settings: Default message time set for %s to %d.", MessageType, MessageTime ), 5 ):ToGroup( PlayerGroup )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
--- **Functional** -- Spawn dynamically new GROUPs in your missions.
|
||||
--- **Core** -- SPAWN class dynamically spawns new groups of units in your missions.
|
||||
--
|
||||
-- 
|
||||
--
|
||||
@ -55,8 +55,12 @@
|
||||
|
||||
--- # SPAWN class, extends @{Base#BASE}
|
||||
--
|
||||
-- -- 
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- The SPAWN class allows to spawn dynamically new groups.
|
||||
-- Each SPAWN object needs to be have a related **template group** setup in the Mission Editor (ME),
|
||||
-- Each SPAWN object needs to be have related **template groups** setup in the Mission Editor (ME),
|
||||
-- which is a normal group with the **Late Activation** flag set.
|
||||
-- This template group will never be activated in your mission.
|
||||
-- SPAWN uses that **template group** to reference to all the characteristics
|
||||
@ -200,6 +204,7 @@
|
||||
-- * @{#SPAWN.SpawnFromStatic}(): Spawn a new group from a structure, taking the position of a @{Static}.
|
||||
-- * @{#SPAWN.SpawnFromUnit}(): Spawn a new group taking the position of a @{Unit}.
|
||||
-- * @{#SPAWN.SpawnInZone}(): Spawn a new group in a @{Zone}.
|
||||
-- * @{#SPAWN.SpawnAtAirbase}(): Spawn a new group at an @{Airbase}, which can be an airdrome, ship or helipad.
|
||||
--
|
||||
-- Note that @{#SPAWN.Spawn} and @{#SPAWN.ReSpawn} return a @{GROUP#GROUP.New} object, that contains a reference to the DCSGroup object.
|
||||
-- You can use the @{GROUP} object to do further actions with the DCSGroup.
|
||||
@ -270,8 +275,12 @@ SPAWN = {
|
||||
-- @extends Wrapper.Group#GROUP.Takeoff
|
||||
|
||||
--- @field #SPAWN.Takeoff Takeoff
|
||||
SPAWN.Takeoff = GROUP.Takeoff
|
||||
|
||||
SPAWN.Takeoff = {
|
||||
Air = 1,
|
||||
Runway = 2,
|
||||
Hot = 3,
|
||||
Cold = 4,
|
||||
}
|
||||
|
||||
--- @type SPAWN.SpawnZoneTable
|
||||
-- @list <Core.Zone#ZONE_BASE> SpawnZone
|
||||
@ -289,7 +298,7 @@ function SPAWN:New( SpawnTemplatePrefix )
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWN
|
||||
self:F( { SpawnTemplatePrefix } )
|
||||
|
||||
local TemplateGroup = Group.getByName( SpawnTemplatePrefix )
|
||||
local TemplateGroup = GROUP:FindByName( SpawnTemplatePrefix )
|
||||
if TemplateGroup then
|
||||
self.SpawnTemplatePrefix = SpawnTemplatePrefix
|
||||
self.SpawnIndex = 0
|
||||
@ -333,7 +342,7 @@ function SPAWN:NewWithAlias( SpawnTemplatePrefix, SpawnAliasPrefix )
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
self:F( { SpawnTemplatePrefix, SpawnAliasPrefix } )
|
||||
|
||||
local TemplateGroup = Group.getByName( SpawnTemplatePrefix )
|
||||
local TemplateGroup = GROUP:FindByName( SpawnTemplatePrefix )
|
||||
if TemplateGroup then
|
||||
self.SpawnTemplatePrefix = SpawnTemplatePrefix
|
||||
self.SpawnAliasPrefix = SpawnAliasPrefix
|
||||
@ -520,6 +529,73 @@ function SPAWN:InitRandomizeTemplate( SpawnTemplatePrefixTable )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Randomize templates to be used as the unit representatives for the Spawned group, defined using a SET_GROUP object.
|
||||
-- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor,
|
||||
-- but they will all follow the same Template route and have the same prefix name.
|
||||
-- In other words, this method randomizes between a defined set of groups the template to be used for each new spawn of a group.
|
||||
-- @param #SPAWN self
|
||||
-- @param Core.Set#SET_GROUP SpawnTemplateSet A SET_GROUP object set, that contains the groups that are possible unit representatives of the group to be spawned.
|
||||
-- @return #SPAWN
|
||||
-- @usage
|
||||
-- -- NATO Tank Platoons invading Gori.
|
||||
--
|
||||
-- -- Choose between different 'US Tank Platoon Template' configurations to be spawned for the
|
||||
-- -- 'US Tank Platoon Left', 'US Tank Platoon Middle' and 'US Tank Platoon Right' SPAWN objects.
|
||||
--
|
||||
-- -- Each new SPAWN will randomize the route, with a defined time interval of 200 seconds with 40% time variation (randomization) and
|
||||
-- -- with a limit set of maximum 12 Units alive simulteneously and 150 Groups to be spawned during the whole mission.
|
||||
--
|
||||
-- Spawn_US_PlatoonSet = SET_GROUP:New():FilterPrefixes( "US Tank Platoon Templates" ):FilterOnce()
|
||||
--
|
||||
-- --- Now use the Spawn_US_PlatoonSet to define the templates using InitRandomizeTemplateSet.
|
||||
-- Spawn_US_Platoon_Left = SPAWN:New( 'US Tank Platoon Left' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplateSet( Spawn_US_PlatoonSet ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
function SPAWN:InitRandomizeTemplateSet( SpawnTemplateSet ) -- R2.3
|
||||
self:F( { self.SpawnTemplatePrefix } )
|
||||
|
||||
self.SpawnTemplatePrefixTable = SpawnTemplateSet:GetSetNames()
|
||||
self.SpawnRandomizeTemplate = true
|
||||
|
||||
for SpawnGroupID = 1, self.SpawnMaxGroups do
|
||||
self:_RandomizeTemplate( SpawnGroupID )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Randomize templates to be used as the unit representatives for the Spawned group, defined by specifying the prefix names.
|
||||
-- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor,
|
||||
-- but they will all follow the same Template route and have the same prefix name.
|
||||
-- In other words, this method randomizes between a defined set of groups the template to be used for each new spawn of a group.
|
||||
-- @param #SPAWN self
|
||||
-- @param #string SpawnTemplatePrefixes A string or a list of string that contains the prefixes of the groups that are possible unit representatives of the group to be spawned.
|
||||
-- @return #SPAWN
|
||||
-- @usage
|
||||
-- -- NATO Tank Platoons invading Gori.
|
||||
--
|
||||
-- -- Choose between different 'US Tank Platoon Templates' configurations to be spawned for the
|
||||
-- -- 'US Tank Platoon Left', 'US Tank Platoon Middle' and 'US Tank Platoon Right' SPAWN objects.
|
||||
--
|
||||
-- -- Each new SPAWN will randomize the route, with a defined time interval of 200 seconds with 40% time variation (randomization) and
|
||||
-- -- with a limit set of maximum 12 Units alive simulteneously and 150 Groups to be spawned during the whole mission.
|
||||
--
|
||||
-- Spawn_US_Platoon_Left = SPAWN:New( 'US Tank Platoon Left' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Middle = SPAWN:New( 'US Tank Platoon Middle' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
-- Spawn_US_Platoon_Right = SPAWN:New( 'US Tank Platoon Right' ):InitLimit( 12, 150 ):Schedule( 200, 0.4 ):InitRandomizeTemplatePrefixes( "US Tank Platoon Templates" ):InitRandomizeRoute( 3, 3, 2000 )
|
||||
function SPAWN:InitRandomizeTemplatePrefixes( SpawnTemplatePrefixes ) --R2.3
|
||||
self:F( { self.SpawnTemplatePrefix } )
|
||||
|
||||
local SpawnTemplateSet = SET_GROUP:New():FilterPrefixes( SpawnTemplatePrefixes ):FilterOnce()
|
||||
|
||||
self:InitRandomizeTemplateSet( SpawnTemplateSet )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- When spawning a new group, make the grouping of the units according the InitGrouping setting.
|
||||
-- @param #SPAWN self
|
||||
-- @param #number Grouping Indicates the maximum amount of units in the group.
|
||||
@ -982,19 +1058,53 @@ function SPAWN:OnSpawnGroup( SpawnCallBackFunction, ... )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Will spawn a group at an airbase.
|
||||
--- Will spawn a group at an @{Airbase}.
|
||||
-- This method is mostly advisable to be used if you want to simulate spawning units at an airbase.
|
||||
-- Note that each point in the route assigned to the spawning group is reset to the point of the spawn.
|
||||
-- You can use the returned group to further define the route to be followed.
|
||||
--
|
||||
-- The @{Airbase#AIRBASE} object must refer to a valid airbase known in the sim.
|
||||
-- You can use the following enumerations to search for the pre-defined airbases on the current known maps of DCS:
|
||||
--
|
||||
-- * @{Airbase#AIRBASE.Caucasus}: The airbases on the Caucasus map.
|
||||
-- * @{Airbase#AIRBASE.Nevada}: The airbases on the Nevada (NTTR) map.
|
||||
-- * @{Airbase#AIRBASE.Normandy}: The airbases on the Normandy map.
|
||||
--
|
||||
-- Use the method @{Airbase#AIRBASE.FindByName}() to retrieve the airbase object.
|
||||
-- The known AIRBASE objects are automatically imported at mission start by MOOSE.
|
||||
-- Therefore, there isn't any New() constructor defined for AIRBASE objects.
|
||||
--
|
||||
-- Ships and Farps are added within the mission, and are therefore not known.
|
||||
-- For these AIRBASE objects, there isn't an @{Airbase#AIRBASE} enumeration defined.
|
||||
-- You need to provide the **exact name** of the airbase as the parameter to the @{Airbase#AIRBASE.FindByName}() method!
|
||||
--
|
||||
-- @param #SPAWN self
|
||||
-- @param Wrapper.Airbase#AIRBASE Airbase The @{Airbase} where to spawn the group.
|
||||
-- @param Wrapper.Airbase#AIRBASE SpawnAirbase The @{Airbase} where to spawn the group.
|
||||
-- @param #SPAWN.Takeoff Takeoff (optional) The location and takeoff method. Default is Hot.
|
||||
-- @param #number TakeoffAltitude (optional) The altitude above the ground.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil Nothing was spawned.
|
||||
function SPAWN:SpawnAtAirbase( Airbase, Takeoff ) -- R2.2
|
||||
self:F( { self.SpawnTemplatePrefix, Airbase } )
|
||||
-- @usage
|
||||
-- Spawn_Plane = SPAWN:New( "Plane" )
|
||||
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Cold )
|
||||
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Hot )
|
||||
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( AIRBASE.Caucasus.Krymsk ), SPAWN.Takeoff.Runway )
|
||||
--
|
||||
-- Spawn_Plane:SpawnAtAirbase( AIRBASE:FindByName( "Carrier" ), SPAWN.Takeoff.Cold )
|
||||
--
|
||||
-- Spawn_Heli = SPAWN:New( "Heli")
|
||||
--
|
||||
-- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "FARP Cold" ), SPAWN.Takeoff.Cold )
|
||||
-- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "FARP Hot" ), SPAWN.Takeoff.Hot )
|
||||
-- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "FARP Runway" ), SPAWN.Takeoff.Runway )
|
||||
-- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "FARP Air" ), SPAWN.Takeoff.Air )
|
||||
--
|
||||
-- Spawn_Heli:SpawnAtAirbase( AIRBASE:FindByName( "Carrier" ), SPAWN.Takeoff.Cold )
|
||||
--
|
||||
function SPAWN:SpawnAtAirbase( SpawnAirbase, Takeoff, TakeoffAltitude ) -- R2.2
|
||||
self:E( { self.SpawnTemplatePrefix, SpawnAirbase, Takeoff, TakeoffAltitude } )
|
||||
|
||||
local PointVec3 = Airbase:GetPointVec3()
|
||||
local PointVec3 = SpawnAirbase:GetPointVec3()
|
||||
self:T2(PointVec3)
|
||||
|
||||
Takeoff = Takeoff or SPAWN.Takeoff.Hot
|
||||
@ -1005,34 +1115,87 @@ function SPAWN:SpawnAtAirbase( Airbase, Takeoff ) -- R2.2
|
||||
|
||||
if SpawnTemplate then
|
||||
|
||||
self:T( { "Current point of ", self.SpawnTemplatePrefix, Airbase } )
|
||||
self:T( { "Current point of ", self.SpawnTemplatePrefix, SpawnAirbase } )
|
||||
|
||||
local SpawnPoint = SpawnTemplate.route.points[1]
|
||||
|
||||
-- These are only for ships.
|
||||
SpawnPoint.linkUnit = nil
|
||||
SpawnPoint.helipadId = nil
|
||||
SpawnPoint.airdromeId = nil
|
||||
|
||||
local AirbaseID = SpawnAirbase:GetID()
|
||||
local AirbaseCategory = SpawnAirbase:GetDesc().category
|
||||
self:F( { AirbaseCategory = AirbaseCategory } )
|
||||
|
||||
if AirbaseCategory == Airbase.Category.SHIP then
|
||||
SpawnPoint.linkUnit = AirbaseID
|
||||
SpawnPoint.helipadId = AirbaseID
|
||||
elseif AirbaseCategory == Airbase.Category.HELIPAD then
|
||||
SpawnPoint.linkUnit = AirbaseID
|
||||
SpawnPoint.helipadId = AirbaseID
|
||||
elseif AirbaseCategory == Airbase.Category.AIRDROME then
|
||||
SpawnPoint.airdromeId = AirbaseID
|
||||
end
|
||||
|
||||
SpawnPoint.alt = 0
|
||||
|
||||
SpawnPoint.type = GROUPTEMPLATE.Takeoff[Takeoff][1] -- type
|
||||
SpawnPoint.action = GROUPTEMPLATE.Takeoff[Takeoff][2] -- action
|
||||
|
||||
|
||||
-- Translate the position of the Group Template to the Vec3.
|
||||
for UnitID = 1, #SpawnTemplate.units do
|
||||
self:T( 'Before Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y )
|
||||
|
||||
-- These cause a lot of confusion.
|
||||
local UnitTemplate = SpawnTemplate.units[UnitID]
|
||||
|
||||
UnitTemplate.parking = nil
|
||||
UnitTemplate.parking_id = nil
|
||||
UnitTemplate.alt = 0
|
||||
|
||||
local SX = UnitTemplate.x
|
||||
local SY = UnitTemplate.y
|
||||
local BX = SpawnTemplate.route.points[1].x
|
||||
local BY = SpawnTemplate.route.points[1].y
|
||||
local BX = SpawnPoint.x
|
||||
local BY = SpawnPoint.y
|
||||
local TX = PointVec3.x + ( SX - BX )
|
||||
local TY = PointVec3.z + ( SY - BY )
|
||||
SpawnTemplate.units[UnitID].x = TX
|
||||
SpawnTemplate.units[UnitID].y = TY
|
||||
SpawnTemplate.units[UnitID].alt = PointVec3.y
|
||||
self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y )
|
||||
|
||||
UnitTemplate.x = TX
|
||||
UnitTemplate.y = TY
|
||||
|
||||
if Takeoff == GROUP.Takeoff.Air then
|
||||
UnitTemplate.alt = PointVec3.y + ( TakeoffAltitude or 200 )
|
||||
--else
|
||||
-- UnitTemplate.alt = PointVec3.y + 10
|
||||
end
|
||||
self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. UnitTemplate.y )
|
||||
end
|
||||
|
||||
SpawnTemplate.route.points[1].x = PointVec3.x
|
||||
SpawnTemplate.route.points[1].y = PointVec3.z
|
||||
SpawnTemplate.route.points[1].alt = Airbase.y
|
||||
SpawnTemplate.route.points[1].type = GROUPTEMPLATE.Takeoff[Takeoff]
|
||||
SpawnTemplate.route.points[1].airdromeId = Airbase:GetID()
|
||||
SpawnPoint.x = PointVec3.x
|
||||
SpawnPoint.y = PointVec3.z
|
||||
|
||||
if Takeoff == GROUP.Takeoff.Air then
|
||||
SpawnPoint.alt = PointVec3.y + ( TakeoffAltitude or 200 )
|
||||
--else
|
||||
-- SpawnPoint.alt = PointVec3.y + 10
|
||||
end
|
||||
|
||||
SpawnTemplate.x = PointVec3.x
|
||||
SpawnTemplate.y = PointVec3.z
|
||||
|
||||
return self:SpawnWithIndex( self.SpawnIndex )
|
||||
|
||||
local GroupSpawned = self:SpawnWithIndex( self.SpawnIndex )
|
||||
|
||||
-- When spawned in the air, we need to generate a Takeoff Event
|
||||
|
||||
if Takeoff == GROUP.Takeoff.Air then
|
||||
for UnitID, UnitSpawned in pairs( GroupSpawned:GetUnits() ) do
|
||||
SCHEDULER:New( nil, BASE.CreateEventTakeoff, { timer.getTime(), UnitSpawned:GetDCSObject() } , 1 )
|
||||
end
|
||||
end
|
||||
|
||||
return GroupSpawned
|
||||
end
|
||||
end
|
||||
|
||||
@ -1068,6 +1231,8 @@ function SPAWN:SpawnFromVec3( Vec3, SpawnIndex )
|
||||
if SpawnTemplate then
|
||||
|
||||
self:T( { "Current point of ", self.SpawnTemplatePrefix, Vec3 } )
|
||||
|
||||
local TemplateHeight = SpawnTemplate.route.points[1].alt
|
||||
|
||||
-- Translate the position of the Group Template to the Vec3.
|
||||
for UnitID = 1, #SpawnTemplate.units do
|
||||
@ -1081,16 +1246,20 @@ function SPAWN:SpawnFromVec3( Vec3, SpawnIndex )
|
||||
local TY = Vec3.z + ( SY - BY )
|
||||
SpawnTemplate.units[UnitID].x = TX
|
||||
SpawnTemplate.units[UnitID].y = TY
|
||||
SpawnTemplate.units[UnitID].alt = Vec3.y
|
||||
if SpawnTemplate.CategoryID ~= Group.Category.SHIP then
|
||||
SpawnTemplate.units[UnitID].alt = Vec3.y or TemplateHeight
|
||||
end
|
||||
self:T( 'After Translation SpawnTemplate.units['..UnitID..'].x = ' .. SpawnTemplate.units[UnitID].x .. ', SpawnTemplate.units['..UnitID..'].y = ' .. SpawnTemplate.units[UnitID].y )
|
||||
end
|
||||
|
||||
SpawnTemplate.route.points[1].x = Vec3.x
|
||||
SpawnTemplate.route.points[1].y = Vec3.z
|
||||
SpawnTemplate.route.points[1].alt = Vec3.y
|
||||
|
||||
if SpawnTemplate.CategoryID ~= Group.Category.SHIP then
|
||||
SpawnTemplate.route.points[1].alt = Vec3.y or TemplateHeight
|
||||
end
|
||||
SpawnTemplate.x = Vec3.x
|
||||
SpawnTemplate.y = Vec3.z
|
||||
SpawnTemplate.alt = Vec3.y or TemplateHeight
|
||||
|
||||
return self:SpawnWithIndex( self.SpawnIndex )
|
||||
end
|
||||
@ -1105,14 +1274,31 @@ end
|
||||
-- You can use the returned group to further define the route to be followed.
|
||||
-- @param #SPAWN self
|
||||
-- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 coordinates where to spawn the group.
|
||||
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
|
||||
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
|
||||
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil Nothing was spawned.
|
||||
function SPAWN:SpawnFromVec2( Vec2, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, Vec2, SpawnIndex } )
|
||||
-- @usage
|
||||
--
|
||||
-- local SpawnVec2 = ZONE:New( ZoneName ):GetVec2()
|
||||
--
|
||||
-- -- Spawn at the zone center position at the height specified in the ME of the group template!
|
||||
-- SpawnAirplanes:SpawnFromVec2( SpawnVec2 )
|
||||
--
|
||||
-- -- Spawn from the static position at the height randomized between 2000 and 4000 meters.
|
||||
-- SpawnAirplanes:SpawnFromVec2( SpawnVec2, 2000, 4000 )
|
||||
--
|
||||
function SPAWN:SpawnFromVec2( Vec2, MinHeight, MaxHeight, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, self.SpawnIndex, Vec2, MinHeight, MaxHeight, SpawnIndex } )
|
||||
|
||||
local PointVec2 = POINT_VEC2:NewFromVec2( Vec2 )
|
||||
return self:SpawnFromVec3( PointVec2:GetVec3(), SpawnIndex )
|
||||
local Height = nil
|
||||
|
||||
if MinHeight and MaxHeight then
|
||||
Height = math.random( MinHeight, MaxHeight)
|
||||
end
|
||||
|
||||
return self:SpawnFromVec3( { x = Vec2.x, y = Height, z = Vec2.y }, SpawnIndex ) -- y can be nil. In this case, spawn on the ground for vehicles, and in the template altitude for air.
|
||||
end
|
||||
|
||||
|
||||
@ -1121,14 +1307,26 @@ end
|
||||
-- You can use the returned group to further define the route to be followed.
|
||||
-- @param #SPAWN self
|
||||
-- @param Wrapper.Unit#UNIT HostUnit The air or ground unit dropping or unloading the group.
|
||||
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
|
||||
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
|
||||
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil Nothing was spawned.
|
||||
function SPAWN:SpawnFromUnit( HostUnit, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, HostUnit, SpawnIndex } )
|
||||
-- @usage
|
||||
--
|
||||
-- local SpawnStatic = STATIC:FindByName( StaticName )
|
||||
--
|
||||
-- -- Spawn from the static position at the height specified in the ME of the group template!
|
||||
-- SpawnAirplanes:SpawnFromUnit( SpawnStatic )
|
||||
--
|
||||
-- -- Spawn from the static position at the height randomized between 2000 and 4000 meters.
|
||||
-- SpawnAirplanes:SpawnFromUnit( SpawnStatic, 2000, 4000 )
|
||||
--
|
||||
function SPAWN:SpawnFromUnit( HostUnit, MinHeight, MaxHeight, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, HostUnit, MinHeight, MaxHeight, SpawnIndex } )
|
||||
|
||||
if HostUnit and HostUnit:IsAlive() ~= nil then -- and HostUnit:getUnit(1):inAir() == false then
|
||||
return self:SpawnFromVec3( HostUnit:GetVec3(), SpawnIndex )
|
||||
return self:SpawnFromVec2( HostUnit:GetVec2(), MinHeight, MaxHeight, SpawnIndex )
|
||||
end
|
||||
|
||||
return nil
|
||||
@ -1138,14 +1336,26 @@ end
|
||||
-- You can use the returned group to further define the route to be followed.
|
||||
-- @param #SPAWN self
|
||||
-- @param Wrapper.Static#STATIC HostStatic The static dropping or unloading the group.
|
||||
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
|
||||
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
|
||||
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil Nothing was spawned.
|
||||
function SPAWN:SpawnFromStatic( HostStatic, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, HostStatic, SpawnIndex } )
|
||||
-- @usage
|
||||
--
|
||||
-- local SpawnStatic = STATIC:FindByName( StaticName )
|
||||
--
|
||||
-- -- Spawn from the static position at the height specified in the ME of the group template!
|
||||
-- SpawnAirplanes:SpawnFromStatic( SpawnStatic )
|
||||
--
|
||||
-- -- Spawn from the static position at the height randomized between 2000 and 4000 meters.
|
||||
-- SpawnAirplanes:SpawnFromStatic( SpawnStatic, 2000, 4000 )
|
||||
--
|
||||
function SPAWN:SpawnFromStatic( HostStatic, MinHeight, MaxHeight, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, HostStatic, MinHeight, MaxHeight, SpawnIndex } )
|
||||
|
||||
if HostStatic and HostStatic:IsAlive() then
|
||||
return self:SpawnFromVec3( HostStatic:GetVec3(), SpawnIndex )
|
||||
return self:SpawnFromVec2( HostStatic:GetVec2(), MinHeight, MaxHeight, SpawnIndex )
|
||||
end
|
||||
|
||||
return nil
|
||||
@ -1158,17 +1368,38 @@ end
|
||||
-- @param #SPAWN self
|
||||
-- @param Core.Zone#ZONE Zone The zone where the group is to be spawned.
|
||||
-- @param #boolean RandomizeGroup (optional) Randomization of the @{Group} position in the zone.
|
||||
-- @param #number MinHeight (optional) The minimum height to spawn an airborne group into the zone.
|
||||
-- @param #number MaxHeight (optional) The maximum height to spawn an airborne group into the zone.
|
||||
-- @param #number SpawnIndex (optional) The index which group to spawn within the given zone.
|
||||
-- @return Wrapper.Group#GROUP that was spawned.
|
||||
-- @return #nil when nothing was spawned.
|
||||
function SPAWN:SpawnInZone( Zone, RandomizeGroup, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, Zone, RandomizeGroup, SpawnIndex } )
|
||||
-- @usage
|
||||
--
|
||||
-- local SpawnZone = ZONE:New( ZoneName )
|
||||
--
|
||||
-- -- Spawn at the zone center position at the height specified in the ME of the group template!
|
||||
-- SpawnAirplanes:SpawnInZone( SpawnZone )
|
||||
--
|
||||
-- -- Spawn in the zone at a random position at the height specified in the Me of the group template.
|
||||
-- SpawnAirplanes:SpawnInZone( SpawnZone, true )
|
||||
--
|
||||
-- -- Spawn in the zone at a random position at the height randomized between 2000 and 4000 meters.
|
||||
-- SpawnAirplanes:SpawnInZone( SpawnZone, true, 2000, 4000 )
|
||||
--
|
||||
-- -- Spawn at the zone center position at the height randomized between 2000 and 4000 meters.
|
||||
-- SpawnAirplanes:SpawnInZone( SpawnZone, false, 2000, 4000 )
|
||||
--
|
||||
-- -- Spawn at the zone center position at the height randomized between 2000 and 4000 meters.
|
||||
-- SpawnAirplanes:SpawnInZone( SpawnZone, nil, 2000, 4000 )
|
||||
--
|
||||
function SPAWN:SpawnInZone( Zone, RandomizeGroup, MinHeight, MaxHeight, SpawnIndex )
|
||||
self:F( { self.SpawnTemplatePrefix, Zone, RandomizeGroup, MinHeight, MaxHeight, SpawnIndex } )
|
||||
|
||||
if Zone then
|
||||
if RandomizeGroup then
|
||||
return self:SpawnFromVec2( Zone:GetRandomVec2(), SpawnIndex )
|
||||
return self:SpawnFromVec2( Zone:GetRandomVec2(), MinHeight, MaxHeight, SpawnIndex )
|
||||
else
|
||||
return self:SpawnFromVec2( Zone:GetVec2(), SpawnIndex )
|
||||
return self:SpawnFromVec2( Zone:GetVec2(), MinHeight, MaxHeight, SpawnIndex )
|
||||
end
|
||||
end
|
||||
|
||||
@ -116,6 +116,33 @@ function SPAWNSTATIC:NewFromType( SpawnTypeName, SpawnShapeName, SpawnCategory,
|
||||
return self
|
||||
end
|
||||
|
||||
--- Creates a new @{Static} at the original position.
|
||||
-- @param #SPAWNSTATIC self
|
||||
-- @param #number Heading The heading of the static, which is a number in degrees from 0 to 360.
|
||||
-- @param #string (optional) The name of the new static.
|
||||
-- @return #SPAWNSTATIC
|
||||
function SPAWNSTATIC:Spawn( Heading, NewName ) --R2.3
|
||||
self:F( { Heading, NewName } )
|
||||
|
||||
local CountryName = _DATABASE.COUNTRY_NAME[self.CountryID]
|
||||
|
||||
local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix )
|
||||
|
||||
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
|
||||
StaticTemplate.heading = ( Heading / 180 ) * math.pi
|
||||
|
||||
StaticTemplate.CountryID = nil
|
||||
StaticTemplate.CoalitionID = nil
|
||||
StaticTemplate.CategoryID = nil
|
||||
|
||||
local Static = coalition.addStaticObject( self.CountryID, StaticTemplate )
|
||||
|
||||
self.SpawnIndex = self.SpawnIndex + 1
|
||||
|
||||
return Static
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Creates a new @{Static} from a POINT_VEC2.
|
||||
-- @param #SPAWNSTATIC self
|
||||
@ -130,8 +157,13 @@ function SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1
|
||||
|
||||
local StaticTemplate = _DATABASE:GetStaticUnitTemplate( self.SpawnTemplatePrefix )
|
||||
|
||||
StaticTemplate.x = PointVec2:GetLat()
|
||||
StaticTemplate.y = PointVec2:GetLon()
|
||||
StaticTemplate.x = PointVec2.x
|
||||
StaticTemplate.y = PointVec2.z
|
||||
|
||||
StaticTemplate.units = nil
|
||||
StaticTemplate.route = nil
|
||||
StaticTemplate.groupId = nil
|
||||
|
||||
|
||||
StaticTemplate.name = NewName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex )
|
||||
StaticTemplate.heading = ( Heading / 180 ) * math.pi
|
||||
|
||||
@ -222,7 +222,7 @@ do
|
||||
|
||||
self:HandleEvent( EVENTS.Dead )
|
||||
|
||||
self:__Lasing( -0.2 )
|
||||
self:__Lasing( -1 )
|
||||
end
|
||||
|
||||
--- @param #SPOT self
|
||||
|
||||
95
Moose Development/Moose/Core/UserFlag.lua
Normal file
95
Moose Development/Moose/Core/UserFlag.lua
Normal file
@ -0,0 +1,95 @@
|
||||
--- **Core (WIP)** -- Manage user flags.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- Management of DCS User Flags.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- ### Author: **Sven Van de Velde (FlightControl)**
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- @module UserFlag
|
||||
|
||||
do -- UserFlag
|
||||
|
||||
--- @type USERFLAG
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- # USERFLAG class, extends @{Base#BASE}
|
||||
--
|
||||
-- Management of DCS User Flags.
|
||||
--
|
||||
-- ## 1. USERFLAG constructor
|
||||
--
|
||||
-- * @{#USERFLAG.New}(): Creates a new USERFLAG object.
|
||||
--
|
||||
-- @field #USERFLAG
|
||||
USERFLAG = {
|
||||
ClassName = "USERFLAG",
|
||||
}
|
||||
|
||||
--- USERFLAG Constructor.
|
||||
-- @param #USERFLAG self
|
||||
-- @param #string UserFlagName The name of the userflag, which is a free text string.
|
||||
-- @return #USERFLAG
|
||||
function USERFLAG:New( UserFlagName ) --R2.3
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #USERFLAG
|
||||
|
||||
self.UserFlagName = UserFlagName
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the userflag to a given Number.
|
||||
-- @param #USERFLAG self
|
||||
-- @param #number Number The number value to be checked if it is the same as the userflag.
|
||||
-- @return #USERFLAG The userflag instance.
|
||||
-- @usage
|
||||
-- local BlueVictory = USERFLAG:New( "VictoryBlue" )
|
||||
-- BlueVictory:Set( 100 ) -- Set the UserFlag VictoryBlue to 100.
|
||||
--
|
||||
function USERFLAG:Set( Number ) --R2.3
|
||||
|
||||
self:F( { Number = Number } )
|
||||
|
||||
trigger.action.setUserFlag( self.UserFlagName, Number )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Get the userflag Number.
|
||||
-- @param #USERFLAG self
|
||||
-- @return #number Number The number value to be checked if it is the same as the userflag.
|
||||
-- @usage
|
||||
-- local BlueVictory = USERFLAG:New( "VictoryBlue" )
|
||||
-- local BlueVictoryValue = BlueVictory:Get() -- Get the UserFlag VictoryBlue value.
|
||||
--
|
||||
function USERFLAG:Get( Number ) --R2.3
|
||||
|
||||
return trigger.misc.getUserFlag( self.UserFlagName )
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Check if the userflag has a value of Number.
|
||||
-- @param #USERFLAG self
|
||||
-- @param #number Number The number value to be checked if it is the same as the userflag.
|
||||
-- @return #boolean true if the Number is the value of the userflag.
|
||||
-- @usage
|
||||
-- local BlueVictory = USERFLAG:New( "VictoryBlue" )
|
||||
-- if BlueVictory:Is( 1 ) then
|
||||
-- return "Blue has won"
|
||||
-- end
|
||||
function USERFLAG:Is( Number ) --R2.3
|
||||
|
||||
return trigger.misc.getUserFlag( self.UserFlagName ) == Number
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
129
Moose Development/Moose/Core/UserSound.lua
Normal file
129
Moose Development/Moose/Core/UserSound.lua
Normal file
@ -0,0 +1,129 @@
|
||||
--- **Core (WIP)** -- Manage user sound.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- Management of DCS User Sound.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- ### Author: **Sven Van de Velde (FlightControl)**
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- @module UserSound
|
||||
|
||||
do -- UserSound
|
||||
|
||||
--- @type USERSOUND
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- # USERSOUND class, extends @{Base#BASE}
|
||||
--
|
||||
-- Management of DCS User Sound.
|
||||
--
|
||||
-- ## 1. USERSOUND constructor
|
||||
--
|
||||
-- * @{#USERSOUND.New}(): Creates a new USERSOUND object.
|
||||
--
|
||||
-- @field #USERSOUND
|
||||
USERSOUND = {
|
||||
ClassName = "USERSOUND",
|
||||
}
|
||||
|
||||
--- USERSOUND Constructor.
|
||||
-- @param #USERSOUND self
|
||||
-- @param #string UserSoundFileName The filename of the usersound.
|
||||
-- @return #USERSOUND
|
||||
function USERSOUND:New( UserSoundFileName ) --R2.3
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #USERSOUND
|
||||
|
||||
self.UserSoundFileName = UserSoundFileName
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set usersound filename.
|
||||
-- @param #USERSOUND self
|
||||
-- @param #string UserSoundFileName The filename of the usersound.
|
||||
-- @return #USERSOUND The usersound instance.
|
||||
-- @usage
|
||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||
-- BlueVictory:SetFileName( "BlueVictoryLoud.ogg" ) -- Set the BlueVictory to change the file name to play a louder sound.
|
||||
--
|
||||
function USERSOUND:SetFileName( UserSoundFileName ) --R2.3
|
||||
|
||||
self.UserSoundFileName = UserSoundFileName
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Play the usersound to all players.
|
||||
-- @param #USERSOUND self
|
||||
-- @return #USERSOUND The usersound instance.
|
||||
-- @usage
|
||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||
-- BlueVictory:ToAll() -- Play the sound that Blue has won.
|
||||
--
|
||||
function USERSOUND:ToAll() --R2.3
|
||||
|
||||
trigger.action.outSound( self.UserSoundFileName )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Play the usersound to the given coalition.
|
||||
-- @param #USERSOUND self
|
||||
-- @param Dcs.DCScoalition#coalition Coalition The coalition to play the usersound to.
|
||||
-- @return #USERSOUND The usersound instance.
|
||||
-- @usage
|
||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||
-- BlueVictory:ToCoalition( coalition.side.BLUE ) -- Play the sound that Blue has won to the blue coalition.
|
||||
--
|
||||
function USERSOUND:ToCoalition( Coalition ) --R2.3
|
||||
|
||||
trigger.action.outSoundForCoalition(Coalition, self.UserSoundFileName )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Play the usersound to the given country.
|
||||
-- @param #USERSOUND self
|
||||
-- @param Dcs.DCScountry#country Country The country to play the usersound to.
|
||||
-- @return #USERSOUND The usersound instance.
|
||||
-- @usage
|
||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||
-- BlueVictory:ToCountry( country.id.USA ) -- Play the sound that Blue has won to the USA country.
|
||||
--
|
||||
function USERSOUND:ToCountry( Country ) --R2.3
|
||||
|
||||
trigger.action.outSoundForCountry( Country, self.UserSoundFileName )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Play the usersound to the given @{Group}.
|
||||
-- @param #USERSOUND self
|
||||
-- @param Wrapper.Group#GROUP Group The @{Group} to play the usersound to.
|
||||
-- @return #USERSOUND The usersound instance.
|
||||
-- @usage
|
||||
-- local BlueVictory = USERSOUND:New( "BlueVictory.ogg" )
|
||||
-- local PlayerGroup = GROUP:FindByName( "PlayerGroup" ) -- Search for the active group named "PlayerGroup", that contains a human player.
|
||||
-- BlueVictory:ToGroup( PlayerGroup ) -- Play the sound that Blue has won to the player group.
|
||||
--
|
||||
function USERSOUND:ToGroup( Group ) --R2.3
|
||||
|
||||
trigger.action.outSoundForGroup( Group:GetID(), self.UserSoundFileName )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
end
|
||||
184
Moose Development/Moose/Core/Velocity.lua
Normal file
184
Moose Development/Moose/Core/Velocity.lua
Normal file
@ -0,0 +1,184 @@
|
||||
--- **Core** -- VELOCITY models a speed, which can be expressed in various formats according the Settings.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **Sven Van de Velde (FlightControl)**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- @module Velocity
|
||||
|
||||
do -- Velocity
|
||||
|
||||
--- @type VELOCITY
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- # VELOCITY class, extends @{Base#BASE}
|
||||
--
|
||||
-- VELOCITY models a speed, which can be expressed in various formats according the Settings.
|
||||
--
|
||||
-- ## 1. VELOCITY constructor
|
||||
--
|
||||
-- * @{#VELOCITY.New}(): Creates a new VELOCITY object.
|
||||
--
|
||||
-- @field #VELOCITY
|
||||
VELOCITY = {
|
||||
ClassName = "VELOCITY",
|
||||
}
|
||||
|
||||
--- VELOCITY Constructor.
|
||||
-- @param #VELOCITY self
|
||||
-- @param #number VelocityMps The velocity in meters per second.
|
||||
-- @return #VELOCITY
|
||||
function VELOCITY:New( VelocityMps )
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #VELOCITY
|
||||
self:F( {} )
|
||||
self.Velocity = VelocityMps
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the velocity in Mps (meters per second).
|
||||
-- @param #VELOCITY self
|
||||
-- @param #number VelocityMps The velocity in meters per second.
|
||||
-- @return #VELOCITY
|
||||
function VELOCITY:Set( VelocityMps )
|
||||
self.Velocity = VelocityMps
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the velocity in Mps (meters per second).
|
||||
-- @param #VELOCITY self
|
||||
-- @return #number The velocity in meters per second.
|
||||
function VELOCITY:Get()
|
||||
return self.Velocity
|
||||
end
|
||||
|
||||
--- Set the velocity in Kmph (kilometers per hour).
|
||||
-- @param #VELOCITY self
|
||||
-- @param #number VelocityKmph The velocity in kilometers per hour.
|
||||
-- @return #VELOCITY
|
||||
function VELOCITY:SetKmph( VelocityKmph )
|
||||
self.Velocity = UTILS.KmphToMps( VelocityKmph )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the velocity in Kmph (kilometers per hour).
|
||||
-- @param #VELOCITY self
|
||||
-- @return #number The velocity in kilometers per hour.
|
||||
function VELOCITY:GetKmph()
|
||||
|
||||
return UTILS.MpsToKmph( self.Velocity )
|
||||
end
|
||||
|
||||
--- Set the velocity in Miph (miles per hour).
|
||||
-- @param #VELOCITY self
|
||||
-- @param #number VelocityMiph The velocity in miles per hour.
|
||||
-- @return #VELOCITY
|
||||
function VELOCITY:SetMiph( VelocityMiph )
|
||||
self.Velocity = UTILS.MiphToMps( VelocityMiph )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the velocity in Miph (miles per hour).
|
||||
-- @param #VELOCITY self
|
||||
-- @return #number The velocity in miles per hour.
|
||||
function VELOCITY:GetMiph()
|
||||
return UTILS.MpsToMiph( self.Velocity )
|
||||
end
|
||||
|
||||
|
||||
--- Get the velocity in text, according the player @{Settings}.
|
||||
-- @param #VELOCITY self
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @return #string The velocity in text.
|
||||
function VELOCITY:GetText( Settings )
|
||||
local Settings = Settings or _SETTINGS
|
||||
if self.Velocity ~= 0 then
|
||||
if Settings:IsMetric() then
|
||||
return string.format( "%d km/h", UTILS.MpsToKmph( self.Velocity ) )
|
||||
else
|
||||
return string.format( "%d mi/h", UTILS.MpsToMiph( self.Velocity ) )
|
||||
end
|
||||
else
|
||||
return "stationary"
|
||||
end
|
||||
end
|
||||
|
||||
--- Get the velocity in text, according the player or default @{Settings}.
|
||||
-- @param #VELOCITY self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
-- @param Core.Settings#SETTINGS Settings
|
||||
-- @return #string The velocity in text according the player or default @{Settings}
|
||||
function VELOCITY:ToString( VelocityGroup, Settings ) -- R2.3
|
||||
self:F( { Group = VelocityGroup and VelocityGroup:GetName() } )
|
||||
local Settings = Settings or ( VelocityGroup and _DATABASE:GetPlayerSettings( VelocityGroup:GetPlayerName() ) ) or _SETTINGS
|
||||
return self:GetText( Settings )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
do -- VELOCITY_POSITIONABLE
|
||||
|
||||
--- @type VELOCITY_POSITIONABLE
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
--- # VELOCITY_POSITIONABLE class, extends @{Base#BASE}
|
||||
--
|
||||
-- VELOCITY_POSITIONABLE monitors the speed of an @{Positionable} in the simulation, which can be expressed in various formats according the Settings.
|
||||
--
|
||||
-- ## 1. VELOCITY_POSITIONABLE constructor
|
||||
--
|
||||
-- * @{#VELOCITY_POSITIONABLE.New}(): Creates a new VELOCITY_POSITIONABLE object.
|
||||
--
|
||||
-- @field #VELOCITY_POSITIONABLE
|
||||
VELOCITY_POSITIONABLE = {
|
||||
ClassName = "VELOCITY_POSITIONABLE",
|
||||
}
|
||||
|
||||
--- VELOCITY_POSITIONABLE Constructor.
|
||||
-- @param #VELOCITY_POSITIONABLE self
|
||||
-- @param Wrapper.Positionable#POSITIONABLE Positionable The Positionable to monitor the speed.
|
||||
-- @return #VELOCITY_POSITIONABLE
|
||||
function VELOCITY_POSITIONABLE:New( Positionable )
|
||||
local self = BASE:Inherit( self, VELOCITY:New() ) -- #VELOCITY_POSITIONABLE
|
||||
self:F( {} )
|
||||
self.Positionable = Positionable
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the velocity in Mps (meters per second).
|
||||
-- @param #VELOCITY_POSITIONABLE self
|
||||
-- @return #number The velocity in meters per second.
|
||||
function VELOCITY_POSITIONABLE:Get()
|
||||
return self.Positionable:GetVelocityMPS() or 0
|
||||
end
|
||||
|
||||
--- Get the velocity in Kmph (kilometers per hour).
|
||||
-- @param #VELOCITY_POSITIONABLE self
|
||||
-- @return #number The velocity in kilometers per hour.
|
||||
function VELOCITY_POSITIONABLE:GetKmph()
|
||||
|
||||
return UTILS.MpsToKmph( self.Positionable:GetVelocityMPS() or 0)
|
||||
end
|
||||
|
||||
--- Get the velocity in Miph (miles per hour).
|
||||
-- @param #VELOCITY_POSITIONABLE self
|
||||
-- @return #number The velocity in miles per hour.
|
||||
function VELOCITY_POSITIONABLE:GetMiph()
|
||||
return UTILS.MpsToMiph( self.Positionable:GetVelocityMPS() or 0 )
|
||||
end
|
||||
|
||||
--- Get the velocity in text, according the player or default @{Settings}.
|
||||
-- @param #VELOCITY_POSITIONABLE self
|
||||
-- @return #string The velocity in text according the player or default @{Settings}
|
||||
function VELOCITY_POSITIONABLE:ToString() -- R2.3
|
||||
self:F( { Group = self.Positionable and self.Positionable:GetName() } )
|
||||
local Settings = Settings or ( self.Positionable and _DATABASE:GetPlayerSettings( self.Positionable:GetPlayerName() ) ) or _SETTINGS
|
||||
self.Velocity = self.Positionable:GetVelocityMPS()
|
||||
return self:GetText( Settings )
|
||||
end
|
||||
|
||||
end
|
||||
@ -50,6 +50,8 @@
|
||||
-- ## Each zone has a name:
|
||||
--
|
||||
-- * @{#ZONE_BASE.GetName}(): Returns the name of the zone.
|
||||
-- * @{#ZONE_BASE.SetName}(): Sets the name of the zone.
|
||||
--
|
||||
--
|
||||
-- ## Each zone implements two polymorphic functions defined in @{Zone#ZONE_BASE}:
|
||||
--
|
||||
@ -121,6 +123,17 @@ function ZONE_BASE:GetName()
|
||||
return self.ZoneName
|
||||
end
|
||||
|
||||
|
||||
--- Sets the name of the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param #string ZoneName The name of the zone.
|
||||
-- @return #ZONE_BASE
|
||||
function ZONE_BASE:SetName( ZoneName )
|
||||
self:F2()
|
||||
|
||||
self.ZoneName = ZoneName
|
||||
end
|
||||
|
||||
--- Returns if a Vec2 is within the zone.
|
||||
-- @param #ZONE_BASE self
|
||||
-- @param Dcs.DCSTypes#Vec2 Vec2 The Vec2 to test.
|
||||
@ -445,12 +458,17 @@ end
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The smoke color.
|
||||
-- @param #number Points (optional) The amount of points in the circle.
|
||||
-- @param #number AddHeight (optional) The height to be added for the smoke.
|
||||
-- @param #number AddOffSet (optional) The angle to be added for the smoking start position.
|
||||
-- @return #ZONE_RADIUS self
|
||||
function ZONE_RADIUS:SmokeZone( SmokeColor, Points )
|
||||
function ZONE_RADIUS:SmokeZone( SmokeColor, Points, AddHeight, AngleOffset )
|
||||
self:F2( SmokeColor )
|
||||
|
||||
local Point = {}
|
||||
local Vec2 = self:GetVec2()
|
||||
|
||||
AddHeight = AddHeight or 0
|
||||
AngleOffset = AngleOffset or 0
|
||||
|
||||
Points = Points and Points or 360
|
||||
|
||||
@ -458,10 +476,10 @@ function ZONE_RADIUS:SmokeZone( SmokeColor, Points )
|
||||
local RadialBase = math.pi*2
|
||||
|
||||
for Angle = 0, 360, 360 / Points do
|
||||
local Radial = Angle * RadialBase / 360
|
||||
local Radial = ( Angle + AngleOffset ) * RadialBase / 360
|
||||
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
|
||||
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
|
||||
POINT_VEC2:New( Point.x, Point.y ):Smoke( SmokeColor )
|
||||
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Smoke( SmokeColor )
|
||||
end
|
||||
|
||||
return self
|
||||
@ -473,13 +491,16 @@ end
|
||||
-- @param Utilities.Utils#FLARECOLOR FlareColor The flare color.
|
||||
-- @param #number Points (optional) The amount of points in the circle.
|
||||
-- @param Dcs.DCSTypes#Azimuth Azimuth (optional) Azimuth The azimuth of the flare.
|
||||
-- @param #number AddHeight (optional) The height to be added for the smoke.
|
||||
-- @return #ZONE_RADIUS self
|
||||
function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth )
|
||||
function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth, AddHeight )
|
||||
self:F2( { FlareColor, Azimuth } )
|
||||
|
||||
local Point = {}
|
||||
local Vec2 = self:GetVec2()
|
||||
|
||||
AddHeight = AddHeight or 0
|
||||
|
||||
Points = Points and Points or 360
|
||||
|
||||
local Angle
|
||||
@ -489,7 +510,7 @@ function ZONE_RADIUS:FlareZone( FlareColor, Points, Azimuth )
|
||||
local Radial = Angle * RadialBase / 360
|
||||
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
|
||||
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
|
||||
POINT_VEC2:New( Point.x, Point.y ):Flare( FlareColor, Azimuth )
|
||||
POINT_VEC2:New( Point.x, Point.y, AddHeight ):Flare( FlareColor, Azimuth )
|
||||
end
|
||||
|
||||
return self
|
||||
@ -562,6 +583,189 @@ function ZONE_RADIUS:GetVec3( Height )
|
||||
end
|
||||
|
||||
|
||||
--- Scan the zone
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param ObjectCategories
|
||||
-- @param Coalition
|
||||
function ZONE_RADIUS:Scan( ObjectCategories )
|
||||
|
||||
self.ScanData = {}
|
||||
self.ScanData.Coalitions = {}
|
||||
self.ScanData.Scenery = {}
|
||||
|
||||
local ZoneCoord = self:GetCoordinate()
|
||||
local ZoneRadius = self:GetRadius()
|
||||
|
||||
self:E({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()})
|
||||
|
||||
local SphereSearch = {
|
||||
id = world.VolumeType.SPHERE,
|
||||
params = {
|
||||
point = ZoneCoord:GetVec3(),
|
||||
radius = ZoneRadius,
|
||||
}
|
||||
}
|
||||
|
||||
local function EvaluateZone( ZoneObject )
|
||||
if ZoneObject:isExist() then
|
||||
local ObjectCategory = ZoneObject:getCategory()
|
||||
if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isActive() ) or
|
||||
ObjectCategory == Object.Category.STATIC then
|
||||
local CoalitionDCSUnit = ZoneObject:getCoalition()
|
||||
self.ScanData.Coalitions[CoalitionDCSUnit] = true
|
||||
self:E( { Name = ZoneObject:getName(), Coalition = CoalitionDCSUnit } )
|
||||
end
|
||||
if ObjectCategory == Object.Category.SCENERY then
|
||||
local SceneryType = ZoneObject:getTypeName()
|
||||
local SceneryName = ZoneObject:getName()
|
||||
self.ScanData.Scenery[SceneryType] = self.ScanData.Scenery[SceneryType] or {}
|
||||
self.ScanData.Scenery[SceneryType][SceneryName] = SCENERY:Register( SceneryName, ZoneObject )
|
||||
self:E( { SCENERY = self.ScanData.Scenery[SceneryType][SceneryName] } )
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
world.searchObjects( ObjectCategories, SphereSearch, EvaluateZone )
|
||||
|
||||
end
|
||||
|
||||
|
||||
function ZONE_RADIUS:CountScannedCoalitions()
|
||||
|
||||
local Count = 0
|
||||
|
||||
for CoalitionID, Coalition in pairs( self.ScanData.Coalitions ) do
|
||||
Count = Count + 1
|
||||
end
|
||||
return Count
|
||||
end
|
||||
|
||||
|
||||
--- Get Coalitions of the units in the Zone, or Check if there are units of the given Coalition in the Zone.
|
||||
-- Returns nil if there are none ot two Coalitions in the zone!
|
||||
-- Returns one Coalition if there are only Units of one Coalition in the Zone.
|
||||
-- Returns the Coalition for the given Coalition if there are units of the Coalition in the Zone
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @return #table
|
||||
function ZONE_RADIUS:GetScannedCoalition( Coalition )
|
||||
|
||||
if Coalition then
|
||||
return self.ScanData.Coalitions[Coalition]
|
||||
else
|
||||
local Count = 0
|
||||
local ReturnCoalition = nil
|
||||
|
||||
for CoalitionID, Coalition in pairs( self.ScanData.Coalitions ) do
|
||||
Count = Count + 1
|
||||
ReturnCoalition = CoalitionID
|
||||
end
|
||||
|
||||
if Count ~= 1 then
|
||||
ReturnCoalition = nil
|
||||
end
|
||||
|
||||
return ReturnCoalition
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function ZONE_RADIUS:GetScannedSceneryType( SceneryType )
|
||||
return self.ScanData.Scenery[SceneryType]
|
||||
end
|
||||
|
||||
|
||||
function ZONE_RADIUS:GetScannedScenery()
|
||||
return self.ScanData.Scenery
|
||||
end
|
||||
|
||||
|
||||
--- Is All in Zone of Coalition?
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param Coalition
|
||||
-- @return #boolean
|
||||
function ZONE_RADIUS:IsAllInZoneOfCoalition( Coalition )
|
||||
|
||||
return self:CountScannedCoalitions() == 1 and self:GetScannedCoalition( Coalition ) == true
|
||||
end
|
||||
|
||||
|
||||
--- Is All in Zone of Other Coalition?
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param Coalition
|
||||
-- @return #boolean
|
||||
function ZONE_RADIUS:IsAllInZoneOfOtherCoalition( Coalition )
|
||||
|
||||
self:E( { Coalitions = self.Coalitions, Count = self:CountScannedCoalitions() } )
|
||||
return self:CountScannedCoalitions() == 1 and self:GetScannedCoalition( Coalition ) == nil
|
||||
end
|
||||
|
||||
|
||||
--- Is Some in Zone of Coalition?
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param Coalition
|
||||
-- @return #boolean
|
||||
function ZONE_RADIUS:IsSomeInZoneOfCoalition( Coalition )
|
||||
|
||||
return self:CountScannedCoalitions() > 1 and self:GetScannedCoalition( Coalition ) == true
|
||||
end
|
||||
|
||||
|
||||
--- Is None in Zone of Coalition?
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param Coalition
|
||||
-- @return #boolean
|
||||
function ZONE_RADIUS:IsNoneInZoneOfCoalition( Coalition )
|
||||
|
||||
return self:GetScannedCoalition( Coalition ) == nil
|
||||
end
|
||||
|
||||
|
||||
--- Is None in Zone?
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @return #boolean
|
||||
function ZONE_RADIUS:IsNoneInZone()
|
||||
|
||||
return self:CountScannedCoalitions() == 0
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Searches the zone
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param ObjectCategories A list of categories, which are members of Object.Category
|
||||
-- @param EvaluateFunction
|
||||
function ZONE_RADIUS:SearchZone( EvaluateFunction, ObjectCategories )
|
||||
|
||||
local SearchZoneResult = true
|
||||
|
||||
local ZoneCoord = self:GetCoordinate()
|
||||
local ZoneRadius = self:GetRadius()
|
||||
|
||||
self:E({ZoneCoord = ZoneCoord, ZoneRadius = ZoneRadius, ZoneCoordLL = ZoneCoord:ToStringLLDMS()})
|
||||
|
||||
local SphereSearch = {
|
||||
id = world.VolumeType.SPHERE,
|
||||
params = {
|
||||
point = ZoneCoord:GetVec3(),
|
||||
radius = ZoneRadius / 2,
|
||||
}
|
||||
}
|
||||
|
||||
local function EvaluateZone( ZoneDCSUnit )
|
||||
|
||||
env.info( ZoneDCSUnit:getName() )
|
||||
|
||||
local ZoneUnit = UNIT:Find( ZoneDCSUnit )
|
||||
|
||||
return EvaluateFunction( ZoneUnit )
|
||||
end
|
||||
|
||||
world.searchObjects( Object.Category.UNIT, SphereSearch, EvaluateZone )
|
||||
|
||||
end
|
||||
|
||||
--- Returns if a location is within the zone.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param Dcs.DCSTypes#Vec2 Vec2 The location to test.
|
||||
@ -645,6 +849,22 @@ function ZONE_RADIUS:GetRandomPointVec3( inner, outer )
|
||||
end
|
||||
|
||||
|
||||
--- Returns a @{Point#COORDINATE} object reflecting a random 3D location within the zone.
|
||||
-- @param #ZONE_RADIUS self
|
||||
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
||||
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
||||
-- @return Core.Point#COORDINATE
|
||||
function ZONE_RADIUS:GetRandomCoordinate( inner, outer )
|
||||
self:F( self.ZoneName, inner, outer )
|
||||
|
||||
local Coordinate = COORDINATE:NewFromVec2( self:GetRandomVec2() )
|
||||
|
||||
self:T3( { Coordinate = Coordinate } )
|
||||
|
||||
return Coordinate
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @type ZONE
|
||||
-- @extends #ZONE_RADIUS
|
||||
@ -834,6 +1054,20 @@ function ZONE_GROUP:GetRandomVec2()
|
||||
return Point
|
||||
end
|
||||
|
||||
--- Returns a @{Point#POINT_VEC2} object reflecting a random 2D location within the zone.
|
||||
-- @param #ZONE_GROUP self
|
||||
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
|
||||
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
|
||||
-- @return Core.Point#POINT_VEC2 The @{Point#POINT_VEC2} object reflecting the random 3D location within the zone.
|
||||
function ZONE_GROUP:GetRandomPointVec2( inner, outer )
|
||||
self:F( self.ZoneName, inner, outer )
|
||||
|
||||
local PointVec2 = POINT_VEC2:NewFromVec2( self:GetRandomVec2() )
|
||||
|
||||
self:T3( { PointVec2 } )
|
||||
|
||||
return PointVec2
|
||||
end
|
||||
|
||||
|
||||
--- @type ZONE_POLYGON_BASE
|
||||
@ -1077,6 +1311,20 @@ function ZONE_POLYGON_BASE:GetRandomPointVec3()
|
||||
end
|
||||
|
||||
|
||||
--- Return a @{Point#COORDINATE} object representing a random 3D point at landheight within the zone.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @return Core.Point#COORDINATE
|
||||
function ZONE_POLYGON_BASE:GetRandomCoordinate()
|
||||
self:F2()
|
||||
|
||||
local Coordinate = COORDINATE:NewFromVec2( self:GetRandomVec2() )
|
||||
|
||||
self:T2( Coordinate )
|
||||
|
||||
return Coordinate
|
||||
end
|
||||
|
||||
|
||||
--- Get the bounding square the zone.
|
||||
-- @param #ZONE_POLYGON_BASE self
|
||||
-- @return #ZONE_POLYGON_BASE.BoundingSquare The bounding square.
|
||||
@ -1114,7 +1362,7 @@ ZONE_POLYGON = {
|
||||
ClassName="ZONE_POLYGON",
|
||||
}
|
||||
|
||||
--- Constructor to create a ZONE_POLYGON instance, taking the zone name and the name of the @{Group#GROUP} defined within the Mission Editor.
|
||||
--- Constructor to create a ZONE_POLYGON instance, taking the zone name and the @{Group#GROUP} defined within the Mission Editor.
|
||||
-- The @{Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected by ZONE_POLYGON.
|
||||
-- @param #ZONE_POLYGON self
|
||||
-- @param #string ZoneName Name of the zone.
|
||||
@ -1130,3 +1378,22 @@ function ZONE_POLYGON:New( ZoneName, ZoneGroup )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Constructor to create a ZONE_POLYGON instance, taking the zone name and the **name** of the @{Group#GROUP} defined within the Mission Editor.
|
||||
-- The @{Group#GROUP} waypoints define the polygon corners. The first and the last point are automatically connected by ZONE_POLYGON.
|
||||
-- @param #ZONE_POLYGON self
|
||||
-- @param #string ZoneName Name of the zone.
|
||||
-- @param #string GroupName The group name of the GROUP defining the waypoints within the Mission Editor to define the polygon shape.
|
||||
-- @return #ZONE_POLYGON self
|
||||
function ZONE_POLYGON:NewFromGroupName( ZoneName, GroupName )
|
||||
|
||||
local ZoneGroup = GROUP:FindByName( GroupName )
|
||||
|
||||
local GroupPoints = ZoneGroup:GetTaskRoute()
|
||||
|
||||
local self = BASE:Inherit( self, ZONE_POLYGON_BASE:New( ZoneName, GroupPoints ) )
|
||||
self:F( { ZoneName, ZoneGroup, self._.Polygon } )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
2311
Moose Development/Moose/Functional/ATC_Ground.lua
Normal file
2311
Moose Development/Moose/Functional/ATC_Ground.lua
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,136 +1,234 @@
|
||||
--- **Functional** -- The CLEANUP class keeps an area clean of crashing or colliding airplanes. It also prevents airplanes from firing within this area.
|
||||
--- **Functional** -- The CLEANUP_AIRBASE class keeps an area clean of crashing or colliding airplanes. It also prevents airplanes from firing within this area.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **Sven Van de Velde (FlightControl)**
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- @module CleanUp
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- The CLEANUP class.
|
||||
-- @type CLEANUP
|
||||
--- @type CLEANUP_AIRBASE.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-)
|
||||
-- @field #map<#string,Wrapper.Airbase#AIRBASE> Airbases Map of Airbases.
|
||||
-- @extends Core.Base#BASE
|
||||
CLEANUP = {
|
||||
ClassName = "CLEANUP",
|
||||
ZoneNames = {},
|
||||
TimeInterval = 300,
|
||||
|
||||
--- @type CLEANUP_AIRBASE
|
||||
-- @extends #CLEANUP_AIRBASE.__
|
||||
|
||||
--- # CLEANUP_AIRBASE, extends @{Base#BASE}
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- The CLEANUP_AIRBASE class keeps airbases clean, and tries to guarantee continuous airbase operations, even under combat.
|
||||
-- Specific airbases need to be provided that need to be guarded. Each airbase registered, will be guarded within a zone of 8 km around the airbase.
|
||||
-- Any unit that fires a missile, or shoots within the zone of an airbase, will be monitored by CLEANUP_AIRBASE.
|
||||
-- Within the 8km zone, units cannot fire any missile, which prevents the airbase runway to receive missile or bomb hits.
|
||||
-- Any airborne or ground unit that is on the runway below 30 meters (default value) will be automatically removed if it is damaged.
|
||||
--
|
||||
-- This is not a full 100% secure implementation. It is still possible that CLEANUP_AIRBASE cannot prevent (in-time) to keep the airbase clean.
|
||||
-- The following situations may happen that will still stop the runway of an airbase:
|
||||
--
|
||||
-- * A damaged unit is not removed on time when above the runway, and crashes on the runway.
|
||||
-- * A bomb or missile is still able to dropped on the runway.
|
||||
-- * Units collide on the airbase, and could not be removed on time.
|
||||
--
|
||||
-- When a unit is within the airbase zone and needs to be monitored,
|
||||
-- its status will be checked every 0.25 seconds! This is required to ensure that the airbase is kept clean.
|
||||
-- But as a result, there is more CPU overload.
|
||||
--
|
||||
-- So as an advise, I suggest you use the CLEANUP_AIRBASE class with care:
|
||||
--
|
||||
-- * Only monitor airbases that really need to be monitored!
|
||||
-- * Try not to monitor airbases that are likely to be invaded by enemy troops.
|
||||
-- For these airbases, there is little use to keep them clean, as they will be invaded anyway...
|
||||
--
|
||||
-- By following the above guidelines, you can add airbase cleanup with acceptable CPU overhead.
|
||||
--
|
||||
-- ## 1. CLEANUP_AIRBASE Constructor
|
||||
--
|
||||
-- Creates the main object which is preventing the airbase to get polluted with debris on the runway, which halts the airbase.
|
||||
--
|
||||
-- -- Clean these Zones.
|
||||
-- CleanUpAirports = CLEANUP_AIRBASE:New( { AIRBASE.Caucasus.Tbilisi, AIRBASE.Caucasus.Kutaisi )
|
||||
--
|
||||
-- -- or
|
||||
-- CleanUpTbilisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Tbilisi )
|
||||
-- CleanUpKutaisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Kutaisi )
|
||||
--
|
||||
-- ## 2. Add or Remove airbases
|
||||
--
|
||||
-- The method @{#CLEANUP_AIRBASE.AddAirbase}() to add an airbase to the cleanup validation process.
|
||||
-- The method @{#CLEANUP_AIRBASE.RemoveAirbase}() removes an airbase from the cleanup validation process.
|
||||
--
|
||||
-- ## 3. Clean missiles and bombs within the airbase zone.
|
||||
--
|
||||
-- When missiles or bombs hit the runway, the airbase operations stop.
|
||||
-- Use the method @{#CLEANUP_AIRBASE.SetCleanMissiles}() to control the cleaning of missiles, which will prevent airbases to stop.
|
||||
-- Note that this method will not allow anymore airbases to be attacked, so there is a trade-off here to do.
|
||||
--
|
||||
-- @field #CLEANUP_AIRBASE
|
||||
CLEANUP_AIRBASE = {
|
||||
ClassName = "CLEANUP_AIRBASE",
|
||||
TimeInterval = 0.2,
|
||||
CleanUpList = {},
|
||||
}
|
||||
|
||||
-- @field #CLEANUP_AIRBASE.__
|
||||
CLEANUP_AIRBASE.__ = {}
|
||||
|
||||
--- @field #CLEANUP_AIRBASE.__.Airbases
|
||||
CLEANUP_AIRBASE.__.Airbases = {}
|
||||
|
||||
--- Creates the main object which is handling the cleaning of the debris within the given Zone Names.
|
||||
-- @param #CLEANUP self
|
||||
-- @param #table ZoneNames Is a table of zone names where the debris should be cleaned. Also a single string can be passed with one zone name.
|
||||
-- @param #number TimeInterval The interval in seconds when the clean activity takes place. The default is 300 seconds, thus every 5 minutes.
|
||||
-- @return #CLEANUP
|
||||
-- @param #CLEANUP_AIRBASE self
|
||||
-- @param #list<#string> AirbaseNames Is a table of airbase names where the debris should be cleaned. Also a single string can be passed with one airbase name.
|
||||
-- @return #CLEANUP_AIRBASE
|
||||
-- @usage
|
||||
-- -- Clean these Zones.
|
||||
-- CleanUpAirports = CLEANUP:New( { 'CLEAN Tbilisi', 'CLEAN Kutaisi' }, 150 )
|
||||
-- CleanUpAirports = CLEANUP_AIRBASE:New( { AIRBASE.Caucasus.Tbilisi, AIRBASE.Caucasus.Kutaisi )
|
||||
-- or
|
||||
-- CleanUpTbilisi = CLEANUP:New( 'CLEAN Tbilisi', 150 )
|
||||
-- CleanUpKutaisi = CLEANUP:New( 'CLEAN Kutaisi', 600 )
|
||||
function CLEANUP:New( ZoneNames, TimeInterval )
|
||||
-- CleanUpTbilisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Tbilisi )
|
||||
-- CleanUpKutaisi = CLEANUP_AIRBASE:New( AIRBASE.Caucasus.Kutaisi )
|
||||
function CLEANUP_AIRBASE:New( AirbaseNames )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #CLEANUP
|
||||
self:F( { ZoneNames, TimeInterval } )
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #CLEANUP_AIRBASE
|
||||
self:F( { AirbaseNames } )
|
||||
|
||||
if type( ZoneNames ) == 'table' then
|
||||
self.ZoneNames = ZoneNames
|
||||
if type( AirbaseNames ) == 'table' then
|
||||
for AirbaseID, AirbaseName in pairs( AirbaseNames ) do
|
||||
self:AddAirbase( AirbaseName )
|
||||
end
|
||||
else
|
||||
self.ZoneNames = { ZoneNames }
|
||||
end
|
||||
if TimeInterval then
|
||||
self.TimeInterval = TimeInterval
|
||||
local AirbaseName = AirbaseNames
|
||||
self:AddAirbase( AirbaseName )
|
||||
end
|
||||
|
||||
self:HandleEvent( EVENTS.Birth )
|
||||
self:HandleEvent( EVENTS.Birth, self.__.OnEventBirth )
|
||||
|
||||
self.CleanUpScheduler = SCHEDULER:New( self, self._CleanUpScheduler, {}, 1, TimeInterval )
|
||||
self.__.CleanUpScheduler = SCHEDULER:New( self, self.__.CleanUpSchedule, {}, 1, self.TimeInterval )
|
||||
|
||||
self:HandleEvent( EVENTS.EngineShutdown , self.__.EventAddForCleanUp )
|
||||
self:HandleEvent( EVENTS.EngineStartup, self.__.EventAddForCleanUp )
|
||||
self:HandleEvent( EVENTS.Hit, self.__.EventAddForCleanUp )
|
||||
self:HandleEvent( EVENTS.PilotDead, self.__.OnEventCrash )
|
||||
self:HandleEvent( EVENTS.Dead, self.__.OnEventCrash )
|
||||
self:HandleEvent( EVENTS.Crash, self.__.OnEventCrash )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Destroys a group from the simulator, but checks first if it is still existing!
|
||||
-- @param #CLEANUP self
|
||||
-- @param Dcs.DCSWrapper.Group#Group GroupObject The object to be destroyed.
|
||||
-- @param #string CleanUpGroupName The groupname...
|
||||
function CLEANUP:_DestroyGroup( GroupObject, CleanUpGroupName )
|
||||
self:F( { GroupObject, CleanUpGroupName } )
|
||||
|
||||
if GroupObject then -- and GroupObject:isExist() then
|
||||
trigger.action.deactivateGroup(GroupObject)
|
||||
self:T( { "GroupObject Destroyed", GroupObject } )
|
||||
end
|
||||
--- Adds an airbase to the airbase validation list.
|
||||
-- @param #CLEANUP_AIRBASE self
|
||||
-- @param #string AirbaseName
|
||||
-- @return #CLEANUP_AIRBASE
|
||||
function CLEANUP_AIRBASE:AddAirbase( AirbaseName )
|
||||
self.__.Airbases[AirbaseName] = AIRBASE:FindByName( AirbaseName )
|
||||
self:F({"Airbase:", AirbaseName, self.__.Airbases[AirbaseName]:GetDesc()})
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Destroys a @{DCSWrapper.Unit#Unit} from the simulator, but checks first if it is still existing!
|
||||
-- @param #CLEANUP self
|
||||
-- @param Dcs.DCSWrapper.Unit#Unit CleanUpUnit The object to be destroyed.
|
||||
-- @param #string CleanUpUnitName The Unit name ...
|
||||
function CLEANUP:_DestroyUnit( CleanUpUnit, CleanUpUnitName )
|
||||
self:F( { CleanUpUnit, CleanUpUnitName } )
|
||||
--- Removes an airbase from the airbase validation list.
|
||||
-- @param #CLEANUP_AIRBASE self
|
||||
-- @param #string AirbaseName
|
||||
-- @return #CLEANUP_AIRBASE
|
||||
function CLEANUP_AIRBASE:RemoveAirbase( AirbaseName )
|
||||
self.__.Airbases[AirbaseName] = nil
|
||||
return self
|
||||
end
|
||||
|
||||
--- Enables or disables the cleaning of missiles within the airbase zones.
|
||||
-- Airbase operations stop when a missile or bomb is dropped at a runway.
|
||||
-- Note that when this method is used, the airbase operations won't stop if
|
||||
-- the missile or bomb was cleaned within the airbase zone, which is 8km from the center of the airbase.
|
||||
-- However, there is a trade-off to make. Attacks on airbases won't be possible anymore if this method is used.
|
||||
-- Note, one can also use the method @{#CLEANUP_AIRBASE.RemoveAirbase}() to remove the airbase from the control process as a whole,
|
||||
-- when an enemy unit is near. That is also an option...
|
||||
-- @param #CLEANUP_AIRBASE self
|
||||
-- @param #string CleanMissiles (Default=true) If true, missiles fired are immediately destroyed. If false missiles are not controlled.
|
||||
-- @return #CLEANUP_AIRBASE
|
||||
function CLEANUP_AIRBASE:SetCleanMissiles( CleanMissiles )
|
||||
|
||||
if CleanMissiles then
|
||||
self:HandleEvent( EVENTS.Shot, self.__.OnEventShot )
|
||||
else
|
||||
self:UnHandleEvent( EVENTS.Shot )
|
||||
end
|
||||
end
|
||||
|
||||
function CLEANUP_AIRBASE.__:IsInAirbase( Vec2 )
|
||||
|
||||
local InAirbase = false
|
||||
for AirbaseName, Airbase in pairs( self.__.Airbases ) do
|
||||
local Airbase = Airbase -- Wrapper.Airbase#AIRBASE
|
||||
if Airbase:GetZone():IsVec2InZone( Vec2 ) then
|
||||
InAirbase = true
|
||||
break;
|
||||
end
|
||||
end
|
||||
|
||||
return InAirbase
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Destroys a @{Unit} from the simulator, but checks first if it is still existing!
|
||||
-- @param #CLEANUP_AIRBASE self
|
||||
-- @param Wrapper.Unit#UNIT CleanUpUnit The object to be destroyed.
|
||||
function CLEANUP_AIRBASE.__:DestroyUnit( CleanUpUnit )
|
||||
self:F( { CleanUpUnit } )
|
||||
|
||||
if CleanUpUnit then
|
||||
local CleanUpGroup = Unit.getGroup(CleanUpUnit)
|
||||
local CleanUpUnitName = CleanUpUnit:GetName()
|
||||
local CleanUpGroup = CleanUpUnit:GetGroup()
|
||||
-- TODO Client bug in 1.5.3
|
||||
if CleanUpGroup and CleanUpGroup:isExist() then
|
||||
local CleanUpGroupUnits = CleanUpGroup:getUnits()
|
||||
if CleanUpGroup:IsAlive() then
|
||||
local CleanUpGroupUnits = CleanUpGroup:GetUnits()
|
||||
if #CleanUpGroupUnits == 1 then
|
||||
local CleanUpGroupName = CleanUpGroup:getName()
|
||||
--self:CreateEventCrash( timer.getTime(), CleanUpUnit )
|
||||
CleanUpGroup:destroy()
|
||||
self:T( { "Destroyed Group:", CleanUpGroupName } )
|
||||
local CleanUpGroupName = CleanUpGroup:GetName()
|
||||
CleanUpGroup:Destroy()
|
||||
else
|
||||
CleanUpUnit:destroy()
|
||||
self:T( { "Destroyed Unit:", CleanUpUnitName } )
|
||||
CleanUpUnit:Destroy()
|
||||
end
|
||||
self.CleanUpList[CleanUpUnitName] = nil -- Cleaning from the list
|
||||
CleanUpUnit = nil
|
||||
self.CleanUpList[CleanUpUnitName] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- TODO check Dcs.DCSTypes#Weapon
|
||||
--- Destroys a missile from the simulator, but checks first if it is still existing!
|
||||
-- @param #CLEANUP self
|
||||
-- @param Dcs.DCSTypes#Weapon MissileObject
|
||||
function CLEANUP:_DestroyMissile( MissileObject )
|
||||
self:F( { MissileObject } )
|
||||
|
||||
|
||||
--- Destroys a missile from the simulator, but checks first if it is still existing!
|
||||
-- @param #CLEANUP_AIRBASE self
|
||||
-- @param Dcs.DCSTypes#Weapon MissileObject
|
||||
function CLEANUP_AIRBASE.__:DestroyMissile( MissileObject )
|
||||
self:F( { MissileObject } )
|
||||
|
||||
if MissileObject and MissileObject:isExist() then
|
||||
MissileObject:destroy()
|
||||
self:T( "MissileObject Destroyed")
|
||||
end
|
||||
end
|
||||
|
||||
--- @param #CLEANUP self
|
||||
--- @param #CLEANUP_AIRBASE self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function CLEANUP:_OnEventBirth( EventData )
|
||||
function CLEANUP_AIRBASE.__:OnEventBirth( EventData )
|
||||
self:F( { EventData } )
|
||||
|
||||
self.CleanUpList[EventData.IniDCSUnitName] = {}
|
||||
self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniDCSUnit
|
||||
self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniDCSGroup
|
||||
self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnit = EventData.IniUnit
|
||||
self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroup = EventData.IniGroup
|
||||
self.CleanUpList[EventData.IniDCSUnitName].CleanUpGroupName = EventData.IniDCSGroupName
|
||||
self.CleanUpList[EventData.IniDCSUnitName].CleanUpUnitName = EventData.IniDCSUnitName
|
||||
|
||||
EventData.IniUnit:HandleEvent( EVENTS.EngineShutdown , self._EventAddForCleanUp )
|
||||
EventData.IniUnit:HandleEvent( EVENTS.EngineStartup, self._EventAddForCleanUp )
|
||||
EventData.IniUnit:HandleEvent( EVENTS.Hit, self._EventAddForCleanUp )
|
||||
EventData.IniUnit:HandleEvent( EVENTS.PilotDead, self._EventCrash )
|
||||
EventData.IniUnit:HandleEvent( EVENTS.Dead, self._EventCrash )
|
||||
EventData.IniUnit:HandleEvent( EVENTS.Crash, self._EventCrash )
|
||||
EventData.IniUnit:HandleEvent( EVENTS.Shot, self._EventShot )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Detects if a crash event occurs.
|
||||
-- Crashed units go into a CleanUpList for removal.
|
||||
-- @param #CLEANUP self
|
||||
-- @param Dcs.DCSTypes#Event event
|
||||
function CLEANUP:_EventCrash( Event )
|
||||
-- @param #CLEANUP_AIRBASE self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function CLEANUP_AIRBASE.__:OnEventCrash( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
--TODO: This stuff is not working due to a DCS bug. Burning units cannot be destroyed.
|
||||
@ -141,171 +239,164 @@ function CLEANUP:_EventCrash( Event )
|
||||
-- self:T("after deactivateGroup")
|
||||
-- event.initiator:destroy()
|
||||
|
||||
self.CleanUpList[Event.IniDCSUnitName] = {}
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniDCSUnit
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniDCSGroup
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName
|
||||
if Event.IniDCSUnitName and Event.IniCategory == Object.Category.UNIT then
|
||||
self.CleanUpList[Event.IniDCSUnitName] = {}
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnit = Event.IniUnit
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroup = Event.IniGroup
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpGroupName = Event.IniDCSGroupName
|
||||
self.CleanUpList[Event.IniDCSUnitName].CleanUpUnitName = Event.IniDCSUnitName
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Detects if a unit shoots a missile.
|
||||
-- If this occurs within one of the zones, then the weapon used must be destroyed.
|
||||
-- @param #CLEANUP self
|
||||
-- @param Dcs.DCSTypes#Event event
|
||||
function CLEANUP:_EventShot( Event )
|
||||
-- If this occurs within one of the airbases, then the weapon used must be destroyed.
|
||||
-- @param #CLEANUP_AIRBASE self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function CLEANUP_AIRBASE.__:OnEventShot( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
-- Test if the missile was fired within one of the CLEANUP.ZoneNames.
|
||||
local CurrentLandingZoneID = 0
|
||||
CurrentLandingZoneID = routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames )
|
||||
if ( CurrentLandingZoneID ) then
|
||||
-- Okay, the missile was fired within the CLEANUP.ZoneNames, destroy the fired weapon.
|
||||
--_SEADmissile:destroy()
|
||||
SCHEDULER:New( self, CLEANUP._DestroyMissile, { Event.Weapon }, 0.1 )
|
||||
-- Test if the missile was fired within one of the CLEANUP_AIRBASE.AirbaseNames.
|
||||
if self:IsInAirbase( Event.IniUnit:GetVec2() ) then
|
||||
-- Okay, the missile was fired within the CLEANUP_AIRBASE.AirbaseNames, destroy the fired weapon.
|
||||
self:DestroyMissile( Event.Weapon )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Detects if the Unit has an S_EVENT_HIT within the given ZoneNames. If this is the case, destroy the unit.
|
||||
-- @param #CLEANUP self
|
||||
-- @param Dcs.DCSTypes#Event event
|
||||
function CLEANUP:_EventHitCleanUp( Event )
|
||||
--- Detects if the Unit has an S_EVENT_HIT within the given AirbaseNames. If this is the case, destroy the unit.
|
||||
-- @param #CLEANUP_AIRBASE self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function CLEANUP_AIRBASE.__:OnEventHit( Event )
|
||||
self:F( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
if routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) ~= nil then
|
||||
self:T( { "Life: ", Event.IniDCSUnitName, ' = ', Event.IniDCSUnit:getLife(), "/", Event.IniDCSUnit:getLife0() } )
|
||||
if Event.IniDCSUnit:getLife() < Event.IniDCSUnit:getLife0() then
|
||||
if Event.IniUnit then
|
||||
if self:IsInAirbase( Event.IniUnit:GetVec2() ) then
|
||||
self:T( { "Life: ", Event.IniDCSUnitName, ' = ', Event.IniUnit:GetLife(), "/", Event.IniUnit:GetLife0() } )
|
||||
if Event.IniUnit:GetLife() < Event.IniUnit:GetLife0() then
|
||||
self:T( "CleanUp: Destroy: " .. Event.IniDCSUnitName )
|
||||
SCHEDULER:New( self, CLEANUP._DestroyUnit, { Event.IniDCSUnit }, 0.1 )
|
||||
CLEANUP_AIRBASE.__:DestroyUnit( Event.IniUnit )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Event.TgtDCSUnit then
|
||||
if routines.IsUnitInZones( Event.TgtDCSUnit, self.ZoneNames ) ~= nil then
|
||||
self:T( { "Life: ", Event.TgtDCSUnitName, ' = ', Event.TgtDCSUnit:getLife(), "/", Event.TgtDCSUnit:getLife0() } )
|
||||
if Event.TgtDCSUnit:getLife() < Event.TgtDCSUnit:getLife0() then
|
||||
if Event.TgtUnit then
|
||||
if self:IsInAirbase( Event.TgtUnit:GetVec2() ) then
|
||||
self:T( { "Life: ", Event.TgtDCSUnitName, ' = ', Event.TgtUnit:GetLife(), "/", Event.TgtUnit:GetLife0() } )
|
||||
if Event.TgtUnit:GetLife() < Event.TgtUnit:GetLife0() then
|
||||
self:T( "CleanUp: Destroy: " .. Event.TgtDCSUnitName )
|
||||
SCHEDULER:New( self, CLEANUP._DestroyUnit, { Event.TgtDCSUnit }, 0.1 )
|
||||
CLEANUP_AIRBASE.__:DestroyUnit( Event.TgtUnit )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Add the @{DCSWrapper.Unit#Unit} to the CleanUpList for CleanUp.
|
||||
function CLEANUP:_AddForCleanUp( CleanUpUnit, CleanUpUnitName )
|
||||
-- @param #CLEANUP_AIRBASE self
|
||||
-- @param Wrapper.Unit#UNIT CleanUpUnit
|
||||
-- @oaram #string CleanUpUnitName
|
||||
function CLEANUP_AIRBASE.__:AddForCleanUp( CleanUpUnit, CleanUpUnitName )
|
||||
self:F( { CleanUpUnit, CleanUpUnitName } )
|
||||
|
||||
self.CleanUpList[CleanUpUnitName] = {}
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpUnit = CleanUpUnit
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpUnitName = CleanUpUnitName
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpGroup = Unit.getGroup(CleanUpUnit)
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpGroupName = Unit.getGroup(CleanUpUnit):getName()
|
||||
|
||||
local CleanUpGroup = CleanUpUnit:GetGroup()
|
||||
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpGroup = CleanUpGroup
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpGroupName = CleanUpGroup:GetName()
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpTime = timer.getTime()
|
||||
self.CleanUpList[CleanUpUnitName].CleanUpMoved = false
|
||||
|
||||
self:T( { "CleanUp: Add to CleanUpList: ", Unit.getGroup(CleanUpUnit):getName(), CleanUpUnitName } )
|
||||
self:T( { "CleanUp: Add to CleanUpList: ", CleanUpGroup:GetName(), CleanUpUnitName } )
|
||||
|
||||
end
|
||||
|
||||
--- Detects if the Unit has an S_EVENT_ENGINE_SHUTDOWN or an S_EVENT_HIT within the given ZoneNames. If this is the case, add the Group to the CLEANUP List.
|
||||
-- @param #CLEANUP self
|
||||
-- @param Dcs.DCSTypes#Event event
|
||||
function CLEANUP:_EventAddForCleanUp( Event )
|
||||
--- Detects if the Unit has an S_EVENT_ENGINE_SHUTDOWN or an S_EVENT_HIT within the given AirbaseNames. If this is the case, add the Group to the CLEANUP_AIRBASE List.
|
||||
-- @param #CLEANUP_AIRBASE.__ self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function CLEANUP_AIRBASE.__:EventAddForCleanUp( Event )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
self:F({Event})
|
||||
|
||||
|
||||
if Event.IniDCSUnit and Event.IniCategory == Object.Category.UNIT then
|
||||
if self.CleanUpList[Event.IniDCSUnitName] == nil then
|
||||
if routines.IsUnitInZones( Event.IniDCSUnit, self.ZoneNames ) ~= nil then
|
||||
self:_AddForCleanUp( Event.IniDCSUnit, Event.IniDCSUnitName )
|
||||
if self:IsInAirbase( Event.IniUnit:GetVec2() ) then
|
||||
self:AddForCleanUp( Event.IniUnit, Event.IniDCSUnitName )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Event.TgtDCSUnit then
|
||||
if Event.TgtDCSUnit and Event.TgtCategory == Object.Category.UNIT then
|
||||
if self.CleanUpList[Event.TgtDCSUnitName] == nil then
|
||||
if routines.IsUnitInZones( Event.TgtDCSUnit, self.ZoneNames ) ~= nil then
|
||||
self:_AddForCleanUp( Event.TgtDCSUnit, Event.TgtDCSUnitName )
|
||||
if self:IsInAirbase( Event.TgtUnit:GetVec2() ) then
|
||||
self:AddForCleanUp( Event.TgtUnit, Event.TgtDCSUnitName )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local CleanUpSurfaceTypeText = {
|
||||
"LAND",
|
||||
"SHALLOW_WATER",
|
||||
"WATER",
|
||||
"ROAD",
|
||||
"RUNWAY"
|
||||
}
|
||||
|
||||
--- At the defined time interval, CleanUp the Groups within the CleanUpList.
|
||||
-- @param #CLEANUP self
|
||||
function CLEANUP:_CleanUpScheduler()
|
||||
self:F( { "CleanUp Scheduler" } )
|
||||
-- @param #CLEANUP_AIRBASE self
|
||||
function CLEANUP_AIRBASE.__:CleanUpSchedule()
|
||||
|
||||
local CleanUpCount = 0
|
||||
for CleanUpUnitName, UnitData in pairs( self.CleanUpList ) do
|
||||
for CleanUpUnitName, CleanUpListData in pairs( self.CleanUpList ) do
|
||||
CleanUpCount = CleanUpCount + 1
|
||||
|
||||
self:T( { CleanUpUnitName, UnitData } )
|
||||
local CleanUpUnit = Unit.getByName(UnitData.CleanUpUnitName)
|
||||
local CleanUpGroupName = UnitData.CleanUpGroupName
|
||||
local CleanUpUnitName = UnitData.CleanUpUnitName
|
||||
if CleanUpUnit then
|
||||
self:T( { "CleanUp Scheduler", "Checking:", CleanUpUnitName } )
|
||||
local CleanUpUnit = CleanUpListData.CleanUpUnit -- Wrapper.Unit#UNIT
|
||||
local CleanUpGroupName = CleanUpListData.CleanUpGroupName
|
||||
|
||||
if CleanUpUnit:IsAlive() ~= nil then
|
||||
|
||||
if _DATABASE:GetStatusGroup( CleanUpGroupName ) ~= "ReSpawn" then
|
||||
local CleanUpUnitVec3 = CleanUpUnit:getPoint()
|
||||
--self:T( CleanUpUnitVec3 )
|
||||
local CleanUpUnitVec2 = {}
|
||||
CleanUpUnitVec2.x = CleanUpUnitVec3.x
|
||||
CleanUpUnitVec2.y = CleanUpUnitVec3.z
|
||||
--self:T( CleanUpUnitVec2 )
|
||||
local CleanUpSurfaceType = land.getSurfaceType(CleanUpUnitVec2)
|
||||
--self:T( CleanUpSurfaceType )
|
||||
|
||||
if CleanUpUnit and CleanUpUnit:getLife() <= CleanUpUnit:getLife0() * 0.95 then
|
||||
if CleanUpSurfaceType == land.SurfaceType.RUNWAY then
|
||||
if CleanUpUnit:inAir() then
|
||||
local CleanUpLandHeight = land.getHeight(CleanUpUnitVec2)
|
||||
local CleanUpUnitHeight = CleanUpUnitVec3.y - CleanUpLandHeight
|
||||
self:T( { "CleanUp Scheduler", "Height = " .. CleanUpUnitHeight } )
|
||||
if CleanUpUnitHeight < 30 then
|
||||
|
||||
local CleanUpCoordinate = CleanUpUnit:GetCoordinate()
|
||||
|
||||
self:T( { "CleanUp Scheduler", CleanUpUnitName } )
|
||||
if CleanUpUnit:GetLife() <= CleanUpUnit:GetLife0() * 0.95 then
|
||||
if CleanUpUnit:IsAboveRunway() then
|
||||
if CleanUpUnit:InAir() then
|
||||
|
||||
local CleanUpLandHeight = CleanUpCoordinate:GetLandHeight()
|
||||
local CleanUpUnitHeight = CleanUpCoordinate.y - CleanUpLandHeight
|
||||
|
||||
if CleanUpUnitHeight < 100 then
|
||||
self:T( { "CleanUp Scheduler", "Destroy " .. CleanUpUnitName .. " because below safe height and damaged." } )
|
||||
self:_DestroyUnit(CleanUpUnit, CleanUpUnitName)
|
||||
self:DestroyUnit( CleanUpUnit )
|
||||
end
|
||||
else
|
||||
self:T( { "CleanUp Scheduler", "Destroy " .. CleanUpUnitName .. " because on runway and damaged." } )
|
||||
self:_DestroyUnit(CleanUpUnit, CleanUpUnitName)
|
||||
self:DestroyUnit( CleanUpUnit )
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Clean Units which are waiting for a very long time in the CleanUpZone.
|
||||
if CleanUpUnit then
|
||||
local CleanUpUnitVelocity = CleanUpUnit:getVelocity()
|
||||
local CleanUpUnitVelocityTotal = math.abs(CleanUpUnitVelocity.x) + math.abs(CleanUpUnitVelocity.y) + math.abs(CleanUpUnitVelocity.z)
|
||||
if CleanUpUnitVelocityTotal < 1 then
|
||||
if UnitData.CleanUpMoved then
|
||||
if UnitData.CleanUpTime + 180 <= timer.getTime() then
|
||||
local CleanUpUnitVelocity = CleanUpUnit:GetVelocityKMH()
|
||||
if CleanUpUnitVelocity < 1 then
|
||||
if CleanUpListData.CleanUpMoved then
|
||||
if CleanUpListData.CleanUpTime + 180 <= timer.getTime() then
|
||||
self:T( { "CleanUp Scheduler", "Destroy due to not moving anymore " .. CleanUpUnitName } )
|
||||
self:_DestroyUnit(CleanUpUnit, CleanUpUnitName)
|
||||
self:DestroyUnit( CleanUpUnit )
|
||||
end
|
||||
end
|
||||
else
|
||||
UnitData.CleanUpTime = timer.getTime()
|
||||
UnitData.CleanUpMoved = true
|
||||
CleanUpListData.CleanUpTime = timer.getTime()
|
||||
CleanUpListData.CleanUpMoved = true
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
-- Do nothing ...
|
||||
self.CleanUpList[CleanUpUnitName] = nil -- Not anymore in the DCSRTE
|
||||
self.CleanUpList[CleanUpUnitName] = nil
|
||||
end
|
||||
else
|
||||
self:T( "CleanUp: Group " .. CleanUpUnitName .. " cannot be found in DCS RTE, removing ..." )
|
||||
self.CleanUpList[CleanUpUnitName] = nil -- Not anymore in the DCSRTE
|
||||
self.CleanUpList[CleanUpUnitName] = nil
|
||||
end
|
||||
end
|
||||
self:T(CleanUpCount)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
--- **Functional** -- Management of target **Designation**.
|
||||
--- **Functional** -- Management of target **Designation**. Lase, smoke and illuminate targets.
|
||||
--
|
||||
-- --
|
||||
--
|
||||
@ -70,10 +70,14 @@ do -- DESIGNATE
|
||||
-- The RecceSet is continuously detecting for potential Targets, executing its task as part of the DetectionObject.
|
||||
-- Once Targets have been detected, the DesignateObject will trigger the **Detect Event**.
|
||||
--
|
||||
-- In order to prevent an overflow in the DesignateObject of detected targets, there is a maximum
|
||||
-- amount of DetectionItems that can be put in **scope** of the DesignateObject.
|
||||
-- We call this the **MaximumDesignations** term.
|
||||
--
|
||||
-- As part of the Detect Event, the DetectionItems list is used by the DesignateObject to provide the Players with:
|
||||
--
|
||||
-- * The RecceGroups are reporting to each AttackGroup, sending **Messages** containing the Threat Level and the TargetSet composition.
|
||||
-- * **Menu options** are created and updated for each AttackGroup, containing the Threat Level and the TargetSet composition.
|
||||
-- * **Menu options** are created and updated for each AttackGroup, containing the Detection ID and the Coordinates.
|
||||
--
|
||||
-- A Player can then select an action from the Designate Menu.
|
||||
--
|
||||
@ -109,7 +113,7 @@ do -- DESIGNATE
|
||||
--
|
||||
-- ### 2.1 DESIGNATE States
|
||||
--
|
||||
-- * **Designating** ( Group ): The process is not started yet.
|
||||
-- * **Designating** ( Group ): The designation process.
|
||||
--
|
||||
-- ### 2.2 DESIGNATE Events
|
||||
--
|
||||
@ -119,9 +123,17 @@ do -- DESIGNATE
|
||||
-- * **@{#DESIGNATE.Smoke}**: Smoke the targets with the specified Index.
|
||||
-- * **@{#DESIGNATE.Status}**: Report designation status.
|
||||
--
|
||||
-- ## 3. Laser codes
|
||||
-- ## 3. Maximum Designations
|
||||
--
|
||||
-- ### 3.1 Set possible laser codes
|
||||
-- In order to prevent an overflow of designations due to many Detected Targets, there is a
|
||||
-- Maximum Designations scope that is set in the DesignationObject.
|
||||
--
|
||||
-- The method @{#DESIGNATE.SetMaximumDesignations}() will put a limit on the amount of designations put in scope of the DesignationObject.
|
||||
-- Using the menu system, the player can "forget" a designation, so that gradually a new designation can be put in scope when detected.
|
||||
--
|
||||
-- ## 4. Laser codes
|
||||
--
|
||||
-- ### 4.1. Set possible laser codes
|
||||
--
|
||||
-- An array of laser codes can be provided, that will be used by the DESIGNATE when lasing.
|
||||
-- The laser code is communicated by the Recce when it is lasing a larget.
|
||||
@ -139,11 +151,20 @@ do -- DESIGNATE
|
||||
--
|
||||
-- The above sets a collection of possible laser codes that can be assigned. **Note the { } notation!**
|
||||
--
|
||||
-- ### 3.2 Auto generate laser codes
|
||||
-- ### 4.2. Auto generate laser codes
|
||||
--
|
||||
-- Use the method @{#DESIGNATE.GenerateLaserCodes}() to generate all possible laser codes. Logic implemented and advised by Ciribob!
|
||||
--
|
||||
-- ## 4. Autolase to automatically lase detected targets.
|
||||
-- ### 4.3. Add specific lase codes to the lase menu
|
||||
--
|
||||
-- Certain plane types can only drop laser guided ordonnance when targets are lased with specific laser codes.
|
||||
-- The SU-25T needs targets to be lased using laser code 1113.
|
||||
-- The A-10A needs targets to be lased using laser code 1680.
|
||||
--
|
||||
-- The method @{#DESIGNATE.AddMenuLaserCode}() to allow a player to lase a target using a specific laser code.
|
||||
-- Remove such a lase menu option using @{#DESIGNATE.RemoveMenuLaserCode}().
|
||||
--
|
||||
-- ## 5. Autolase to automatically lase detected targets.
|
||||
--
|
||||
-- DetectionItems can be auto lased once detected by Recces. As such, there is almost no action required from the Players using the Designate Menu.
|
||||
-- The **auto lase** function can be activated through the Designation Menu.
|
||||
@ -154,7 +175,7 @@ do -- DESIGNATE
|
||||
--
|
||||
-- Activate the auto lasing.
|
||||
--
|
||||
-- ## 5. Target prioritization on threat level
|
||||
-- ## 6. Target prioritization on threat level
|
||||
--
|
||||
-- Targets can be detected of different types in one DetectionItem. Depending on the type of the Target, a different threat level applies in an Air to Ground combat context.
|
||||
-- SAMs are of a higher threat than normal tanks. So, if the Target type was recognized, the Recces will select those targets that form the biggest threat first,
|
||||
@ -167,7 +188,12 @@ do -- DESIGNATE
|
||||
--
|
||||
-- The example will activate the threat level prioritization for this the Designate object. Threats will be marked based on the threat level of the Target.
|
||||
--
|
||||
-- ## 6. Status Report
|
||||
-- ## 6. Designate Menu Location for a Mission
|
||||
--
|
||||
-- You can make DESIGNATE work for a @{Mission#MISSION} object. In this way, the designate menu will not appear in the root of the radio menu, but in the menu of the Mission.
|
||||
-- Use the method @{#DESIGNATE.SetMission}() to set the @{Mission} object for the designate function.
|
||||
--
|
||||
-- ## 7. Status Report
|
||||
--
|
||||
-- A status report is available that displays the current Targets detected, grouped per DetectionItem, and a list of which Targets are currently being marked.
|
||||
--
|
||||
@ -182,7 +208,6 @@ do -- DESIGNATE
|
||||
-- The example will activate the flashing of the status menu for this Designate object.
|
||||
--
|
||||
-- @field #DESIGNATE
|
||||
--
|
||||
DESIGNATE = {
|
||||
ClassName = "DESIGNATE",
|
||||
}
|
||||
@ -192,8 +217,9 @@ do -- DESIGNATE
|
||||
-- @param Tasking.CommandCenter#COMMANDCENTER CC
|
||||
-- @param Functional.Detection#DETECTION_BASE Detection
|
||||
-- @param Core.Set#SET_GROUP AttackSet The Attack collection of GROUP objects to designate and report for.
|
||||
-- @param Tasking.Mission#MISSION Mission (Optional) The Mission where the menu needs to be attached.
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:New( CC, Detection, AttackSet )
|
||||
function DESIGNATE:New( CC, Detection, AttackSet, Mission )
|
||||
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #DESIGNATE
|
||||
self:F( { Detection } )
|
||||
@ -360,22 +386,33 @@ do -- DESIGNATE
|
||||
self.RecceSet = Detection:GetDetectionSetGroup()
|
||||
self.Recces = {}
|
||||
self.Designating = {}
|
||||
self:SetDesignateName()
|
||||
|
||||
self.LaseDuration = 60
|
||||
|
||||
self:SetFlashStatusMenu( false )
|
||||
self:SetDesignateMenu()
|
||||
self:SetMission( Mission )
|
||||
|
||||
self:SetLaserCodes( 1688 ) -- set self.LaserCodes
|
||||
self:SetAutoLase( false ) -- set self.Autolase
|
||||
self:SetLaserCodes( { 1688, 1130, 4785, 6547, 1465, 4578 } ) -- set self.LaserCodes
|
||||
self:SetAutoLase( false, false ) -- set self.Autolase and don't send message.
|
||||
|
||||
self:SetThreatLevelPrioritization( false ) -- self.ThreatLevelPrioritization, default is threat level priorization off
|
||||
self:SetMaximumDesignations( 5 ) -- Sets the maximum designations. The default is 5 designations.
|
||||
self:SetMaximumDistanceDesignations( 12000 ) -- Sets the maximum distance on which designations can be accepted. The default is 8000 meters.
|
||||
self:SetMaximumMarkings( 2 ) -- Per target group, a maximum of 2 markings will be made by default.
|
||||
|
||||
self:SetDesignateMenu()
|
||||
|
||||
self.LaserCodesUsed = {}
|
||||
|
||||
|
||||
self.MenuLaserCodes = {} -- This map contains the laser codes that will be shown in the designate menu to lase with specific laser codes.
|
||||
|
||||
self.Detection:__Start( 2 )
|
||||
|
||||
self:__Detect( -15 )
|
||||
|
||||
self.MarkScheduler = SCHEDULER:New( self )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@ -399,6 +436,56 @@ do -- DESIGNATE
|
||||
end
|
||||
|
||||
|
||||
--- Set the maximum amount of designations.
|
||||
-- @param #DESIGNATE self
|
||||
-- @param #number MaximumDesignations
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:SetMaximumDesignations( MaximumDesignations )
|
||||
self.MaximumDesignations = MaximumDesignations
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the maximum ground designation distance.
|
||||
-- @param #DESIGNATE self
|
||||
-- @param #number MaximumDistanceGroundDesignation Maximum ground designation distance in meters.
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:SetMaximumDistanceGroundDesignation( MaximumDistanceGroundDesignation )
|
||||
self.MaximumDistanceGroundDesignation = MaximumDistanceGroundDesignation
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the maximum air designation distance.
|
||||
-- @param #DESIGNATE self
|
||||
-- @param #number MaximumDistanceAirDesignation Maximum air designation distance in meters.
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:SetMaximumDistanceAirDesignation( MaximumDistanceAirDesignation )
|
||||
self.MaximumDistanceAirDesignation = MaximumDistanceAirDesignation
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the overall maximum distance when designations can be accepted.
|
||||
-- @param #DESIGNATE self
|
||||
-- @param #number MaximumDistanceDesignations Maximum distance in meters to accept designations.
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:SetMaximumDistanceDesignations( MaximumDistanceDesignations )
|
||||
self.MaximumDistanceDesignations = MaximumDistanceDesignations
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the maximum amount of markings FACs will do, per designated target group.
|
||||
-- @param #DESIGNATE self
|
||||
-- @param #number MaximumMarkings Maximum markings FACs will do, per designated target group.
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:SetMaximumMarkings( MaximumMarkings )
|
||||
self.MaximumMarkings = MaximumMarkings
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set an array of possible laser codes.
|
||||
-- Each new lase will select a code from this table.
|
||||
-- @param #DESIGNATE self
|
||||
@ -407,13 +494,64 @@ do -- DESIGNATE
|
||||
function DESIGNATE:SetLaserCodes( LaserCodes ) --R2.1
|
||||
|
||||
self.LaserCodes = ( type( LaserCodes ) == "table" ) and LaserCodes or { LaserCodes }
|
||||
self:E(self.LaserCodes)
|
||||
self:E( { LaserCodes = self.LaserCodes } )
|
||||
|
||||
self.LaserCodesUsed = {}
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Add a specific lase code to the designate lase menu to lase targets with a specific laser code.
|
||||
-- The MenuText will appear in the lase menu.
|
||||
-- @param #DESIGNATE self
|
||||
-- @param #number LaserCode The specific laser code to be added to the lase menu.
|
||||
-- @param #string MenuText The text to be shown to the player. If you specify a %d in the MenuText, the %d will be replaced with the LaserCode specified.
|
||||
-- @return #DESIGNATE
|
||||
-- @usage
|
||||
-- RecceDesignation:AddMenuLaserCode( 1113, "Lase with %d for Su-25T" )
|
||||
-- RecceDesignation:AddMenuLaserCode( 1680, "Lase with %d for A-10A" )
|
||||
--
|
||||
function DESIGNATE:AddMenuLaserCode( LaserCode, MenuText )
|
||||
|
||||
self.MenuLaserCodes[LaserCode] = MenuText
|
||||
self:SetDesignateMenu()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Removes a specific lase code from the designate lase menu.
|
||||
-- @param #DESIGNATE self
|
||||
-- @param #number LaserCode The specific laser code that was set to be added to the lase menu.
|
||||
-- @return #DESIGNATE
|
||||
-- @usage
|
||||
-- RecceDesignation:RemoveMenuLaserCode( 1113 )
|
||||
--
|
||||
function DESIGNATE:RemoveMenuLaserCode( LaserCode )
|
||||
|
||||
self.MenuLaserCodes[LaserCode] = nil
|
||||
self:SetDesignateMenu()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Set the name of the designation. The name will appear in the menu.
|
||||
-- This method can be used to control different designations for different plane types.
|
||||
-- @param #DESIGNATE self
|
||||
-- @param #string DesignateName
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:SetDesignateName( DesignateName )
|
||||
|
||||
self.DesignateName = "Designation" .. ( DesignateName and ( " for " .. DesignateName ) or "" )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Generate an array of possible laser codes.
|
||||
-- Each new lase will select a code from this table.
|
||||
-- The entered value can range from 1111 - 1788,
|
||||
@ -470,21 +608,22 @@ do -- DESIGNATE
|
||||
--- Set auto lase.
|
||||
-- Auto lase will start lasing targets immediately when these are in range.
|
||||
-- @param #DESIGNATE self
|
||||
-- @param #boolean AutoLase
|
||||
-- @param #boolean AutoLase (optional) true sets autolase on, false off. Default is off.
|
||||
-- @param #boolean Message (optional) true is send message, false or nil won't send a message. Default is no message sent.
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:SetAutoLase( AutoLase ) --R2.1
|
||||
function DESIGNATE:SetAutoLase( AutoLase, Message )
|
||||
|
||||
self.AutoLase = AutoLase
|
||||
self.AutoLase = AutoLase or false
|
||||
|
||||
local AutoLaseOnOff = ( AutoLase == true ) and "On" or "Off"
|
||||
|
||||
local CC = self.CC:GetPositionable()
|
||||
|
||||
if CC then
|
||||
CC:MessageToSetGroup( "Auto Lase " .. AutoLaseOnOff .. ".", 15, self.AttackSet )
|
||||
if Message then
|
||||
local AutoLaseOnOff = ( self.AutoLase == true ) and "On" or "Off"
|
||||
local CC = self.CC:GetPositionable()
|
||||
if CC then
|
||||
CC:MessageToSetGroup( self.DesignateName .. ": Auto Lase " .. AutoLaseOnOff .. ".", 15, self.AttackSet )
|
||||
end
|
||||
end
|
||||
|
||||
self:ActivateAutoLase()
|
||||
self:CoordinateLase()
|
||||
self:SetDesignateMenu()
|
||||
|
||||
return self
|
||||
@ -501,6 +640,17 @@ do -- DESIGNATE
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set the MISSION object for which designate will function.
|
||||
-- When a MISSION object is assigned, the menu for the designation will be located at the Mission Menu.
|
||||
-- @param #DESIGNATE self
|
||||
-- @param Tasking.Mission#MISSION Mission The MISSION object.
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:SetMission( Mission ) --R2.2
|
||||
|
||||
self.Mission = Mission
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
@ -508,15 +658,96 @@ do -- DESIGNATE
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:onafterDetect()
|
||||
|
||||
self:__Detect( -60 )
|
||||
self:__Detect( -math.random( 60 ) )
|
||||
|
||||
self:ActivateAutoLase()
|
||||
self:DesignationScope()
|
||||
self:CoordinateLase()
|
||||
self:SendStatus()
|
||||
self:SetDesignateMenu()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Adapt the designation scope according the detected items.
|
||||
-- @param #DESIGNATE self
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:DesignationScope()
|
||||
|
||||
local DetectedItems = self.Detection:GetDetectedItems()
|
||||
|
||||
local DetectedItemCount = 0
|
||||
|
||||
for DesignateIndex, Designating in pairs( self.Designating ) do
|
||||
local DetectedItem = DetectedItems[DesignateIndex]
|
||||
if DetectedItem then
|
||||
-- Check LOS...
|
||||
local IsDetected = self.Detection:IsDetectedItemDetected( DetectedItem )
|
||||
self:F({IsDetected = IsDetected, DetectedItem })
|
||||
if IsDetected == false then
|
||||
self:F("Removing")
|
||||
-- This Detection is obsolete, remove from the designate scope
|
||||
self.Designating[DesignateIndex] = nil
|
||||
self.AttackSet:ForEachGroup(
|
||||
function( AttackGroup )
|
||||
local DetectionText = self.Detection:DetectedItemReportSummary( DesignateIndex, AttackGroup ):Text( ", " )
|
||||
self.CC:GetPositionable():MessageToGroup( "Targets out of LOS\n" .. DetectionText, 10, AttackGroup, self.DesignateName )
|
||||
end
|
||||
)
|
||||
else
|
||||
DetectedItemCount = DetectedItemCount + 1
|
||||
end
|
||||
else
|
||||
-- This Detection is obsolete, remove from the designate scope
|
||||
self.Designating[DesignateIndex] = nil
|
||||
end
|
||||
end
|
||||
|
||||
if DetectedItemCount < 5 then
|
||||
for DesignateIndex, DetectedItem in pairs( DetectedItems ) do
|
||||
local IsDetected = self.Detection:IsDetectedItemDetected( DetectedItem )
|
||||
if IsDetected == true then
|
||||
self:F( { DistanceRecce = DetectedItem.DistanceRecce } )
|
||||
if DetectedItem.DistanceRecce <= self.MaximumDistanceDesignations then
|
||||
if self.Designating[DesignateIndex] == nil then
|
||||
-- ok, we added one item to the designate scope.
|
||||
self.AttackSet:ForEachGroup(
|
||||
function( AttackGroup )
|
||||
local DetectionText = self.Detection:DetectedItemReportSummary( DesignateIndex, AttackGroup ):Text( ", " )
|
||||
self.CC:GetPositionable():MessageToGroup( "Targets detected at \n" .. DetectionText, 10, AttackGroup, self.DesignateName )
|
||||
end
|
||||
)
|
||||
self.Designating[DesignateIndex] = ""
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Coordinates the Auto Lase.
|
||||
-- @param #DESIGNATE self
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:CoordinateLase()
|
||||
|
||||
local DetectedItems = self.Detection:GetDetectedItems()
|
||||
|
||||
for DesignateIndex, Designating in pairs( self.Designating ) do
|
||||
local DetectedItem = DetectedItems[DesignateIndex]
|
||||
if DetectedItem then
|
||||
if self.AutoLase then
|
||||
self:LaseOn( DesignateIndex, self.LaseDuration )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Sends the status to the Attack Groups.
|
||||
-- @param #DESIGNATE self
|
||||
-- @param Wrapper.Group#GROUP AttackGroup
|
||||
@ -533,20 +764,23 @@ do -- DESIGNATE
|
||||
|
||||
if self.FlashStatusMenu[AttackGroup] or ( MenuAttackGroup and ( AttackGroup:GetName() == MenuAttackGroup:GetName() ) ) then
|
||||
|
||||
local DetectedReport = REPORT:New( "Targets designated:\n" )
|
||||
local DetectedReport = REPORT:New( "Targets ready for Designation:" )
|
||||
local DetectedItems = self.Detection:GetDetectedItems()
|
||||
|
||||
for Index, DetectedItemData in pairs( DetectedItems ) do
|
||||
|
||||
local Report = self.Detection:DetectedItemReportSummary( Index, AttackGroup )
|
||||
DetectedReport:Add(" - " .. Report)
|
||||
for DesignateIndex, Designating in pairs( self.Designating ) do
|
||||
local DetectedItem = DetectedItems[DesignateIndex]
|
||||
if DetectedItem then
|
||||
local Report = self.Detection:DetectedItemReportSummary( DesignateIndex, AttackGroup ):Text( ", " )
|
||||
DetectedReport:Add( string.rep( "-", 140 ) )
|
||||
DetectedReport:Add( " - " .. Report )
|
||||
end
|
||||
end
|
||||
|
||||
local CC = self.CC:GetPositionable()
|
||||
|
||||
CC:MessageToGroup( DetectedReport:Text( "\n" ), Duration, AttackGroup )
|
||||
CC:MessageToGroup( DetectedReport:Text( "\n" ), Duration, AttackGroup, self.DesignateName )
|
||||
|
||||
local DesignationReport = REPORT:New( "Targets marked:\n" )
|
||||
local DesignationReport = REPORT:New( "Marking Targets:\n" )
|
||||
|
||||
self.RecceSet:ForEachGroup(
|
||||
function( RecceGroup )
|
||||
@ -560,34 +794,7 @@ do -- DESIGNATE
|
||||
end
|
||||
)
|
||||
|
||||
CC:MessageToGroup( DesignationReport:Text(), Duration, AttackGroup )
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Coordinates the Auto Lase.
|
||||
-- @param #DESIGNATE self
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:ActivateAutoLase()
|
||||
|
||||
self.AttackSet:Flush()
|
||||
|
||||
self.AttackSet:ForEachGroup(
|
||||
|
||||
--- @param Wrapper.Group#GROUP GroupReport
|
||||
function( AttackGroup )
|
||||
|
||||
local DetectedItems = self.Detection:GetDetectedItems()
|
||||
|
||||
for Index, DetectedItemData in pairs( DetectedItems ) do
|
||||
if self.AutoLase then
|
||||
if not self.Designating[Index] then
|
||||
self:LaseOn( Index, self.LaseDuration )
|
||||
end
|
||||
end
|
||||
CC:MessageToGroup( DesignationReport:Text(), Duration, AttackGroup, self.DesignateName )
|
||||
end
|
||||
end
|
||||
)
|
||||
@ -606,66 +813,79 @@ do -- DESIGNATE
|
||||
|
||||
--- @param Wrapper.Group#GROUP GroupReport
|
||||
function( AttackGroup )
|
||||
local DesignateMenu = AttackGroup:GetState( AttackGroup, "DesignateMenu" ) -- Core.Menu#MENU_GROUP
|
||||
if DesignateMenu then
|
||||
DesignateMenu:Remove()
|
||||
DesignateMenu = nil
|
||||
self:E("Remove Menu")
|
||||
end
|
||||
DesignateMenu = MENU_GROUP:New( AttackGroup, "Designate" )
|
||||
self:E(DesignateMenu)
|
||||
AttackGroup:SetState( AttackGroup, "DesignateMenu", DesignateMenu )
|
||||
self.MenuDesignate = self.MenuDesignate or {}
|
||||
|
||||
local MissionMenu = nil
|
||||
|
||||
if self.Mission then
|
||||
MissionMenu = self.Mission:GetRootMenu( AttackGroup )
|
||||
end
|
||||
|
||||
local MenuTime = timer.getTime()
|
||||
|
||||
self.MenuDesignate[AttackGroup] = MENU_GROUP:New( AttackGroup, self.DesignateName, MissionMenu ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
local MenuDesignate = self.MenuDesignate[AttackGroup] -- Core.Menu#MENU_GROUP
|
||||
|
||||
-- Set Menu option for auto lase
|
||||
|
||||
if self.AutoLase then
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Auto Lase Off", DesignateMenu, self.MenuAutoLase, self, false )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Auto Lase Off", MenuDesignate, self.MenuAutoLase, self, false ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
else
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Auto Lase On", DesignateMenu, self.MenuAutoLase, self, true )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Auto Lase On", MenuDesignate, self.MenuAutoLase, self, true ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
end
|
||||
|
||||
local StatusMenu = MENU_GROUP:New( AttackGroup, "Status", DesignateMenu )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Report Status 15s", StatusMenu, self.MenuStatus, self, AttackGroup, 15 )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Report Status 30s", StatusMenu, self.MenuStatus, self, AttackGroup, 30 )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Report Status 60s", StatusMenu, self.MenuStatus, self, AttackGroup, 60 )
|
||||
local StatusMenu = MENU_GROUP:New( AttackGroup, "Status", MenuDesignate ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Report Status 15s", StatusMenu, self.MenuStatus, self, AttackGroup, 15 ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Report Status 30s", StatusMenu, self.MenuStatus, self, AttackGroup, 30 ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Report Status 60s", StatusMenu, self.MenuStatus, self, AttackGroup, 60 ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
|
||||
if self.FlashStatusMenu[AttackGroup] then
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Flash Status Report Off", StatusMenu, self.MenuFlashStatus, self, AttackGroup, false )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Flash Status Report Off", StatusMenu, self.MenuFlashStatus, self, AttackGroup, false ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
else
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Flash Status Report On", StatusMenu, self.MenuFlashStatus, self, AttackGroup, true )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Flash Status Report On", StatusMenu, self.MenuFlashStatus, self, AttackGroup, true ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
end
|
||||
|
||||
local DetectedItems = self.Detection:GetDetectedItems()
|
||||
|
||||
for Index, DetectedItemData in pairs( DetectedItems ) do
|
||||
for DesignateIndex, Designating in pairs( self.Designating ) do
|
||||
|
||||
local DetectedItem = self.Detection:GetDetectedItem( DesignateIndex )
|
||||
|
||||
if DetectedItem then
|
||||
|
||||
local Report = self.Detection:DetectedItemMenu( Index, AttackGroup )
|
||||
|
||||
if not self.Designating[Index] then
|
||||
local DetectedMenu = MENU_GROUP:New( AttackGroup, Report, DesignateMenu )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Lase target 60 secs", DetectedMenu, self.MenuLaseOn, self, Index, 60 )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Lase target 120 secs", DetectedMenu, self.MenuLaseOn, self, Index, 120 )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke red", DetectedMenu, self.MenuSmoke, self, Index, SMOKECOLOR.Red )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke blue", DetectedMenu, self.MenuSmoke, self, Index, SMOKECOLOR.Blue )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke green", DetectedMenu, self.MenuSmoke, self, Index, SMOKECOLOR.Green )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke white", DetectedMenu, self.MenuSmoke, self, Index, SMOKECOLOR.White )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke orange", DetectedMenu, self.MenuSmoke, self, Index, SMOKECOLOR.Orange )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Illuminate", DetectedMenu, self.MenuIlluminate, self, Index )
|
||||
else
|
||||
if self.Designating[Index] == "Laser" then
|
||||
Report = "Lasing " .. Report
|
||||
elseif self.Designating[Index] == "Smoke" then
|
||||
Report = "Smoking " .. Report
|
||||
elseif self.Designating[Index] == "Illuminate" then
|
||||
Report = "Illuminating " .. Report
|
||||
end
|
||||
local DetectedMenu = MENU_GROUP:New( AttackGroup, Report, DesignateMenu )
|
||||
if self.Designating[Index] == "Laser" then
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Stop lasing", DetectedMenu, self.MenuLaseOff, self, Index )
|
||||
local Coord = self.Detection:GetDetectedItemCoordinate( DesignateIndex )
|
||||
local ID = self.Detection:GetDetectedItemID( DesignateIndex )
|
||||
local MenuText = ID .. ", " .. Coord:ToStringA2G( AttackGroup )
|
||||
|
||||
if Designating == "" then
|
||||
MenuText = "(-) " .. MenuText
|
||||
local DetectedMenu = MENU_GROUP:New( AttackGroup, MenuText, MenuDesignate ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Search other target", DetectedMenu, self.MenuForget, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
for LaserCode, MenuText in pairs( self.MenuLaserCodes ) do
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, string.format( MenuText, LaserCode ), DetectedMenu, self.MenuLaseCode, self, DesignateIndex, 60, LaserCode ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
end
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Lase with random laser code(s)", DetectedMenu, self.MenuLaseOn, self, DesignateIndex, 60 ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke red", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Red ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke blue", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Blue ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke green", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Green ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke white", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.White ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Smoke orange", DetectedMenu, self.MenuSmoke, self, DesignateIndex, SMOKECOLOR.Orange ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Illuminate", DetectedMenu, self.MenuIlluminate, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
else
|
||||
if Designating == "Laser" then
|
||||
MenuText = "(L) " .. MenuText
|
||||
elseif Designating == "Smoke" then
|
||||
MenuText = "(S) " .. MenuText
|
||||
elseif Designating == "Illuminate" then
|
||||
MenuText = "(I) " .. MenuText
|
||||
end
|
||||
local DetectedMenu = MENU_GROUP:New( AttackGroup, MenuText, MenuDesignate ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
if Designating == "Laser" then
|
||||
MENU_GROUP_COMMAND:New( AttackGroup, "Stop lasing", DetectedMenu, self.MenuLaseOff, self, DesignateIndex ):SetTime( MenuTime ):SetTag( self.DesignateName )
|
||||
else
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
MenuDesignate:Remove( MenuTime, self.DesignateName )
|
||||
end
|
||||
)
|
||||
|
||||
@ -692,13 +912,23 @@ do -- DESIGNATE
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #DESIGNATE self
|
||||
function DESIGNATE:MenuForget( Index )
|
||||
|
||||
self:E("Forget")
|
||||
|
||||
self.Designating[Index] = nil
|
||||
self:SetDesignateMenu()
|
||||
end
|
||||
|
||||
---
|
||||
-- @param #DESIGNATE self
|
||||
function DESIGNATE:MenuAutoLase( AutoLase )
|
||||
|
||||
self:E("AutoLase")
|
||||
|
||||
self:SetAutoLase( AutoLase )
|
||||
self:SetAutoLase( AutoLase, true )
|
||||
end
|
||||
|
||||
---
|
||||
@ -708,7 +938,7 @@ do -- DESIGNATE
|
||||
self:E("Designate through Smoke")
|
||||
|
||||
self.Designating[Index] = "Smoke"
|
||||
self:__Smoke( 1, Index, Color )
|
||||
self:Smoke( Index, Color )
|
||||
end
|
||||
|
||||
---
|
||||
@ -729,96 +959,204 @@ do -- DESIGNATE
|
||||
self:E("Designate through Lase")
|
||||
|
||||
self:__LaseOn( 1, Index, Duration )
|
||||
self:SetDesignateMenu()
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #DESIGNATE self
|
||||
function DESIGNATE:MenuLaseCode( Index, Duration, LaserCode )
|
||||
|
||||
self:E( "Designate through Lase using " .. LaserCode )
|
||||
|
||||
self:__LaseOn( 1, Index, Duration, LaserCode )
|
||||
self:SetDesignateMenu()
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #DESIGNATE self
|
||||
function DESIGNATE:MenuLaseOff( Index, Duration )
|
||||
|
||||
self:E("Lasing off")
|
||||
|
||||
self.Designating[Index] = nil
|
||||
self.Designating[Index] = ""
|
||||
self:__LaseOff( 1, Index )
|
||||
self:SetDesignateMenu()
|
||||
end
|
||||
|
||||
---
|
||||
-- @param #DESIGNATE self
|
||||
function DESIGNATE:onafterLaseOn( From, Event, To, Index, Duration )
|
||||
function DESIGNATE:onafterLaseOn( From, Event, To, Index, Duration, LaserCode )
|
||||
|
||||
self.Designating[Index] = "Laser"
|
||||
self:Lasing( Index, Duration )
|
||||
self.LaseStart = timer.getTime()
|
||||
self.LaseDuration = Duration
|
||||
self:__Lasing( -1, Index, Duration, LaserCode )
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #DESIGNATE self
|
||||
-- @return #DESIGNATE
|
||||
function DESIGNATE:onafterLasing( From, Event, To, Index, Duration )
|
||||
function DESIGNATE:onafterLasing( From, Event, To, Index, Duration, LaserCodeRequested )
|
||||
|
||||
|
||||
local TargetSetUnit = self.Detection:GetDetectedSet( Index )
|
||||
|
||||
local MarkingCount = 0
|
||||
local MarkedTypes = {}
|
||||
local ReportTypes = REPORT:New()
|
||||
local ReportLaserCodes = REPORT:New()
|
||||
|
||||
TargetSetUnit:Flush()
|
||||
|
||||
--self:F( { Recces = self.Recces } )
|
||||
for TargetUnit, RecceData in pairs( self.Recces ) do
|
||||
local Recce = RecceData -- Wrapper.Unit#UNIT
|
||||
self:F( { TargetUnit = TargetUnit, Recce = Recce:GetName() } )
|
||||
if not Recce:IsLasing() then
|
||||
local LaserCode = Recce:GetLaserCode() --(Not deleted when stopping with lasing).
|
||||
local LaserCode = Recce:GetLaserCode() -- (Not deleted when stopping with lasing).
|
||||
self:F( { ClearingLaserCode = LaserCode } )
|
||||
self.LaserCodesUsed[LaserCode] = nil
|
||||
self.Recces[TargetUnit] = nil
|
||||
end
|
||||
end
|
||||
|
||||
TargetSetUnit:ForEachUnitPerThreatLevel( 10, 0,
|
||||
--- @param Wrapper.Unit#UNIT SmokeUnit
|
||||
function( TargetUnit )
|
||||
self:E("In procedure")
|
||||
if TargetUnit:IsAlive() then
|
||||
local Recce = self.Recces[TargetUnit]
|
||||
if not Recce then
|
||||
for RecceGroupID, RecceGroup in pairs( self.RecceSet:GetSet() ) do
|
||||
for UnitID, UnitData in pairs( RecceGroup:GetUnits() or {} ) do
|
||||
local RecceUnit = UnitData -- Wrapper.Unit#UNIT
|
||||
if RecceUnit:IsLasing() == false then
|
||||
if RecceUnit:IsDetected( TargetUnit ) and RecceUnit:IsLOS( TargetUnit ) then
|
||||
local LaserCodeIndex = math.random( 1, #self.LaserCodes )
|
||||
local LaserCode = self.LaserCodes[LaserCodeIndex]
|
||||
if not self.LaserCodesUsed[LaserCode] then
|
||||
self.LaserCodesUsed[LaserCode] = LaserCodeIndex
|
||||
local Spot = RecceUnit:LaseUnit( TargetUnit, LaserCode, Duration )
|
||||
local AttackSet = self.AttackSet
|
||||
function Spot:OnAfterDestroyed( From, Event, To )
|
||||
self:E( "Destroyed Message" )
|
||||
self.Recce:MessageToSetGroup( "Target " .. TargetUnit:GetTypeName() .. " destroyed. " .. TargetSetUnit:Count() .. " targets left.", 5, AttackSet )
|
||||
end
|
||||
self.Recces[TargetUnit] = RecceUnit
|
||||
RecceUnit:MessageToSetGroup( "Marking " .. TargetUnit:GetTypeName() .. " with laser " .. RecceUnit:GetSpot().LaserCode .. " for " .. Duration .. "s.", 5, self.AttackSet )
|
||||
break
|
||||
end
|
||||
else
|
||||
RecceUnit:MessageToSetGroup( "Can't mark " .. TargetUnit:GetTypeName(), 5, self.AttackSet )
|
||||
end
|
||||
else
|
||||
-- The Recce is lasing, but the Target is not detected or within LOS. So stop lasing and send a report.
|
||||
if not RecceUnit:IsDetected( TargetUnit ) or not RecceUnit:IsLOS( TargetUnit ) then
|
||||
local Recce = self.Recces[TargetUnit] -- Wrapper.Unit#UNIT
|
||||
if Recce then
|
||||
Recce:LaseOff()
|
||||
Recce:MessageToGroup( "Target " .. TargetUnit:GetTypeName() "out of LOS. Cancelling lase!", 5, self.AttackSet )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
Recce:MessageToSetGroup( "Marking " .. TargetUnit:GetTypeName() .. " with laser " .. Recce.LaserCode .. ".", 5, self.AttackSet )
|
||||
end
|
||||
|
||||
-- If a specific lasercode is requested, we disable one active lase!
|
||||
if LaserCodeRequested then
|
||||
for TargetUnit, RecceData in pairs( self.Recces ) do -- We break after the first has been processed.
|
||||
local Recce = RecceData -- Wrapper.Unit#UNIT
|
||||
self:F( { TargetUnit = TargetUnit, Recce = Recce:GetName() } )
|
||||
if Recce:IsLasing() then
|
||||
-- When a Recce is lasing, we switch the lasing off, and clear the references to the lasing in the DESIGNATE class.
|
||||
Recce:LaseOff() -- Switch off the lasing.
|
||||
local LaserCode = Recce:GetLaserCode() -- (Not deleted when stopping with lasing).
|
||||
self:F( { ClearingLaserCode = LaserCode } )
|
||||
self.LaserCodesUsed[LaserCode] = nil
|
||||
self.Recces[TargetUnit] = nil
|
||||
break
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
self:__Lasing( 15, Index, Duration )
|
||||
end
|
||||
|
||||
self:SetDesignateMenu()
|
||||
if self.AutoLase or ( not self.AutoLase and ( self.LaseStart + Duration >= timer.getTime() ) ) then
|
||||
|
||||
TargetSetUnit:ForEachUnitPerThreatLevel( 10, 0,
|
||||
--- @param Wrapper.Unit#UNIT SmokeUnit
|
||||
function( TargetUnit )
|
||||
|
||||
self:F( { TargetUnit = TargetUnit:GetName() } )
|
||||
|
||||
if MarkingCount < self.MaximumMarkings then
|
||||
|
||||
if TargetUnit:IsAlive() then
|
||||
|
||||
local Recce = self.Recces[TargetUnit]
|
||||
|
||||
if not Recce then
|
||||
|
||||
self:E( "Lasing..." )
|
||||
self.RecceSet:Flush()
|
||||
|
||||
for RecceGroupID, RecceGroup in pairs( self.RecceSet:GetSet() ) do
|
||||
for UnitID, UnitData in pairs( RecceGroup:GetUnits() or {} ) do
|
||||
|
||||
local RecceUnit = UnitData -- Wrapper.Unit#UNIT
|
||||
local RecceUnitDesc = RecceUnit:GetDesc()
|
||||
--self:F( { RecceUnit = RecceUnit:GetName(), RecceDescription = RecceUnitDesc } )
|
||||
|
||||
if RecceUnit:IsLasing() == false then
|
||||
--self:F( { IsDetected = RecceUnit:IsDetected( TargetUnit ), IsLOS = RecceUnit:IsLOS( TargetUnit ) } )
|
||||
|
||||
if RecceUnit:IsDetected( TargetUnit ) and RecceUnit:IsLOS( TargetUnit ) then
|
||||
|
||||
local LaserCodeIndex = math.random( 1, #self.LaserCodes )
|
||||
local LaserCode = self.LaserCodes[LaserCodeIndex]
|
||||
--self:F( { LaserCode = LaserCode, LaserCodeUsed = self.LaserCodesUsed[LaserCode] } )
|
||||
|
||||
if LaserCodeRequested and LaserCodeRequested ~= LaserCode then
|
||||
LaserCode = LaserCodeRequested
|
||||
LaserCodeRequested = nil
|
||||
end
|
||||
|
||||
if not self.LaserCodesUsed[LaserCode] then
|
||||
|
||||
self.LaserCodesUsed[LaserCode] = LaserCodeIndex
|
||||
local Spot = RecceUnit:LaseUnit( TargetUnit, LaserCode, Duration )
|
||||
local AttackSet = self.AttackSet
|
||||
|
||||
function Spot:OnAfterDestroyed( From, Event, To )
|
||||
self:E( "Destroyed Message" )
|
||||
self.Recce:ToSetGroup( "Target " .. TargetUnit:GetTypeName() .. " destroyed. " .. TargetSetUnit:Count() .. " targets left.", 5, AttackSet, self.DesignateName )
|
||||
end
|
||||
|
||||
self.Recces[TargetUnit] = RecceUnit
|
||||
RecceUnit:MessageToSetGroup( "Marking " .. TargetUnit:GetTypeName() .. " with laser " .. RecceUnit:GetSpot().LaserCode .. " for " .. Duration .. "s.", 5, self.AttackSet, self.DesignateName )
|
||||
-- OK. We have assigned for the Recce a TargetUnit. We can exit the function.
|
||||
MarkingCount = MarkingCount + 1
|
||||
local TargetUnitType = TargetUnit:GetTypeName()
|
||||
if not MarkedTypes[TargetUnitType] then
|
||||
MarkedTypes[TargetUnitType] = true
|
||||
ReportTypes:Add(TargetUnitType)
|
||||
end
|
||||
ReportLaserCodes:Add(RecceUnit.LaserCode)
|
||||
return
|
||||
end
|
||||
else
|
||||
--RecceUnit:MessageToSetGroup( "Can't mark " .. TargetUnit:GetTypeName(), 5, self.AttackSet )
|
||||
end
|
||||
else
|
||||
-- The Recce is lasing, but the Target is not detected or within LOS. So stop lasing and send a report.
|
||||
|
||||
if not RecceUnit:IsDetected( TargetUnit ) or not RecceUnit:IsLOS( TargetUnit ) then
|
||||
|
||||
local Recce = self.Recces[TargetUnit] -- Wrapper.Unit#UNIT
|
||||
|
||||
if Recce then
|
||||
Recce:LaseOff()
|
||||
Recce:MessageToSetGroup( "Target " .. TargetUnit:GetTypeName() "out of LOS. Cancelling lase!", 5, self.AttackSet, self.DesignateName )
|
||||
end
|
||||
else
|
||||
MarkingCount = MarkingCount + 1
|
||||
local TargetUnitType = TargetUnit:GetTypeName()
|
||||
if not MarkedTypes[TargetUnitType] then
|
||||
MarkedTypes[TargetUnitType] = true
|
||||
ReportTypes:Add(TargetUnitType)
|
||||
end
|
||||
ReportLaserCodes:Add(RecceUnit.LaserCode)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
MarkingCount = MarkingCount + 1
|
||||
local TargetUnitType = TargetUnit:GetTypeName()
|
||||
if not MarkedTypes[TargetUnitType] then
|
||||
MarkedTypes[TargetUnitType] = true
|
||||
ReportTypes:Add(TargetUnitType)
|
||||
end
|
||||
ReportLaserCodes:Add(Recce.LaserCode)
|
||||
--Recce:MessageToSetGroup( self.DesignateName .. ": Marking " .. TargetUnit:GetTypeName() .. " with laser " .. Recce.LaserCode .. ".", 5, self.AttackSet )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
||||
|
||||
local MarkedTypesText = ReportTypes:Text(', ')
|
||||
local MarkedLaserCodesText = ReportLaserCodes:Text(', ')
|
||||
for MarkedType, MarketCount in pairs( MarkedTypes ) do
|
||||
self.CC:GetPositionable():MessageToSetGroup( "Marking " .. MarkingCount .. " x " .. MarkedTypesText .. " with lasers " .. MarkedLaserCodesText .. ".", 5, self.AttackSet, self.DesignateName )
|
||||
end
|
||||
|
||||
self:__Lasing( -30, Index, Duration, LaserCodeRequested )
|
||||
|
||||
self:SetDesignateMenu()
|
||||
|
||||
else
|
||||
self:__LaseOff( 1 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -830,7 +1168,7 @@ do -- DESIGNATE
|
||||
local CC = self.CC:GetPositionable()
|
||||
|
||||
if CC then
|
||||
CC:MessageToSetGroup( "Stopped lasing.", 5, self.AttackSet )
|
||||
CC:MessageToSetGroup( "Stopped lasing.", 5, self.AttackSet, self.DesignateName )
|
||||
end
|
||||
|
||||
local TargetSetUnit = self.Detection:GetDetectedSet( Index )
|
||||
@ -839,7 +1177,7 @@ do -- DESIGNATE
|
||||
|
||||
for TargetID, RecceData in pairs( Recces ) do
|
||||
local Recce = RecceData -- Wrapper.Unit#UNIT
|
||||
Recce:MessageToSetGroup( "Stopped lasing " .. Recce:GetSpot().Target:GetTypeName() .. ".", 5, self.AttackSet )
|
||||
Recce:MessageToSetGroup( "Stopped lasing " .. Recce:GetSpot().Target:GetTypeName() .. ".", 5, self.AttackSet, self.DesignateName )
|
||||
Recce:LaseOff()
|
||||
end
|
||||
|
||||
@ -859,19 +1197,29 @@ do -- DESIGNATE
|
||||
local TargetSetUnit = self.Detection:GetDetectedSet( Index )
|
||||
local TargetSetUnitCount = TargetSetUnit:Count()
|
||||
|
||||
TargetSetUnit:ForEachUnit(
|
||||
local MarkedCount = 0
|
||||
|
||||
TargetSetUnit:ForEachUnitPerThreatLevel( 10, 0,
|
||||
--- @param Wrapper.Unit#UNIT SmokeUnit
|
||||
function( SmokeUnit )
|
||||
self:E("In procedure")
|
||||
if math.random( 1, TargetSetUnitCount ) == math.random( 1, TargetSetUnitCount ) then
|
||||
|
||||
if MarkedCount < self.MaximumMarkings then
|
||||
|
||||
MarkedCount = MarkedCount + 1
|
||||
|
||||
self:E( "Smoking ..." )
|
||||
|
||||
local RecceGroup = self.RecceSet:FindNearestGroupFromPointVec2(SmokeUnit:GetPointVec2())
|
||||
local RecceUnit = RecceGroup:GetUnit( 1 )
|
||||
|
||||
if RecceUnit then
|
||||
RecceUnit:MessageToSetGroup( "Smoking " .. SmokeUnit:GetTypeName() .. ".", 5, self.AttackSet )
|
||||
SCHEDULER:New( self,
|
||||
|
||||
RecceUnit:MessageToSetGroup( "Smoking " .. SmokeUnit:GetTypeName() .. ".", 5, self.AttackSet, self.DesignateName )
|
||||
|
||||
self.MarkScheduler:Schedule( self,
|
||||
function()
|
||||
if SmokeUnit:IsAlive() then
|
||||
SmokeUnit:Smoke( Color, 150 )
|
||||
SmokeUnit:Smoke( Color, 50, 2 )
|
||||
end
|
||||
self:Done( Index )
|
||||
end, {}, math.random( 5, 20 )
|
||||
@ -896,8 +1244,8 @@ do -- DESIGNATE
|
||||
local RecceGroup = self.RecceSet:FindNearestGroupFromPointVec2(TargetUnit:GetPointVec2())
|
||||
local RecceUnit = RecceGroup:GetUnit( 1 )
|
||||
if RecceUnit then
|
||||
RecceUnit:MessageToSetGroup( "Illuminating " .. TargetUnit:GetTypeName() .. ".", 5, self.AttackSet )
|
||||
SCHEDULER:New( self,
|
||||
RecceUnit:MessageToSetGroup( "Illuminating " .. TargetUnit:GetTypeName() .. ".", 5, self.AttackSet, self.DesignateName )
|
||||
self.MarkScheduler:Schedule( self,
|
||||
function()
|
||||
if TargetUnit:IsAlive() then
|
||||
TargetUnit:GetPointVec3():AddY(300):IlluminationBomb()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -875,7 +875,7 @@ function ESCORT:_AttackTarget( DetectedItemID )
|
||||
end, Tasks
|
||||
)
|
||||
|
||||
Tasks[#Tasks+1] = EscortGroup:TaskFunction( 1, 2, "_Resume", { "''" } )
|
||||
Tasks[#Tasks+1] = EscortGroup:TaskFunction( "_Resume", { "''" } )
|
||||
|
||||
EscortGroup:SetTask(
|
||||
EscortGroup:TaskCombo(
|
||||
@ -1161,19 +1161,23 @@ function ESCORT:_ReportTargetsScheduler()
|
||||
for ClientEscortGroupName, EscortGroupData in pairs( self.EscortClient._EscortGroups ) do
|
||||
|
||||
local ClientEscortTargets = EscortGroupData.Detection
|
||||
--local EscortUnit = EscortGroupData:GetUnit( 1 )
|
||||
|
||||
for DetectedItemID, DetectedItem in ipairs( DetectedItems ) do
|
||||
for DetectedItemID, DetectedItem in pairs( DetectedItems ) do
|
||||
self:E( { DetectedItemID, DetectedItem } )
|
||||
-- Remove the sub menus of the Attack menu of the Escort for the EscortGroup.
|
||||
|
||||
local DetectedItemReportSummary = self.Detection:DetectedItemReportSummary( DetectedItemID, EscortGroupData )
|
||||
local DetectedItemReportSummary = self.Detection:DetectedItemReportSummary( DetectedItemID, EscortGroupData.EscortGroup, _DATABASE:GetPlayerSettings( self.EscortClient:GetPlayerName() ) )
|
||||
|
||||
if ClientEscortGroupName == EscortGroupName then
|
||||
|
||||
DetectedMsgs[#DetectedMsgs+1] = DetectedItemReportSummary
|
||||
local DetectedMsg = DetectedItemReportSummary:Text("\n")
|
||||
DetectedMsgs[#DetectedMsgs+1] = DetectedMsg
|
||||
|
||||
self:T( DetectedMsg )
|
||||
|
||||
MENU_CLIENT_COMMAND:New( self.EscortClient,
|
||||
DetectedItemReportSummary,
|
||||
DetectedMsg,
|
||||
self.EscortMenuAttackNearbyTargets,
|
||||
ESCORT._AttackTarget,
|
||||
self,
|
||||
@ -1182,10 +1186,12 @@ function ESCORT:_ReportTargetsScheduler()
|
||||
else
|
||||
if self.EscortMenuTargetAssistance then
|
||||
|
||||
self:T( DetectedItemReportSummary )
|
||||
local DetectedMsg = DetectedItemReportSummary:Text("\n")
|
||||
self:T( DetectedMsg )
|
||||
|
||||
local MenuTargetAssistance = MENU_CLIENT:New( self.EscortClient, EscortGroupData.EscortName, self.EscortMenuTargetAssistance )
|
||||
MENU_CLIENT_COMMAND:New( self.EscortClient,
|
||||
DetectedItemReportSummary,
|
||||
DetectedMsg,
|
||||
MenuTargetAssistance,
|
||||
ESCORT._AssistTarget,
|
||||
self,
|
||||
@ -1201,7 +1207,7 @@ function ESCORT:_ReportTargetsScheduler()
|
||||
end
|
||||
self:E( DetectedMsgs )
|
||||
if DetectedTargets then
|
||||
self.EscortGroup:MessageToClient( "Detected targets:\n" .. table.concat( DetectedMsgs, "\n" ), 20, self.EscortClient )
|
||||
self.EscortGroup:MessageToClient( "Reporting detected targets:\n" .. table.concat( DetectedMsgs, "\n" ), 20, self.EscortClient )
|
||||
else
|
||||
self.EscortGroup:MessageToClient( "No targets detected.", 10, self.EscortClient )
|
||||
end
|
||||
|
||||
@ -442,7 +442,7 @@ function MISSILETRAINER._MenuMessages( MenuParameters )
|
||||
|
||||
if MenuParameters.Distance ~= nil then
|
||||
self.Distance = MenuParameters.Distance
|
||||
MESSAGE:New( "Hit detection distance set to " .. self.Distance * 1000 .. " meters", 15, "Menu" ):ToAll()
|
||||
MESSAGE:New( "Hit detection distance set to " .. ( self.Distance * 1000 ) .. " meters", 15, "Menu" ):ToAll()
|
||||
end
|
||||
|
||||
end
|
||||
@ -570,72 +570,76 @@ function MISSILETRAINER:_TrackMissiles()
|
||||
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
|
||||
|
||||
local Client = ClientData.Client
|
||||
self:T2( { Client:GetName() } )
|
||||
|
||||
if Client and Client:IsAlive() then
|
||||
|
||||
for MissileDataID, MissileData in pairs( ClientData.MissileData ) do
|
||||
self:T3( MissileDataID )
|
||||
|
||||
local TrainerSourceUnit = MissileData.TrainerSourceUnit
|
||||
local TrainerWeapon = MissileData.TrainerWeapon
|
||||
local TrainerTargetUnit = MissileData.TrainerTargetUnit
|
||||
local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName
|
||||
local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched
|
||||
for MissileDataID, MissileData in pairs( ClientData.MissileData ) do
|
||||
self:T3( MissileDataID )
|
||||
|
||||
if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then
|
||||
local PositionMissile = TrainerWeapon:getPosition().p
|
||||
local TargetVec3 = Client:GetVec3()
|
||||
|
||||
local Distance = ( ( PositionMissile.x - TargetVec3.x )^2 +
|
||||
( PositionMissile.y - TargetVec3.y )^2 +
|
||||
( PositionMissile.z - TargetVec3.z )^2
|
||||
) ^ 0.5 / 1000
|
||||
|
||||
if Distance <= self.Distance then
|
||||
-- Hit alert
|
||||
TrainerWeapon:destroy()
|
||||
if self.MessagesOnOff == true and self.AlertsHitsOnOff == true then
|
||||
|
||||
self:T( "killed" )
|
||||
|
||||
local Message = MESSAGE:New(
|
||||
string.format( "%s launched by %s killed %s",
|
||||
TrainerWeapon:getTypeName(),
|
||||
TrainerSourceUnit:GetTypeName(),
|
||||
TrainerTargetUnit:GetPlayerName()
|
||||
), 15, "Hit Alert" )
|
||||
|
||||
if self.AlertsToAll == true then
|
||||
Message:ToAll()
|
||||
else
|
||||
Message:ToClient( Client )
|
||||
local TrainerSourceUnit = MissileData.TrainerSourceUnit
|
||||
local TrainerWeapon = MissileData.TrainerWeapon
|
||||
local TrainerTargetUnit = MissileData.TrainerTargetUnit
|
||||
local TrainerWeaponTypeName = MissileData.TrainerWeaponTypeName
|
||||
local TrainerWeaponLaunched = MissileData.TrainerWeaponLaunched
|
||||
|
||||
if Client and Client:IsAlive() and TrainerSourceUnit and TrainerSourceUnit:IsAlive() and TrainerWeapon and TrainerWeapon:isExist() and TrainerTargetUnit and TrainerTargetUnit:IsAlive() then
|
||||
local PositionMissile = TrainerWeapon:getPosition().p
|
||||
local TargetVec3 = Client:GetVec3()
|
||||
|
||||
local Distance = ( ( PositionMissile.x - TargetVec3.x )^2 +
|
||||
( PositionMissile.y - TargetVec3.y )^2 +
|
||||
( PositionMissile.z - TargetVec3.z )^2
|
||||
) ^ 0.5 / 1000
|
||||
|
||||
if Distance <= self.Distance then
|
||||
-- Hit alert
|
||||
TrainerWeapon:destroy()
|
||||
if self.MessagesOnOff == true and self.AlertsHitsOnOff == true then
|
||||
|
||||
self:T( "killed" )
|
||||
|
||||
local Message = MESSAGE:New(
|
||||
string.format( "%s launched by %s killed %s",
|
||||
TrainerWeapon:getTypeName(),
|
||||
TrainerSourceUnit:GetTypeName(),
|
||||
TrainerTargetUnit:GetPlayerName()
|
||||
), 15, "Hit Alert" )
|
||||
|
||||
if self.AlertsToAll == true then
|
||||
Message:ToAll()
|
||||
else
|
||||
Message:ToClient( Client )
|
||||
end
|
||||
|
||||
MissileData = nil
|
||||
table.remove( ClientData.MissileData, MissileDataID )
|
||||
self:T(ClientData.MissileData)
|
||||
end
|
||||
end
|
||||
else
|
||||
if not ( TrainerWeapon and TrainerWeapon:isExist() ) then
|
||||
if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then
|
||||
-- Weapon does not exist anymore. Delete from Table
|
||||
local Message = MESSAGE:New(
|
||||
string.format( "%s launched by %s self destructed!",
|
||||
TrainerWeaponTypeName,
|
||||
TrainerSourceUnit:GetTypeName()
|
||||
), 5, "Tracking" )
|
||||
|
||||
if self.AlertsToAll == true then
|
||||
Message:ToAll()
|
||||
else
|
||||
Message:ToClient( Client )
|
||||
end
|
||||
end
|
||||
|
||||
MissileData = nil
|
||||
table.remove( ClientData.MissileData, MissileDataID )
|
||||
self:T(ClientData.MissileData)
|
||||
self:T( ClientData.MissileData )
|
||||
end
|
||||
end
|
||||
else
|
||||
if not ( TrainerWeapon and TrainerWeapon:isExist() ) then
|
||||
if self.MessagesOnOff == true and self.AlertsLaunchesOnOff == true then
|
||||
-- Weapon does not exist anymore. Delete from Table
|
||||
local Message = MESSAGE:New(
|
||||
string.format( "%s launched by %s self destructed!",
|
||||
TrainerWeaponTypeName,
|
||||
TrainerSourceUnit:GetTypeName()
|
||||
), 5, "Tracking" )
|
||||
|
||||
if self.AlertsToAll == true then
|
||||
Message:ToAll()
|
||||
else
|
||||
Message:ToClient( Client )
|
||||
end
|
||||
end
|
||||
MissileData = nil
|
||||
table.remove( ClientData.MissileData, MissileDataID )
|
||||
self:T( ClientData.MissileData )
|
||||
end
|
||||
end
|
||||
else
|
||||
self.TrackingMissiles[ClientDataID] = nil
|
||||
end
|
||||
end
|
||||
|
||||
@ -651,7 +655,7 @@ function MISSILETRAINER:_TrackMissiles()
|
||||
for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do
|
||||
|
||||
local Client = ClientData.Client
|
||||
self:T2( { Client:GetName() } )
|
||||
--self:T2( { Client:GetName() } )
|
||||
|
||||
|
||||
ClientData.MessageToClient = ""
|
||||
@ -661,7 +665,7 @@ function MISSILETRAINER:_TrackMissiles()
|
||||
for TrackingDataID, TrackingData in pairs( self.TrackingMissiles ) do
|
||||
|
||||
for MissileDataID, MissileData in pairs( TrackingData.MissileData ) do
|
||||
self:T3( MissileDataID )
|
||||
--self:T3( MissileDataID )
|
||||
|
||||
local TrainerSourceUnit = MissileData.TrainerSourceUnit
|
||||
local TrainerWeapon = MissileData.TrainerWeapon
|
||||
|
||||
305
Moose Development/Moose/Functional/Protect.lua
Normal file
305
Moose Development/Moose/Functional/Protect.lua
Normal file
@ -0,0 +1,305 @@
|
||||
--- **Functional** -- The PROTECT class handles the protection of objects, which can be zones, units, scenery.
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **Sven Van de Velde (FlightControl)**
|
||||
-- ### Contributions: **MillerTime**
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- @module Protect
|
||||
|
||||
--- @type PROTECT.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-)
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- @type PROTECT
|
||||
-- @extends #PROTECT.__
|
||||
|
||||
--- # PROTECT, extends @{Base#BASE}
|
||||
--
|
||||
-- @field #PROTECT
|
||||
PROTECT = {
|
||||
ClassName = "PROTECT",
|
||||
}
|
||||
|
||||
--- Get the ProtectZone
|
||||
-- @param #PROTECT self
|
||||
-- @return Core.Zone#ZONE_BASE
|
||||
function PROTECT:GetProtectZone()
|
||||
return self.ProtectZone
|
||||
end
|
||||
|
||||
|
||||
--- Get the name of the ProtectZone
|
||||
-- @param #PROTECT self
|
||||
-- @return #string
|
||||
function PROTECT:GetProtectZoneName()
|
||||
return self.ProtectZone:GetName()
|
||||
end
|
||||
|
||||
|
||||
--- Set the owning coalition of the zone.
|
||||
-- @param #PROTECT self
|
||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition
|
||||
function PROTECT:SetCoalition( Coalition )
|
||||
self.Coalition = Coalition
|
||||
end
|
||||
|
||||
|
||||
--- Get the owning coalition of the zone.
|
||||
-- @param #PROTECT self
|
||||
-- @return DCSCoalition.DCSCoalition#coalition Coalition.
|
||||
function PROTECT:GetCoalition()
|
||||
return self.Coalition
|
||||
end
|
||||
|
||||
|
||||
--- Get the owning coalition name of the zone.
|
||||
-- @param #PROTECT self
|
||||
-- @return #string Coalition name.
|
||||
function PROTECT:GetCoalitionName()
|
||||
|
||||
if self.Coalition == coalition.side.BLUE then
|
||||
return "Blue"
|
||||
end
|
||||
|
||||
if self.Coalition == coalition.side.RED then
|
||||
return "Red"
|
||||
end
|
||||
|
||||
if self.Coalition == coalition.side.NEUTRAL then
|
||||
return "Neutral"
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
|
||||
function PROTECT:IsGuarded()
|
||||
|
||||
local IsGuarded = self.ProtectZone:IsAllInZoneOfCoalition( self.Coalition )
|
||||
self:E( { IsGuarded = IsGuarded } )
|
||||
return IsGuarded
|
||||
end
|
||||
|
||||
function PROTECT:IsCaptured()
|
||||
|
||||
local IsCaptured = self.ProtectZone:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
self:E( { IsCaptured = IsCaptured } )
|
||||
return IsCaptured
|
||||
end
|
||||
|
||||
|
||||
function PROTECT:IsAttacked()
|
||||
|
||||
local IsAttacked = self.ProtectZone:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
self:E( { IsAttacked = IsAttacked } )
|
||||
return IsAttacked
|
||||
end
|
||||
|
||||
|
||||
function PROTECT:IsEmpty()
|
||||
|
||||
local IsEmpty = self.ProtectZone:IsNoneInZone()
|
||||
self:E( { IsEmpty = IsEmpty } )
|
||||
return IsEmpty
|
||||
end
|
||||
|
||||
|
||||
--- Check if the units are still alive.
|
||||
-- @param #PROTECT self
|
||||
function PROTECT:AreProtectUnitsAlive()
|
||||
|
||||
local IsAlive = false
|
||||
|
||||
local UnitSet = self.ProtectUnitSet
|
||||
UnitSet:Flush()
|
||||
local UnitList = UnitSet:GetSet()
|
||||
|
||||
for UnitID, ProtectUnit in pairs( UnitList ) do
|
||||
local IsUnitAlive = ProtectUnit:IsAlive()
|
||||
if IsUnitAlive == true then
|
||||
IsAlive = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return IsAlive
|
||||
end
|
||||
|
||||
--- Check if the statics are still alive.
|
||||
-- @param #PROTECT self
|
||||
function PROTECT:AreProtectStaticsAlive()
|
||||
|
||||
local IsAlive = false
|
||||
|
||||
local StaticSet = self.ProtectStaticSet
|
||||
StaticSet:Flush()
|
||||
local StaticList = StaticSet:GetSet()
|
||||
|
||||
for UnitID, ProtectStatic in pairs( StaticList ) do
|
||||
local IsStaticAlive = ProtectStatic:IsAlive()
|
||||
if IsStaticAlive == true then
|
||||
IsAlive = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return IsAlive
|
||||
end
|
||||
|
||||
|
||||
--- Check if there is a capture unit in the zone.
|
||||
-- @param #PROTECT self
|
||||
function PROTECT:IsCaptureUnitInZone()
|
||||
|
||||
local CaptureUnitSet = self.CaptureUnitSet
|
||||
CaptureUnitSet:Flush()
|
||||
|
||||
local IsInZone = self.CaptureUnitSet:IsPartiallyInZone( self.ProtectZone )
|
||||
|
||||
self:E({IsInZone = IsInZone})
|
||||
|
||||
return IsInZone
|
||||
end
|
||||
|
||||
--- Smoke.
|
||||
-- @param #PROTECT self
|
||||
-- @param #SMOKECOLOR.Color SmokeColor
|
||||
function PROTECT:Smoke( SmokeColor )
|
||||
|
||||
self.SmokeColor = SmokeColor
|
||||
end
|
||||
|
||||
|
||||
--- Flare.
|
||||
-- @param #PROTECT self
|
||||
-- @param #SMOKECOLOR.Color FlareColor
|
||||
function PROTECT:Flare( FlareColor )
|
||||
self.ProtectZone:FlareZone( FlareColor, math.random( 1, 360 ) )
|
||||
end
|
||||
|
||||
|
||||
--- Mark.
|
||||
-- @param #PROTECT self
|
||||
function PROTECT:Mark()
|
||||
|
||||
local Coord = self.ProtectZone:GetCoordinate()
|
||||
local ZoneName = self:GetProtectZoneName()
|
||||
local State = self:GetState()
|
||||
|
||||
if self.MarkRed and self.MarkBlue then
|
||||
self:E( { MarkRed = self.MarkRed, MarkBlue = self.MarkBlue } )
|
||||
Coord:RemoveMark( self.MarkRed )
|
||||
Coord:RemoveMark( self.MarkBlue )
|
||||
end
|
||||
|
||||
if self.Coalition == coalition.side.BLUE then
|
||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Guard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
self.MarkRed = Coord:MarkToCoalitionRed( "Capture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
else
|
||||
self.MarkRed = Coord:MarkToCoalitionRed( "Guard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Capture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Bound.
|
||||
-- @param #PROTECT self
|
||||
function PROTECT:onafterStart()
|
||||
|
||||
self:ScheduleRepeat( 5, 15, 0.1, nil, self.StatusCoalition, self )
|
||||
self:ScheduleRepeat( 5, 15, 0.1, nil, self.StatusZone, self )
|
||||
self:ScheduleRepeat( 10, 15, 0, nil, self.StatusSmoke, self )
|
||||
end
|
||||
|
||||
--- Bound.
|
||||
-- @param #PROTECT self
|
||||
function PROTECT:onenterGuarded()
|
||||
|
||||
|
||||
if self.Coalition == coalition.side.BLUE then
|
||||
--elf.ProtectZone:BoundZone( 12, country.id.USA )
|
||||
else
|
||||
--self.ProtectZone:BoundZone( 12, country.id.RUSSIA )
|
||||
end
|
||||
|
||||
self:Mark()
|
||||
|
||||
end
|
||||
|
||||
function PROTECT:onenterCaptured()
|
||||
|
||||
local NewCoalition = self.ProtectZone:GetCoalition()
|
||||
self:E( { NewCoalition = NewCoalition } )
|
||||
self:SetCoalition( NewCoalition )
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
function PROTECT:onenterEmpty()
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
function PROTECT:onenterAttacked()
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
--- Check status Coalition ownership.
|
||||
-- @param #PROTECT self
|
||||
function PROTECT:StatusCoalition()
|
||||
|
||||
self:E( { State = self:GetState() } )
|
||||
|
||||
self.ProtectZone:Scan()
|
||||
|
||||
if self:IsGuarded() then
|
||||
self:Guard()
|
||||
else
|
||||
if self:IsCaptured() then
|
||||
self:Capture()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Check status Zone.
|
||||
-- @param #PROTECT self
|
||||
function PROTECT:StatusZone()
|
||||
|
||||
self:E( { State = self:GetState() } )
|
||||
|
||||
self.ProtectZone:Scan()
|
||||
|
||||
if self:IsAttacked() then
|
||||
self:Attack()
|
||||
else
|
||||
if self:IsEmpty() then
|
||||
self:Empty()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Check status Smoke.
|
||||
-- @param #PROTECT self
|
||||
function PROTECT:StatusSmoke()
|
||||
|
||||
local CurrentTime = timer.getTime()
|
||||
|
||||
if self.SmokeTime == nil or self.SmokeTime + 300 <= CurrentTime then
|
||||
if self.SmokeColor then
|
||||
self.ProtectZone:GetCoordinate():Smoke( self.SmokeColor )
|
||||
--self.SmokeColor = nil
|
||||
self.SmokeTime = CurrentTime
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
4272
Moose Development/Moose/Functional/RAT.lua
Normal file
4272
Moose Development/Moose/Functional/RAT.lua
Normal file
File diff suppressed because it is too large
Load Diff
@ -102,7 +102,9 @@
|
||||
-- A mission has goals and achievements. The scoring system provides an API to set additional scores when a goal or achievement event happens.
|
||||
-- Use the method @{#SCORING.AddGoalScore}() to add a score for a Player at any time in your mission.
|
||||
--
|
||||
-- ## 1.5) Configure fratricide level.
|
||||
-- ## 1.5) (Decommissioned) Configure fratricide level.
|
||||
--
|
||||
-- **This functionality is decomissioned until the DCS bug concerning Unit:destroy() not being functional in multi player for player units has been fixed by ED**.
|
||||
--
|
||||
-- When a player commits too much damage to friendlies, his penalty score will reach a certain level.
|
||||
-- Use the method @{#SCORING.SetFratricide}() to define the level when a player gets kicked.
|
||||
@ -258,7 +260,7 @@ function SCORING:New( GameName )
|
||||
|
||||
-- Configure Messages
|
||||
self:SetMessagesToAll()
|
||||
self:SetMessagesHit( true )
|
||||
self:SetMessagesHit( false )
|
||||
self:SetMessagesDestroy( true )
|
||||
self:SetMessagesScore( true )
|
||||
self:SetMessagesZone( true )
|
||||
@ -294,7 +296,7 @@ end
|
||||
-- @param #string DisplayMessagePrefix (Default="Scoring: ") The scoring prefix string.
|
||||
-- @return #SCORING
|
||||
function SCORING:SetDisplayMessagePrefix( DisplayMessagePrefix )
|
||||
self.DisplayMessagePrefix = DisplayMessagePrefix or "Scoring: "
|
||||
self.DisplayMessagePrefix = DisplayMessagePrefix or ""
|
||||
return self
|
||||
end
|
||||
|
||||
@ -608,14 +610,15 @@ function SCORING:_AddPlayerFromUnit( UnitData )
|
||||
if self.Players[PlayerName].UnitCoalition ~= UnitCoalition then
|
||||
self.Players[PlayerName].Penalty = self.Players[PlayerName].Penalty + 50
|
||||
self.Players[PlayerName].PenaltyCoalition = self.Players[PlayerName].PenaltyCoalition + 1
|
||||
MESSAGE:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' changed coalition from " .. _SCORINGCoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. _SCORINGCoalition[UnitCoalition] ..
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' changed coalition from " .. _SCORINGCoalition[self.Players[PlayerName].UnitCoalition] .. " to " .. _SCORINGCoalition[UnitCoalition] ..
|
||||
"(changed " .. self.Players[PlayerName].PenaltyCoalition .. " times the coalition). 50 Penalty points added.",
|
||||
2
|
||||
MESSAGE.Type.Information
|
||||
):ToAll()
|
||||
self:ScoreCSV( PlayerName, "", "COALITION_PENALTY", 1, -50, self.Players[PlayerName].UnitName, _SCORINGCoalition[self.Players[PlayerName].UnitCoalition], _SCORINGCategory[self.Players[PlayerName].UnitCategory], self.Players[PlayerName].UnitType,
|
||||
UnitName, _SCORINGCoalition[UnitCoalition], _SCORINGCategory[UnitCategory], UnitData:GetTypeName() )
|
||||
end
|
||||
end
|
||||
|
||||
self.Players[PlayerName].UnitName = UnitName
|
||||
self.Players[PlayerName].UnitCoalition = UnitCoalition
|
||||
self.Players[PlayerName].UnitCategory = UnitCategory
|
||||
@ -624,26 +627,59 @@ function SCORING:_AddPlayerFromUnit( UnitData )
|
||||
self.Players[PlayerName].ThreatLevel = UnitThreatLevel
|
||||
self.Players[PlayerName].ThreatType = UnitThreatType
|
||||
|
||||
-- TODO: DCS bug concerning Units with skill level client don't get destroyed in multi player. This logic is deactivated until this bug gets fixed.
|
||||
--[[
|
||||
if self.Players[PlayerName].Penalty > self.Fratricide * 0.50 then
|
||||
if self.Players[PlayerName].PenaltyWarning < 1 then
|
||||
MESSAGE:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than " .. self.Fratricide .. ", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty,
|
||||
30
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "': WARNING! If you continue to commit FRATRICIDE and have a PENALTY score higher than " .. self.Fratricide .. ", you will be COURT MARTIALED and DISMISSED from this mission! \nYour total penalty is: " .. self.Players[PlayerName].Penalty,
|
||||
MESSAGE.Type.Information
|
||||
):ToAll()
|
||||
self.Players[PlayerName].PenaltyWarning = self.Players[PlayerName].PenaltyWarning + 1
|
||||
end
|
||||
end
|
||||
|
||||
if self.Players[PlayerName].Penalty > self.Fratricide then
|
||||
--UnitData:Destroy()
|
||||
MESSAGE:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!",
|
||||
10
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' committed FRATRICIDE, he will be COURT MARTIALED and is DISMISSED from this mission!",
|
||||
MESSAGE.Type.Information
|
||||
):ToAll()
|
||||
UnitData:GetGroup():Destroy()
|
||||
end
|
||||
--]]
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Add a goal score for a player.
|
||||
-- The method takes the Player name for which the Goal score needs to be set.
|
||||
-- The GoalTag is a string or identifier that is taken into the CSV file scoring log to identify the goal.
|
||||
-- A free text can be given that is shown to the players.
|
||||
-- The Score can be both positive and negative.
|
||||
-- @param #SCORING self
|
||||
-- @param #string PlayerName The name of the Player.
|
||||
-- @param #string GoalTag The string or identifier that is used in the CSV file to identify the goal (sort or group later in Excel).
|
||||
-- @param #string Text A free text that is shown to the players.
|
||||
-- @param #number Score The score can be both positive or negative ( Penalty ).
|
||||
function SCORING:AddGoalScorePlayer( PlayerName, GoalTag, Text, Score )
|
||||
|
||||
self:E( { PlayerName, PlayerName, GoalTag, Text, Score } )
|
||||
|
||||
-- PlayerName can be nil, if the Unit with the player crashed or due to another reason.
|
||||
if PlayerName then
|
||||
local PlayerData = self.Players[PlayerName]
|
||||
|
||||
PlayerData.Goals[GoalTag] = PlayerData.Goals[GoalTag] or { Score = 0 }
|
||||
PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score
|
||||
PlayerData.Score = PlayerData.Score + Score
|
||||
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. Text, MESSAGE.Type.Information ):ToAll()
|
||||
|
||||
self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, nil )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Add a goal score for a player.
|
||||
-- The method takes the PlayerUnit for which the Goal score needs to be set.
|
||||
-- The GoalTag is a string or identifier that is taken into the CSV file scoring log to identify the goal.
|
||||
@ -668,7 +704,7 @@ function SCORING:AddGoalScore( PlayerUnit, GoalTag, Text, Score )
|
||||
PlayerData.Goals[GoalTag].Score = PlayerData.Goals[GoalTag].Score + Score
|
||||
PlayerData.Score = PlayerData.Score + Score
|
||||
|
||||
MESSAGE:New( self.DisplayMessagePrefix .. Text, 30 ):ToAll()
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. Text, MESSAGE.Type.Information ):ToAll()
|
||||
|
||||
self:ScoreCSV( PlayerName, "", "GOAL_" .. string.upper( GoalTag ), 1, Score, PlayerUnit:GetName() )
|
||||
end
|
||||
@ -704,12 +740,45 @@ function SCORING:_AddMissionTaskScore( Mission, PlayerUnit, Text, Score )
|
||||
PlayerData.Score = self.Players[PlayerName].Score + Score
|
||||
PlayerData.Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score
|
||||
|
||||
MESSAGE:New( self.DisplayMessagePrefix .. MissionName .. " : " .. Text .. " Score: " .. Score, 30 ):ToAll()
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. MissionName .. " : " .. Text .. " Score: " .. Score, MESSAGE.Type.Information ):ToAll()
|
||||
|
||||
self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score, PlayerUnit:GetName() )
|
||||
end
|
||||
end
|
||||
|
||||
--- Registers Scores the players completing a Mission Task.
|
||||
-- @param #SCORING self
|
||||
-- @param Tasking.Mission#MISSION Mission
|
||||
-- @param #string PlayerName
|
||||
-- @param #string Text
|
||||
-- @param #number Score
|
||||
function SCORING:_AddMissionGoalScore( Mission, PlayerName, Text, Score )
|
||||
|
||||
local MissionName = Mission:GetName()
|
||||
|
||||
self:E( { Mission:GetName(), PlayerName, Text, Score } )
|
||||
|
||||
-- PlayerName can be nil, if the Unit with the player crashed or due to another reason.
|
||||
if PlayerName then
|
||||
local PlayerData = self.Players[PlayerName]
|
||||
|
||||
if not PlayerData.Mission[MissionName] then
|
||||
PlayerData.Mission[MissionName] = {}
|
||||
PlayerData.Mission[MissionName].ScoreTask = 0
|
||||
PlayerData.Mission[MissionName].ScoreMission = 0
|
||||
end
|
||||
|
||||
self:T( PlayerName )
|
||||
self:T( PlayerData.Mission[MissionName] )
|
||||
|
||||
PlayerData.Score = self.Players[PlayerName].Score + Score
|
||||
PlayerData.Mission[MissionName].ScoreTask = self.Players[PlayerName].Mission[MissionName].ScoreTask + Score
|
||||
|
||||
MESSAGE:NewType( string.format( "%s%s: %s! Player %s receives %d score!", self.DisplayMessagePrefix, MissionName, Text, PlayerName, Score ), MESSAGE.Type.Information ):ToAll()
|
||||
|
||||
self:ScoreCSV( PlayerName, "", "TASK_" .. MissionName:gsub( ' ', '_' ), 1, Score )
|
||||
end
|
||||
end
|
||||
|
||||
--- Registers Mission Scores for possible multiple players that contributed in the Mission.
|
||||
-- @param #SCORING self
|
||||
@ -732,9 +801,9 @@ function SCORING:_AddMissionScore( Mission, Text, Score )
|
||||
PlayerData.Score = PlayerData.Score + Score
|
||||
PlayerData.Mission[MissionName].ScoreMission = PlayerData.Mission[MissionName].ScoreMission + Score
|
||||
|
||||
MESSAGE:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' has " .. Text .. " in Mission '" .. MissionName .. "'. " ..
|
||||
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' has " .. Text .. " in Mission '" .. MissionName .. "'. " ..
|
||||
Score .. " mission score!",
|
||||
60 ):ToAll()
|
||||
MESSAGE.Type.Information ):ToAll()
|
||||
|
||||
self:ScoreCSV( PlayerName, "", "MISSION_" .. MissionName:gsub( ' ', '_' ), 1, Score )
|
||||
end
|
||||
@ -902,19 +971,19 @@ function SCORING:_EventOnHit( Event )
|
||||
|
||||
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
|
||||
MESSAGE
|
||||
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " ..
|
||||
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly player '" .. TargetPlayerName .. "' " ..
|
||||
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
|
||||
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||
2
|
||||
MESSAGE.Type.Update
|
||||
)
|
||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||
else
|
||||
MESSAGE
|
||||
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit a friendly target " ..
|
||||
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit friendly target " ..
|
||||
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
|
||||
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||
2
|
||||
MESSAGE.Type.Update
|
||||
)
|
||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||
@ -926,19 +995,19 @@ function SCORING:_EventOnHit( Event )
|
||||
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
|
||||
if TargetPlayerName ~= nil then -- It is a player hitting another player ...
|
||||
MESSAGE
|
||||
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " ..
|
||||
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy player '" .. TargetPlayerName .. "' " ..
|
||||
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
|
||||
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||
2
|
||||
MESSAGE.Type.Update
|
||||
)
|
||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||
else
|
||||
MESSAGE
|
||||
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit an enemy target " ..
|
||||
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit enemy target " ..
|
||||
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
|
||||
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||
2
|
||||
MESSAGE.Type.Update
|
||||
)
|
||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||
@ -947,8 +1016,8 @@ function SCORING:_EventOnHit( Event )
|
||||
end
|
||||
else -- A scenery object was hit.
|
||||
MESSAGE
|
||||
:New( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit a scenery object.",
|
||||
2
|
||||
:NewType( self.DisplayMessagePrefix .. "Player '" .. InitPlayerName .. "' hit scenery object.",
|
||||
MESSAGE.Type.Update
|
||||
)
|
||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||
@ -1008,10 +1077,10 @@ function SCORING:_EventOnHit( Event )
|
||||
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit + 1
|
||||
|
||||
MESSAGE
|
||||
:New( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit a friendly target " ..
|
||||
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.PenaltyHit .. " times. " ..
|
||||
"Penalty: -" .. PlayerHit.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||
2
|
||||
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit friendly target " ..
|
||||
TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
|
||||
"Penalty: -" .. PlayerHit.Penalty .. " = " .. Player.Score - Player.Penalty,
|
||||
MESSAGE.Type.Update
|
||||
)
|
||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||
@ -1021,10 +1090,10 @@ function SCORING:_EventOnHit( Event )
|
||||
PlayerHit.Score = PlayerHit.Score + 1
|
||||
PlayerHit.ScoreHit = PlayerHit.ScoreHit + 1
|
||||
MESSAGE
|
||||
:New( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit an enemy target " ..
|
||||
TargetUnitCategory .. " ( " .. TargetType .. " ) " .. PlayerHit.ScoreHit .. " times. " ..
|
||||
"Score: " .. PlayerHit.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||
2
|
||||
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit enemy target " ..
|
||||
TargetUnitCategory .. " ( " .. TargetType .. " ) " ..
|
||||
"Score: +" .. PlayerHit.Score .. " = " .. Player.Score - Player.Penalty,
|
||||
MESSAGE.Type.Update
|
||||
)
|
||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( Event.WeaponCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||
@ -1032,8 +1101,8 @@ function SCORING:_EventOnHit( Event )
|
||||
end
|
||||
else -- A scenery object was hit.
|
||||
MESSAGE
|
||||
:New( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit a scenery object.",
|
||||
2
|
||||
:NewType( self.DisplayMessagePrefix .. "Player '" .. Event.WeaponPlayerName .. "' hit scenery object.",
|
||||
MESSAGE.Type.Update
|
||||
)
|
||||
:ToAllIf( self:IfMessagesHit() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesHit() and self:IfMessagesToCoalition() )
|
||||
@ -1131,19 +1200,19 @@ function SCORING:_EventOnDeadOrCrash( Event )
|
||||
|
||||
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
|
||||
MESSAGE
|
||||
:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " ..
|
||||
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.PenaltyDestroy .. " times. " ..
|
||||
"Penalty: -" .. TargetDestroy.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||
15
|
||||
:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " ..
|
||||
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty,
|
||||
MESSAGE.Type.Information
|
||||
)
|
||||
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
||||
else
|
||||
MESSAGE
|
||||
:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed a friendly target " ..
|
||||
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.PenaltyDestroy .. " times. " ..
|
||||
"Penalty: -" .. TargetDestroy.Penalty .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||
15
|
||||
:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " ..
|
||||
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||
"Penalty: -" .. TargetDestroy.Penalty .. " = " .. Player.Score - Player.Penalty,
|
||||
MESSAGE.Type.Information
|
||||
)
|
||||
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
||||
@ -1165,19 +1234,19 @@ function SCORING:_EventOnDeadOrCrash( Event )
|
||||
TargetDestroy.ScoreDestroy = TargetDestroy.ScoreDestroy + 1
|
||||
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
|
||||
MESSAGE
|
||||
:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " ..
|
||||
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.ScoreDestroy .. " times. " ..
|
||||
"Score: " .. TargetDestroy.Score .. ". Score Total:" .. Player.Score - Player.Penalty,
|
||||
15
|
||||
:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " ..
|
||||
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty,
|
||||
MESSAGE.Type.Information
|
||||
)
|
||||
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
||||
else
|
||||
MESSAGE
|
||||
:New( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed an enemy " ..
|
||||
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. TargetDestroy.ScoreDestroy .. " times. " ..
|
||||
"Score: " .. TargetDestroy.Score .. ". Total:" .. Player.Score - Player.Penalty,
|
||||
15
|
||||
:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " ..
|
||||
TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
|
||||
"Score: +" .. TargetDestroy.Score .. " = " .. Player.Score - Player.Penalty,
|
||||
MESSAGE.Type.Information
|
||||
)
|
||||
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
|
||||
@ -1191,9 +1260,9 @@ function SCORING:_EventOnDeadOrCrash( Event )
|
||||
Player.Score = Player.Score + Score
|
||||
TargetDestroy.Score = TargetDestroy.Score + Score
|
||||
MESSAGE
|
||||
:New( self.DisplayMessagePrefix .. "Special target '" .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. " destroyed! " ..
|
||||
:NewType( self.DisplayMessagePrefix .. "Special target '" .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " .. " destroyed! " ..
|
||||
"Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! Total: " .. Player.Score - Player.Penalty,
|
||||
15
|
||||
MESSAGE.Type.Information
|
||||
)
|
||||
:ToAllIf( self:IfMessagesScore() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesScore() and self:IfMessagesToCoalition() )
|
||||
@ -1210,10 +1279,10 @@ function SCORING:_EventOnDeadOrCrash( Event )
|
||||
Player.Score = Player.Score + Score
|
||||
TargetDestroy.Score = TargetDestroy.Score + Score
|
||||
MESSAGE
|
||||
:New( self.DisplayMessagePrefix .. "Target destroyed in zone '" .. ScoreZone:GetName() .. "'." ..
|
||||
:NewType( self.DisplayMessagePrefix .. "Target destroyed in zone '" .. ScoreZone:GetName() .. "'." ..
|
||||
"Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " ..
|
||||
"Total: " .. Player.Score - Player.Penalty,
|
||||
15 )
|
||||
MESSAGE.Type.Information )
|
||||
:ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() )
|
||||
self:ScoreCSV( PlayerName, TargetPlayerName, "DESTROY_SCORE", 1, Score, InitUnitName, InitUnitCoalition, InitUnitCategory, InitUnitType, TargetUnitName, TargetUnitCoalition, TargetUnitCategory, TargetUnitType )
|
||||
@ -1232,10 +1301,10 @@ function SCORING:_EventOnDeadOrCrash( Event )
|
||||
Player.Score = Player.Score + Score
|
||||
TargetDestroy.Score = TargetDestroy.Score + Score
|
||||
MESSAGE
|
||||
:New( self.DisplayMessagePrefix .. "Scenery destroyed in zone '" .. ScoreZone:GetName() .. "'." ..
|
||||
:NewType( self.DisplayMessagePrefix .. "Scenery destroyed in zone '" .. ScoreZone:GetName() .. "'." ..
|
||||
"Player '" .. PlayerName .. "' receives an extra " .. Score .. " points! " ..
|
||||
"Total: " .. Player.Score - Player.Penalty,
|
||||
15
|
||||
MESSAGE.Type.Information
|
||||
)
|
||||
:ToAllIf( self:IfMessagesZone() and self:IfMessagesToAll() )
|
||||
:ToCoalitionIf( InitCoalition, self:IfMessagesZone() and self:IfMessagesToCoalition() )
|
||||
@ -1522,7 +1591,7 @@ function SCORING:ReportScoreGroupSummary( PlayerGroup )
|
||||
PlayerScore,
|
||||
PlayerPenalty
|
||||
)
|
||||
MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup )
|
||||
MESSAGE:NewType( PlayerMessage, MESSAGE.Type.Detailed ):ToGroup( PlayerGroup )
|
||||
end
|
||||
end
|
||||
|
||||
@ -1579,7 +1648,7 @@ function SCORING:ReportScoreGroupDetailed( PlayerGroup )
|
||||
ReportGoals,
|
||||
ReportMissions
|
||||
)
|
||||
MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup )
|
||||
MESSAGE:NewType( PlayerMessage, MESSAGE.Type.Detailed ):ToGroup( PlayerGroup )
|
||||
end
|
||||
end
|
||||
|
||||
@ -1628,7 +1697,7 @@ function SCORING:ReportScoreAllSummary( PlayerGroup )
|
||||
PlayerScore,
|
||||
PlayerPenalty
|
||||
)
|
||||
MESSAGE:New( PlayerMessage, 30, "Player '" .. PlayerName .. "'" ):ToGroup( PlayerGroup )
|
||||
MESSAGE:NewType( PlayerMessage, MESSAGE.Type.Overview ):ToGroup( PlayerGroup )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
442
Moose Development/Moose/Functional/ZoneCaptureCoalition.lua
Normal file
442
Moose Development/Moose/Functional/ZoneCaptureCoalition.lua
Normal file
@ -0,0 +1,442 @@
|
||||
--- **Functional** -- (WIP R2.3) Models the process to capture a Zone for a Coalition, which is guarded by another Coalition.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- ### Contributions: **Millertime**: Concept
|
||||
-- ### Author: **Sven Van de Velde (FlightControl)**
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- @module ZoneCaptureCoalition
|
||||
|
||||
do -- ZONE_CAPTURE_COALITION
|
||||
|
||||
--- @type ZONE_CAPTURE_COALITION
|
||||
-- @extends Functional.ZoneGoalCoalition#ZONE_GOAL_COALITION
|
||||
|
||||
|
||||
--- # ZONE\_CAPTURE\_COALITION class, extends @{ZoneGoalCoalition#ZONE_GOAL_COALITION}
|
||||
--
|
||||
-- Models the process to capture a Zone for a Coalition, which is guarded by another Coalition.
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- 
|
||||
--
|
||||
-- ---
|
||||
--
|
||||
-- The Zone is initially **Guarded** by the __owning coalition__, which is the coalition that initially occupies the zone with units of its coalition.
|
||||
-- Once units of an other coalition are entering the Zone, the state will change to **Attacked**. As long as these units remain in the zone, the state keeps set to Attacked.
|
||||
-- When all units are destroyed in the Zone, the state will change to **Empty**, which expresses that the Zone is empty, and can be captured.
|
||||
-- When units of the other coalition are in the Zone, and no other units of the owning coalition is in the Zone, the Zone is captured, and its state will change to **Captured**.
|
||||
--
|
||||
-- Event handlers can be defined by the mission designer to action on the state transitions.
|
||||
--
|
||||
-- ## 1. ZONE\_CAPTURE\_COALITION constructor
|
||||
--
|
||||
-- * @{#ZONE_CAPTURE_COALITION.New}(): Creates a new ZONE\_CAPTURE\_COALITION object.
|
||||
--
|
||||
-- ## 2. ZONE\_CAPTURE\_COALITION is a finite state machine (FSM).
|
||||
--
|
||||
-- ### 2.1 ZONE\_CAPTURE\_COALITION States
|
||||
--
|
||||
-- * **Captured**: The Zone has been captured by an other coalition.
|
||||
-- * **Attacked**: The Zone is currently intruded by an other coalition. There are units of the owning coalition and an other coalition in the Zone.
|
||||
-- * **Guarded**: The Zone is guarded by the owning coalition. There is no other unit of an other coalition in the Zone.
|
||||
-- * **Empty**: The Zone is empty. There is not valid unit in the Zone.
|
||||
--
|
||||
-- ### 2.2 ZONE\_CAPTURE\_COALITION Events
|
||||
--
|
||||
-- * **Capture**: The Zone has been captured by an other coalition.
|
||||
-- * **Attack**: The Zone is currently intruded by an other coalition. There are units of the owning coalition and an other coalition in the Zone.
|
||||
-- * **Guard**: The Zone is guarded by the owning coalition. There is no other unit of an other coalition in the Zone.
|
||||
-- * **Empty**: The Zone is empty. There is not valid unit in the Zone.
|
||||
--
|
||||
-- @field #ZONE_CAPTURE_COALITION
|
||||
ZONE_CAPTURE_COALITION = {
|
||||
ClassName = "ZONE_CAPTURE_COALITION",
|
||||
}
|
||||
|
||||
--- @field #table ZONE_CAPTURE_COALITION.States
|
||||
ZONE_CAPTURE_COALITION.States = {}
|
||||
|
||||
--- ZONE_CAPTURE_COALITION Constructor.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved.
|
||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone.
|
||||
-- @return #ZONE_CAPTURE_COALITION
|
||||
-- @usage
|
||||
--
|
||||
-- AttackZone = ZONE:New( "AttackZone" )
|
||||
--
|
||||
-- ZoneCaptureCoalition = ZONE_CAPTURE_COALITION:New( AttackZone, coalition.side.RED ) -- Create a new ZONE_CAPTURE_COALITION object of zone AttackZone with ownership RED coalition.
|
||||
-- ZoneCaptureCoalition:__Guard( 1 ) -- Start the Guarding of the AttackZone.
|
||||
--
|
||||
function ZONE_CAPTURE_COALITION:New( Zone, Coalition )
|
||||
|
||||
local self = BASE:Inherit( self, ZONE_GOAL_COALITION:New( Zone, Coalition ) ) -- #ZONE_CAPTURE_COALITION
|
||||
|
||||
self:F( { Zone = Zone, Coalition = Coalition } )
|
||||
|
||||
do
|
||||
|
||||
--- Captured State Handler OnLeave for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnLeaveCaptured
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Captured State Handler OnEnter for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnEnterCaptured
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
|
||||
--- Attacked State Handler OnLeave for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnLeaveAttacked
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Attacked State Handler OnEnter for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnEnterAttacked
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
end
|
||||
|
||||
do
|
||||
|
||||
--- Guarded State Handler OnLeave for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnLeaveGuarded
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Guarded State Handler OnEnter for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnEnterGuarded
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
|
||||
--- Empty State Handler OnLeave for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnLeaveEmpty
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Empty State Handler OnEnter for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnEnterEmpty
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
end
|
||||
|
||||
self:AddTransition( "*", "Guard", "Guarded" )
|
||||
|
||||
--- Guard Handler OnBefore for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnBeforeGuard
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Guard Handler OnAfter for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnAfterGuard
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Guard Trigger for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] Guard
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
|
||||
--- Guard Asynchronous Trigger for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] __Guard
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #number Delay
|
||||
|
||||
self:AddTransition( "*", "Empty", "Empty" )
|
||||
|
||||
--- Empty Handler OnBefore for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnBeforeEmpty
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Empty Handler OnAfter for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnAfterEmpty
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Empty Trigger for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] Empty
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
|
||||
--- Empty Asynchronous Trigger for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] __Empty
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #number Delay
|
||||
|
||||
|
||||
self:AddTransition( { "Guarded", "Empty" }, "Attack", "Attacked" )
|
||||
|
||||
--- Attack Handler OnBefore for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnBeforeAttack
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Attack Handler OnAfter for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnAfterAttack
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Attack Trigger for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] Attack
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
|
||||
--- Attack Asynchronous Trigger for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] __Attack
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #number Delay
|
||||
|
||||
self:AddTransition( { "Guarded", "Attacked", "Empty" }, "Capture", "Captured" )
|
||||
|
||||
--- Capture Handler OnBefore for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnBeforeCapture
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Capture Handler OnAfter for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] OnAfterCapture
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Capture Trigger for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] Capture
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
|
||||
--- Capture Asynchronous Trigger for ZONE\_CAPTURE\_COALITION
|
||||
-- @function [parent=#ZONE_CAPTURE_COALITION] __Capture
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
-- @param #number Delay
|
||||
|
||||
if not self.ScheduleStatusZone then
|
||||
self.ScheduleStatusZone = self:ScheduleRepeat( 15, 15, 0.1, nil, self.StatusZone, self )
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onenterCaptured()
|
||||
|
||||
self:GetParent( self, ZONE_CAPTURE_COALITION ).onenterCaptured( self )
|
||||
|
||||
self.Goal:Achieved()
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsGuarded()
|
||||
|
||||
local IsGuarded = self.Zone:IsAllInZoneOfCoalition( self.Coalition )
|
||||
self:E( { IsGuarded = IsGuarded } )
|
||||
return IsGuarded
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsEmpty()
|
||||
|
||||
local IsEmpty = self.Zone:IsNoneInZone()
|
||||
self:E( { IsEmpty = IsEmpty } )
|
||||
return IsEmpty
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsCaptured()
|
||||
|
||||
local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
self:E( { IsCaptured = IsCaptured } )
|
||||
return IsCaptured
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsAttacked()
|
||||
|
||||
local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
self:E( { IsAttacked = IsAttacked } )
|
||||
return IsAttacked
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Mark.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:Mark()
|
||||
|
||||
local Coord = self.Zone:GetCoordinate()
|
||||
local ZoneName = self:GetZoneName()
|
||||
local State = self:GetState()
|
||||
|
||||
if self.MarkRed and self.MarkBlue then
|
||||
self:E( { MarkRed = self.MarkRed, MarkBlue = self.MarkBlue } )
|
||||
Coord:RemoveMark( self.MarkRed )
|
||||
Coord:RemoveMark( self.MarkBlue )
|
||||
end
|
||||
|
||||
if self.Coalition == coalition.side.BLUE then
|
||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Blue\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Blue\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
else
|
||||
self.MarkRed = Coord:MarkToCoalitionRed( "Coalition: Red\nGuard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Coalition: Red\nCapture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
end
|
||||
end
|
||||
|
||||
--- Bound.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onenterGuarded()
|
||||
|
||||
--self:GetParent( self ):onenterGuarded()
|
||||
|
||||
if self.Coalition == coalition.side.BLUE then
|
||||
--elf.ProtectZone:BoundZone( 12, country.id.USA )
|
||||
else
|
||||
--self.ProtectZone:BoundZone( 12, country.id.RUSSIA )
|
||||
end
|
||||
|
||||
self:Mark()
|
||||
|
||||
end
|
||||
|
||||
function ZONE_CAPTURE_COALITION:onenterCaptured()
|
||||
|
||||
--self:GetParent( self ):onenterCaptured()
|
||||
|
||||
local NewCoalition = self.Zone:GetScannedCoalition()
|
||||
self:E( { NewCoalition = NewCoalition } )
|
||||
self:SetCoalition( NewCoalition )
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:onenterEmpty()
|
||||
|
||||
--self:GetParent( self ):onenterEmpty()
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:onenterAttacked()
|
||||
|
||||
--self:GetParent( self ):onenterAttacked()
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
--- When started, check the Coalition status.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:onafterGuard()
|
||||
|
||||
--self:E({BASE:GetParent( self )})
|
||||
--BASE:GetParent( self ).onafterGuard( self )
|
||||
|
||||
if not self.SmokeScheduler then
|
||||
self.SmokeScheduler = self:ScheduleRepeat( 1, 1, 0.1, nil, self.StatusSmoke, self )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsCaptured()
|
||||
|
||||
local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
self:E( { IsCaptured = IsCaptured } )
|
||||
return IsCaptured
|
||||
end
|
||||
|
||||
|
||||
function ZONE_CAPTURE_COALITION:IsAttacked()
|
||||
|
||||
local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
self:E( { IsAttacked = IsAttacked } )
|
||||
return IsAttacked
|
||||
end
|
||||
|
||||
|
||||
--- Check status Coalition ownership.
|
||||
-- @param #ZONE_CAPTURE_COALITION self
|
||||
function ZONE_CAPTURE_COALITION:StatusZone()
|
||||
|
||||
local State = self:GetState()
|
||||
self:E( { State = self:GetState() } )
|
||||
|
||||
self:GetParent( self, ZONE_CAPTURE_COALITION ).StatusZone( self )
|
||||
|
||||
if State ~= "Guarded" and self:IsGuarded() then
|
||||
self:Guard()
|
||||
end
|
||||
|
||||
if State ~= "Empty" and self:IsEmpty() then
|
||||
self:Empty()
|
||||
end
|
||||
|
||||
if State ~= "Attacked" and self:IsAttacked() then
|
||||
self:Attack()
|
||||
end
|
||||
|
||||
if State ~= "Captured" and self:IsCaptured() then
|
||||
self:Capture()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
177
Moose Development/Moose/Functional/ZoneGoal.lua
Normal file
177
Moose Development/Moose/Functional/ZoneGoal.lua
Normal file
@ -0,0 +1,177 @@
|
||||
--- **Functional (WIP)** -- Base class that models processes to achieve goals involving a Zone.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- ZONE_GOAL models processes that have a Goal with a defined achievement involving a Zone.
|
||||
-- Derived classes implement the ways how the achievements can be realized.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- ### Author: **Sven Van de Velde (FlightControl)**
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- @module ZoneGoal
|
||||
|
||||
do -- Zone
|
||||
|
||||
--- @type ZONE_GOAL
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
|
||||
--- # ZONE_GOAL class, extends @{Fsm#FSM}
|
||||
--
|
||||
-- ZONE_GOAL models processes that have a Goal with a defined achievement involving a Zone.
|
||||
-- Derived classes implement the ways how the achievements can be realized.
|
||||
--
|
||||
-- ## 1. ZONE_GOAL constructor
|
||||
--
|
||||
-- * @{#ZONE_GOAL.New}(): Creates a new ZONE_GOAL object.
|
||||
--
|
||||
-- ## 2. ZONE_GOAL is a finite state machine (FSM).
|
||||
--
|
||||
-- ### 2.1 ZONE_GOAL States
|
||||
--
|
||||
-- * None: Initial State
|
||||
--
|
||||
-- ### 2.2 ZONE_GOAL Events
|
||||
--
|
||||
-- * DestroyedUnit: A @{Unit} is destroyed in the Zone. The event will only get triggered if the method @{#ZONE_GOAL.MonitorDestroyedUnits}() is used.
|
||||
--
|
||||
-- @field #ZONE_GOAL
|
||||
ZONE_GOAL = {
|
||||
ClassName = "ZONE_GOAL",
|
||||
}
|
||||
|
||||
--- ZONE_GOAL Constructor.
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @param Core.Zone#ZONE_BASE Zone A @{Zone} object with the goal to be achieved.
|
||||
-- @return #ZONE_GOAL
|
||||
function ZONE_GOAL:New( Zone )
|
||||
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #ZONE_GOAL
|
||||
self:F( { Zone = Zone } )
|
||||
|
||||
self.Zone = Zone -- Core.Zone#ZONE_BASE
|
||||
self.Goal = GOAL:New()
|
||||
|
||||
self.SmokeTime = nil
|
||||
|
||||
self:AddTransition( "*", "DestroyedUnit", "*" )
|
||||
|
||||
--- DestroyedUnit Handler OnAfter for ZONE_GOAL
|
||||
-- @function [parent=#ZONE_GOAL] OnAfterDestroyedUnit
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Unit#UNIT DestroyedUnit The destroyed unit.
|
||||
-- @param #string PlayerName The name of the player.
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Get the Zone
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @return Core.Zone#ZONE_BASE
|
||||
function ZONE_GOAL:GetZone()
|
||||
return self.Zone
|
||||
end
|
||||
|
||||
|
||||
--- Get the name of the ProtectZone
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @return #string
|
||||
function ZONE_GOAL:GetZoneName()
|
||||
return self.Zone:GetName()
|
||||
end
|
||||
|
||||
|
||||
--- Smoke the center of theh zone.
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @param #SMOKECOLOR.Color SmokeColor
|
||||
function ZONE_GOAL:Smoke( SmokeColor )
|
||||
|
||||
self:F( { SmokeColor = SmokeColor} )
|
||||
|
||||
self.SmokeColor = SmokeColor
|
||||
end
|
||||
|
||||
|
||||
--- Flare the center of the zone.
|
||||
-- @param #ZONE_GOAL self
|
||||
-- @param #SMOKECOLOR.Color FlareColor
|
||||
function ZONE_GOAL:Flare( FlareColor )
|
||||
self.Zone:FlareZone( FlareColor, math.random( 1, 360 ) )
|
||||
end
|
||||
|
||||
|
||||
--- When started, check the Smoke and the Zone status.
|
||||
-- @param #ZONE_GOAL self
|
||||
function ZONE_GOAL:onafterGuard()
|
||||
|
||||
--self:GetParent( self ):onafterStart()
|
||||
|
||||
self:E("Guard")
|
||||
|
||||
--self:ScheduleRepeat( 15, 15, 0.1, nil, self.StatusZone, self )
|
||||
if not self.SmokeScheduler then
|
||||
self.SmokeScheduler = self:ScheduleRepeat( 1, 1, 0.1, nil, self.StatusSmoke, self )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Check status Smoke.
|
||||
-- @param #ZONE_GOAL self
|
||||
function ZONE_GOAL:StatusSmoke()
|
||||
|
||||
self:F({self.SmokeTime, self.SmokeColor})
|
||||
|
||||
local CurrentTime = timer.getTime()
|
||||
|
||||
if self.SmokeTime == nil or self.SmokeTime + 300 <= CurrentTime then
|
||||
if self.SmokeColor then
|
||||
self.Zone:GetCoordinate():Smoke( self.SmokeColor )
|
||||
--self.SmokeColor = nil
|
||||
self.SmokeTime = CurrentTime
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- @param #ZONE_GOAL self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
function ZONE_GOAL:__Destroyed( EventData )
|
||||
self:E( { "EventDead", EventData } )
|
||||
|
||||
self:E( { EventData.IniUnit } )
|
||||
|
||||
local Vec3 = EventData.IniDCSUnit:getPosition().p
|
||||
self:E( { Vec3 = Vec3 } )
|
||||
local ZoneGoal = self:GetZone()
|
||||
self:E({ZoneGoal})
|
||||
|
||||
if EventData.IniDCSUnit then
|
||||
if ZoneGoal:IsVec3InZone(Vec3) then
|
||||
local PlayerHits = _DATABASE.HITS[EventData.IniUnitName]
|
||||
if PlayerHits then
|
||||
for PlayerName, PlayerHit in pairs( PlayerHits.Players or {} ) do
|
||||
self.Goal:AddPlayerContribution( PlayerName )
|
||||
self:DestroyedUnit( EventData.IniUnitName, PlayerName )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Activate the event UnitDestroyed to be fired when a unit is destroyed in the zone.
|
||||
-- @param #ZONE_GOAL self
|
||||
function ZONE_GOAL:MonitorDestroyedUnits()
|
||||
|
||||
self:HandleEvent( EVENTS.Dead, self.__Destroyed )
|
||||
self:HandleEvent( EVENTS.Crash, self.__Destroyed )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
452
Moose Development/Moose/Functional/ZoneGoalCargo.lua
Normal file
452
Moose Development/Moose/Functional/ZoneGoalCargo.lua
Normal file
@ -0,0 +1,452 @@
|
||||
--- **Functional (WIP)** -- Base class that models processes to achieve goals involving a Zone and Cargo.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- ZONE_GOAL_CARGO models processes that have a Goal with a defined achievement involving a Zone and Cargo.
|
||||
-- Derived classes implement the ways how the achievements can be realized.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- ### Author: **Sven Van de Velde (FlightControl)**
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- @module ZoneGoalCargo
|
||||
|
||||
do -- ZoneGoal
|
||||
|
||||
--- @type ZONE_GOAL_CARGO
|
||||
-- @extends Functional.ZoneGoal#ZONE_GOAL
|
||||
|
||||
|
||||
--- # ZONE_GOAL_CARGO class, extends @{ZoneGoal#ZONE_GOAL}
|
||||
--
|
||||
-- ZONE_GOAL_CARGO models processes that have a Goal with a defined achievement involving a Zone and Cargo.
|
||||
-- Derived classes implement the ways how the achievements can be realized.
|
||||
--
|
||||
-- ## 1. ZONE_GOAL_CARGO constructor
|
||||
--
|
||||
-- * @{#ZONE_GOAL_CARGO.New}(): Creates a new ZONE_GOAL_CARGO object.
|
||||
--
|
||||
-- ## 2. ZONE_GOAL_CARGO is a finite state machine (FSM).
|
||||
--
|
||||
-- ### 2.1 ZONE_GOAL_CARGO States
|
||||
--
|
||||
-- * **Deployed**: The Zone has been captured by an other coalition.
|
||||
-- * **Airborne**: The Zone is currently intruded by an other coalition. There are units of the owning coalition and an other coalition in the Zone.
|
||||
-- * **Loaded**: The Zone is guarded by the owning coalition. There is no other unit of an other coalition in the Zone.
|
||||
-- * **Empty**: The Zone is empty. There is not valid unit in the Zone.
|
||||
--
|
||||
-- ### 2.2 ZONE_GOAL_CARGO Events
|
||||
--
|
||||
-- * **Capture**: The Zone has been captured by an other coalition.
|
||||
-- * **Attack**: The Zone is currently intruded by an other coalition. There are units of the owning coalition and an other coalition in the Zone.
|
||||
-- * **Guard**: The Zone is guarded by the owning coalition. There is no other unit of an other coalition in the Zone.
|
||||
-- * **Empty**: The Zone is empty. There is not valid unit in the Zone.
|
||||
--
|
||||
-- ### 2.3 ZONE_GOAL_CARGO State Machine
|
||||
--
|
||||
-- @field #ZONE_GOAL_CARGO
|
||||
ZONE_GOAL_CARGO = {
|
||||
ClassName = "ZONE_GOAL_CARGO",
|
||||
}
|
||||
|
||||
--- @field #table ZONE_GOAL_CARGO.States
|
||||
ZONE_GOAL_CARGO.States = {}
|
||||
|
||||
--- ZONE_GOAL_CARGO Constructor.
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved.
|
||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone.
|
||||
-- @return #ZONE_GOAL_CARGO
|
||||
function ZONE_GOAL_CARGO:New( Zone, Coalition )
|
||||
|
||||
local self = BASE:Inherit( self, ZONE_GOAL:New( Zone ) ) -- #ZONE_GOAL_CARGO
|
||||
self:F( { Zone = Zone, Coalition = Coalition } )
|
||||
|
||||
self:SetCoalition( Coalition )
|
||||
|
||||
do
|
||||
|
||||
--- Captured State Handler OnLeave for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnLeaveCaptured
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Captured State Handler OnEnter for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnEnterCaptured
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
|
||||
--- Attacked State Handler OnLeave for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnLeaveAttacked
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Attacked State Handler OnEnter for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnEnterAttacked
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
end
|
||||
|
||||
do
|
||||
|
||||
--- Guarded State Handler OnLeave for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnLeaveGuarded
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Guarded State Handler OnEnter for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnEnterGuarded
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
end
|
||||
|
||||
|
||||
do
|
||||
|
||||
--- Empty State Handler OnLeave for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnLeaveEmpty
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Empty State Handler OnEnter for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnEnterEmpty
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
end
|
||||
|
||||
self:AddTransition( "*", "Guard", "Guarded" )
|
||||
|
||||
--- Guard Handler OnBefore for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnBeforeGuard
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Guard Handler OnAfter for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnAfterGuard
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Guard Trigger for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] Guard
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
|
||||
--- Guard Asynchronous Trigger for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] __Guard
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #number Delay
|
||||
|
||||
self:AddTransition( "*", "Empty", "Empty" )
|
||||
|
||||
--- Empty Handler OnBefore for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnBeforeEmpty
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Empty Handler OnAfter for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnAfterEmpty
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Empty Trigger for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] Empty
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
|
||||
--- Empty Asynchronous Trigger for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] __Empty
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #number Delay
|
||||
|
||||
|
||||
self:AddTransition( { "Guarded", "Empty" }, "Attack", "Attacked" )
|
||||
|
||||
--- Attack Handler OnBefore for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnBeforeAttack
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Attack Handler OnAfter for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnAfterAttack
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Attack Trigger for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] Attack
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
|
||||
--- Attack Asynchronous Trigger for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] __Attack
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #number Delay
|
||||
|
||||
self:AddTransition( { "Guarded", "Attacked", "Empty" }, "Capture", "Captured" )
|
||||
|
||||
--- Capture Handler OnBefore for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnBeforeCapture
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @return #boolean
|
||||
|
||||
--- Capture Handler OnAfter for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] OnAfterCapture
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
|
||||
--- Capture Trigger for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] Capture
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
|
||||
--- Capture Asynchronous Trigger for ZONE_GOAL_CARGO
|
||||
-- @function [parent=#ZONE_GOAL_CARGO] __Capture
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param #number Delay
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition
|
||||
function ZONE_GOAL_CARGO:SetCoalition( Coalition )
|
||||
self.Coalition = Coalition
|
||||
end
|
||||
|
||||
|
||||
--- Get the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @return DCSCoalition.DCSCoalition#coalition Coalition.
|
||||
function ZONE_GOAL_CARGO:GetCoalition()
|
||||
return self.Coalition
|
||||
end
|
||||
|
||||
|
||||
--- Get the owning coalition name of the zone.
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
-- @return #string Coalition name.
|
||||
function ZONE_GOAL_CARGO:GetCoalitionName()
|
||||
|
||||
if self.Coalition == coalition.side.BLUE then
|
||||
return "Blue"
|
||||
end
|
||||
|
||||
if self.Coalition == coalition.side.RED then
|
||||
return "Red"
|
||||
end
|
||||
|
||||
if self.Coalition == coalition.side.NEUTRAL then
|
||||
return "Neutral"
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
|
||||
function ZONE_GOAL_CARGO:IsGuarded()
|
||||
|
||||
local IsGuarded = self.Zone:IsAllInZoneOfCoalition( self.Coalition )
|
||||
self:E( { IsGuarded = IsGuarded } )
|
||||
return IsGuarded
|
||||
end
|
||||
|
||||
|
||||
function ZONE_GOAL_CARGO:IsEmpty()
|
||||
|
||||
local IsEmpty = self.Zone:IsNoneInZone()
|
||||
self:E( { IsEmpty = IsEmpty } )
|
||||
return IsEmpty
|
||||
end
|
||||
|
||||
|
||||
function ZONE_GOAL_CARGO:IsCaptured()
|
||||
|
||||
local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
self:E( { IsCaptured = IsCaptured } )
|
||||
return IsCaptured
|
||||
end
|
||||
|
||||
|
||||
function ZONE_GOAL_CARGO:IsAttacked()
|
||||
|
||||
local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
self:E( { IsAttacked = IsAttacked } )
|
||||
return IsAttacked
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Mark.
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
function ZONE_GOAL_CARGO:Mark()
|
||||
|
||||
local Coord = self.Zone:GetCoordinate()
|
||||
local ZoneName = self:GetZoneName()
|
||||
local State = self:GetState()
|
||||
|
||||
if self.MarkRed and self.MarkBlue then
|
||||
self:E( { MarkRed = self.MarkRed, MarkBlue = self.MarkBlue } )
|
||||
Coord:RemoveMark( self.MarkRed )
|
||||
Coord:RemoveMark( self.MarkBlue )
|
||||
end
|
||||
|
||||
if self.Coalition == coalition.side.BLUE then
|
||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Guard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
self.MarkRed = Coord:MarkToCoalitionRed( "Capture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
else
|
||||
self.MarkRed = Coord:MarkToCoalitionRed( "Guard Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
self.MarkBlue = Coord:MarkToCoalitionBlue( "Capture Zone: " .. ZoneName .. "\nStatus: " .. State )
|
||||
end
|
||||
end
|
||||
|
||||
--- Bound.
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
function ZONE_GOAL_CARGO:onenterGuarded()
|
||||
|
||||
--self:GetParent( self ):onenterGuarded()
|
||||
|
||||
if self.Coalition == coalition.side.BLUE then
|
||||
--elf.ProtectZone:BoundZone( 12, country.id.USA )
|
||||
else
|
||||
--self.ProtectZone:BoundZone( 12, country.id.RUSSIA )
|
||||
end
|
||||
|
||||
self:Mark()
|
||||
|
||||
end
|
||||
|
||||
function ZONE_GOAL_CARGO:onenterCaptured()
|
||||
|
||||
--self:GetParent( self ):onenterCaptured()
|
||||
|
||||
local NewCoalition = self.Zone:GetCoalition()
|
||||
self:E( { NewCoalition = NewCoalition } )
|
||||
self:SetCoalition( NewCoalition )
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
function ZONE_GOAL_CARGO:onenterEmpty()
|
||||
|
||||
--self:GetParent( self ):onenterEmpty()
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
function ZONE_GOAL_CARGO:onenterAttacked()
|
||||
|
||||
--self:GetParent( self ):onenterAttacked()
|
||||
|
||||
self:Mark()
|
||||
end
|
||||
|
||||
|
||||
--- When started, check the Coalition status.
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
function ZONE_GOAL_CARGO:onafterGuard()
|
||||
|
||||
--self:E({BASE:GetParent( self )})
|
||||
--BASE:GetParent( self ).onafterGuard( self )
|
||||
|
||||
if not self.SmokeScheduler then
|
||||
self.SmokeScheduler = self:ScheduleRepeat( 1, 1, 0.1, nil, self.StatusSmoke, self )
|
||||
end
|
||||
if not self.ScheduleStatusZone then
|
||||
self.ScheduleStatusZone = self:ScheduleRepeat( 15, 15, 0.1, nil, self.StatusZone, self )
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function ZONE_GOAL_CARGO:IsCaptured()
|
||||
|
||||
local IsCaptured = self.Zone:IsAllInZoneOfOtherCoalition( self.Coalition )
|
||||
self:E( { IsCaptured = IsCaptured } )
|
||||
return IsCaptured
|
||||
end
|
||||
|
||||
|
||||
function ZONE_GOAL_CARGO:IsAttacked()
|
||||
|
||||
local IsAttacked = self.Zone:IsSomeInZoneOfCoalition( self.Coalition )
|
||||
self:E( { IsAttacked = IsAttacked } )
|
||||
return IsAttacked
|
||||
end
|
||||
|
||||
--- Check status Coalition ownership.
|
||||
-- @param #ZONE_GOAL_CARGO self
|
||||
function ZONE_GOAL_CARGO:StatusZone()
|
||||
|
||||
local State = self:GetState()
|
||||
self:E( { State = self:GetState() } )
|
||||
|
||||
self.Zone:Scan()
|
||||
|
||||
if State ~= "Guarded" and self:IsGuarded() then
|
||||
self:Guard()
|
||||
end
|
||||
|
||||
if State ~= "Empty" and self:IsEmpty() then
|
||||
self:Empty()
|
||||
end
|
||||
|
||||
if State ~= "Attacked" and self:IsAttacked() then
|
||||
self:Attack()
|
||||
end
|
||||
|
||||
if State ~= "Captured" and self:IsCaptured() then
|
||||
self:Capture()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
113
Moose Development/Moose/Functional/ZoneGoalCoalition.lua
Normal file
113
Moose Development/Moose/Functional/ZoneGoalCoalition.lua
Normal file
@ -0,0 +1,113 @@
|
||||
--- **Functional (WIP)** -- Base class that models processes to achieve goals involving a Zone for a Coalition.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- ZONE_GOAL_COALITION models processes that have a Goal with a defined achievement involving a Zone for a Coalition.
|
||||
-- Derived classes implement the ways how the achievements can be realized.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- ### Author: **Sven Van de Velde (FlightControl)**
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- @module ZoneGoalCoalition
|
||||
|
||||
do -- ZoneGoal
|
||||
|
||||
--- @type ZONE_GOAL_COALITION
|
||||
-- @extends Functional.ZoneGoal#ZONE_GOAL
|
||||
|
||||
|
||||
--- # ZONE_GOAL_COALITION class, extends @{ZoneGoal#ZONE_GOAL}
|
||||
--
|
||||
-- ZONE_GOAL_COALITION models processes that have a Goal with a defined achievement involving a Zone for a Coalition.
|
||||
-- Derived classes implement the ways how the achievements can be realized.
|
||||
--
|
||||
-- ## 1. ZONE_GOAL_COALITION constructor
|
||||
--
|
||||
-- * @{#ZONE_GOAL_COALITION.New}(): Creates a new ZONE_GOAL_COALITION object.
|
||||
--
|
||||
-- ## 2. ZONE_GOAL_COALITION is a finite state machine (FSM).
|
||||
--
|
||||
-- ### 2.1 ZONE_GOAL_COALITION States
|
||||
--
|
||||
-- ### 2.2 ZONE_GOAL_COALITION Events
|
||||
--
|
||||
-- ### 2.3 ZONE_GOAL_COALITION State Machine
|
||||
--
|
||||
-- @field #ZONE_GOAL_COALITION
|
||||
ZONE_GOAL_COALITION = {
|
||||
ClassName = "ZONE_GOAL_COALITION",
|
||||
}
|
||||
|
||||
--- @field #table ZONE_GOAL_COALITION.States
|
||||
ZONE_GOAL_COALITION.States = {}
|
||||
|
||||
--- ZONE_GOAL_COALITION Constructor.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @param Core.Zone#ZONE Zone A @{Zone} object with the goal to be achieved.
|
||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition The initial coalition owning the zone.
|
||||
-- @return #ZONE_GOAL_COALITION
|
||||
function ZONE_GOAL_COALITION:New( Zone, Coalition )
|
||||
|
||||
local self = BASE:Inherit( self, ZONE_GOAL:New( Zone ) ) -- #ZONE_GOAL_COALITION
|
||||
self:F( { Zone = Zone, Coalition = Coalition } )
|
||||
|
||||
self:SetCoalition( Coalition )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Set the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @param DCSCoalition.DCSCoalition#coalition Coalition
|
||||
function ZONE_GOAL_COALITION:SetCoalition( Coalition )
|
||||
self.Coalition = Coalition
|
||||
end
|
||||
|
||||
|
||||
--- Get the owning coalition of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @return DCSCoalition.DCSCoalition#coalition Coalition.
|
||||
function ZONE_GOAL_COALITION:GetCoalition()
|
||||
return self.Coalition
|
||||
end
|
||||
|
||||
|
||||
--- Get the owning coalition name of the zone.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
-- @return #string Coalition name.
|
||||
function ZONE_GOAL_COALITION:GetCoalitionName()
|
||||
|
||||
if self.Coalition == coalition.side.BLUE then
|
||||
return "Blue"
|
||||
end
|
||||
|
||||
if self.Coalition == coalition.side.RED then
|
||||
return "Red"
|
||||
end
|
||||
|
||||
if self.Coalition == coalition.side.NEUTRAL then
|
||||
return "Neutral"
|
||||
end
|
||||
|
||||
return ""
|
||||
end
|
||||
|
||||
|
||||
--- Check status Coalition ownership.
|
||||
-- @param #ZONE_GOAL_COALITION self
|
||||
function ZONE_GOAL_COALITION:StatusZone()
|
||||
|
||||
local State = self:GetState()
|
||||
self:E( { State = self:GetState() } )
|
||||
|
||||
self.Zone:Scan( { Object.Category.UNIT, Object.Category.STATIC } )
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -13,80 +13,7 @@
|
||||
|
||||
|
||||
|
||||
--- The REPORT class
|
||||
-- @type REPORT
|
||||
-- @extends Core.Base#BASE
|
||||
REPORT = {
|
||||
ClassName = "REPORT",
|
||||
Title = "",
|
||||
}
|
||||
|
||||
--- Create a new REPORT.
|
||||
-- @param #REPORT self
|
||||
-- @param #string Title
|
||||
-- @return #REPORT
|
||||
function REPORT:New( Title )
|
||||
|
||||
local self = BASE:Inherit( self, BASE:New() ) -- #REPORT
|
||||
|
||||
self.Report = {}
|
||||
|
||||
Title = Title or ""
|
||||
if Title then
|
||||
self.Title = Title
|
||||
end
|
||||
|
||||
self:SetIndent( 3 )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Has the REPORT Text?
|
||||
-- @param #REPORT self
|
||||
-- @return #boolean
|
||||
function REPORT:HasText() --R2.1
|
||||
|
||||
return #self.Report > 0
|
||||
end
|
||||
|
||||
|
||||
--- Set indent of a REPORT.
|
||||
-- @param #REPORT self
|
||||
-- @param #number Indent
|
||||
-- @return #REPORT
|
||||
function REPORT:SetIndent( Indent ) --R2.1
|
||||
self.Indent = Indent
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Add a new line to a REPORT.
|
||||
-- @param #REPORT self
|
||||
-- @param #string Text
|
||||
-- @return #REPORT
|
||||
function REPORT:Add( Text )
|
||||
self.Report[#self.Report+1] = Text
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a new line to a REPORT.
|
||||
-- @param #REPORT self
|
||||
-- @param #string Text
|
||||
-- @return #REPORT
|
||||
function REPORT:AddIndent( Text ) --R2.1
|
||||
self.Report[#self.Report+1] = string.rep(" ", self.Indent ) .. Text:gsub("\n","\n"..string.rep( " ", self.Indent ) )
|
||||
return self
|
||||
end
|
||||
|
||||
--- Produces the text of the report, taking into account an optional delimeter, which is \n by default.
|
||||
-- @param #REPORT self
|
||||
-- @param #string Delimiter (optional) A delimiter text.
|
||||
-- @return #string The report text.
|
||||
function REPORT:Text( Delimiter )
|
||||
Delimiter = Delimiter or "\n"
|
||||
local ReportText = ( self.Title ~= "" and self.Title .. Delimiter or self.Title ) .. table.concat( self.Report, Delimiter ) or ""
|
||||
return ReportText
|
||||
end
|
||||
|
||||
--- The COMMANDCENTER class
|
||||
-- @type COMMANDCENTER
|
||||
@ -207,6 +134,7 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
|
||||
local PlayerGroup = EventData.IniGroup -- The GROUP object should be filled!
|
||||
Mission:JoinUnit( PlayerUnit, PlayerGroup )
|
||||
end
|
||||
self:SetMenu()
|
||||
end
|
||||
)
|
||||
|
||||
@ -260,6 +188,8 @@ function COMMANDCENTER:New( CommandCenterPositionable, CommandCenterName )
|
||||
)
|
||||
|
||||
self:SetMenu()
|
||||
|
||||
_SETTINGS:SetSystemMenu( CommandCenterPositionable )
|
||||
|
||||
return self
|
||||
end
|
||||
@ -428,10 +358,20 @@ end
|
||||
-- @param #COMMANDCENTER self
|
||||
-- @param #string Message
|
||||
-- @param Wrapper.Group#GROUP TaskGroup
|
||||
-- @param #sring Name (optional) The name of the Group used as a prefix for the message to the Group. If not provided, there will be nothing shown.
|
||||
function COMMANDCENTER:MessageToGroup( Message, TaskGroup )
|
||||
|
||||
self:GetPositionable():MessageToGroup( Message , 15, TaskGroup, self:GetName() )
|
||||
self:GetPositionable():MessageToGroup( Message, 15, TaskGroup, self:GetName() )
|
||||
|
||||
end
|
||||
|
||||
--- Send a CC message of a specified type to a GROUP.
|
||||
-- @param #COMMANDCENTER self
|
||||
-- @param #string Message
|
||||
-- @param Wrapper.Group#GROUP TaskGroup
|
||||
-- @param Core.Message#MESSAGE.MessageType MessageType The type of the message, resulting in automatic time duration and prefix of the message.
|
||||
function COMMANDCENTER:MessageTypeToGroup( Message, TaskGroup, MessageType )
|
||||
|
||||
self:GetPositionable():MessageTypeToGroup( Message, MessageType, TaskGroup, self:GetName() )
|
||||
|
||||
end
|
||||
|
||||
@ -447,6 +387,20 @@ function COMMANDCENTER:MessageToCoalition( Message )
|
||||
end
|
||||
|
||||
|
||||
--- Send a CC message of a specified type to the coalition of the CC.
|
||||
-- @param #COMMANDCENTER self
|
||||
-- @param #string Message The message.
|
||||
-- @param Core.Message#MESSAGE.MessageType MessageType The type of the message, resulting in automatic time duration and prefix of the message.
|
||||
function COMMANDCENTER:MessageTypeToCoalition( Message, MessageType )
|
||||
|
||||
local CCCoalition = self:GetPositionable():GetCoalition()
|
||||
--TODO: Fix coalition bug!
|
||||
|
||||
self:GetPositionable():MessageTypeToCoalition( Message, MessageType, CCCoalition )
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Report the status of all MISSIONs to a GROUP.
|
||||
-- Each Mission is listed, with an indication how many Tasks are still to be completed.
|
||||
-- @param #COMMANDCENTER self
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
-- ---------------------------------
|
||||
-- Derived DETECTION_MANAGER classes will reports detected units using the method @{DetectionManager#DETECTION_MANAGER.ReportDetected}(). This method implements polymorphic behaviour.
|
||||
--
|
||||
-- The time interval in seconds of the reporting can be changed using the methods @{DetectionManager#DETECTION_MANAGER.SetReportInterval}().
|
||||
-- The time interval in seconds of the reporting can be changed using the methods @{DetectionManager#DETECTION_MANAGER.SetRefreshTimeInterval}().
|
||||
-- To control how long a reporting message is displayed, use @{DetectionManager#DETECTION_MANAGER.SetReportDisplayTime}().
|
||||
-- Derived classes need to implement the method @{DetectionManager#DETECTION_MANAGER.GetReportDisplayTime}() to use the correct display time for displayed messages during a report.
|
||||
--
|
||||
@ -126,7 +126,7 @@ do -- DETECTION MANAGER
|
||||
|
||||
self:AddTransition( "Started", "Report", "Started" )
|
||||
|
||||
self:SetReportInterval( 30 )
|
||||
self:SetRefreshTimeInterval( 30 )
|
||||
self:SetReportDisplayTime( 25 )
|
||||
|
||||
self:E( { Detection = Detection } )
|
||||
@ -143,19 +143,19 @@ do -- DETECTION MANAGER
|
||||
|
||||
self:E( "onafterReport" )
|
||||
|
||||
self:__Report( -self._ReportInterval )
|
||||
self:__Report( -self._RefreshTimeInterval )
|
||||
|
||||
self:ProcessDetected( self.Detection )
|
||||
end
|
||||
|
||||
--- Set the reporting time interval.
|
||||
-- @param #DETECTION_MANAGER self
|
||||
-- @param #number ReportInterval The interval in seconds when a report needs to be done.
|
||||
-- @param #number RefreshTimeInterval The interval in seconds when a report needs to be done.
|
||||
-- @return #DETECTION_MANAGER self
|
||||
function DETECTION_MANAGER:SetReportInterval( ReportInterval )
|
||||
function DETECTION_MANAGER:SetRefreshTimeInterval( RefreshTimeInterval )
|
||||
self:F2()
|
||||
|
||||
self._ReportInterval = ReportInterval
|
||||
self._RefreshTimeInterval = RefreshTimeInterval
|
||||
end
|
||||
|
||||
|
||||
|
||||
@ -264,14 +264,14 @@ function MISSION:New( CommandCenter, MissionName, MissionPriority, MissionBriefi
|
||||
end
|
||||
|
||||
|
||||
-- FSM function for a MISSION
|
||||
--- FSM function for a MISSION
|
||||
-- @param #MISSION self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
function MISSION:onenterCOMPLETED( From, Event, To )
|
||||
|
||||
self:GetCommandCenter():MessageToCoalition( self:GetName() .. " has been completed! Good job guys!" )
|
||||
self:GetCommandCenter():MessageTypeToCoalition( self:GetName() .. " has been completed! Good job guys!", MESSAGE.Type.Information )
|
||||
end
|
||||
|
||||
--- Gets the mission name.
|
||||
@ -450,7 +450,7 @@ do -- Group Assignment
|
||||
local MissionGroupName = MissionGroup:GetName()
|
||||
|
||||
self.AssignedGroups[MissionGroupName] = nil
|
||||
self:E( string.format( "Mission %s is unassigned to %s", MissionName, MissionGroupName ) )
|
||||
--self:E( string.format( "Mission %s is unassigned to %s", MissionName, MissionGroupName ) )
|
||||
|
||||
return self
|
||||
end
|
||||
@ -475,7 +475,23 @@ function MISSION:RemoveTaskMenu( Task )
|
||||
end
|
||||
|
||||
|
||||
--- Gets the mission menu for the coalition.
|
||||
--- Gets the root mission menu for the TaskGroup.
|
||||
-- @param #MISSION self
|
||||
-- @return Core.Menu#MENU_COALITION self
|
||||
function MISSION:GetRootMenu( TaskGroup ) -- R2.2
|
||||
|
||||
local CommandCenter = self:GetCommandCenter()
|
||||
local CommandCenterMenu = CommandCenter:GetMenu()
|
||||
|
||||
local MissionName = self:GetName()
|
||||
--local MissionMenu = CommandCenterMenu:GetMenu( MissionName )
|
||||
|
||||
self.MissionMenu = self.MissionMenu or MENU_COALITION:New( self.MissionCoalition, self:GetName(), CommandCenterMenu )
|
||||
|
||||
return self.MissionMenu
|
||||
end
|
||||
|
||||
--- Gets the mission menu for the TaskGroup.
|
||||
-- @param #MISSION self
|
||||
-- @return Core.Menu#MENU_COALITION self
|
||||
function MISSION:GetMenu( TaskGroup ) -- R2.1 -- Changed Menu Structure
|
||||
@ -486,27 +502,28 @@ function MISSION:GetMenu( TaskGroup ) -- R2.1 -- Changed Menu Structure
|
||||
local MissionName = self:GetName()
|
||||
--local MissionMenu = CommandCenterMenu:GetMenu( MissionName )
|
||||
|
||||
self.MissionMenu = self.MissionMenu or {}
|
||||
self.MissionMenu[TaskGroup] = self.MissionMenu[TaskGroup] or {}
|
||||
self.MissionGroupMenu = self.MissionGroupMenu or {}
|
||||
self.MissionGroupMenu[TaskGroup] = self.MissionGroupMenu[TaskGroup] or {}
|
||||
|
||||
local Menu = self.MissionMenu[TaskGroup]
|
||||
local GroupMenu = self.MissionGroupMenu[TaskGroup]
|
||||
|
||||
Menu.MainMenu = Menu.MainMenu or MENU_GROUP:New( TaskGroup, self:GetName(), CommandCenterMenu )
|
||||
Menu.BriefingMenu = Menu.BriefingMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Mission Briefing", Menu.MainMenu, self.MenuReportBriefing, self, TaskGroup )
|
||||
self.MissionMenu = self.MissionMenu or MENU_COALITION:New( self.MissionCoalition, self:GetName(), CommandCenterMenu )
|
||||
|
||||
GroupMenu.BriefingMenu = GroupMenu.BriefingMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Mission Briefing", self.MissionMenu, self.MenuReportBriefing, self, TaskGroup )
|
||||
|
||||
Menu.TaskReportsMenu = Menu.TaskReportsMenu or MENU_GROUP:New( TaskGroup, "Task Reports", Menu.MainMenu )
|
||||
Menu.ReportTasksMenu = Menu.ReportTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Tasks", Menu.TaskReportsMenu, self.MenuReportTasksSummary, self, TaskGroup )
|
||||
Menu.ReportPlannedTasksMenu = Menu.ReportPlannedTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Planned Tasks", Menu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Planned" )
|
||||
Menu.ReportAssignedTasksMenu = Menu.ReportAssignedTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Assigned Tasks", Menu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Assigned" )
|
||||
Menu.ReportSuccessTasksMenu = Menu.ReportSuccessTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Successful Tasks", Menu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Success" )
|
||||
Menu.ReportFailedTasksMenu = Menu.ReportFailedTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Failed Tasks", Menu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Failed" )
|
||||
Menu.ReportHeldTasksMenu = Menu.ReportHeldTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Held Tasks", Menu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Hold" )
|
||||
GroupMenu.TaskReportsMenu = GroupMenu.TaskReportsMenu or MENU_GROUP:New( TaskGroup, "Task Reports", self.MissionMenu )
|
||||
GroupMenu.ReportTasksMenu = GroupMenu.ReportTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksSummary, self, TaskGroup )
|
||||
GroupMenu.ReportPlannedTasksMenu = GroupMenu.ReportPlannedTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Planned Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Planned" )
|
||||
GroupMenu.ReportAssignedTasksMenu = GroupMenu.ReportAssignedTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Assigned Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Assigned" )
|
||||
GroupMenu.ReportSuccessTasksMenu = GroupMenu.ReportSuccessTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Successful Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Success" )
|
||||
GroupMenu.ReportFailedTasksMenu = GroupMenu.ReportFailedTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Failed Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Failed" )
|
||||
GroupMenu.ReportHeldTasksMenu = GroupMenu.ReportHeldTasksMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Held Tasks", GroupMenu.TaskReportsMenu, self.MenuReportTasksPerStatus, self, TaskGroup, "Hold" )
|
||||
|
||||
Menu.PlayerReportsMenu = Menu.PlayerReportsMenu or MENU_GROUP:New( TaskGroup, "Statistics Reports", Menu.MainMenu )
|
||||
Menu.ReportMissionHistory = Menu.ReportPlayersHistory or MENU_GROUP_COMMAND:New( TaskGroup, "Report Mission Progress", Menu.PlayerReportsMenu, self.MenuReportPlayersProgress, self, TaskGroup )
|
||||
Menu.ReportPlayersPerTaskMenu = Menu.ReportPlayersPerTaskMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Players per Task", Menu.PlayerReportsMenu, self.MenuReportPlayersPerTask, self, TaskGroup )
|
||||
GroupMenu.PlayerReportsMenu = GroupMenu.PlayerReportsMenu or MENU_GROUP:New( TaskGroup, "Statistics Reports", self.MissionMenu )
|
||||
GroupMenu.ReportMissionHistory = GroupMenu.ReportPlayersHistory or MENU_GROUP_COMMAND:New( TaskGroup, "Report Mission Progress", GroupMenu.PlayerReportsMenu, self.MenuReportPlayersProgress, self, TaskGroup )
|
||||
GroupMenu.ReportPlayersPerTaskMenu = GroupMenu.ReportPlayersPerTaskMenu or MENU_GROUP_COMMAND:New( TaskGroup, "Report Players per Task", GroupMenu.PlayerReportsMenu, self.MenuReportPlayersPerTask, self, TaskGroup )
|
||||
|
||||
return Menu.MainMenu
|
||||
return self.MissionMenu
|
||||
end
|
||||
|
||||
|
||||
@ -843,8 +860,9 @@ end
|
||||
|
||||
--- Create a summary report of the Mission (one line).
|
||||
-- @param #MISSION self
|
||||
-- @param Wrapper.Group#GROUP ReportGroup
|
||||
-- @return #string
|
||||
function MISSION:ReportSummary()
|
||||
function MISSION:ReportSummary( ReportGroup )
|
||||
|
||||
local Report = REPORT:New()
|
||||
|
||||
@ -857,9 +875,9 @@ function MISSION:ReportSummary()
|
||||
Report:Add( string.format( '%s - %s - Task Overview Report', Name, Status ) )
|
||||
|
||||
-- Determine how many tasks are remaining.
|
||||
for TaskID, Task in pairs( self:GetTasks() ) do
|
||||
for TaskID, Task in UTILS.spairs( self:GetTasks(), function( t, a, b ) return t[a]:ReportOrder( ReportGroup ) < t[b]:ReportOrder( ReportGroup ) end ) do
|
||||
local Task = Task -- Tasking.Task#TASK
|
||||
Report:Add( "- " .. Task:ReportSummary() )
|
||||
Report:Add( "- " .. Task:ReportSummary( ReportGroup ) )
|
||||
end
|
||||
|
||||
return Report:Text()
|
||||
@ -870,6 +888,8 @@ end
|
||||
-- @return #string
|
||||
function MISSION:ReportOverview( ReportGroup, TaskStatus )
|
||||
|
||||
self:F( { TaskStatus = TaskStatus } )
|
||||
|
||||
local Report = REPORT:New()
|
||||
|
||||
-- List the name of the mission.
|
||||
@ -881,12 +901,17 @@ function MISSION:ReportOverview( ReportGroup, TaskStatus )
|
||||
Report:Add( string.format( '%s - %s - %s Tasks Report', Name, Status, TaskStatus ) )
|
||||
|
||||
-- Determine how many tasks are remaining.
|
||||
local TasksRemaining = 0
|
||||
for TaskID, Task in pairs( self:GetTasks() ) do
|
||||
local Tasks = 0
|
||||
for TaskID, Task in UTILS.spairs( self:GetTasks(), function( t, a, b ) return t[a]:ReportOrder( ReportGroup ) < t[b]:ReportOrder( ReportGroup ) end ) do
|
||||
local Task = Task -- Tasking.Task#TASK
|
||||
if Task:Is( TaskStatus ) then
|
||||
Report:Add( string.rep( "-", 140 ) )
|
||||
Report:Add( " - " .. Task:ReportOverview( ReportGroup ) )
|
||||
end
|
||||
Tasks = Tasks + 1
|
||||
if Tasks >= 8 then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return Report:Text()
|
||||
@ -935,7 +960,7 @@ function MISSION:MenuReportBriefing( ReportGroup )
|
||||
|
||||
local Report = self:ReportBriefing()
|
||||
|
||||
self:GetCommandCenter():MessageToGroup( Report, ReportGroup )
|
||||
self:GetCommandCenter():MessageTypeToGroup( Report, ReportGroup, MESSAGE.Type.Briefing )
|
||||
end
|
||||
|
||||
|
||||
@ -945,9 +970,9 @@ end
|
||||
-- @param Wrapper.Group#GROUP ReportGroup
|
||||
function MISSION:MenuReportTasksSummary( ReportGroup )
|
||||
|
||||
local Report = self:ReportSummary()
|
||||
local Report = self:ReportSummary( ReportGroup )
|
||||
|
||||
self:GetCommandCenter():MessageToGroup( Report, ReportGroup )
|
||||
self:GetCommandCenter():MessageTypeToGroup( Report, ReportGroup, MESSAGE.Type.Overview )
|
||||
end
|
||||
|
||||
|
||||
@ -960,7 +985,7 @@ function MISSION:MenuReportTasksPerStatus( ReportGroup, TaskStatus )
|
||||
|
||||
local Report = self:ReportOverview( ReportGroup, TaskStatus )
|
||||
|
||||
self:GetCommandCenter():MessageToGroup( Report, ReportGroup )
|
||||
self:GetCommandCenter():MessageTypeToGroup( Report, ReportGroup, MESSAGE.Type.Overview )
|
||||
end
|
||||
|
||||
|
||||
@ -970,7 +995,7 @@ function MISSION:MenuReportPlayersPerTask( ReportGroup )
|
||||
|
||||
local Report = self:ReportPlayersPerTask()
|
||||
|
||||
self:GetCommandCenter():MessageToGroup( Report, ReportGroup )
|
||||
self:GetCommandCenter():MessageTypeToGroup( Report, ReportGroup, MESSAGE.Type.Overview )
|
||||
end
|
||||
|
||||
--- @param #MISSION self
|
||||
@ -979,7 +1004,7 @@ function MISSION:MenuReportPlayersProgress( ReportGroup )
|
||||
|
||||
local Report = self:ReportPlayersProgress()
|
||||
|
||||
self:GetCommandCenter():MessageToGroup( Report, ReportGroup )
|
||||
self:GetCommandCenter():MessageTypeToGroup( Report, ReportGroup, MESSAGE.Type.Overview )
|
||||
end
|
||||
|
||||
|
||||
|
||||
@ -175,28 +175,34 @@ function TASK:New( Mission, SetGroupAssign, TaskName, TaskType, TaskBriefing )
|
||||
--- Goal Handler OnBefore for TASK
|
||||
-- @function [parent=#TASK] OnBeforeGoal
|
||||
-- @param #TASK self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Unit#UNIT PlayerUnit The @{Unit} of the player.
|
||||
-- @param #string PlayerName The name of the player.
|
||||
-- @return #boolean
|
||||
|
||||
--- Goal Handler OnAfter for TASK
|
||||
-- @function [parent=#TASK] OnAfterGoal
|
||||
-- @param #TASK self
|
||||
-- @param Wrapper.Controllable#CONTROLLABLE Controllable
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Unit#UNIT PlayerUnit The @{Unit} of the player.
|
||||
-- @param #string PlayerName The name of the player.
|
||||
|
||||
--- Goal Trigger for TASK
|
||||
-- @function [parent=#TASK] Goal
|
||||
-- @param #TASK self
|
||||
-- @param Wrapper.Unit#UNIT PlayerUnit The @{Unit} of the player.
|
||||
-- @param #string PlayerName The name of the player.
|
||||
|
||||
--- Goal Asynchronous Trigger for TASK
|
||||
-- @function [parent=#TASK] __Goal
|
||||
-- @param #TASK self
|
||||
-- @param #number Delay
|
||||
-- @param Wrapper.Unit#UNIT PlayerUnit The @{Unit} of the player.
|
||||
-- @param #string PlayerName The name of the player.
|
||||
|
||||
|
||||
|
||||
@ -272,7 +278,7 @@ function TASK:JoinUnit( PlayerUnit, PlayerGroup )
|
||||
-- Check if the PlayerGroup is already assigned to the Task. If yes, the PlayerGroup is added to the Task.
|
||||
-- If the PlayerGroup is not assigned to the Task, the menu needs to be set. In that case, the PlayerUnit will become the GroupPlayer leader.
|
||||
if self:IsStatePlanned() or self:IsStateReplanned() then
|
||||
self:SetMenuForGroup( PlayerGroup )
|
||||
--self:SetMenuForGroup( PlayerGroup )
|
||||
--self:MessageToGroups( PlayerUnit:GetPlayerName() .. " is planning to join Task " .. self:GetName() )
|
||||
end
|
||||
if self:IsStateAssigned() then
|
||||
@ -309,7 +315,7 @@ function TASK:AbortGroup( PlayerGroup )
|
||||
self:E( { IsGroupAssigned = IsGroupAssigned } )
|
||||
if IsGroupAssigned then
|
||||
local PlayerName = PlayerGroup:GetUnit(1):GetPlayerName()
|
||||
self:MessageToGroups( PlayerName .. " aborted Task " .. self:GetName() )
|
||||
--self:MessageToGroups( PlayerName .. " aborted Task " .. self:GetName() )
|
||||
self:UnAssignFromGroup( PlayerGroup )
|
||||
--self:Abort()
|
||||
|
||||
@ -469,7 +475,7 @@ do -- Group Assignment
|
||||
local TaskGroupName = TaskGroup:GetName()
|
||||
|
||||
self.AssignedGroups[TaskGroupName] = nil
|
||||
self:E( string.format( "Task %s is unassigned to %s", TaskName, TaskGroupName ) )
|
||||
--self:E( string.format( "Task %s is unassigned to %s", TaskName, TaskGroupName ) )
|
||||
|
||||
-- Set the group to be assigned at mission level. This allows to decide the menu options on mission level for this group.
|
||||
self:GetMission():ClearGroupAssignment( TaskGroup )
|
||||
@ -479,9 +485,9 @@ do -- Group Assignment
|
||||
SetAssignedGroups:ForEachGroup(
|
||||
function( AssignedGroup )
|
||||
if self:IsGroupAssigned(AssignedGroup) then
|
||||
self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is unassigned from group %s.", TaskName, TaskGroupName ), AssignedGroup )
|
||||
--self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is unassigned from group %s.", TaskName, TaskGroupName ), AssignedGroup )
|
||||
else
|
||||
self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is unassigned from your group.", TaskName ), AssignedGroup )
|
||||
--self:GetMission():GetCommandCenter():MessageToGroup( string.format( "Task %s is unassigned from your group.", TaskName ), AssignedGroup )
|
||||
end
|
||||
end
|
||||
)
|
||||
@ -530,8 +536,9 @@ do -- Group Assignment
|
||||
|
||||
--- UnAssign the @{Task} from a @{Group}.
|
||||
-- @param #TASK self
|
||||
-- @param Wrapper.Group#GROUP TaskGroup
|
||||
function TASK:UnAssignFromGroup( TaskGroup )
|
||||
self:F2( { TaskGroup } )
|
||||
self:F2( { TaskGroup = TaskGroup:GetName() } )
|
||||
|
||||
self:ClearGroupAssignment( TaskGroup )
|
||||
|
||||
@ -539,7 +546,7 @@ do -- Group Assignment
|
||||
for UnitID, UnitData in pairs( TaskUnits ) do
|
||||
local TaskUnit = UnitData -- Wrapper.Unit#UNIT
|
||||
local PlayerName = TaskUnit:GetPlayerName()
|
||||
if PlayerName ~= nil or PlayerName ~= "" then
|
||||
if PlayerName ~= nil and PlayerName ~= "" then -- Only remove units that have players!
|
||||
self:UnAssignFromUnit( TaskUnit )
|
||||
end
|
||||
end
|
||||
@ -573,7 +580,6 @@ function TASK:AssignToUnit( TaskUnit )
|
||||
|
||||
-- Assign a new FsmUnit to TaskUnit.
|
||||
local FsmUnit = self:SetStateMachine( TaskUnit, FsmTemplate:Copy( TaskUnit, self ) ) -- Core.Fsm#FSM_PROCESS
|
||||
self:E({"Address FsmUnit", tostring( FsmUnit ) } )
|
||||
|
||||
FsmUnit:SetStartState( "Planned" )
|
||||
|
||||
@ -738,13 +744,16 @@ function TASK:SetPlannedMenuForGroup( TaskGroup, MenuTime )
|
||||
|
||||
--local MissionMenu = Mission:GetMenu( TaskGroup )
|
||||
|
||||
local TaskPlannedMenu = MENU_GROUP:New( TaskGroup, "Planned Tasks", MissionMenu ):SetTime( MenuTime )
|
||||
local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, TaskPlannedMenu ):SetTime( MenuTime ):SetRemoveParent( true )
|
||||
local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskText, TaskTypeMenu ):SetTime( MenuTime ):SetRemoveParent( true )
|
||||
local ReportTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Status" ), TaskTypeMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetRemoveParent( true )
|
||||
self.MenuPlanned = self.MenuPlanned or {}
|
||||
self.MenuPlanned[TaskGroup] = MENU_GROUP:New( TaskGroup, "Join Planned Task", MissionMenu, Mission.MenuReportTasksPerStatus, Mission, TaskGroup, "Planned" ):SetTime( MenuTime ):SetTag( "Tasking" )
|
||||
local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskType, self.MenuPlanned[TaskGroup] ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true )
|
||||
local TaskTypeMenu = MENU_GROUP:New( TaskGroup, TaskText, TaskTypeMenu ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true )
|
||||
local ReportTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Status" ), TaskTypeMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true )
|
||||
|
||||
if not Mission:IsGroupAssigned( TaskGroup ) then
|
||||
local JoinTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Join Task" ), TaskTypeMenu, self.MenuAssignToGroup, { self = self, TaskGroup = TaskGroup } ):SetTime( MenuTime ):SetRemoveParent( true )
|
||||
self:F( { "Replacing Join Task menu" } )
|
||||
local JoinTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Join Task" ), TaskTypeMenu, self.MenuAssignToGroup, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true )
|
||||
local MarkTaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Mark Task on Map" ), TaskTypeMenu, self.MenuMarkToGroup, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true )
|
||||
end
|
||||
|
||||
return self
|
||||
@ -775,9 +784,10 @@ function TASK:SetAssignedMenuForGroup( TaskGroup, MenuTime )
|
||||
-- local MissionMenu = MENU_GROUP:New( TaskGroup, MissionName, CommandCenterMenu ):SetTime( MenuTime )
|
||||
-- local MissionMenu = Mission:GetMenu( TaskGroup )
|
||||
|
||||
local TaskAssignedMenu = MENU_GROUP:New( TaskGroup, string.format( "Assigned Task %s", TaskName ), MissionMenu ):SetTime( MenuTime )
|
||||
local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Status" ), TaskAssignedMenu, self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetRemoveParent( true )
|
||||
local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Abort Group from Task" ), TaskAssignedMenu, self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ):SetRemoveParent( true )
|
||||
self.MenuAssigned = self.MenuAssigned or {}
|
||||
self.MenuAssigned[TaskGroup] = MENU_GROUP:New( TaskGroup, string.format( "Assigned Task %s", TaskName ), MissionMenu ):SetTime( MenuTime ):SetTag( "Tasking" )
|
||||
local TaskTypeMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Report Task Status" ), self.MenuAssigned[TaskGroup], self.MenuTaskStatus, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true )
|
||||
local TaskMenu = MENU_GROUP_COMMAND:New( TaskGroup, string.format( "Abort Group from Task" ), self.MenuAssigned[TaskGroup], self.MenuTaskAbort, self, TaskGroup ):SetTime( MenuTime ):SetTag( "Tasking" ):SetRemoveParent( true )
|
||||
|
||||
return self
|
||||
end
|
||||
@ -791,9 +801,7 @@ function TASK:RemoveMenu( MenuTime )
|
||||
|
||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||
local TaskGroup = TaskGroup -- Wrapper.Group#GROUP
|
||||
if TaskGroup:IsAlive() and TaskGroup:GetPlayerNames() then
|
||||
self:RefreshMenus( TaskGroup, MenuTime )
|
||||
end
|
||||
self:RefreshMenus( TaskGroup, MenuTime )
|
||||
end
|
||||
end
|
||||
|
||||
@ -814,15 +822,18 @@ function TASK:RefreshMenus( TaskGroup, MenuTime )
|
||||
local MissionMenu = Mission:GetMenu( TaskGroup )
|
||||
|
||||
local TaskName = self:GetName()
|
||||
local PlannedMenu = MissionMenu:GetMenu( "Planned Tasks" )
|
||||
local AssignedMenu = MissionMenu:GetMenu( string.format( "Assigned Task %s", TaskName ) )
|
||||
self.MenuPlanned = self.MenuPlanned or {}
|
||||
local PlannedMenu = self.MenuPlanned[TaskGroup]
|
||||
|
||||
self.MenuAssigned = self.MenuAssigned or {}
|
||||
local AssignedMenu = self.MenuAssigned[TaskGroup]
|
||||
|
||||
if PlannedMenu then
|
||||
PlannedMenu:Remove( MenuTime )
|
||||
PlannedMenu:Remove( MenuTime , "Tasking")
|
||||
end
|
||||
|
||||
if AssignedMenu then
|
||||
AssignedMenu:Remove( MenuTime )
|
||||
AssignedMenu:Remove( MenuTime, "Tasking" )
|
||||
end
|
||||
|
||||
end
|
||||
@ -846,16 +857,44 @@ function TASK:RemoveAssignedMenuForGroup( TaskGroup )
|
||||
|
||||
end
|
||||
|
||||
function TASK.MenuAssignToGroup( MenuParam )
|
||||
--- @param #TASK self
|
||||
-- @param Wrapper.Group#GROUP TaskGroup
|
||||
function TASK:MenuAssignToGroup( TaskGroup )
|
||||
|
||||
local self = MenuParam.self
|
||||
local TaskGroup = MenuParam.TaskGroup
|
||||
|
||||
self:E( "Assigned menu selected")
|
||||
self:E( "Join Task menu selected")
|
||||
|
||||
self:AssignToGroup( TaskGroup )
|
||||
end
|
||||
|
||||
--- @param #TASK self
|
||||
-- @param Wrapper.Group#GROUP TaskGroup
|
||||
function TASK:MenuMarkToGroup( TaskGroup )
|
||||
|
||||
self:E( "Mark Task menu selected")
|
||||
|
||||
self:UpdateTaskInfo()
|
||||
|
||||
local Report = REPORT:New():SetIndent( 0 )
|
||||
|
||||
-- List the name of the Task.
|
||||
local Name = self:GetName()
|
||||
Report:Add( "Task " .. Name .. ": " .. self:GetTaskBriefing() .. "\n" )
|
||||
|
||||
for TaskInfoID, TaskInfo in pairs( self.TaskInfo, function( t, a, b ) return t[a].TaskInfoOrder < t[b].TaskInfoOrder end ) do
|
||||
|
||||
local ReportText = self:GetMarkInfo( TaskInfoID, TaskInfo )
|
||||
if ReportText then
|
||||
Report:Add( ReportText )
|
||||
end
|
||||
end
|
||||
|
||||
local TargetCoordinate = self:GetInfo( "Coordinate" ) -- Core.Point#COORDINATE
|
||||
local MarkText = Report:Text( ", " )
|
||||
self:F( { Coordinate = TargetCoordinate, MarkText = MarkText } )
|
||||
TargetCoordinate:MarkToGroup( MarkText, TaskGroup )
|
||||
--Coordinate:MarkToAll( Briefing )
|
||||
end
|
||||
|
||||
--- Report the task status.
|
||||
-- @param #TASK self
|
||||
function TASK:MenuTaskStatus( TaskGroup )
|
||||
@ -863,7 +902,7 @@ function TASK:MenuTaskStatus( TaskGroup )
|
||||
local ReportText = self:ReportDetails( TaskGroup )
|
||||
|
||||
self:T( ReportText )
|
||||
self:GetMission():GetCommandCenter():MessageToGroup( ReportText, TaskGroup )
|
||||
self:GetMission():GetCommandCenter():MessageTypeToGroup( ReportText, TaskGroup, MESSAGE.Type.Detailed )
|
||||
|
||||
end
|
||||
|
||||
@ -947,7 +986,7 @@ end
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @return #TASK self
|
||||
function TASK:RemoveStateMachine( TaskUnit )
|
||||
self:F( { TaskUnit, self.Fsm[TaskUnit] ~= nil } )
|
||||
self:F( { TaskUnit = TaskUnit:GetName(), HasFsm = ( self.Fsm[TaskUnit] ~= nil ) } )
|
||||
|
||||
--self:E( self.Fsm )
|
||||
--for TaskUnitT, Fsm in pairs( self.Fsm ) do
|
||||
@ -956,13 +995,16 @@ function TASK:RemoveStateMachine( TaskUnit )
|
||||
--self.Fsm[TaskUnit] = nil
|
||||
--end
|
||||
|
||||
self.Fsm[TaskUnit]:Remove()
|
||||
self.Fsm[TaskUnit] = nil
|
||||
if self.Fsm[TaskUnit] then
|
||||
self.Fsm[TaskUnit]:Remove()
|
||||
self.Fsm[TaskUnit] = nil
|
||||
end
|
||||
|
||||
collectgarbage()
|
||||
self:E( "Garbage Collected, Processes should be finalized now ...")
|
||||
end
|
||||
|
||||
|
||||
--- Checks if there is a FiniteStateMachine assigned to Task@{Unit} for @{Task}
|
||||
-- @param #TASK self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
@ -1016,10 +1058,26 @@ end
|
||||
|
||||
--- Sets the Information on the Task
|
||||
-- @param #TASK self
|
||||
-- @param #string TaskInfo
|
||||
function TASK:SetInfo( TaskInfo, TaskInfoText )
|
||||
-- @param #string TaskInfo The key and title of the task information.
|
||||
-- @param #string TaskInfoText The Task info text.
|
||||
-- @param #number TaskInfoOrder The ordering, a number between 0 and 99.
|
||||
function TASK:SetInfo( TaskInfo, TaskInfoText, TaskInfoOrder )
|
||||
|
||||
self.TaskInfo[TaskInfo] = TaskInfoText
|
||||
self.TaskInfo = self.TaskInfo or {}
|
||||
self.TaskInfo[TaskInfo] = self.TaskInfo[TaskInfo] or {}
|
||||
self.TaskInfo[TaskInfo].TaskInfoText = TaskInfoText
|
||||
self.TaskInfo[TaskInfo].TaskInfoOrder = TaskInfoOrder
|
||||
end
|
||||
|
||||
--- Gets the Information of the Task
|
||||
-- @param #TASK self
|
||||
-- @param #string TaskInfo The key and title of the task information.
|
||||
-- @return #string TaskInfoText The Task info text.
|
||||
function TASK:GetInfo( TaskInfo )
|
||||
|
||||
self.TaskInfo = self.TaskInfo or {}
|
||||
self.TaskInfo[TaskInfo] = self.TaskInfo[TaskInfo] or {}
|
||||
return self.TaskInfo[TaskInfo].TaskInfoText
|
||||
end
|
||||
|
||||
--- Gets the Type of the Task
|
||||
@ -1181,9 +1239,11 @@ end
|
||||
-- @param #string To
|
||||
function TASK:onenterAssigned( From, Event, To, PlayerUnit, PlayerName )
|
||||
|
||||
self:E( { "Task Assigned", self.Dispatcher } )
|
||||
|
||||
|
||||
--- This test is required, because the state transition will be fired also when the state does not change in case of an event.
|
||||
if From ~= "Assigned" then
|
||||
self:E( { From, Event, To, PlayerUnit:GetName(), PlayerName } )
|
||||
|
||||
self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " is assigned." )
|
||||
|
||||
-- Set the total Progress to be achieved.
|
||||
@ -1198,7 +1258,7 @@ function TASK:onenterAssigned( From, Event, To, PlayerUnit, PlayerName )
|
||||
self:GetMission():__Start( 1 )
|
||||
|
||||
-- When the task is assigned, the task goal needs to be checked of the derived classes.
|
||||
self:__Goal( -10 ) -- Polymorphic
|
||||
self:__Goal( -10, PlayerUnit, PlayerName ) -- Polymorphic
|
||||
|
||||
self:SetMenu()
|
||||
end
|
||||
@ -1239,6 +1299,23 @@ function TASK:onenterAborted( From, Event, To )
|
||||
|
||||
end
|
||||
|
||||
--- FSM function for a TASK
|
||||
-- @param #TASK self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
function TASK:onenterCancelled( From, Event, To )
|
||||
|
||||
self:E( "Task Cancelled" )
|
||||
|
||||
if From ~= "Cancelled" then
|
||||
self:GetMission():GetCommandCenter():MessageToCoalition( "Task " .. self:GetName() .. " has been cancelled! The tactical situation has changed." )
|
||||
self:UnAssignFromGroups()
|
||||
self:SetMenu()
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- FSM function for a TASK
|
||||
-- @param #TASK self
|
||||
-- @param #string From
|
||||
@ -1313,7 +1390,7 @@ function TASK:onbeforeTimeOut( From, Event, To )
|
||||
return false
|
||||
end
|
||||
|
||||
do -- Dispatcher
|
||||
do -- Links
|
||||
|
||||
--- Set dispatcher of a task
|
||||
-- @param #TASK self
|
||||
@ -1323,6 +1400,19 @@ do -- Dispatcher
|
||||
self.Dispatcher = Dispatcher
|
||||
end
|
||||
|
||||
--- Set detection of a task
|
||||
-- @param #TASK self
|
||||
-- @param Function.Detection#DETECTION_BASE Detection
|
||||
-- @param #number DetectedItemIndex
|
||||
-- @return #TASK
|
||||
function TASK:SetDetection( Detection, DetectedItemIndex )
|
||||
|
||||
self:E({DetectedItemIndex,Detection})
|
||||
|
||||
self.Detection = Detection
|
||||
self.DetectedItemIndex = DetectedItemIndex
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
do -- Reporting
|
||||
@ -1330,55 +1420,74 @@ do -- Reporting
|
||||
--- Create a summary report of the Task.
|
||||
-- List the Task Name and Status
|
||||
-- @param #TASK self
|
||||
-- @param Wrapper.Group#GROUP ReportGroup
|
||||
-- @return #string
|
||||
function TASK:ReportSummary() --R2.1 fixed report. Now nicely formatted and contains the info required.
|
||||
function TASK:ReportSummary( ReportGroup )
|
||||
|
||||
local Report = REPORT:New()
|
||||
|
||||
-- List the name of the Task.
|
||||
local Name = self:GetName()
|
||||
Report:Add( "Task " .. self:GetName() )
|
||||
|
||||
-- Determine the status of the Task.
|
||||
local Status = "<" .. self:GetState() .. ">"
|
||||
Report:Add( "State: <" .. self:GetState() .. ">" )
|
||||
|
||||
Report:Add( 'Task ' .. Name .. ' - State ' .. Status )
|
||||
|
||||
return Report:Text()
|
||||
if self.TaskInfo["Coordinate"] then
|
||||
local TaskInfoIDText = string.format( "%s: ", "Coordinate" )
|
||||
local TaskCoord = self.TaskInfo["Coordinate"].TaskInfoText -- Core.Point#COORDINATE
|
||||
Report:Add( TaskInfoIDText .. TaskCoord:ToString( ReportGroup, nil, self ) )
|
||||
end
|
||||
|
||||
return Report:Text( ', ' )
|
||||
end
|
||||
|
||||
--- Create an overiew report of the Task.
|
||||
-- List the Task Name and Status
|
||||
-- @param #TASK self
|
||||
-- @return #string
|
||||
function TASK:ReportOverview( ReportGroup ) --R2.1 fixed report. Now nicely formatted and contains the info required.
|
||||
function TASK:ReportOverview( ReportGroup )
|
||||
|
||||
self:UpdateTaskInfo()
|
||||
|
||||
-- List the name of the Task.
|
||||
local Name = self:GetName()
|
||||
local Report = REPORT:New( Name )
|
||||
local TaskName = self:GetName()
|
||||
local Report = REPORT:New()
|
||||
|
||||
local Line = 0
|
||||
local LineReport = REPORT:New()
|
||||
|
||||
-- Determine the status of the Task.
|
||||
local Status = "<" .. self:GetState() .. ">"
|
||||
|
||||
for TaskInfoID, TaskInfo in pairs( self.TaskInfo ) do
|
||||
for TaskInfoID, TaskInfo in UTILS.spairs( self.TaskInfo, function( t, a, b ) return t[a].TaskInfoOrder < t[b].TaskInfoOrder end ) do
|
||||
|
||||
self:F( { TaskInfo = TaskInfo } )
|
||||
|
||||
if Line < math.floor( TaskInfo.TaskInfoOrder / 10 ) then
|
||||
if Line ~= 0 then
|
||||
Report:AddIndent( LineReport:Text( ", " ) )
|
||||
else
|
||||
Report:Add( "Task " .. TaskName .. ", " .. LineReport:Text( ", " ) )
|
||||
end
|
||||
LineReport = REPORT:New()
|
||||
Line = math.floor( TaskInfo.TaskInfoOrder / 10 )
|
||||
end
|
||||
|
||||
local TaskInfoIDText = string.format( "%s: ", TaskInfoID )
|
||||
|
||||
if type(TaskInfo) == "string" then
|
||||
Report:Add( TaskInfoIDText .. TaskInfo )
|
||||
|
||||
if type( TaskInfo.TaskInfoText ) == "string" then
|
||||
LineReport:Add( TaskInfoIDText .. TaskInfo.TaskInfoText )
|
||||
elseif type(TaskInfo) == "table" then
|
||||
if TaskInfoID == "Coordinates" then
|
||||
local FromCoordinate = ReportGroup:GetUnit(1):GetCoordinate()
|
||||
local ToCoordinate = TaskInfo -- Core.Point#COORDINATE
|
||||
if TaskInfoID == "Coordinate" then
|
||||
local ToCoordinate = TaskInfo.TaskInfoText -- Core.Point#COORDINATE
|
||||
--Report:Add( TaskInfoIDText )
|
||||
Report:Add( ToCoordinate:ToString( ReportGroup ) )
|
||||
LineReport:Add( TaskInfoIDText .. ToCoordinate:ToString( ReportGroup, nil, self ) )
|
||||
--Report:AddIndent( ToCoordinate:ToStringBULLS( ReportGroup:GetCoalition() ) )
|
||||
else
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Report:AddIndent( LineReport:Text( ", " ) )
|
||||
|
||||
return Report:Text( ", ")
|
||||
return Report:Text()
|
||||
end
|
||||
|
||||
--- Create a count of the players in the Task.
|
||||
@ -1430,6 +1539,8 @@ end
|
||||
-- @return #string
|
||||
function TASK:ReportDetails( ReportGroup )
|
||||
|
||||
self:UpdateTaskInfo()
|
||||
|
||||
local Report = REPORT:New():SetIndent( 3 )
|
||||
|
||||
-- List the name of the Task.
|
||||
@ -1438,6 +1549,8 @@ function TASK:ReportDetails( ReportGroup )
|
||||
-- Determine the status of the Task.
|
||||
local Status = "<" .. self:GetState() .. ">"
|
||||
|
||||
Report:Add( "Task " .. Name .. " - " .. Status .. " - Detailed Report" )
|
||||
|
||||
-- Loop each Unit active in the Task, and find Player Names.
|
||||
local PlayerNames = self:GetPlayerNames()
|
||||
|
||||
@ -1446,30 +1559,21 @@ function TASK:ReportDetails( ReportGroup )
|
||||
PlayerReport:Add( "Group " .. PlayerGroup:GetCallsign() .. ": " .. PlayerName )
|
||||
end
|
||||
local Players = PlayerReport:Text()
|
||||
|
||||
Report:Add( "Task: " .. Name .. " - " .. Status .. " - Detailed Report" )
|
||||
Report:Add( " - Players:" )
|
||||
Report:AddIndent( Players )
|
||||
|
||||
for TaskInfoID, TaskInfo in pairs( self.TaskInfo ) do
|
||||
if Players ~= "" then
|
||||
Report:Add( " - Players assigned:" )
|
||||
Report:AddIndent( Players )
|
||||
end
|
||||
|
||||
for TaskInfoID, TaskInfo in pairs( self.TaskInfo, function( t, a, b ) return t[a].TaskInfoOrder < t[b].TaskInfoOrder end ) do
|
||||
|
||||
local TaskInfoIDText = string.format( " - %s: ", TaskInfoID )
|
||||
|
||||
if type(TaskInfo) == "string" then
|
||||
Report:Add( TaskInfoIDText .. TaskInfo )
|
||||
elseif type(TaskInfo) == "table" then
|
||||
if TaskInfoID == "Coordinates" then
|
||||
local FromCoordinate = ReportGroup:GetUnit(1):GetCoordinate()
|
||||
local ToCoordinate = TaskInfo -- Core.Point#COORDINATE
|
||||
Report:Add( TaskInfoIDText )
|
||||
Report:AddIndent( ToCoordinate:ToStringBRA( FromCoordinate ) .. ", " .. TaskInfo:ToStringAspect( FromCoordinate ) )
|
||||
Report:AddIndent( ToCoordinate:ToStringBULLS( ReportGroup:GetCoalition() ) )
|
||||
else
|
||||
end
|
||||
local ReportText = self:GetReportDetail( ReportGroup, TaskInfoID, TaskInfo )
|
||||
if ReportText then
|
||||
Report:Add( ReportText )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
return Report:Text()
|
||||
end
|
||||
|
||||
|
||||
287
Moose Development/Moose/Tasking/TaskZoneCapture.lua
Normal file
287
Moose Development/Moose/Tasking/TaskZoneCapture.lua
Normal file
@ -0,0 +1,287 @@
|
||||
--- **Tasking** - The TASK_Protect models tasks for players to protect or capture specific zones.
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- ### Author: **Sven Van de Velde (FlightControl)**
|
||||
--
|
||||
-- ### Contributions: MillerTime
|
||||
--
|
||||
-- ====
|
||||
--
|
||||
-- @module TaskZoneCapture
|
||||
|
||||
do -- TASK_ZONE_GOAL
|
||||
|
||||
--- The TASK_ZONE_GOAL class
|
||||
-- @type TASK_ZONE_GOAL
|
||||
-- @field Core.ZoneGoal#ZONE_GOAL ZoneGoal
|
||||
-- @extends Tasking.Task#TASK
|
||||
|
||||
--- # TASK_ZONE_GOAL class, extends @{Task#TASK}
|
||||
--
|
||||
-- The TASK_ZONE_GOAL class defines the task to protect or capture a protection zone.
|
||||
-- The TASK_ZONE_GOAL is implemented using a @{Fsm#FSM_TASK}, and has the following statuses:
|
||||
--
|
||||
-- * **None**: Start of the process
|
||||
-- * **Planned**: The A2G task is planned.
|
||||
-- * **Assigned**: The A2G task is assigned to a @{Group#GROUP}.
|
||||
-- * **Success**: The A2G task is successfully completed.
|
||||
-- * **Failed**: The A2G task has failed. This will happen if the player exists the task early, without communicating a possible cancellation to HQ.
|
||||
--
|
||||
-- ## Set the scoring of achievements in an A2G attack.
|
||||
--
|
||||
-- Scoring or penalties can be given in the following circumstances:
|
||||
--
|
||||
-- * @{#TASK_ZONE_GOAL.SetScoreOnDestroy}(): Set a score when a target in scope of the A2G attack, has been destroyed.
|
||||
-- * @{#TASK_ZONE_GOAL.SetScoreOnSuccess}(): Set a score when all the targets in scope of the A2G attack, have been destroyed.
|
||||
-- * @{#TASK_ZONE_GOAL.SetPenaltyOnFailed}(): Set a penalty when the A2G attack has failed.
|
||||
--
|
||||
-- @field #TASK_ZONE_GOAL
|
||||
TASK_ZONE_GOAL = {
|
||||
ClassName = "TASK_ZONE_GOAL",
|
||||
}
|
||||
|
||||
--- Instantiates a new TASK_ZONE_GOAL.
|
||||
-- @param #TASK_ZONE_GOAL self
|
||||
-- @param Tasking.Mission#MISSION Mission
|
||||
-- @param Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
|
||||
-- @param #string TaskName The name of the Task.
|
||||
-- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal
|
||||
-- @return #TASK_ZONE_GOAL self
|
||||
function TASK_ZONE_GOAL:New( Mission, SetGroup, TaskName, ZoneGoal, TaskType, TaskBriefing )
|
||||
local self = BASE:Inherit( self, TASK:New( Mission, SetGroup, TaskName, TaskType, TaskBriefing ) ) -- #TASK_ZONE_GOAL
|
||||
self:F()
|
||||
|
||||
self.ZoneGoal = ZoneGoal
|
||||
self.TaskType = TaskType
|
||||
|
||||
local Fsm = self:GetUnitProcess()
|
||||
|
||||
|
||||
Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "StartMonitoring", Rejected = "Reject" } )
|
||||
|
||||
Fsm:AddTransition( "Assigned", "StartMonitoring", "Monitoring" )
|
||||
Fsm:AddTransition( "Monitoring", "Monitor", "Monitoring", {} )
|
||||
Fsm:AddTransition( "Monitoring", "RouteTo", "Monitoring" )
|
||||
Fsm:AddProcess( "Monitoring", "RouteToZone", ACT_ROUTE_ZONE:New(), {} )
|
||||
|
||||
--Fsm:AddTransition( "Accounted", "DestroyedAll", "Accounted" )
|
||||
--Fsm:AddTransition( "Accounted", "Success", "Success" )
|
||||
Fsm:AddTransition( "Rejected", "Reject", "Aborted" )
|
||||
Fsm:AddTransition( "Failed", "Fail", "Failed" )
|
||||
|
||||
self:SetTargetZone( self.ZoneGoal:GetZone() )
|
||||
|
||||
--- Test
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task#TASK_ZONE_GOAL Task
|
||||
function Fsm:onafterStartMonitoring( TaskUnit, Task )
|
||||
self:E( { self } )
|
||||
self:__Monitor( 0.1 )
|
||||
self:__RouteTo( 0.1 )
|
||||
end
|
||||
|
||||
--- Monitor Loop
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task#TASK_ZONE_GOAL Task
|
||||
function Fsm:onafterMonitor( TaskUnit, Task )
|
||||
self:E( { self } )
|
||||
self:__Monitor( 15 )
|
||||
end
|
||||
|
||||
--- Test
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_A2G#TASK_ZONE_GOAL Task
|
||||
function Fsm:onafterRouteTo( TaskUnit, Task )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
-- Determine the first Unit from the self.TargetSetUnit
|
||||
|
||||
if Task:GetTargetZone( TaskUnit ) then
|
||||
self:__RouteTo( 0.1 )
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
|
||||
end
|
||||
|
||||
--- @param #TASK_ZONE_GOAL self
|
||||
-- @param Core.ZoneGoal#ZONE_GOAL ZoneGoal The ZoneGoal Engine.
|
||||
function TASK_ZONE_GOAL:SetProtect( ZoneGoal )
|
||||
|
||||
self.ZoneGoal = ZoneGoal -- Core.ZoneGoal#ZONE_GOAL
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @param #TASK_ZONE_GOAL self
|
||||
function TASK_ZONE_GOAL:GetPlannedMenuText()
|
||||
return self:GetStateString() .. " - " .. self:GetTaskName() .. " ( " .. self.ZoneGoal:GetZoneName() .. " )"
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_ZONE_GOAL self
|
||||
-- @param Core.Zone#ZONE_BASE TargetZone The Zone object where the Target is located on the map.
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
function TASK_ZONE_GOAL:SetTargetZone( TargetZone, TaskUnit )
|
||||
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
local ActRouteZone = ProcessUnit:GetProcess( "Monitoring", "RouteToZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
|
||||
ActRouteZone:SetZone( TargetZone )
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_ZONE_GOAL self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @return Core.Zone#ZONE_BASE The Zone object where the Target is located on the map.
|
||||
function TASK_ZONE_GOAL:GetTargetZone( TaskUnit )
|
||||
|
||||
local ProcessUnit = self:GetUnitProcess( TaskUnit )
|
||||
|
||||
local ActRouteZone = ProcessUnit:GetProcess( "Monitoring", "RouteToZone" ) -- Actions.Act_Route#ACT_ROUTE_ZONE
|
||||
return ActRouteZone:GetZone()
|
||||
end
|
||||
|
||||
function TASK_ZONE_GOAL:SetGoalTotal( GoalTotal )
|
||||
|
||||
self.GoalTotal = GoalTotal
|
||||
end
|
||||
|
||||
function TASK_ZONE_GOAL:GetGoalTotal()
|
||||
|
||||
return self.GoalTotal
|
||||
end
|
||||
|
||||
function TASK_ZONE_GOAL:GetMarkInfo( TaskInfoID, TaskInfo )
|
||||
|
||||
if type( TaskInfo.TaskInfoText ) == "string" then
|
||||
return string.format( "%s: %s", TaskInfoID, TaskInfo.TaskInfoText )
|
||||
elseif type( TaskInfo ) == "table" then
|
||||
if TaskInfoID == "Coordinate" then
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function TASK_ZONE_GOAL:GetReportDetail( ReportGroup, TaskInfoID, TaskInfo )
|
||||
|
||||
if type( TaskInfo.TaskInfoText ) == "string" then
|
||||
return string.format( " - %s: %s", TaskInfoID, TaskInfo.TaskInfoText )
|
||||
elseif type(TaskInfo) == "table" then
|
||||
if TaskInfoID == "Coordinate" then
|
||||
local FromCoordinate = ReportGroup:GetUnit(1):GetCoordinate()
|
||||
local ToCoordinate = TaskInfo.TaskInfoText -- Core.Point#COORDINATE
|
||||
return string.format( " - %s: %s", TaskInfoID, ToCoordinate:ToString( ReportGroup:GetUnit( 1 ), nil, self ) )
|
||||
else
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
do -- TASK_ZONE_CAPTURE
|
||||
|
||||
--- The TASK_ZONE_CAPTURE class
|
||||
-- @type TASK_ZONE_CAPTURE
|
||||
-- @field Core.ZoneGoalCoalition#ZONE_GOAL_COALITION ZoneGoal
|
||||
-- @extends #TASK_ZONE_GOAL
|
||||
|
||||
--- # TASK_ZONE_CAPTURE class, extends @{TaskZoneGoal#TASK_ZONE_GOAL}
|
||||
--
|
||||
-- The TASK_ZONE_CAPTURE class defines an Suppression or Extermination of Air Defenses task for a human player to be executed.
|
||||
-- These tasks are important to be executed as they will help to achieve air superiority at the vicinity.
|
||||
--
|
||||
-- The TASK_ZONE_CAPTURE is used by the @{Task_A2G_Dispatcher#TASK_A2G_DISPATCHER} to automatically create SEAD tasks
|
||||
-- based on detected enemy ground targets.
|
||||
--
|
||||
-- @field #TASK_ZONE_CAPTURE
|
||||
TASK_ZONE_CAPTURE = {
|
||||
ClassName = "TASK_ZONE_CAPTURE",
|
||||
}
|
||||
|
||||
|
||||
--- Instantiates a new TASK_ZONE_CAPTURE.
|
||||
-- @param #TASK_ZONE_CAPTURE self
|
||||
-- @param Tasking.Mission#MISSION Mission
|
||||
-- @param Core.Set#SET_GROUP SetGroup The set of groups for which the Task can be assigned.
|
||||
-- @param #string TaskName The name of the Task.
|
||||
-- @param Core.ZoneGoalCoalition#ZONE_GOAL_COALITION ZoneGoalCoalition
|
||||
-- @param #string TaskBriefing The briefing of the task.
|
||||
-- @return #TASK_ZONE_CAPTURE self
|
||||
function TASK_ZONE_CAPTURE:New( Mission, SetGroup, TaskName, ZoneGoalCoalition, TaskBriefing)
|
||||
local self = BASE:Inherit( self, TASK_ZONE_GOAL:New( Mission, SetGroup, TaskName, ZoneGoalCoalition, "CAPTURE", TaskBriefing ) ) -- #TASK_ZONE_CAPTURE
|
||||
self:F()
|
||||
|
||||
Mission:AddTask( self )
|
||||
|
||||
self.TaskCoalition = ZoneGoalCoalition:GetCoalition()
|
||||
self.TaskCoalitionName = ZoneGoalCoalition:GetCoalitionName()
|
||||
self.TaskZoneName = ZoneGoalCoalition:GetZoneName()
|
||||
|
||||
ZoneGoalCoalition:MonitorDestroyedUnits()
|
||||
|
||||
self:SetBriefing(
|
||||
TaskBriefing or
|
||||
"Capture Zone " .. self.TaskZoneName
|
||||
)
|
||||
|
||||
self:UpdateTaskInfo()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Instantiates a new TASK_ZONE_CAPTURE.
|
||||
-- @param #TASK_ZONE_CAPTURE self
|
||||
function TASK_ZONE_CAPTURE:UpdateTaskInfo()
|
||||
|
||||
|
||||
local ZoneCoordinate = self.ZoneGoal:GetZone():GetCoordinate()
|
||||
self:SetInfo( "Coordinate", ZoneCoordinate, 0 )
|
||||
self:SetInfo( "Zone Name", self.ZoneGoal:GetZoneName(), 10 )
|
||||
self:SetInfo( "Zone Coalition", self.ZoneGoal:GetCoalitionName(), 11 )
|
||||
end
|
||||
|
||||
|
||||
function TASK_ZONE_CAPTURE:ReportOrder( ReportGroup )
|
||||
local Coordinate = self:GetInfo( "Coordinate" )
|
||||
--local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
|
||||
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
||||
|
||||
return Distance
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_ZONE_CAPTURE self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
function TASK_ZONE_CAPTURE:OnAfterGoal( From, Event, To, PlayerUnit, PlayerName )
|
||||
|
||||
self:E( { PlayerUnit = PlayerUnit } )
|
||||
|
||||
if self.ZoneGoal then
|
||||
if self.ZoneGoal.Goal:IsAchieved() then
|
||||
self:Success()
|
||||
local TotalContributions = self.ZoneGoal.Goal:GetTotalContributions()
|
||||
local PlayerContributions = self.ZoneGoal.Goal:GetPlayerContributions()
|
||||
self:E( { TotalContributions = TotalContributions, PlayerContributions = PlayerContributions } )
|
||||
for PlayerName, PlayerContribution in pairs( PlayerContributions ) do
|
||||
local Scoring = self:GetScoring()
|
||||
if Scoring then
|
||||
Scoring:_AddMissionGoalScore( self.Mission, PlayerName, "Zone " .. self.ZoneGoal:GetZoneName() .." captured", PlayerContribution * 200 / TotalContributions )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:__Goal( -10, PlayerUnit, PlayerName )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -75,7 +75,7 @@ do -- TASK_A2A
|
||||
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" )
|
||||
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" )
|
||||
|
||||
Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, self.TaskType ), {} )
|
||||
Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} )
|
||||
Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" )
|
||||
Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} )
|
||||
Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} )
|
||||
@ -277,6 +277,35 @@ do -- TASK_A2A
|
||||
return self.GoalTotal
|
||||
end
|
||||
|
||||
function TASK_A2A:GetMarkInfo( TaskInfoID, TaskInfo )
|
||||
|
||||
if type( TaskInfo.TaskInfoText ) == "string" then
|
||||
if TaskInfoID == "Targets" then
|
||||
else
|
||||
return string.format( "%s: %s", TaskInfoID, TaskInfo.TaskInfoText )
|
||||
end
|
||||
elseif type( TaskInfo ) == "table" then
|
||||
if TaskInfoID == "Coordinate" then
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function TASK_A2A:GetReportDetail( ReportGroup, TaskInfoID, TaskInfo )
|
||||
|
||||
if type( TaskInfo.TaskInfoText ) == "string" then
|
||||
return string.format( "%s: %s", TaskInfoID, TaskInfo.TaskInfoText )
|
||||
elseif type(TaskInfo) == "table" then
|
||||
if TaskInfoID == "Coordinate" then
|
||||
local FromCoordinate = ReportGroup:GetUnit(1):GetCoordinate()
|
||||
local ToCoordinate = TaskInfo.TaskInfoText -- Core.Point#COORDINATE
|
||||
return string.format( " - %s: %s", TaskInfoID, ToCoordinate:ToString( ReportGroup:GetUnit(1), nil, self ) )
|
||||
else
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
@ -328,16 +357,48 @@ do -- TASK_A2A_INTERCEPT
|
||||
"Intercept incoming intruders.\n"
|
||||
)
|
||||
|
||||
local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate()
|
||||
self:SetInfo( "Coordinates", TargetCoordinate )
|
||||
|
||||
self:SetInfo( "Threat", "[" .. string.rep( "■", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]" )
|
||||
local DetectedItemsCount = TargetSetUnit:Count()
|
||||
local DetectedItemsTypes = TargetSetUnit:GetTypeNames()
|
||||
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ) )
|
||||
self:UpdateTaskInfo()
|
||||
|
||||
return self
|
||||
end
|
||||
end
|
||||
|
||||
function TASK_A2A_INTERCEPT:UpdateTaskInfo()
|
||||
|
||||
local TargetCoordinate = self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) or self.TargetSetUnit:GetFirst():GetCoordinate()
|
||||
self:SetInfo( "Coordinate", TargetCoordinate, 0 )
|
||||
|
||||
self:SetInfo( "Threat", "[" .. string.rep( "■", self.Detection and self.Detection:GetDetectedItemThreatLevel( self.DetectedItemIndex ) or self.TargetSetUnit:CalculateThreatLevelA2G() ) .. "]", 11 )
|
||||
|
||||
if self.Detection then
|
||||
local DetectedItemsCount = self.TargetSetUnit:Count()
|
||||
local ReportTypes = REPORT:New()
|
||||
local TargetTypes = {}
|
||||
for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do
|
||||
local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit )
|
||||
if not TargetTypes[TargetType] then
|
||||
TargetTypes[TargetType] = TargetType
|
||||
ReportTypes:Add( TargetType )
|
||||
end
|
||||
end
|
||||
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, ReportTypes:Text( ", " ) ), 10 )
|
||||
else
|
||||
local DetectedItemsCount = self.TargetSetUnit:Count()
|
||||
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
|
||||
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 10 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_A2A_INTERCEPT self
|
||||
-- @param Wrapper.Group#GROUP ReportGroup
|
||||
function TASK_A2A_INTERCEPT:ReportOrder( ReportGroup )
|
||||
self:F( { TaskInfo = self.TaskInfo } )
|
||||
local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
|
||||
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
||||
|
||||
return Distance
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_A2A_INTERCEPT self
|
||||
@ -439,7 +500,7 @@ do -- TASK_A2A_SWEEP
|
||||
-- @param #string TaskBriefing The briefing of the task.
|
||||
-- @return #TASK_A2A_SWEEP self
|
||||
function TASK_A2A_SWEEP:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
|
||||
local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "INTERCEPT", TaskBriefing ) ) -- #TASK_A2A_SWEEP
|
||||
local self = BASE:Inherit( self, TASK_A2A:New( Mission, SetGroup, TaskName, TargetSetUnit, "SWEEP", TaskBriefing ) ) -- #TASK_A2A_SWEEP
|
||||
self:F()
|
||||
|
||||
Mission:AddTask( self )
|
||||
@ -451,17 +512,47 @@ do -- TASK_A2A_SWEEP
|
||||
"Perform a fighter sweep. Incoming intruders were detected and could be hiding at the location.\n"
|
||||
)
|
||||
|
||||
local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate()
|
||||
self:SetInfo( "Coordinates", TargetCoordinate )
|
||||
|
||||
self:SetInfo( "Assumed Threat", "[" .. string.rep( "■", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]" )
|
||||
local DetectedItemsCount = TargetSetUnit:Count()
|
||||
local DetectedItemsTypes = TargetSetUnit:GetTypeNames()
|
||||
self:SetInfo( "Lost Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ) )
|
||||
|
||||
self:UpdateTaskInfo()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function TASK_A2A_SWEEP:UpdateTaskInfo()
|
||||
|
||||
local TargetCoordinate = self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) or self.TargetSetUnit:GetFirst():GetCoordinate()
|
||||
self:SetInfo( "Coordinate", TargetCoordinate, 0 )
|
||||
|
||||
self:SetInfo( "Assumed Threat", "[" .. string.rep( "■", self.Detection and self.Detection:GetDetectedItemThreatLevel( self.DetectedItemIndex ) or self.TargetSetUnit:CalculateThreatLevelA2G() ) .. "]", 11 )
|
||||
|
||||
if self.Detection then
|
||||
local DetectedItemsCount = self.TargetSetUnit:Count()
|
||||
local ReportTypes = REPORT:New()
|
||||
local TargetTypes = {}
|
||||
for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do
|
||||
local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit )
|
||||
if not TargetTypes[TargetType] then
|
||||
TargetTypes[TargetType] = TargetType
|
||||
ReportTypes:Add( TargetType )
|
||||
end
|
||||
end
|
||||
self:SetInfo( "Lost Targets", string.format( "%d of %s", DetectedItemsCount, ReportTypes:Text( ", " ) ), 10 )
|
||||
else
|
||||
local DetectedItemsCount = self.TargetSetUnit:Count()
|
||||
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
|
||||
self:SetInfo( "Lost Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 10 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
function TASK_A2A_SWEEP:ReportOrder( ReportGroup )
|
||||
local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
|
||||
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
||||
|
||||
return Distance
|
||||
end
|
||||
|
||||
--- @param #TASK_A2A_SWEEP self
|
||||
function TASK_A2A_SWEEP:onafterGoal( TaskUnit, From, Event, To )
|
||||
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
|
||||
@ -570,17 +661,46 @@ do -- TASK_A2A_ENGAGE
|
||||
"Bogeys are nearby! Players close by are ordered to ENGAGE the intruders!\n"
|
||||
)
|
||||
|
||||
local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate()
|
||||
self:SetInfo( "Coordinates", TargetCoordinate )
|
||||
|
||||
self:SetInfo( "Threat", "[" .. string.rep( "■", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]" )
|
||||
local DetectedItemsCount = TargetSetUnit:Count()
|
||||
local DetectedItemsTypes = TargetSetUnit:GetTypeNames()
|
||||
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ) )
|
||||
self:UpdateTaskInfo()
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function TASK_A2A_ENGAGE:UpdateTaskInfo()
|
||||
|
||||
local TargetCoordinate = self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) or self.TargetSetUnit:GetFirst():GetCoordinate()
|
||||
self:SetInfo( "Coordinate", TargetCoordinate, 0 )
|
||||
|
||||
self:SetInfo( "Threat", "[" .. string.rep( "■", self.Detection and self.Detection:GetDetectedItemThreatLevel( self.DetectedItemIndex ) or self.TargetSetUnit:CalculateThreatLevelA2G() ) .. "]", 11 )
|
||||
|
||||
if self.Detection then
|
||||
local DetectedItemsCount = self.TargetSetUnit:Count()
|
||||
local ReportTypes = REPORT:New()
|
||||
local TargetTypes = {}
|
||||
for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do
|
||||
local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit )
|
||||
if not TargetTypes[TargetType] then
|
||||
TargetTypes[TargetType] = TargetType
|
||||
ReportTypes:Add( TargetType )
|
||||
end
|
||||
end
|
||||
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, ReportTypes:Text( ", " ) ), 10 )
|
||||
else
|
||||
local DetectedItemsCount = self.TargetSetUnit:Count()
|
||||
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
|
||||
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 10 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function TASK_A2A_ENGAGE:ReportOrder( ReportGroup )
|
||||
local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
|
||||
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
||||
|
||||
return Distance
|
||||
end
|
||||
|
||||
--- @param #TASK_A2A_ENGAGE self
|
||||
function TASK_A2A_ENGAGE:onafterGoal( TaskUnit, From, Event, To )
|
||||
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
|
||||
|
||||
@ -93,7 +93,7 @@ do -- TASK_A2A_DISPATCHER
|
||||
--
|
||||
-- local EWRDetection = DETECTION_AREAS:New( EWRSet, 6000 )
|
||||
-- EWRDetection:SetFriendliesRange( 10000 )
|
||||
-- EWRDetection:SetDetectionInterval(30)
|
||||
-- EWRDetection:SetRefreshTimeInterval(30)
|
||||
--
|
||||
-- -- Setup the A2A dispatcher, and initialize it.
|
||||
-- A2ADispatcher = TASK_A2A_DISPATCHER:New( Mission, AttackGroups, EWRDetection )
|
||||
@ -197,7 +197,7 @@ do -- TASK_A2A_DISPATCHER
|
||||
-- TODO: Check detection through radar.
|
||||
self.Detection:FilterCategories( Unit.Category.AIRPLANE, Unit.Category.HELICOPTER )
|
||||
self.Detection:InitDetectRadar( true )
|
||||
self.Detection:SetDetectionInterval( 30 )
|
||||
self.Detection:SetRefreshTimeInterval( 30 )
|
||||
|
||||
self:AddTransition( "Started", "Assign", "Started" )
|
||||
|
||||
@ -382,9 +382,7 @@ do -- TASK_A2A_DISPATCHER
|
||||
end
|
||||
|
||||
if DetectedItemChanged == true or Remove then
|
||||
--self:E( "Removing Tasking: " .. Task:GetTaskName() )
|
||||
Mission:RemoveTask( Task )
|
||||
self.Tasks[DetectedItemIndex] = nil
|
||||
Task = self:RemoveTask( DetectedItemIndex )
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -482,6 +480,11 @@ do -- TASK_A2A_DISPATCHER
|
||||
return PlayersCount, PlayerTypesReport
|
||||
end
|
||||
|
||||
function TASK_A2A_DISPATCHER:RemoveTask( TaskIndex )
|
||||
self.Mission:RemoveTask( self.Tasks[TaskIndex] )
|
||||
self.Tasks[TaskIndex] = nil
|
||||
end
|
||||
|
||||
|
||||
--- Assigns tasks in relation to the detected items to the @{Set#SET_GROUP}.
|
||||
-- @param #TASK_A2A_DISPATCHER self
|
||||
@ -510,8 +513,7 @@ do -- TASK_A2A_DISPATCHER
|
||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||
Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2A task %s for %s removed.", TaskText, Mission:GetName() ), TaskGroup )
|
||||
end
|
||||
Mission:RemoveTask( Task )
|
||||
self.Tasks[TaskIndex] = nil
|
||||
Task = self:RemoveTask( TaskIndex )
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -538,21 +540,24 @@ do -- TASK_A2A_DISPATCHER
|
||||
local TargetSetUnit = self:EvaluateENGAGE( DetectedItem ) -- Returns a SetUnit if there are targets to be INTERCEPTed...
|
||||
if TargetSetUnit then
|
||||
Task = TASK_A2A_ENGAGE:New( Mission, self.SetGroup, string.format( "ENGAGE.%03d", DetectedID ), TargetSetUnit )
|
||||
Task:SetDetection( Detection, TaskIndex )
|
||||
else
|
||||
local TargetSetUnit = self:EvaluateINTERCEPT( DetectedItem ) -- Returns a SetUnit if there are targets to be INTERCEPTed...
|
||||
if TargetSetUnit then
|
||||
Task = TASK_A2A_INTERCEPT:New( Mission, self.SetGroup, string.format( "INTERCEPT.%03d", DetectedID ), TargetSetUnit )
|
||||
Task:SetDetection( Detection, TaskIndex )
|
||||
else
|
||||
local TargetSetUnit = self:EvaluateSWEEP( DetectedItem ) -- Returns a SetUnit
|
||||
if TargetSetUnit then
|
||||
Task = TASK_A2A_SWEEP:New( Mission, self.SetGroup, string.format( "SWEEP.%03d", DetectedID ), TargetSetUnit )
|
||||
Task:SetDetection( Detection, TaskIndex )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Task then
|
||||
self.Tasks[TaskIndex] = Task
|
||||
Task:SetTargetZone( DetectedZone, DetectedSet:GetFirst():GetAltitude(), DetectedSet:GetFirst():GetHeading() )
|
||||
Task:SetTargetZone( DetectedZone, DetectedItem.Coordinate.y, DetectedItem.Coordinate.Heading )
|
||||
Task:SetDispatcher( self )
|
||||
Mission:AddTask( Task )
|
||||
|
||||
@ -565,9 +570,9 @@ do -- TASK_A2A_DISPATCHER
|
||||
|
||||
if Task then
|
||||
local FriendliesCount, FriendliesReport = self:GetFriendliesNearBy( DetectedItem )
|
||||
Task:SetInfo( "Friendlies", string.format( "%d ( %s )", FriendliesCount, FriendliesReport:Text( "," ) ) )
|
||||
Task:SetInfo( "Friendlies", string.format( "%d ( %s )", FriendliesCount, FriendliesReport:Text( "," ) ), 30 )
|
||||
local PlayersCount, PlayersReport = self:GetPlayerFriendliesNearBy( DetectedItem )
|
||||
Task:SetInfo( "Players", string.format( "%d ( %s )", PlayersCount, PlayersReport:Text( "," ) ) )
|
||||
Task:SetInfo( "Players", string.format( "%d ( %s )", PlayersCount, PlayersReport:Text( "," ) ), 31 )
|
||||
end
|
||||
|
||||
-- OK, so the tasking has been done, now delete the changes reported for the area.
|
||||
|
||||
@ -75,7 +75,7 @@ do -- TASK_A2G
|
||||
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "Engage", "Engaging" )
|
||||
Fsm:AddTransition( { "ArrivedAtRendezVous", "HoldingAtRendezVous" }, "HoldAtRendezVous", "HoldingAtRendezVous" )
|
||||
|
||||
Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New( self.TargetSetUnit, self.TaskType ), {} )
|
||||
Fsm:AddProcess ( "Engaging", "Account", ACT_ACCOUNT_DEADS:New(), {} )
|
||||
Fsm:AddTransition( "Engaging", "RouteToTarget", "Engaging" )
|
||||
Fsm:AddProcess( "Engaging", "RouteToTargetZone", ACT_ROUTE_ZONE:New(), {} )
|
||||
Fsm:AddProcess( "Engaging", "RouteToTargetPoint", ACT_ROUTE_POINT:New(), {} )
|
||||
@ -141,9 +141,9 @@ do -- TASK_A2G
|
||||
else
|
||||
local TargetUnit = Task.TargetSetUnit:GetFirst() -- Wrapper.Unit#UNIT
|
||||
if TargetUnit then
|
||||
local Coordinate = TargetUnit:GetCoordinate()
|
||||
local Coordinate = TargetUnit:GetPointVec3()
|
||||
self:T( { TargetCoordinate = Coordinate, Coordinate:GetX(), Coordinate:GetY(), Coordinate:GetZ() } )
|
||||
Task:SetTargetCoordinate( TargetUnit:GetCoordinate(), TaskUnit )
|
||||
Task:SetTargetCoordinate( Coordinate, TaskUnit )
|
||||
end
|
||||
self:__RouteToTargetPoint( 0.1 )
|
||||
end
|
||||
@ -165,6 +165,15 @@ do -- TASK_A2G
|
||||
return self
|
||||
|
||||
end
|
||||
|
||||
--- @param #TASK_A2G self
|
||||
-- @param Core.Set#SET_UNIT TargetSetUnit The set of targets.
|
||||
function TASK_A2G:SetTargetSetUnit( TargetSetUnit )
|
||||
|
||||
self.TargetSetUnit = TargetSetUnit
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- @param #TASK_A2G self
|
||||
function TASK_A2G:GetPlannedMenuText()
|
||||
@ -276,6 +285,36 @@ do -- TASK_A2G
|
||||
|
||||
return self.GoalTotal
|
||||
end
|
||||
|
||||
function TASK_A2G:GetMarkInfo( TaskInfoID, TaskInfo )
|
||||
|
||||
if type( TaskInfo.TaskInfoText ) == "string" then
|
||||
if TaskInfoID == "Targets" then
|
||||
else
|
||||
return string.format( "%s: %s", TaskInfoID, TaskInfo.TaskInfoText )
|
||||
end
|
||||
elseif type( TaskInfo ) == "table" then
|
||||
if TaskInfoID == "Coordinate" then
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
function TASK_A2G:GetReportDetail( ReportGroup, TaskInfoID, TaskInfo )
|
||||
|
||||
if type( TaskInfo.TaskInfoText ) == "string" then
|
||||
return string.format( "%s: %s", TaskInfoID, TaskInfo.TaskInfoText )
|
||||
elseif type(TaskInfo) == "table" then
|
||||
if TaskInfoID == "Coordinate" then
|
||||
local FromCoordinate = ReportGroup:GetUnit(1):GetCoordinate()
|
||||
local ToCoordinate = TaskInfo.TaskInfoText -- Core.Point#COORDINATE
|
||||
return string.format( " - %s: %s", TaskInfoID, ToCoordinate:ToString( ReportGroup:GetUnit(1), nil, self ) )
|
||||
else
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -308,7 +347,7 @@ do -- TASK_A2G_SEAD
|
||||
-- @param Core.Set#SET_UNIT TargetSetUnit
|
||||
-- @param #string TaskBriefing The briefing of the task.
|
||||
-- @return #TASK_A2G_SEAD self
|
||||
function TASK_A2G_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing )
|
||||
function TASK_A2G_SEAD:New( Mission, SetGroup, TaskName, TargetSetUnit, TaskBriefing)
|
||||
local self = BASE:Inherit( self, TASK_A2G:New( Mission, SetGroup, TaskName, TargetSetUnit, "SEAD", TaskBriefing ) ) -- #TASK_A2G_SEAD
|
||||
self:F()
|
||||
|
||||
@ -316,20 +355,55 @@ do -- TASK_A2G_SEAD
|
||||
|
||||
self:SetBriefing(
|
||||
TaskBriefing or
|
||||
"Execute a Suppression of Enemy Air Defenses.\n"
|
||||
"Execute a Suppression of Enemy Air Defenses."
|
||||
)
|
||||
|
||||
local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate()
|
||||
self:SetInfo( "Coordinates", TargetCoordinate )
|
||||
|
||||
self:SetInfo( "Threat", "[" .. string.rep( "■", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]" )
|
||||
local DetectedItemsCount = TargetSetUnit:Count()
|
||||
local DetectedItemsTypes = TargetSetUnit:GetTypeNames()
|
||||
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ) )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function TASK_A2G_SEAD:UpdateTaskInfo()
|
||||
|
||||
|
||||
local TargetCoordinate = self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) or self.TargetSetUnit:GetFirst():GetCoordinate()
|
||||
self:SetInfo( "Coordinate", TargetCoordinate, 0 )
|
||||
|
||||
local ThreatLevel, ThreatText
|
||||
if self.Detection then
|
||||
ThreatLevel, ThreatText = self.Detection:GetDetectedItemThreatLevel( self.DetectedItemIndex )
|
||||
else
|
||||
ThreatLevel, ThreatText = self.TargetSetUnit:CalculateThreatLevelA2G()
|
||||
end
|
||||
self:SetInfo( "Threat", ThreatText .. " [" .. string.rep( "■", ThreatLevel ) .. "]", 11 )
|
||||
|
||||
if self.Detection then
|
||||
local DetectedItemsCount = self.TargetSetUnit:Count()
|
||||
local ReportTypes = REPORT:New()
|
||||
local TargetTypes = {}
|
||||
for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do
|
||||
local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit )
|
||||
if not TargetTypes[TargetType] then
|
||||
TargetTypes[TargetType] = TargetType
|
||||
ReportTypes:Add( TargetType )
|
||||
end
|
||||
end
|
||||
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, ReportTypes:Text( ", " ) ), 10 )
|
||||
else
|
||||
local DetectedItemsCount = self.TargetSetUnit:Count()
|
||||
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
|
||||
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 10 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function TASK_A2G_SEAD:ReportOrder( ReportGroup )
|
||||
local Coordinate = self:GetInfo( "Coordinate" )
|
||||
--local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
|
||||
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
||||
|
||||
return Distance
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_A2G_SEAD self
|
||||
function TASK_A2G_SEAD:onafterGoal( TaskUnit, From, Event, To )
|
||||
local TargetSetUnit = self.TargetSetUnit -- Core.Set#SET_UNIT
|
||||
@ -341,7 +415,7 @@ do -- TASK_A2G_SEAD
|
||||
self:__Goal( -10 )
|
||||
end
|
||||
|
||||
--- Set a score when a target in scope of the A2A attack, has been destroyed .
|
||||
--- Set a score when a target in scope of the A2G attack, has been destroyed .
|
||||
-- @param #TASK_A2G_SEAD self
|
||||
-- @param #string PlayerName The name of the player.
|
||||
-- @param #number Score The score in points to be granted when task process has been achieved.
|
||||
@ -357,7 +431,7 @@ do -- TASK_A2G_SEAD
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a score when all the targets in scope of the A2A attack, have been destroyed.
|
||||
--- Set a score when all the targets in scope of the A2G attack, have been destroyed.
|
||||
-- @param #TASK_A2G_SEAD self
|
||||
-- @param #string PlayerName The name of the player.
|
||||
-- @param #number Score The score in points.
|
||||
@ -373,7 +447,7 @@ do -- TASK_A2G_SEAD
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a penalty when the A2A attack has failed.
|
||||
--- Set a penalty when the A2G attack has failed.
|
||||
-- @param #TASK_A2G_SEAD self
|
||||
-- @param #string PlayerName The name of the player.
|
||||
-- @param #number Penalty The penalty in points, must be a negative value!
|
||||
@ -429,19 +503,66 @@ do -- TASK_A2G_BAI
|
||||
|
||||
self:SetBriefing(
|
||||
TaskBriefing or
|
||||
"Execute a Battlefield Air Interdiction of a group of enemy targets.\n"
|
||||
"Execute a Battlefield Air Interdiction of a group of enemy targets."
|
||||
)
|
||||
|
||||
local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate()
|
||||
self:SetInfo( "Coordinates", TargetCoordinate )
|
||||
|
||||
self:SetInfo( "Threat", "[" .. string.rep( "■", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]" )
|
||||
local DetectedItemsCount = TargetSetUnit:Count()
|
||||
local DetectedItemsTypes = TargetSetUnit:GetTypeNames()
|
||||
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ) )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
end
|
||||
|
||||
function TASK_A2G_BAI:UpdateTaskInfo()
|
||||
|
||||
self:E({self.Detection, self.DetectedItemIndex})
|
||||
|
||||
local TargetCoordinate = self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) or self.TargetSetUnit:GetFirst():GetCoordinate()
|
||||
self:SetInfo( "Coordinate", TargetCoordinate, 0 )
|
||||
|
||||
local ThreatLevel, ThreatText
|
||||
if self.Detection then
|
||||
ThreatLevel, ThreatText = self.Detection:GetDetectedItemThreatLevel( self.DetectedItemIndex )
|
||||
else
|
||||
ThreatLevel, ThreatText = self.TargetSetUnit:CalculateThreatLevelA2G()
|
||||
end
|
||||
self:SetInfo( "Threat", ThreatText .. " [" .. string.rep( "■", ThreatLevel ) .. "]", 11 )
|
||||
|
||||
if self.Detection then
|
||||
local DetectedItemsCount = self.TargetSetUnit:Count()
|
||||
local ReportTypes = REPORT:New()
|
||||
local TargetTypes = {}
|
||||
for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do
|
||||
local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit )
|
||||
if not TargetTypes[TargetType] then
|
||||
TargetTypes[TargetType] = TargetType
|
||||
ReportTypes:Add( TargetType )
|
||||
end
|
||||
end
|
||||
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, ReportTypes:Text( ", " ) ), 10 )
|
||||
else
|
||||
local DetectedItemsCount = self.TargetSetUnit:Count()
|
||||
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
|
||||
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 10 )
|
||||
end
|
||||
|
||||
local TargetCoordinate = self:GetInfo( "Coordinate" ) -- Core.Point#COORDINATE
|
||||
|
||||
local Velocity = self.TargetSetUnit:GetVelocityVec3()
|
||||
local Heading = self.TargetSetUnit:GetHeading()
|
||||
|
||||
TargetCoordinate:SetHeading( Heading )
|
||||
TargetCoordinate:SetVelocity( Velocity )
|
||||
|
||||
self:SetInfo( "Position", "Targets are" .. TargetCoordinate:GetMovingText() .. ".", 12 )
|
||||
|
||||
end
|
||||
|
||||
|
||||
function TASK_A2G_BAI:ReportOrder( ReportGroup )
|
||||
local Coordinate = self:GetInfo( "Coordinate" )
|
||||
--local Coordinate = self.TaskInfo.Coordinates.TaskInfoText
|
||||
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
||||
|
||||
return Distance
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_A2G_BAI self
|
||||
function TASK_A2G_BAI:onafterGoal( TaskUnit, From, Event, To )
|
||||
@ -454,7 +575,7 @@ do -- TASK_A2G_BAI
|
||||
self:__Goal( -10 )
|
||||
end
|
||||
|
||||
--- Set a score when a target in scope of the A2A attack, has been destroyed .
|
||||
--- Set a score when a target in scope of the A2G attack, has been destroyed .
|
||||
-- @param #TASK_A2G_BAI self
|
||||
-- @param #string PlayerName The name of the player.
|
||||
-- @param #number Score The score in points to be granted when task process has been achieved.
|
||||
@ -470,7 +591,7 @@ do -- TASK_A2G_BAI
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a score when all the targets in scope of the A2A attack, have been destroyed.
|
||||
--- Set a score when all the targets in scope of the A2G attack, have been destroyed.
|
||||
-- @param #TASK_A2G_BAI self
|
||||
-- @param #string PlayerName The name of the player.
|
||||
-- @param #number Score The score in points.
|
||||
@ -486,7 +607,7 @@ do -- TASK_A2G_BAI
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a penalty when the A2A attack has failed.
|
||||
--- Set a penalty when the A2G attack has failed.
|
||||
-- @param #TASK_A2G_BAI self
|
||||
-- @param #string PlayerName The name of the player.
|
||||
-- @param #number Penalty The penalty in points, must be a negative value!
|
||||
@ -541,20 +662,56 @@ do -- TASK_A2G_CAS
|
||||
|
||||
self:SetBriefing(
|
||||
TaskBriefing or
|
||||
"Execute a Close Air Support for a group of enemy targets.\n" ..
|
||||
"Beware of friendlies at the vicinity!\n"
|
||||
"Execute a Close Air Support for a group of enemy targets. " ..
|
||||
"Beware of friendlies at the vicinity! "
|
||||
)
|
||||
|
||||
local TargetCoordinate = TargetSetUnit:GetFirst():GetCoordinate()
|
||||
self:SetInfo( "Coordinates", TargetCoordinate )
|
||||
|
||||
self:SetInfo( "Threat", "[" .. string.rep( "■", TargetSetUnit:CalculateThreatLevelA2G() ) .. "]" )
|
||||
local DetectedItemsCount = TargetSetUnit:Count()
|
||||
local DetectedItemsTypes = TargetSetUnit:GetTypeNames()
|
||||
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ) )
|
||||
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function TASK_A2G_CAS:UpdateTaskInfo()
|
||||
|
||||
local TargetCoordinate = ( self.Detection and self.Detection:GetDetectedItemCoordinate( self.DetectedItemIndex ) ) or self.TargetSetUnit:GetFirst():GetCoordinate()
|
||||
self:SetInfo( "Coordinate", TargetCoordinate, 0 )
|
||||
|
||||
local ThreatLevel, ThreatText
|
||||
if self.Detection then
|
||||
ThreatLevel, ThreatText = self.Detection:GetDetectedItemThreatLevel( self.DetectedItemIndex )
|
||||
else
|
||||
ThreatLevel, ThreatText = self.TargetSetUnit:CalculateThreatLevelA2G()
|
||||
end
|
||||
self:SetInfo( "Threat", ThreatText .. " [" .. string.rep( "■", ThreatLevel ) .. "]", 11 )
|
||||
|
||||
if self.Detection then
|
||||
local DetectedItemsCount = self.TargetSetUnit:Count()
|
||||
local ReportTypes = REPORT:New()
|
||||
local TargetTypes = {}
|
||||
for TargetUnitName, TargetUnit in pairs( self.TargetSetUnit:GetSet() ) do
|
||||
local TargetType = self.Detection:GetDetectedUnitTypeName( TargetUnit )
|
||||
if not TargetTypes[TargetType] then
|
||||
TargetTypes[TargetType] = TargetType
|
||||
ReportTypes:Add( TargetType )
|
||||
end
|
||||
end
|
||||
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, ReportTypes:Text( ", " ) ), 10 )
|
||||
else
|
||||
local DetectedItemsCount = self.TargetSetUnit:Count()
|
||||
local DetectedItemsTypes = self.TargetSetUnit:GetTypeNames()
|
||||
self:SetInfo( "Targets", string.format( "%d of %s", DetectedItemsCount, DetectedItemsTypes ), 10 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- @param #TASK_A2G_CAS self
|
||||
function TASK_A2G_CAS:ReportOrder( ReportGroup )
|
||||
|
||||
local Coordinate = self:GetInfo( "Coordinate" )
|
||||
local Distance = ReportGroup:GetCoordinate():Get2DDistance( Coordinate )
|
||||
|
||||
return Distance
|
||||
end
|
||||
|
||||
|
||||
--- @param #TASK_A2G_CAS self
|
||||
function TASK_A2G_CAS:onafterGoal( TaskUnit, From, Event, To )
|
||||
@ -567,7 +724,7 @@ do -- TASK_A2G_CAS
|
||||
self:__Goal( -10 )
|
||||
end
|
||||
|
||||
--- Set a score when a target in scope of the A2A attack, has been destroyed .
|
||||
--- Set a score when a target in scope of the A2G attack, has been destroyed .
|
||||
-- @param #TASK_A2G_CAS self
|
||||
-- @param #string PlayerName The name of the player.
|
||||
-- @param #number Score The score in points to be granted when task process has been achieved.
|
||||
@ -583,7 +740,7 @@ do -- TASK_A2G_CAS
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a score when all the targets in scope of the A2A attack, have been destroyed.
|
||||
--- Set a score when all the targets in scope of the A2G attack, have been destroyed.
|
||||
-- @param #TASK_A2G_CAS self
|
||||
-- @param #string PlayerName The name of the player.
|
||||
-- @param #number Score The score in points.
|
||||
@ -599,7 +756,7 @@ do -- TASK_A2G_CAS
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set a penalty when the A2A attack has failed.
|
||||
--- Set a penalty when the A2G attack has failed.
|
||||
-- @param #TASK_A2G_CAS self
|
||||
-- @param #string PlayerName The name of the player.
|
||||
-- @param #number Penalty The penalty in points, must be a negative value!
|
||||
|
||||
@ -59,6 +59,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
self.Mission = Mission
|
||||
|
||||
self.Detection:FilterCategories( Unit.Category.GROUND_UNIT, Unit.Category.SHIP )
|
||||
self.Detection:FilterFriendliesCategory( Unit.Category.GROUND_UNIT )
|
||||
|
||||
self:AddTransition( "Started", "Assign", "Started" )
|
||||
|
||||
@ -81,7 +82,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
--- Creates a SEAD task when there are targets for it.
|
||||
-- @param #TASK_A2G_DISPATCHER self
|
||||
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
|
||||
-- @return Set#SET_UNIT TargetSetUnit: The target set of units.
|
||||
-- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units.
|
||||
-- @return #nil If there are no targets to be set.
|
||||
function TASK_A2G_DISPATCHER:EvaluateSEAD( DetectedItem )
|
||||
self:F( { DetectedItem.ItemID } )
|
||||
@ -109,7 +110,8 @@ do -- TASK_A2G_DISPATCHER
|
||||
--- Creates a CAS task when there are targets for it.
|
||||
-- @param #TASK_A2G_DISPATCHER self
|
||||
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
|
||||
-- @return Tasking.Task#TASK
|
||||
-- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units.
|
||||
-- @return #nil If there are no targets to be set.
|
||||
function TASK_A2G_DISPATCHER:EvaluateCAS( DetectedItem )
|
||||
self:F( { DetectedItem.ItemID } )
|
||||
|
||||
@ -120,8 +122,9 @@ do -- TASK_A2G_DISPATCHER
|
||||
-- Determine if the set has radar targets. If it does, construct a SEAD task.
|
||||
local GroundUnitCount = DetectedSet:HasGroundUnits()
|
||||
local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem )
|
||||
local RadarCount = DetectedSet:HasSEAD()
|
||||
|
||||
if GroundUnitCount > 0 and FriendliesNearBy == true then
|
||||
if RadarCount == 0 and GroundUnitCount > 0 and FriendliesNearBy == true then
|
||||
|
||||
-- Copy the Set
|
||||
local TargetSetUnit = SET_UNIT:New()
|
||||
@ -137,7 +140,8 @@ do -- TASK_A2G_DISPATCHER
|
||||
--- Creates a BAI task when there are targets for it.
|
||||
-- @param #TASK_A2G_DISPATCHER self
|
||||
-- @param Functional.Detection#DETECTION_AREAS.DetectedItem DetectedItem
|
||||
-- @return Tasking.Task#TASK
|
||||
-- @return Core.Set#SET_UNIT TargetSetUnit: The target set of units.
|
||||
-- @return #nil If there are no targets to be set.
|
||||
function TASK_A2G_DISPATCHER:EvaluateBAI( DetectedItem, FriendlyCoalition )
|
||||
self:F( { DetectedItem.ItemID } )
|
||||
|
||||
@ -148,8 +152,9 @@ do -- TASK_A2G_DISPATCHER
|
||||
-- Determine if the set has radar targets. If it does, construct a SEAD task.
|
||||
local GroundUnitCount = DetectedSet:HasGroundUnits()
|
||||
local FriendliesNearBy = self.Detection:IsFriendliesNearBy( DetectedItem )
|
||||
local RadarCount = DetectedSet:HasSEAD()
|
||||
|
||||
if GroundUnitCount > 0 and FriendliesNearBy == false then
|
||||
if RadarCount == 0 and GroundUnitCount > 0 and FriendliesNearBy == false then
|
||||
|
||||
-- Copy the Set
|
||||
local TargetSetUnit = SET_UNIT:New()
|
||||
@ -162,6 +167,12 @@ do -- TASK_A2G_DISPATCHER
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
function TASK_A2G_DISPATCHER:RemoveTask( TaskIndex )
|
||||
self.Mission:RemoveTask( self.Tasks[TaskIndex] )
|
||||
self.Tasks[TaskIndex] = nil
|
||||
end
|
||||
|
||||
--- Evaluates the removal of the Task from the Mission.
|
||||
-- Can only occur when the DetectedItem is Changed AND the state of the Task is "Planned".
|
||||
-- @param #TASK_A2G_DISPATCHER self
|
||||
@ -173,10 +184,9 @@ do -- TASK_A2G_DISPATCHER
|
||||
function TASK_A2G_DISPATCHER:EvaluateRemoveTask( Mission, Task, TaskIndex, DetectedItemChanged )
|
||||
|
||||
if Task then
|
||||
if Task:IsStatePlanned() and DetectedItemChanged == true then
|
||||
if ( Task:IsStatePlanned() and DetectedItemChanged == true ) or Task:IsStateCancelled() then
|
||||
--self:E( "Removing Tasking: " .. Task:GetTaskName() )
|
||||
Mission:RemoveTask( Task )
|
||||
self.Tasks[TaskIndex] = nil
|
||||
self:RemoveTask( TaskIndex )
|
||||
end
|
||||
end
|
||||
|
||||
@ -211,6 +221,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||
Mission:GetCommandCenter():MessageToGroup( string.format( "Obsolete A2G task %s for %s removed.", TaskText, Mission:GetName() ), TaskGroup )
|
||||
end
|
||||
Task = self:RemoveTask( TaskIndex )
|
||||
Mission:RemoveTask( Task )
|
||||
self.Tasks[TaskIndex] = nil
|
||||
end
|
||||
@ -224,20 +235,119 @@ do -- TASK_A2G_DISPATCHER
|
||||
local DetectedSet = DetectedItem.Set -- Core.Set#SET_UNIT
|
||||
local DetectedZone = DetectedItem.Zone
|
||||
--self:E( { "Targets in DetectedItem", DetectedItem.ItemID, DetectedSet:Count(), tostring( DetectedItem ) } )
|
||||
DetectedSet:Flush()
|
||||
--DetectedSet:Flush()
|
||||
|
||||
local DetectedItemID = DetectedItem.ID
|
||||
local TaskIndex = DetectedItem.Index
|
||||
local DetectedItemChanged = DetectedItem.Changed
|
||||
|
||||
local Task = self.Tasks[TaskIndex]
|
||||
Task = self:EvaluateRemoveTask( Mission, Task, TaskIndex, DetectedItemChanged ) -- Task will be removed if it is planned and changed.
|
||||
self:E( { DetectedItemChanged = DetectedItemChanged, DetectedItemID = DetectedItemID, TaskIndex = TaskIndex } )
|
||||
|
||||
local Task = self.Tasks[TaskIndex] -- Tasking.Task_A2G#TASK_A2G
|
||||
|
||||
if Task then
|
||||
-- If there is a Task and the task was assigned, then we check if the task was changed ... If it was, we need to reevaluate the targets.
|
||||
if Task:IsStateAssigned() then
|
||||
if DetectedItemChanged == true then -- The detection has changed, thus a new TargetSet is to be evaluated and set
|
||||
local TargetsReport = REPORT:New()
|
||||
local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed...
|
||||
if TargetSetUnit then
|
||||
if Task:IsInstanceOf( TASK_A2G_SEAD ) then
|
||||
Task:SetTargetSetUnit( TargetSetUnit )
|
||||
Task:UpdateTaskInfo()
|
||||
TargetsReport:Add( Detection:GetChangeText( DetectedItem ) )
|
||||
else
|
||||
Task:Cancel()
|
||||
end
|
||||
else
|
||||
local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed...
|
||||
if TargetSetUnit then
|
||||
if Task:IsInstanceOf( TASK_A2G_CAS ) then
|
||||
Task:SetTargetSetUnit( TargetSetUnit )
|
||||
Task:SetDetection( Detection, TaskIndex )
|
||||
Task:UpdateTaskInfo()
|
||||
TargetsReport:Add( Detection:GetChangeText( DetectedItem ) )
|
||||
else
|
||||
Task:Cancel()
|
||||
Task = self:RemoveTask( TaskIndex )
|
||||
end
|
||||
else
|
||||
local TargetSetUnit = self:EvaluateBAI( DetectedItem ) -- Returns a SetUnit if there are targets to be BAIed...
|
||||
if TargetSetUnit then
|
||||
if Task:IsInstanceOf( TASK_A2G_BAI ) then
|
||||
Task:SetTargetSetUnit( TargetSetUnit )
|
||||
Task:SetDetection( Detection, TaskIndex )
|
||||
Task:UpdateTaskInfo()
|
||||
TargetsReport:Add( Detection:GetChangeText( DetectedItem ) )
|
||||
else
|
||||
Task:Cancel()
|
||||
Task = self:RemoveTask( TaskIndex )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Now we send to each group the changes, if any.
|
||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||
local TargetsText = TargetsReport:Text(", ")
|
||||
if ( Mission:IsGroupAssigned(TaskGroup) ) and TargetsText ~= "" then
|
||||
Mission:GetCommandCenter():MessageToGroup( string.format( "Task %s has change of targets:\n %s", Task:GetName(), TargetsText ), TaskGroup )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Task then
|
||||
if Task:IsStatePlanned() then
|
||||
if DetectedItemChanged == true then -- The detection has changed, thus a new TargetSet is to be evaluated and set
|
||||
if Task:IsInstanceOf( TASK_A2G_SEAD ) then
|
||||
local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed...
|
||||
if TargetSetUnit then
|
||||
Task:SetTargetSetUnit( TargetSetUnit )
|
||||
Task:UpdateTaskInfo()
|
||||
else
|
||||
Task:Cancel()
|
||||
Task = self:RemoveTask( TaskIndex )
|
||||
end
|
||||
else
|
||||
if Task:IsInstanceOf( TASK_A2G_CAS ) then
|
||||
local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed...
|
||||
if TargetSetUnit then
|
||||
Task:SetTargetSetUnit( TargetSetUnit )
|
||||
Task:SetDetection( Detection, TaskIndex )
|
||||
Task:UpdateTaskInfo()
|
||||
else
|
||||
Task:Cancel()
|
||||
Task = self:RemoveTask( TaskIndex )
|
||||
end
|
||||
else
|
||||
if Task:IsInstanceOf( TASK_A2G_BAI ) then
|
||||
local TargetSetUnit = self:EvaluateBAI( DetectedItem ) -- Returns a SetUnit if there are targets to be BAIed...
|
||||
if TargetSetUnit then
|
||||
Task:SetTargetSetUnit( TargetSetUnit )
|
||||
Task:SetDetection( Detection, TaskIndex )
|
||||
Task:UpdateTaskInfo()
|
||||
else
|
||||
Task:Cancel()
|
||||
Task = self:RemoveTask( TaskIndex )
|
||||
end
|
||||
else
|
||||
Task:Cancel()
|
||||
Task = self:RemoveTask( TaskIndex )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Evaluate SEAD
|
||||
if not Task then
|
||||
local TargetSetUnit = self:EvaluateSEAD( DetectedItem ) -- Returns a SetUnit if there are targets to be SEADed...
|
||||
if TargetSetUnit then
|
||||
Task = TASK_A2G_SEAD:New( Mission, self.SetGroup, string.format( "SEAD.%03d", DetectedItemID ), TargetSetUnit )
|
||||
Task:SetDetection( Detection, TaskIndex )
|
||||
end
|
||||
|
||||
-- Evaluate CAS
|
||||
@ -245,6 +355,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
local TargetSetUnit = self:EvaluateCAS( DetectedItem ) -- Returns a SetUnit if there are targets to be CASed...
|
||||
if TargetSetUnit then
|
||||
Task = TASK_A2G_CAS:New( Mission, self.SetGroup, string.format( "CAS.%03d", DetectedItemID ), TargetSetUnit )
|
||||
Task:SetDetection( Detection, TaskIndex )
|
||||
end
|
||||
|
||||
-- Evaluate BAI
|
||||
@ -252,6 +363,7 @@ do -- TASK_A2G_DISPATCHER
|
||||
local TargetSetUnit = self:EvaluateBAI( DetectedItem, self.Mission:GetCommandCenter():GetPositionable():GetCoalition() ) -- Returns a SetUnit if there are targets to be BAIed...
|
||||
if TargetSetUnit then
|
||||
Task = TASK_A2G_BAI:New( Mission, self.SetGroup, string.format( "BAI.%03d", DetectedItemID ), TargetSetUnit )
|
||||
Task:SetDetection( Detection, TaskIndex )
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -260,13 +372,13 @@ do -- TASK_A2G_DISPATCHER
|
||||
self.Tasks[TaskIndex] = Task
|
||||
Task:SetTargetZone( DetectedZone )
|
||||
Task:SetDispatcher( self )
|
||||
Task:UpdateTaskInfo()
|
||||
Mission:AddTask( Task )
|
||||
|
||||
TaskReport:Add( Task:GetName() )
|
||||
else
|
||||
self:E("This should not happen")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@ -278,7 +390,6 @@ do -- TASK_A2G_DISPATCHER
|
||||
Mission:GetCommandCenter():SetMenu()
|
||||
|
||||
local TaskText = TaskReport:Text(", ")
|
||||
|
||||
for TaskGroupID, TaskGroup in pairs( self.SetGroup:GetSet() ) do
|
||||
if ( not Mission:IsGroupAssigned(TaskGroup) ) and TaskText ~= "" then
|
||||
Mission:GetCommandCenter():MessageToGroup( string.format( "%s has tasks %s. Subscribe to a task using the radio menu.", Mission:GetName(), TaskText ), TaskGroup )
|
||||
|
||||
@ -161,10 +161,15 @@ do -- TASK_CARGO
|
||||
self.TaskType = TaskType
|
||||
self.SmokeColor = SMOKECOLOR.Red
|
||||
|
||||
self.CargoItemCount = {} -- Map of Carriers having a cargo item count to check the cargo loading limits.
|
||||
self.CargoLimit = 2
|
||||
|
||||
self.DeployZones = {} -- setmetatable( {}, { __mode = "v" } ) -- weak table on value
|
||||
|
||||
|
||||
local Fsm = self:GetUnitProcess()
|
||||
|
||||
Fsm:SetStartState( "Planned" )
|
||||
|
||||
Fsm:AddProcess ( "Planned", "Accept", ACT_ASSIGN_ACCEPT:New( self.TaskBriefing ), { Assigned = "SelectAction", Rejected = "Reject" } )
|
||||
|
||||
@ -202,12 +207,19 @@ do -- TASK_CARGO
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_CARGO#TASK_CARGO Task
|
||||
function Fsm:onafterSelectAction( TaskUnit, Task )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
local TaskUnitName = TaskUnit:GetName()
|
||||
|
||||
self:E( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
local MenuTime = timer.getTime()
|
||||
|
||||
TaskUnit.Menu = MENU_GROUP:New( TaskUnit:GetGroup(), Task:GetName() .. " @ " .. TaskUnit:GetName() ):SetTime( MenuTime )
|
||||
|
||||
local CargoItemCount = TaskUnit:CargoItemCount()
|
||||
|
||||
--Task:GetMission():GetCommandCenter():MessageToGroup( "Cargo in carrier: " .. CargoItemCount, TaskUnit:GetGroup() )
|
||||
|
||||
|
||||
Task.SetCargo:ForEachCargo(
|
||||
|
||||
@ -226,51 +238,37 @@ do -- TASK_CARGO
|
||||
-- Cargo
|
||||
-- ):SetTime(MenuTime)
|
||||
-- end
|
||||
|
||||
|
||||
|
||||
if Cargo:IsUnLoaded() then
|
||||
if Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then
|
||||
MENU_GROUP_COMMAND:New(
|
||||
TaskUnit:GetGroup(),
|
||||
"Board cargo " .. Cargo.Name,
|
||||
TaskUnit.Menu,
|
||||
self.MenuBoardCargo,
|
||||
self,
|
||||
Cargo
|
||||
):SetTime(MenuTime)
|
||||
else
|
||||
MENU_GROUP_COMMAND:New(
|
||||
TaskUnit:GetGroup(),
|
||||
"Route to Pickup cargo " .. Cargo.Name,
|
||||
TaskUnit.Menu,
|
||||
self.MenuRouteToPickup,
|
||||
self,
|
||||
Cargo
|
||||
):SetTime(MenuTime)
|
||||
if CargoItemCount < Task.CargoLimit then
|
||||
if Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then
|
||||
local NotInDeployZones = true
|
||||
for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do
|
||||
if Cargo:IsInZone( DeployZone ) then
|
||||
NotInDeployZones = false
|
||||
end
|
||||
end
|
||||
if NotInDeployZones then
|
||||
if not TaskUnit:InAir() then
|
||||
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Board cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuBoardCargo, self, Cargo ):SetTime(MenuTime)
|
||||
end
|
||||
end
|
||||
else
|
||||
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Route to Pickup cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuRouteToPickup, self, Cargo ):SetTime(MenuTime)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Cargo:IsLoaded() then
|
||||
|
||||
MENU_GROUP_COMMAND:New(
|
||||
TaskUnit:GetGroup(),
|
||||
"Unboard cargo " .. Cargo.Name,
|
||||
TaskUnit.Menu,
|
||||
self.MenuUnBoardCargo,
|
||||
self,
|
||||
Cargo
|
||||
):SetTime(MenuTime)
|
||||
|
||||
if not TaskUnit:InAir() then
|
||||
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Unboard cargo " .. Cargo.Name, TaskUnit.Menu, self.MenuUnBoardCargo, self, Cargo ):SetTime(MenuTime)
|
||||
end
|
||||
-- Deployzones are optional zones that can be selected to request routing information.
|
||||
for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do
|
||||
if not Cargo:IsInZone( DeployZone ) then
|
||||
MENU_GROUP_COMMAND:New(
|
||||
TaskUnit:GetGroup(),
|
||||
"Route to Deploy cargo at " .. DeployZoneName,
|
||||
TaskUnit.Menu,
|
||||
self.MenuRouteToDeploy,
|
||||
self,
|
||||
DeployZone
|
||||
):SetTime(MenuTime)
|
||||
MENU_GROUP_COMMAND:New( TaskUnit:GetGroup(), "Route to Deploy cargo at " .. DeployZoneName, TaskUnit.Menu, self.MenuRouteToDeploy, self, DeployZone ):SetTime(MenuTime)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -284,9 +282,9 @@ do -- TASK_CARGO
|
||||
|
||||
self:__SelectAction( -15 )
|
||||
|
||||
--Task:GetMission():GetCommandCenter():MessageToGroup("Cargo menu is ready ...", TaskUnit:GetGroup() )
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
@ -320,8 +318,7 @@ do -- TASK_CARGO
|
||||
--#Wrapper.Unit#UNIT
|
||||
|
||||
|
||||
--- Route to Cargo
|
||||
-- @param #FSM_PROCESS self
|
||||
--- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
-- @param From
|
||||
@ -341,15 +338,15 @@ do -- TASK_CARGO
|
||||
|
||||
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
--- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterArriveAtPickup( TaskUnit, Task )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
if self.Cargo:IsAlive() then
|
||||
TaskUnit:Smoke( Task:GetSmokeColor(), 15 )
|
||||
self.Cargo:Smoke( Task:GetSmokeColor(), 15 )
|
||||
if TaskUnit:IsAir() then
|
||||
Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() )
|
||||
self:__Land( -0.1, "Pickup" )
|
||||
else
|
||||
self:__SelectAction( -0.1 )
|
||||
@ -358,8 +355,7 @@ do -- TASK_CARGO
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
--- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterCancelRouteToPickup( TaskUnit, Task )
|
||||
@ -369,8 +365,7 @@ do -- TASK_CARGO
|
||||
end
|
||||
|
||||
|
||||
--- Route to DeployZone
|
||||
-- @param #FSM_PROCESS self
|
||||
--- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
function Fsm:onafterRouteToDeploy( TaskUnit, Task, From, Event, To, DeployZone )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
@ -382,14 +377,14 @@ do -- TASK_CARGO
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
--- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterArriveAtDeploy( TaskUnit, Task )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
if TaskUnit:IsAir() then
|
||||
Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() )
|
||||
self:__Land( -0.1, "Deploy" )
|
||||
else
|
||||
self:__SelectAction( -0.1 )
|
||||
@ -397,8 +392,7 @@ do -- TASK_CARGO
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
--- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterCancelRouteToDeploy( TaskUnit, Task )
|
||||
@ -409,7 +403,7 @@ do -- TASK_CARGO
|
||||
|
||||
|
||||
|
||||
-- @param #FSM_PROCESS self
|
||||
--- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterLand( TaskUnit, Task, From, Event, To, Action )
|
||||
@ -418,7 +412,6 @@ do -- TASK_CARGO
|
||||
if self.Cargo:IsAlive() then
|
||||
if self.Cargo:IsInRadius( TaskUnit:GetPointVec2() ) then
|
||||
if TaskUnit:InAir() then
|
||||
Task:GetMission():GetCommandCenter():MessageToGroup( "Land", TaskUnit:GetGroup() )
|
||||
self:__Land( -10, Action )
|
||||
else
|
||||
Task:GetMission():GetCommandCenter():MessageToGroup( "Landed ...", TaskUnit:GetGroup() )
|
||||
@ -434,8 +427,7 @@ do -- TASK_CARGO
|
||||
end
|
||||
end
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
--- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterLanded( TaskUnit, Task, From, Event, To, Action )
|
||||
@ -458,8 +450,7 @@ do -- TASK_CARGO
|
||||
end
|
||||
end
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
--- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterPrepareBoarding( TaskUnit, Task, From, Event, To, Cargo )
|
||||
@ -471,8 +462,7 @@ do -- TASK_CARGO
|
||||
end
|
||||
end
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
--- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterBoard( TaskUnit, Task )
|
||||
@ -498,14 +488,18 @@ do -- TASK_CARGO
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #FSM_PROCESS self
|
||||
--- @param #FSM_PROCESS self
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterBoarded( TaskUnit, Task )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
local TaskUnitName = TaskUnit:GetName()
|
||||
self:E( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
self.Cargo:MessageToGroup( "Boarded ...", TaskUnit:GetGroup() )
|
||||
|
||||
TaskUnit:AddCargo( self.Cargo )
|
||||
|
||||
self:__SelectAction( 1 )
|
||||
|
||||
-- TODO:I need to find a more decent solution for this.
|
||||
@ -579,12 +573,28 @@ do -- TASK_CARGO
|
||||
-- @param Wrapper.Unit#UNIT TaskUnit
|
||||
-- @param Tasking.Task_Cargo#TASK_CARGO Task
|
||||
function Fsm:onafterUnBoarded( TaskUnit, Task )
|
||||
self:E( { TaskUnit = TaskUnit, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
local TaskUnitName = TaskUnit:GetName()
|
||||
self:E( { TaskUnit = TaskUnitName, Task = Task and Task:GetClassNameAndID() } )
|
||||
|
||||
self.Cargo:MessageToGroup( "UnBoarded ...", TaskUnit:GetGroup() )
|
||||
|
||||
TaskUnit:RemoveCargo( self.Cargo )
|
||||
|
||||
local NotInDeployZones = true
|
||||
for DeployZoneName, DeployZone in pairs( Task.DeployZones ) do
|
||||
if self.Cargo:IsInZone( DeployZone ) then
|
||||
NotInDeployZones = false
|
||||
end
|
||||
end
|
||||
|
||||
if NotInDeployZones == false then
|
||||
self.Cargo:SetDeployed( true )
|
||||
end
|
||||
|
||||
-- TODO:I need to find a more decent solution for this.
|
||||
Task:E( { CargoDeployed = Task.CargoDeployed } )
|
||||
Task:E( { CargoDeployed = Task.CargoDeployed and "true" or "false" } )
|
||||
Task:E( { CargoIsAlive = self.Cargo:IsAlive() and "true" or "false" } )
|
||||
if self.Cargo:IsAlive() then
|
||||
if Task.CargoDeployed then
|
||||
Task:CargoDeployed( TaskUnit, self.Cargo, self.DeployZone )
|
||||
@ -598,7 +608,17 @@ do -- TASK_CARGO
|
||||
return self
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Set a limit on the amount of cargo items that can be loaded into the Carriers.
|
||||
-- @param #TASK_CARGO self
|
||||
-- @param CargoLimit Specifies a number of cargo items that can be loaded in the helicopter.
|
||||
-- @return #TASK_CARGO
|
||||
function TASK_CARGO:SetCargoLimit( CargoLimit )
|
||||
self.CargoLimit = CargoLimit
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
---@param Color Might be SMOKECOLOR.Blue, SMOKECOLOR.Red SMOKECOLOR.Orange, SMOKECOLOR.White or SMOKECOLOR.Green
|
||||
function TASK_CARGO:SetSmokeColor(SmokeColor)
|
||||
@ -770,6 +790,17 @@ do -- TASK_CARGO
|
||||
return self
|
||||
end
|
||||
|
||||
function TASK_CARGO:SetGoalTotal()
|
||||
|
||||
self.GoalTotal = self.SetCargo:Count()
|
||||
end
|
||||
|
||||
function TASK_CARGO:GetGoalTotal()
|
||||
|
||||
return self.GoalTotal
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
@ -802,6 +833,8 @@ do -- TASK_CARGO_TRANSPORT
|
||||
self:AddTransition( "*", "CargoPickedUp", "*" )
|
||||
self:AddTransition( "*", "CargoDeployed", "*" )
|
||||
|
||||
self:E( { CargoDeployed = self.CargoDeployed ~= nil and "true" or "false" } )
|
||||
|
||||
--- OnBefore Transition Handler for Event CargoPickedUp.
|
||||
-- @function [parent=#TASK_CARGO_TRANSPORT] OnBeforeCargoPickedUp
|
||||
-- @param #TASK_CARGO_TRANSPORT self
|
||||
@ -880,7 +913,7 @@ do -- TASK_CARGO_TRANSPORT
|
||||
local CargoType = Cargo:GetType()
|
||||
local CargoName = Cargo:GetName()
|
||||
local CargoCoordinate = Cargo:GetCoordinate()
|
||||
CargoReport:Add( string.format( '- "%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToString() ) )
|
||||
CargoReport:Add( string.format( '- "%s" (%s) at %s', CargoName, CargoType, CargoCoordinate:ToStringMGRS() ) )
|
||||
end
|
||||
)
|
||||
|
||||
@ -892,6 +925,12 @@ do -- TASK_CARGO_TRANSPORT
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
function TASK_CARGO_TRANSPORT:ReportOrder( ReportGroup )
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
-- @param #TASK_CARGO_TRANSPORT self
|
||||
@ -908,27 +947,36 @@ do -- TASK_CARGO_TRANSPORT
|
||||
-- Loop the CargoSet (so evaluate each Cargo in the SET_CARGO ).
|
||||
for CargoID, CargoData in pairs( Set ) do
|
||||
local Cargo = CargoData -- Core.Cargo#CARGO
|
||||
|
||||
if Cargo:IsDeployed() then
|
||||
|
||||
-- Loop the DeployZones set for the TASK_CARGO_TRANSPORT.
|
||||
for DeployZoneID, DeployZone in pairs( DeployZones ) do
|
||||
|
||||
-- If there is a Cargo not in one of DeployZones, then not all Cargo is deployed.
|
||||
self:T( { Cargo.CargoObject } )
|
||||
if Cargo:IsInZone( DeployZone ) then
|
||||
else
|
||||
CargoDeployed = false
|
||||
-- Loop the DeployZones set for the TASK_CARGO_TRANSPORT.
|
||||
for DeployZoneID, DeployZone in pairs( DeployZones ) do
|
||||
|
||||
-- If there is a Cargo not in one of DeployZones, then not all Cargo is deployed.
|
||||
self:T( { Cargo.CargoObject } )
|
||||
if Cargo:IsInZone( DeployZone ) == false then
|
||||
CargoDeployed = false
|
||||
end
|
||||
end
|
||||
else
|
||||
CargoDeployed = false
|
||||
end
|
||||
end
|
||||
|
||||
return CargoDeployed
|
||||
end
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
--- @param #TASK_CARGO_TRANSPORT self
|
||||
function TASK_CARGO_TRANSPORT:onafterGoal( TaskUnit, From, Event, To )
|
||||
local CargoSet = self.CargoSet
|
||||
|
||||
if self:IsAllCargoTransported() then
|
||||
self:Success()
|
||||
end
|
||||
|
||||
self:__Goal( -10 )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
@ -31,7 +31,72 @@ FLARECOLOR = trigger.flareColor -- #FLARECOLOR
|
||||
|
||||
--- Utilities static class.
|
||||
-- @type UTILS
|
||||
UTILS = {}
|
||||
UTILS = {
|
||||
_MarkID = 1
|
||||
}
|
||||
|
||||
--- Function to infer instance of an object
|
||||
--
|
||||
-- ### Examples:
|
||||
--
|
||||
-- * UTILS.IsInstanceOf( 'some text', 'string' ) will return true
|
||||
-- * UTILS.IsInstanceOf( some_function, 'function' ) will return true
|
||||
-- * UTILS.IsInstanceOf( 10, 'number' ) will return true
|
||||
-- * UTILS.IsInstanceOf( false, 'boolean' ) will return true
|
||||
-- * UTILS.IsInstanceOf( nil, 'nil' ) will return true
|
||||
--
|
||||
-- * UTILS.IsInstanceOf( ZONE:New( 'some zone', ZONE ) will return true
|
||||
-- * UTILS.IsInstanceOf( ZONE:New( 'some zone', 'ZONE' ) will return true
|
||||
-- * UTILS.IsInstanceOf( ZONE:New( 'some zone', 'zone' ) will return true
|
||||
-- * UTILS.IsInstanceOf( ZONE:New( 'some zone', 'BASE' ) will return true
|
||||
--
|
||||
-- * UTILS.IsInstanceOf( ZONE:New( 'some zone', 'GROUP' ) will return false
|
||||
--
|
||||
--
|
||||
-- @param object is the object to be evaluated
|
||||
-- @param className is the name of the class to evaluate (can be either a string or a Moose class)
|
||||
-- @return #boolean
|
||||
UTILS.IsInstanceOf = function( object, className )
|
||||
-- Is className NOT a string ?
|
||||
if not type( className ) == 'string' then
|
||||
|
||||
-- Is className a Moose class ?
|
||||
if type( className ) == 'table' and className.IsInstanceOf ~= nil then
|
||||
|
||||
-- Get the name of the Moose class as a string
|
||||
className = className.ClassName
|
||||
|
||||
-- className is neither a string nor a Moose class, throw an error
|
||||
else
|
||||
|
||||
-- I'm not sure if this should take advantage of MOOSE logging function, or throw an error for pcall
|
||||
local err_str = 'className parameter should be a string; parameter received: '..type( className )
|
||||
self:E( err_str )
|
||||
return false
|
||||
-- error( err_str )
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
-- Is the object a Moose class instance ?
|
||||
if type( object ) == 'table' and object.IsInstanceOf ~= nil then
|
||||
|
||||
-- Use the IsInstanceOf method of the BASE class
|
||||
return object:IsInstanceOf( className )
|
||||
else
|
||||
|
||||
-- If the object is not an instance of a Moose class, evaluate against lua basic data types
|
||||
local basicDataTypes = { 'string', 'number', 'function', 'boolean', 'nil', 'table' }
|
||||
for _, basicDataType in ipairs( basicDataTypes ) do
|
||||
if className == basicDataType then
|
||||
return type( object ) == basicDataType
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Check failed
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
--from http://lua-users.org/wiki/CopyTable
|
||||
@ -171,22 +236,36 @@ UTILS.FeetToMeters = function(feet)
|
||||
return feet*0.3048
|
||||
end
|
||||
|
||||
UTILS.MpsToKnots = function(mps)
|
||||
return mps*3600/1852
|
||||
UTILS.KnotsToKmph = function(knots)
|
||||
return knots* 1.852
|
||||
end
|
||||
|
||||
UTILS.MpsToKmph = function(mps)
|
||||
return mps*3.6
|
||||
UTILS.KmphToMps = function( kmph )
|
||||
return kmph / 3.6
|
||||
end
|
||||
|
||||
UTILS.KnotsToMps = function(knots)
|
||||
return knots*1852/3600
|
||||
UTILS.MpsToKmph = function( mps )
|
||||
return mps * 3.6
|
||||
end
|
||||
|
||||
UTILS.KmphToMps = function(kmph)
|
||||
return kmph/3.6
|
||||
UTILS.MiphToMps = function( miph )
|
||||
return miph * 0.44704
|
||||
end
|
||||
|
||||
UTILS.MpsToMiph = function( mps )
|
||||
return mps / 0.44704
|
||||
end
|
||||
|
||||
UTILS.MpsToKnots = function( mps )
|
||||
return mps * 3600 / 1852
|
||||
end
|
||||
|
||||
UTILS.KnotsToMps = function( knots )
|
||||
return knots * 1852 / 3600
|
||||
end
|
||||
|
||||
|
||||
|
||||
--[[acc:
|
||||
in DM: decimal point of minutes.
|
||||
In DMS: decimal point of seconds.
|
||||
@ -238,12 +317,13 @@ UTILS.tostringLL = function( lat, lon, acc, DMS)
|
||||
end
|
||||
|
||||
local secFrmtStr -- create the formatting string for the seconds place
|
||||
if acc <= 0 then -- no decimal place.
|
||||
secFrmtStr = '%02d'
|
||||
else
|
||||
local width = 3 + acc -- 01.310 - that's a width of 6, for example.
|
||||
secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
|
||||
end
|
||||
secFrmtStr = '%02d'
|
||||
-- if acc <= 0 then -- no decimal place.
|
||||
-- secFrmtStr = '%02d'
|
||||
-- else
|
||||
-- local width = 3 + acc -- 01.310 - that's a width of 6, for example.
|
||||
-- secFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
|
||||
-- end
|
||||
|
||||
return string.format('%02d', latDeg) .. ' ' .. string.format('%02d', latMin) .. '\' ' .. string.format(secFrmtStr, latSec) .. '"' .. latHemi .. ' '
|
||||
.. string.format('%02d', lonDeg) .. ' ' .. string.format('%02d', lonMin) .. '\' ' .. string.format(secFrmtStr, lonSec) .. '"' .. lonHemi
|
||||
@ -327,3 +407,28 @@ function UTILS.spairs( t, order )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- get a new mark ID for markings
|
||||
function UTILS.GetMarkID()
|
||||
|
||||
UTILS._MarkID = UTILS._MarkID + 1
|
||||
return UTILS._MarkID
|
||||
|
||||
end
|
||||
|
||||
|
||||
-- Test if a Vec2 is in a radius of another Vec2
|
||||
function UTILS.IsInRadius( InVec2, Vec2, Radius )
|
||||
|
||||
local InRadius = ( ( InVec2.x - Vec2.x ) ^2 + ( InVec2.y - Vec2.y ) ^2 ) ^ 0.5 <= Radius
|
||||
|
||||
return InRadius
|
||||
end
|
||||
|
||||
-- Test if a Vec3 is in the sphere of another Vec3
|
||||
function UTILS.IsInSphere( InVec3, Vec3, Radius )
|
||||
|
||||
local InSphere = ( ( InVec3.x - Vec3.x ) ^2 + ( InVec3.y - Vec3.y ) ^2 + ( InVec3.z - Vec3.z ) ^2 ) ^ 0.5 <= Radius
|
||||
|
||||
return InSphere
|
||||
end
|
||||
|
||||
@ -57,7 +57,33 @@ AIRBASE = {
|
||||
},
|
||||
}
|
||||
|
||||
--- @field Caucasus
|
||||
--- Enumeration to identify the airbases in the Caucasus region.
|
||||
--
|
||||
-- These are all airbases of Caucasus:
|
||||
--
|
||||
-- * AIRBASE.Caucasus.Gelendzhik
|
||||
-- * AIRBASE.Caucasus.Krasnodar_Pashkovsky
|
||||
-- * AIRBASE.Caucasus.Sukhumi_Babushara
|
||||
-- * AIRBASE.Caucasus.Gudauta
|
||||
-- * AIRBASE.Caucasus.Batumi
|
||||
-- * AIRBASE.Caucasus.Senaki_Kolkhi
|
||||
-- * AIRBASE.Caucasus.Kobuleti
|
||||
-- * AIRBASE.Caucasus.Kutaisi
|
||||
-- * AIRBASE.Caucasus.Tbilisi_Lochini
|
||||
-- * AIRBASE.Caucasus.Soganlug
|
||||
-- * AIRBASE.Caucasus.Vaziani
|
||||
-- * AIRBASE.Caucasus.Anapa_Vityazevo
|
||||
-- * AIRBASE.Caucasus.Krasnodar_Center
|
||||
-- * AIRBASE.Caucasus.Novorossiysk
|
||||
-- * AIRBASE.Caucasus.Krymsk
|
||||
-- * AIRBASE.Caucasus.Maykop_Khanskaya
|
||||
-- * AIRBASE.Caucasus.Sochi_Adler
|
||||
-- * AIRBASE.Caucasus.Mineralnye_Vody
|
||||
-- * AIRBASE.Caucasus.Nalchik
|
||||
-- * AIRBASE.Caucasus.Mozdok
|
||||
-- * AIRBASE.Caucasus.Beslan
|
||||
--
|
||||
-- @field Caucasus
|
||||
AIRBASE.Caucasus = {
|
||||
["Gelendzhik"] = "Gelendzhik",
|
||||
["Krasnodar_Pashkovsky"] = "Krasnodar-Pashkovsky",
|
||||
@ -83,6 +109,28 @@ AIRBASE.Caucasus = {
|
||||
}
|
||||
|
||||
--- @field Nevada
|
||||
--
|
||||
-- These are all airbases of Nevada:
|
||||
--
|
||||
-- * AIRBASE.Nevada.Creech_AFB
|
||||
-- * AIRBASE.Nevada.Groom_Lake_AFB
|
||||
-- * AIRBASE.Nevada.McCarran_International_Airport
|
||||
-- * AIRBASE.Nevada.Nellis_AFB
|
||||
-- * AIRBASE.Nevada.Beatty_Airport
|
||||
-- * AIRBASE.Nevada.Boulder_City_Airport
|
||||
-- * AIRBASE.Nevada.Echo_Bay
|
||||
-- * AIRBASE.Nevada.Henderson_Executive_Airport
|
||||
-- * AIRBASE.Nevada.Jean_Airport
|
||||
-- * AIRBASE.Nevada.Laughlin_Airport
|
||||
-- * AIRBASE.Nevada.Lincoln_County
|
||||
-- * AIRBASE.Nevada.Mellan_Airstrip
|
||||
-- * AIRBASE.Nevada.Mesquite
|
||||
-- * AIRBASE.Nevada.Mina_Airport_3Q0
|
||||
-- * AIRBASE.Nevada.North_Las_Vegas
|
||||
-- * AIRBASE.Nevada.Pahute_Mesa_Airstrip
|
||||
-- * AIRBASE.Nevada.Tonopah_Airport
|
||||
-- * AIRBASE.Nevada.Tonopah_Test_Range_Airfield
|
||||
--
|
||||
AIRBASE.Nevada = {
|
||||
["Creech_AFB"] = "Creech AFB",
|
||||
["Groom_Lake_AFB"] = "Groom Lake AFB",
|
||||
@ -105,6 +153,40 @@ AIRBASE.Nevada = {
|
||||
}
|
||||
|
||||
--- @field Normandy
|
||||
--
|
||||
-- These are all airbases of Normandy:
|
||||
--
|
||||
-- * AIRBASE.Normandy.Saint_Pierre_du_Mont
|
||||
-- * AIRBASE.Normandy.Lignerolles
|
||||
-- * AIRBASE.Normandy.Cretteville
|
||||
-- * AIRBASE.Normandy.Maupertus
|
||||
-- * AIRBASE.Normandy.Brucheville
|
||||
-- * AIRBASE.Normandy.Meautis
|
||||
-- * AIRBASE.Normandy.Cricqueville_en_Bessin
|
||||
-- * AIRBASE.Normandy.Lessay
|
||||
-- * AIRBASE.Normandy.Sainte_Laurent_sur_Mer
|
||||
-- * AIRBASE.Normandy.Biniville
|
||||
-- * AIRBASE.Normandy.Cardonville
|
||||
-- * AIRBASE.Normandy.Deux_Jumeaux
|
||||
-- * AIRBASE.Normandy.Chippelle
|
||||
-- * AIRBASE.Normandy.Beuzeville
|
||||
-- * AIRBASE.Normandy.Azeville
|
||||
-- * AIRBASE.Normandy.Picauville
|
||||
-- * AIRBASE.Normandy.Le_Molay
|
||||
-- * AIRBASE.Normandy.Longues_sur_Mer
|
||||
-- * AIRBASE.Normandy.Carpiquet
|
||||
-- * AIRBASE.Normandy.Bazenville
|
||||
-- * AIRBASE.Normandy.Sainte_Croix_sur_Mer
|
||||
-- * AIRBASE.Normandy.Beny_sur_Mer
|
||||
-- * AIRBASE.Normandy.Rucqueville
|
||||
-- * AIRBASE.Normandy.Sommervieu
|
||||
-- * AIRBASE.Normandy.Lantheuil
|
||||
-- * AIRBASE.Normandy.Evreux
|
||||
-- * AIRBASE.Normandy.Chailey
|
||||
-- * AIRBASE.Normandy.Needs_Oar_Point
|
||||
-- * AIRBASE.Normandy.Funtington
|
||||
-- * AIRBASE.Normandy.Tangmere
|
||||
-- * AIRBASE.Normandy.Ford
|
||||
AIRBASE.Normandy = {
|
||||
["Saint_Pierre_du_Mont"] = "Saint Pierre du Mont",
|
||||
["Lignerolles"] = "Lignerolles",
|
||||
@ -149,6 +231,7 @@ function AIRBASE:Register( AirbaseName )
|
||||
|
||||
local self = BASE:Inherit( self, POSITIONABLE:New( AirbaseName ) )
|
||||
self.AirbaseName = AirbaseName
|
||||
self.AirbaseZone = ZONE_RADIUS:New( AirbaseName, self:GetVec2(), 2500 )
|
||||
return self
|
||||
end
|
||||
|
||||
@ -185,5 +268,12 @@ function AIRBASE:GetDCSObject()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Get the airbase zone.
|
||||
-- @param #AIRBASE self
|
||||
-- @return Core.Zone#ZONE_RADIUS The zone radius of the airbase.
|
||||
function AIRBASE:GetZone()
|
||||
return self.AirbaseZone
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
@ -95,6 +95,22 @@
|
||||
-- * @{#CONTROLLABLE.TaskCondition}: Return a condition section for a controlled task.
|
||||
-- * @{#CONTROLLABLE.TaskControlled}: Return a Controlled Task taking a Task and a TaskCondition.
|
||||
--
|
||||
-- ### Call a function as a Task
|
||||
--
|
||||
-- A function can be called which is part of a Task. The method @{#CONTROLLABLE.TaskFunction}() prepares
|
||||
-- a Task that can call a GLOBAL function from within the Controller execution.
|
||||
-- This method can also be used to **embed a function call when a certain waypoint has been reached**.
|
||||
-- See below the **Tasks at Waypoints** section.
|
||||
--
|
||||
-- Demonstration Mission: [GRP-502 - Route at waypoint to random point](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/release-2-2-pre/GRP - Group Commands/GRP-502 - Route at waypoint to random point)
|
||||
--
|
||||
-- ### Tasks at Waypoints
|
||||
--
|
||||
-- Special Task methods are available to set tasks at certain waypoints.
|
||||
-- The method @{#CONTROLLABLE.SetTaskWaypoint}() helps preparing a Route, embedding a Task at the Waypoint of the Route.
|
||||
--
|
||||
-- This creates a Task element, with an action to call a function as part of a Wrapped Task.
|
||||
--
|
||||
-- ### Obtain the mission from controllable templates
|
||||
--
|
||||
-- Controllable templates contain complete mission descriptions. Sometimes you want to copy a complete mission from a controllable and assign it to another:
|
||||
@ -108,7 +124,15 @@
|
||||
-- * @{#CONTROLLABLE.CommandDoScript}: Do Script command.
|
||||
-- * @{#CONTROLLABLE.CommandSwitchWayPoint}: Perform a switch waypoint command.
|
||||
--
|
||||
-- ## CONTROLLABLE Option methods
|
||||
-- ## Routing of Controllables
|
||||
--
|
||||
-- Different routing methods exist to route GROUPs and UNITs to different locations:
|
||||
--
|
||||
-- * @{#CONTROLLABLE.Route}(): Make the Controllable to follow a given route.
|
||||
-- * @{#CONTROLLABLE.RouteGroundTo}(): Make the GROUND Controllable to drive towards a specific coordinate.
|
||||
-- * @{#CONTROLLABLE.RouteAirTo}(): Make the AIR Controllable to fly towards a specific coordinate.
|
||||
--
|
||||
-- ## Option methods
|
||||
--
|
||||
-- Controllable **Option methods** change the behaviour of the Controllable while being alive.
|
||||
--
|
||||
@ -257,6 +281,16 @@ function CONTROLLABLE:GetLife0()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks.
|
||||
-- This method returns nil to ensure polymorphic behaviour! This method needs to be overridden by GROUP or UNIT.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #nil The CONTROLLABLE is not existing or alive.
|
||||
function CONTROLLABLE:GetFuel()
|
||||
self:F( self.ControllableName )
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
@ -334,16 +368,25 @@ function CONTROLLABLE:SetTask( DCSTask, WaitTime )
|
||||
|
||||
if DCSControllable then
|
||||
|
||||
local Controller = self:_GetController()
|
||||
local DCSControllableName = self:GetName()
|
||||
|
||||
-- When a controllable SPAWNs, it takes about a second to get the controllable in the simulator. Setting tasks to unspawned controllables provides unexpected results.
|
||||
-- Therefore we schedule the functions to set the mission and options for the Controllable.
|
||||
-- Controller.setTask( Controller, DCSTask )
|
||||
|
||||
if not WaitTime then
|
||||
Controller:setTask( DCSTask )
|
||||
local function SetTask( Controller, DCSTask )
|
||||
if self and self:IsAlive() then
|
||||
local Controller = self:_GetController()
|
||||
Controller:setTask( DCSTask )
|
||||
else
|
||||
BASE:E( DCSControllableName .. " is not alive anymore. Cannot set DCSTask " .. DCSTask )
|
||||
end
|
||||
end
|
||||
|
||||
if not WaitTime or WaitTime == 0 then
|
||||
SetTask( self, DCSTask )
|
||||
else
|
||||
self.TaskScheduler:Schedule( Controller, Controller.setTask, { DCSTask }, WaitTime )
|
||||
self.TaskScheduler:Schedule( self, SetTask, { DCSTask }, WaitTime )
|
||||
end
|
||||
|
||||
return self
|
||||
@ -449,11 +492,11 @@ function CONTROLLABLE:TaskWrappedAction( DCSCommand, Index )
|
||||
self:F2( { DCSCommand } )
|
||||
|
||||
local DCSTaskWrappedAction
|
||||
|
||||
|
||||
DCSTaskWrappedAction = {
|
||||
id = "WrappedAction",
|
||||
enabled = true,
|
||||
number = Index,
|
||||
number = Index or 1,
|
||||
auto = false,
|
||||
params = {
|
||||
action = DCSCommand,
|
||||
@ -464,6 +507,22 @@ function CONTROLLABLE:TaskWrappedAction( DCSCommand, Index )
|
||||
return DCSTaskWrappedAction
|
||||
end
|
||||
|
||||
--- Set a Task at a Waypoint using a Route list.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #table Waypoint The Waypoint!
|
||||
-- @param Dcs.DCSTasking.Task#Task Task The Task structure to be executed!
|
||||
-- @return Dcs.DCSTasking.Task#Task
|
||||
function CONTROLLABLE:SetTaskWaypoint( Waypoint, Task )
|
||||
|
||||
Waypoint.task = self:TaskCombo( { Task } )
|
||||
|
||||
self:T3( { Waypoint.task } )
|
||||
return Waypoint.task
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Executes a command action
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param Dcs.DCSCommand#Command DCSCommand
|
||||
@ -1480,6 +1539,84 @@ function CONTROLLABLE:TaskEmbarkToTransport( Point, Radius )
|
||||
return DCSTask
|
||||
end
|
||||
|
||||
--- This creates a Task element, with an action to call a function as part of a Wrapped Task.
|
||||
-- This Task can then be embedded at a Waypoint by calling the method @{#CONTROLLABLE.SetTaskWaypoint}.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #string FunctionString The function name embedded as a string that will be called.
|
||||
-- @param ... The variable arguments passed to the function when called! These arguments can be of any type!
|
||||
-- @return #CONTROLLABLE
|
||||
-- @usage
|
||||
--
|
||||
-- local ZoneList = {
|
||||
-- ZONE:New( "ZONE1" ),
|
||||
-- ZONE:New( "ZONE2" ),
|
||||
-- ZONE:New( "ZONE3" ),
|
||||
-- ZONE:New( "ZONE4" ),
|
||||
-- ZONE:New( "ZONE5" )
|
||||
-- }
|
||||
--
|
||||
-- GroundGroup = GROUP:FindByName( "Vehicle" )
|
||||
--
|
||||
-- --- @param Wrapper.Group#GROUP GroundGroup
|
||||
-- function RouteToZone( Vehicle, ZoneRoute )
|
||||
--
|
||||
-- local Route = {}
|
||||
--
|
||||
-- Vehicle:E( { ZoneRoute = ZoneRoute } )
|
||||
--
|
||||
-- Vehicle:MessageToAll( "Moving to zone " .. ZoneRoute:GetName(), 10 )
|
||||
--
|
||||
-- -- Get the current coordinate of the Vehicle
|
||||
-- local FromCoord = Vehicle:GetCoordinate()
|
||||
--
|
||||
-- -- Select a random Zone and get the Coordinate of the new Zone.
|
||||
-- local RandomZone = ZoneList[ math.random( 1, #ZoneList ) ] -- Core.Zone#ZONE
|
||||
-- local ToCoord = RandomZone:GetCoordinate()
|
||||
--
|
||||
-- -- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task
|
||||
-- Route[#Route+1] = FromCoord:WaypointGround( 72 )
|
||||
-- Route[#Route+1] = ToCoord:WaypointGround( 60, "Vee" )
|
||||
--
|
||||
-- local TaskRouteToZone = Vehicle:TaskFunction( "RouteToZone", RandomZone )
|
||||
--
|
||||
-- Vehicle:SetTaskWaypoint( Route, #Route, TaskRouteToZone ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
|
||||
--
|
||||
-- Vehicle:Route( Route, math.random( 10, 20 ) ) -- Move after a random seconds to the Route. See the Route method for details.
|
||||
--
|
||||
-- end
|
||||
--
|
||||
-- RouteToZone( GroundGroup, ZoneList[1] )
|
||||
--
|
||||
function CONTROLLABLE:TaskFunction( FunctionString, ... )
|
||||
self:F2( { FunctionString, arg } )
|
||||
|
||||
local DCSTask
|
||||
|
||||
local DCSScript = {}
|
||||
DCSScript[#DCSScript+1] = "local MissionControllable = GROUP:Find( ... ) "
|
||||
|
||||
if arg and arg.n > 0 then
|
||||
local ArgumentKey = '_' .. tostring( arg ):match("table: (.*)")
|
||||
self:SetState( self, ArgumentKey, arg )
|
||||
DCSScript[#DCSScript+1] = "local Arguments = MissionControllable:GetState( MissionControllable, '" .. ArgumentKey .. "' ) "
|
||||
--DCSScript[#DCSScript+1] = "MissionControllable:ClearState( MissionControllable, '" .. ArgumentKey .. "' ) "
|
||||
DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable, unpack( Arguments ) )"
|
||||
else
|
||||
DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable )"
|
||||
end
|
||||
|
||||
DCSTask = self:TaskWrappedAction(
|
||||
self:CommandDoScript(
|
||||
table.concat( DCSScript )
|
||||
)
|
||||
)
|
||||
|
||||
self:T( DCSTask )
|
||||
|
||||
return DCSTask
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- (AIR + GROUND) Return a mission task from a mission template.
|
||||
@ -1496,6 +1633,140 @@ function CONTROLLABLE:TaskMission( TaskMission )
|
||||
return DCSTask
|
||||
end
|
||||
|
||||
|
||||
do -- Patrol methods
|
||||
|
||||
--- (GROUND) Patrol iteratively using the waypoints the for the (parent) group.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE
|
||||
function CONTROLLABLE:PatrolRoute()
|
||||
|
||||
local PatrolGroup = self -- Wrapper.Group#GROUP
|
||||
|
||||
if not self:IsInstanceOf( "GROUP" ) then
|
||||
PatrolGroup = self:GetGroup() -- Wrapper.Group#GROUP
|
||||
end
|
||||
|
||||
self:E( { PatrolGroup = PatrolGroup:GetName() } )
|
||||
|
||||
if PatrolGroup:IsGround() or PatrolGroup:IsShip() then
|
||||
|
||||
local Waypoints = PatrolGroup:GetTemplateRoutePoints()
|
||||
|
||||
-- Calculate the new Route.
|
||||
local FromCoord = PatrolGroup:GetCoordinate()
|
||||
local From = FromCoord:WaypointGround( 120 )
|
||||
|
||||
table.insert( Waypoints, 1, From )
|
||||
|
||||
local TaskRoute = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolRoute" )
|
||||
|
||||
self:E({Waypoints = Waypoints})
|
||||
local Waypoint = Waypoints[#Waypoints]
|
||||
PatrolGroup:SetTaskWaypoint( Waypoint, TaskRoute ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
|
||||
|
||||
PatrolGroup:Route( Waypoints ) -- Move after a random seconds to the Route. See the Route method for details.
|
||||
end
|
||||
end
|
||||
|
||||
--- (GROUND) Patrol randomly to the waypoints the for the (parent) group.
|
||||
-- A random waypoint will be picked and the group will move towards that point.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE
|
||||
function CONTROLLABLE:PatrolRouteRandom( Speed, Formation, ToWaypoint )
|
||||
|
||||
local PatrolGroup = self -- Wrapper.Group#GROUP
|
||||
|
||||
if not self:IsInstanceOf( "GROUP" ) then
|
||||
PatrolGroup = self:GetGroup() -- Wrapper.Group#GROUP
|
||||
end
|
||||
|
||||
self:E( { PatrolGroup = PatrolGroup:GetName() } )
|
||||
|
||||
if PatrolGroup:IsGround() or PatrolGroup:IsShip() then
|
||||
|
||||
local Waypoints = PatrolGroup:GetTemplateRoutePoints()
|
||||
|
||||
-- Calculate the new Route.
|
||||
local FromCoord = PatrolGroup:GetCoordinate()
|
||||
local FromWaypoint = 1
|
||||
if ToWaypoint then
|
||||
FromWaypoint = ToWaypoint
|
||||
end
|
||||
|
||||
-- Loop until a waypoint has been found that is not the same as the current waypoint.
|
||||
-- Otherwise the object zon't move or drive in circles and the algorithm would not do exactly
|
||||
-- what it is supposed to do, which is making groups drive around.
|
||||
local ToWaypoint
|
||||
repeat
|
||||
-- Select a random waypoint and check if it is not the same waypoint as where the object is about.
|
||||
ToWaypoint = math.random( 1, #Waypoints )
|
||||
until( ToWaypoint ~= FromWaypoint )
|
||||
self:E( { FromWaypoint = FromWaypoint, ToWaypoint = ToWaypoint } )
|
||||
|
||||
local Waypoint = Waypoints[ToWaypoint] -- Select random waypoint.
|
||||
local ToCoord = COORDINATE:NewFromVec2( { x = Waypoint.x, y = Waypoint.y } )
|
||||
-- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task
|
||||
local Route = {}
|
||||
Route[#Route+1] = FromCoord:WaypointGround( 0 )
|
||||
Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation )
|
||||
|
||||
|
||||
local TaskRouteToZone = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolRouteRandom", Speed, Formation, ToWaypoint )
|
||||
|
||||
PatrolGroup:SetTaskWaypoint( Route[#Route], TaskRouteToZone ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
|
||||
|
||||
PatrolGroup:Route( Route, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
|
||||
end
|
||||
end
|
||||
|
||||
--- (GROUND) Patrol randomly to the waypoints the for the (parent) group.
|
||||
-- A random waypoint will be picked and the group will move towards that point.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE
|
||||
function CONTROLLABLE:PatrolZones( ZoneList, Speed, Formation )
|
||||
|
||||
if not type( ZoneList ) == "table" then
|
||||
ZoneList = { ZoneList }
|
||||
end
|
||||
|
||||
local PatrolGroup = self -- Wrapper.Group#GROUP
|
||||
|
||||
if not self:IsInstanceOf( "GROUP" ) then
|
||||
PatrolGroup = self:GetGroup() -- Wrapper.Group#GROUP
|
||||
end
|
||||
|
||||
self:E( { PatrolGroup = PatrolGroup:GetName() } )
|
||||
|
||||
if PatrolGroup:IsGround() or PatrolGroup:IsShip() then
|
||||
|
||||
local Waypoints = PatrolGroup:GetTemplateRoutePoints()
|
||||
local Waypoint = Waypoints[math.random( 1, #Waypoints )] -- Select random waypoint.
|
||||
|
||||
-- Calculate the new Route.
|
||||
local FromCoord = PatrolGroup:GetCoordinate()
|
||||
|
||||
-- Select a random Zone and get the Coordinate of the new Zone.
|
||||
local RandomZone = ZoneList[ math.random( 1, #ZoneList ) ] -- Core.Zone#ZONE
|
||||
local ToCoord = RandomZone:GetRandomCoordinate( 10 )
|
||||
|
||||
-- Create a "ground route point", which is a "point" structure that can be given as a parameter to a Task
|
||||
local Route = {}
|
||||
Route[#Route+1] = FromCoord:WaypointGround( 120 )
|
||||
Route[#Route+1] = ToCoord:WaypointGround( Speed, Formation )
|
||||
|
||||
|
||||
local TaskRouteToZone = PatrolGroup:TaskFunction( "CONTROLLABLE.PatrolZones", ZoneList, Speed, Formation )
|
||||
|
||||
PatrolGroup:SetTaskWaypoint( Route[#Route], TaskRouteToZone ) -- Set for the given Route at Waypoint 2 the TaskRouteToZone.
|
||||
|
||||
PatrolGroup:Route( Route, 1 ) -- Move after a random seconds to the Route. See the Route method for details.
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
--- Return a Misson task to follow a given route defined by Points.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #table Points A table of route points.
|
||||
@ -1620,19 +1891,16 @@ end
|
||||
|
||||
--- Make the controllable to follow a given route.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param #table GoPoints A table of Route Points.
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:Route( GoPoints )
|
||||
self:F2( GoPoints )
|
||||
-- @param #table Route A table of Route Points.
|
||||
-- @param #number DelaySeconds Wait for the specified seconds before executing the Route.
|
||||
-- @return #CONTROLLABLE The CONTROLLABLE.
|
||||
function CONTROLLABLE:Route( Route, DelaySeconds )
|
||||
self:F2( Route )
|
||||
|
||||
local DCSControllable = self:GetDCSObject()
|
||||
|
||||
if DCSControllable then
|
||||
local Points = routines.utils.deepCopy( GoPoints )
|
||||
local MissionTask = { id = 'Mission', params = { route = { points = Points, }, }, }
|
||||
local Controller = self:_GetController()
|
||||
--Controller.setTask( Controller, MissionTask )
|
||||
self.TaskScheduler:Schedule( Controller, Controller.setTask, { MissionTask }, 1 )
|
||||
local RouteTask = self:TaskRoute( Route ) -- Create a RouteTask, that will route the CONTROLLABLE to the Route.
|
||||
self:SetTask( RouteTask, DelaySeconds or 1 ) -- Execute the RouteTask after the specified seconds (default is 1).
|
||||
return self
|
||||
end
|
||||
|
||||
@ -1640,6 +1908,47 @@ function CONTROLLABLE:Route( GoPoints )
|
||||
end
|
||||
|
||||
|
||||
--- Make the GROUND Controllable to drive towards a specific point.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to.
|
||||
-- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h.
|
||||
-- @param #string Formation (optional) The route point Formation, which is a text string that specifies exactly the Text in the Type of the route point, like "Vee", "Echelon Right".
|
||||
-- @param #number DelaySeconds Wait for the specified seconds before executing the Route.
|
||||
-- @return #CONTROLLABLE The CONTROLLABLE.
|
||||
function CONTROLLABLE:RouteGroundTo( ToCoordinate, Speed, Formation, DelaySeconds )
|
||||
|
||||
local FromCoordinate = self:GetCoordinate()
|
||||
|
||||
local FromWP = FromCoordinate:WaypointGround()
|
||||
local ToWP = ToCoordinate:WaypointGround( Speed, Formation )
|
||||
|
||||
self:Route( { FromWP, ToWP }, DelaySeconds )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Make the AIR Controllable fly towards a specific point.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @param Core.Point#COORDINATE ToCoordinate A Coordinate to drive to.
|
||||
-- @param Core.Point#COORDINATE.RoutePointAltType AltType The altitude type.
|
||||
-- @param Core.Point#COORDINATE.RoutePointType Type The route point type.
|
||||
-- @param Core.Point#COORDINATE.RoutePointAction Action The route point action.
|
||||
-- @param #number Speed (optional) Speed in km/h. The default speed is 999 km/h.
|
||||
-- @param #number DelaySeconds Wait for the specified seconds before executing the Route.
|
||||
-- @return #CONTROLLABLE The CONTROLLABLE.
|
||||
function CONTROLLABLE:RouteAirTo( ToCoordinate, AltType, Type, Action, Speed, DelaySeconds )
|
||||
|
||||
local FromCoordinate = self:GetCoordinate()
|
||||
local FromWP = FromCoordinate:WaypointAir()
|
||||
|
||||
local ToWP = ToCoordinate:WaypointAir( AltType, Type, Action, Speed )
|
||||
|
||||
self:Route( { FromWP, ToWP }, DelaySeconds )
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- (AIR + GROUND) Route the controllable to a given zone.
|
||||
-- The controllable final destination point can be randomized.
|
||||
@ -2228,6 +2537,72 @@ function CONTROLLABLE:OptionROTVertical()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Alarm state to Auto: AI will automatically switch alarm states based on the presence of threats. The AI kind of cheats in this regard.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:OptionAlarmStateAuto()
|
||||
self:F2( { self.ControllableName } )
|
||||
|
||||
local DCSControllable = self:GetDCSObject()
|
||||
if DCSControllable then
|
||||
local Controller = self:_GetController()
|
||||
|
||||
if self:IsGround() then
|
||||
Controller:setOption(AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.AUTO)
|
||||
elseif self:IsShip() then
|
||||
Controller:setOption(AI.Option.Naval.id.ALARM_STATE, AI.Option.Naval.val.ALARM_STATE.AUTO)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Alarm state to Green: Group is not combat ready. Sensors are stowed if possible.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:OptionAlarmStateGreen()
|
||||
self:F2( { self.ControllableName } )
|
||||
|
||||
local DCSControllable = self:GetDCSObject()
|
||||
if DCSControllable then
|
||||
local Controller = self:_GetController()
|
||||
|
||||
if self:IsGround() then
|
||||
Controller:setOption(AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.GREEN)
|
||||
elseif self:IsShip() then
|
||||
Controller:setOption(AI.Option.Naval.id.ALARM_STATE, AI.Option.Naval.val.ALARM_STATE.GREEN)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Alarm state to Red: Group is combat ready and actively searching for targets.
|
||||
-- @param #CONTROLLABLE self
|
||||
-- @return #CONTROLLABLE self
|
||||
function CONTROLLABLE:OptionAlarmStateRed()
|
||||
self:F2( { self.ControllableName } )
|
||||
|
||||
local DCSControllable = self:GetDCSObject()
|
||||
if DCSControllable then
|
||||
local Controller = self:_GetController()
|
||||
|
||||
if self:IsGround() then
|
||||
Controller:setOption(AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.RED)
|
||||
elseif self:IsShip() then
|
||||
Controller:setOption(AI.Option.Naval.id.ALARM_STATE, AI.Option.Naval.val.ALARM_STATE.RED)
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Set RTB on bingo fuel.
|
||||
-- @param #CONTROLLABLE self
|
||||
@ -2321,37 +2696,11 @@ function CONTROLLABLE:WayPointFunction( WayPoint, WayPointIndex, WayPointFunctio
|
||||
self:F2( { WayPoint, WayPointIndex, WayPointFunction } )
|
||||
|
||||
table.insert( self.WayPoints[WayPoint].task.params.tasks, WayPointIndex )
|
||||
self.WayPoints[WayPoint].task.params.tasks[WayPointIndex] = self:TaskFunction( WayPoint, WayPointIndex, WayPointFunction, arg )
|
||||
self.WayPoints[WayPoint].task.params.tasks[WayPointIndex] = self:TaskFunction( WayPointFunction, arg )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
function CONTROLLABLE:TaskFunction( WayPoint, WayPointIndex, FunctionString, FunctionArguments )
|
||||
self:F2( { WayPoint, WayPointIndex, FunctionString, FunctionArguments } )
|
||||
|
||||
local DCSTask
|
||||
|
||||
local DCSScript = {}
|
||||
DCSScript[#DCSScript+1] = "local MissionControllable = GROUP:Find( ... ) "
|
||||
|
||||
if FunctionArguments and #FunctionArguments > 0 then
|
||||
DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable, " .. table.concat( FunctionArguments, "," ) .. ")"
|
||||
else
|
||||
DCSScript[#DCSScript+1] = FunctionString .. "( MissionControllable )"
|
||||
end
|
||||
|
||||
DCSTask = self:TaskWrappedAction(
|
||||
self:CommandDoScript(
|
||||
table.concat( DCSScript )
|
||||
), WayPointIndex
|
||||
)
|
||||
|
||||
self:T( DCSTask )
|
||||
|
||||
return DCSTask
|
||||
|
||||
end
|
||||
|
||||
--- Executes the WayPoint plan.
|
||||
-- The function gets a WayPoint parameter, that you can use to restart the mission at a specific WayPoint.
|
||||
-- Note that when the WayPoint parameter is used, the new start mission waypoint of the controllable will be 1!
|
||||
@ -2388,12 +2737,22 @@ function CONTROLLABLE:IsAirPlane()
|
||||
|
||||
if DCSObject then
|
||||
local Category = DCSObject:getDesc().category
|
||||
self:T( Category )
|
||||
return Category == Unit.Category.AIRPLANE
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
function CONTROLLABLE:GetSize()
|
||||
|
||||
local DCSObject = self:GetDCSObject()
|
||||
|
||||
if DCSObject then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Message APIs
|
||||
@ -104,15 +104,39 @@ GROUP.Takeoff = {
|
||||
GROUPTEMPLATE = {}
|
||||
|
||||
GROUPTEMPLATE.Takeoff = {
|
||||
[GROUP.Takeoff.Air] = "Turning Point",
|
||||
[GROUP.Takeoff.Runway] = "TakeOff",
|
||||
[GROUP.Takeoff.Hot] = "TakeOffParkingHot",
|
||||
[GROUP.Takeoff.Cold] = "TakeOffParking",
|
||||
[GROUP.Takeoff.Air] = { "Turning Point", "Turning Point" },
|
||||
[GROUP.Takeoff.Runway] = { "TakeOff", "From Runway" },
|
||||
[GROUP.Takeoff.Hot] = { "TakeOffParkingHot", "From Parking Area Hot" },
|
||||
[GROUP.Takeoff.Cold] = { "TakeOffParking", "From Parking Area" }
|
||||
}
|
||||
|
||||
--- Create a new GROUP from a DCSGroup
|
||||
--- Create a new GROUP from a given GroupTemplate as a parameter.
|
||||
-- Note that the GroupTemplate is NOT spawned into the mission.
|
||||
-- It is merely added to the @{Database}.
|
||||
-- @param #GROUP self
|
||||
-- @param Dcs.DCSWrapper.Group#Group GroupName The DCS Group name
|
||||
-- @param #table GroupTemplate The GroupTemplate Structure exactly as defined within the mission editor.
|
||||
-- @param Dcs.DCScoalition#coalition.side CoalitionSide The coalition.side of the group.
|
||||
-- @param Dcs.DCSGroup#Group.Category CategoryID The Group.Category of the group.
|
||||
-- @param Dcs.DCScountry#country.id CountryID the country.id of the group.
|
||||
-- @return #GROUP self
|
||||
function GROUP:NewTemplate( GroupTemplate, CoalitionSide, CategoryID, CountryID )
|
||||
local GroupName = GroupTemplate.name
|
||||
_DATABASE:_RegisterGroupTemplate( GroupTemplate, CategoryID, CountryID, CoalitionSide, GroupName )
|
||||
self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) )
|
||||
self:F2( GroupName )
|
||||
self.GroupName = GroupName
|
||||
|
||||
_DATABASE:AddGroup( GroupName )
|
||||
|
||||
self:SetEventPriority( 4 )
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Create a new GROUP from an existing Group in the Mission.
|
||||
-- @param #GROUP self
|
||||
-- @param #string GroupName The Group name
|
||||
-- @return #GROUP self
|
||||
function GROUP:Register( GroupName )
|
||||
self = BASE:Inherit( self, CONTROLLABLE:New( GroupName ) )
|
||||
@ -224,6 +248,7 @@ function GROUP:Destroy()
|
||||
for Index, UnitData in pairs( DCSGroup:getUnits() ) do
|
||||
self:CreateEventCrash( timer.getTime(), UnitData )
|
||||
end
|
||||
USERFLAG:New( self:GetName() ):Set( 100 )
|
||||
DCSGroup:destroy()
|
||||
DCSGroup = nil
|
||||
end
|
||||
@ -231,6 +256,7 @@ function GROUP:Destroy()
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Returns category of the DCS Group.
|
||||
-- @param #GROUP self
|
||||
-- @return Dcs.DCSWrapper.Group#Group.Category The category ID
|
||||
@ -353,8 +379,13 @@ function GROUP:GetSize()
|
||||
|
||||
if DCSGroup then
|
||||
local GroupSize = DCSGroup:getSize()
|
||||
self:T3( GroupSize )
|
||||
return GroupSize
|
||||
|
||||
if GroupSize then
|
||||
self:T3( GroupSize )
|
||||
return GroupSize
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
@ -502,7 +533,6 @@ end
|
||||
--- Returns a COORDINATE object indicating the point of the first UNIT of the GROUP within the mission.
|
||||
-- @param Wrapper.Group#GROUP self
|
||||
-- @return Core.Point#COORDINATE The COORDINATE of the GROUP.
|
||||
-- @return #nil The POSITIONABLE is not existing or alive.
|
||||
function GROUP:GetCoordinate()
|
||||
self:F2( self.PositionableName )
|
||||
|
||||
@ -560,6 +590,32 @@ function GROUP:GetHeading()
|
||||
|
||||
end
|
||||
|
||||
--- Returns relative amount of fuel (from 0.0 to 1.0) the group has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0.
|
||||
-- @param #GROUP self
|
||||
-- @return #number The relative amount of fuel (from 0.0 to 1.0).
|
||||
-- @return #nil The GROUP is not existing or alive.
|
||||
function GROUP:GetFuel()
|
||||
self:F( self.ControllableName )
|
||||
|
||||
local DCSControllable = self:GetDCSObject()
|
||||
|
||||
if DCSControllable then
|
||||
local GroupSize = self:GetSize()
|
||||
local TotalFuel = 0
|
||||
for UnitID, UnitData in pairs( self:GetUnits() ) do
|
||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||
local UnitFuel = Unit:GetFuel()
|
||||
self:F( { Fuel = UnitFuel } )
|
||||
TotalFuel = TotalFuel + UnitFuel
|
||||
end
|
||||
local GroupFuel = TotalFuel / GroupSize
|
||||
return GroupFuel
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
|
||||
do -- Is Zone methods
|
||||
|
||||
--- Returns true if all units of the group are within a @{Zone}.
|
||||
@ -569,6 +625,8 @@ do -- Is Zone methods
|
||||
function GROUP:IsCompletelyInZone( Zone )
|
||||
self:F2( { self.GroupName, Zone } )
|
||||
|
||||
if not self:IsAlive() then return false end
|
||||
|
||||
for UnitID, UnitData in pairs( self:GetUnits() ) do
|
||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||
if Zone:IsVec3InZone( Unit:GetVec3() ) then
|
||||
@ -590,6 +648,8 @@ function GROUP:IsPartlyInZone( Zone )
|
||||
local IsOneUnitInZone = false
|
||||
local IsOneUnitOutsideZone = false
|
||||
|
||||
if not self:IsAlive() then return false end
|
||||
|
||||
for UnitID, UnitData in pairs( self:GetUnits() ) do
|
||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||
if Zone:IsVec3InZone( Unit:GetVec3() ) then
|
||||
@ -613,6 +673,8 @@ end
|
||||
function GROUP:IsNotInZone( Zone )
|
||||
self:F2( { self.GroupName, Zone } )
|
||||
|
||||
if not self:IsAlive() then return true end
|
||||
|
||||
for UnitID, UnitData in pairs( self:GetUnits() ) do
|
||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||
if Zone:IsVec3InZone( Unit:GetVec3() ) then
|
||||
@ -631,6 +693,8 @@ function GROUP:CountInZone( Zone )
|
||||
self:F2( {self.GroupName, Zone} )
|
||||
local Count = 0
|
||||
|
||||
if not self:IsAlive() then return Count end
|
||||
|
||||
for UnitID, UnitData in pairs( self:GetUnits() ) do
|
||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||
if Zone:IsVec3InZone( Unit:GetVec3() ) then
|
||||
@ -864,25 +928,28 @@ end
|
||||
-- @param #table Template The template of the Group retrieved with GROUP:GetTemplate()
|
||||
function GROUP:Respawn( Template )
|
||||
|
||||
local Vec3 = self:GetVec3()
|
||||
Template.x = Vec3.x
|
||||
Template.y = Vec3.z
|
||||
--Template.x = nil
|
||||
--Template.y = nil
|
||||
|
||||
self:E( #Template.units )
|
||||
for UnitID, UnitData in pairs( self:GetUnits() ) do
|
||||
local GroupUnit = UnitData -- Wrapper.Unit#UNIT
|
||||
self:E( GroupUnit:GetName() )
|
||||
if GroupUnit:IsAlive() then
|
||||
local GroupUnitVec3 = GroupUnit:GetVec3()
|
||||
local GroupUnitHeading = GroupUnit:GetHeading()
|
||||
Template.units[UnitID].alt = GroupUnitVec3.y
|
||||
Template.units[UnitID].x = GroupUnitVec3.x
|
||||
Template.units[UnitID].y = GroupUnitVec3.z
|
||||
Template.units[UnitID].heading = GroupUnitHeading
|
||||
self:E( { UnitID, Template.units[UnitID], Template.units[UnitID] } )
|
||||
if self:IsAlive() then
|
||||
local Vec3 = self:GetVec3()
|
||||
Template.x = Vec3.x
|
||||
Template.y = Vec3.z
|
||||
--Template.x = nil
|
||||
--Template.y = nil
|
||||
|
||||
self:E( #Template.units )
|
||||
for UnitID, UnitData in pairs( self:GetUnits() ) do
|
||||
local GroupUnit = UnitData -- Wrapper.Unit#UNIT
|
||||
self:E( GroupUnit:GetName() )
|
||||
if GroupUnit:IsAlive() then
|
||||
local GroupUnitVec3 = GroupUnit:GetVec3()
|
||||
local GroupUnitHeading = GroupUnit:GetHeading()
|
||||
Template.units[UnitID].alt = GroupUnitVec3.y
|
||||
Template.units[UnitID].x = GroupUnitVec3.x
|
||||
Template.units[UnitID].y = GroupUnitVec3.z
|
||||
Template.units[UnitID].heading = GroupUnitHeading
|
||||
self:E( { UnitID, Template.units[UnitID], Template.units[UnitID] } )
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
self:Destroy()
|
||||
@ -897,10 +964,19 @@ end
|
||||
-- @return #table
|
||||
function GROUP:GetTemplate()
|
||||
local GroupName = self:GetName()
|
||||
self:E( GroupName )
|
||||
return _DATABASE:GetGroupTemplate( GroupName )
|
||||
return UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ) )
|
||||
end
|
||||
|
||||
--- Returns the group template route.points[] (the waypoints) from the @{DATABASE} (_DATABASE object).
|
||||
-- @param #GROUP self
|
||||
-- @return #table
|
||||
function GROUP:GetTemplateRoutePoints()
|
||||
local GroupName = self:GetName()
|
||||
return UTILS.DeepCopy( _DATABASE:GetGroupTemplate( GroupName ).route.points )
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Sets the controlled status in a Template.
|
||||
-- @param #GROUP self
|
||||
-- @param #boolean Controlled true is controlled, false is uncontrolled.
|
||||
@ -1075,7 +1151,7 @@ do -- Route methods
|
||||
|
||||
local PointTo = {}
|
||||
local AirbasePointVec2 = RTBAirbase:GetPointVec2()
|
||||
local AirbaseAirPoint = AirbasePointVec2:RoutePointAir(
|
||||
local AirbaseAirPoint = AirbasePointVec2:WaypointAir(
|
||||
POINT_VEC3.RoutePointAltType.BARO,
|
||||
"Land",
|
||||
"Landing",
|
||||
@ -1120,9 +1196,9 @@ do -- Event Handling
|
||||
-- @param Core.Event#EVENTS Event
|
||||
-- @param #function EventFunction (optional) The function to be called when the event occurs for the GROUP.
|
||||
-- @return #GROUP
|
||||
function GROUP:HandleEvent( Event, EventFunction )
|
||||
function GROUP:HandleEvent( Event, EventFunction, ... )
|
||||
|
||||
self:EventDispatcher():OnEventForGroup( self:GetName(), EventFunction, self, Event )
|
||||
self:EventDispatcher():OnEventForGroup( self:GetName(), EventFunction, self, Event, ... )
|
||||
|
||||
return self
|
||||
end
|
||||
@ -1133,7 +1209,7 @@ do -- Event Handling
|
||||
-- @return #GROUP
|
||||
function GROUP:UnHandleEvent( Event )
|
||||
|
||||
self:EventDispatcher():Remove( self, Event )
|
||||
self:EventDispatcher():RemoveEvent( self, Event )
|
||||
|
||||
return self
|
||||
end
|
||||
@ -1162,7 +1238,7 @@ do -- Players
|
||||
-- @return #nil The group has no players
|
||||
function GROUP:GetPlayerNames()
|
||||
|
||||
local PlayerNames = nil
|
||||
local PlayerNames = {}
|
||||
|
||||
local Units = self:GetUnits()
|
||||
for UnitID, UnitData in pairs( Units ) do
|
||||
@ -1178,4 +1254,96 @@ do -- Players
|
||||
return PlayerNames
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
--do -- Smoke
|
||||
--
|
||||
----- Signal a flare at the position of the GROUP.
|
||||
---- @param #GROUP self
|
||||
---- @param Utilities.Utils#FLARECOLOR FlareColor
|
||||
--function GROUP:Flare( FlareColor )
|
||||
-- self:F2()
|
||||
-- trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 )
|
||||
--end
|
||||
--
|
||||
----- Signal a white flare at the position of the GROUP.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:FlareWhite()
|
||||
-- self:F2()
|
||||
-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 )
|
||||
--end
|
||||
--
|
||||
----- Signal a yellow flare at the position of the GROUP.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:FlareYellow()
|
||||
-- self:F2()
|
||||
-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 )
|
||||
--end
|
||||
--
|
||||
----- Signal a green flare at the position of the GROUP.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:FlareGreen()
|
||||
-- self:F2()
|
||||
-- trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 )
|
||||
--end
|
||||
--
|
||||
----- Signal a red flare at the position of the GROUP.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:FlareRed()
|
||||
-- self:F2()
|
||||
-- local Vec3 = self:GetVec3()
|
||||
-- if Vec3 then
|
||||
-- trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 )
|
||||
-- end
|
||||
--end
|
||||
--
|
||||
----- Smoke the GROUP.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:Smoke( SmokeColor, Range )
|
||||
-- self:F2()
|
||||
-- if Range then
|
||||
-- trigger.action.smoke( self:GetRandomVec3( Range ), SmokeColor )
|
||||
-- else
|
||||
-- trigger.action.smoke( self:GetVec3(), SmokeColor )
|
||||
-- end
|
||||
--
|
||||
--end
|
||||
--
|
||||
----- Smoke the GROUP Green.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:SmokeGreen()
|
||||
-- self:F2()
|
||||
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green )
|
||||
--end
|
||||
--
|
||||
----- Smoke the GROUP Red.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:SmokeRed()
|
||||
-- self:F2()
|
||||
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red )
|
||||
--end
|
||||
--
|
||||
----- Smoke the GROUP White.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:SmokeWhite()
|
||||
-- self:F2()
|
||||
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White )
|
||||
--end
|
||||
--
|
||||
----- Smoke the GROUP Orange.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:SmokeOrange()
|
||||
-- self:F2()
|
||||
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange )
|
||||
--end
|
||||
--
|
||||
----- Smoke the GROUP Blue.
|
||||
---- @param #GROUP self
|
||||
--function GROUP:SmokeBlue()
|
||||
-- self:F2()
|
||||
-- trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue )
|
||||
--end
|
||||
--
|
||||
--
|
||||
--
|
||||
--end
|
||||
@ -83,15 +83,8 @@ end
|
||||
function IDENTIFIABLE:GetName()
|
||||
self:F2( self.IdentifiableName )
|
||||
|
||||
local DCSIdentifiable = self:GetDCSObject()
|
||||
|
||||
if DCSIdentifiable then
|
||||
local IdentifiableName = self.IdentifiableName
|
||||
return IdentifiableName
|
||||
end
|
||||
|
||||
self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" )
|
||||
return nil
|
||||
local IdentifiableName = self.IdentifiableName
|
||||
return IdentifiableName
|
||||
end
|
||||
|
||||
|
||||
@ -166,6 +159,36 @@ function IDENTIFIABLE:GetCoalition()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns the name of the coalition of the Identifiable.
|
||||
-- @param #IDENTIFIABLE self
|
||||
-- @return #string The name of the coalition.
|
||||
-- @return #nil The DCS Identifiable is not existing or alive.
|
||||
function IDENTIFIABLE:GetCoalitionName()
|
||||
self:F2( self.IdentifiableName )
|
||||
|
||||
local DCSIdentifiable = self:GetDCSObject()
|
||||
|
||||
if DCSIdentifiable then
|
||||
local IdentifiableCoalition = DCSIdentifiable:getCoalition()
|
||||
self:T3( IdentifiableCoalition )
|
||||
|
||||
if IdentifiableCoalition == coalition.side.BLUE then
|
||||
return "Blue"
|
||||
end
|
||||
|
||||
if IdentifiableCoalition == coalition.side.RED then
|
||||
return "Red"
|
||||
end
|
||||
|
||||
if IdentifiableCoalition == coalition.side.NEUTRAL then
|
||||
return "Neutral"
|
||||
end
|
||||
end
|
||||
|
||||
self:E( self.ClassName .. " " .. self.IdentifiableName .. " not found!" )
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns country of the Identifiable.
|
||||
-- @param #IDENTIFIABLE self
|
||||
-- @return Dcs.DCScountry#country.id The country identifier.
|
||||
|
||||
@ -79,7 +79,7 @@ function OBJECT:Destroy()
|
||||
local DCSObject = self:GetDCSObject()
|
||||
|
||||
if DCSObject then
|
||||
|
||||
--BASE:CreateEventCrash( timer.getTime(), DCSObject )
|
||||
DCSObject:destroy()
|
||||
end
|
||||
|
||||
|
||||
@ -10,13 +10,11 @@
|
||||
--
|
||||
-- @module Positionable
|
||||
|
||||
|
||||
--- The POSITIONABLE class
|
||||
-- @type POSITIONABLE
|
||||
--- @type POSITIONABLE.__ Methods which are not intended for mission designers, but which are used interally by the moose designer :-)
|
||||
-- @extends Wrapper.Identifiable#IDENTIFIABLE
|
||||
|
||||
--- @type POSITIONABLE
|
||||
-- @extends Wrapper.Identifiable#IDENTIFIABLE
|
||||
-- @field #string PositionableName The name of the measurable.
|
||||
-- @field Core.Spot#SPOT Spot The laser Spot.
|
||||
-- @field #number LaserCode The last assigned laser code.
|
||||
|
||||
|
||||
--- # POSITIONABLE class, extends @{Identifiable#IDENTIFIABLE}
|
||||
@ -49,6 +47,14 @@ POSITIONABLE = {
|
||||
ClassName = "POSITIONABLE",
|
||||
PositionableName = "",
|
||||
}
|
||||
|
||||
--- @field #POSITIONABLE.__
|
||||
POSITIONABLE.__ = {}
|
||||
|
||||
--- @field #POSITIONABLE.__.Cargo
|
||||
POSITIONABLE.__.Cargo = {}
|
||||
|
||||
|
||||
--- A DCSPositionable
|
||||
-- @type DCSPositionable
|
||||
-- @field id_ The ID of the controllable in DCS
|
||||
@ -150,7 +156,6 @@ end
|
||||
--- Returns a COORDINATE object indicating the point in 3D of the POSITIONABLE within the mission.
|
||||
-- @param Wrapper.Positionable#POSITIONABLE self
|
||||
-- @return Core.Point#COORDINATE The COORDINATE of the POSITIONABLE.
|
||||
-- @return #nil The POSITIONABLE is not existing or alive.
|
||||
function POSITIONABLE:GetCoordinate()
|
||||
self:F2( self.PositionableName )
|
||||
|
||||
@ -159,8 +164,9 @@ function POSITIONABLE:GetCoordinate()
|
||||
if DCSPositionable then
|
||||
local PositionableVec3 = self:GetPositionVec3()
|
||||
|
||||
local PositionableCoordinate = POINT_VEC3:NewFromVec3( PositionableVec3 )
|
||||
local PositionableCoordinate = COORDINATE:NewFromVec3( PositionableVec3 )
|
||||
PositionableCoordinate:SetHeading( self:GetHeading() )
|
||||
PositionableCoordinate:SetVelocity( self:GetVelocityMPS() )
|
||||
|
||||
self:T2( PositionableCoordinate )
|
||||
return PositionableCoordinate
|
||||
@ -320,16 +326,35 @@ function POSITIONABLE:InAir()
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Returns the POSITIONABLE velocity vector.
|
||||
|
||||
--- Returns the a @{Velocity} object from the positionable.
|
||||
-- @param Wrapper.Positionable#POSITIONABLE self
|
||||
-- @return Dcs.DCSTypes#Vec3 The velocity vector
|
||||
-- @return Core.Velocity#VELOCITY Velocity The Velocity object.
|
||||
-- @return #nil The POSITIONABLE is not existing or alive.
|
||||
function POSITIONABLE:GetVelocity()
|
||||
self:F2( self.PositionableName )
|
||||
|
||||
local DCSPositionable = self:GetDCSObject()
|
||||
|
||||
if DCSPositionable then
|
||||
local Velocity = VELOCITY:New( self )
|
||||
return Velocity
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- Returns the POSITIONABLE velocity Vec3 vector.
|
||||
-- @param Wrapper.Positionable#POSITIONABLE self
|
||||
-- @return Dcs.DCSTypes#Vec3 The velocity Vec3 vector
|
||||
-- @return #nil The POSITIONABLE is not existing or alive.
|
||||
function POSITIONABLE:GetVelocityVec3()
|
||||
self:F2( self.PositionableName )
|
||||
|
||||
local DCSPositionable = self:GetDCSObject()
|
||||
|
||||
if DCSPositionable then
|
||||
local PositionableVelocityVec3 = DCSPositionable:getVelocity()
|
||||
self:T3( PositionableVelocityVec3 )
|
||||
@ -365,40 +390,38 @@ end
|
||||
--- Returns the POSITIONABLE velocity in km/h.
|
||||
-- @param Wrapper.Positionable#POSITIONABLE self
|
||||
-- @return #number The velocity in km/h
|
||||
-- @return #nil The POSITIONABLE is not existing or alive.
|
||||
function POSITIONABLE:GetVelocityKMH()
|
||||
self:F2( self.PositionableName )
|
||||
|
||||
local DCSPositionable = self:GetDCSObject()
|
||||
|
||||
if DCSPositionable then
|
||||
local VelocityVec3 = self:GetVelocity()
|
||||
local VelocityVec3 = self:GetVelocityVec3()
|
||||
local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec
|
||||
local Velocity = Velocity * 3.6 -- now it is in km/h.
|
||||
self:T3( Velocity )
|
||||
return Velocity
|
||||
end
|
||||
|
||||
return nil
|
||||
return 0
|
||||
end
|
||||
|
||||
--- Returns the POSITIONABLE velocity in meters per second.
|
||||
-- @param Wrapper.Positionable#POSITIONABLE self
|
||||
-- @return #number The velocity in meters per second.
|
||||
-- @return #nil The POSITIONABLE is not existing or alive.
|
||||
function POSITIONABLE:GetVelocityMPS()
|
||||
self:F2( self.PositionableName )
|
||||
|
||||
local DCSPositionable = self:GetDCSObject()
|
||||
|
||||
if DCSPositionable then
|
||||
local VelocityVec3 = self:GetVelocity()
|
||||
local VelocityVec3 = self:GetVelocityVec3()
|
||||
local Velocity = ( VelocityVec3.x ^ 2 + VelocityVec3.y ^ 2 + VelocityVec3.z ^ 2 ) ^ 0.5 -- in meters / sec
|
||||
self:T3( Velocity )
|
||||
return Velocity
|
||||
end
|
||||
|
||||
return nil
|
||||
return 0
|
||||
end
|
||||
|
||||
|
||||
@ -412,8 +435,8 @@ function POSITIONABLE:GetMessageText( Message, Name ) --R2.1 added
|
||||
local DCSObject = self:GetDCSObject()
|
||||
if DCSObject then
|
||||
Name = Name and ( " (" .. Name .. ")" ) or ""
|
||||
local Callsign = string.format( "[%s]", self:GetCallsign() ~= "" and self:GetCallsign() or self:GetName() )
|
||||
local MessageText = Callsign .. Name .. ": " .. Message
|
||||
local Callsign = string.format( "%s", self:GetCallsign() ~= "" and self:GetCallsign() or self:GetName() )
|
||||
local MessageText = string.format("[%s%s]: %s", Callsign, Name, Message )
|
||||
return MessageText
|
||||
end
|
||||
|
||||
@ -438,6 +461,23 @@ function POSITIONABLE:GetMessage( Message, Duration, Name ) --R2.1 changed calls
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Returns a message of a specified type with the callsign embedded (if there is one).
|
||||
-- @param #POSITIONABLE self
|
||||
-- @param #string Message The message text
|
||||
-- @param Core.Message#MESSAGE MessageType MessageType The message type.
|
||||
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
|
||||
-- @return Core.Message#MESSAGE
|
||||
function POSITIONABLE:GetMessageType( Message, MessageType, Name ) -- R2.2 changed callsign and name and using GetMessageText
|
||||
|
||||
local DCSObject = self:GetDCSObject()
|
||||
if DCSObject then
|
||||
local MessageText = self:GetMessageText( Message, Name )
|
||||
return MESSAGE:NewType( MessageText, MessageType )
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Send a message to all coalitions.
|
||||
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
|
||||
-- @param #POSITIONABLE self
|
||||
@ -468,12 +508,6 @@ function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition )
|
||||
|
||||
local DCSObject = self:GetDCSObject()
|
||||
if DCSObject then
|
||||
if MessageCoalition == coalition.side.BLUE then
|
||||
Name = "Blue coalition"
|
||||
end
|
||||
if MessageCoalition == coalition.side.RED then
|
||||
Name = "Red coalition"
|
||||
end
|
||||
self:GetMessage( Message, Duration, Name ):ToCoalition( MessageCoalition )
|
||||
end
|
||||
|
||||
@ -481,6 +515,26 @@ function POSITIONABLE:MessageToCoalition( Message, Duration, MessageCoalition )
|
||||
end
|
||||
|
||||
|
||||
--- Send a message to a coalition.
|
||||
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @param #string Message The message text
|
||||
-- @param Core.Message#MESSAGE.Type MessageType The message type that determines the duration.
|
||||
-- @param Dcs.DCScoalition#coalition MessageCoalition The Coalition receiving the message.
|
||||
function POSITIONABLE:MessageTypeToCoalition( Message, MessageType, MessageCoalition )
|
||||
self:F2( { Message, MessageType } )
|
||||
|
||||
local Name = ""
|
||||
|
||||
local DCSObject = self:GetDCSObject()
|
||||
if DCSObject then
|
||||
self:GetMessageType( Message, MessageType, Name ):ToCoalition( MessageCoalition )
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Send a message to the red coalition.
|
||||
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
|
||||
-- @param #POSITIONABLE self
|
||||
@ -553,6 +607,26 @@ function POSITIONABLE:MessageToGroup( Message, Duration, MessageGroup, Name )
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Send a message of a message type to a @{Group}.
|
||||
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @param #string Message The message text
|
||||
-- @param Core.Message#MESSAGE.Type MessageType The message type that determines the duration.
|
||||
-- @param Wrapper.Group#GROUP MessageGroup The GROUP object receiving the message.
|
||||
-- @param #string Name (optional) The Name of the sender. If not provided, the Name is the type of the Positionable.
|
||||
function POSITIONABLE:MessageTypeToGroup( Message, MessageType, MessageGroup, Name )
|
||||
self:F2( { Message, MessageType } )
|
||||
|
||||
local DCSObject = self:GetDCSObject()
|
||||
if DCSObject then
|
||||
if DCSObject:isExist() then
|
||||
self:GetMessageType( Message, MessageType, Name ):ToGroup( MessageGroup )
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Send a message to a @{Set#SET_GROUP}.
|
||||
-- The message will appear in the message area. The message will begin with the callsign of the group and the type of the first unit sending the message.
|
||||
-- @param #POSITIONABLE self
|
||||
@ -679,5 +753,139 @@ function POSITIONABLE:GetLaserCode() --R2.1
|
||||
return self.LaserCode
|
||||
end
|
||||
|
||||
--- Add cargo.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @param Core.Cargo#CARGO Cargo
|
||||
-- @return #POSITIONABLE
|
||||
function POSITIONABLE:AddCargo( Cargo )
|
||||
self.__.Cargo[Cargo] = Cargo
|
||||
return self
|
||||
end
|
||||
|
||||
--- Remove cargo.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @param Core.Cargo#CARGO Cargo
|
||||
-- @return #POSITIONABLE
|
||||
function POSITIONABLE:RemoveCargo( Cargo )
|
||||
self.__.Cargo[Cargo] = nil
|
||||
return self
|
||||
end
|
||||
|
||||
--- Returns if carrier has given cargo.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @return Core.Cargo#CARGO Cargo
|
||||
function POSITIONABLE:HasCargo( Cargo )
|
||||
return self.__.Cargo[Cargo]
|
||||
end
|
||||
|
||||
--- Clear all cargo.
|
||||
-- @param #POSITIONABLE self
|
||||
function POSITIONABLE:ClearCargo()
|
||||
self.__.Cargo = {}
|
||||
end
|
||||
|
||||
--- Get cargo item count.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @return Core.Cargo#CARGO Cargo
|
||||
function POSITIONABLE:CargoItemCount()
|
||||
local ItemCount = 0
|
||||
for CargoName, Cargo in pairs( self.__.Cargo ) do
|
||||
ItemCount = ItemCount + Cargo:GetCount()
|
||||
end
|
||||
return ItemCount
|
||||
end
|
||||
|
||||
--- Signal a flare at the position of the POSITIONABLE.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @param Utilities.Utils#FLARECOLOR FlareColor
|
||||
function POSITIONABLE:Flare( FlareColor )
|
||||
self:F2()
|
||||
trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 )
|
||||
end
|
||||
|
||||
--- Signal a white flare at the position of the POSITIONABLE.
|
||||
-- @param #POSITIONABLE self
|
||||
function POSITIONABLE:FlareWhite()
|
||||
self:F2()
|
||||
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 )
|
||||
end
|
||||
|
||||
--- Signal a yellow flare at the position of the POSITIONABLE.
|
||||
-- @param #POSITIONABLE self
|
||||
function POSITIONABLE:FlareYellow()
|
||||
self:F2()
|
||||
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 )
|
||||
end
|
||||
|
||||
--- Signal a green flare at the position of the POSITIONABLE.
|
||||
-- @param #POSITIONABLE self
|
||||
function POSITIONABLE:FlareGreen()
|
||||
self:F2()
|
||||
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 )
|
||||
end
|
||||
|
||||
--- Signal a red flare at the position of the POSITIONABLE.
|
||||
-- @param #POSITIONABLE self
|
||||
function POSITIONABLE:FlareRed()
|
||||
self:F2()
|
||||
local Vec3 = self:GetVec3()
|
||||
if Vec3 then
|
||||
trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 )
|
||||
end
|
||||
end
|
||||
|
||||
--- Smoke the POSITIONABLE.
|
||||
-- @param #POSITIONABLE self
|
||||
-- @param Utilities.Utils#SMOKECOLOR SmokeColor The color to smoke to positionable.
|
||||
-- @param #number Range The range in meters to randomize the smoking around the positionable.
|
||||
-- @param #number AddHeight The height in meters to add to the altitude of the positionable.
|
||||
function POSITIONABLE:Smoke( SmokeColor, Range, AddHeight )
|
||||
self:F2()
|
||||
if Range then
|
||||
local Vec3 = self:GetRandomVec3( Range )
|
||||
Vec3.y = Vec3.y + AddHeight or 0
|
||||
trigger.action.smoke( Vec3, SmokeColor )
|
||||
else
|
||||
local Vec3 = self:GetVec3()
|
||||
Vec3.y = Vec3.y + AddHeight or 0
|
||||
trigger.action.smoke( self:GetVec3(), SmokeColor )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Smoke the POSITIONABLE Green.
|
||||
-- @param #POSITIONABLE self
|
||||
function POSITIONABLE:SmokeGreen()
|
||||
self:F2()
|
||||
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green )
|
||||
end
|
||||
|
||||
--- Smoke the POSITIONABLE Red.
|
||||
-- @param #POSITIONABLE self
|
||||
function POSITIONABLE:SmokeRed()
|
||||
self:F2()
|
||||
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red )
|
||||
end
|
||||
|
||||
--- Smoke the POSITIONABLE White.
|
||||
-- @param #POSITIONABLE self
|
||||
function POSITIONABLE:SmokeWhite()
|
||||
self:F2()
|
||||
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White )
|
||||
end
|
||||
|
||||
--- Smoke the POSITIONABLE Orange.
|
||||
-- @param #POSITIONABLE self
|
||||
function POSITIONABLE:SmokeOrange()
|
||||
self:F2()
|
||||
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange )
|
||||
end
|
||||
|
||||
--- Smoke the POSITIONABLE Blue.
|
||||
-- @param #POSITIONABLE self
|
||||
function POSITIONABLE:SmokeBlue()
|
||||
self:F2()
|
||||
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue )
|
||||
end
|
||||
|
||||
|
||||
|
||||
@ -61,7 +61,6 @@ function STATIC:FindByName( StaticName, RaiseError )
|
||||
|
||||
if StaticFound then
|
||||
StaticFound:F3( { StaticName } )
|
||||
|
||||
return StaticFound
|
||||
end
|
||||
|
||||
@ -92,4 +91,18 @@ end
|
||||
function STATIC:GetThreatLevel()
|
||||
|
||||
return 1, "Static"
|
||||
end
|
||||
end
|
||||
|
||||
--- Respawn the @{Unit} using a (tweaked) template of the parent Group.
|
||||
-- @param #UNIT self
|
||||
-- @param Core.Point#COORDINATE Coordinate The coordinate where to spawn the new Static.
|
||||
-- @param #number Heading The heading of the unit respawn.
|
||||
function STATIC:ReSpawn( Coordinate, Heading )
|
||||
|
||||
|
||||
-- todo: need to fix country
|
||||
local SpawnStatic = SPAWNSTATIC:NewFromStatic( self.StaticName, country.id.USA )
|
||||
|
||||
SpawnStatic:SpawnFromPointVec2( Coordinate, Heading, self.StaticName )
|
||||
end
|
||||
|
||||
|
||||
@ -159,6 +159,27 @@ function UNIT:GetDCSObject()
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Destroys the UNIT.
|
||||
-- @param #UNIT self
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:Destroy()
|
||||
self:F2( self.ObjectName )
|
||||
|
||||
local DCSObject = self:GetDCSObject()
|
||||
|
||||
if DCSObject then
|
||||
local UnitGroup = self:GetGroup()
|
||||
local UnitGroupName = UnitGroup:GetName()
|
||||
self:F( { UnitGroupName = UnitGroupName } )
|
||||
USERFLAG:New( UnitGroupName ):Set( 100 )
|
||||
--BASE:CreateEventCrash( timer.getTime(), DCSObject )
|
||||
DCSObject:destroy()
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
--- Respawn the @{Unit} using a (tweaked) template of the parent Group.
|
||||
--
|
||||
-- This function will:
|
||||
@ -486,12 +507,12 @@ function UNIT:GetRadar()
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
--- Returns relative amount of fuel (from 0.0 to 1.0) the unit has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0.
|
||||
--- Returns relative amount of fuel (from 0.0 to 1.0) the UNIT has in its internal tanks. If there are additional fuel tanks the value may be greater than 1.0.
|
||||
-- @param #UNIT self
|
||||
-- @return #number The relative amount of fuel (from 0.0 to 1.0).
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
function UNIT:GetFuel()
|
||||
self:F2( self.UnitName )
|
||||
self:F( self.UnitName )
|
||||
|
||||
local DCSUnit = self:GetDCSObject()
|
||||
|
||||
@ -536,7 +557,7 @@ function UNIT:GetLife()
|
||||
return UnitLife
|
||||
end
|
||||
|
||||
return nil
|
||||
return -1
|
||||
end
|
||||
|
||||
--- Returns the Unit's initial health.
|
||||
@ -553,7 +574,7 @@ function UNIT:GetLife0()
|
||||
return UnitLife0
|
||||
end
|
||||
|
||||
return nil
|
||||
return 0
|
||||
end
|
||||
|
||||
--- Returns the category name of the #UNIT.
|
||||
@ -598,122 +619,128 @@ end
|
||||
-- @param #UNIT self
|
||||
function UNIT:GetThreatLevel()
|
||||
|
||||
local Attributes = self:GetDesc().attributes
|
||||
self:T( Attributes )
|
||||
|
||||
local ThreatLevel = 0
|
||||
local ThreatText = ""
|
||||
|
||||
if self:IsGround() then
|
||||
local Descriptor = self:GetDesc()
|
||||
|
||||
self:T( "Ground" )
|
||||
if Descriptor then
|
||||
|
||||
local ThreatLevels = {
|
||||
"Unarmed",
|
||||
"Infantry",
|
||||
"Old Tanks & APCs",
|
||||
"Tanks & IFVs without ATGM",
|
||||
"Tanks & IFV with ATGM",
|
||||
"Modern Tanks",
|
||||
"AAA",
|
||||
"IR Guided SAMs",
|
||||
"SR SAMs",
|
||||
"MR SAMs",
|
||||
"LR SAMs"
|
||||
}
|
||||
local Attributes = Descriptor.attributes
|
||||
self:T( Attributes )
|
||||
|
||||
if self:IsGround() then
|
||||
|
||||
self:T( "Ground" )
|
||||
|
||||
if Attributes["LR SAM"] then ThreatLevel = 10
|
||||
elseif Attributes["MR SAM"] then ThreatLevel = 9
|
||||
elseif Attributes["SR SAM"] and
|
||||
not Attributes["IR Guided SAM"] then ThreatLevel = 8
|
||||
elseif ( Attributes["SR SAM"] or Attributes["MANPADS"] ) and
|
||||
Attributes["IR Guided SAM"] then ThreatLevel = 7
|
||||
elseif Attributes["AAA"] then ThreatLevel = 6
|
||||
elseif Attributes["Modern Tanks"] then ThreatLevel = 5
|
||||
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
|
||||
Attributes["ATGM"] then ThreatLevel = 4
|
||||
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
|
||||
not Attributes["ATGM"] then ThreatLevel = 3
|
||||
elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2
|
||||
elseif Attributes["Infantry"] then ThreatLevel = 1
|
||||
local ThreatLevels = {
|
||||
"Unarmed",
|
||||
"Infantry",
|
||||
"Old Tanks & APCs",
|
||||
"Tanks & IFVs without ATGM",
|
||||
"Tanks & IFV with ATGM",
|
||||
"Modern Tanks",
|
||||
"AAA",
|
||||
"IR Guided SAMs",
|
||||
"SR SAMs",
|
||||
"MR SAMs",
|
||||
"LR SAMs"
|
||||
}
|
||||
|
||||
|
||||
if Attributes["LR SAM"] then ThreatLevel = 10
|
||||
elseif Attributes["MR SAM"] then ThreatLevel = 9
|
||||
elseif Attributes["SR SAM"] and
|
||||
not Attributes["IR Guided SAM"] then ThreatLevel = 8
|
||||
elseif ( Attributes["SR SAM"] or Attributes["MANPADS"] ) and
|
||||
Attributes["IR Guided SAM"] then ThreatLevel = 7
|
||||
elseif Attributes["AAA"] then ThreatLevel = 6
|
||||
elseif Attributes["Modern Tanks"] then ThreatLevel = 5
|
||||
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
|
||||
Attributes["ATGM"] then ThreatLevel = 4
|
||||
elseif ( Attributes["Tanks"] or Attributes["IFV"] ) and
|
||||
not Attributes["ATGM"] then ThreatLevel = 3
|
||||
elseif Attributes["Old Tanks"] or Attributes["APC"] or Attributes["Artillery"] then ThreatLevel = 2
|
||||
elseif Attributes["Infantry"] then ThreatLevel = 1
|
||||
end
|
||||
|
||||
ThreatText = ThreatLevels[ThreatLevel+1]
|
||||
end
|
||||
|
||||
ThreatText = ThreatLevels[ThreatLevel+1]
|
||||
end
|
||||
|
||||
if self:IsAir() then
|
||||
|
||||
self:T( "Air" )
|
||||
|
||||
local ThreatLevels = {
|
||||
"Unarmed",
|
||||
"Tanker",
|
||||
"AWACS",
|
||||
"Transport Helicopter",
|
||||
"UAV",
|
||||
"Bomber",
|
||||
"Strategic Bomber",
|
||||
"Attack Helicopter",
|
||||
"Battleplane",
|
||||
"Multirole Fighter",
|
||||
"Fighter"
|
||||
}
|
||||
if self:IsAir() then
|
||||
|
||||
|
||||
if Attributes["Fighters"] then ThreatLevel = 10
|
||||
elseif Attributes["Multirole fighters"] then ThreatLevel = 9
|
||||
elseif Attributes["Battleplanes"] then ThreatLevel = 8
|
||||
elseif Attributes["Attack helicopters"] then ThreatLevel = 7
|
||||
elseif Attributes["Strategic bombers"] then ThreatLevel = 6
|
||||
elseif Attributes["Bombers"] then ThreatLevel = 5
|
||||
elseif Attributes["UAVs"] then ThreatLevel = 4
|
||||
elseif Attributes["Transport helicopters"] then ThreatLevel = 3
|
||||
elseif Attributes["AWACS"] then ThreatLevel = 2
|
||||
elseif Attributes["Tankers"] then ThreatLevel = 1
|
||||
self:T( "Air" )
|
||||
|
||||
local ThreatLevels = {
|
||||
"Unarmed",
|
||||
"Tanker",
|
||||
"AWACS",
|
||||
"Transport Helicopter",
|
||||
"UAV",
|
||||
"Bomber",
|
||||
"Strategic Bomber",
|
||||
"Attack Helicopter",
|
||||
"Battleplane",
|
||||
"Multirole Fighter",
|
||||
"Fighter"
|
||||
}
|
||||
|
||||
|
||||
if Attributes["Fighters"] then ThreatLevel = 10
|
||||
elseif Attributes["Multirole fighters"] then ThreatLevel = 9
|
||||
elseif Attributes["Battleplanes"] then ThreatLevel = 8
|
||||
elseif Attributes["Attack helicopters"] then ThreatLevel = 7
|
||||
elseif Attributes["Strategic bombers"] then ThreatLevel = 6
|
||||
elseif Attributes["Bombers"] then ThreatLevel = 5
|
||||
elseif Attributes["UAVs"] then ThreatLevel = 4
|
||||
elseif Attributes["Transport helicopters"] then ThreatLevel = 3
|
||||
elseif Attributes["AWACS"] then ThreatLevel = 2
|
||||
elseif Attributes["Tankers"] then ThreatLevel = 1
|
||||
end
|
||||
|
||||
ThreatText = ThreatLevels[ThreatLevel+1]
|
||||
end
|
||||
|
||||
ThreatText = ThreatLevels[ThreatLevel+1]
|
||||
end
|
||||
|
||||
if self:IsShip() then
|
||||
|
||||
self:T( "Ship" )
|
||||
|
||||
--["Aircraft Carriers"] = {"Heavy armed ships",},
|
||||
--["Cruisers"] = {"Heavy armed ships",},
|
||||
--["Destroyers"] = {"Heavy armed ships",},
|
||||
--["Frigates"] = {"Heavy armed ships",},
|
||||
--["Corvettes"] = {"Heavy armed ships",},
|
||||
--["Heavy armed ships"] = {"Armed ships", "Armed Air Defence", "HeavyArmoredUnits",},
|
||||
--["Light armed ships"] = {"Armed ships","NonArmoredUnits"},
|
||||
--["Armed ships"] = {"Ships"},
|
||||
--["Unarmed ships"] = {"Ships","HeavyArmoredUnits",},
|
||||
|
||||
local ThreatLevels = {
|
||||
"Unarmed ship",
|
||||
"Light armed ships",
|
||||
"Corvettes",
|
||||
"",
|
||||
"Frigates",
|
||||
"",
|
||||
"Cruiser",
|
||||
"",
|
||||
"Destroyer",
|
||||
"",
|
||||
"Aircraft Carrier"
|
||||
}
|
||||
|
||||
if self:IsShip() then
|
||||
|
||||
self:T( "Ship" )
|
||||
|
||||
--["Aircraft Carriers"] = {"Heavy armed ships",},
|
||||
--["Cruisers"] = {"Heavy armed ships",},
|
||||
--["Destroyers"] = {"Heavy armed ships",},
|
||||
--["Frigates"] = {"Heavy armed ships",},
|
||||
--["Corvettes"] = {"Heavy armed ships",},
|
||||
--["Heavy armed ships"] = {"Armed ships", "Armed Air Defence", "HeavyArmoredUnits",},
|
||||
--["Light armed ships"] = {"Armed ships","NonArmoredUnits"},
|
||||
--["Armed ships"] = {"Ships"},
|
||||
--["Unarmed ships"] = {"Ships","HeavyArmoredUnits",},
|
||||
|
||||
if Attributes["Aircraft Carriers"] then ThreatLevel = 10
|
||||
elseif Attributes["Destroyers"] then ThreatLevel = 8
|
||||
elseif Attributes["Cruisers"] then ThreatLevel = 6
|
||||
elseif Attributes["Frigates"] then ThreatLevel = 4
|
||||
elseif Attributes["Corvettes"] then ThreatLevel = 2
|
||||
elseif Attributes["Light armed ships"] then ThreatLevel = 1
|
||||
local ThreatLevels = {
|
||||
"Unarmed ship",
|
||||
"Light armed ships",
|
||||
"Corvettes",
|
||||
"",
|
||||
"Frigates",
|
||||
"",
|
||||
"Cruiser",
|
||||
"",
|
||||
"Destroyer",
|
||||
"",
|
||||
"Aircraft Carrier"
|
||||
}
|
||||
|
||||
|
||||
if Attributes["Aircraft Carriers"] then ThreatLevel = 10
|
||||
elseif Attributes["Destroyers"] then ThreatLevel = 8
|
||||
elseif Attributes["Cruisers"] then ThreatLevel = 6
|
||||
elseif Attributes["Frigates"] then ThreatLevel = 4
|
||||
elseif Attributes["Corvettes"] then ThreatLevel = 2
|
||||
elseif Attributes["Light armed ships"] then ThreatLevel = 1
|
||||
end
|
||||
|
||||
ThreatText = ThreatLevels[ThreatLevel+1]
|
||||
end
|
||||
|
||||
ThreatText = ThreatLevels[ThreatLevel+1]
|
||||
end
|
||||
|
||||
self:T2( ThreatLevel )
|
||||
@ -734,10 +761,9 @@ function UNIT:IsInZone( Zone )
|
||||
if self:IsAlive() then
|
||||
local IsInZone = Zone:IsVec3InZone( self:GetVec3() )
|
||||
|
||||
self:T2( { IsInZone } )
|
||||
self:E( { Unit = self.UnitName, IsInZone = IsInZone } )
|
||||
return IsInZone
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
@ -788,92 +814,6 @@ end
|
||||
|
||||
|
||||
|
||||
--- Signal a flare at the position of the UNIT.
|
||||
-- @param #UNIT self
|
||||
-- @param Utilities.Utils#FLARECOLOR FlareColor
|
||||
function UNIT:Flare( FlareColor )
|
||||
self:F2()
|
||||
trigger.action.signalFlare( self:GetVec3(), FlareColor , 0 )
|
||||
end
|
||||
|
||||
--- Signal a white flare at the position of the UNIT.
|
||||
-- @param #UNIT self
|
||||
function UNIT:FlareWhite()
|
||||
self:F2()
|
||||
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.White , 0 )
|
||||
end
|
||||
|
||||
--- Signal a yellow flare at the position of the UNIT.
|
||||
-- @param #UNIT self
|
||||
function UNIT:FlareYellow()
|
||||
self:F2()
|
||||
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Yellow , 0 )
|
||||
end
|
||||
|
||||
--- Signal a green flare at the position of the UNIT.
|
||||
-- @param #UNIT self
|
||||
function UNIT:FlareGreen()
|
||||
self:F2()
|
||||
trigger.action.signalFlare( self:GetVec3(), trigger.flareColor.Green , 0 )
|
||||
end
|
||||
|
||||
--- Signal a red flare at the position of the UNIT.
|
||||
-- @param #UNIT self
|
||||
function UNIT:FlareRed()
|
||||
self:F2()
|
||||
local Vec3 = self:GetVec3()
|
||||
if Vec3 then
|
||||
trigger.action.signalFlare( Vec3, trigger.flareColor.Red, 0 )
|
||||
end
|
||||
end
|
||||
|
||||
--- Smoke the UNIT.
|
||||
-- @param #UNIT self
|
||||
function UNIT:Smoke( SmokeColor, Range )
|
||||
self:F2()
|
||||
if Range then
|
||||
trigger.action.smoke( self:GetRandomVec3( Range ), SmokeColor )
|
||||
else
|
||||
trigger.action.smoke( self:GetVec3(), SmokeColor )
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Smoke the UNIT Green.
|
||||
-- @param #UNIT self
|
||||
function UNIT:SmokeGreen()
|
||||
self:F2()
|
||||
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Green )
|
||||
end
|
||||
|
||||
--- Smoke the UNIT Red.
|
||||
-- @param #UNIT self
|
||||
function UNIT:SmokeRed()
|
||||
self:F2()
|
||||
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Red )
|
||||
end
|
||||
|
||||
--- Smoke the UNIT White.
|
||||
-- @param #UNIT self
|
||||
function UNIT:SmokeWhite()
|
||||
self:F2()
|
||||
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.White )
|
||||
end
|
||||
|
||||
--- Smoke the UNIT Orange.
|
||||
-- @param #UNIT self
|
||||
function UNIT:SmokeOrange()
|
||||
self:F2()
|
||||
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Orange )
|
||||
end
|
||||
|
||||
--- Smoke the UNIT Blue.
|
||||
-- @param #UNIT self
|
||||
function UNIT:SmokeBlue()
|
||||
self:F2()
|
||||
trigger.action.smoke( self:GetVec3(), trigger.smokeColor.Blue )
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- Is methods
|
||||
|
||||
@ -2,6 +2,9 @@ Utilities/Routines.lua
|
||||
Utilities/Utils.lua
|
||||
|
||||
Core/Base.lua
|
||||
Core/UserFlag.lua
|
||||
Core/UserSound.lua
|
||||
Core/Report.lua
|
||||
Core/Scheduler.lua
|
||||
Core/ScheduleDispatcher.lua
|
||||
Core/Event.lua
|
||||
@ -11,10 +14,13 @@ Core/Zone.lua
|
||||
Core/Database.lua
|
||||
Core/Set.lua
|
||||
Core/Point.lua
|
||||
Core/Velocity.lua
|
||||
Core/Message.lua
|
||||
Core/Fsm.lua
|
||||
Core/Radio.lua
|
||||
Core/Spawn.lua
|
||||
Core/SpawnStatic.lua
|
||||
Core/Goal.lua
|
||||
Core/Cargo.lua
|
||||
Core/Spot.lua
|
||||
|
||||
@ -31,14 +37,17 @@ Wrapper/Scenery.lua
|
||||
|
||||
Functional/Scoring.lua
|
||||
Functional/CleanUp.lua
|
||||
Functional/Spawn.lua
|
||||
Functional/Movement.lua
|
||||
Functional/Sead.lua
|
||||
Functional/Escort.lua
|
||||
Functional/MissileTrainer.lua
|
||||
Functional/AirbasePolice.lua
|
||||
Functional/ATC_Ground.lua
|
||||
Functional/Detection.lua
|
||||
Functional/Designate.lua
|
||||
Functional/RAT.lua
|
||||
Functional/ZoneGoal.lua
|
||||
Functional/ZoneGoalCoalition.lua
|
||||
Functional/ZoneCaptureCoalition.lua
|
||||
|
||||
AI/AI_Balancer.lua
|
||||
AI/AI_A2A.lua
|
||||
@ -66,6 +75,7 @@ Tasking/Task_A2G.lua
|
||||
Tasking/Task_A2A_Dispatcher.lua
|
||||
Tasking/Task_A2A.lua
|
||||
Tasking/Task_Cargo.lua
|
||||
Tasking/TaskZoneCapture.lua
|
||||
|
||||
Moose.lua
|
||||
Mission.lua
|
||||
|
||||
93
Moose Mission Setup/Moose_.lua
Normal file
93
Moose Mission Setup/Moose_.lua
Normal file
@ -0,0 +1,93 @@
|
||||
env.info('*** MOOSE DYNAMIC INCLUDE START *** ')
|
||||
env.info('Moose Generation Timestamp: 20171031_0744')
|
||||
local base=_G
|
||||
__Moose={}
|
||||
__Moose.Include=function(IncludeFile)
|
||||
if not __Moose.Includes[IncludeFile]then
|
||||
__Moose.Includes[IncludeFile]=IncludeFile
|
||||
local f=assert(base.loadfile(__Moose.ProgramPath..IncludeFile))
|
||||
if f==nil then
|
||||
error("Moose: Could not load Moose file "..IncludeFile)
|
||||
else
|
||||
env.info("Moose: "..IncludeFile.." dynamically loaded from "..__Moose.ProgramPath)
|
||||
return f()
|
||||
end
|
||||
end
|
||||
end
|
||||
__Moose.ProgramPath="Scripts/Moose/"
|
||||
__Moose.Includes={}
|
||||
__Moose.Include('Utilities/Routines.lua')
|
||||
__Moose.Include('Utilities/Utils.lua')
|
||||
__Moose.Include('Core/Base.lua')
|
||||
__Moose.Include('Core/UserFlag.lua')
|
||||
__Moose.Include('Core/UserSound.lua')
|
||||
__Moose.Include('Core/Report.lua')
|
||||
__Moose.Include('Core/Scheduler.lua')
|
||||
__Moose.Include('Core/ScheduleDispatcher.lua')
|
||||
__Moose.Include('Core/Event.lua')
|
||||
__Moose.Include('Core/Settings.lua')
|
||||
__Moose.Include('Core/Menu.lua')
|
||||
__Moose.Include('Core/Zone.lua')
|
||||
__Moose.Include('Core/Database.lua')
|
||||
__Moose.Include('Core/Set.lua')
|
||||
__Moose.Include('Core/Point.lua')
|
||||
__Moose.Include('Core/Velocity.lua')
|
||||
__Moose.Include('Core/Message.lua')
|
||||
__Moose.Include('Core/Fsm.lua')
|
||||
__Moose.Include('Core/Radio.lua')
|
||||
__Moose.Include('Core/Spawn.lua')
|
||||
__Moose.Include('Core/SpawnStatic.lua')
|
||||
__Moose.Include('Core/Goal.lua')
|
||||
__Moose.Include('Core/Cargo.lua')
|
||||
__Moose.Include('Core/Spot.lua')
|
||||
__Moose.Include('Wrapper/Object.lua')
|
||||
__Moose.Include('Wrapper/Identifiable.lua')
|
||||
__Moose.Include('Wrapper/Positionable.lua')
|
||||
__Moose.Include('Wrapper/Controllable.lua')
|
||||
__Moose.Include('Wrapper/Group.lua')
|
||||
__Moose.Include('Wrapper/Unit.lua')
|
||||
__Moose.Include('Wrapper/Client.lua')
|
||||
__Moose.Include('Wrapper/Static.lua')
|
||||
__Moose.Include('Wrapper/Airbase.lua')
|
||||
__Moose.Include('Wrapper/Scenery.lua')
|
||||
__Moose.Include('Functional/Scoring.lua')
|
||||
__Moose.Include('Functional/CleanUp.lua')
|
||||
__Moose.Include('Functional/Movement.lua')
|
||||
__Moose.Include('Functional/Sead.lua')
|
||||
__Moose.Include('Functional/Escort.lua')
|
||||
__Moose.Include('Functional/MissileTrainer.lua')
|
||||
__Moose.Include('Functional/ATC_Ground.lua')
|
||||
__Moose.Include('Functional/Detection.lua')
|
||||
__Moose.Include('Functional/Designate.lua')
|
||||
__Moose.Include('Functional/RAT.lua')
|
||||
__Moose.Include('Functional/ZoneGoal.lua')
|
||||
__Moose.Include('Functional/ZoneGoalCoalition.lua')
|
||||
__Moose.Include('Functional/ZoneCaptureCoalition.lua')
|
||||
__Moose.Include('AI/AI_Balancer.lua')
|
||||
__Moose.Include('AI/AI_A2A.lua')
|
||||
__Moose.Include('AI/AI_A2A_Patrol.lua')
|
||||
__Moose.Include('AI/AI_A2A_Cap.lua')
|
||||
__Moose.Include('AI/AI_A2A_Gci.lua')
|
||||
__Moose.Include('AI/AI_A2A_Dispatcher.lua')
|
||||
__Moose.Include('AI/AI_Patrol.lua')
|
||||
__Moose.Include('AI/AI_Cap.lua')
|
||||
__Moose.Include('AI/AI_Cas.lua')
|
||||
__Moose.Include('AI/AI_Bai.lua')
|
||||
__Moose.Include('AI/AI_Formation.lua')
|
||||
__Moose.Include('Actions/Act_Assign.lua')
|
||||
__Moose.Include('Actions/Act_Route.lua')
|
||||
__Moose.Include('Actions/Act_Account.lua')
|
||||
__Moose.Include('Actions/Act_Assist.lua')
|
||||
__Moose.Include('Tasking/CommandCenter.lua')
|
||||
__Moose.Include('Tasking/Mission.lua')
|
||||
__Moose.Include('Tasking/Task.lua')
|
||||
__Moose.Include('Tasking/DetectionManager.lua')
|
||||
__Moose.Include('Tasking/Task_A2G_Dispatcher.lua')
|
||||
__Moose.Include('Tasking/Task_A2G.lua')
|
||||
__Moose.Include('Tasking/Task_A2A_Dispatcher.lua')
|
||||
__Moose.Include('Tasking/Task_A2A.lua')
|
||||
__Moose.Include('Tasking/Task_Cargo.lua')
|
||||
__Moose.Include('Tasking/TaskZoneCapture.lua')
|
||||
__Moose.Include('Moose.lua')
|
||||
BASE:TraceOnOff(true)
|
||||
env.info('*** MOOSE INCLUDE END *** ')
|
||||
362
Release 2.1.bb
362
Release 2.1.bb
@ -1,362 +0,0 @@
|
||||
[SIZE=7]MOOSE Release 2.1.0[/SIZE]
|
||||
|
||||
Finally it is here, release 2.1.0 of MOOSE!
|
||||
It took some time to prepare this release, as it was a lot of work to get the building blocks of the framework developed and tested. You'll find in this release a lot of new features as well as a couple of important bug fixes.
|
||||
|
||||
Release 2.1.0 is now published into the [B]master-release-2.1[/B] branch of this repository on github.
|
||||
You can download the file moose.lua below to use MOOSE in your missions.
|
||||
The moose.lua file is also located [URL="https://github.com/FlightControl-Master/MOOSE/blob/master-release-2.1/Moose%20Mission%20Setup/Moose.lua"]here[/URL] in the [B]master-release-2.1[/B] branch.
|
||||
|
||||
Those who are using the [B]master[/B] branch can continue to beta test, as new bleeding edge features will be added soon in preparation for release 2.2.0! There are many topics on the agenda to be added.
|
||||
|
||||
[B]This release would not have been possible without the help and contribution of many members of this community. THANK YOU![/B]
|
||||
|
||||
|
||||
|
||||
[SIZE=6]In summary:[/SIZE]
|
||||
|
||||
This release brings you [B]an improved tasking mechanism[/B].
|
||||
Tasking is the system in MOOSE that allows to:
|
||||
|
||||
* Execute [B]co-op[/B] missions and tasks
|
||||
* [B]Detect[/B] targets dynamically
|
||||
* Define new tasks [B]dynamically[/B]
|
||||
* Execute the tasks
|
||||
* Complete the mission [B]goals[/B]
|
||||
* Extensive menu system and briefings/reports for [B]player interaction[/B]
|
||||
* Improved Scoring of mission goal achievements, and task achievements.
|
||||
|
||||
On top, release brings you new functionality by the introduction of new classes to:
|
||||
|
||||
* [B]Designate targets[/B] (lase, smoke or illuminate targets) by AI, assisting your attack. Allows to drop laser guides bombs.
|
||||
* A new [B]tasking[/B] system to [B]transport cargo[/B] of various types
|
||||
* Dynamically [B]spawn static objects[/B]
|
||||
* Improved [B]coordinate system[/B]
|
||||
* Build [B]large formations[/B], like bombers flying to a target area
|
||||
|
||||
|
||||
|
||||
|
||||
[SIZE=6]1. TASKING SYSTEM![/SIZE]
|
||||
|
||||
A lot of work has been done in improving the tasking framework within MOOSE.
|
||||
|
||||
**The tasking system comes with TASK DISPATCHING mechanisms, that DYNAMICALLY
|
||||
allocate new tasks based on the tactical or strategical situation in the mission!!!
|
||||
These tasks can then be engaged upon by the players!!!**
|
||||
|
||||
The [URL="http://flightcontrol-master.github.io/MOOSE/Documentation/Task_A2G_Dispatcher.html"]TASK_A2G_DISPATCHER[/URL] class implements the dynamic dispatching of tasks upon groups of detected units determined a Set of FAC (groups). The FAC will detect units, will group them, and will dispatch Tasks to groups of players. Depending on the type of target detected, different tasks will be dispatched. Find a summary below describing for which situation a task type is created:
|
||||
|
||||
* [B]CAS Task[/B]: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter.
|
||||
* [B]BAI Task[/B]: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter.
|
||||
* [B]SEAD Task[/B]: Is created when there are enemy ground units wihtin range of the FAC, with air search radars.
|
||||
|
||||
More TASK_... dispatcher classes are to come in the future, like A2A, G2G, etc...
|
||||
|
||||
Improvements on the TASKING are in summary:
|
||||
|
||||
* A COMMANDCENTER has a dedicated menu.
|
||||
* A MISSION has a dedicated menu system.
|
||||
* A MISSION has a briefing report.
|
||||
* A MISSION has dedicated status reports.
|
||||
* A MISSION has for each TASK TYPE a menu.
|
||||
* A MISSION has for each TASK TYPE a dedicated menu system for each TASK defined.
|
||||
* A MISSION has an "assigned" task menu that contains menu actions relevant to the assigned task.
|
||||
* A TASK (of various types) has a dedicated menu system.
|
||||
* A TASK has a briefing report.
|
||||
* A TASK has dedicated status reports.
|
||||
* Player reports can be retrieved that explain which player is at which task.
|
||||
* ...
|
||||
|
||||
TASKING is vast, and at the moment there is too much to explain.
|
||||
[B]The best way to explore the TASKING is to TRY it...[/B]
|
||||
I suggest you have a look at the [URL="https://www.youtube.com/watch?v=v2Us8SS1-44&t=1070s"]GORI Valley Mission - Iteration 3[/URL].
|
||||
|
||||
Many people have contributed in the testing of the mechanism, especially:
|
||||
@baluballa, @doom, @whiplash
|
||||
|
||||
|
||||
|
||||
[SIZE=6]2. New MOOSE classes have been added.[/SIZE]
|
||||
|
||||
MOOSE 2.1.0 comes with new classes that extends the functionality of the MOOSE framework and allow you to do new things in your missions:
|
||||
|
||||
|
||||
|
||||
[SIZE=5]2.1. Target designation by laser, smoke or illumination.[/SIZE]
|
||||
|
||||
[URL="http://flightcontrol-master.github.io/MOOSE/Documentation/Designate.html"]DESIGNATE[/URL] is orchestrating the designation of potential targets executed by a Recce group,
|
||||
and communicates these to a dedicated attacking group of players,
|
||||
so that following a dynamically generated menu system,
|
||||
each detected set of potential targets can be lased or smoked...
|
||||
|
||||
Targets can be:
|
||||
|
||||
* [B]Lased[/B] for a period of time.
|
||||
* [B]Smoked[/B]. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.)
|
||||
* [B]Illuminated[/B] through an illumination bomb. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.
|
||||
|
||||
This class was made with the help of @EasyEB and many others.
|
||||
|
||||
[URL="https://www.youtube.com/playlist?list=PL7ZUrU4zZUl0dQ9UKQMb7YL8z2sKSqemH"]DESIGNATE is demonstrated on youtube[/URL]
|
||||
|
||||
DESIGNATE demonstration missions:
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/DES%20-%20Designation"]DES - Designation[/URL]
|
||||
|
||||
|
||||
|
||||
[SIZE=5]2.2. Transport cargo of different types to various locations as a human task within a mission.[/SIZE]
|
||||
|
||||
The Moose framework provides various CARGO classes that allow DCS physical or logical objects to be transported or sling loaded by Carriers.
|
||||
The CARGO_ classes, as part of the moose core, are able to Board, Load, UnBoard and UnLoad cargo between Carrier units.
|
||||
This collection of classes in this module define tasks for human players to handle these cargo objects.
|
||||
Cargo can be transported, picked-up, deployed and sling-loaded from and to other places.
|
||||
|
||||
[URL="http://flightcontrol-master.github.io/MOOSE/Documentation/Task_Cargo.html#TASK_CARGO_TRANSPORT"]TASK_CARGO_TRANSPORT[/URL] defines a task for a human player to transport a set of cargo between various zones.
|
||||
It is the first class that forms part of the TASK_CARGO classes suite.
|
||||
|
||||
The TASK_CARGO classes provide you with a flexible tasking sytem,
|
||||
that allows you to transport cargo of various types between various locations
|
||||
and various dedicated deployment zones.
|
||||
|
||||
A human player can join the battle field in a client airborne slot or a ground vehicle within the CA module (ALT-J).
|
||||
The player needs to accept the task from the task overview list within the mission, using the radio menus.
|
||||
Once the TASK_CARGO_TRANSPORT is assigned to the player and accepted by the player, the player will obtain
|
||||
an extra [B]Cargo Handling Radio Menu[/B] that contains the CARGO objects that need to be transported.
|
||||
Cargo can be transported towards different [B]Deployment Zones[/B], but can also be deployed anywhere within the battle field.
|
||||
|
||||
The Cargo Handling Radio Menu system allows to execute [B]various actions[/B] to handle the cargo.
|
||||
In the menu, you'll find for each CARGO, that is part of the scope of the task, various actions that can be completed.
|
||||
Depending on the location of your Carrier unit, the menu options will vary.
|
||||
|
||||
The [URL="http://flightcontrol-master.github.io/MOOSE/Documentation/Cargo.html#CARGO_GROUP"]CARGO_GROUP[/URL] class defines a
|
||||
cargo that is represented by a GROUP object within the simulator, and can be transported by a carrier.
|
||||
|
||||
The [URL="http://flightcontrol-master.github.io/MOOSE/Documentation/Cargo.html#CARGO_UNIT"]CARGO_UNIT[/URL] class defines a
|
||||
cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier.
|
||||
|
||||
Mission designers can use the [URL="http://flightcontrol-master.github.io/MOOSE/Documentation/Set.html#SET_CARGO"]SET_CARGO[/URL]
|
||||
class to build sets of cargos.
|
||||
|
||||
Note 1: [B]Various other CARGO classes are defined and are WIP[/B].
|
||||
Now that the foundation for Cargo handling is getting form, future releases will bring other types of CARGO handling
|
||||
classes to the MOOSE framework quickly. Sling-loading, package, beacon and other types of CARGO will be released soon.
|
||||
|
||||
Note 2: [B]AI_CARGO has been renamed to CARGO and now forms part of the Core or MOOSE[/B].
|
||||
If you were using AI_CARGO in your missions, please rename AI_CARGO with CARGO...
|
||||
|
||||
TASK_TRANSPORT_CARGO is demonstrated at the [URL="https://www.youtube.com/watch?v=v2Us8SS1-44&t=1070s"]GORI Valley Mission - Iteration 4[/URL]
|
||||
|
||||
TASK_TRANSPORT_CARGO demonstration missions:
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-110%20-%20Ground%20-%20Transport%20Cargo%20Group"]TSK-110 - Ground - Transport Cargo Group[/URL]
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-210%20-%20Helicopter%20-%20Transport%20Cargo%20Group"]TSK-210 - Helicopter - Transport Cargo Group[/URL]
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-211%20-%20Helicopter%20-%20Transport%20Multiple%20Cargo%20Groups"]TSK-211 - Helicopter - Transport Multiple Cargo Groups[/URL]
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-212%20-%20Helicopter%20-%20Cargo%20handle%20PickedUp%20and%20Deployed%20events"]TSK-212 - Helicopter - Cargo handle PickedUp and Deployed events[/URL]
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-213%20-%20Helicopter%20-%20Cargo%20Group%20Destroyed"]TSK-213 - Helicopter - Cargo Group Destroyed[/URL]
|
||||
|
||||
|
||||
|
||||
[SIZE=5]2.3. Dynamically spawn STATIC objects into your mission.[/SIZE]
|
||||
|
||||
The [URL="http://flightcontrol-master.github.io/MOOSE/Documentation/SpawnStatic.html#SPAWNSTATIC"]SPAWNSTATIC[/URL] class allows to spawn dynamically new Statics.
|
||||
By creating a copy of an existing static object template as defined in the Mission Editor (ME), SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc), and "copy" these properties to create a new static object and place it at the desired coordinate.
|
||||
New spawned Statics get the same name as the name of the template Static, or gets the given name when a new name is provided at the Spawn method.
|
||||
|
||||
SPAWNSTATIC demonstration missions:
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/SPS%20-%20Spawning%20Statics/SPS-100%20-%20Simple%20Spawning"]SPS-100 - Simple Spawning[/URL]
|
||||
|
||||
|
||||
|
||||
[SIZE=5]2.4. Better coordinate management in MGRS or LLor LLDecimal.[/SIZE]
|
||||
|
||||
The [URL="http://flightcontrol-master.github.io/MOOSE/Documentation/Point.html#COORDINATE"]COORDINATE[/URL] class
|
||||
defines a 2D coordinate in the simulator. A COORDINATE can be expressed in LL or in MGRS.
|
||||
|
||||
|
||||
|
||||
[SIZE=5]2.5. Improved scoring system[/SIZE]
|
||||
|
||||
Scoring is implemented throught the [URL="http://flightcontrol-master.github.io/MOOSE/Documentation/Scoring.html"]SCORING[/URL] class.
|
||||
The scoring system has been improved a lot! Now, the scoring is correctly counting scores on normal units, statics and scenary objects.
|
||||
Specific scores can be registered for specific targets. The scoring works together with the tasking system, so players can achieve
|
||||
additional scores when they achieve goals!
|
||||
|
||||
SCORING demonstration missions:
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCO%20-%20Scoring/SCO-100%20-%20Scoring%20of%20Statics"]SCO-100 - Scoring of Statics[/URL]
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCO%20-%20Scoring/SCO-101%20-%20Scoring%20Client%20to%20Client"]SCO-101 - Scoring Client to Client[/URL]
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCO%20-%20Scoring/SCO-500%20-%20Scoring%20Multi%20Player%20Demo%20Mission%201"]SCO-500 - Scoring Multi Player Demo Mission 1[/URL]
|
||||
|
||||
|
||||
|
||||
[SIZE=5]2.6. Beacons and Radio[/SIZE]
|
||||
|
||||
The Radio contains 2 classes : RADIO and BEACON
|
||||
|
||||
What are radio communications in DCS ?
|
||||
|
||||
* Radio transmissions consist of [B]sound files[/B] that are broadcasted on a specific [B]frequency[/B] (e.g. 115MHz) and [B]modulation[/B] (e.g. AM),
|
||||
* They can be [B]subtitled[/B] for a specific [B]duration[/B], the [B]power[/B] in Watts of the transmiter's antenna can be set, and the transmission can be [B]looped[/B].
|
||||
|
||||
These classes are the work of @Grey-Echo.
|
||||
|
||||
RADIO and BEACON demonstration missions:
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAD%20-%20Radio/RAD-000%20-%20Transmission%20from%20Static"]RAD-000 - Transmission from Static[/URL]
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAD%20-%20Radio/RAD-001%20-%20Transmission%20from%20UNIT%20or%20GROUP"]RAD-001 - Transmission from UNIT or GROUP[/URL]
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAD%20-%20Radio/RAD-002%20-%20Transmission%20Tips%20and%20Tricks"]RAD-002 - Transmission Tips and Tricks[/URL]
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAD%20-%20Radio/RAD-010%20-%20Beacons"] RAD-010 - Beacons[/URL]
|
||||
|
||||
|
||||
|
||||
[SIZE=5]2.7. Build large formations of AI.[/SIZE]
|
||||
|
||||
[URL="http://flightcontrol-master.github.io/MOOSE/Documentation/AI_Formation.html"]AI_FORMATION[/URL] makes AI @{GROUP}s fly in formation of various compositions.
|
||||
The AI_FORMATION class models formations in a different manner than the internal DCS formation logic!!!
|
||||
The purpose of the class is to:
|
||||
|
||||
* Make formation building a process that can be managed while in flight, rather than a task.
|
||||
* Human players can guide formations, consisting of larget planes.
|
||||
* Build large formations (like a large bomber field).
|
||||
* Form formations that DCS does not support off the shelve.
|
||||
|
||||
AI_FORMATION Demo Missions: [URL=""]FOR - AI Group Formation[/URL]
|
||||
|
||||
AI_FORMATION demonstration missions:
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-100%20-%20Bomber%20Left%20Line%20Formation"]FOR-100 - Bomber Left Line Formation[/URL]
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-101%20-%20Bomber%20Right%20Line%20Formation"]FOR-101 - Bomber Right Line Formation[/URL]
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-102%20-%20Bomber%20Left%20Wing%20Formation"]FOR-102 - Bomber Left Wing Formation[/URL]
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-103%20-%20Bomber%20Right%20Wing%20Formation"]FOR-103 - Bomber Right Wing Formation[/URL]
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-104%20-%20Bomber%20Center%20Wing%20Formation"]FOR-104 - Bomber Center Wing Formation[/URL]
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-105%20-%20Bomber%20Trail%20Formation"]FOR-105 - Bomber Trail Formation[/URL]
|
||||
* [URL="https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-106%20-%20Bomber%20Box%20Formation"]FOR-106 - Bomber Box Formation[/URL]
|
||||
|
||||
Note: The AI_FORMATION is currently a first version showing the potential, a "building block". From this class, further classes will be derived and the class will be fine-tuned.
|
||||
|
||||
|
||||
|
||||
[SIZE=6]3. A lot of components have been reworked and bugs have been fixed.[/SIZE]
|
||||
|
||||
|
||||
|
||||
[SIZE=5]3.1. Better event handling and event dispatching.[/SIZE]
|
||||
|
||||
The underlying mechanisms to handle DCS events has been improved. Bugs have been fixed.
|
||||
The MISSION_END event is now also supported.
|
||||
|
||||
|
||||
|
||||
[SIZE=5]2.2. Cargo handling has been made much better now.[/SIZE]
|
||||
|
||||
As a result, some of the WIP cargo classes that were defined earlier are still WIP.
|
||||
But as mentioned earlier, new CARGO classes can be published faster now.
|
||||
The framework is now more consistent internally.
|
||||
|
||||
|
||||
|
||||
[SIZE=6]3. A lot of new methods have been defined in several existing or new classes.[/SIZE]
|
||||
|
||||
AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefing ) --R2.1
|
||||
AI_FORMATION:TestSmokeDirectionVector( SmokeDirection ) --R2.1
|
||||
AI_FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1
|
||||
AI_FORMATION:onafterFormationTrail( FollowGroupSet, From , Event , To, XStart, XSpace, YStart ) --R2.1
|
||||
AI_FORMATION:onafterFormationStack( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace ) --R2.1
|
||||
AI_FORMATION:onafterFormationLeftLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1
|
||||
AI_FORMATION:onafterFormationRightLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1
|
||||
AI_FORMATION:onafterFormationLeftWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1
|
||||
AI_FORMATION:onafterFormationRightWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1
|
||||
AI_FORMATION:onafterFormationCenterWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1
|
||||
AI_FORMATION:onafterFormationVic( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1
|
||||
AI_FORMATION:onafterFormationBox( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels ) --R2.1
|
||||
AI_FORMATION:SetFlightRandomization( FlightRandomization ) --R2.1
|
||||
AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
|
||||
|
||||
CARGO:GetName()
|
||||
CARGO:GetObjectName()
|
||||
|
||||
DATABASE:ForEachStatic( IteratorFunction, FinalizeFunction, ... )
|
||||
|
||||
EVENT:Reset( EventObject ) --R2.1
|
||||
|
||||
POINT_VEC3:IsLOS( ToPointVec3 ) --R2.1
|
||||
|
||||
COORDINATE:New( x, y, LandHeightAdd ) --R2.1 Fixes issue #424.
|
||||
COORDINATE:NewFromVec2( Vec2, LandHeightAdd ) --R2.1 Fixes issue #424.
|
||||
COORDINATE:NewFromVec3( Vec3 ) --R2.1 Fixes issue #424.
|
||||
COORDINATE:ToStringLL( LL_Accuracy, LL_DMS ) --R2.1 Fixes issue #424.
|
||||
COORDINATE:ToStringMGRS( MGRS_Accuracy ) --R2.1 Fixes issue #424.
|
||||
COORDINATE:ToString() --R2.1 Fixes issue #424.
|
||||
COORDINATE:CoordinateMenu( RootMenu ) --R2.1 Fixes issue #424.
|
||||
COORDINATE:MenuSystem( System ) --R2.1 Fixes issue #424.
|
||||
COORDINATE:MenuLL_Accuracy( LL_Accuracy ) --R2.1 Fixes issue #424.
|
||||
COORDINATE:MenuLL_DMS( LL_DMS ) --R2.1 Fixes issue #424.
|
||||
COORDINATE:MenuMGRS_Accuracy( MGRS_Accuracy ) --R2.1 Fixes issue #424.
|
||||
|
||||
SET_BASE:FilterDeads() --R2.1 allow deads to be filtered to automatically handle deads in the collection.
|
||||
SET_BASE:FilterCrashes() --R2.1 allow crashes to be filtered to automatically handle crashes in the collection.
|
||||
|
||||
SET_UNIT:ForEachUnitPerThreatLevel( FromThreatLevel, ToThreatLevel, IteratorFunction, ... ) --R2.1 Threat Level implementation
|
||||
|
||||
SET_CARGO:New() --R2.1
|
||||
SET_CARGO:AddCargosByName( AddCargoNames ) --R2.1
|
||||
SET_CARGO:RemoveCargosByName( RemoveCargoNames ) --R2.1
|
||||
SET_CARGO:FindCargo( CargoName ) --R2.1
|
||||
SET_CARGO:FilterCoalitions( Coalitions ) --R2.1
|
||||
SET_CARGO:FilterTypes( Types ) --R2.1
|
||||
SET_CARGO:FilterCountries( Countries ) --R2.1
|
||||
SET_CARGO:FilterPrefixes( Prefixes ) --R2.1
|
||||
SET_CARGO:FilterStart() --R2.1
|
||||
SET_CARGO:AddInDatabase( Event ) --R2.1
|
||||
SET_CARGO:FindInDatabase( Event ) --R2.1
|
||||
SET_CARGO:ForEachCargo( IteratorFunction, ... ) --R2.1
|
||||
SET_CARGO:FindNearestCargoFromPointVec2( PointVec2 ) --R2.1
|
||||
SET_CARGO:IsIncludeObject( MCargo ) --R2.1
|
||||
SET_CARGO:OnEventNewCargo( EventData ) --R2.1
|
||||
SET_CARGO:OnEventDeleteCargo( EventData ) --R2.1 SpawnStatic.lua (5 matches)
|
||||
|
||||
SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, CountryID ) --R2.1
|
||||
SPAWNSTATIC:NewFromType( SpawnTypeName, SpawnShapeName, SpawnCategory, CountryID ) --R2.1
|
||||
SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1
|
||||
SPAWNSTATIC:SpawnFromZone( Zone, Heading, NewName ) --R2.1
|
||||
|
||||
ZONE_BASE:GetCoordinate( Height ) --R2.1
|
||||
|
||||
DESIGNATE:SetFlashStatusMenu( FlashMenu ) --R2.1
|
||||
DESIGNATE:SetLaserCodes( LaserCodes ) --R2.1
|
||||
DESIGNATE:GenerateLaserCodes() --R2.1
|
||||
DESIGNATE:SetAutoLase( AutoLase ) --R2.1
|
||||
DESIGNATE:SetThreatLevelPrioritization( Prioritize ) --R2.1
|
||||
|
||||
DETECTION_BASE:CleanDetectionItems() --R2.1 Clean the DetectionItems list
|
||||
DETECTION_BASE:GetDetectedItemID( Index ) --R2.1
|
||||
DETECTION_BASE:GetDetectedID( Index ) --R2.1
|
||||
DETECTION_AREAS:DetectedReportDetailed() --R2.1 Fixed missing report
|
||||
|
||||
REPORT:HasText() --R2.1
|
||||
REPORT:SetIndent( Indent ) --R2.1
|
||||
REPORT:AddIndent( Text ) --R2.1
|
||||
|
||||
MISSION:GetMenu( TaskGroup ) -- R2.1 -- Changed Menu Structure
|
||||
|
||||
TASK:SetMenu( MenuTime ) --R2.1 Mission Reports and Task Reports added. Fixes issue #424.
|
||||
TASK:ReportSummary() --R2.1 fixed report. Now nicely formatted and contains the info required.
|
||||
TASK:ReportOverview() --R2.1 fixed report. Now nicely formatted and contains the info required.
|
||||
TASK:GetPlayerCount() --R2.1 Get a count of the players.
|
||||
TASK:GetPlayerNames() --R2.1 Get a map of the players.
|
||||
TASK:ReportDetails() --R2.1 fixed report. Now nicely formatted and contains the info required.
|
||||
|
||||
UTILS.tostringMGRS = function(MGRS, acc) --R2.1
|
||||
|
||||
POSITIONABLE:GetBoundingBox() --R2.1
|
||||
POSITIONABLE:GetHeight() --R2.1
|
||||
POSITIONABLE:GetMessageText( Message, Name ) --R2.1 added
|
||||
POSITIONABLE:GetMessage( Message, Duration, Name ) --R2.1 changed callsign and name and using GetMessageText
|
||||
POSITIONABLE:MessageToSetGroup( Message, Duration, MessageSetGroup, Name ) --R2.1
|
||||
POSITIONABLE:GetRadio() --R2.1
|
||||
POSITIONABLE:GetBeacon() --R2.1
|
||||
POSITIONABLE:LaseUnit( Target, LaserCode, Duration ) --R2.1
|
||||
POSITIONABLE:LaseOff() --R2.1
|
||||
POSITIONABLE:IsLasing() --R2.1
|
||||
POSITIONABLE:GetSpot() --R2.1
|
||||
POSITIONABLE:GetLaserCode() --R2.1
|
||||
|
||||
UNIT:IsDetected( TargetUnit ) --R2.1
|
||||
UNIT:IsLOS( TargetUnit ) --R2.1
|
||||
363
Release 2.1.md
363
Release 2.1.md
@ -1,363 +0,0 @@
|
||||
# MOOSE Release 2.1.0
|
||||
|
||||
Finally it is here, release 2.1.0 of MOOSE!
|
||||
It took some time to prepare this release, as it was a lot of work to get the building blocks of the framework developed and tested. You'll find in this release a lot of new features as well as a couple of important bug fixes.
|
||||
|
||||
Release 2.1.0 is now published into the **master-release-2.1** branch of this repository on github.
|
||||
You can download the file moose.lua below to use MOOSE in your missions.
|
||||
The moose.lua file is also located [here](https://github.com/FlightControl-Master/MOOSE/blob/master-release-2.1/Moose%20Mission%20Setup/Moose.lua) in the **master-release-2.1** branch.
|
||||
|
||||
Those who are using the **master** branch can continue to beta test, as new bleeding edge features will be added soon in preparation for release 2.2.0! There are many topics on the agenda to be added.
|
||||
|
||||
**This release would not have been possible without the help and contribution of many
|
||||
members of this community. THANK YOU!**
|
||||
|
||||
|
||||
|
||||
## In summary:
|
||||
|
||||
This release brings you **an improved tasking mechanism**.
|
||||
Tasking is the system in MOOSE that allows to:
|
||||
|
||||
* Execute **co-op** missions and tasks
|
||||
* **Detect** targets dynamically
|
||||
* Define new tasks **dynamically**
|
||||
* Execute the tasks
|
||||
* Complete the mission **goals**
|
||||
* Extensive menu system and briefings/reports for **player interaction**
|
||||
* Improved Scoring of mission goal achievements, and task achievements.
|
||||
|
||||
On top, release brings you new functionality by the introduction of new classes to:
|
||||
|
||||
* **Designate targets** (lase, smoke or illuminate targets) by AI, assisting your attack. Allows to drop laser guides bombs.
|
||||
* A new **tasking** system to **transport cargo** of various types
|
||||
* Dynamically **spawn static objects**
|
||||
* Improved **coordinate system**
|
||||
* Build **large formations**, like bombers flying to a target area
|
||||
|
||||
|
||||
|
||||
|
||||
## 1. TASKING SYSTEM!
|
||||
|
||||
A lot of work has been done in improving the tasking framework within MOOSE.
|
||||
|
||||
**The tasking system comes with TASK DISPATCHING mechanisms, that DYNAMICALLY
|
||||
allocate new tasks based on the tactical or strategical situation in the mission!!!
|
||||
These tasks can then be engaged upon by the players!!!**
|
||||
|
||||
The [TASK\_A2G\_DISPATCHER](http://flightcontrol-master.github.io/MOOSE/Documentation/Task_A2G_Dispatcher.html) class implements the dynamic dispatching of tasks upon groups of detected units determined a Set of FAC (groups). The FAC will detect units, will group them, and will dispatch Tasks to groups of players. Depending on the type of target detected, different tasks will be dispatched. Find a summary below describing for which situation a task type is created:
|
||||
|
||||
* **CAS Task**: Is created when there are enemy ground units within range of the FAC, while there are friendly units in the FAC perimeter.
|
||||
* **BAI Task**: Is created when there are enemy ground units within range of the FAC, while there are NO other friendly units within the FAC perimeter.
|
||||
* **SEAD Task**: Is created when there are enemy ground units wihtin range of the FAC, with air search radars.
|
||||
|
||||
More TASK_... dispatcher classes are to come in the future, like A2A, G2G, etc...
|
||||
|
||||
Improvements on the TASKING are in summary:
|
||||
|
||||
* A COMMANDCENTER has a dedicated menu.
|
||||
* A MISSION has a dedicated menu system.
|
||||
* A MISSION has a briefing report.
|
||||
* A MISSION has dedicated status reports.
|
||||
* A MISSION has for each TASK TYPE a menu.
|
||||
* A MISSION has for each TASK TYPE a dedicated menu system for each TASK defined.
|
||||
* A MISSION has an "assigned" task menu that contains menu actions relevant to the assigned task.
|
||||
* A TASK (of various types) has a dedicated menu system.
|
||||
* A TASK has a briefing report.
|
||||
* A TASK has dedicated status reports.
|
||||
* Player reports can be retrieved that explain which player is at which task.
|
||||
* ...
|
||||
|
||||
TASKING is vast, and at the moment there is too much to explain.
|
||||
**The best way to explore the TASKING is to TRY it...**
|
||||
I suggest you have a look at the [GORI Valley Mission - Iteration 3](https://www.youtube.com/watch?v=v2Us8SS1-44&t=1070s).
|
||||
|
||||
Many people have contributed in the testing of the mechanism, especially:
|
||||
@baluballa, @doom, @whiplash
|
||||
|
||||
|
||||
|
||||
## 2. New MOOSE classes have been added.
|
||||
|
||||
MOOSE 2.1.0 comes with new classes that extends the functionality of the MOOSE framework and allow you to do new things in your missions:
|
||||
|
||||
|
||||
|
||||
### 2.1. Target designation by laser, smoke or illumination.
|
||||
|
||||
[DESIGNATE](http://flightcontrol-master.github.io/MOOSE/Documentation/Designate.html) is orchestrating the designation of potential targets executed by a Recce group,
|
||||
and communicates these to a dedicated attacking group of players,
|
||||
so that following a dynamically generated menu system,
|
||||
each detected set of potential targets can be lased or smoked...
|
||||
|
||||
Targets can be:
|
||||
|
||||
* **Lased** for a period of time.
|
||||
* **Smoked**. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.)
|
||||
* **Illuminated** through an illumination bomb. Artillery or airplanes with Illuminatino ordonance need to be present. (WIP, but early demo ready.
|
||||
|
||||
This class was made with the help of @EasyEB and many others.
|
||||
|
||||
[DESIGNATE is demonstrated on youtube](https://www.youtube.com/playlist?list=PL7ZUrU4zZUl0dQ9UKQMb7YL8z2sKSqemH)
|
||||
|
||||
DESIGNATE demonstration missions:
|
||||
* [DES - Designation](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/DES%20-%20Designation)
|
||||
|
||||
|
||||
|
||||
### 2.2. Transport cargo of different types to various locations as a human task within a mission.
|
||||
|
||||
The Moose framework provides various CARGO classes that allow DCS physical or logical objects to be transported or sling loaded by Carriers.
|
||||
The CARGO_ classes, as part of the moose core, are able to Board, Load, UnBoard and UnLoad cargo between Carrier units.
|
||||
This collection of classes in this module define tasks for human players to handle these cargo objects.
|
||||
Cargo can be transported, picked-up, deployed and sling-loaded from and to other places.
|
||||
|
||||
[TASK\_CARGO\_TRANSPORT](http://flightcontrol-master.github.io/MOOSE/Documentation/Task_Cargo.html#TASK_CARGO_TRANSPORT) defines a task for a human player to transport a set of cargo between various zones.
|
||||
It is the first class that forms part of the TASK_CARGO classes suite.
|
||||
|
||||
The TASK_CARGO classes provide you with a flexible tasking sytem,
|
||||
that allows you to transport cargo of various types between various locations
|
||||
and various dedicated deployment zones.
|
||||
|
||||
A human player can join the battle field in a client airborne slot or a ground vehicle within the CA module (ALT-J).
|
||||
The player needs to accept the task from the task overview list within the mission, using the radio menus.
|
||||
Once the TASK\_CARGO\_TRANSPORT is assigned to the player and accepted by the player, the player will obtain
|
||||
an extra **Cargo Handling Radio Menu** that contains the CARGO objects that need to be transported.
|
||||
Cargo can be transported towards different **Deployment Zones**, but can also be deployed anywhere within the battle field.
|
||||
|
||||
The Cargo Handling Radio Menu system allows to execute **various actions** to handle the cargo.
|
||||
In the menu, you'll find for each CARGO, that is part of the scope of the task, various actions that can be completed.
|
||||
Depending on the location of your Carrier unit, the menu options will vary.
|
||||
|
||||
The [CARGO_GROUP](http://flightcontrol-master.github.io/MOOSE/Documentation/Cargo.html#CARGO_GROUP) class defines a
|
||||
cargo that is represented by a GROUP object within the simulator, and can be transported by a carrier.
|
||||
|
||||
The [CARGO_UNIT](http://flightcontrol-master.github.io/MOOSE/Documentation/Cargo.html#CARGO_UNIT) class defines a
|
||||
cargo that is represented by a UNIT object within the simulator, and can be transported by a carrier.
|
||||
|
||||
Mission designers can use the [SET_CARGO](http://flightcontrol-master.github.io/MOOSE/Documentation/Set.html#SET_CARGO)
|
||||
class to build sets of cargos.
|
||||
|
||||
Note 1: **Various other CARGO classes are defined and are WIP**.
|
||||
Now that the foundation for Cargo handling is getting form, future releases will bring other types of CARGO handling
|
||||
classes to the MOOSE framework quickly. Sling-loading, package, beacon and other types of CARGO will be released soon.
|
||||
|
||||
Note 2: **AI_CARGO has been renamed to CARGO and now forms part of the Core or MOOSE**.
|
||||
If you were using AI_CARGO in your missions, please rename AI_CARGO with CARGO...
|
||||
|
||||
TASK\_TRANSPORT\_CARGO is demonstrated at the [GORI Valley Mission - Iteration 4](https://www.youtube.com/watch?v=v2Us8SS1-44&t=1070s)
|
||||
|
||||
TASK_TRANSPORT_CARGO demonstration missions:
|
||||
* [TSK-110 - Ground - Transport Cargo Group](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-110%20-%20Ground%20-%20Transport%20Cargo%20Group)
|
||||
* [TSK-210 - Helicopter - Transport Cargo Group](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-210%20-%20Helicopter%20-%20Transport%20Cargo%20Group)
|
||||
* [TSK-211 - Helicopter - Transport Multiple Cargo Groups](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-211%20-%20Helicopter%20-%20Transport%20Multiple%20Cargo%20Groups)
|
||||
* [TSK-212 - Helicopter - Cargo handle PickedUp and Deployed events](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-212%20-%20Helicopter%20-%20Cargo%20handle%20PickedUp%20and%20Deployed%20events)
|
||||
* [TSK-213 - Helicopter - Cargo Group Destroyed](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/TSK%20-%20Task%20Modelling/TSK-213%20-%20Helicopter%20-%20Cargo%20Group%20Destroyed)
|
||||
|
||||
|
||||
|
||||
### 2.3. Dynamically spawn STATIC objects into your mission.
|
||||
|
||||
The [SPAWNSTATIC](http://flightcontrol-master.github.io/MOOSE/Documentation/SpawnStatic.html#SPAWNSTATIC) class allows to spawn dynamically new Statics.
|
||||
By creating a copy of an existing static object template as defined in the Mission Editor (ME), SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc), and "copy" these properties to create a new static object and place it at the desired coordinate.
|
||||
New spawned Statics get the same name as the name of the template Static, or gets the given name when a new name is provided at the Spawn method.
|
||||
|
||||
SPAWNSTATIC demonstration missions:
|
||||
* [SPS-100 - Simple Spawning](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master-release-2.1/SPS%20-%20Spawning%20Statics/SPS-100%20-%20Simple%20Spawning)
|
||||
|
||||
|
||||
|
||||
### 2.4. Better coordinate management in MGRS or LLor LLDecimal.
|
||||
|
||||
The [COORDINATE](http://flightcontrol-master.github.io/MOOSE/Documentation/Point.html#COORDINATE) class
|
||||
defines a 2D coordinate in the simulator. A COORDINATE can be expressed in LL or in MGRS.
|
||||
|
||||
|
||||
|
||||
### 2.5. Improved scoring system
|
||||
|
||||
Scoring is implemented throught the [SCORING](http://flightcontrol-master.github.io/MOOSE/Documentation/Scoring.html) class.
|
||||
The scoring system has been improved a lot! Now, the scoring is correctly counting scores on normal units, statics and scenary objects.
|
||||
Specific scores can be registered for specific targets. The scoring works together with the tasking system, so players can achieve
|
||||
additional scores when they achieve goals!
|
||||
|
||||
SCORING demonstration missions:
|
||||
* [SCO-100 - Scoring of Statics](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCO%20-%20Scoring/SCO-100%20-%20Scoring%20of%20Statics)
|
||||
* [SCO-101 - Scoring Client to Client](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCO%20-%20Scoring/SCO-101%20-%20Scoring%20Client%20to%20Client)
|
||||
* [SCO-500 - Scoring Multi Player Demo Mission 1](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/SCO%20-%20Scoring/SCO-500%20-%20Scoring%20Multi%20Player%20Demo%20Mission%201)
|
||||
|
||||
|
||||
|
||||
### 2.6. Beacons and Radio
|
||||
|
||||
The Radio contains 2 classes : RADIO and BEACON
|
||||
|
||||
What are radio communications in DCS ?
|
||||
|
||||
* Radio transmissions consist of **sound files** that are broadcasted on a specific **frequency** (e.g. 115MHz) and **modulation** (e.g. AM),
|
||||
* They can be **subtitled** for a specific **duration**, the **power** in Watts of the transmiter's antenna can be set, and the transmission can be **looped**.
|
||||
|
||||
These classes are the work of @Grey-Echo.
|
||||
|
||||
RADIO and BEACON demonstration missions:
|
||||
* [RAD-000 - Transmission from Static](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAD%20-%20Radio/RAD-000%20-%20Transmission%20from%20Static)
|
||||
* [RAD-001 - Transmission from UNIT or GROUP](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAD%20-%20Radio/RAD-001%20-%20Transmission%20from%20UNIT%20or%20GROUP)
|
||||
* [RAD-002 - Transmission Tips and Tricks](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAD%20-%20Radio/RAD-002%20-%20Transmission%20Tips%20and%20Tricks)
|
||||
* [ RAD-010 - Beacons](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/RAD%20-%20Radio/RAD-010%20-%20Beacons)
|
||||
|
||||
|
||||
|
||||
### 2.7. Build large formations of AI.
|
||||
|
||||
[AI_FORMATION](http://flightcontrol-master.github.io/MOOSE/Documentation/AI_Formation.html) makes AI @{GROUP}s fly in formation of various compositions.
|
||||
The AI_FORMATION class models formations in a different manner than the internal DCS formation logic!!!
|
||||
The purpose of the class is to:
|
||||
|
||||
* Make formation building a process that can be managed while in flight, rather than a task.
|
||||
* Human players can guide formations, consisting of larget planes.
|
||||
* Build large formations (like a large bomber field).
|
||||
* Form formations that DCS does not support off the shelve.
|
||||
|
||||
AI_FORMATION Demo Missions: [FOR - AI Group Formation]()
|
||||
|
||||
AI\_FORMATION demonstration missions:
|
||||
* [FOR-100 - Bomber Left Line Formation](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-100%20-%20Bomber%20Left%20Line%20Formation)
|
||||
* [FOR-101 - Bomber Right Line Formation](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-101%20-%20Bomber%20Right%20Line%20Formation)
|
||||
* [FOR-102 - Bomber Left Wing Formation](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-102%20-%20Bomber%20Left%20Wing%20Formation)
|
||||
* [FOR-103 - Bomber Right Wing Formation](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-103%20-%20Bomber%20Right%20Wing%20Formation)
|
||||
* [FOR-104 - Bomber Center Wing Formation](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-104%20-%20Bomber%20Center%20Wing%20Formation)
|
||||
* [FOR-105 - Bomber Trail Formation](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-105%20-%20Bomber%20Trail%20Formation)
|
||||
* [FOR-106 - Bomber Box Formation](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/FOR%20-%20AI%20Group%20Formation/FOR-106%20-%20Bomber%20Box%20Formation)
|
||||
|
||||
Note: The AI_FORMATION is currently a first version showing the potential, a "building block". From this class, further classes will be derived and the class will be fine-tuned.
|
||||
|
||||
|
||||
|
||||
## 3. A lot of components have been reworked and bugs have been fixed.
|
||||
|
||||
|
||||
|
||||
### 3.1. Better event handling and event dispatching.
|
||||
|
||||
The underlying mechanisms to handle DCS events has been improved. Bugs have been fixed.
|
||||
The MISSION_END event is now also supported.
|
||||
|
||||
|
||||
|
||||
### 2.2. Cargo handling has been made much better now.
|
||||
|
||||
As a result, some of the WIP cargo classes that were defined earlier are still WIP.
|
||||
But as mentioned earlier, new CARGO classes can be published faster now.
|
||||
The framework is now more consistent internally.
|
||||
|
||||
|
||||
|
||||
## 3. A lot of new methods have been defined in several existing or new classes.
|
||||
|
||||
AI_FORMATION:New( FollowUnit, FollowGroupSet, FollowName, FollowBriefing ) --R2.1
|
||||
AI_FORMATION:TestSmokeDirectionVector( SmokeDirection ) --R2.1
|
||||
AI_FORMATION:onafterFormationLine( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1
|
||||
AI_FORMATION:onafterFormationTrail( FollowGroupSet, From , Event , To, XStart, XSpace, YStart ) --R2.1
|
||||
AI_FORMATION:onafterFormationStack( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace ) --R2.1
|
||||
AI_FORMATION:onafterFormationLeftLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1
|
||||
AI_FORMATION:onafterFormationRightLine( FollowGroupSet, From , Event , To, XStart, YStart, ZStart, ZSpace ) --R2.1
|
||||
AI_FORMATION:onafterFormationLeftWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1
|
||||
AI_FORMATION:onafterFormationRightWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, ZStart, ZSpace ) --R2.1
|
||||
AI_FORMATION:onafterFormationCenterWing( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1
|
||||
AI_FORMATION:onafterFormationVic( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace ) --R2.1
|
||||
AI_FORMATION:onafterFormationBox( FollowGroupSet, From , Event , To, XStart, XSpace, YStart, YSpace, ZStart, ZSpace, ZLevels ) --R2.1
|
||||
AI_FORMATION:SetFlightRandomization( FlightRandomization ) --R2.1
|
||||
AI_FORMATION:onenterFollowing( FollowGroupSet ) --R2.1
|
||||
|
||||
CARGO:GetName()
|
||||
CARGO:GetObjectName()
|
||||
|
||||
DATABASE:ForEachStatic( IteratorFunction, FinalizeFunction, ... )
|
||||
|
||||
EVENT:Reset( EventObject ) --R2.1
|
||||
|
||||
POINT_VEC3:IsLOS( ToPointVec3 ) --R2.1
|
||||
|
||||
COORDINATE:New( x, y, LandHeightAdd ) --R2.1 Fixes issue #424.
|
||||
COORDINATE:NewFromVec2( Vec2, LandHeightAdd ) --R2.1 Fixes issue #424.
|
||||
COORDINATE:NewFromVec3( Vec3 ) --R2.1 Fixes issue #424.
|
||||
COORDINATE:ToStringLL( LL_Accuracy, LL_DMS ) --R2.1 Fixes issue #424.
|
||||
COORDINATE:ToStringMGRS( MGRS_Accuracy ) --R2.1 Fixes issue #424.
|
||||
COORDINATE:ToString() --R2.1 Fixes issue #424.
|
||||
COORDINATE:CoordinateMenu( RootMenu ) --R2.1 Fixes issue #424.
|
||||
COORDINATE:MenuSystem( System ) --R2.1 Fixes issue #424.
|
||||
COORDINATE:MenuLL_Accuracy( LL_Accuracy ) --R2.1 Fixes issue #424.
|
||||
COORDINATE:MenuLL_DMS( LL_DMS ) --R2.1 Fixes issue #424.
|
||||
COORDINATE:MenuMGRS_Accuracy( MGRS_Accuracy ) --R2.1 Fixes issue #424.
|
||||
|
||||
SET_BASE:FilterDeads() --R2.1 allow deads to be filtered to automatically handle deads in the collection.
|
||||
SET_BASE:FilterCrashes() --R2.1 allow crashes to be filtered to automatically handle crashes in the collection.
|
||||
|
||||
SET_UNIT:ForEachUnitPerThreatLevel( FromThreatLevel, ToThreatLevel, IteratorFunction, ... ) --R2.1 Threat Level implementation
|
||||
|
||||
SET_CARGO:New() --R2.1
|
||||
SET_CARGO:AddCargosByName( AddCargoNames ) --R2.1
|
||||
SET_CARGO:RemoveCargosByName( RemoveCargoNames ) --R2.1
|
||||
SET_CARGO:FindCargo( CargoName ) --R2.1
|
||||
SET_CARGO:FilterCoalitions( Coalitions ) --R2.1
|
||||
SET_CARGO:FilterTypes( Types ) --R2.1
|
||||
SET_CARGO:FilterCountries( Countries ) --R2.1
|
||||
SET_CARGO:FilterPrefixes( Prefixes ) --R2.1
|
||||
SET_CARGO:FilterStart() --R2.1
|
||||
SET_CARGO:AddInDatabase( Event ) --R2.1
|
||||
SET_CARGO:FindInDatabase( Event ) --R2.1
|
||||
SET_CARGO:ForEachCargo( IteratorFunction, ... ) --R2.1
|
||||
SET_CARGO:FindNearestCargoFromPointVec2( PointVec2 ) --R2.1
|
||||
SET_CARGO:IsIncludeObject( MCargo ) --R2.1
|
||||
SET_CARGO:OnEventNewCargo( EventData ) --R2.1
|
||||
SET_CARGO:OnEventDeleteCargo( EventData ) --R2.1 SpawnStatic.lua (5 matches)
|
||||
|
||||
SPAWNSTATIC:NewFromStatic( SpawnTemplatePrefix, CountryID ) --R2.1
|
||||
SPAWNSTATIC:NewFromType( SpawnTypeName, SpawnShapeName, SpawnCategory, CountryID ) --R2.1
|
||||
SPAWNSTATIC:SpawnFromPointVec2( PointVec2, Heading, NewName ) --R2.1
|
||||
SPAWNSTATIC:SpawnFromZone( Zone, Heading, NewName ) --R2.1
|
||||
|
||||
ZONE_BASE:GetCoordinate( Height ) --R2.1
|
||||
|
||||
DESIGNATE:SetFlashStatusMenu( FlashMenu ) --R2.1
|
||||
DESIGNATE:SetLaserCodes( LaserCodes ) --R2.1
|
||||
DESIGNATE:GenerateLaserCodes() --R2.1
|
||||
DESIGNATE:SetAutoLase( AutoLase ) --R2.1
|
||||
DESIGNATE:SetThreatLevelPrioritization( Prioritize ) --R2.1
|
||||
|
||||
DETECTION_BASE:CleanDetectionItems() --R2.1 Clean the DetectionItems list
|
||||
DETECTION_BASE:GetDetectedItemID( Index ) --R2.1
|
||||
DETECTION_BASE:GetDetectedID( Index ) --R2.1
|
||||
DETECTION_AREAS:DetectedReportDetailed() --R2.1 Fixed missing report
|
||||
|
||||
REPORT:HasText() --R2.1
|
||||
REPORT:SetIndent( Indent ) --R2.1
|
||||
REPORT:AddIndent( Text ) --R2.1
|
||||
|
||||
MISSION:GetMenu( TaskGroup ) -- R2.1 -- Changed Menu Structure
|
||||
|
||||
TASK:SetMenu( MenuTime ) --R2.1 Mission Reports and Task Reports added. Fixes issue #424.
|
||||
TASK:ReportSummary() --R2.1 fixed report. Now nicely formatted and contains the info required.
|
||||
TASK:ReportOverview() --R2.1 fixed report. Now nicely formatted and contains the info required.
|
||||
TASK:GetPlayerCount() --R2.1 Get a count of the players.
|
||||
TASK:GetPlayerNames() --R2.1 Get a map of the players.
|
||||
TASK:ReportDetails() --R2.1 fixed report. Now nicely formatted and contains the info required.
|
||||
|
||||
UTILS.tostringMGRS = function(MGRS, acc) --R2.1
|
||||
|
||||
POSITIONABLE:GetBoundingBox() --R2.1
|
||||
POSITIONABLE:GetHeight() --R2.1
|
||||
POSITIONABLE:GetMessageText( Message, Name ) --R2.1 added
|
||||
POSITIONABLE:GetMessage( Message, Duration, Name ) --R2.1 changed callsign and name and using GetMessageText
|
||||
POSITIONABLE:MessageToSetGroup( Message, Duration, MessageSetGroup, Name ) --R2.1
|
||||
POSITIONABLE:GetRadio() --R2.1
|
||||
POSITIONABLE:GetBeacon() --R2.1
|
||||
POSITIONABLE:LaseUnit( Target, LaserCode, Duration ) --R2.1
|
||||
POSITIONABLE:LaseOff() --R2.1
|
||||
POSITIONABLE:IsLasing() --R2.1
|
||||
POSITIONABLE:GetSpot() --R2.1
|
||||
POSITIONABLE:GetLaserCode() --R2.1
|
||||
|
||||
UNIT:IsDetected( TargetUnit ) --R2.1
|
||||
UNIT:IsLOS( TargetUnit ) --R2.1
|
||||
BIN
Release Notes 2.2.0.docx
Normal file
BIN
Release Notes 2.2.0.docx
Normal file
Binary file not shown.
BIN
Release Notes 2.2.0.pdf
Normal file
BIN
Release Notes 2.2.0.pdf
Normal file
Binary file not shown.
565
Utils/DCS_ControlAPI.txt
Normal file
565
Utils/DCS_ControlAPI.txt
Normal file
@ -0,0 +1,565 @@
|
||||
DCS Simulation Control User Scripts
|
||||
====================================
|
||||
|
||||
The behaviour of the DCS can be altered using the *GameGUI.lua scripts.
|
||||
You define the hooks to the DCS events, and then do what you want using the provided API.
|
||||
===================================================================================================
|
||||
|
||||
When loading, DCS searches for Saved Games\DCS\Scripts\*GameGUI.lua files,
|
||||
sorts them by name and then loads into the GUI Lua-state.
|
||||
Each user script is loaded into an isolated environment, so the only
|
||||
thing they share is the state of the simulator.
|
||||
|
||||
Each script defines a set of callbacks to the DCS events and sets them with the call
|
||||
DCS.setUserCallbacks(cb_table)
|
||||
For each callback type the hooks of all user scripts will be called in order of loading.
|
||||
|
||||
For callbacks which are supposed to returning a value, currently there are 3 of them:
|
||||
onPlayerTryConnect
|
||||
onPlayerTrySendChat
|
||||
onPlayerTryChangeSlot
|
||||
returning a value means breaking the hook call chain.
|
||||
Returning nothing (or nil) means continuing the hook chain, which ends with the default allow-all handlers.
|
||||
|
||||
The example user script 'testGameGUI.lua':
|
||||
----------------------------------------------------------------------------------------------
|
||||
local test = {}
|
||||
|
||||
function test.onPlayerTryConnect(ipaddr, name, ucid, playerID)
|
||||
print('onPlayerTryConnect(%s, %s, %s, %d)', ipaddr, name, ucid, playerID)
|
||||
-- if you want to gently intercept the call, allowing other user scripts to get it,
|
||||
-- you better return nothing here
|
||||
return true -- allow the player to connect
|
||||
end
|
||||
|
||||
function test.onSimulationStart()
|
||||
print('Current mission is '..DCS.getMissionName())
|
||||
end
|
||||
|
||||
DCS.setUserCallbacks(test) -- here we set our callbacks
|
||||
----------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
The available API are documented below.
|
||||
The full list of the callbacks is at the end of this document.
|
||||
|
||||
In addition, all standard lua 5.1 libraries are available as well, namely:
|
||||
base api, like print, etc,
|
||||
math.*
|
||||
table.*
|
||||
string.*
|
||||
io.*
|
||||
os.*
|
||||
debug.*
|
||||
|
||||
===================================================================================================
|
||||
|
||||
|
||||
|
||||
Lua File System (lfs) API
|
||||
-------------------------------
|
||||
lfs.currentdir() -> string
|
||||
Returns the path of the DCS install folder
|
||||
|
||||
lfs.writedir() -> string
|
||||
Returns the path of the current 'Saved Games\DCS' folder.
|
||||
|
||||
lfs.tempdir() -> string
|
||||
Returns the pat of the DCS Temp folder (AppData\Local\Temp\DCS).
|
||||
|
||||
lfs.mkdir()
|
||||
lfs.rmdir()
|
||||
lfs.attributes()
|
||||
lfs.dir()
|
||||
lfs.normpath()
|
||||
lfs.realpath()
|
||||
|
||||
|
||||
|
||||
DCS Control API, table 'DCS.*'
|
||||
-------------------------------
|
||||
|
||||
DCS.setPause(bool)
|
||||
Pauses/resumes the simulation. Server-side only.
|
||||
|
||||
DCS.getPause() -> bool
|
||||
true if simulation is paused
|
||||
|
||||
DCS.stopMission()
|
||||
stops current mission
|
||||
|
||||
DCS.exitProcess()
|
||||
Exits the DCS process.
|
||||
|
||||
DCS.isMultiplayer() -> bool
|
||||
True when running in the multiplayer mode.
|
||||
|
||||
DCS.isServer() -> bool
|
||||
True when running as a server or in the single-player mode.
|
||||
|
||||
DCS.getModelTime() -> number
|
||||
returns current DCS simulation time in seconds.
|
||||
|
||||
DCS.getRealTime() -> number
|
||||
returns current DCS real time in seconds relative to the DCS start time.
|
||||
|
||||
DCS.getMissionOptions() -> table
|
||||
Returns the value of 'mission.options'
|
||||
|
||||
DCS.getMissionDescription() -> string
|
||||
translated mission.descriptionText string
|
||||
|
||||
DCS.getAvailableCoalitions() -> table {
|
||||
[coalition_id] = { name = "coalition name", }
|
||||
...
|
||||
}
|
||||
Returns a list of coalitions which have available slots.
|
||||
|
||||
DCS.getAvailableSlots(coalitionID) -> array of {unitId, type, role, callsign, groupName, country}
|
||||
Returns the list of available slots.
|
||||
NOTE: the returned unitID is actually a slotID, which for multi-seat units is 'unitID_seatID'
|
||||
|
||||
DCS.getCurrentMission() -> table with the currently loaded mission
|
||||
NOTE: to get valid mission.options use DCS.getMissionOptions()
|
||||
|
||||
DCS.getMissionName() -> string
|
||||
Returns the name of the current mission
|
||||
|
||||
DCS.getMissionFilename() -> string
|
||||
Returns the file name of the current mission (returns nil when acting as a multiplayer client).
|
||||
|
||||
DCS.getMissionResult(string side) -> integer [0, 100]
|
||||
Gets missin result for either 'red' or 'blue'
|
||||
|
||||
DCS.getUnitProperty(missionId, propertyId) -> string
|
||||
propertyId:
|
||||
DCS.UNIT_RUNTIME_ID, // unique within runtime mission. int
|
||||
DCS.UNIT_MISSION_ID, // unique within mission file. int>0
|
||||
DCS.UNIT_NAME, // unit name, as assigned by mission designer.
|
||||
DCS.UNIT_TYPE, // unit type (Ural, ZU-23, etc)
|
||||
DCS.UNIT_CATEGORY,
|
||||
DCS.UNIT_GROUP_MISSION_ID, // group ID, unique within mission file. int>0
|
||||
DCS.UNIT_GROUPNAME, // group name, as assigned by mission designer.
|
||||
DCS.UNIT_GROUPCATEGORY,
|
||||
DCS.UNIT_CALLSIGN,
|
||||
DCS.UNIT_HIDDEN,// ME hiding
|
||||
DCS.UNIT_COALITION,// "blue", "red" or "unknown"
|
||||
DCS.UNIT_COUNTRY_ID,
|
||||
DCS.UNIT_TASK, //"unit.group.task"
|
||||
DCS.UNIT_PLAYER_NAME, // valid for network "humanable" units
|
||||
DCS.UNIT_ROLE,//"artillery_commander", "instructor", etc
|
||||
DCS.UNIT_INVISIBLE_MAP_ICON,//ME invisible map icon
|
||||
|
||||
DCS.getUnitType(missionId) -> typeId
|
||||
a shortcut for DCS.getUnitProperty(missionId, DCS.UNIT_TYPE)
|
||||
|
||||
DCS.getUnitTypeAttribute(typeId, attr) -> string
|
||||
Returns a value from Database: Objects[typeId][attr],
|
||||
for example DCS.getUnitTypeAttribute("Ural", "DisplayName")
|
||||
|
||||
DCS.writeDebriefing(str)
|
||||
Writes a custom string to the debriefing file
|
||||
|
||||
DCS.setUserCallbacks(cb_table)
|
||||
Hooks the callbacks using the handlers from the provided table.
|
||||
See: "GameGUI scripts" section.
|
||||
|
||||
|
||||
|
||||
Logging API 'log.*'
|
||||
------------------------
|
||||
Logging works as follows:
|
||||
a) each log message is accompanied with 2 attributes: a subsystem, and level.
|
||||
b) after each messages gets into a logger it passes (asynchronously) through
|
||||
a series of output filters which decide where the message will be written to.
|
||||
|
||||
Writing to log is done by:
|
||||
|
||||
log.write(SUBSYSTEM_NAME, LOG_LEVEL, message, ...)
|
||||
if there are any arguments after 'message',
|
||||
the actual string is formed as string.format(message, ...)
|
||||
|
||||
SUBSYSTEM_NAME is a string
|
||||
LOG_LEVEL is one of the values, listed below
|
||||
see log.set_output()
|
||||
|
||||
log.set_output(log_file_name_wo_ext, rule_subsystem_name, rule_level_mask, rule_output_mode)
|
||||
|
||||
the args:
|
||||
log_file_name_wo_ext: resulting log will be written to $WRITE_DIR/Logs/<log_file_name_wo_ext>.log
|
||||
|
||||
rule_subsytem_name: the name of the subsystem whose messages to write or empty string to match all subsystems
|
||||
|
||||
rule_level_mask: a sum of log-level bit flags to match messages
|
||||
valid flags are:
|
||||
log.ALERT
|
||||
log.ERROR
|
||||
log.WARNING
|
||||
log.INFO
|
||||
log.DEBUG
|
||||
log.ALL - includes all of the above
|
||||
log.TRACE - a special level which is excluded from dcs.log file
|
||||
|
||||
rule_output_mode: a sum of output flags:
|
||||
log.MESSAGE
|
||||
log.TIME
|
||||
log.MODULE - this is a 'subsystem', not a dlc
|
||||
log.LEVEL
|
||||
log.FULL - all of the above
|
||||
|
||||
So, in order to save net.trace(msg) messages to a file, you should issue a call:
|
||||
log.set_output('lua-net', 'LuaNET', log.TRACE, log.MESSAGE + log.TIME)
|
||||
|
||||
This will write to a Logs/lua-net.log file
|
||||
|
||||
Or, to save everything lua-network-related:
|
||||
log.set_output('lua-net', 'LuaNET', log.TRACE + log.ALL, log.MESSAGE + log.TIME + log.LEVEL)
|
||||
|
||||
To close the log file, you must use
|
||||
log.set_output('lua-net', '', 0, 0)
|
||||
|
||||
log.* API is available in the 'Saved Games\DCS\Config\autoexec.cfg' file as well so you can control log output in you local machine.
|
||||
|
||||
|
||||
|
||||
Network specific API, available through the table 'net.'
|
||||
----------------------------------------------------------------
|
||||
|
||||
net.log(msg) -- equivalent to log.write('LuaNET', log.INFO, msg)
|
||||
net.trace(msg) -- equivalent to log.write('LuaNET', log.TRACE, msg)
|
||||
|
||||
What is the difference: log() always writes to dcs.log, but may lose messages if the output rate is too high.
|
||||
trace() output never appears in the dcs.log file, it must be explicitly directed to a log file.
|
||||
It never loses messages when there's an active output, but it may block if output rate is higher than writing to the log file.
|
||||
To control logger output you can use $WRITE_DIR/Config/autoexec.cfg file, or call this from your network script
|
||||
(log.* API, see above)
|
||||
|
||||
|
||||
net.dostring_in(state, string) -> string
|
||||
Executes a lua-string in a given internal lua-state and returns a string result
|
||||
Valid state names are:
|
||||
'config': the state in which $INSTALL_DIR/Config/main.cfg is executed, as well as $WRITE_DIR/Config/autoexec.cfg
|
||||
used for configuration settings
|
||||
'mission': holds current mission
|
||||
'export': runs $WRITE_DIR/Scripts/Export.lua and the relevant export API
|
||||
|
||||
net.send_chat(string message, bool all)
|
||||
Send chat message. If not all, then send to my coalition (side) only.
|
||||
|
||||
net.send_chat_to(string message, playerID to)
|
||||
Send direct chat message to a player
|
||||
Server-side only:
|
||||
net.send_chat_to(string message, playerID to[, playerID from])
|
||||
|
||||
net.recv_chat(message[, int from=0])
|
||||
Receive chat message locally[, pretending it was sent by another player].
|
||||
from = 0 means from the system
|
||||
|
||||
net.load_mission(miz_filename)
|
||||
Loads a specified mission, temporarily overriding the server mission list.
|
||||
SERVER ONLY
|
||||
|
||||
net.load_next_mission() -> bool
|
||||
Load the next mission from the server mission list. Returns false if list end is reached
|
||||
SERVER ONLY
|
||||
|
||||
net.get_player_list() -> array of playerID
|
||||
Returns the list of currently connected players
|
||||
|
||||
net.get_my_player_id() -> playerID
|
||||
Returns the playerID of the local player. Currently always 1 for the server.
|
||||
|
||||
net.get_server_id() -> playerID
|
||||
Returns playerID of the server. Currently, always 1.
|
||||
|
||||
net.get_player_info(playerID) -> table
|
||||
Returns a table of all player attributes or nil if playerID is invalid
|
||||
|
||||
net.get_player_info(playerID, attrName) -> value
|
||||
Returns a value of a given attribute for the playerID.
|
||||
|
||||
Currently defined attributes are:
|
||||
'id': playerID
|
||||
'name': player name
|
||||
'side': 0 - spectators, 1 - red, 2 - blue
|
||||
'slot': slotID of the player or ''
|
||||
'ping': ping of the player in ms
|
||||
'ipaddr': IP address of the player, SERVER ONLY
|
||||
'ucid': Unique Client Identifier, SERVER ONLY
|
||||
|
||||
net.kick(id, message)
|
||||
Kick a player.
|
||||
|
||||
net.get_stat(playerID, statID) -> integer
|
||||
Get statistics for player. statIDs are:
|
||||
net.PS_PING (0) - ping (in ms)
|
||||
net.PS_CRASH (1) - number of crashes
|
||||
net.PS_CAR (2) - number of destroyed vehicles
|
||||
net.PS_PLANE (3) - ... planes/helicopters
|
||||
net.PS_SHIP (4) - ... ships
|
||||
net.PS_SCORE (5) - total score
|
||||
net.PS_LAND (6) - number of landings
|
||||
net.PS_EJECT (7) - of ejects
|
||||
|
||||
|
||||
net.get_name(playerID) -> string
|
||||
The same as net.get_player_info(playerID, 'name')
|
||||
FIXME: implement in ServMan_compat.lua ?
|
||||
|
||||
net.get_slot(playerID) -> sideID, slotID
|
||||
The same as:
|
||||
net.get_player_info(playerID, 'side'), net.get_player_info(playerID, 'slot')
|
||||
FIXME: implement in ServMan_compat.lua ?
|
||||
|
||||
|
||||
|
||||
net.set_slot(sideID, slotID)
|
||||
Try to set the local player's slot. Empty slotID ('') puts the player into spectators.
|
||||
|
||||
net.force_player_slot(playerID, sideID, slotID) -> boolean
|
||||
Forces a player to occupy a set slot. Slot '' means no slot (moves player to spectators)
|
||||
SideID: 0 - spectators, 1 - red, 2 - blue
|
||||
|
||||
net.set_name(playerID, name) -- OBSOLETE, works only locally
|
||||
|
||||
|
||||
net.lua2json(value) -> string
|
||||
Convert a Lua value to JSON string
|
||||
|
||||
net.json2lua(json_string) -> value
|
||||
Convert JSON string to a Lua value
|
||||
|
||||
|
||||
LuaExport API 'Export.Lo*'
|
||||
----------------------------------------------------------------
|
||||
See Scripts/Export.lua for the documentation. Note that all export
|
||||
API functions are available here in the Export. namespace, not the global one.
|
||||
In multiplayer the availability of the API on clients depends on the server setting.
|
||||
|
||||
The calls to check export capabilities:
|
||||
Export.LoIsObjectExportAllowed() -- returns the value of server.advanced.allow_object_export
|
||||
Export.LoIsSensorExportAllowed() -- returns the value of server.advanced.allow_sensor_export
|
||||
Export.LoIsOwnshipExportAllowed() -- returns the value of server.advanced.allow_ownship_export
|
||||
|
||||
These calls are only available on clients when LoIsObjectExportAllowed() is true:
|
||||
Export.LoGetObjectById
|
||||
Export.LoGetWorldObjects
|
||||
|
||||
These calls are only available on clients when LoIsSensorExportAllowed() is true:
|
||||
Export.LoGetTWSInfo
|
||||
Export.LoGetTargetInformation
|
||||
Export.LoGetLockedTargetInformation
|
||||
Export.LoGetF15_TWS_Contacts
|
||||
Export.LoGetSightingSystemInfo
|
||||
Export.LoGetWingTargets
|
||||
|
||||
These calls are only available on clients when LoIsOwnshipExportAllowed() is true:
|
||||
Export.LoGetPlayerPlaneId
|
||||
Export.LoGetIndicatedAirSpeed
|
||||
Export.LoGetAngleOfAttack
|
||||
Export.LoGetAngleOfSideSlip
|
||||
Export.LoGetAccelerationUnits
|
||||
Export.LoGetVerticalVelocity
|
||||
Export.LoGetADIPitchBankYaw
|
||||
Export.LoGetTrueAirSpeed
|
||||
Export.LoGetAltitudeAboveSeaLevel
|
||||
Export.LoGetAltitudeAboveGroundLevel
|
||||
Export.LoGetMachNumber
|
||||
Export.LoGetRadarAltimeter
|
||||
Export.LoGetMagneticYaw
|
||||
Export.LoGetGlideDeviation
|
||||
Export.LoGetSideDeviation
|
||||
Export.LoGetSlipBallPosition
|
||||
Export.LoGetBasicAtmospherePressure
|
||||
Export.LoGetControlPanel_HSI
|
||||
Export.LoGetEngineInfo
|
||||
Export.LoGetSelfData
|
||||
Export.LoGetCameraPosition
|
||||
Export.LoSetCameraPosition
|
||||
Export.LoSetCommand
|
||||
Export.LoGetMCPState
|
||||
Export.LoGetRoute
|
||||
Export.LoGetNavigationInfo
|
||||
Export.LoGetPayloadInfo
|
||||
Export.LoGetWingInfo
|
||||
Export.LoGetMechInfo
|
||||
Export.LoGetRadioBeaconsStatus
|
||||
Export.LoGetVectorVelocity
|
||||
Export.LoGetVectorWindVelocity
|
||||
Export.LoGetSnares
|
||||
Export.LoGetAngularVelocity
|
||||
Export.LoGetHeightWithObjects
|
||||
Export.LoGetFMData
|
||||
|
||||
These functions are always available:
|
||||
Export.LoGetPilotName
|
||||
Export.LoGetAltitude
|
||||
Export.LoGetNameByType
|
||||
Export.LoGeoCoordinatesToLoCoordinates
|
||||
Export.LoCoordinatesToGeoCoordinates
|
||||
Export.LoGetVersionInfo
|
||||
Export.LoGetWindAtPoint
|
||||
Export.LoGetModelTime
|
||||
Export.LoGetMissionStartTime
|
||||
|
||||
These are not available in the *GameGUI state:
|
||||
-- Export.LoSetSharedTexture
|
||||
-- Export.LoRemoveSharedTexture
|
||||
-- Export.LoUpdateSharedTexture
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------------------
|
||||
--- The Callbacks.
|
||||
-------------------------------------------------------------------------------------------
|
||||
|
||||
function onMissionLoadBegin()
|
||||
end
|
||||
|
||||
function onMissionLoadProgress(progress, message)
|
||||
end
|
||||
|
||||
function onMissionLoadEnd()
|
||||
end
|
||||
|
||||
|
||||
function onSimulationStart()
|
||||
end
|
||||
|
||||
function onSimulationStop()
|
||||
end
|
||||
|
||||
function onSimulationFrame()
|
||||
end
|
||||
|
||||
function onSimulationPause()
|
||||
end
|
||||
|
||||
function onSimulationResume()
|
||||
end
|
||||
|
||||
|
||||
function onGameEvent(eventName,arg1,arg2,arg3,arg4)
|
||||
--"friendly_fire", playerID, weaponName, victimPlayerID
|
||||
--"mission_end", winner, msg
|
||||
--"kill", killerPlayerID, killerUnitType, killerSide, victimPlayerID, victimUnitType, victimSide, weaponName
|
||||
--"self_kill", playerID
|
||||
--"change_slot", playerID, slotID, prevSide
|
||||
--"connect", playerID, name
|
||||
--"disconnect", playerID, name, playerSide, reason_code
|
||||
--"crash", playerID, unit_missionID
|
||||
--"eject", playerID, unit_missionID
|
||||
--"takeoff", playerID, unit_missionID, airdromeName
|
||||
--"landing", playerID, unit_missionID, airdromeName
|
||||
--"pilot_death", playerID, unit_missionID
|
||||
end
|
||||
|
||||
function onNetConnect(localPlayerID)
|
||||
end
|
||||
|
||||
function onNetMissionChanged(newMissionName)
|
||||
end
|
||||
|
||||
function onNetDisconnect(reason_msg, err_code)
|
||||
end
|
||||
|
||||
-- disconnect reason codes:
|
||||
net.ERR_INVALID_ADDRESS
|
||||
net.ERR_CONNECT_FAILED
|
||||
net.ERR_WRONG_VERSION
|
||||
net.ERR_PROTOCOL_ERROR
|
||||
net.ERR_TAINTED_CLIENT
|
||||
net.ERR_INVALID_PASSWORD
|
||||
net.ERR_BANNED
|
||||
net.ERR_BAD_CALLSIGN
|
||||
|
||||
net.ERR_TIMEOUT
|
||||
net.ERR_KICKED
|
||||
|
||||
|
||||
function onPlayerConnect(id)
|
||||
end
|
||||
|
||||
function onPlayerDisconnect(id, err_code)
|
||||
-- this is never called for local playerID
|
||||
end
|
||||
|
||||
function onPlayerStart(id)
|
||||
-- a player entered the simulation
|
||||
-- this is never called for local playerID
|
||||
end
|
||||
|
||||
function onPlayerStop(id)
|
||||
-- a player left the simulation (happens right before a disconnect, if player exited by desire)
|
||||
-- this is never called for local playerID
|
||||
end
|
||||
|
||||
function onPlayerChangeSlot(id)
|
||||
-- a player successfully changed the slot
|
||||
-- this will also come as onGameEvent('change_slot', playerID, slotID),
|
||||
-- if allowed by server.advanced.event_Connect setting
|
||||
end
|
||||
|
||||
|
||||
--- These 3 functions are different from the rest:
|
||||
--- 1. they are called directly from the network code, so try to make them as fast as possible
|
||||
--- 2. they return a result
|
||||
-- The code shows the default implementations.
|
||||
|
||||
function onPlayerTryConnect(addr, name, ucid, playerID) --> true | false, "disconnect reason"
|
||||
return true
|
||||
end
|
||||
|
||||
function onPlayerTrySendChat(playerID, msg, all) -- -> filteredMessage | "" - empty string drops the message
|
||||
return msg
|
||||
end
|
||||
|
||||
function onPlayerTryChangeSlot(playerID, side, slotID) -- -> true | false
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- GUI callbacks
|
||||
function onChatMessage(message, from)
|
||||
-- this one may be useful for chat archiving
|
||||
end
|
||||
|
||||
function onShowRadioMenu(a_h)
|
||||
end
|
||||
|
||||
function onShowPool()
|
||||
end
|
||||
|
||||
function onShowGameMenu()
|
||||
end
|
||||
|
||||
function onShowBriefing()
|
||||
end
|
||||
|
||||
function onShowChatAll()
|
||||
end
|
||||
|
||||
function onShowChatTeam()
|
||||
end
|
||||
|
||||
function onShowChatRead()
|
||||
end
|
||||
|
||||
function onShowMessage(a_text, a_duration)
|
||||
end
|
||||
|
||||
function onTriggerMessage(message, duration, clearView)
|
||||
end
|
||||
|
||||
function onRadioMessage(message, duration)
|
||||
end
|
||||
|
||||
function onRadioCommand(command_message)
|
||||
end
|
||||
|
||||
===================================================================================================
|
||||
|
||||
Happy hacking!
|
||||
|
||||
Sincerely,
|
||||
dsb at eagle dot ru
|
||||
5
Utils/Generate_Moose.bat
Normal file
5
Utils/Generate_Moose.bat
Normal file
@ -0,0 +1,5 @@
|
||||
%~dp0luarocks\lua5.1.exe %1 %2 %3 %4 %5
|
||||
call %~dp0LuaSrcDiet.bat --basic --opt-emptylines %5\Moose.lua
|
||||
rem del %5\Moose.lua
|
||||
rem copy %5\Moose_.lua %5\Moose.lua
|
||||
rem del Moose_.lua
|
||||
BIN
Utils/luarocks/lib/lua/5.1/lfs.dll
Normal file
BIN
Utils/luarocks/lib/lua/5.1/lfs.dll
Normal file
Binary file not shown.
@ -0,0 +1,178 @@
|
||||
#!/usr/bin/lua
|
||||
--------------------------------------------------------------------------------
|
||||
-- Copyright (c) 2012-2014 Sierra Wireless.
|
||||
-- All rights reserved. This program and the accompanying materials
|
||||
-- are made available under the terms of the Eclipse Public License v1.0
|
||||
-- which accompanies this distribution, and is available at
|
||||
-- http://www.eclipse.org/legal/epl-v10.html
|
||||
--
|
||||
-- Contributors:
|
||||
-- Kevin KIN-FOO <kkinfoo@sierrawireless.com>
|
||||
-- - initial API and implementation and initial documentation
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
-- Check interpreter version
|
||||
if _VERSION ~= "Lua 5.1" then
|
||||
print("Luadocumentor is only compatible with Lua 5.1")
|
||||
return
|
||||
end
|
||||
|
||||
--
|
||||
-- Defining help message.
|
||||
--
|
||||
|
||||
-- This message is compliant to 'lapp', which will match options and arguments
|
||||
-- from command line.
|
||||
local help = [[luadocumentor v0.1.4: tool for Lua Documentation Language
|
||||
-f, --format (default doc) Define output format :
|
||||
* doc: Will produce HTML documentation from specified file(s) or directories.
|
||||
* api: Will produce API file(s) from specified file(s) or directories.
|
||||
-d, --dir (default docs) Define an output directory. If the given directory doesn't exist, it will be created.
|
||||
-h, --help Display the help.
|
||||
-n, --noheuristic Do not use code analysis, use only comments to generate documentation.
|
||||
-s, --style (default !) The path of your own css file, if you don't want to use the default one. (usefull only for the doc format)
|
||||
[directories|files] Define the paths or the directories of inputs files. Only Lua or C files containing a @module tag will be considered.
|
||||
]]
|
||||
local docgenerator = require 'docgenerator'
|
||||
local lddextractor = require 'lddextractor'
|
||||
local lapp = require 'pl.lapp'
|
||||
local args = lapp( help )
|
||||
|
||||
if not args or #args < 1 then
|
||||
print('No directory provided')
|
||||
return
|
||||
elseif args.help then
|
||||
-- Just print help
|
||||
print( help )
|
||||
return
|
||||
end
|
||||
|
||||
--
|
||||
-- define css file name
|
||||
--
|
||||
local cssfilename = "stylesheet.css"
|
||||
|
||||
--
|
||||
-- Parse files from given folders
|
||||
--
|
||||
|
||||
-- Check if all folders exist
|
||||
local fs = require 'fs.lfs'
|
||||
local allpresent, missing = fs.checkdirectory(args)
|
||||
|
||||
-- Some of given directories are absent
|
||||
if missing then
|
||||
-- List missing directories
|
||||
print 'Unable to open'
|
||||
for _, file in ipairs( missing ) do
|
||||
print('\t'.. file)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- Get files from given directories
|
||||
local filestoparse, error = fs.filelist( args )
|
||||
if not filestoparse then
|
||||
print ( error )
|
||||
return
|
||||
end
|
||||
|
||||
--
|
||||
-- Generate documentation only files
|
||||
--
|
||||
if args.format == 'api' then
|
||||
for _, filename in ipairs( filestoparse ) do
|
||||
|
||||
-- Loading file content
|
||||
print('Dealing with "'..filename..'".')
|
||||
local file, error = io.open(filename, 'r')
|
||||
if not file then
|
||||
print ('Unable to open "'..filename.."'.\n"..error)
|
||||
else
|
||||
local code = file:read('*all')
|
||||
file:close()
|
||||
|
||||
--
|
||||
-- Creating comment file
|
||||
--
|
||||
local commentfile, error = lddextractor.generatecommentfile(filename, code)
|
||||
|
||||
-- Getting module name
|
||||
-- Optimize me
|
||||
local module, moduleerror = lddextractor.generateapimodule(filename, code)
|
||||
if not commentfile then
|
||||
print('Unable to create documentation file for "'..filename..'"\n'..error)
|
||||
elseif not module or not module.name then
|
||||
local error = moduleerror and '\n'..moduleerror or ''
|
||||
print('Unable to compute module name for "'..filename..'".'..error)
|
||||
else
|
||||
--
|
||||
-- Flush documentation file on disk
|
||||
--
|
||||
local path = args.dir..fs.separator..module.name..'.lua'
|
||||
local status, err = fs.fill(path, commentfile)
|
||||
if not status then
|
||||
print(err)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
print('Done')
|
||||
return
|
||||
end
|
||||
|
||||
-- Deal only supported output types
|
||||
if args.format ~= 'doc' then
|
||||
print ('"'..args.format..'" format is not handled.')
|
||||
return
|
||||
end
|
||||
-- Generate html form files
|
||||
local parsedfiles, unparsed = docgenerator.generatedocforfiles(filestoparse, cssfilename,args.noheuristic)
|
||||
|
||||
-- Show warnings on unparsed files
|
||||
if #unparsed > 0 then
|
||||
for _, faultyfile in ipairs( unparsed ) do
|
||||
print( faultyfile )
|
||||
end
|
||||
end
|
||||
-- This loop is just for counting parsed files
|
||||
-- TODO: Find a more elegant way to do it
|
||||
local parsedfilescount = 0
|
||||
for _, p in pairs(parsedfiles) do
|
||||
parsedfilescount = parsedfilescount + 1
|
||||
end
|
||||
print (parsedfilescount .. ' file(s) parsed.')
|
||||
|
||||
-- Create html files
|
||||
local generated = 0
|
||||
for _, apifile in pairs ( parsedfiles ) do
|
||||
local status, err = fs.fill(args.dir..fs.separator..apifile.name..'.html', apifile.body)
|
||||
if status then
|
||||
generated = generated + 1
|
||||
else
|
||||
print( 'Unable to create '..apifile.name..'.html on disk.')
|
||||
end
|
||||
end
|
||||
print (generated .. ' file(s) generated.')
|
||||
|
||||
-- Copying css
|
||||
local csscontent
|
||||
if args.style == '!' then
|
||||
csscontent = require 'defaultcss'
|
||||
else
|
||||
local css, error = io.open(args.style, 'r')
|
||||
if not css then
|
||||
print('Unable to open "'..args.style .. '".\n'..error)
|
||||
return
|
||||
end
|
||||
csscontent = css:read("*all")
|
||||
css:close()
|
||||
end
|
||||
|
||||
local status, error = fs.fill(args.dir..fs.separator..cssfilename, csscontent)
|
||||
if not status then
|
||||
print(error)
|
||||
return
|
||||
end
|
||||
print('Adding css')
|
||||
print('Done')
|
||||
@ -0,0 +1,198 @@
|
||||
Eclipse Public License - v 1.0
|
||||
|
||||
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
|
||||
LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
|
||||
CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
|
||||
|
||||
1. DEFINITIONS
|
||||
|
||||
"Contribution" means:
|
||||
|
||||
a) in the case of the initial Contributor, the initial code and documentation
|
||||
distributed under this Agreement, and
|
||||
b) in the case of each subsequent Contributor:
|
||||
i) changes to the Program, and
|
||||
ii) additions to the Program;
|
||||
|
||||
where such changes and/or additions to the Program originate from and are
|
||||
distributed by that particular Contributor. A Contribution 'originates' from
|
||||
a Contributor if it was added to the Program by such Contributor itself or
|
||||
anyone acting on such Contributor's behalf. Contributions do not include
|
||||
additions to the Program which: (i) are separate modules of software
|
||||
distributed in conjunction with the Program under their own license
|
||||
agreement, and (ii) are not derivative works of the Program.
|
||||
|
||||
"Contributor" means any person or entity that distributes the Program.
|
||||
|
||||
"Licensed Patents" mean patent claims licensable by a Contributor which are
|
||||
necessarily infringed by the use or sale of its Contribution alone or when
|
||||
combined with the Program.
|
||||
|
||||
"Program" means the Contributions distributed in accordance with this Agreement.
|
||||
|
||||
"Recipient" means anyone who receives the Program under this Agreement,
|
||||
including all Contributors.
|
||||
|
||||
2. GRANT OF RIGHTS
|
||||
a) Subject to the terms of this Agreement, each Contributor hereby grants
|
||||
Recipient a non-exclusive, worldwide, royalty-free copyright license to
|
||||
reproduce, prepare derivative works of, publicly display, publicly perform,
|
||||
distribute and sublicense the Contribution of such Contributor, if any, and
|
||||
such derivative works, in source code and object code form.
|
||||
b) Subject to the terms of this Agreement, each Contributor hereby grants
|
||||
Recipient a non-exclusive, worldwide, royalty-free patent license under
|
||||
Licensed Patents to make, use, sell, offer to sell, import and otherwise
|
||||
transfer the Contribution of such Contributor, if any, in source code and
|
||||
object code form. This patent license shall apply to the combination of the
|
||||
Contribution and the Program if, at the time the Contribution is added by
|
||||
the Contributor, such addition of the Contribution causes such combination
|
||||
to be covered by the Licensed Patents. The patent license shall not apply
|
||||
to any other combinations which include the Contribution. No hardware per
|
||||
se is licensed hereunder.
|
||||
c) Recipient understands that although each Contributor grants the licenses to
|
||||
its Contributions set forth herein, no assurances are provided by any
|
||||
Contributor that the Program does not infringe the patent or other
|
||||
intellectual property rights of any other entity. Each Contributor
|
||||
disclaims any liability to Recipient for claims brought by any other entity
|
||||
based on infringement of intellectual property rights or otherwise. As a
|
||||
condition to exercising the rights and licenses granted hereunder, each
|
||||
Recipient hereby assumes sole responsibility to secure any other
|
||||
intellectual property rights needed, if any. For example, if a third party
|
||||
patent license is required to allow Recipient to distribute the Program, it
|
||||
is Recipient's responsibility to acquire that license before distributing
|
||||
the Program.
|
||||
d) Each Contributor represents that to its knowledge it has sufficient
|
||||
copyright rights in its Contribution, if any, to grant the copyright
|
||||
license set forth in this Agreement.
|
||||
|
||||
3. REQUIREMENTS
|
||||
|
||||
A Contributor may choose to distribute the Program in object code form under its
|
||||
own license agreement, provided that:
|
||||
|
||||
a) it complies with the terms and conditions of this Agreement; and
|
||||
b) its license agreement:
|
||||
i) effectively disclaims on behalf of all Contributors all warranties and
|
||||
conditions, express and implied, including warranties or conditions of
|
||||
title and non-infringement, and implied warranties or conditions of
|
||||
merchantability and fitness for a particular purpose;
|
||||
ii) effectively excludes on behalf of all Contributors all liability for
|
||||
damages, including direct, indirect, special, incidental and
|
||||
consequential damages, such as lost profits;
|
||||
iii) states that any provisions which differ from this Agreement are offered
|
||||
by that Contributor alone and not by any other party; and
|
||||
iv) states that source code for the Program is available from such
|
||||
Contributor, and informs licensees how to obtain it in a reasonable
|
||||
manner on or through a medium customarily used for software exchange.
|
||||
|
||||
When the Program is made available in source code form:
|
||||
|
||||
a) it must be made available under this Agreement; and
|
||||
b) a copy of this Agreement must be included with each copy of the Program.
|
||||
Contributors may not remove or alter any copyright notices contained within
|
||||
the Program.
|
||||
|
||||
Each Contributor must identify itself as the originator of its Contribution, if
|
||||
any, in a manner that reasonably allows subsequent Recipients to identify the
|
||||
originator of the Contribution.
|
||||
|
||||
4. COMMERCIAL DISTRIBUTION
|
||||
|
||||
Commercial distributors of software may accept certain responsibilities with
|
||||
respect to end users, business partners and the like. While this license is
|
||||
intended to facilitate the commercial use of the Program, the Contributor who
|
||||
includes the Program in a commercial product offering should do so in a manner
|
||||
which does not create potential liability for other Contributors. Therefore, if
|
||||
a Contributor includes the Program in a commercial product offering, such
|
||||
Contributor ("Commercial Contributor") hereby agrees to defend and indemnify
|
||||
every other Contributor ("Indemnified Contributor") against any losses, damages
|
||||
and costs (collectively "Losses") arising from claims, lawsuits and other legal
|
||||
actions brought by a third party against the Indemnified Contributor to the
|
||||
extent caused by the acts or omissions of such Commercial Contributor in
|
||||
connection with its distribution of the Program in a commercial product
|
||||
offering. The obligations in this section do not apply to any claims or Losses
|
||||
relating to any actual or alleged intellectual property infringement. In order
|
||||
to qualify, an Indemnified Contributor must: a) promptly notify the Commercial
|
||||
Contributor in writing of such claim, and b) allow the Commercial Contributor to
|
||||
control, and cooperate with the Commercial Contributor in, the defense and any
|
||||
related settlement negotiations. The Indemnified Contributor may participate in
|
||||
any such claim at its own expense.
|
||||
|
||||
For example, a Contributor might include the Program in a commercial product
|
||||
offering, Product X. That Contributor is then a Commercial Contributor. If that
|
||||
Commercial Contributor then makes performance claims, or offers warranties
|
||||
related to Product X, those performance claims and warranties are such
|
||||
Commercial Contributor's responsibility alone. Under this section, the
|
||||
Commercial Contributor would have to defend claims against the other
|
||||
Contributors related to those performance claims and warranties, and if a court
|
||||
requires any other Contributor to pay any damages as a result, the Commercial
|
||||
Contributor must pay those damages.
|
||||
|
||||
5. NO WARRANTY
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN
|
||||
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
|
||||
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each
|
||||
Recipient is solely responsible for determining the appropriateness of using and
|
||||
distributing the Program and assumes all risks associated with its exercise of
|
||||
rights under this Agreement , including but not limited to the risks and costs
|
||||
of program errors, compliance with applicable laws, damage to or loss of data,
|
||||
programs or equipment, and unavailability or interruption of operations.
|
||||
|
||||
6. DISCLAIMER OF LIABILITY
|
||||
|
||||
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
|
||||
CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
|
||||
PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS
|
||||
GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
7. GENERAL
|
||||
|
||||
If any provision of this Agreement is invalid or unenforceable under applicable
|
||||
law, it shall not affect the validity or enforceability of the remainder of the
|
||||
terms of this Agreement, and without further action by the parties hereto, such
|
||||
provision shall be reformed to the minimum extent necessary to make such
|
||||
provision valid and enforceable.
|
||||
|
||||
If Recipient institutes patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Program itself
|
||||
(excluding combinations of the Program with other software or hardware)
|
||||
infringes such Recipient's patent(s), then such Recipient's rights granted under
|
||||
Section 2(b) shall terminate as of the date such litigation is filed.
|
||||
|
||||
All Recipient's rights under this Agreement shall terminate if it fails to
|
||||
comply with any of the material terms or conditions of this Agreement and does
|
||||
not cure such failure in a reasonable period of time after becoming aware of
|
||||
such noncompliance. If all Recipient's rights under this Agreement terminate,
|
||||
Recipient agrees to cease use and distribution of the Program as soon as
|
||||
reasonably practicable. However, Recipient's obligations under this Agreement
|
||||
and any licenses granted by Recipient relating to the Program shall continue and
|
||||
survive.
|
||||
|
||||
Everyone is permitted to copy and distribute copies of this Agreement, but in
|
||||
order to avoid inconsistency the Agreement is copyrighted and may only be
|
||||
modified in the following manner. The Agreement Steward reserves the right to
|
||||
publish new versions (including revisions) of this Agreement from time to time.
|
||||
No one other than the Agreement Steward has the right to modify this Agreement.
|
||||
The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation
|
||||
may assign the responsibility to serve as the Agreement Steward to a suitable
|
||||
separate entity. Each new version of the Agreement will be given a
|
||||
distinguishing version number. The Program (including Contributions) may always
|
||||
be distributed subject to the version of the Agreement under which it was
|
||||
received. In addition, after a new version of the Agreement is published,
|
||||
Contributor may elect to distribute the Program (including its Contributions)
|
||||
under the new version. Except as expressly stated in Sections 2(a) and 2(b)
|
||||
above, Recipient receives no rights or licenses to the intellectual property of
|
||||
any Contributor under this Agreement, whether expressly, by implication,
|
||||
estoppel or otherwise. All rights in the Program not expressly granted under
|
||||
this Agreement are reserved.
|
||||
|
||||
This Agreement is governed by the laws of the State of New York and the
|
||||
intellectual property laws of the United States of America. No party to this
|
||||
Agreement will bring a legal action under this Agreement more than one year
|
||||
after the cause of action arose. Each party waives its rights to a jury trial in
|
||||
any resulting litigation.
|
||||
@ -0,0 +1,7 @@
|
||||
# Lua Documentor
|
||||
|
||||
LuaDocumentor allow users to generate HTML and API files from code documented
|
||||
using Lua documentation language.
|
||||
|
||||
Documentation is
|
||||
[available here](http://wiki.eclipse.org/Koneki/LDT/User_Area/LuaDocumentor).
|
||||
@ -0,0 +1,57 @@
|
||||
package = 'LuaDocumentor'
|
||||
version = '0.1.5-1'
|
||||
description = {
|
||||
summary = 'LuaDocumentor allow users to generate HTML and API files from code documented using Lua documentation language.',
|
||||
detailed = [[
|
||||
This is an example for the LuaRocks tutorial.
|
||||
Here we would put a detailed, typically
|
||||
paragraph-long description.
|
||||
]],
|
||||
homepage = 'http://wiki.eclipse.org/Koneki/LDT/User_Area/LuaDocumentor',
|
||||
license = 'EPL'
|
||||
}
|
||||
source = {
|
||||
url = 'git://github.com/LuaDevelopmentTools/luadocumentor.git',
|
||||
tag = 'v0.1.5-1'
|
||||
}
|
||||
dependencies = {
|
||||
'lua ~> 5.1',
|
||||
'luafilesystem ~> 1.6',
|
||||
'markdown ~> 0.32',
|
||||
'metalua-compiler ~> 0.7',
|
||||
'penlight ~> 0.9'
|
||||
}
|
||||
build = {
|
||||
type = 'builtin',
|
||||
install = {
|
||||
bin = {
|
||||
luadocumentor = 'luadocumentor.lua'
|
||||
},
|
||||
lua = {
|
||||
['models.internalmodelbuilder'] = 'models/internalmodelbuilder.mlua'
|
||||
}
|
||||
},
|
||||
modules = {
|
||||
defaultcss = 'defaultcss.lua',
|
||||
docgenerator = 'docgenerator.lua',
|
||||
extractors = 'extractors.lua',
|
||||
lddextractor = 'lddextractor.lua',
|
||||
templateengine = 'templateengine.lua',
|
||||
|
||||
['fs.lfs'] = 'fs/lfs.lua',
|
||||
|
||||
['models.apimodel'] = 'models/apimodel.lua',
|
||||
['models.apimodelbuilder'] = 'models/apimodelbuilder.lua',
|
||||
['models.internalmodel'] = 'models/internalmodel.lua',
|
||||
['models.ldparser'] = 'models/ldparser.lua',
|
||||
|
||||
['template.file'] = 'template/file.lua',
|
||||
['template.index'] = 'template/index.lua',
|
||||
['template.index.recordtypedef'] = 'template/index/recordtypedef.lua',
|
||||
['template.item'] = 'template/item.lua',
|
||||
['template.page'] = 'template/page.lua',
|
||||
['template.recordtypedef'] = 'template/recordtypedef.lua',
|
||||
['template.usage'] = 'template/usage.lua',
|
||||
['template.utils'] = 'template/utils.lua',
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,39 @@
|
||||
rock_manifest = {
|
||||
bin = {
|
||||
luadocumentor = "bc5cc07f56db2cf1dbe80f0827332873"
|
||||
},
|
||||
doc = {
|
||||
LICENSE = "52a21f73ac77fd790dc40dc5acda0fc2",
|
||||
["README.md"] = "fcef1f43c69f3559b347d854b2626deb"
|
||||
},
|
||||
lua = {
|
||||
["defaultcss.lua"] = "dd9b2b89e5080972bbb52056247c0c65",
|
||||
["docgenerator.lua"] = "92d0a3947d88226340014d2f033be37f",
|
||||
["extractors.lua"] = "74191695e5217706ee355925e5ca40fa",
|
||||
fs = {
|
||||
["lfs.lua"] = "4d00f9bc942b02a86ccea16544d3e85d"
|
||||
},
|
||||
["lddextractor.lua"] = "56edde775a5d57818aa0a07b4f723536",
|
||||
models = {
|
||||
["apimodel.lua"] = "3c401de18691b1222b0ad253958260ee",
|
||||
["apimodelbuilder.lua"] = "4c4a3c0b48b404973542dd99f994eb2c",
|
||||
["internalmodel.lua"] = "a1a21e50af8db0f0a0b9d164ccc08853",
|
||||
["internalmodelbuilder.mlua"] = "ff95dfca573ccc1c19a79434e96a492d",
|
||||
["ldparser.lua"] = "538904a3adbfff4ff83deda029847323"
|
||||
},
|
||||
template = {
|
||||
["file.lua"] = "41f095bc049ef161060d8e3b4ac9de63",
|
||||
index = {
|
||||
["recordtypedef.lua"] = "0977ff0048a837389c2ac10285eb1ce1"
|
||||
},
|
||||
["index.lua"] = "5a3b3cface3b1fd9cb2d56f1edd5487b",
|
||||
["item.lua"] = "5d5a6d9bffd8935c4ed283105ede331b",
|
||||
["page.lua"] = "351f4a7215272f7e448faeece4945bc0",
|
||||
["recordtypedef.lua"] = "69938e1d60e94eed7f95b0999f1386ca",
|
||||
["usage.lua"] = "979503deb84877cb221130a5be7c1535",
|
||||
["utils.lua"] = "ad97fb4e3de9fb6480b25cdd877b50d9"
|
||||
},
|
||||
["templateengine.lua"] = "09bfc6350e14f4ab509d14fb0fb295c0"
|
||||
},
|
||||
["luadocumentor-0.1.5-1.rockspec"] = "4ba1b88898dce89e7fd8fb6a700496a4"
|
||||
}
|
||||
@ -0,0 +1,212 @@
|
||||
body {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
font-family: arial, helvetica, geneva, sans-serif;
|
||||
background-color:#ffffff; margin:0px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: "Andale Mono", monospace;
|
||||
}
|
||||
|
||||
tt {
|
||||
font-family: "Andale Mono", monospace;
|
||||
}
|
||||
|
||||
body, td, th { font-size: 11pt; }
|
||||
|
||||
h1, h2, h3, h4 { margin-left: 0em; }
|
||||
|
||||
textarea, pre, tt { font-size:10pt; }
|
||||
body, td, th { color:#000000; }
|
||||
small { font-size:0.85em; }
|
||||
h1 { font-size:1.5em; }
|
||||
h2 { font-size:1.25em; }
|
||||
h3 { font-size:1.15em; }
|
||||
h4 { font-size:1.06em; }
|
||||
|
||||
a:link { font-weight:bold; color: #004080; text-decoration: none; }
|
||||
a:visited { font-weight:bold; color: #006699; text-decoration: none; }
|
||||
a:link:hover { text-decoration:underline; }
|
||||
hr { color:#cccccc }
|
||||
img { border-width: 0px; }
|
||||
|
||||
h3 { padding-top: 1em; }
|
||||
|
||||
p { margin-left: 1em; }
|
||||
|
||||
p.name {
|
||||
font-family: "Andale Mono", monospace;
|
||||
padding-top: 1em;
|
||||
margin-left: 0em;
|
||||
}
|
||||
|
||||
blockquote { margin-left: 3em; }
|
||||
|
||||
.example {
|
||||
background-color: rgb(245, 245, 245);
|
||||
border-top-width: 1px;
|
||||
border-right-width: 1px;
|
||||
border-bottom-width: 1px;
|
||||
border-left-width: 1px;
|
||||
border-top-style: solid;
|
||||
border-right-style: solid;
|
||||
border-bottom-style: solid;
|
||||
border-left-style: solid;
|
||||
border-top-color: silver;
|
||||
border-right-color: silver;
|
||||
border-bottom-color: silver;
|
||||
border-left-color: silver;
|
||||
padding: 1em;
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
font-family: "Andale Mono", monospace;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin-left: 0em;
|
||||
background: #00007f;
|
||||
border: 0px;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
ul { list-style-type: disc; }
|
||||
|
||||
table.index { border: 1px #00007f; }
|
||||
table.index td { text-align: left; vertical-align: top; }
|
||||
table.index ul { padding-top: 0em; margin-top: 0em; }
|
||||
|
||||
table {
|
||||
border: 1px solid black;
|
||||
border-collapse: collapse;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
th {
|
||||
border: 1px solid black;
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
td {
|
||||
border: 1px solid black;
|
||||
padding: 0.5em;
|
||||
}
|
||||
div.header, div.footer { margin-left: 0em; }
|
||||
|
||||
#container {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
#product {
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #cccccc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#product big {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
#product_logo {
|
||||
}
|
||||
|
||||
#product_name {
|
||||
}
|
||||
|
||||
#product_description {
|
||||
}
|
||||
|
||||
#main {
|
||||
background-color: #f0f0f0;
|
||||
border-left: 2px solid #cccccc;
|
||||
}
|
||||
|
||||
#navigation {
|
||||
float: left;
|
||||
width: 12em;
|
||||
margin: 0;
|
||||
vertical-align: top;
|
||||
background-color: #f0f0f0;
|
||||
overflow:visible;
|
||||
}
|
||||
|
||||
#navigation h1 {
|
||||
background-color:#e7e7e7;
|
||||
font-size:1.1em;
|
||||
color:#000000;
|
||||
text-align:left;
|
||||
margin:0px;
|
||||
padding:0.2em;
|
||||
border-top:1px solid #dddddd;
|
||||
border-bottom:1px solid #dddddd;
|
||||
}
|
||||
|
||||
#navigation ul {
|
||||
font-size:1em;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
#navigation li {
|
||||
text-indent: -1em;
|
||||
margin: 0em 0em 0em 0.5em;
|
||||
display: block;
|
||||
padding: 3px 0px 0px 12px;
|
||||
}
|
||||
|
||||
#navigation li li a {
|
||||
padding: 0px 3px 0px -1em;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin-left: 12em;
|
||||
padding: 1em;
|
||||
border-left: 2px solid #cccccc;
|
||||
border-right: 2px solid #cccccc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
#about {
|
||||
clear: both;
|
||||
margin: 0;
|
||||
padding: 5px;
|
||||
border-top: 2px solid #cccccc;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
font: 10pt "Times New Roman", "TimeNR", Times, serif;
|
||||
}
|
||||
a {
|
||||
font-weight:bold; color: #004080; text-decoration: underline;
|
||||
}
|
||||
#main {
|
||||
background-color: #ffffff; border-left: 0px;
|
||||
}
|
||||
#container {
|
||||
margin-left: 2%; margin-right: 2%; background-color: #ffffff;
|
||||
}
|
||||
#content {
|
||||
margin-left: 0px; padding: 1em; border-left: 0px; border-right: 0px; background-color: #ffffff;
|
||||
}
|
||||
#navigation {
|
||||
display: none;
|
||||
}
|
||||
#product_logo {
|
||||
display: none;
|
||||
}
|
||||
#about img {
|
||||
display: none;
|
||||
}
|
||||
.example {
|
||||
font-family: "Andale Mono", monospace;
|
||||
font-size: 8pt;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,103 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>LuaFileSystem</title>
|
||||
<link rel="stylesheet" href="doc.css" type="text/css"/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="product">
|
||||
<div id="product_logo">
|
||||
<a href="http://www.keplerproject.org">
|
||||
<img alt="LuaFileSystem" src="luafilesystem.png"/>
|
||||
</a>
|
||||
</div>
|
||||
<div id="product_name"><big><strong>LuaFileSystem</strong></big></div>
|
||||
<div id="product_description">File System Library for the Lua Programming Language</div>
|
||||
</div> <!-- id="product" -->
|
||||
|
||||
<div id="main">
|
||||
|
||||
<div id="navigation">
|
||||
<h1>LuaFileSystem</h1>
|
||||
<ul>
|
||||
<li><a href="index.html">Home</a>
|
||||
<ul>
|
||||
<li><a href="index.html#overview">Overview</a></li>
|
||||
<li><a href="index.html#status">Status</a></li>
|
||||
<li><a href="index.html#download">Download</a></li>
|
||||
<li><a href="index.html#history">History</a></li>
|
||||
<li><a href="index.html#credits">Credits</a></li>
|
||||
<li><a href="index.html#contact">Contact us</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="manual.html">Manual</a>
|
||||
<ul>
|
||||
<li><a href="manual.html#introduction">Introduction</a></li>
|
||||
<li><a href="manual.html#building">Building</a></li>
|
||||
<li><a href="manual.html#installation">Installation</a></li>
|
||||
<li><a href="manual.html#reference">Reference</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>Examples</strong></li>
|
||||
<li><a href="https://github.com/keplerproject/luafilesystem">Project</a>
|
||||
<ul>
|
||||
<li><a href="https://github.com/keplerproject/luafilesystem/issues">Bug Tracker</a></li>
|
||||
<li><a href="https://github.com/keplerproject/luafilesystem">Git</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="license.html">License</a></li>
|
||||
</ul>
|
||||
</div> <!-- id="navigation" -->
|
||||
|
||||
<div id="content">
|
||||
|
||||
<h2><a name="example"></a>Examples</h2>
|
||||
|
||||
<h3>Directory iterator</h3>
|
||||
|
||||
<p>The following example iterates over a directory and recursively lists the
|
||||
attributes for each file inside it.</p>
|
||||
|
||||
<pre class="example">
|
||||
local lfs = require"lfs"
|
||||
|
||||
function attrdir (path)
|
||||
for file in lfs.dir(path) do
|
||||
if file ~= "." and file ~= ".." then
|
||||
local f = path..'/'..file
|
||||
print ("\t "..f)
|
||||
local attr = lfs.attributes (f)
|
||||
assert (type(attr) == "table")
|
||||
if attr.mode == "directory" then
|
||||
attrdir (f)
|
||||
else
|
||||
for name, value in pairs(attr) do
|
||||
print (name, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
attrdir (".")
|
||||
</pre>
|
||||
|
||||
</div> <!-- id="content" -->
|
||||
|
||||
</div> <!-- id="main" -->
|
||||
|
||||
<div id="about">
|
||||
<p><a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.0!</a></p>
|
||||
<p><small>$Id: examples.html,v 1.8 2007/12/14 15:28:04 carregal Exp $</small></p>
|
||||
</div> <!-- id="about" -->
|
||||
|
||||
</div> <!-- id="container" -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,218 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>LuaFileSystem</title>
|
||||
<link rel="stylesheet" href="doc.css" type="text/css"/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="product">
|
||||
<div id="product_logo">
|
||||
<a href="http://www.keplerproject.org">
|
||||
<img alt="LuaFileSystem" src="luafilesystem.png"/>
|
||||
</a>
|
||||
</div>
|
||||
<div id="product_name"><big><strong>LuaFileSystem</strong></big></div>
|
||||
<div id="product_description">File System Library for the Lua Programming Language</div>
|
||||
</div> <!-- id="product" -->
|
||||
|
||||
<div id="main">
|
||||
|
||||
<div id="navigation">
|
||||
<h1>LuaFileSystem</h1>
|
||||
<ul>
|
||||
<li><strong>Home</strong>
|
||||
<ul>
|
||||
<li><a href="index.html#overview">Overview</a></li>
|
||||
<li><a href="index.html#status">Status</a></li>
|
||||
<li><a href="index.html#download">Download</a></li>
|
||||
<li><a href="index.html#history">History</a></li>
|
||||
<li><a href="index.html#credits">Credits</a></li>
|
||||
<li><a href="index.html#contact">Contact us</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="manual.html">Manual</a>
|
||||
<ul>
|
||||
<li><a href="manual.html#introduction">Introduction</a></li>
|
||||
<li><a href="manual.html#building">Building</a></li>
|
||||
<li><a href="manual.html#installation">Installation</a></li>
|
||||
<li><a href="manual.html#reference">Reference</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="examples.html">Examples</a></li>
|
||||
<li><a href="https://github.com/keplerproject/luafilesystem">Project</a>
|
||||
<ul>
|
||||
<li><a href="https://github.com/keplerproject/luafilesystem/issues">Bug Tracker</a></li>
|
||||
<li><a href="https://github.com/keplerproject/luafilesystem">Git</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="license.html">License</a></li>
|
||||
</ul>
|
||||
</div> <!-- id="navigation" -->
|
||||
|
||||
<div id="content">
|
||||
|
||||
<h2><a name="overview"></a>Overview</h2>
|
||||
|
||||
<p>LuaFileSystem is a <a href="http://www.lua.org">Lua</a> library
|
||||
developed to complement the set of functions related to file
|
||||
systems offered by the standard Lua distribution.</p>
|
||||
|
||||
<p>LuaFileSystem offers a portable way to access
|
||||
the underlying directory structure and file attributes.</p>
|
||||
|
||||
<p>LuaFileSystem is free software and uses the same
|
||||
<a href="license.html">license</a> as Lua 5.1.</p>
|
||||
|
||||
<h2><a name="status"></a>Status</h2>
|
||||
|
||||
<p>Current version is 1.6.3. It works with Lua 5.1, 5.2 and 5.3.</p>
|
||||
|
||||
<h2><a name="download"></a>Download</h2>
|
||||
|
||||
<p>LuaFileSystem source can be downloaded from its
|
||||
<a href="http://github.com/keplerproject/luafilesystem">Github</a>
|
||||
page.</p>
|
||||
|
||||
<h2><a name="history"></a>History</h2>
|
||||
|
||||
<dl class="history">
|
||||
<dt><strong>Version 1.6.3</strong> [15/Jan/2015]</dt>
|
||||
<dd><ul>
|
||||
<li>Lua 5.3 support.</li>
|
||||
<li>Assorted bugfixes.</li>
|
||||
</ul></dd>
|
||||
|
||||
<dt><strong>Version 1.6.2</strong> [??/Oct/2012]</dt>
|
||||
<dd><ul>
|
||||
<li>Full Lua 5.2 compatibility (with Lua 5.1 fallbacks)</li>
|
||||
</ul></dd>
|
||||
|
||||
<dt><strong>Version 1.6.1</strong> [01/Oct/2012]</dt>
|
||||
<dd><ul>
|
||||
<li>fix build for Lua 5.2</li>
|
||||
</ul></dd>
|
||||
|
||||
<dt><strong>Version 1.6.0</strong> [26/Sep/2012]</dt>
|
||||
<dd><ul>
|
||||
<li>getcwd fix for Android</li>
|
||||
<li>support for Lua 5.2</li>
|
||||
<li>add lfs.link</li>
|
||||
<li>other bug fixes</li>
|
||||
</ul></dd>
|
||||
|
||||
<dt><strong>Version 1.5.0</strong> [20/Oct/2009]</dt>
|
||||
<dd><ul>
|
||||
<li>Added explicit next and close methods to second return value of lfs.dir
|
||||
(the directory object), for explicit iteration or explicit closing.</li>
|
||||
<li>Added directory locking via lfs.lock_dir function (see the <a href="manual.html">manual</a>).</li>
|
||||
</ul></dd>
|
||||
<dt><strong>Version 1.4.2</strong> [03/Feb/2009]</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>fixed bug [<a href="http://luaforge.net/tracker/?func=detail&group_id=66&aid=13198&atid=356">#13198</a>]
|
||||
lfs.attributes(filename, 'size') overflow on files > 2 Gb again (bug report and patch by KUBO Takehiro).</li>
|
||||
<li>fixed bug [<a href="http://luaforge.net/tracker/?group_id=66&atid=356&func=detail&aid=39794">#39794</a>]
|
||||
Compile error on Solaris 10 (bug report and patch by Aaron B).</li>
|
||||
<li>fixed compilation problems with Borland C.</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
<dt><strong>Version 1.4.1</strong> [07/May/2008]</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>documentation review</li>
|
||||
<li>fixed Windows compilation issues</li>
|
||||
<li>fixed bug in the Windows tests (patch by Shmuel Zeigerman)</li>
|
||||
<li>fixed bug [<a href="http://luaforge.net/tracker/?func=detail&group_id=66&aid=2185&atid=356">#2185</a>]
|
||||
<code>lfs.attributes(filename, 'size')</code> overflow on files > 2 Gb
|
||||
</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
<dt><strong>Version 1.4.0</strong> [13/Feb/2008]</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>added function
|
||||
<a href="manual.html#setmode"><code>lfs.setmode</code></a>
|
||||
(works only in Windows systems).</li>
|
||||
<li><a href="manual.html#attributes"><code>lfs.attributes</code></a>
|
||||
raises an error if attribute does not exist</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
<dt><strong>Version 1.3.0</strong> [26/Oct/2007]</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>added function
|
||||
<a href="manual.html#symlinkattributes"><code>lfs.symlinkattributes</code></a>
|
||||
(works only in non Windows systems).</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
<dt><strong>Version 1.2.1</strong> [08/May/2007]</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>compatible only with Lua 5.1 (Lua 5.0 support was dropped)</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
<dt><strong>Version 1.2</strong> [15/Mar/2006]</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>added optional argument to
|
||||
<a href="manual.html#attributes"><code>lfs.attributes</code></a></li>
|
||||
<li>added function
|
||||
<a href="manual.html#rmdir"><code>lfs.rmdir</code></a></li>
|
||||
<li>bug correction on <a href="manual.html#dir"><code>lfs.dir</code></a></li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
<dt><strong>Version 1.1</strong> [30/May/2005]</dt>
|
||||
<dd>
|
||||
<ul>
|
||||
<li>added function <a href="manual.html#touch"><code>lfs.touch</code></a>.</li>
|
||||
</ul>
|
||||
</dd>
|
||||
|
||||
<dt><strong>Version 1.0</strong> [21/Jan/2005]</dt>
|
||||
<dd />
|
||||
|
||||
<dt><strong>Version 1.0 Beta</strong> [10/Nov/2004]</dt>
|
||||
<dd />
|
||||
</dl>
|
||||
|
||||
<h2><a name="credits"></a>Credits</h2>
|
||||
|
||||
<p>LuaFileSystem was designed by Roberto Ierusalimschy,
|
||||
André Carregal and Tomás Guisasola as part of the
|
||||
<a href="http://www.keplerproject.org">Kepler Project</a>,
|
||||
which holds its copyright. LuaFileSystem is currently maintained by Fábio Mascarenhas.</p>
|
||||
|
||||
<h2><a name="contact"></a>Contact us</h2>
|
||||
|
||||
<p>For more information please
|
||||
<a href="mailto:info-NO-SPAM-THANKS@keplerproject.org">contact us</a>.
|
||||
Comments are welcome!</p>
|
||||
|
||||
<p>You can also reach other Kepler developers and users on the Kepler Project
|
||||
<a href="http://luaforge.net/mail/?group_id=104">mailing list</a>.</p>
|
||||
|
||||
</div> <!-- id="content" -->
|
||||
|
||||
</div> <!-- id="main" -->
|
||||
|
||||
<div id="about">
|
||||
<p><a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.0!</a></p>
|
||||
<p><small>$Id: index.html,v 1.44 2009/02/04 21:21:33 carregal Exp $</small></p>
|
||||
</div> <!-- id="about" -->
|
||||
|
||||
</div> <!-- id="container" -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,122 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>LuaFileSystem</title>
|
||||
<link rel="stylesheet" href="doc.css" type="text/css"/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="product">
|
||||
<div id="product_logo">
|
||||
<a href="http://www.keplerproject.org">
|
||||
<img alt="LuaFileSystem" src="luafilesystem.png"/>
|
||||
</a>
|
||||
</div>
|
||||
<div id="product_name"><big><strong>LuaFileSystem</strong></big></div>
|
||||
<div id="product_description">File System Library for the Lua Programming Language</div>
|
||||
</div> <!-- id="product" -->
|
||||
|
||||
<div id="main">
|
||||
|
||||
<div id="navigation">
|
||||
<h1>LuaFileSystem</h1>
|
||||
<ul>
|
||||
<li><a href="index.html">Home</a>
|
||||
<ul>
|
||||
<li><a href="index.html#overview">Overview</a></li>
|
||||
<li><a href="index.html#status">Status</a></li>
|
||||
<li><a href="index.html#download">Download</a></li>
|
||||
<li><a href="index.html#history">History</a></li>
|
||||
<li><a href="index.html#credits">Credits</a></li>
|
||||
<li><a href="index.html#contact">Contact us</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="manual.html">Manual</a>
|
||||
<ul>
|
||||
<li><a href="manual.html#introduction">Introduction</a></li>
|
||||
<li><a href="manual.html#building">Building</a></li>
|
||||
<li><a href="manual.html#installation">Installation</a></li>
|
||||
<li><a href="manual.html#reference">Reference</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="examples.html">Examples</a></li>
|
||||
<li><a href="https://github.com/keplerproject/luafilesystem">Project</a>
|
||||
<ul>
|
||||
<li><a href="https://github.com/keplerproject/luafilesystem/issues/">Bug Tracker</a></li>
|
||||
<li><a href="https://github.com/keplerproject/luafilesystem">Git</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>License</strong></li>
|
||||
</ul>
|
||||
</div> <!-- id="navigation" -->
|
||||
|
||||
<div id="content">
|
||||
|
||||
<h1>License</h1>
|
||||
|
||||
<p>
|
||||
LuaFileSystem is free software: it can be used for both academic
|
||||
and commercial purposes at absolutely no cost. There are no
|
||||
royalties or GNU-like "copyleft" restrictions. LuaFileSystem
|
||||
qualifies as
|
||||
<a href="http://www.opensource.org/docs/definition.html">Open Source</a>
|
||||
software.
|
||||
Its licenses are compatible with
|
||||
<a href="http://www.gnu.org/licenses/gpl.html">GPL</a>.
|
||||
LuaFileSystem is not in the public domain and the
|
||||
<a href="http://www.keplerproject.org">Kepler Project</a>
|
||||
keep its copyright.
|
||||
The legal details are below.
|
||||
</p>
|
||||
|
||||
<p>The spirit of the license is that you are free to use
|
||||
LuaFileSystem for any purpose at no cost without having to ask us.
|
||||
The only requirement is that if you do use LuaFileSystem, then you
|
||||
should give us credit by including the appropriate copyright notice
|
||||
somewhere in your product or its documentation.</p>
|
||||
|
||||
<p>The LuaFileSystem library is designed and implemented by Roberto
|
||||
Ierusalimschy, André Carregal and Tomás Guisasola.
|
||||
The implementation is not derived from licensed software.</p>
|
||||
|
||||
<hr/>
|
||||
<p>Copyright © 2003 Kepler Project.</p>
|
||||
|
||||
<p>Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:</p>
|
||||
|
||||
<p>The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.</p>
|
||||
|
||||
<p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.</p>
|
||||
|
||||
</div> <!-- id="content" -->
|
||||
|
||||
</div> <!-- id="main" -->
|
||||
|
||||
<div id="about">
|
||||
<p><a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.0!</a></p>
|
||||
<p><small>$Id: license.html,v 1.13 2008/02/11 22:42:21 carregal Exp $</small></p>
|
||||
</div><!-- id="about" -->
|
||||
|
||||
</div><!-- id="container" -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8.3 KiB |
@ -0,0 +1,280 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<title>LuaFileSystem</title>
|
||||
<link rel="stylesheet" href="doc.css" type="text/css"/>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="container">
|
||||
|
||||
<div id="product">
|
||||
<div id="product_logo">
|
||||
<a href="http://www.keplerproject.org"><img alt="LuaFileSystem" src="luafilesystem.png"/></a>
|
||||
</div>
|
||||
<div id="product_name"><big><strong>LuaFileSystem</strong></big></div>
|
||||
<div id="product_description">File System Library for the Lua Programming Language</div>
|
||||
</div> <!-- id="product" -->
|
||||
|
||||
<div id="main">
|
||||
|
||||
<div id="navigation">
|
||||
<h1>LuaFileSystem</h1>
|
||||
<ul>
|
||||
<li><a href="index.html">Home</a>
|
||||
<ul>
|
||||
<li><a href="index.html#overview">Overview</a></li>
|
||||
<li><a href="index.html#status">Status</a></li>
|
||||
<li><a href="index.html#download">Download</a></li>
|
||||
<li><a href="index.html#history">History</a></li>
|
||||
<li><a href="index.html#credits">Credits</a></li>
|
||||
<li><a href="index.html#contact">Contact us</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>Manual</strong>
|
||||
<ul>
|
||||
<li><a href="manual.html#introduction">Introduction</a></li>
|
||||
<li><a href="manual.html#building">Building</a></li>
|
||||
<li><a href="manual.html#installation">Installation</a></li>
|
||||
<li><a href="manual.html#reference">Reference</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="examples.html">Examples</a></li>
|
||||
<li><a href="https://github.com/keplerproject/luafilesystem">Project</a>
|
||||
<ul>
|
||||
<li><a href="https://github.com/keplerproject/luafilesystem/issues">Bug Tracker</a></li>
|
||||
<li><a href="https://github.com/keplerproject/luafilesystem">Git</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="license.html">License</a></li>
|
||||
</ul>
|
||||
</div> <!-- id="navigation" -->
|
||||
|
||||
<div id="content">
|
||||
|
||||
<h2><a name="introduction"></a>Introduction</h2>
|
||||
|
||||
<p>LuaFileSystem is a <a href="http://www.lua.org">Lua</a> library
|
||||
developed to complement the set of functions related to file
|
||||
systems offered by the standard Lua distribution.</p>
|
||||
|
||||
<p>LuaFileSystem offers a portable way to access
|
||||
the underlying directory structure and file attributes.</p>
|
||||
|
||||
<h2><a name="building"></a>Building</h2>
|
||||
|
||||
<p>
|
||||
LuaFileSystem should be built with Lua 5.1 so the language library
|
||||
and header files for the target version must be installed properly.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
LuaFileSystem offers a Makefile and a separate configuration file,
|
||||
<code>config</code>,
|
||||
which should be edited to suit your installation before running
|
||||
<code>make</code>.
|
||||
The file has some definitions like paths to the external libraries,
|
||||
compiler options and the like.
|
||||
</p>
|
||||
|
||||
<p>On Windows, the C runtime used to compile LuaFileSystem must be the same
|
||||
runtime that Lua uses, or some LuaFileSystem functions will not work.</p>
|
||||
|
||||
<h2><a name="installation"></a>Installation</h2>
|
||||
|
||||
<p>The easiest way to install LuaFileSystem is to use LuaRocks:</p>
|
||||
|
||||
<pre class="example">
|
||||
luarocks install luafilesystem
|
||||
</pre>
|
||||
|
||||
<p>If you prefer to install LuaFileSystem manually, the compiled binary should be copied to a directory in your
|
||||
<a href="http://www.lua.org/manual/5.1/manual.html#pdf-package.cpath">C path</a>.</p>
|
||||
|
||||
<h2><a name="reference"></a>Reference</h2>
|
||||
|
||||
<p>
|
||||
LuaFileSystem offers the following functions:
|
||||
</p>
|
||||
|
||||
<dl class="reference">
|
||||
<dt><a name="attributes"></a><strong><code>lfs.attributes (filepath [, aname])</code></strong></dt>
|
||||
<dd>Returns a table with the file attributes corresponding to
|
||||
<code>filepath</code> (or <code>nil</code> followed by an error message
|
||||
in case of error).
|
||||
If the second optional argument is given, then only the value of the
|
||||
named attribute is returned (this use is equivalent to
|
||||
<code>lfs.attributes(filepath).aname</code>, but the table is not created
|
||||
and only one attribute is retrieved from the O.S.).
|
||||
The attributes are described as follows;
|
||||
attribute <code>mode</code> is a string, all the others are numbers,
|
||||
and the time related attributes use the same time reference of
|
||||
<a href="http://www.lua.org/manual/5.1/manual.html#pdf-os.time"><code>os.time</code></a>:
|
||||
<dl>
|
||||
<dt><strong><code>dev</code></strong></dt>
|
||||
<dd>on Unix systems, this represents the device that the inode resides on. On Windows systems,
|
||||
represents the drive number of the disk containing the file</dd>
|
||||
|
||||
<dt><strong><code>ino</code></strong></dt>
|
||||
<dd>on Unix systems, this represents the inode number. On Windows systems this has no meaning</dd>
|
||||
|
||||
<dt><strong><code>mode</code></strong></dt>
|
||||
<dd>string representing the associated protection mode (the values could be
|
||||
<code>file</code>, <code>directory</code>, <code>link</code>, <code>socket</code>,
|
||||
<code>named pipe</code>, <code>char device</code>, <code>block device</code> or
|
||||
<code>other</code>)</dd>
|
||||
|
||||
<dt><strong><code>nlink</code></strong></dt>
|
||||
<dd>number of hard links to the file</dd>
|
||||
|
||||
<dt><strong><code>uid</code></strong></dt>
|
||||
<dd>user-id of owner (Unix only, always 0 on Windows)</dd>
|
||||
|
||||
<dt><strong><code>gid</code></strong></dt>
|
||||
<dd>group-id of owner (Unix only, always 0 on Windows)</dd>
|
||||
|
||||
<dt><strong><code>rdev</code></strong></dt>
|
||||
<dd>on Unix systems, represents the device type, for special file inodes.
|
||||
On Windows systems represents the same as <code>dev</code></dd>
|
||||
|
||||
<dt><strong><code>access</code></strong></dt>
|
||||
<dd>time of last access</dd>
|
||||
|
||||
<dt><strong><code>modification</code></strong></dt>
|
||||
<dd>time of last data modification</dd>
|
||||
|
||||
<dt><strong><code>change</code></strong></dt>
|
||||
<dd>time of last file status change</dd>
|
||||
|
||||
<dt><strong><code>size</code></strong></dt>
|
||||
<dd>file size, in bytes</dd>
|
||||
|
||||
<dt><strong><code>blocks</code></strong></dt>
|
||||
<dd>block allocated for file; (Unix only)</dd>
|
||||
|
||||
<dt><strong><code>blksize</code></strong></dt>
|
||||
<dd>optimal file system I/O blocksize; (Unix only)</dd>
|
||||
</dl>
|
||||
This function uses <code>stat</code> internally thus if the given
|
||||
<code>filepath</code> is a symbolic link, it is followed (if it points to
|
||||
another link the chain is followed recursively) and the information
|
||||
is about the file it refers to.
|
||||
To obtain information about the link itself, see function
|
||||
<a href="#symlinkattributes">lfs.symlinkattributes</a>.
|
||||
</dd>
|
||||
|
||||
<dt><a name="chdir"></a><strong><code>lfs.chdir (path)</code></strong></dt>
|
||||
<dd>Changes the current working directory to the given
|
||||
<code>path</code>.<br />
|
||||
Returns <code>true</code> in case of success or <code>nil</code> plus an
|
||||
error string.</dd>
|
||||
|
||||
<dt><a name="chdir"></a><strong><code>lfs.lock_dir(path, [seconds_stale])</code></strong></dt>
|
||||
<dd>Creates a lockfile (called lockfile.lfs) in <code>path</code> if it does not
|
||||
exist and returns the lock. If the lock already exists checks if
|
||||
it's stale, using the second parameter (default for the second
|
||||
parameter is <code>INT_MAX</code>, which in practice means the lock will never
|
||||
be stale. To free the the lock call <code>lock:free()</code>. <br/>
|
||||
In case of any errors it returns nil and the error message. In
|
||||
particular, if the lock exists and is not stale it returns the
|
||||
"File exists" message.</dd>
|
||||
|
||||
<dt><a name="getcwd"></a><strong><code>lfs.currentdir ()</code></strong></dt>
|
||||
<dd>Returns a string with the current working directory or <code>nil</code>
|
||||
plus an error string.</dd>
|
||||
|
||||
<dt><a name="dir"></a><strong><code>iter, dir_obj = lfs.dir (path)</code></strong></dt>
|
||||
<dd>
|
||||
Lua iterator over the entries of a given directory.
|
||||
Each time the iterator is called with <code>dir_obj</code> it returns a directory entry's name as a string, or
|
||||
<code>nil</code> if there are no more entries. You can also iterate by calling <code>dir_obj:next()</code>, and
|
||||
explicitly close the directory before the iteration finished with <code>dir_obj:close()</code>.
|
||||
Raises an error if <code>path</code> is not a directory.
|
||||
</dd>
|
||||
|
||||
<dt><a name="lock"></a><strong><code>lfs.lock (filehandle, mode[, start[, length]])</code></strong></dt>
|
||||
<dd>Locks a file or a part of it. This function works on <em>open files</em>; the
|
||||
file handle should be specified as the first argument.
|
||||
The string <code>mode</code> could be either
|
||||
<code>r</code> (for a read/shared lock) or <code>w</code> (for a
|
||||
write/exclusive lock). The optional arguments <code>start</code>
|
||||
and <code>length</code> can be used to specify a starting point and
|
||||
its length; both should be numbers.<br />
|
||||
Returns <code>true</code> if the operation was successful; in
|
||||
case of error, it returns <code>nil</code> plus an error string.
|
||||
</dd>
|
||||
|
||||
<dt><a name="link"></a><strong><code>lfs.link (old, new[, symlink])</code></strong></dt>
|
||||
<dd>Creates a link. The first argument is the object to link to
|
||||
and the second is the name of the link. If the optional third
|
||||
argument is true, the link will by a symbolic link (by default, a
|
||||
hard link is created).
|
||||
</dd>
|
||||
|
||||
<dt><a name="mkdir"></a><strong><code>lfs.mkdir (dirname)</code></strong></dt>
|
||||
<dd>Creates a new directory. The argument is the name of the new
|
||||
directory.<br />
|
||||
Returns <code>true</code> if the operation was successful;
|
||||
in case of error, it returns <code>nil</code> plus an error string.
|
||||
</dd>
|
||||
|
||||
<dt><a name="rmdir"></a><strong><code>lfs.rmdir (dirname)</code></strong></dt>
|
||||
<dd>Removes an existing directory. The argument is the name of the directory.<br />
|
||||
Returns <code>true</code> if the operation was successful;
|
||||
in case of error, it returns <code>nil</code> plus an error string.</dd>
|
||||
|
||||
<dt><a name="setmode"></a><strong><code>lfs.setmode (file, mode)</code></strong></dt>
|
||||
<dd>Sets the writing mode for a file. The mode string can be either <code>"binary"</code> or <code>"text"</code>.
|
||||
Returns <code>true</code> followed the previous mode string for the file, or
|
||||
<code>nil</code> followed by an error string in case of errors.
|
||||
On non-Windows platforms, where the two modes are identical,
|
||||
setting the mode has no effect, and the mode is always returned as <code>binary</code>.
|
||||
</dd>
|
||||
|
||||
<dt><a name="symlinkattributes"></a><strong><code>lfs.symlinkattributes (filepath [, aname])</code></strong></dt>
|
||||
<dd>Identical to <a href="#attributes">lfs.attributes</a> except that
|
||||
it obtains information about the link itself (not the file it refers to).
|
||||
On Windows this function does not yet support links, and is identical to
|
||||
<code>lfs.attributes</code>.
|
||||
</dd>
|
||||
|
||||
<dt><a name="touch"></a><strong><code>lfs.touch (filepath [, atime [, mtime]])</code></strong></dt>
|
||||
<dd>Set access and modification times of a file. This function is
|
||||
a bind to <code>utime</code> function. The first argument is the
|
||||
filename, the second argument (<code>atime</code>) is the access time,
|
||||
and the third argument (<code>mtime</code>) is the modification time.
|
||||
Both times are provided in seconds (which should be generated with
|
||||
Lua standard function <code>os.time</code>).
|
||||
If the modification time is omitted, the access time provided is used;
|
||||
if both times are omitted, the current time is used.<br />
|
||||
Returns <code>true</code> if the operation was successful;
|
||||
in case of error, it returns <code>nil</code> plus an error string.
|
||||
</dd>
|
||||
|
||||
<dt><a name="unlock"></a><strong><code>lfs.unlock (filehandle[, start[, length]])</code></strong></dt>
|
||||
<dd>Unlocks a file or a part of it. This function works on
|
||||
<em>open files</em>; the file handle should be specified as the first
|
||||
argument. The optional arguments <code>start</code> and
|
||||
<code>length</code> can be used to specify a starting point and its
|
||||
length; both should be numbers.<br />
|
||||
Returns <code>true</code> if the operation was successful;
|
||||
in case of error, it returns <code>nil</code> plus an error string.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
</div> <!-- id="content" -->
|
||||
|
||||
</div> <!-- id="main" -->
|
||||
|
||||
<div id="about">
|
||||
<p><a href="http://validator.w3.org/check?uri=referer">Valid XHTML 1.0!</a></p>
|
||||
<p><small>$Id: manual.html,v 1.45 2009/06/03 20:53:55 mascarenhas Exp $</small></p>
|
||||
</div> <!-- id="about" -->
|
||||
|
||||
</div> <!-- id="container" -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,29 @@
|
||||
package = "LuaFileSystem"
|
||||
version = "1.6.3-2"
|
||||
source = {
|
||||
url = "git://github.com/keplerproject/luafilesystem",
|
||||
tag = "v_1_6_3"
|
||||
}
|
||||
description = {
|
||||
summary = "File System Library for the Lua Programming Language",
|
||||
detailed = [[
|
||||
LuaFileSystem is a Lua library developed to complement the set of
|
||||
functions related to file systems offered by the standard Lua
|
||||
distribution. LuaFileSystem offers a portable way to access the
|
||||
underlying directory structure and file attributes.
|
||||
]],
|
||||
homepage = "http://keplerproject.github.io/luafilesystem",
|
||||
license = "MIT/X11"
|
||||
}
|
||||
dependencies = {
|
||||
"lua >= 5.1"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
lfs = "src/lfs.c"
|
||||
},
|
||||
copy_directories = {
|
||||
"doc", "tests"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
rock_manifest = {
|
||||
doc = {
|
||||
us = {
|
||||
["doc.css"] = "d0a913514fb190240b3b4033d105cbc0",
|
||||
["examples.html"] = "5832f72021728374cf57b621d62ce0ff",
|
||||
["index.html"] = "96885bdda963939f0a363b5fa6b16b59",
|
||||
["license.html"] = "e3a756835cb7c8ae277d5e513c8e32ee",
|
||||
["luafilesystem.png"] = "81e923e976e99f894ea0aa8b52baff29",
|
||||
["manual.html"] = "d6473799b73ce486c3ea436586cb3b34"
|
||||
}
|
||||
},
|
||||
lib = {
|
||||
["lfs.dll"] = "c0e2145e1ef2815ae5fae01454291b66"
|
||||
},
|
||||
["luafilesystem-1.6.3-2.rockspec"] = "eb0ef7c190516892eb8357af799eea5f",
|
||||
tests = {
|
||||
["test.lua"] = "7b4ddb5bdb7e0b1b1ed0150d473535c9"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env lua5.1
|
||||
|
||||
local tmp = "/tmp"
|
||||
local sep = string.match (package.config, "[^\n]+")
|
||||
local upper = ".."
|
||||
|
||||
local lfs = require"lfs"
|
||||
print (lfs._VERSION)
|
||||
|
||||
io.write(".")
|
||||
io.flush()
|
||||
|
||||
function attrdir (path)
|
||||
for file in lfs.dir(path) do
|
||||
if file ~= "." and file ~= ".." then
|
||||
local f = path..sep..file
|
||||
print ("\t=> "..f.." <=")
|
||||
local attr = lfs.attributes (f)
|
||||
assert (type(attr) == "table")
|
||||
if attr.mode == "directory" then
|
||||
attrdir (f)
|
||||
else
|
||||
for name, value in pairs(attr) do
|
||||
print (name, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Checking changing directories
|
||||
local current = assert (lfs.currentdir())
|
||||
local reldir = string.gsub (current, "^.*%"..sep.."([^"..sep.."])$", "%1")
|
||||
assert (lfs.chdir (upper), "could not change to upper directory")
|
||||
assert (lfs.chdir (reldir), "could not change back to current directory")
|
||||
assert (lfs.currentdir() == current, "error trying to change directories")
|
||||
assert (lfs.chdir ("this couldn't be an actual directory") == nil, "could change to a non-existent directory")
|
||||
|
||||
io.write(".")
|
||||
io.flush()
|
||||
|
||||
-- Changing creating and removing directories
|
||||
local tmpdir = current..sep.."lfs_tmp_dir"
|
||||
local tmpfile = tmpdir..sep.."tmp_file"
|
||||
-- Test for existence of a previous lfs_tmp_dir
|
||||
-- that may have resulted from an interrupted test execution and remove it
|
||||
if lfs.chdir (tmpdir) then
|
||||
assert (lfs.chdir (upper), "could not change to upper directory")
|
||||
assert (os.remove (tmpfile), "could not remove file from previous test")
|
||||
assert (lfs.rmdir (tmpdir), "could not remove directory from previous test")
|
||||
end
|
||||
|
||||
io.write(".")
|
||||
io.flush()
|
||||
|
||||
-- tries to create a directory
|
||||
assert (lfs.mkdir (tmpdir), "could not make a new directory")
|
||||
local attrib, errmsg = lfs.attributes (tmpdir)
|
||||
if not attrib then
|
||||
error ("could not get attributes of file `"..tmpdir.."':\n"..errmsg)
|
||||
end
|
||||
local f = io.open(tmpfile, "w")
|
||||
f:close()
|
||||
|
||||
io.write(".")
|
||||
io.flush()
|
||||
|
||||
-- Change access time
|
||||
local testdate = os.time({ year = 2007, day = 10, month = 2, hour=0})
|
||||
assert (lfs.touch (tmpfile, testdate))
|
||||
local new_att = assert (lfs.attributes (tmpfile))
|
||||
assert (new_att.access == testdate, "could not set access time")
|
||||
assert (new_att.modification == testdate, "could not set modification time")
|
||||
|
||||
io.write(".")
|
||||
io.flush()
|
||||
|
||||
-- Change access and modification time
|
||||
local testdate1 = os.time({ year = 2007, day = 10, month = 2, hour=0})
|
||||
local testdate2 = os.time({ year = 2007, day = 11, month = 2, hour=0})
|
||||
|
||||
assert (lfs.touch (tmpfile, testdate2, testdate1))
|
||||
local new_att = assert (lfs.attributes (tmpfile))
|
||||
assert (new_att.access == testdate2, "could not set access time")
|
||||
assert (new_att.modification == testdate1, "could not set modification time")
|
||||
|
||||
io.write(".")
|
||||
io.flush()
|
||||
|
||||
-- Checking link (does not work on Windows)
|
||||
if lfs.link (tmpfile, "_a_link_for_test_", true) then
|
||||
assert (lfs.attributes"_a_link_for_test_".mode == "file")
|
||||
assert (lfs.symlinkattributes"_a_link_for_test_".mode == "link")
|
||||
assert (lfs.link (tmpfile, "_a_hard_link_for_test_"))
|
||||
assert (lfs.attributes (tmpfile, "nlink") == 2)
|
||||
assert (os.remove"_a_link_for_test_")
|
||||
assert (os.remove"_a_hard_link_for_test_")
|
||||
end
|
||||
|
||||
io.write(".")
|
||||
io.flush()
|
||||
|
||||
-- Checking text/binary modes (only has an effect in Windows)
|
||||
local f = io.open(tmpfile, "w")
|
||||
local result, mode = lfs.setmode(f, "binary")
|
||||
assert(result) -- on non-Windows platforms, mode is always returned as "binary"
|
||||
result, mode = lfs.setmode(f, "text")
|
||||
assert(result and mode == "binary")
|
||||
f:close()
|
||||
|
||||
io.write(".")
|
||||
io.flush()
|
||||
|
||||
-- Restore access time to current value
|
||||
assert (lfs.touch (tmpfile, attrib.access, attrib.modification))
|
||||
new_att = assert (lfs.attributes (tmpfile))
|
||||
assert (new_att.access == attrib.access)
|
||||
assert (new_att.modification == attrib.modification)
|
||||
|
||||
io.write(".")
|
||||
io.flush()
|
||||
|
||||
-- Check consistency of lfs.attributes values
|
||||
local attr = lfs.attributes (tmpfile)
|
||||
for key, value in pairs(attr) do
|
||||
assert (value == lfs.attributes (tmpfile, key),
|
||||
"lfs.attributes values not consistent")
|
||||
end
|
||||
|
||||
-- Remove new file and directory
|
||||
assert (os.remove (tmpfile), "could not remove new file")
|
||||
assert (lfs.rmdir (tmpdir), "could not remove new directory")
|
||||
assert (lfs.mkdir (tmpdir..sep.."lfs_tmp_dir") == nil, "could create a directory inside a non-existent one")
|
||||
|
||||
io.write(".")
|
||||
io.flush()
|
||||
|
||||
-- Trying to get attributes of a non-existent file
|
||||
assert (lfs.attributes ("this couldn't be an actual file") == nil, "could get attributes of a non-existent file")
|
||||
assert (type(lfs.attributes (upper)) == "table", "couldn't get attributes of upper directory")
|
||||
|
||||
io.write(".")
|
||||
io.flush()
|
||||
|
||||
-- Stressing directory iterator
|
||||
count = 0
|
||||
for i = 1, 4000 do
|
||||
for file in lfs.dir (tmp) do
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
|
||||
io.write(".")
|
||||
io.flush()
|
||||
|
||||
-- Stressing directory iterator, explicit version
|
||||
count = 0
|
||||
for i = 1, 4000 do
|
||||
local iter, dir = lfs.dir(tmp)
|
||||
local file = dir:next()
|
||||
while file do
|
||||
count = count + 1
|
||||
file = dir:next()
|
||||
end
|
||||
assert(not pcall(dir.next, dir))
|
||||
end
|
||||
|
||||
io.write(".")
|
||||
io.flush()
|
||||
|
||||
-- directory explicit close
|
||||
local iter, dir = lfs.dir(tmp)
|
||||
dir:close()
|
||||
assert(not pcall(dir.next, dir))
|
||||
print"Ok!"
|
||||
@ -0,0 +1,653 @@
|
||||
#!/usr/bin/env lua
|
||||
---------
|
||||
-- LuaSrcDiet
|
||||
--
|
||||
-- Compresses Lua source code by removing unnecessary characters.
|
||||
-- For Lua 5.1+ source code.
|
||||
--
|
||||
-- **Notes:**
|
||||
--
|
||||
-- * Remember to update version and date information below (MSG_TITLE).
|
||||
-- * TODO: passing data tables around is a horrific mess.
|
||||
-- * TODO: to implement pcall() to properly handle lexer etc. errors.
|
||||
-- * TODO: need some automatic testing for a semblance of sanity.
|
||||
-- * TODO: the plugin module is highly experimental and unstable.
|
||||
----
|
||||
local equiv = require "luasrcdiet.equiv"
|
||||
local fs = require "luasrcdiet.fs"
|
||||
local llex = require "luasrcdiet.llex"
|
||||
local lparser = require "luasrcdiet.lparser"
|
||||
local luasrcdiet = require "luasrcdiet.init"
|
||||
local optlex = require "luasrcdiet.optlex"
|
||||
local optparser = require "luasrcdiet.optparser"
|
||||
|
||||
local byte = string.byte
|
||||
local concat = table.concat
|
||||
local find = string.find
|
||||
local fmt = string.format
|
||||
local gmatch = string.gmatch
|
||||
local match = string.match
|
||||
local print = print
|
||||
local rep = string.rep
|
||||
local sub = string.sub
|
||||
|
||||
local plugin
|
||||
|
||||
local LUA_VERSION = match(_VERSION, " (5%.[123])$") or "5.1"
|
||||
|
||||
-- Is --opt-binequiv available for this Lua version?
|
||||
local BIN_EQUIV_AVAIL = LUA_VERSION == "5.1" and not package.loaded.jit
|
||||
|
||||
|
||||
---------------------- Messages and textual data ----------------------
|
||||
|
||||
local MSG_TITLE = fmt([[
|
||||
LuaSrcDiet: Puts your Lua 5.1+ source code on a diet
|
||||
Version %s <%s>
|
||||
]], luasrcdiet._VERSION, luasrcdiet._HOMEPAGE)
|
||||
|
||||
local MSG_USAGE = [[
|
||||
usage: luasrcdiet [options] [filenames]
|
||||
|
||||
example:
|
||||
>luasrcdiet myscript.lua -o myscript_.lua
|
||||
|
||||
options:
|
||||
-v, --version prints version information
|
||||
-h, --help prints usage information
|
||||
-o <file> specify file name to write output
|
||||
-s <suffix> suffix for output files (default '_')
|
||||
--keep <msg> keep block comment with <msg> inside
|
||||
--plugin <module> run <module> in plugin/ directory
|
||||
- stop handling arguments
|
||||
|
||||
(optimization levels)
|
||||
--none all optimizations off (normalizes EOLs only)
|
||||
--basic lexer-based optimizations only
|
||||
--maximum maximize reduction of source
|
||||
|
||||
(informational)
|
||||
--quiet process files quietly
|
||||
--read-only read file and print token stats only
|
||||
--dump-lexer dump raw tokens from lexer to stdout
|
||||
--dump-parser dump variable tracking tables from parser
|
||||
--details extra info (strings, numbers, locals)
|
||||
|
||||
features (to disable, insert 'no' prefix like --noopt-comments):
|
||||
%s
|
||||
default settings:
|
||||
%s]]
|
||||
|
||||
-- Optimization options, for ease of switching on and off.
|
||||
--
|
||||
-- * Positive to enable optimization, negative (no) to disable.
|
||||
-- * These options should follow --opt-* and --noopt-* style for now.
|
||||
local OPTION = [[
|
||||
--opt-comments,'remove comments and block comments'
|
||||
--opt-whitespace,'remove whitespace excluding EOLs'
|
||||
--opt-emptylines,'remove empty lines'
|
||||
--opt-eols,'all above, plus remove unnecessary EOLs'
|
||||
--opt-strings,'optimize strings and long strings'
|
||||
--opt-numbers,'optimize numbers'
|
||||
--opt-locals,'optimize local variable names'
|
||||
--opt-entropy,'tries to reduce symbol entropy of locals'
|
||||
--opt-srcequiv,'insist on source (lexer stream) equivalence'
|
||||
--opt-binequiv,'insist on binary chunk equivalence (only for PUC Lua 5.1)'
|
||||
--opt-experimental,'apply experimental optimizations'
|
||||
]]
|
||||
|
||||
-- Preset configuration.
|
||||
local DEFAULT_CONFIG = [[
|
||||
--opt-comments --opt-whitespace --opt-emptylines
|
||||
--opt-numbers --opt-locals
|
||||
--opt-srcequiv --noopt-binequiv
|
||||
]]
|
||||
-- Override configurations: MUST explicitly enable/disable everything.
|
||||
local BASIC_CONFIG = [[
|
||||
--opt-comments --opt-whitespace --opt-emptylines
|
||||
--noopt-eols --noopt-strings --noopt-numbers
|
||||
--noopt-locals --noopt-entropy
|
||||
--opt-srcequiv --noopt-binequiv
|
||||
]]
|
||||
local MAXIMUM_CONFIG = [[
|
||||
--opt-comments --opt-whitespace --opt-emptylines
|
||||
--opt-eols --opt-strings --opt-numbers
|
||||
--opt-locals --opt-entropy
|
||||
--opt-srcequiv
|
||||
]] .. (BIN_EQUIV_AVAIL and ' --opt-binequiv' or ' --noopt-binequiv')
|
||||
|
||||
local NONE_CONFIG = [[
|
||||
--noopt-comments --noopt-whitespace --noopt-emptylines
|
||||
--noopt-eols --noopt-strings --noopt-numbers
|
||||
--noopt-locals --noopt-entropy
|
||||
--opt-srcequiv --noopt-binequiv
|
||||
]]
|
||||
|
||||
local DEFAULT_SUFFIX = "_" -- default suffix for file renaming
|
||||
local PLUGIN_SUFFIX = "luasrcdiet.plugin." -- relative location of plugins
|
||||
|
||||
|
||||
------------- Startup and initialize option list handling -------------
|
||||
|
||||
--- Simple error message handler; change to error if traceback wanted.
|
||||
--
|
||||
-- @tparam string msg The message to print.
|
||||
local function die(msg)
|
||||
print("LuaSrcDiet (error): "..msg); os.exit(1)
|
||||
end
|
||||
--die = error--DEBUG
|
||||
|
||||
-- Prepare text for list of optimizations, prepare lookup table.
|
||||
local MSG_OPTIONS = ""
|
||||
do
|
||||
local WIDTH = 24
|
||||
local o = {}
|
||||
for op, desc in gmatch(OPTION, "%s*([^,]+),'([^']+)'") do
|
||||
local msg = " "..op
|
||||
msg = msg..rep(" ", WIDTH - #msg)..desc.."\n"
|
||||
MSG_OPTIONS = MSG_OPTIONS..msg
|
||||
o[op] = true
|
||||
o["--no"..sub(op, 3)] = true
|
||||
end
|
||||
OPTION = o -- replace OPTION with lookup table
|
||||
end
|
||||
|
||||
MSG_USAGE = fmt(MSG_USAGE, MSG_OPTIONS, DEFAULT_CONFIG)
|
||||
|
||||
|
||||
--------- Global variable initialization, option set handling ---------
|
||||
|
||||
local suffix = DEFAULT_SUFFIX -- file suffix
|
||||
local option = {} -- program options
|
||||
local stat_c, stat_l -- statistics tables
|
||||
|
||||
--- Sets option lookup table based on a text list of options.
|
||||
--
|
||||
-- Note: additional forced settings for --opt-eols is done in optlex.lua.
|
||||
--
|
||||
-- @tparam string CONFIG
|
||||
local function set_options(CONFIG)
|
||||
for op in gmatch(CONFIG, "(%-%-%S+)") do
|
||||
if sub(op, 3, 4) == "no" and -- handle negative options
|
||||
OPTION["--"..sub(op, 5)] then
|
||||
option[sub(op, 5)] = false
|
||||
else
|
||||
option[sub(op, 3)] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-------------------------- Support functions --------------------------
|
||||
|
||||
-- List of token types, parser-significant types are up to TTYPE_GRAMMAR
|
||||
-- while the rest are not used by parsers; arranged for stats display.
|
||||
local TTYPES = {
|
||||
"TK_KEYWORD", "TK_NAME", "TK_NUMBER", -- grammar
|
||||
"TK_STRING", "TK_LSTRING", "TK_OP",
|
||||
"TK_EOS",
|
||||
"TK_COMMENT", "TK_LCOMMENT", -- non-grammar
|
||||
"TK_EOL", "TK_SPACE",
|
||||
}
|
||||
local TTYPE_GRAMMAR = 7
|
||||
|
||||
local EOLTYPES = { -- EOL names for token dump
|
||||
["\n"] = "LF", ["\r"] = "CR",
|
||||
["\n\r"] = "LFCR", ["\r\n"] = "CRLF",
|
||||
}
|
||||
|
||||
--- Reads source code from the file.
|
||||
--
|
||||
-- @tparam string fname Path of the file to read.
|
||||
-- @treturn string Content of the file.
|
||||
local function load_file(fname)
|
||||
local data, err = fs.read_file(fname, "rb")
|
||||
if not data then die(err) end
|
||||
return data
|
||||
end
|
||||
|
||||
--- Saves source code to the file.
|
||||
--
|
||||
-- @tparam string fname Path of the destination file.
|
||||
-- @tparam string dat The data to write into the file.
|
||||
local function save_file(fname, dat)
|
||||
local ok, err = fs.write_file(fname, dat, "wb")
|
||||
if not ok then die(err) end
|
||||
end
|
||||
|
||||
|
||||
------------------ Functions to deal with statistics ------------------
|
||||
|
||||
--- Initializes the statistics table.
|
||||
local function stat_init()
|
||||
stat_c, stat_l = {}, {}
|
||||
for i = 1, #TTYPES do
|
||||
local ttype = TTYPES[i]
|
||||
stat_c[ttype], stat_l[ttype] = 0, 0
|
||||
end
|
||||
end
|
||||
|
||||
--- Adds a token to the statistics table.
|
||||
--
|
||||
-- @tparam string tok The token.
|
||||
-- @param seminfo
|
||||
local function stat_add(tok, seminfo)
|
||||
stat_c[tok] = stat_c[tok] + 1
|
||||
stat_l[tok] = stat_l[tok] + #seminfo
|
||||
end
|
||||
|
||||
--- Computes totals for the statistics table, returns average table.
|
||||
--
|
||||
-- @treturn table
|
||||
local function stat_calc()
|
||||
local function avg(c, l) -- safe average function
|
||||
if c == 0 then return 0 end
|
||||
return l / c
|
||||
end
|
||||
local stat_a = {}
|
||||
local c, l = 0, 0
|
||||
for i = 1, TTYPE_GRAMMAR do -- total grammar tokens
|
||||
local ttype = TTYPES[i]
|
||||
c = c + stat_c[ttype]; l = l + stat_l[ttype]
|
||||
end
|
||||
stat_c.TOTAL_TOK, stat_l.TOTAL_TOK = c, l
|
||||
stat_a.TOTAL_TOK = avg(c, l)
|
||||
c, l = 0, 0
|
||||
for i = 1, #TTYPES do -- total all tokens
|
||||
local ttype = TTYPES[i]
|
||||
c = c + stat_c[ttype]; l = l + stat_l[ttype]
|
||||
stat_a[ttype] = avg(stat_c[ttype], stat_l[ttype])
|
||||
end
|
||||
stat_c.TOTAL_ALL, stat_l.TOTAL_ALL = c, l
|
||||
stat_a.TOTAL_ALL = avg(c, l)
|
||||
return stat_a
|
||||
end
|
||||
|
||||
|
||||
----------------------------- Main tasks -----------------------------
|
||||
|
||||
--- A simple token dumper, minimal translation of seminfo data.
|
||||
--
|
||||
-- @tparam string srcfl Path of the source file.
|
||||
local function dump_tokens(srcfl)
|
||||
-- Load file and process source input into tokens.
|
||||
local z = load_file(srcfl)
|
||||
local toklist, seminfolist = llex.lex(z)
|
||||
|
||||
-- Display output.
|
||||
for i = 1, #toklist do
|
||||
local tok, seminfo = toklist[i], seminfolist[i]
|
||||
if tok == "TK_OP" and byte(seminfo) < 32 then
|
||||
seminfo = "("..byte(seminfo)..")"
|
||||
elseif tok == "TK_EOL" then
|
||||
seminfo = EOLTYPES[seminfo]
|
||||
else
|
||||
seminfo = "'"..seminfo.."'"
|
||||
end
|
||||
print(tok.." "..seminfo)
|
||||
end--for
|
||||
end
|
||||
|
||||
--- Dumps globalinfo and localinfo tables.
|
||||
--
|
||||
-- @tparam string srcfl Path of the source file.
|
||||
local function dump_parser(srcfl)
|
||||
-- Load file and process source input into tokens,
|
||||
local z = load_file(srcfl)
|
||||
local toklist, seminfolist, toklnlist = llex.lex(z)
|
||||
|
||||
-- Do parser optimization here.
|
||||
local xinfo = lparser.parse(toklist, seminfolist, toklnlist)
|
||||
local globalinfo, localinfo = xinfo.globalinfo, xinfo.localinfo
|
||||
|
||||
-- Display output.
|
||||
local hl = rep("-", 72)
|
||||
print("*** Local/Global Variable Tracker Tables ***")
|
||||
print(hl.."\n GLOBALS\n"..hl)
|
||||
-- global tables have a list of xref numbers only
|
||||
for i = 1, #globalinfo do
|
||||
local obj = globalinfo[i]
|
||||
local msg = "("..i..") '"..obj.name.."' -> "
|
||||
local xref = obj.xref
|
||||
for j = 1, #xref do msg = msg..xref[j].." " end
|
||||
print(msg)
|
||||
end
|
||||
-- Local tables have xref numbers and a few other special
|
||||
-- numbers that are specially named: decl (declaration xref),
|
||||
-- act (activation xref), rem (removal xref).
|
||||
print(hl.."\n LOCALS (decl=declared act=activated rem=removed)\n"..hl)
|
||||
for i = 1, #localinfo do
|
||||
local obj = localinfo[i]
|
||||
local msg = "("..i..") '"..obj.name.."' decl:"..obj.decl..
|
||||
" act:"..obj.act.." rem:"..obj.rem
|
||||
if obj.is_special then
|
||||
msg = msg.." is_special"
|
||||
end
|
||||
msg = msg.." -> "
|
||||
local xref = obj.xref
|
||||
for j = 1, #xref do msg = msg..xref[j].." " end
|
||||
print(msg)
|
||||
end
|
||||
print(hl.."\n")
|
||||
end
|
||||
|
||||
--- Reads source file(s) and reports some statistics.
|
||||
--
|
||||
-- @tparam string srcfl Path of the source file.
|
||||
local function read_only(srcfl)
|
||||
-- Load file and process source input into tokens.
|
||||
local z = load_file(srcfl)
|
||||
local toklist, seminfolist = llex.lex(z)
|
||||
print(MSG_TITLE)
|
||||
print("Statistics for: "..srcfl.."\n")
|
||||
|
||||
-- Collect statistics.
|
||||
stat_init()
|
||||
for i = 1, #toklist do
|
||||
local tok, seminfo = toklist[i], seminfolist[i]
|
||||
stat_add(tok, seminfo)
|
||||
end--for
|
||||
local stat_a = stat_calc()
|
||||
|
||||
-- Display output.
|
||||
local function figures(tt)
|
||||
return stat_c[tt], stat_l[tt], stat_a[tt]
|
||||
end
|
||||
local tabf1, tabf2 = "%-16s%8s%8s%10s", "%-16s%8d%8d%10.2f"
|
||||
local hl = rep("-", 42)
|
||||
print(fmt(tabf1, "Lexical", "Input", "Input", "Input"))
|
||||
print(fmt(tabf1, "Elements", "Count", "Bytes", "Average"))
|
||||
print(hl)
|
||||
for i = 1, #TTYPES do
|
||||
local ttype = TTYPES[i]
|
||||
print(fmt(tabf2, ttype, figures(ttype)))
|
||||
if ttype == "TK_EOS" then print(hl) end
|
||||
end
|
||||
print(hl)
|
||||
print(fmt(tabf2, "Total Elements", figures("TOTAL_ALL")))
|
||||
print(hl)
|
||||
print(fmt(tabf2, "Total Tokens", figures("TOTAL_TOK")))
|
||||
print(hl.."\n")
|
||||
end
|
||||
|
||||
--- Processes source file(s), writes output and reports some statistics.
|
||||
--
|
||||
-- @tparam string srcfl Path of the source file.
|
||||
-- @tparam string destfl Path of the destination file where to write optimized source.
|
||||
local function process_file(srcfl, destfl)
|
||||
-- handle quiet option
|
||||
local function print(...) --luacheck: ignore 431
|
||||
if option.QUIET then return end
|
||||
_G.print(...)
|
||||
end
|
||||
if plugin and plugin.init then -- plugin init
|
||||
option.EXIT = false
|
||||
plugin.init(option, srcfl, destfl)
|
||||
if option.EXIT then return end
|
||||
end
|
||||
print(MSG_TITLE) -- title message
|
||||
|
||||
-- Load file and process source input into tokens.
|
||||
local z = load_file(srcfl)
|
||||
if plugin and plugin.post_load then -- plugin post-load
|
||||
z = plugin.post_load(z) or z
|
||||
if option.EXIT then return end
|
||||
end
|
||||
local toklist, seminfolist, toklnlist = llex.lex(z)
|
||||
if plugin and plugin.post_lex then -- plugin post-lex
|
||||
plugin.post_lex(toklist, seminfolist, toklnlist)
|
||||
if option.EXIT then return end
|
||||
end
|
||||
|
||||
-- Collect 'before' statistics.
|
||||
stat_init()
|
||||
for i = 1, #toklist do
|
||||
local tok, seminfo = toklist[i], seminfolist[i]
|
||||
stat_add(tok, seminfo)
|
||||
end--for
|
||||
local stat1_a = stat_calc()
|
||||
local stat1_c, stat1_l = stat_c, stat_l
|
||||
|
||||
-- Do parser optimization here.
|
||||
optparser.print = print -- hack
|
||||
local xinfo = lparser.parse(toklist, seminfolist, toklnlist)
|
||||
if plugin and plugin.post_parse then -- plugin post-parse
|
||||
plugin.post_parse(xinfo.globalinfo, xinfo.localinfo)
|
||||
if option.EXIT then return end
|
||||
end
|
||||
optparser.optimize(option, toklist, seminfolist, xinfo)
|
||||
if plugin and plugin.post_optparse then -- plugin post-optparse
|
||||
plugin.post_optparse()
|
||||
if option.EXIT then return end
|
||||
end
|
||||
|
||||
-- Do lexer optimization here, save output file.
|
||||
local warn = optlex.warn -- use this as a general warning lookup
|
||||
optlex.print = print -- hack
|
||||
toklist, seminfolist, toklnlist
|
||||
= optlex.optimize(option, toklist, seminfolist, toklnlist)
|
||||
if plugin and plugin.post_optlex then -- plugin post-optlex
|
||||
plugin.post_optlex(toklist, seminfolist, toklnlist)
|
||||
if option.EXIT then return end
|
||||
end
|
||||
local dat = concat(seminfolist)
|
||||
-- Depending on options selected, embedded EOLs in long strings and
|
||||
-- long comments may not have been translated to \n, tack a warning.
|
||||
if find(dat, "\r\n", 1, 1) or
|
||||
find(dat, "\n\r", 1, 1) then
|
||||
warn.MIXEDEOL = true
|
||||
end
|
||||
|
||||
-- Test source and binary chunk equivalence.
|
||||
equiv.init(option, llex, warn)
|
||||
equiv.source(z, dat)
|
||||
if BIN_EQUIV_AVAIL then
|
||||
equiv.binary(z, dat)
|
||||
end
|
||||
local smsg = "before and after lexer streams are NOT equivalent!"
|
||||
local bmsg = "before and after binary chunks are NOT equivalent!"
|
||||
-- for reporting, die if option was selected, else just warn
|
||||
if warn.SRC_EQUIV then
|
||||
if option["opt-srcequiv"] then die(smsg) end
|
||||
else
|
||||
print("*** SRCEQUIV: token streams are sort of equivalent")
|
||||
if option["opt-locals"] then
|
||||
print("(but no identifier comparisons since --opt-locals enabled)")
|
||||
end
|
||||
print()
|
||||
end
|
||||
if warn.BIN_EQUIV then
|
||||
if option["opt-binequiv"] then die(bmsg) end
|
||||
elseif BIN_EQUIV_AVAIL then
|
||||
print("*** BINEQUIV: binary chunks are sort of equivalent")
|
||||
print()
|
||||
end
|
||||
|
||||
-- Save optimized source stream to output file.
|
||||
save_file(destfl, dat)
|
||||
|
||||
-- Collect 'after' statistics.
|
||||
stat_init()
|
||||
for i = 1, #toklist do
|
||||
local tok, seminfo = toklist[i], seminfolist[i]
|
||||
stat_add(tok, seminfo)
|
||||
end--for
|
||||
local stat_a = stat_calc()
|
||||
|
||||
-- Display output.
|
||||
print("Statistics for: "..srcfl.." -> "..destfl.."\n")
|
||||
local function figures(tt)
|
||||
return stat1_c[tt], stat1_l[tt], stat1_a[tt],
|
||||
stat_c[tt], stat_l[tt], stat_a[tt]
|
||||
end
|
||||
local tabf1, tabf2 = "%-16s%8s%8s%10s%8s%8s%10s",
|
||||
"%-16s%8d%8d%10.2f%8d%8d%10.2f"
|
||||
local hl = rep("-", 68)
|
||||
print("*** lexer-based optimizations summary ***\n"..hl)
|
||||
print(fmt(tabf1, "Lexical",
|
||||
"Input", "Input", "Input",
|
||||
"Output", "Output", "Output"))
|
||||
print(fmt(tabf1, "Elements",
|
||||
"Count", "Bytes", "Average",
|
||||
"Count", "Bytes", "Average"))
|
||||
print(hl)
|
||||
for i = 1, #TTYPES do
|
||||
local ttype = TTYPES[i]
|
||||
print(fmt(tabf2, ttype, figures(ttype)))
|
||||
if ttype == "TK_EOS" then print(hl) end
|
||||
end
|
||||
print(hl)
|
||||
print(fmt(tabf2, "Total Elements", figures("TOTAL_ALL")))
|
||||
print(hl)
|
||||
print(fmt(tabf2, "Total Tokens", figures("TOTAL_TOK")))
|
||||
print(hl)
|
||||
|
||||
-- Report warning flags from optimizing process.
|
||||
if warn.LSTRING then
|
||||
print("* WARNING: "..warn.LSTRING)
|
||||
elseif warn.MIXEDEOL then
|
||||
print("* WARNING: ".."output still contains some CRLF or LFCR line endings")
|
||||
elseif warn.SRC_EQUIV then
|
||||
print("* WARNING: "..smsg)
|
||||
elseif warn.BIN_EQUIV then
|
||||
print("* WARNING: "..bmsg)
|
||||
end
|
||||
print()
|
||||
end
|
||||
|
||||
|
||||
---------------------------- Main functions ---------------------------
|
||||
|
||||
local arg = {...} -- program arguments
|
||||
set_options(DEFAULT_CONFIG) -- set to default options at beginning
|
||||
|
||||
--- Does per-file handling, ship off to tasks.
|
||||
--
|
||||
-- @tparam {string,...} fspec List of source files.
|
||||
local function do_files(fspec)
|
||||
for i = 1, #fspec do
|
||||
local srcfl = fspec[i]
|
||||
local destfl
|
||||
|
||||
-- Find and replace extension for filenames.
|
||||
local extb, exte = find(srcfl, "%.[^%.%\\%/]*$")
|
||||
local basename, extension = srcfl, ""
|
||||
if extb and extb > 1 then
|
||||
basename = sub(srcfl, 1, extb - 1)
|
||||
extension = sub(srcfl, extb, exte)
|
||||
end
|
||||
destfl = basename..suffix..extension
|
||||
if #fspec == 1 and option.OUTPUT_FILE then
|
||||
destfl = option.OUTPUT_FILE
|
||||
end
|
||||
if srcfl == destfl then
|
||||
die("output filename identical to input filename")
|
||||
end
|
||||
|
||||
-- Perform requested operations.
|
||||
if option.DUMP_LEXER then
|
||||
dump_tokens(srcfl)
|
||||
elseif option.DUMP_PARSER then
|
||||
dump_parser(srcfl)
|
||||
elseif option.READ_ONLY then
|
||||
read_only(srcfl)
|
||||
else
|
||||
process_file(srcfl, destfl)
|
||||
end
|
||||
end--for
|
||||
end
|
||||
|
||||
--- The main function.
|
||||
local function main()
|
||||
local fspec = {}
|
||||
local argn, i = #arg, 1
|
||||
if argn == 0 then
|
||||
option.HELP = true
|
||||
end
|
||||
|
||||
-- Handle arguments.
|
||||
while i <= argn do
|
||||
local o, p = arg[i], arg[i + 1]
|
||||
local dash = match(o, "^%-%-?")
|
||||
if dash == "-" then -- single-dash options
|
||||
if o == "-h" then
|
||||
option.HELP = true; break
|
||||
elseif o == "-v" then
|
||||
option.VERSION = true; break
|
||||
elseif o == "-s" then
|
||||
if not p then die("-s option needs suffix specification") end
|
||||
suffix = p
|
||||
i = i + 1
|
||||
elseif o == "-o" then
|
||||
if not p then die("-o option needs a file name") end
|
||||
option.OUTPUT_FILE = p
|
||||
i = i + 1
|
||||
elseif o == "-" then
|
||||
break -- ignore rest of args
|
||||
else
|
||||
die("unrecognized option "..o)
|
||||
end
|
||||
elseif dash == "--" then -- double-dash options
|
||||
if o == "--help" then
|
||||
option.HELP = true; break
|
||||
elseif o == "--version" then
|
||||
option.VERSION = true; break
|
||||
elseif o == "--keep" then
|
||||
if not p then die("--keep option needs a string to match for") end
|
||||
option.KEEP = p
|
||||
i = i + 1
|
||||
elseif o == "--plugin" then
|
||||
if not p then die("--plugin option needs a module name") end
|
||||
if option.PLUGIN then die("only one plugin can be specified") end
|
||||
option.PLUGIN = p
|
||||
plugin = require(PLUGIN_SUFFIX..p)
|
||||
i = i + 1
|
||||
elseif o == "--quiet" then
|
||||
option.QUIET = true
|
||||
elseif o == "--read-only" then
|
||||
option.READ_ONLY = true
|
||||
elseif o == "--basic" then
|
||||
set_options(BASIC_CONFIG)
|
||||
elseif o == "--maximum" then
|
||||
set_options(MAXIMUM_CONFIG)
|
||||
elseif o == "--none" then
|
||||
set_options(NONE_CONFIG)
|
||||
elseif o == "--dump-lexer" then
|
||||
option.DUMP_LEXER = true
|
||||
elseif o == "--dump-parser" then
|
||||
option.DUMP_PARSER = true
|
||||
elseif o == "--details" then
|
||||
option.DETAILS = true
|
||||
elseif OPTION[o] then -- lookup optimization options
|
||||
set_options(o)
|
||||
else
|
||||
die("unrecognized option "..o)
|
||||
end
|
||||
else
|
||||
fspec[#fspec + 1] = o -- potential filename
|
||||
end
|
||||
i = i + 1
|
||||
end--while
|
||||
if option.HELP then
|
||||
print(MSG_TITLE..MSG_USAGE); return true
|
||||
elseif option.VERSION then
|
||||
print(MSG_TITLE); return true
|
||||
end
|
||||
if option["opt-binequiv"] and not BIN_EQUIV_AVAIL then
|
||||
die("--opt-binequiv is available only for PUC Lua 5.1!")
|
||||
end
|
||||
if #fspec > 0 then
|
||||
if #fspec > 1 and option.OUTPUT_FILE then
|
||||
die("with -o, only one source file can be specified")
|
||||
end
|
||||
do_files(fspec)
|
||||
return true
|
||||
else
|
||||
die("nothing to do!")
|
||||
end
|
||||
end
|
||||
|
||||
-- entry point -> main() -> do_files()
|
||||
if not main() then
|
||||
die("Please run with option -h or --help for usage information")
|
||||
end
|
||||
@ -0,0 +1,300 @@
|
||||
= Features and Usage
|
||||
Kein-Hong Man
|
||||
2011-09-13
|
||||
|
||||
|
||||
== Features
|
||||
|
||||
LuaSrcDiet features include the following:
|
||||
|
||||
* Predefined default, _--basic_ (token-only) and _--maximum_ settings.
|
||||
* Avoid deleting a block comment with a certain message with _--keep_; this is for copyright or license texts.
|
||||
* Special handling for `#!` (shbang) lines and in functions, `self` implicit parameters.
|
||||
* Dumping of raw information using _--dump-lexer_ and _--dump-parser_.
|
||||
See the `samples` directory.
|
||||
* A HTML plugin: outputs files that highlights globals and locals, useful for eliminating globals. See the `samples` directory.
|
||||
* An SLOC plugin: counts significant lines of Lua code, like SLOCCount.
|
||||
* Source and binary equivalence testing with _--opt-srcequiv_ and _--opt-binequiv_.
|
||||
|
||||
List of optimizations:
|
||||
|
||||
* Line endings are always normalized to LF, except those embedded in comments or strings.
|
||||
* _--opt-comments_: Removal of comments and comment blocks.
|
||||
* _--opt-whitespace_: Removal of whitespace, excluding end-of-line characters.
|
||||
* _--opt-emptylines_: Removal of empty lines.
|
||||
* _--opt-eols_: Removal of unnecessary end-of-line characters.
|
||||
* _--opt-strings_: Rewrite strings and long strings. See the `samples` directory.
|
||||
* _--opt-numbers_: Rewrite numbers. See the `samples` directory.
|
||||
* _--opt-locals_: Rename local variable names. Does not rename field or method names.
|
||||
* _--opt-entropy_: Tries to improve symbol entropy when renaming locals by calculating actual letter frequencies.
|
||||
* _--opt-experimental_: Apply experimental optimizations.
|
||||
|
||||
LuaSrcDiet tries to allow each option to be enabled or disabled separately, but they are not completely orthogonal.
|
||||
|
||||
If comment removal is disabled, LuaSrcDiet only removes trailing whitespace.
|
||||
Trailing whitespace is not removed in long strings, a warning is generated instead.
|
||||
If empty line removal is disabled, LuaSrcDiet keeps all significant code on the same lines.
|
||||
Thus, a user is able to debug using the original sources as a reference since the line numbering is unchanged.
|
||||
|
||||
String optimization deals mainly with optimizing escape sequences, but delimiters can be switched between single quotes and double quotes if the source size of the string can be reduced.
|
||||
For long strings and long comments, LuaSrcDiet also tries to reduce the `=` separators in the
|
||||
delimiters if possible.
|
||||
For number optimization, LuaSrcDiet saves space by trying to generate the shortest possible sequence, and in the process it does not produce “proper” scientific notation (e.g. 1.23e5) but does away with the decimal point (e.g. 123e3) instead.
|
||||
|
||||
The local variable name optimizer uses a full parser of Lua 5.1 source code, thus it can rename all local variables, including upvalues and function parameters.
|
||||
It should handle the implicit `self` parameter gracefully.
|
||||
In addition, local variable names are either renamed into the shortest possible names following English frequent letter usage or are arranged by calculating entropy with the _--opt-entropy_ option.
|
||||
Variable names are reused whenever possible, reducing the number of unique variable names.
|
||||
For example, for `LuaSrcDiet.lua` (version 0.11.0), 683 local identifiers representing 88 unique names were optimized into 32 unique names, all which are one character in length, saving over 2600 bytes.
|
||||
|
||||
If you need some kind of reassurance that your app will still work at reduced size, see the section on verification below.
|
||||
|
||||
|
||||
== Usage
|
||||
|
||||
LuaSrcDiet needs a Lua 5.1.x (preferably Lua 5.1.4) binary to run.
|
||||
On Unix machines, one can use the following command line:
|
||||
|
||||
[source, sh]
|
||||
LuaSrcDiet myscript.lua -o myscript_.lua
|
||||
|
||||
On Windows machines, the above command line can be used on Cygwin, or you can run Lua with the LuaSrcDiet script like this:
|
||||
|
||||
[source, sh]
|
||||
lua LuaSrcDiet.lua myscript.lua -o myscript_.lua
|
||||
|
||||
When run without arguments, LuaSrcDiet prints a list of options.
|
||||
Also, you can check the `Makefile` for some examples of command lines to use.
|
||||
For example, for maximum code size reduction and maximum verbosity, use:
|
||||
|
||||
[source, sh]
|
||||
LuaSrcDiet --maximum --details myscript.lua -o myscript_.lua
|
||||
|
||||
|
||||
=== Output Example
|
||||
|
||||
A sample output of LuaSrcDiet 0.11.0 for processing `llex.lua` at _--maximum_ settings is as follows:
|
||||
|
||||
----
|
||||
Statistics for: LuaSrcDiet.lua -> sample/LuaSrcDiet.lua
|
||||
|
||||
*** local variable optimization summary ***
|
||||
----------------------------------------------------------
|
||||
Variable Unique Decl. Token Size Average
|
||||
Types Names Count Count Bytes Bytes
|
||||
----------------------------------------------------------
|
||||
Global 10 0 19 95 5.00
|
||||
----------------------------------------------------------
|
||||
Local (in) 88 153 683 3340 4.89
|
||||
TOTAL (in) 98 153 702 3435 4.89
|
||||
----------------------------------------------------------
|
||||
Local (out) 32 153 683 683 1.00
|
||||
TOTAL (out) 42 153 702 778 1.11
|
||||
----------------------------------------------------------
|
||||
|
||||
*** lexer-based optimizations summary ***
|
||||
--------------------------------------------------------------------
|
||||
Lexical Input Input Input Output Output Output
|
||||
Elements Count Bytes Average Count Bytes Average
|
||||
--------------------------------------------------------------------
|
||||
TK_KEYWORD 374 1531 4.09 374 1531 4.09
|
||||
TK_NAME 795 3963 4.98 795 1306 1.64
|
||||
TK_NUMBER 54 59 1.09 54 59 1.09
|
||||
TK_STRING 152 1725 11.35 152 1717 11.30
|
||||
TK_LSTRING 7 1976 282.29 7 1976 282.29
|
||||
TK_OP 997 1092 1.10 997 1092 1.10
|
||||
TK_EOS 1 0 0.00 1 0 0.00
|
||||
--------------------------------------------------------------------
|
||||
TK_COMMENT 140 6884 49.17 1 18 18.00
|
||||
TK_LCOMMENT 7 1723 246.14 0 0 0.00
|
||||
TK_EOL 543 543 1.00 197 197 1.00
|
||||
TK_SPACE 1270 2465 1.94 263 263 1.00
|
||||
--------------------------------------------------------------------
|
||||
Total Elements 4340 21961 5.06 2841 8159 2.87
|
||||
--------------------------------------------------------------------
|
||||
Total Tokens 2380 10346 4.35 2380 7681 3.23
|
||||
--------------------------------------------------------------------
|
||||
----
|
||||
|
||||
Overall, the file size is reduced by more than 9 kiB.
|
||||
Tokens in the above report can be classified into “real” or actual tokens, and “fake” or whitespace tokens.
|
||||
The number of “real” tokens remained the same.
|
||||
Short comments and long comments were completely eliminated.
|
||||
The number of line endings was reduced by 59, while all but 152 whitespace characters were optimized away.
|
||||
So, token separators (whitespace, including line endings) now takes up just 10 % of the total file size.
|
||||
No optimization of number tokens was possible, while 2 bytes were saved for string tokens.
|
||||
|
||||
For local variable name optimization, the report shows that 38 unique local variable names were reduced to 20 unique names.
|
||||
The number of identifier tokens should stay the same (there is currently no optimization option to optimize away non-essential or unused “real” tokens).
|
||||
Since there can be at most 53 single-character identifiers, all local variables are now one character in length.
|
||||
Over 600 bytes was saved.
|
||||
_--details_ will give a longer report and much more information.
|
||||
|
||||
A sample output of LuaSrcDiet 0.12.0 for processing the one-file `LuaSrcDiet.lua` program itself at _--maximum_ and _--opt-experimental_ settings is as follows:
|
||||
|
||||
----
|
||||
*** local variable optimization summary ***
|
||||
----------------------------------------------------------
|
||||
Variable Unique Decl. Token Size Average
|
||||
Types Names Count Count Bytes Bytes
|
||||
----------------------------------------------------------
|
||||
Global 27 0 51 280 5.49
|
||||
----------------------------------------------------------
|
||||
Local (in) 482 1063 4889 21466 4.39
|
||||
TOTAL (in) 509 1063 4940 21746 4.40
|
||||
----------------------------------------------------------
|
||||
Local (out) 55 1063 4889 4897 1.00
|
||||
TOTAL (out) 82 1063 4940 5177 1.05
|
||||
----------------------------------------------------------
|
||||
|
||||
*** BINEQUIV: binary chunks are sort of equivalent
|
||||
|
||||
Statistics for: LuaSrcDiet.lua -> app_experimental.lua
|
||||
|
||||
*** lexer-based optimizations summary ***
|
||||
--------------------------------------------------------------------
|
||||
Lexical Input Input Input Output Output Output
|
||||
Elements Count Bytes Average Count Bytes Average
|
||||
--------------------------------------------------------------------
|
||||
TK_KEYWORD 3083 12247 3.97 3083 12247 3.97
|
||||
TK_NAME 5401 24121 4.47 5401 7552 1.40
|
||||
TK_NUMBER 467 494 1.06 467 494 1.06
|
||||
TK_STRING 787 7983 10.14 787 7974 10.13
|
||||
TK_LSTRING 14 3453 246.64 14 3453 246.64
|
||||
TK_OP 6381 6861 1.08 6171 6651 1.08
|
||||
TK_EOS 1 0 0.00 1 0 0.00
|
||||
--------------------------------------------------------------------
|
||||
TK_COMMENT 1611 72339 44.90 1 18 18.00
|
||||
TK_LCOMMENT 18 4404 244.67 0 0 0.00
|
||||
TK_EOL 4419 4419 1.00 1778 1778 1.00
|
||||
TK_SPACE 10439 24475 2.34 2081 2081 1.00
|
||||
--------------------------------------------------------------------
|
||||
Total Elements 32621 160796 4.93 19784 42248 2.14
|
||||
--------------------------------------------------------------------
|
||||
Total Tokens 16134 55159 3.42 15924 38371 2.41
|
||||
--------------------------------------------------------------------
|
||||
* WARNING: before and after lexer streams are NOT equivalent!
|
||||
----
|
||||
|
||||
The command line was:
|
||||
|
||||
[source, sh]
|
||||
lua LuaSrcDiet.lua LuaSrcDiet.lua -o app_experimental.lua --maximum --opt-experimental --noopt-srcequiv
|
||||
|
||||
The important thing to note is that while the binary chunks are equivalent, the source lexer streams are not equivalent.
|
||||
Hence, the _--noopt-srcequiv_ makes LuaSrcDiet report a warning for failing the source equivalence test.
|
||||
|
||||
`LuaSrcDiet.lua` was reduced from 157 kiB to about 41.3 kiB.
|
||||
The _--opt-experimental_ option saves an extra 205 bytes over standard _--maximum_.
|
||||
Note the reduction in `TK_OP` count due to a reduction in semicolons and parentheses.
|
||||
`TK_SPACE` has actually increased a bit due to semicolons that are changed into single spaces; some of these spaces could not be removed.
|
||||
|
||||
For more performance numbers, see the <<performance-stats#, Performance Statistics>> page.
|
||||
|
||||
|
||||
== Verification
|
||||
|
||||
Code size reduction can be quite a hairy thing (even I peer at the results in suspicion), so some kind of verification is desirable for users who expect processed files to _not_ blow up.
|
||||
Since LuaSrcDiet has been talked about as a tool to reduce code size in projects such as WoW add-ons, `eLua` and `nspire`, adding a verification step will reduce risk for all users of LuaSrcDiet.
|
||||
|
||||
LuaSrcDiet performs two kinds of equivalence testing as of version 0.12.0.
|
||||
The two tests can be very, very loosely termed as _source equivalence testing_ and _binary equivalence testing_.
|
||||
They are controlled by the _--opt-srcequiv_ and _--opt-binequiv_ options and are enabled by default.
|
||||
|
||||
Testing behaviour can be summarized as follows:
|
||||
|
||||
* Both tests are always executed.
|
||||
The options control the resulting actions taken.
|
||||
* Both options are normally enabled.
|
||||
This will make any failing test to throw an error.
|
||||
* When an option is disabled, LuaSrcDiet will at most print a warning.
|
||||
* For passing results, see the following subsections that describe what the tests actually does.
|
||||
|
||||
You only need to disable a testing option for experimental optimizations (see the following section for more information on this).
|
||||
For anything up to and including _--maximum_, both tests should pass.
|
||||
If any test fail under these conditions, then something has gone wrong with LuaSrcDiet, and I would be interested to know what has blown up.
|
||||
|
||||
|
||||
=== _--opt-srcequiv_ Source Equivalence
|
||||
|
||||
The source equivalence test uses LuaSrcDiet’s lexer to read and compare the _before_ and _after_ lexer token streams.
|
||||
Numbers and strings are dumped as binary chunks using `loadstring()` and `string.dump()` and the results compared.
|
||||
|
||||
If your file passes this test, it means that a Lua 5.1.x binary should see the exact same token streams for both _before_ and _after_ files.
|
||||
That is, the parser in Lua will see the same lexer sequence coming from the source for both files and thus they _should_ be equivalent.
|
||||
Touch wood.
|
||||
Heh.
|
||||
|
||||
However, if you are _cross-compiling_, it may be possible for this test to fail.
|
||||
Experienced Lua developers can modify `equiv.lua` to handle such cases.
|
||||
|
||||
|
||||
=== _--opt-binequiv_ Binary Equivalence
|
||||
|
||||
The binary equivalence test uses `loadstring()` and `string.dump()` to generate binary chunks of the entire _before_ and _after_ files.
|
||||
Also, any shbang (`#!`) lines are removed prior to generation of the binary chunks.
|
||||
|
||||
The binary chunks are then run through a fake `undump` routine to verify the integrity of the binary chunks and to compare all parts that ought to be identical.
|
||||
|
||||
On a per-function prototype basis (where _ignored_ means that any difference between the two binary chunks is ignored):
|
||||
|
||||
* All debug information is ignored.
|
||||
* The source name is ignored.
|
||||
* Any line number data is ignored.
|
||||
For example, `linedefined` and `lastlinedefined`.
|
||||
|
||||
The rest of the two binary chunks must be identical.
|
||||
So, while the two are not binary-exact, they can be loosely termed as “equivalent” and should run in exactly the same manner.
|
||||
Sort of.
|
||||
You get the idea.
|
||||
|
||||
This test may also cause problems if you are _cross-compiling_.
|
||||
|
||||
|
||||
== Experimental Stuff
|
||||
|
||||
The _--opt-experimental_ option applies experimental optimizations that generally, makes changes to “real” tokens.
|
||||
Such changes may or may not lead to the result failing binary chunk equivalence testing.
|
||||
They would likely fail source lexer stream equivalence testing, so the _--noopt-srcequiv_ option needs to be applied so that LuaSrcDiet just gives a warning instead of an error.
|
||||
|
||||
For sample files, see the `samples` directory.
|
||||
|
||||
Currently implemented experimental optimizations are as follows:
|
||||
|
||||
|
||||
=== Semicolon Operator Removal
|
||||
|
||||
The semicolon (`;`) operator is an optional operator that is used to separate statements.
|
||||
The optimization turns all of these operators into single spaces, which are then run through whitespace removal.
|
||||
At worst, there will be no change to file size.
|
||||
|
||||
* _Fails_ source lexer stream equivalence.
|
||||
* _Passes_ binary chunk equivalence.
|
||||
|
||||
|
||||
=== Function Call Syntax Sugar Optimization
|
||||
|
||||
This optimization turns function calls that takes a single string or long string parameter into its syntax-sugar representation, which leaves out the parentheses.
|
||||
Since strings can abut anything, each instance saves 2 bytes.
|
||||
|
||||
For example, the following:
|
||||
|
||||
[source, lua]
|
||||
fish("cow")fish('cow')fish([[cow]])
|
||||
|
||||
is turned into:
|
||||
|
||||
[source, lua]
|
||||
fish"cow"fish'cow'fish[[cow]]
|
||||
|
||||
* _Fails_ source lexer stream equivalence.
|
||||
* _Passes_ binary chunk equivalence.
|
||||
|
||||
|
||||
=== Other Experimental Optimizations
|
||||
|
||||
There are two more of these optimizations planned, before focus is turned to the Lua 5.2.x series:
|
||||
|
||||
* Simple `local` keyword removal.
|
||||
Planned to work for a few kinds of patterns only.
|
||||
* User directed name replacement, which will need user input to modify names or identifiers used in table keys and function methods or fields.
|
||||
@ -0,0 +1,128 @@
|
||||
= Performance Statistics
|
||||
Kein-Hong Man
|
||||
2011-09-13
|
||||
|
||||
|
||||
== Size Comparisons
|
||||
|
||||
The following is the result of processing `llex.lua` from LuaSrcDiet 0.11.0 using various optimization options:
|
||||
|
||||
|===
|
||||
| LuaSrcDiet Option | Size (bytes)
|
||||
|
||||
| Original | 12,421
|
||||
| Empty lines only | 12,395
|
||||
| Whitespace only | 9,372
|
||||
| Local rename only | 11,794
|
||||
| _--basic_ setting | 3,835
|
||||
| Program default | 3,208
|
||||
| _--maximum_ setting | 3,130
|
||||
|===
|
||||
|
||||
The program’s default settings does not remove all unnecessary EOLs.
|
||||
The _--basic_ setting is more conservative than the default settings, it disables optimization of strings and numbers and renaming of locals.
|
||||
|
||||
For version 0.12.0, the following is the result of processing `LuaSrcDiet.lua` using various optimization options:
|
||||
|
||||
|===
|
||||
| LuaSrcDiet Option | Size (bytes)
|
||||
|
||||
| Original | 160,796
|
||||
| _--basic_ setting | 60,219
|
||||
| Program default | 43,650
|
||||
| _--maximum_ setting | 42,453
|
||||
| max + experimental | 42,248
|
||||
|===
|
||||
|
||||
The above best size can go a lot lower with simple `local` keyword removal and user directed name replacement, which will be the subject of the next release of LuaSrcDiet.
|
||||
|
||||
|
||||
== Compression and luac
|
||||
|
||||
File sizes of LuaSrcDiet 0.11.0 main files in various forms:
|
||||
|
||||
[cols="m,5*d", options="header,footer"]
|
||||
|===
|
||||
| Source File | Original Size (bytes) | `luac` normal (bytes) | `luac` stripped (bytes) | LuaSrcDiet _--basic_ (bytes) | LuaSrcDiet _--maximum_ (bytes)
|
||||
|
||||
| LuaSrcDiet.lua | 21,961 | 20,952 | 11,000 | 11,005 | 8,159
|
||||
| llex.lua | 12,421 | 8,613 | 4,247 | 3,835 | 3,130
|
||||
| lparser.lua | 41,757 | 27,215 | 12,506 | 11,755 | 7,666
|
||||
| optlex.lua | 31,009 | 16,992 | 8,021 | 9,129 | 6,858
|
||||
| optparser.lua | 16,511 | 9,021 | 3,520 | 5,087 | 2,999
|
||||
|
||||
| Total | 123,659 | 82,793 | 39,294 | 40,811 | 28,812
|
||||
|===
|
||||
|
||||
* “LuaSrcDiet --maximum” has the smallest total file size.
|
||||
* The ratio of “Original Size” to “LuaSrcDiet --maximum” is *4.3*.
|
||||
* The ratio of “Original Size” to “luac stripped” is *3.1*.
|
||||
* The ratio of “luac stripped” to “LuaSrcDiet --maximum” is *1.4*.
|
||||
|
||||
Compressibility of LuaSrcDiet 0.11.0 main files in various forms:
|
||||
|
||||
|===
|
||||
| Compression Method | Original Size | `luac` normal | `luac` stripped | LuaSrcDiet _--basic_ | LuaSrcDiet _--maximum_
|
||||
|
||||
| Uncompressed originals | 123,659 | 82,793 | 39,294 | 40,811 | 28,812
|
||||
| gzip -9 | 28,288 | 29,210 | 17,732 | 12,041 | 10,451
|
||||
| bzip2 -9 | 24,407 | 27,232 | 16,856 | 11,480 | 9,815
|
||||
| lzma (7-zip max) | 25,530 | 23,908 | 15,741 | 11,241 | 9,685
|
||||
|===
|
||||
|
||||
* “LuaSrcDiet --maximum” has the smallest total file size (but a binary chunk loads faster and works with a smaller Lua executable).
|
||||
* The ratio of “Original size” to “Original size + bzip2” is *5.1*.
|
||||
* The ratio of “Original size” to “LuaSrcDiet --maximum + bzip2” is *12.6*.
|
||||
* The ratio of “LuaSrcDiet --maximum” to “LuaSrcDiet --maximum + bzip2” is *2.9*.
|
||||
* The ratio of “Original size” to “luac stripped + bzip2” is *7.3*.
|
||||
* The ratio of “luac stripped” to “luac stripped + bzip2” is *2.3*.
|
||||
* The ratio of “luac stripped + bzip2” to “LuaSrcDiet --maximum + bzip2” is *1.7*.
|
||||
|
||||
So, squeezed source code are smaller than stripped binary chunks and compresses better than stripped binary chunks, at a ratio of 2.9 for squeezed source code versus 2.3 for stripped binary chunks.
|
||||
Compressed binary chunks is still a very efficient way of storing Lua scripts, because using only binary chunks allow for the parts of Lua needed to compile from sources to be omitted (`llex.o`, `lparser.o`, `lcode.o`, `ldump.o`), saving over 24KB in the process.
|
||||
|
||||
Note that LuaSrcDiet _does not_ answer the question of whether embedding source code is better or embedding binary chunks is better.
|
||||
It is simply a utility for producing smaller source code files and an exercise in processing Lua source code using a Lua-based lexer and parser skeleton.
|
||||
|
||||
|
||||
== Compile Speed
|
||||
|
||||
The following is a primitive attempt to analyze in-memory Lua script loading performance (using the `loadstring` function in Lua).
|
||||
|
||||
The LuaSrcDiet 0.11.0 files (original, squeezed with _--maximum_ and stripped binary chunks versions) are loaded into memory first before a loop runs to repeatedly load the script files for 10 seconds.
|
||||
A null loop is also performed (processing empty strings) and the time taken per null iteration is subtracted as a form of null adjustment.
|
||||
Then, various performance parameters are calculated.
|
||||
Note that `LuaSrcDiet.lua` was slightly modified (`#!` line removed) to let the `loadstring` function run.
|
||||
The results below were obtained with a Lua 5.1.3 executable compiled using `make generic` on Cygwin/Windows XP SP2 on a Sempron 3000+ (1.8GHz).
|
||||
The LuaSrcDiet 0.11.0 source files have 11,180 “real” tokens in total.
|
||||
|
||||
[cols="<h,4*d", options="header"]
|
||||
|===
|
||||
| | Null loop | Stripped binary chunk | Original Sources | Squeezed Sources
|
||||
|
||||
| Total Size (bytes) | 0 | 39,294 | 123,640 | 28,793
|
||||
| Iterations | 312,155 | 9,680 | 1306 | 1,592
|
||||
| Duration (sec) | 10 | 10 | 10 | 10
|
||||
| Time/iteration (msec) | 0.032 | 1.033 | 7.657 | 6.281
|
||||
| _Time/iteration, null adjusted (msec)_ | – | 1.001 | 7.625 | 6.249
|
||||
| _Load rate (MiB/sec)_ | – | 37.44 | 15.46 | 4.39
|
||||
| Load time per byte (ns) | – | 25.5 | 61.7 | 217.0
|
||||
| Load time per token (ns) | – | – | 682 | 559
|
||||
| Source time vs binary chunk time ratio | – | 1.00 | 7.62 | 6.24
|
||||
| Binary chunk rate vs. source rate ratio | – | 1.00 | 2.42 | 8.53
|
||||
|===
|
||||
|
||||
The above shows that stripped binary chunks is still, in many ways, the highest-performance form of fixed Lua scripts.
|
||||
On a very average machine, scripts load at over 37 MiB/sec (in memory).
|
||||
This is very comparable to the burst speeds of common desktop hard disks of 2008.
|
||||
If instant response is paramount, stripped binary chunks has little competition.
|
||||
|
||||
By contrast, source code that is squeezed to the maximum using LuaSrcDiet can only muster an in-memory load rate of 4.4 MiB/sec.
|
||||
The original sources load at about 15.5 MiB/sec, but most of the speed is from the lexer scanning over comments and whitespace.
|
||||
A quick calculation indicates that the speed of the lexer over comments and whitespace can be as much as 65 MiB/sec, but note that the speed is all for naught.
|
||||
What really matters are the real tokens, and the squeezed source code manages to load faster than the original sources by 18 %.
|
||||
|
||||
So, the loading of stripped binary chunks is faster than squeezed source code by a bit over 6×.
|
||||
The 4.4 MiB/sec speed for squeezed source code is still quite respectable.
|
||||
When an application considers the time taken to load data from the disk and perhaps the time taken to decompress, loading source code may be perfectly fine in terms of performance.
|
||||
For programs that already embed source code, using LuaSrcDiet to squeeze the source code probably speeds loading up by a tiny bit in addition to making programs smaller.
|
||||
@ -0,0 +1,386 @@
|
||||
= Technical Notes
|
||||
Kein-Hong Man
|
||||
2011-09-13
|
||||
|
||||
|
||||
== Lexer Notes
|
||||
|
||||
The lexer (`llex.lua`) is a version of the native 5.1.x lexer from Yueliang 0.4.0, with significant modifications.
|
||||
It does have several limitations:
|
||||
|
||||
* The decimal point must be `.` (period).
|
||||
There is no localized decimal point replacement magic.
|
||||
* There is no support for nested `[[`...`]]` long strings (no `LUA_COMPAT_LSTR`).
|
||||
* The lexer may not properly lex source code with characters beyond the normal ASCII character set.
|
||||
Identifiers with accented characters (or any character beyond a byte value of 127) cannot be recognized.
|
||||
|
||||
Instead of returning one token on each call, `llex.lua` processes an entire string (all data from an entire file) and returns.
|
||||
Two lists (tokens and semantic information items) are set up in the module for use by the caller.
|
||||
|
||||
For maximum flexibility during processing, the lexer returns non-grammar lexical elements as tokens too.
|
||||
Non-grammar elements, such as comments, whitespace, line endings, are classified along with “normal” tokens.
|
||||
The lexer classifies 7 kinds of grammar tokens and 4 kinds of non-grammar tokens, as follows:
|
||||
|
||||
[cols="m,d"]
|
||||
|===
|
||||
| Grammar Token | Description
|
||||
|
||||
| TK_KEYWORD | keywords
|
||||
| TK_NAME | identifiers
|
||||
| TK_NUMBER | numbers (unconverted, kept in original form)
|
||||
| TK_STRING | strings (no translation is done, includes delimiters)
|
||||
| TK_LSTRING | long strings (no translation is done, includes delimiters)
|
||||
| TK_OP | operators and punctuation (most single-char, some double)
|
||||
| TK_EOS | end-of-stream (there is only one for each file/stream)
|
||||
|===
|
||||
|
||||
[cols="m,d"]
|
||||
|===
|
||||
| Whitespace Token | Description
|
||||
|
||||
| TK_SPACE | whitespace (generally, spaces, \t, \v and \f)
|
||||
| TK_COMMENT | comments (includes delimiters, also includes special first line shbang, which is handled specially in the optimizer)
|
||||
| TK_LCOMMENT | block comments (includes delimiters)
|
||||
| TK_EOL | end-of-lines (excludes those embedded in strings)
|
||||
|===
|
||||
|
||||
A list of tokens can be generated by using the _--dump-lexer_ option, like this:
|
||||
|
||||
[source, sh]
|
||||
lua LuaSrcDiet.lua --dump-lexer llex.lua > dump_llex.dat
|
||||
|
||||
|
||||
== Lexer Optimizations
|
||||
|
||||
We aim to keep lexer-based optimizations free of parser considerations, i.e. we allow for generalized optimization of token sequences.
|
||||
The table below considers the requirements for all combinations of significant tokens (except `TK_EOS`).
|
||||
Other tokens are whitespace-like.
|
||||
Comments can be considered to be a special kind of whitespace, e.g. a short comment needs to have a following EOL token, if we do not want to optimize away short comments.
|
||||
|
||||
[cols="h,6*m", options="header"]
|
||||
|===
|
||||
| _1st \ 2nd Token_ | Keyword | Name | Number | String | LString | Oper
|
||||
|
||||
| Keyword | [S] | [S] | [S] | – | – | –
|
||||
| Name | [S] | [S] | [S] | – | – | –
|
||||
| Number | [S] | [S] | [S] | – | – | [1]
|
||||
| String | – | – | – | – | – | –
|
||||
| LString | – | – | – | – | – | –
|
||||
| Oper | – | – | [1] | – | – | [2]
|
||||
|===
|
||||
|
||||
A dash (`-`) in the above means that the first token can abut the second token.
|
||||
|
||||
`*[S]*`:: Need at least one whitespace, set as either a space or kept as an EOL.
|
||||
|
||||
`*[1]*`::
|
||||
Need a space if operator is a `.`, all others okay.
|
||||
A `+` or `-` is used as part of a floating-point spec, but there does not appear to be any way of creating a float by joining with number with a `+` or `-` plus another number.
|
||||
Since an `e` has to be somewhere in the first token, this can’t be done.
|
||||
|
||||
`*[2]*`::
|
||||
Normally there cannot be consecutive operators, but we plan to allow for generalized optimization of token sequences, i.e. even sequences that are grammatically illegal; so disallow adjacent operators if:
|
||||
* the first is in `[=<>]` and the second is `=`
|
||||
* disallow dot sequences to be adjacent, but `...` first okay
|
||||
* disallow `[` followed by `=` or `[` (not optimal)
|
||||
|
||||
Also, a minus `-` cannot preceed a Comment or LComment, because comments start with a `--` prefix.
|
||||
Apart from that, all Comment or LComment tokens can be set abut with a real token.
|
||||
|
||||
|
||||
== Local Variable Renaming
|
||||
|
||||
The following discusses the problem of local variable optimization, specifically _local variable renaming_ in order to reduce source code size.
|
||||
|
||||
|
||||
=== TK_NAME Token Considerations
|
||||
|
||||
A `TK_NAME` token means a number of things, and some of these cannot be renamed without analyzing the source code.
|
||||
We are interested in the use of `TK_NAME` in the following:
|
||||
|
||||
[loweralpha]
|
||||
. global variable access,
|
||||
. local variable declaration, including `local` statements, `local` functions, function parameters, implicit `self` locals,
|
||||
. local variable access, including upvalue access.
|
||||
|
||||
`TK_NAME` is also used in parts of the grammar as constant strings – these tokens cannot be optimized without user assistance.
|
||||
These include usage as:
|
||||
|
||||
[loweralpha, start=4]
|
||||
. keys in `key=value` pairs in table construction,
|
||||
. field or method names in `a:b` or `a.b` syntax forms.
|
||||
|
||||
For the local variable name optimization scheme used, we do not consider (d) and (e), and while global variables cannot be renamed without some kind of user assistance, they need to be considered or tracked as part of Lua’s variable access scheme.
|
||||
|
||||
|
||||
=== Lifetime of a Local Variable
|
||||
|
||||
Consider the following example:
|
||||
|
||||
[source, lua]
|
||||
local string, table = string, table
|
||||
|
||||
In the example, the two locals are assigned the values of the globals with the same names.
|
||||
When Lua encounters the declaration portion:
|
||||
|
||||
[source, lua]
|
||||
local string, table
|
||||
|
||||
the parser cannot immediately make the two local variable available to following code.
|
||||
In the parser and code generator, locals are inactive when entries are created.
|
||||
They are activated only when the function `adjustlocalvars()` is called to activate the appropriate local variables.
|
||||
|
||||
NOTE: The terminology used here may not be identical to the ones used in the Dragon Book – they merely follow the LuaSrcDiet code as it was written before I have read the Dragon Book.
|
||||
|
||||
In the example, the two local variables are activated only after the whole statement has been parsed, that is, after the last `table` token.
|
||||
Hence, the statement works as expected.
|
||||
Also, once the two local variables goes out of scope, `removevars()` is called to deactivate them, allowing other variables of the same name to become visible again.
|
||||
|
||||
Another example worth mentioning is:
|
||||
|
||||
[source, lua]
|
||||
local a, a, a, = 1, 2, 3
|
||||
|
||||
The above will assign 3 to `a`.
|
||||
|
||||
Thus, when optimizing local variable names, (1) we need to consider accesses of global variable names affecting the namespace, (2) for the local variable names themselves, we need to consider when they are declared, activated and removed, and (3) within the “live” time of locals, we need to know when they are accessed (since locals that are never accessed don’t really matter.)
|
||||
|
||||
|
||||
=== Local Variable Tracking
|
||||
|
||||
Every local variable declaration is considered an object to be renamed.
|
||||
|
||||
From the parser, we have the original name of the local variable, the token positions for declaration, activation and removal, and the token position for all the `TK_NAME` tokens which references this local.
|
||||
All instances of the implicit `self` local variable are also flagged as such.
|
||||
|
||||
In addition to local variable information, all global variable accesses are tabled, one object entry for one name, and each object has a corresponding list of token positions for the `TK_NAME` tokens, which is where the global variables were accessed.
|
||||
|
||||
The key criteria is: *Our act of renaming cannot change the visibility of any of these locals and globals at the time they are accessed*.
|
||||
However, _their scope of visibility may be changed during which they are not accessed_, so someone who tries to insert a variable reference somewhere into a program that has its locals renamed may find that it now refers to a different variable.
|
||||
|
||||
Of course, if every variable has a unique name, then there is no need for a name allocation algorithm, as there will be no conflict.
|
||||
But, in order to maximize utilization of short identifier names to reduce the final code size, we want to reuse the names as much as possible.
|
||||
In addition, fewer names will likely reduce symbol entropy and may slightly improve compressibility of the source code.
|
||||
LuaSrcDiet avoids the use of non-ASCII letters, so there are only 53 single-character variable names.
|
||||
|
||||
|
||||
=== Name Allocation Theory
|
||||
|
||||
To understand the renaming algorithm, first we need to establish how different local and global variables can operate happily without interfering with each other.
|
||||
|
||||
Consider three objects, local object A, local object B and global object G.
|
||||
A and B involve declaration, activation and removal, and within the period it is active, there may be zero or more accesses of the local.
|
||||
For G, there are only global variable accesses to look into.
|
||||
|
||||
Assume that we have assigned a new name to A and we wish to consider its effects on other locals and globals, for which we choose B and G as examples.
|
||||
We assume local B has not been assigned a new name as we expect our algorithm to take care of collisions.
|
||||
|
||||
A’s lifetime is something like this:
|
||||
|
||||
----
|
||||
Decl Act Rem
|
||||
+ +-------------------------------+
|
||||
-------------------------------------------------
|
||||
----
|
||||
|
||||
where “Decl” is the time of declaration, “Act” is the time of activation, and “Rem” is the time of removal.
|
||||
Between “Act” and “Rem”, the local is alive or “live” and Lua can see it if its corresponding `TK_NAME` identifier comes up.
|
||||
|
||||
----
|
||||
Decl Act Rem
|
||||
+ +-------------------------------+
|
||||
-------------------------------------------------
|
||||
* * * *
|
||||
(1) (2) (3) (4)
|
||||
----
|
||||
|
||||
Recall that the key criteria is to not change the visibility of globals and locals during when they are accessed.
|
||||
Consider local and global accesses at (1), (2), (3) and (4).
|
||||
|
||||
A global G of the same name as A will only collide at (3), where Lua will see A and not G.
|
||||
Since G must be accessed at (3) according to what the parser says, and we cannot modify the positions of “Decl”, “Act” and “Rem”, it follows that A cannot have the same name as G.
|
||||
|
||||
----
|
||||
Decl Act Rem
|
||||
+ +-----------------------+
|
||||
---------------------------------
|
||||
(1)+ +---+ (2)+ +---+ (3)+ +---+ (4)+ +---+
|
||||
--------- --------- --------- ---------
|
||||
----
|
||||
|
||||
For the case of A and B having the same names and colliding, consider the cases for which B is at (1), (2), (3) or (4) in the above.
|
||||
|
||||
(1) and (4) means that A and B are completely isolated from each other, hence in the two cases, A and B can safely use the same variable names.
|
||||
To be specific, since we have assigned A, B is considered completely isolated from A if B’s activation-to-removal period is isolated from the time of A’s first access to last access, meaning B’s active time will never affect any of A’s accesses.
|
||||
|
||||
For (2) and (3), we have two cases where we need to consider which one has been activated first.
|
||||
For (2), B is active before A, so A cannot impose on B.
|
||||
But A’s accesses are valid while B is active, since A can override B.
|
||||
For no collision in the case of (2), we simply need to ensure that the last access of B occurs before A is activated.
|
||||
|
||||
For (3), B is activated before A, hence B can override A’s accesses.
|
||||
For no collision, all of A’s accesses cannot happen while B is active.
|
||||
Thus position (3) follows the “A is never accessed when B is active” rule in a general way.
|
||||
Local variables of a child function are in the position of (3).
|
||||
To illustrate, the local B can use the same name as local A and live in a child function or block scope if each time A is accessed, Lua sees A and not B.
|
||||
So we have to check all accesses of A and see whether they collide with the active period of B.
|
||||
If A is not accessed during that period, then B can be active with the same name.
|
||||
|
||||
The above appears to resolve all sorts of cases where the active times of A and B overlap.
|
||||
Note that in the above, the allocator does not need to know how locals are separated according to function prototypes.
|
||||
Perhaps the allocator can be simplified if knowledge of function structure is utilized.
|
||||
This scheme was implemented in a hurry in 2008 — it could probably be simpler if Lua grammar is considered, but LuaSrcDiet mainly processes various index values in tables.
|
||||
|
||||
|
||||
=== Name Allocation Algorithm
|
||||
|
||||
To begin with, the name generator is mostly separate from the name allocation algorithm.
|
||||
The name generator returns the next shortest name for the algorithm to apply to local variables.
|
||||
To attempt to reduce symbol entropy (which benefit compression algorithms), the name generator follows English frequent letter usage.
|
||||
There is also an option to calculate an actual symbol entropy table from the input data.
|
||||
|
||||
Since there are 53 one-character identifiers and (53 * 63 - 4) two-character identifiers (minus a few keywords), there isn’t a pressing need to optimally maximize name reuse.
|
||||
The single-file version of LuaSrcDiet 0.12.0, at just over 3000 SLOC and 156 kiB in size, currently allocates around 55 unique local variable names.
|
||||
|
||||
In theory, we should need no more than 260 local identifiers by default.
|
||||
Why?
|
||||
Since `LUAI_MAXVARS` is 200 and `LUAI_MAXUPVALUES` is 60, at any block scope, there can be at most `(LUAI_MAXVARS + LUAI_MAXUPVALUES)` locals referenced, or 260.
|
||||
Also, those from outer scopes not referenced in inner scopes can reuse identifiers.
|
||||
The net effect of this is that a local variable name allocation method should not allocate more than 260 identifier names for locals.
|
||||
|
||||
The current algorithm is a simple first-come first-served scheme:
|
||||
|
||||
[loweralpha]
|
||||
. One local object that use the most tokens is named first.
|
||||
. Any other non-conflicting locals with respect to the first object are assigned the same name.
|
||||
. Assigned locals are removed from consideration and the procedure is repeated for objects that have not been assigned new names.
|
||||
. Steps (a) to (c) repeats until no local objects are left.
|
||||
|
||||
In addition, there are a few extra issues to take care of:
|
||||
|
||||
[loweralpha, start=5]
|
||||
. Implicit `self` locals that have been flagged as such are already “assigned to” and so they are left unmodified.
|
||||
. The name generator skips `self` to avoid conflicts.
|
||||
This is not optimal but it is unlikely a script will use so many local variables as to reach `self`.
|
||||
. Keywords are also skipped for the name generator.
|
||||
. Global name conflict resolution.
|
||||
|
||||
For (h), global name conflict resolution is handled just after the new name is generated.
|
||||
The name can still be used for some locals even if it conflicts with other locals.
|
||||
To remove conflicts, global variable accesses for the particular identifier name is checked.
|
||||
Any local variables that are active when a global access is made is marked to be skipped.
|
||||
The rest of the local objects can then use that name.
|
||||
|
||||
The algorithm has additional code for handling locals that use the same name in the same scope.
|
||||
This extends the basic algorithm that was discussed earlier.
|
||||
For example:
|
||||
|
||||
[source, lua]
|
||||
----
|
||||
local foo = 10 -- <1>
|
||||
...
|
||||
local foo = 20 -- <2>
|
||||
...
|
||||
print(e)
|
||||
----
|
||||
|
||||
Since we are considering name visibility, the first `foo` does not really cease to exist when the second `foo` is declared, because if we were to make that assumption, and the first `foo` is removed before (2), then I should be able to use `e` as the name for the first `foo` and after (2), it should not conflict with variables in the outer scope with the same name.
|
||||
To illustrate:
|
||||
|
||||
[source, lua]
|
||||
----
|
||||
local e = 10 -- 'foo' renamed to 'e'
|
||||
...
|
||||
local t = 20 -- error if we assumed 'e' removed here
|
||||
...
|
||||
print(e)
|
||||
----
|
||||
|
||||
Since `e` is a global in the example, we now have an error as the name as been taken over by a local.
|
||||
Thus, the first `foo` local must have its active time extend to the end of the current scope.
|
||||
If there is no conflict between the first and second `foo`, the algorithm may still assign the same names to them.
|
||||
|
||||
The current fix to deal with the above chains local objects in order to find the removal position.
|
||||
It may be possible to handle this in a clean manner – LuaSrcDiet handles it as a fix to the basic algorithm.
|
||||
|
||||
|
||||
== Ideas
|
||||
|
||||
The following is a list of optimization ideas that do not require heavy-duty source code parsing and comprehension.
|
||||
|
||||
|
||||
=== Lexer-Based Optimization Ideas
|
||||
|
||||
* Convert long strings to normal strings, vice versa. +
|
||||
_A little desperate for a few bytes, can be done, but not real keen on implementing it._
|
||||
|
||||
* Special number forms to take advantage of constant number folding. +
|
||||
_For example, 65536 can be represented using 2^16^, and so on.
|
||||
An expression must be evaluated in the same way, otherwise this seems unsafe._
|
||||
|
||||
* Warn if a number has too many digits. +
|
||||
_Should we warn or “test and truncate”?
|
||||
Not really an optimization that will see much use._
|
||||
|
||||
* Warn of opportunity for using a `local` to zap a bunch of globals. +
|
||||
_Current recommendation is to use the HTML plugin to display globals in red.
|
||||
The developer can then visually analyze the source code and make the appropriate fixes.
|
||||
I think this is better than having the program guess the intentions of the developer._
|
||||
|
||||
* Spaces to tabs in comments, long comments, or long strings. +
|
||||
_For long strings, need to know user’s intention.
|
||||
Would rather not implement._
|
||||
|
||||
|
||||
=== Parser-Based Optimization Ideas
|
||||
|
||||
Heavy-duty optimizations will need more data to be generated by the parser.
|
||||
A full AST may eventually be needed.
|
||||
The most attractive idea that can be quickly implemented with a significant code size “win” is to reduce the number of `local` keywords.
|
||||
|
||||
* Remove unused ``local``s that can be removed in the source. +
|
||||
_Need to consider unused ``local``s in multiple assignments._
|
||||
|
||||
* Simplify declaration of ``local``s that can be merged. +
|
||||
_From:_
|
||||
+
|
||||
[source, lua]
|
||||
----
|
||||
-- separate locals
|
||||
local foo
|
||||
local bar
|
||||
-- separate locals with assignments
|
||||
local foo = 123
|
||||
local bar = "pqr"
|
||||
----
|
||||
+
|
||||
_To:_
|
||||
+
|
||||
[source, lua]
|
||||
----
|
||||
-- merged locals
|
||||
local foo,bar
|
||||
-- merged locals with assignments
|
||||
local foo,bar=123,"pqr"
|
||||
----
|
||||
|
||||
* Simplify declarations using `nil`. +
|
||||
_From:_
|
||||
[source, lua]
|
||||
local foo, bar = nil, nil
|
||||
+
|
||||
_To:_
|
||||
[source, lua]
|
||||
local foo,bar
|
||||
|
||||
* Simplify ``return``s using `nil`. +
|
||||
_How desirable is this? From Lua list discussions, it seems to be potentially unsafe unless all return locations are known and checked._
|
||||
|
||||
* Removal of optional semicolons in statements and removal of commas or semicolons in table constructors. +
|
||||
_Yeah, this might save a few bytes._
|
||||
|
||||
* Remove table constructor elements using `nil`. +
|
||||
_Not sure if this is safe to do._
|
||||
|
||||
* Simplify logical or relational operator expressions. +
|
||||
_This is more suitable for an optimizing compiler project._
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user