From 128bb0cc2cf44fba68598a71d99ed3f304adf04a Mon Sep 17 00:00:00 2001 From: Sven Van de Velde Date: Sat, 14 May 2016 06:41:46 +0200 Subject: [PATCH] Documentation --- Embedded/Moose_Embedded.lua | 562 +++++++++++------- .../DCS World - MOOSE - Missile Trainer.pptx | Bin 800876 -> 802009 bytes 2 files changed, 346 insertions(+), 216 deletions(-) diff --git a/Embedded/Moose_Embedded.lua b/Embedded/Moose_Embedded.lua index 55c5cce27..a8d4fa8ba 100644 --- a/Embedded/Moose_Embedded.lua +++ b/Embedded/Moose_Embedded.lua @@ -15263,6 +15263,40 @@ function ESCORT:_ReportTargetsScheduler() end end --- Provides missile training functions. +-- +-- @{#MISSILETRAINER} class +-- ======================== +-- The @{#MISSILETRAINER} class uses the DCS world messaging system to be alerted of any missiles fired, and when a missile would hit your aircraft, +-- the class will destroy the missile within a certain range, to avoid damage to your aircraft. +-- It suports the following functionality: +-- +-- * Track the missiles fired at you and other players, providing bearing and range information of the missiles towards the airplanes. +-- * Provide alerts of missile launches, including detailed information of the units launching, including bearing, range … +-- * Provide alerts when a missile would have killed your aircraft. +-- * Provide alerts when the missile self destructs. +-- * Enable / Disable and Configure the Missile Trainer using the various menu options. +-- +-- MISSILETRAINER construction methods: +-- ==================================== +-- Create a new MISSILETRAINER object with the @{#MISSILETRAINER.New} method: +-- +-- * @{#MISSILETRAINER.New}: Creates a new MISSILETRAINER object taking the maximum distance to your aircraft to evaluate when a missile needs to be destroyed. +-- +-- MISSILETRAINER will collect each unit declared in the mission with a skill level "Client" and "Player", and will monitor the missiles shot at those. +-- +-- MISSILETRAINER initialization methods: +-- ====================================== +-- A MISSILETRAINER object will behave differently based on the usage of initialization methods: +-- +-- * @{#MISSILETRAINER.InitMessagesOnOff}: Sets by default the display of any message to be ON or OFF. +-- * @{#MISSILETRAINER.InitTrackingToAll}: Sets by default the missile tracking report for all players or only for those missiles targetted to you. +-- * @{#MISSILETRAINER.InitTrackingOnOff}: Sets by default the display of missile tracking report to be ON or OFF. +-- * @{#MISSILETRAINER.InitAlertsToAll}: Sets by default the display of alerts to be shown to all players or only to you. +-- * @{#MISSILETRAINER.InitAlertsHitsOnOff}: Sets by default the display of hit alerts ON or OFF. +-- * @{#MISSILETRAINER.InitAlertsLaunchesOnOff}: Sets by default the display of launch alerts ON or OFF. +-- * @{#MISSILETRAINER.InitRangeOnOff}: Sets by default the display of range information of missiles ON of OFF. +-- * @{#MISSILETRAINER.InitBearingOnOff}: Sets by default the display of bearing information of missiles ON of OFF. +-- -- @module MissileTrainer -- @author FlightControl @@ -15273,7 +15307,7 @@ Include.File( "Scheduler" ) -- @type MISSILETRAINER -- @extends Base#BASE MISSILETRAINER = { - ClassName = "MISSILETRAINER", + ClassName = "MISSILETRAINER", } --- Creates the main object which is handling missile tracking. @@ -15282,61 +15316,61 @@ MISSILETRAINER = { -- @param #number Distance The distance in meters when a tracked missile needs to be destroyed when close to a player. -- @return #MISSILETRAINER function MISSILETRAINER:New( Distance ) - local self = BASE:Inherit( self, BASE:New() ) - self:F( Distance ) - - self.Schedulers = {} - self.SchedulerID = 0 - - self.MessageInterval = 2 - self.MessageLastTime = timer.getTime() - - self.Distance = Distance / 1000 + local self = BASE:Inherit( self, BASE:New() ) + self:F( Distance ) - _EVENTDISPATCHER:OnShot( self._EventShot, self ) - - self.DB = DATABASE:New():FilterStart() - self.DBClients = self.DB.Clients - self.DBUnits = self.DB.Units - - for ClientID, Client in pairs( self.DBClients ) do - - local function _Alive( Client ) + self.Schedulers = {} + self.SchedulerID = 0 + + self.MessageInterval = 2 + self.MessageLastTime = timer.getTime() + + self.Distance = Distance / 1000 + + _EVENTDISPATCHER:OnShot( self._EventShot, self ) + + self.DB = DATABASE:New():FilterStart() + self.DBClients = self.DB.Clients + self.DBUnits = self.DB.Units + + for ClientID, Client in pairs( self.DBClients ) do + + local function _Alive( Client ) + + Client:Message( "Hello trainee, welcome to the Missile Trainer.\nUse the F10->F2 menu options in the radio menu to change the Missile Trainer settings.\nGood luck!", 10, "ID", "Trainer" ) + + Client.MainMenu = MENU_CLIENT:New( Client, "Missile Trainer", nil ) + + Client.MenuMessages = MENU_CLIENT:New( Client, "Messages", Client.MainMenu ) + Client.MenuOn = MENU_CLIENT_COMMAND:New( Client, "Messages On", Client.MenuMessages, self._MenuMessages, { MenuSelf = self, MessagesOnOff = true } ) + Client.MenuOff = MENU_CLIENT_COMMAND:New( Client, "Messages Off", Client.MenuMessages, self._MenuMessages, { MenuSelf = self, MessagesOnOff = false } ) + + Client.MenuTracking = MENU_CLIENT:New( Client, "Tracking", Client.MainMenu ) + Client.MenuTrackingToAll = MENU_CLIENT_COMMAND:New( Client, "To All", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingToAll = true } ) + Client.MenuTrackingToTarget = MENU_CLIENT_COMMAND:New( Client, "To Target", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingToAll = false } ) + Client.MenuTrackOn = MENU_CLIENT_COMMAND:New( Client, "Tracking On", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingOnOff = true } ) + Client.MenuTrackOff = MENU_CLIENT_COMMAND:New( Client, "Tracking Off", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingOnOff = false } ) + + Client.MenuAlerts = MENU_CLIENT:New( Client, "Alerts", Client.MainMenu ) + Client.MenuAlertsToAll = MENU_CLIENT_COMMAND:New( Client, "To All", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsToAll = true } ) + Client.MenuAlertsToTarget = MENU_CLIENT_COMMAND:New( Client, "To Target", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsToAll = false } ) + Client.MenuHitsOn = MENU_CLIENT_COMMAND:New( Client, "Hits On", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsHitsOnOff = true } ) + Client.MenuHitsOff = MENU_CLIENT_COMMAND:New( Client, "Hits Off", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsHitsOnOff = false } ) + Client.MenuLaunchesOn = MENU_CLIENT_COMMAND:New( Client, "Launches On", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsLaunchesOnOff = true } ) + Client.MenuLaunchesOff = MENU_CLIENT_COMMAND:New( Client, "Launches Off", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsLaunchesOnOff = false } ) + + Client.MenuDetails = MENU_CLIENT:New( Client, "Details", Client.MainMenu ) + Client.MenuDetailsDistanceOn = MENU_CLIENT_COMMAND:New( Client, "Range On", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsRangeOnOff = true } ) + Client.MenuDetailsDistanceOff = MENU_CLIENT_COMMAND:New( Client, "Range Off", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsRangeOnOff = false } ) + Client.MenuDetailsBearingOn = MENU_CLIENT_COMMAND:New( Client, "Bearing On", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsBearingOnOff = true } ) + Client.MenuDetailsBearingOff = MENU_CLIENT_COMMAND:New( Client, "Bearing Off", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsBearingOnOff = false } ) + + Client.MenuDistance = MENU_CLIENT:New( Client, "Set distance to plane", Client.MainMenu ) + Client.MenuDistance50 = MENU_CLIENT_COMMAND:New( Client, "50 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 50 / 1000 } ) + Client.MenuDistance100 = MENU_CLIENT_COMMAND:New( Client, "100 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 100 / 1000 } ) + Client.MenuDistance150 = MENU_CLIENT_COMMAND:New( Client, "150 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 150 / 1000 } ) + Client.MenuDistance200 = MENU_CLIENT_COMMAND:New( Client, "200 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 200 / 1000 } ) - Client:Message( "Hello trainee, welcome to the Missile Trainer.\nUse the F10->F2 menu options in the radio menu to change the Missile Trainer settings.\nGood luck!", 10, "ID", "Trainer" ) - - Client.MainMenu = MENU_CLIENT:New( Client, "Missile Trainer", nil ) - - Client.MenuMessages = MENU_CLIENT:New( Client, "Messages", Client.MainMenu ) - Client.MenuOn = MENU_CLIENT_COMMAND:New( Client, "Messages On", Client.MenuMessages, self._MenuMessages, { MenuSelf = self, MessagesOnOff = true } ) - Client.MenuOff = MENU_CLIENT_COMMAND:New( Client, "Messages Off", Client.MenuMessages, self._MenuMessages, { MenuSelf = self, MessagesOnOff = false } ) - - Client.MenuTracking = MENU_CLIENT:New( Client, "Tracking", Client.MainMenu ) - Client.MenuTrackingToAll = MENU_CLIENT_COMMAND:New( Client, "To All", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingToAll = true } ) - Client.MenuTrackingToTarget = MENU_CLIENT_COMMAND:New( Client, "To Target", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, TrackingToAll = false } ) - Client.MenuTrackOn = MENU_CLIENT_COMMAND:New( Client, "Tracking On", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, Tracking = true } ) - Client.MenuTrackOff = MENU_CLIENT_COMMAND:New( Client, "Tracking Off", Client.MenuTracking, self._MenuMessages, { MenuSelf = self, Tracking = false } ) - - Client.MenuAlerts = MENU_CLIENT:New( Client, "Alerts", Client.MainMenu ) - Client.MenuAlertsToAll = MENU_CLIENT_COMMAND:New( Client, "To All", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsToAll = true } ) - Client.MenuAlertsToTarget = MENU_CLIENT_COMMAND:New( Client, "To Target", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsToAll = false } ) - Client.MenuHitsOn = MENU_CLIENT_COMMAND:New( Client, "Hits On", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsHits = true } ) - Client.MenuHitsOff = MENU_CLIENT_COMMAND:New( Client, "Hits Off", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsHits = false } ) - Client.MenuLaunchesOn = MENU_CLIENT_COMMAND:New( Client, "Launches On", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsLaunches = true } ) - Client.MenuLaunchesOff = MENU_CLIENT_COMMAND:New( Client, "Launches Off", Client.MenuAlerts, self._MenuMessages, { MenuSelf = self, AlertsLaunches = false } ) - - Client.MenuDetails = MENU_CLIENT:New( Client, "Details", Client.MainMenu ) - Client.MenuDetailsDistanceOn = MENU_CLIENT_COMMAND:New( Client, "Range On", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsRange = true } ) - Client.MenuDetailsDistanceOff = MENU_CLIENT_COMMAND:New( Client, "Range Off", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsRange = false } ) - Client.MenuDetailsBearingOn = MENU_CLIENT_COMMAND:New( Client, "Bearing On", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsBearing = true } ) - Client.MenuDetailsBearingOff = MENU_CLIENT_COMMAND:New( Client, "Bearing Off", Client.MenuDetails, self._MenuMessages, { MenuSelf = self, DetailsBearing = false } ) - - Client.MenuDistance = MENU_CLIENT:New( Client, "Set distance to plane", Client.MainMenu ) - Client.MenuDistance50 = MENU_CLIENT_COMMAND:New( Client, "50 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 50 / 1000 } ) - Client.MenuDistance100 = MENU_CLIENT_COMMAND:New( Client, "100 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 100 / 1000 } ) - Client.MenuDistance150 = MENU_CLIENT_COMMAND:New( Client, "150 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 150 / 1000 } ) - Client.MenuDistance200 = MENU_CLIENT_COMMAND:New( Client, "200 meter", Client.MenuDistance, self._MenuMessages, { MenuSelf = self, Distance = 200 / 1000 } ) - local ClientID = Client:GetID() self:T( ClientID ) @@ -15348,160 +15382,260 @@ function MISSILETRAINER:New( Distance ) self.TrackingMissiles[ClientID].MissileData = {} end end - - Client:Alive( _Alive ) - - end --- self.DB:ForEachClient( --- --- @param Client#CLIENT Client --- function( Client ) --- --- end --- ) - - self.MessagesOnOff = true + + Client:Alive( _Alive ) + + end + -- self.DB:ForEachClient( + -- --- @param Client#CLIENT Client + -- function( Client ) + -- + -- end + -- ) + + self.MessagesOnOff = true self.TrackingToAll = false - self.Tracking = true + self.TrackingOnOff = true self.AlertsToAll = true - self.AlertsHits = true - self.AlertsLaunches = true + self.AlertsHitsOnOff = true + self.AlertsLaunchesOnOff = true + + self.DetailsRangeOnOff = true + self.DetailsBearingOnOff = true - self.DetailsRange = true - self.DetailsBearing = true - self.TrackingMissiles = {} - - self.TrackingScheduler = SCHEDULER:New( self, self._TrackMissiles, {}, 0.5, 0.05, 0 ) - - return self + + self.TrackingScheduler = SCHEDULER:New( self, self._TrackMissiles, {}, 0.5, 0.05, 0 ) + + return self end +-- Initialization methods. + + +--- Sets by default the display of any message to be ON or OFF. +-- @param #MISSILETRAINER self +-- @param #boolean MessagesOnOff true or false +-- @return #MISSILETRAINER self +function MISSILETRAINER:InitMessagesOnOff( MessagesOnOff ) + self:F( MessagesOnOff ) + + self.MessagesOnOff = MessagesOnOff + if self.MessagesOnOff == true then + MESSAGE:New( "Messages ON", "Menu", 15, "ID" ):ToAll() + else + MESSAGE:New( "Messages OFF", "Menu", 15, "ID" ):ToAll() + end + + return self +end + +--- Sets by default the missile tracking report for all players or only for those missiles targetted to you. +-- @param #MISSILETRAINER self +-- @param #boolean TrackingToAll true or false +-- @return #MISSILETRAINER self +function MISSILETRAINER:InitTrackingToAll( TrackingToAll ) + self:F( TrackingToAll ) + + self.TrackingToAll = TrackingToAll + if self.TrackingToAll == true then + MESSAGE:New( "Missile tracking to all players ON", "Menu", 15, "ID" ):ToAll() + else + MESSAGE:New( "Missile tracking to all players OFF", "Menu", 15, "ID" ):ToAll() + end + + return self +end + +--- Sets by default the display of missile tracking report to be ON or OFF. +-- @param #MISSILETRAINER self +-- @param #boolean TrackingOnOff true or false +-- @return #MISSILETRAINER self +function MISSILETRAINER:InitTrackingOnOff( TrackingOnOff ) + self:F( TrackingOnOff ) + + self.TrackingOnOff = TrackingOnOff + if self.TrackingOnOff == true then + MESSAGE:New( "Missile tracking ON", "Menu", 15, "ID" ):ToAll() + else + MESSAGE:New( "Missile tracking OFF", "Menu", 15, "ID" ):ToAll() + end + + return self +end + +--- Sets by default the display of alerts to be shown to all players or only to you. +-- @param #MISSILETRAINER self +-- @param #boolean AlertsToAll true or false +-- @return #MISSILETRAINER self +function MISSILETRAINER:InitAlertsToAll( AlertsToAll ) + self:F( AlertsToAll ) + + self.AlertsToAll = AlertsToAll + if self.AlertsToAll == true then + MESSAGE:New( "Alerts to all players ON", "Menu", 15, "ID" ):ToAll() + else + MESSAGE:New( "Alerts to all players OFF", "Menu", 15, "ID" ):ToAll() + end + + return self +end + +--- Sets by default the display of hit alerts ON or OFF. +-- @param #MISSILETRAINER self +-- @param #boolean AlertsHitsOnOff true or false +-- @return #MISSILETRAINER self +function MISSILETRAINER:InitAlertsHitsOnOff( AlertsHitsOnOff ) + self:F( AlertsHitsOnOff ) + + self.AlertsHitsOnOff = AlertsHitsOnOff + if self.AlertsHitsOnOff == true then + MESSAGE:New( "Alerts Hits ON", "Menu", 15, "ID" ):ToAll() + else + MESSAGE:New( "Alerts Hits OFF", "Menu", 15, "ID" ):ToAll() + end + + return self +end + +--- Sets by default the display of launch alerts ON or OFF. +-- @param #MISSILETRAINER self +-- @param #boolean AlertsLaunchesOnOff true or false +-- @return #MISSILETRAINER self +function MISSILETRAINER:InitAlertsLaunchesOnOff( AlertsLaunchesOnOff ) + self:F( AlertsLaunchesOnOff ) + + self.AlertsLaunchesOnOff = AlertsLaunchesOnOff + if self.AlertsLaunchesOnOff == true then + MESSAGE:New( "Alerts Launches ON", "Menu", 15, "ID" ):ToAll() + else + MESSAGE:New( "Alerts Launches OFF", "Menu", 15, "ID" ):ToAll() + end + + return self +end + +--- Sets by default the display of range information of missiles ON of OFF. +-- @param #MISSILETRAINER self +-- @param #boolean DetailsRangeOnOff true or false +-- @return #MISSILETRAINER self +function MISSILETRAINER:InitRangeOnOff( DetailsRangeOnOff ) + self:F( DetailsRangeOnOff ) + + self.DetailsRangeOnOff = DetailsRangeOnOff + if self.DetailsRangeOnOff == true then + MESSAGE:New( "Range display ON", "Menu", 15, "ID" ):ToAll() + else + MESSAGE:New( "Range display OFF", "Menu", 15, "ID" ):ToAll() + end + + return self +end + +--- Sets by default the display of bearing information of missiles ON of OFF. +-- @param #MISSILETRAINER self +-- @param #boolean DetailsBearingOnOff true or false +-- @return #MISSILETRAINER self +function MISSILETRAINER:InitBearingOnOff( DetailsBearingOnOff ) + self:F( DetailsBearingOnOff ) + + self.DetailsBearingOnOff = DetailsBearingOnOff + if self.DetailsBearingOnOff == true then + MESSAGE:New( "Bearing display OFF", "Menu", 15, "ID" ):ToAll() + else + MESSAGE:New( "Bearing display OFF", "Menu", 15, "ID" ):ToAll() + end + + return self +end + +-- Menu functions + function MISSILETRAINER._MenuMessages( MenuParameters ) local self = MenuParameters.MenuSelf - + if MenuParameters.MessagesOnOff ~= nil then - self.MessagesOnOff = MenuParameters.MessagesOnOff - if self.MessagesOnOff == true then - MESSAGE:New( "Messages ON", "Menu", 15, "ID" ):ToAll() - else - MESSAGE:New( "Messages OFF", "Menu", 15, "ID" ):ToAll() - end + self:InitMessagesOnOff( MenuParameters.MessagesOnOff ) end - + if MenuParameters.TrackingToAll ~= nil then - self.TrackingToAll = MenuParameters.TrackingToAll - if self.TrackingToAll == true then - MESSAGE:New( "Missile tracking to all players ON", "Menu", 15, "ID" ):ToAll() - else - MESSAGE:New( "Missile tracking to all players OFF", "Menu", 15, "ID" ):ToAll() - end + self:InitTrackingToAll( MenuParameters.TrackingToAll ) end - - if MenuParameters.Tracking ~= nil then - self.Tracking = MenuParameters.Tracking - if self.Tracking == true then - MESSAGE:New( "Missile tracking ON", "Menu", 15, "ID" ):ToAll() - else - MESSAGE:New( "Missile tracking OFF", "Menu", 15, "ID" ):ToAll() - end + + if MenuParameters.TrackingOnOff ~= nil then + self:InitTrackingOnOff( MenuParameters.TrackingOnOff ) end if MenuParameters.AlertsToAll ~= nil then - self.AlertsToAll = MenuParameters.AlertsToAll - if self.AlertsToAll == true then - MESSAGE:New( "Alerts to all players ON", "Menu", 15, "ID" ):ToAll() - else - MESSAGE:New( "Alerts to all players OFF", "Menu", 15, "ID" ):ToAll() - end + self:InitAlertsToAll( MenuParameters.AlertsToAll ) end - if MenuParameters.AlertsHits ~= nil then - self.AlertsHits = MenuParameters.AlertsHits - if self.AlertsHits == true then - MESSAGE:New( "Alerts Hits ON", "Menu", 15, "ID" ):ToAll() - else - MESSAGE:New( "Alerts Hits OFF", "Menu", 15, "ID" ):ToAll() - end + if MenuParameters.AlertsHitsOnOff ~= nil then + self:InitAlertsHitsOnOff( MenuParameters.AlertsHitsOnOff ) end - - if MenuParameters.AlertsLaunches ~= nil then - self.AlertsLaunches = MenuParameters.AlertsLaunches - if self.AlertsLaunches == true then - MESSAGE:New( "Alerts Launches ON", "Menu", 15, "ID" ):ToAll() - else - MESSAGE:New( "Alerts Launches OFF", "Menu", 15, "ID" ):ToAll() - end - + + if MenuParameters.AlertsLaunchesOnOff ~= nil then + self:InitAlertsLaunchesOnOff( MenuParameters.AlertsLaunchesOnOff ) end - - if MenuParameters.DetailsRange ~= nil then - self.DetailsRange = MenuParameters.DetailsRange - if self.DetailsRange == true then - MESSAGE:New( "Range display ON", "Menu", 15, "ID" ):ToAll() - else - MESSAGE:New( "Range display OFF", "Menu", 15, "ID" ):ToAll() - end + + if MenuParameters.DetailsRangeOnOff ~= nil then + self:InitRangeOnOff( MenuParameters.DetailsRangeOnOff ) end - - if MenuParameters.DetailsBearing ~= nil then - self.DetailsBearing = MenuParameters.DetailsBearing - if self.DetailsBearing == true then - MESSAGE:New( "Bearing display OFF", "Menu", 15, "ID" ):ToAll() - else - MESSAGE:New( "Bearing display OFF", "Menu", 15, "ID" ):ToAll() - end + + if MenuParameters.DetailsBearingOnOff ~= nil then + self:InitBearingOnOff( MenuParameters.DetailsBearingOnOff ) end - + if MenuParameters.Distance ~= nil then self.Distance = MenuParameters.Distance - MESSAGE:New( "Hit detection distance set to " .. self.Distance .. " meters", "Menu", 15, "ID" ):ToAll() + MESSAGE:New( "Hit detection distance set to " .. self.Distance .. " meters", "Menu", 15, "ID" ):ToAll() end - + end --- Detects if an SA site was shot with an anti radiation missile. In this case, take evasive actions based on the skill level set within the ME. -- @param #MISSILETRAINER self -- @param Event#EVENTDATA Event function MISSILETRAINER:_EventShot( Event ) - self:F( { Event } ) + self:F( { Event } ) - local TrainerSourceDCSUnit = Event.IniDCSUnit - local TrainerSourceDCSUnitName = Event.IniDCSUnitName - local TrainerWeapon = Event.Weapon -- Identify the weapon fired - local TrainerWeaponName = Event.WeaponName -- return weapon type + local TrainerSourceDCSUnit = Event.IniDCSUnit + local TrainerSourceDCSUnitName = Event.IniDCSUnitName + local TrainerWeapon = Event.Weapon -- Identify the weapon fired + local TrainerWeaponName = Event.WeaponName -- return weapon type - self:T( "Missile Launched = " .. TrainerWeaponName ) + self:T( "Missile Launched = " .. TrainerWeaponName ) - local TrainerTargetDCSUnit = TrainerWeapon:getTarget() -- Identify target - local TrainerTargetDCSUnitName = Unit.getName( TrainerTargetDCSUnit ) - local TrainerTargetSkill = _DATABASE.Templates.Units[TrainerTargetDCSUnitName].Template.skill + local TrainerTargetDCSUnit = TrainerWeapon:getTarget() -- Identify target + local TrainerTargetDCSUnitName = Unit.getName( TrainerTargetDCSUnit ) + local TrainerTargetSkill = _DATABASE.Templates.Units[TrainerTargetDCSUnitName].Template.skill - self:T(TrainerTargetDCSUnitName ) - - local Client = self.DBClients[TrainerTargetDCSUnitName] - if Client then + self:T(TrainerTargetDCSUnitName ) - local TrainerSourceUnit = UNIT:New(TrainerSourceDCSUnit) + local Client = self.DBClients[TrainerTargetDCSUnitName] + if Client then + + local TrainerSourceUnit = UNIT:New(TrainerSourceDCSUnit) local TrainerTargetUnit = UNIT:New(TrainerTargetDCSUnit) - if self.MessagesOnOff and self.AlertsLaunches then - - local Message = MESSAGE:New( - string.format( "%s launched a %s", - TrainerSourceUnit:GetTypeName(), - TrainerWeaponName - ) .. self:AddRange( Client, TrainerWeapon ) .. self:AddBearing( Client, TrainerWeapon ),"Launch Alert", 5, "ID" ) - + if self.MessagesOnOff and self.AlertsLaunchesOnOff then + + local Message = MESSAGE:New( + string.format( "%s launched a %s", + TrainerSourceUnit:GetTypeName(), + TrainerWeaponName + ) .. self:_AddRange( Client, TrainerWeapon ) .. self:_AddBearing( Client, TrainerWeapon ),"Launch Alert", 5, "ID" ) + if self.AlertsToAll then Message:ToAll() else Message:ToClient( Client ) end end - + local ClientID = Client:GetID() local MissileData = {} MissileData.TrainerSourceUnit = TrainerSourceUnit @@ -15511,18 +15645,18 @@ function MISSILETRAINER:_EventShot( Event ) MissileData.TrainerWeaponLaunched = true table.insert( self.TrackingMissiles[ClientID].MissileData, MissileData ) --self:T( self.TrackingMissiles ) - end + end end -function MISSILETRAINER:AddRange( Client, TrainerWeapon ) +function MISSILETRAINER:_AddRange( Client, TrainerWeapon ) local RangeText = "" - - if self.DetailsRange then + + if self.DetailsRangeOnOff then local PositionMissile = TrainerWeapon:getPoint() local PositionTarget = Client:GetPositionVec3() - + local Range = ( ( PositionMissile.x - PositionTarget.x )^2 + ( PositionMissile.y - PositionTarget.y )^2 + ( PositionMissile.z - PositionTarget.z )^2 @@ -15530,21 +15664,21 @@ function MISSILETRAINER:AddRange( Client, TrainerWeapon ) RangeText = string.format( ", at %4.2fkm", Range ) end - + return RangeText end -function MISSILETRAINER:AddBearing( Client, TrainerWeapon ) +function MISSILETRAINER:_AddBearing( Client, TrainerWeapon ) local BearingText = "" - - if self.DetailsBearing then + + if self.DetailsBearingOnOff then local PositionMissile = TrainerWeapon:getPoint() local PositionTarget = Client:GetPositionVec3() - + self:T2( { PositionTarget, PositionMissile }) - + local DirectionVector = { x = PositionMissile.x - PositionTarget.x, y = PositionMissile.y - PositionTarget.y, z = PositionMissile.z - PositionTarget.z } local DirectionRadians = math.atan2( DirectionVector.z, DirectionVector.x ) --DirectionRadians = DirectionRadians + routines.getNorthCorrection( PositionTarget ) @@ -15552,79 +15686,75 @@ function MISSILETRAINER:AddBearing( Client, TrainerWeapon ) DirectionRadians = DirectionRadians + 2 * math.pi end local DirectionDegrees = DirectionRadians * 180 / math.pi - + BearingText = string.format( ", %d degrees", DirectionDegrees ) end - + return BearingText end --- -- @param #MISSILETRAINER self --- @param Unit#UNIT TrainerSourceDCSUnit --- @param DCSWeapon#Weapon TrainerWeapon --- @param Unit#UNIT TrainerTargetDCSUnit --- @param Client#CLIENT Client function MISSILETRAINER:_TrackMissiles() self:F2() - + local ShowMessages = false if self.MessagesOnOff and self.MessageLastTime + 3 <= timer.getTime() then self.MessageLastTime = timer.getTime() ShowMessages = true end - + for ClientDataID, ClientData in pairs( self.TrackingMissiles ) do local Client = ClientData.Client self:T2( { Client:GetName() } ) - - + + ClientData.MessageToClient = "" ClientData.MessageToAll = "" - + for TrackingDataID, TrackingData in pairs( self.TrackingMissiles ) do - + for MissileDataID, MissileData in pairs( TrackingData.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 - + 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 PositionTarget = Client:GetPositionVec3() - + local Distance = ( ( PositionMissile.x - PositionTarget.x )^2 + ( PositionMissile.y - PositionTarget.y )^2 + ( PositionMissile.z - PositionTarget.z )^2 ) ^ 0.5 / 1000 - + if Distance <= self.Distance then -- Hit alert TrainerWeapon:destroy() - if self.MessagesOnOff and self.AlertsHits then - + if self.MessagesOnOff and self.AlertsHitsOnOff then + self:T( "killed" ) - - local Message = MESSAGE:New( - string.format( "%s launched by %s killed %s", - TrainerWeapon:getTypeName(), - TrainerSourceUnit:GetTypeName(), - TrainerTargetUnit:GetPlayerName() - ),"Hit Alert", 15, "ID" ) - + + local Message = MESSAGE:New( + string.format( "%s launched by %s killed %s", + TrainerWeapon:getTypeName(), + TrainerSourceUnit:GetTypeName(), + TrainerTargetUnit:GetPlayerName() + ),"Hit Alert", 15, "ID" ) + if self.AlertsToAll then Message:ToAll() else Message:ToClient( Client ) end - + MissileData = nil table.remove( TrackingData.MissileData, MissileDataID ) self:T(TrackingData.MissileData) @@ -15632,35 +15762,35 @@ function MISSILETRAINER:_TrackMissiles() else if ShowMessages then local TrackingTo - TrackingTo = string.format( " -> %s", - TrainerWeaponTypeName - ) - + TrackingTo = string.format( " -> %s", + TrainerWeaponTypeName + ) + if ClientDataID == TrackingDataID then if ClientData.MessageToClient == "" then ClientData.MessageToClient = "Missiles to You:\n" end - ClientData.MessageToClient = ClientData.MessageToClient .. TrackingTo .. self:AddRange( ClientData.Client, TrainerWeapon ) .. self:AddBearing( ClientData.Client, TrainerWeapon ) .. "\n" + ClientData.MessageToClient = ClientData.MessageToClient .. TrackingTo .. self:_AddRange( ClientData.Client, TrainerWeapon ) .. self:_AddBearing( ClientData.Client, TrainerWeapon ) .. "\n" else if self.TrackingToAll then if ClientData.MessageToAll == "" then ClientData.MessageToAll = "Missiles to other Players:\n" end - ClientData.MessageToAll = ClientData.MessageToAll .. TrackingTo .. self:AddRange( ClientData.Client, TrainerWeapon ) .. self:AddBearing( ClientData.Client, TrainerWeapon ) .. " ( " .. TrainerTargetUnit:GetPlayerName() .. " )\n" + ClientData.MessageToAll = ClientData.MessageToAll .. TrackingTo .. self:_AddRange( ClientData.Client, TrainerWeapon ) .. self:_AddBearing( ClientData.Client, TrainerWeapon ) .. " ( " .. TrainerTargetUnit:GetPlayerName() .. " )\n" end end end end else if not ( TrainerWeapon and TrainerWeapon:isExist() ) then - if self.MessagesOnOff and self.AlertsLaunches then - -- Weapon does not exist anymore. Delete from Table - local Message = MESSAGE:New( - string.format( "%s launched by %s self destructed!", - TrainerWeaponTypeName, - TrainerSourceUnit:GetTypeName() - ),"Tracking", 5, "ID" ) - + if self.MessagesOnOff and self.AlertsLaunchesOnOff then + -- Weapon does not exist anymore. Delete from Table + local Message = MESSAGE:New( + string.format( "%s launched by %s self destructed!", + TrainerWeaponTypeName, + TrainerSourceUnit:GetTypeName() + ),"Tracking", 5, "ID" ) + if self.AlertsToAll then Message:ToAll() else @@ -15675,10 +15805,10 @@ function MISSILETRAINER:_TrackMissiles() end end - if self.MessagesOnOff and self.Tracking and ShowMessages then + if self.MessagesOnOff and self.TrackingOnOff and ShowMessages then if ClientData.MessageToClient ~= "" or ClientData.MessageToAll ~= "" then local Message = MESSAGE:New( ClientData.MessageToClient .. ClientData.MessageToAll, "Tracking", 1, "ID" ):ToClient( Client ) - end + end end end diff --git a/Presentations/DCS World - MOOSE - Missile Trainer.pptx b/Presentations/DCS World - MOOSE - Missile Trainer.pptx index 4c6738ffcaf172e204d4ad52efa7bc0f219279fd..e894e5374064bb478b91b32458825b6996b26873 100644 GIT binary patch delta 11439 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 O{@cQWtAz~=r~V&41(i7f delta 10603 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