mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
Merge branch 'develop' into FF/Ops
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
--
|
||||
-- @module Core.ClientMenu
|
||||
-- @image Core_Menu.JPG
|
||||
-- last change: Sept 2023
|
||||
-- last change: Oct 2023
|
||||
|
||||
-- TODO
|
||||
----------------------------------------------------------------------------------------------------------------
|
||||
@@ -304,6 +304,8 @@ end
|
||||
-- @field #table menutree
|
||||
-- @field #number entrycount
|
||||
-- @field #boolean debug
|
||||
-- @field #table PlayerMenu
|
||||
-- @field #number Coalition
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
--- *As a child my family's menu consisted of two choices: take it, or leave it.*
|
||||
@@ -345,7 +347,7 @@ end
|
||||
-- local mymenu_lv3b = menumgr:NewEntry("Level 3 aab",mymenu_lv2a)
|
||||
-- local mymenu_lv3c = menumgr:NewEntry("Level 3 aac",mymenu_lv2a)
|
||||
--
|
||||
-- menumgr:Propagate()
|
||||
-- menumgr:Propagate() -- propagate **once** to all clients in the SET_CLIENT
|
||||
--
|
||||
-- ## Remove a single entry's subtree
|
||||
--
|
||||
@@ -384,13 +386,17 @@ end
|
||||
--
|
||||
-- ## Reset all and clear the reference tree
|
||||
--
|
||||
-- menumgr:ResetMenuComplete()
|
||||
-- menumgr:ResetMenuComplete()
|
||||
--
|
||||
-- ## Set to auto-propagate for CLIENTs joining the SET_CLIENT **after** the script is loaded - handy if you have a single menu tree.
|
||||
--
|
||||
-- menumgr:InitAutoPropagation()
|
||||
--
|
||||
-- @field #CLIENTMENUMANAGER
|
||||
CLIENTMENUMANAGER = {
|
||||
ClassName = "CLIENTMENUMANAGER",
|
||||
lid = "",
|
||||
version = "0.1.1",
|
||||
version = "0.1.3",
|
||||
name = nil,
|
||||
clientset = nil,
|
||||
menutree = {},
|
||||
@@ -399,26 +405,108 @@ CLIENTMENUMANAGER = {
|
||||
entrycount = 0,
|
||||
rootentries = {},
|
||||
debug = true,
|
||||
PlayerMenu = {},
|
||||
Coalition = nil,
|
||||
}
|
||||
|
||||
--- Create a new ClientManager instance.
|
||||
-- @param #CLIENTMENUMANAGER self
|
||||
-- @param Core.Set#SET_CLIENT ClientSet The set of clients to manage.
|
||||
-- @param #string Alias The name of this manager.
|
||||
-- @param #number Coalition (Optional) Coalition of this Manager, defaults to coalition.side.BLUE
|
||||
-- @return #CLIENTMENUMANAGER self
|
||||
function CLIENTMENUMANAGER:New(ClientSet, Alias)
|
||||
function CLIENTMENUMANAGER:New(ClientSet, Alias, Coalition)
|
||||
-- Inherit everything from FSM class.
|
||||
local self=BASE:Inherit(self, BASE:New()) -- #CLIENTMENUMANAGER
|
||||
self.clientset = ClientSet
|
||||
self.PlayerMenu = {}
|
||||
self.name = Alias or "Nightshift"
|
||||
self.Coalition = Coalition or coalition.side.BLUE
|
||||
-- Log id.
|
||||
self.lid=string.format("CLIENTMENUMANAGER %s | %s | ", self.version, self.name)
|
||||
if self.debug then
|
||||
self:T(self.lid.."Created")
|
||||
self:I(self.lid.."Created")
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- [Internal] Event handling
|
||||
-- @param #CLIENTMENUMANAGER self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
-- @return #CLIENTMENUMANAGER self
|
||||
function CLIENTMENUMANAGER:_EventHandler(EventData)
|
||||
self:T(self.lid.."_EventHandler: "..EventData.id)
|
||||
--self:I(self.lid.."_EventHandler: "..tostring(EventData.IniPlayerName))
|
||||
if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then
|
||||
self:T(self.lid.."Leave event for player: "..tostring(EventData.IniPlayerName))
|
||||
local Client = _DATABASE:FindClient( EventData.IniPlayerName )
|
||||
if Client then
|
||||
self:ResetMenu(Client)
|
||||
end
|
||||
elseif (EventData.id == EVENTS.PlayerEnterAircraft) and EventData.IniCoalition == self.Coalition then
|
||||
if EventData.IniPlayerName and EventData.IniGroup then
|
||||
if (not self.clientset:IsIncludeObject(_DATABASE:FindClient( EventData.IniPlayerName ))) then
|
||||
self:T(self.lid.."Client not in SET: "..EventData.IniPlayerName)
|
||||
return self
|
||||
end
|
||||
--self:I(self.lid.."Join event for player: "..EventData.IniPlayerName)
|
||||
local player = _DATABASE:FindClient( EventData.IniPlayerName )
|
||||
self:Propagate(player)
|
||||
end
|
||||
elseif EventData.id == EVENTS.PlayerEnterUnit then
|
||||
-- special for CA slots
|
||||
local grp = GROUP:FindByName(EventData.IniGroupName)
|
||||
if grp:IsGround() then
|
||||
self:T(string.format("Player %s entered GROUND unit %s!",EventData.IniPlayerName,EventData.IniUnitName))
|
||||
local IsPlayer = EventData.IniDCSUnit:getPlayerName()
|
||||
if IsPlayer then
|
||||
|
||||
local client=_DATABASE.CLIENTS[EventData.IniDCSUnitName] --Wrapper.Client#CLIENT
|
||||
|
||||
-- Add client in case it does not exist already.
|
||||
if not client then
|
||||
|
||||
-- Debug info.
|
||||
self:I(string.format("Player '%s' joined ground unit '%s' of group '%s'", tostring(EventData.IniPlayerName), tostring(EventData.IniDCSUnitName), tostring(EventData.IniDCSGroupName)))
|
||||
|
||||
client=_DATABASE:AddClient(EventData.IniDCSUnitName)
|
||||
|
||||
-- Add player.
|
||||
client:AddPlayer(EventData.IniPlayerName)
|
||||
|
||||
-- Add player.
|
||||
if not _DATABASE.PLAYERS[EventData.IniPlayerName] then
|
||||
_DATABASE:AddPlayer( EventData.IniUnitName, EventData.IniPlayerName )
|
||||
end
|
||||
|
||||
-- Player settings.
|
||||
local Settings = SETTINGS:Set( EventData.IniPlayerName )
|
||||
Settings:SetPlayerMenu(EventData.IniUnit)
|
||||
end
|
||||
--local player = _DATABASE:FindClient( EventData.IniPlayerName )
|
||||
self:Propagate(client)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Set this Client Manager to auto-propagate menus to newly joined players. Useful if you have **one** menu structure only.
|
||||
-- @param #CLIENTMENUMANAGER self
|
||||
-- @return #CLIENTMENUMANAGER self
|
||||
function CLIENTMENUMANAGER:InitAutoPropagation()
|
||||
-- Player Events
|
||||
self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventHandler)
|
||||
self:HandleEvent(EVENTS.Ejection, self._EventHandler)
|
||||
self:HandleEvent(EVENTS.Crash, self._EventHandler)
|
||||
self:HandleEvent(EVENTS.PilotDead, self._EventHandler)
|
||||
self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler)
|
||||
self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventHandler)
|
||||
self:SetEventPriority(5)
|
||||
return self
|
||||
end
|
||||
|
||||
--- Create a new entry in the generic structure.
|
||||
-- @param #CLIENTMENUMANAGER self
|
||||
-- @param #string Text Text of the F10 menu entry.
|
||||
@@ -571,7 +659,7 @@ end
|
||||
-- @return #CLIENTMENU Entry
|
||||
function CLIENTMENUMANAGER:Propagate(Client)
|
||||
self:T(self.lid.."Propagate")
|
||||
self:T(Client)
|
||||
--self:I(UTILS.PrintTableToLog(Client,1))
|
||||
local Set = self.clientset.Set
|
||||
if Client then
|
||||
Set = {Client}
|
||||
@@ -792,4 +880,3 @@ end
|
||||
-- End ClientMenu
|
||||
--
|
||||
----------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -126,6 +126,8 @@ function DATABASE:New()
|
||||
self:SetEventPriority( 1 )
|
||||
|
||||
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
|
||||
-- DCS 2.9 fixed CA event for players -- TODO: reset unit when leaving
|
||||
self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventOnPlayerEnterUnit )
|
||||
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
||||
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
||||
self:HandleEvent( EVENTS.RemoveUnit, self._EventOnDeadOrCrash )
|
||||
@@ -810,6 +812,7 @@ function DATABASE:AddPlayer( UnitName, PlayerName )
|
||||
self.PLAYERUNITS[PlayerName] = self:FindUnit( UnitName )
|
||||
self.PLAYERSJOINED[PlayerName] = PlayerName
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--- Deletes a player from the DATABASE based on the Player Name.
|
||||
@@ -1470,39 +1473,43 @@ function DATABASE:_EventOnDeadOrCrash( Event )
|
||||
end
|
||||
|
||||
|
||||
--- Handles the OnPlayerEnterUnit event to fill the active players table (with the unit filter applied).
|
||||
--- Handles the OnPlayerEnterUnit event to fill the active players table for CA units (with the unit filter applied).
|
||||
-- @param #DATABASE self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function DATABASE:_EventOnPlayerEnterUnit( Event )
|
||||
self:F2( { Event } )
|
||||
|
||||
if Event.IniDCSUnit then
|
||||
if Event.IniObjectCategory == 1 then
|
||||
-- Player entering a CA slot
|
||||
if Event.IniObjectCategory == 1 and Event.IniGroup and Event.IniGroup:IsGround() then
|
||||
|
||||
local IsPlayer = Event.IniDCSUnit:getPlayerName()
|
||||
if IsPlayer then
|
||||
|
||||
-- Add unit.
|
||||
self:AddUnit( Event.IniDCSUnitName )
|
||||
-- Debug info.
|
||||
self:I(string.format("Player '%s' joined GROUND unit '%s' of group '%s'", tostring(Event.IniPlayerName), tostring(Event.IniDCSUnitName), tostring(Event.IniDCSGroupName)))
|
||||
|
||||
local client= self.CLIENTS[Event.IniDCSUnitName] --Wrapper.Client#CLIENT
|
||||
|
||||
-- Add client in case it does not exist already.
|
||||
if not client then
|
||||
client=self:AddClient(Event.IniDCSUnitName)
|
||||
end
|
||||
|
||||
-- Add player.
|
||||
client:AddPlayer(Event.IniPlayerName)
|
||||
|
||||
-- Ini unit.
|
||||
Event.IniUnit = self:FindUnit( Event.IniDCSUnitName )
|
||||
|
||||
-- Add group.
|
||||
self:AddGroup( Event.IniDCSGroupName )
|
||||
|
||||
-- Get player unit.
|
||||
local PlayerName = Event.IniDCSUnit:getPlayerName()
|
||||
|
||||
if PlayerName then
|
||||
|
||||
if not self.PLAYERS[PlayerName] then
|
||||
self:AddPlayer( Event.IniDCSUnitName, PlayerName )
|
||||
-- Add player.
|
||||
if not self.PLAYERS[Event.IniPlayerName] then
|
||||
self:AddPlayer( Event.IniUnitName, Event.IniPlayerName )
|
||||
end
|
||||
|
||||
local Settings = SETTINGS:Set( PlayerName )
|
||||
Settings:SetPlayerMenu( Event.IniUnit )
|
||||
-- Player settings.
|
||||
local Settings = SETTINGS:Set( Event.IniPlayerName )
|
||||
Settings:SetPlayerMenu(Event.IniUnit)
|
||||
|
||||
else
|
||||
self:E("ERROR: getPlayerName() returned nil for event PlayerEnterUnit")
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1513,15 +1520,26 @@ end
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
function DATABASE:_EventOnPlayerLeaveUnit( Event )
|
||||
self:F2( { Event } )
|
||||
|
||||
|
||||
local function FindPlayerName(UnitName)
|
||||
local playername = nil
|
||||
for _name,_unitname in pairs(self.PLAYERS) do
|
||||
if _unitname == UnitName then
|
||||
playername = _name
|
||||
break
|
||||
end
|
||||
end
|
||||
return playername
|
||||
end
|
||||
|
||||
if Event.IniUnit then
|
||||
|
||||
if Event.IniObjectCategory == 1 then
|
||||
|
||||
-- Try to get the player name. This can be buggy for multicrew aircraft!
|
||||
local PlayerName = Event.IniUnit:GetPlayerName()
|
||||
|
||||
if PlayerName then --and self.PLAYERS[PlayerName] then
|
||||
local PlayerName = Event.IniUnit:GetPlayerName() or FindPlayerName(Event.IniUnitName)
|
||||
|
||||
if PlayerName then
|
||||
|
||||
-- Debug info.
|
||||
self:I(string.format("Player '%s' left unit %s", tostring(PlayerName), tostring(Event.IniUnitName)))
|
||||
@@ -2005,8 +2023,6 @@ end
|
||||
TargetPlayerName = Event.IniPlayerName
|
||||
|
||||
TargetCoalition = Event.IniCoalition
|
||||
--TargetCategory = TargetUnit:getCategory()
|
||||
--TargetCategory = TargetUnit:getDesc().category -- Workaround
|
||||
TargetCategory = Event.IniCategory
|
||||
TargetType = Event.IniTypeName
|
||||
|
||||
|
||||
@@ -173,7 +173,8 @@
|
||||
-- @image Core_Event.JPG
|
||||
|
||||
|
||||
--- @type EVENT
|
||||
---
|
||||
-- @type EVENT
|
||||
-- @field #EVENT.Events Events
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
@@ -282,6 +283,7 @@ EVENTS = {
|
||||
-- @field Wrapper.Group#GROUP IniGroup (UNIT) The initiating MOOSE wrapper @{Wrapper.Group#GROUP} of the initiator Group object.
|
||||
-- @field #string IniGroupName UNIT) The initiating GROUP name (same as IniDCSGroupName).
|
||||
-- @field #string IniPlayerName (UNIT) The name of the initiating player in case the Unit is a client or player slot.
|
||||
-- @field #string IniPlayerUCID (UNIT) The UCID of the initiating player in case the Unit is a client or player slot and on a multi-player server.
|
||||
-- @field DCS#coalition.side IniCoalition (UNIT) The coalition of the initiator.
|
||||
-- @field DCS#Unit.Category IniCategory (UNIT) The category of the initiator.
|
||||
-- @field #string IniTypeName (UNIT) The type name of the initiator.
|
||||
@@ -297,6 +299,7 @@ EVENTS = {
|
||||
-- @field Wrapper.Group#GROUP TgtGroup (UNIT) The target MOOSE wrapper @{Wrapper.Group#GROUP} of the target Group object.
|
||||
-- @field #string TgtGroupName (UNIT) The target GROUP name (same as TgtDCSGroupName).
|
||||
-- @field #string TgtPlayerName (UNIT) The name of the target player in case the Unit is a client or player slot.
|
||||
-- @field #string TgtPlayerUCID (UNIT) The UCID of the target player in case the Unit is a client or player slot and on a multi-player server.
|
||||
-- @field DCS#coalition.side TgtCoalition (UNIT) The coalition of the target.
|
||||
-- @field DCS#Unit.Category TgtCategory (UNIT) The category of the target.
|
||||
-- @field #string TgtTypeName (UNIT) The type name of the target.
|
||||
@@ -1080,7 +1083,7 @@ function EVENT:onEvent( Event )
|
||||
|
||||
if Event.initiator then
|
||||
|
||||
Event.IniObjectCategory = Object.getCategory(Event.initiator) --Event.initiator:getCategory()
|
||||
Event.IniObjectCategory = Object.getCategory(Event.initiator)
|
||||
|
||||
if Event.IniObjectCategory == Object.Category.STATIC then
|
||||
---
|
||||
@@ -1143,6 +1146,14 @@ function EVENT:onEvent( Event )
|
||||
end
|
||||
|
||||
Event.IniPlayerName = Event.IniDCSUnit:getPlayerName()
|
||||
if Event.IniPlayerName then
|
||||
-- get UUCID
|
||||
local PID = NET.GetPlayerIDByName(nil,Event.IniPlayerName)
|
||||
if PID then
|
||||
Event.IniPlayerUCID = net.get_player_info(tonumber(PID), 'ucid')
|
||||
--env.info("Event.IniPlayerUCID="..tostring(Event.IniPlayerUCID),false)
|
||||
end
|
||||
end
|
||||
Event.IniCoalition = Event.IniDCSUnit:getCoalition()
|
||||
Event.IniTypeName = Event.IniDCSUnit:getTypeName()
|
||||
Event.IniCategory = Event.IniDCSUnit:getDesc().category
|
||||
@@ -1197,7 +1208,7 @@ function EVENT:onEvent( Event )
|
||||
---
|
||||
|
||||
-- Target category.
|
||||
Event.TgtObjectCategory = Object.getCategory(Event.target) --Event.target:getCategory()
|
||||
Event.TgtObjectCategory = Object.getCategory(Event.target)
|
||||
|
||||
if Event.TgtObjectCategory == Object.Category.UNIT then
|
||||
---
|
||||
@@ -1215,6 +1226,14 @@ function EVENT:onEvent( Event )
|
||||
Event.TgtGroupName = Event.TgtDCSGroupName
|
||||
end
|
||||
Event.TgtPlayerName = Event.TgtDCSUnit:getPlayerName()
|
||||
if Event.TgtPlayerName then
|
||||
-- get UUCID
|
||||
local PID = NET.GetPlayerIDByName(nil,Event.TgtPlayerName)
|
||||
if PID then
|
||||
Event.TgtPlayerUCID = net.get_player_info(tonumber(PID), 'ucid')
|
||||
--env.info("Event.TgtPlayerUCID="..tostring(Event.TgtPlayerUCID),false)
|
||||
end
|
||||
end
|
||||
Event.TgtCoalition = Event.TgtDCSUnit:getCoalition()
|
||||
Event.TgtCategory = Event.TgtDCSUnit:getDesc().category
|
||||
Event.TgtTypeName = Event.TgtDCSUnit:getTypeName()
|
||||
@@ -1283,7 +1302,7 @@ function EVENT:onEvent( Event )
|
||||
-- However, this is not a big thing, as the aircraft the pilot ejected from is usually long crashed before the ejected pilot touches the ground.
|
||||
--Event.Place=UNIT:Find(Event.place)
|
||||
else
|
||||
if Event.place:isExist() and Event.place:getCategory() ~= Object.Category.SCENERY then
|
||||
if Event.place:isExist() and Object.getCategory(Event.place) ~= Object.Category.SCENERY then
|
||||
Event.Place=AIRBASE:Find(Event.place)
|
||||
Event.PlaceName=Event.Place:GetName()
|
||||
end
|
||||
|
||||
@@ -465,7 +465,7 @@ _MESSAGESRS = {}
|
||||
-- @param #string PathToCredentials (optional) Path to credentials file for e.g. Google.
|
||||
-- @param #number Frequency Frequency in MHz. Can also be given as a #table of frequencies.
|
||||
-- @param #number Modulation Modulation, i.e. radio.modulation.AM or radio.modulation.FM. Can also be given as a #table of modulations.
|
||||
-- @param #string Gender (optional) Gender, i.e. "male" or "female", defaults to "male".
|
||||
-- @param #string Gender (optional) Gender, i.e. "male" or "female", defaults to "female".
|
||||
-- @param #string Culture (optional) Culture, e.g. "en-US", defaults to "en-GB"
|
||||
-- @param #string Voice (optional) Voice. Will override gender and culture settings, e.g. MSRS.Voices.Microsoft.Hazel or MSRS.Voices.Google.Standard.de_DE_Standard_D. Hint on Microsoft voices - working voices are limited to Hedda, Hazel, David, Zira and Hortense. **Must** be installed on your Desktop or Server!
|
||||
-- @param #number Coalition (optional) Coalition, can be coalition.side.RED, coalition.side.BLUE or coalition.side.NEUTRAL. Defaults to coalition.side.NEUTRAL.
|
||||
@@ -480,24 +480,42 @@ _MESSAGESRS = {}
|
||||
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS()
|
||||
--
|
||||
function MESSAGE.SetMSRS(PathToSRS,Port,PathToCredentials,Frequency,Modulation,Gender,Culture,Voice,Coalition,Volume,Label,Coordinate)
|
||||
_MESSAGESRS.MSRS = MSRS:New(PathToSRS,Frequency,Modulation,Volume)
|
||||
_MESSAGESRS.MSRS:SetCoalition(Coalition)
|
||||
_MESSAGESRS.MSRS = MSRS:New(PathToSRS,Frequency or 243,Modulation or radio.modulation.AM,Volume)
|
||||
|
||||
_MESSAGESRS.frequency = Frequency
|
||||
_MESSAGESRS.modulation = Modulation or radio.modulation.AM
|
||||
|
||||
_MESSAGESRS.MSRS:SetCoalition(Coalition or coalition.side.NEUTRAL)
|
||||
_MESSAGESRS.coalition = Coalition or coalition.side.NEUTRAL
|
||||
|
||||
_MESSAGESRS.coordinate = Coordinate
|
||||
_MESSAGESRS.MSRS:SetCoordinate(Coordinate)
|
||||
|
||||
_MESSAGESRS.MSRS:SetCulture(Culture)
|
||||
_MESSAGESRS.Culture = Culture
|
||||
--_MESSAGESRS.MSRS:SetFrequencies(Frequency)
|
||||
_MESSAGESRS.Culture = Culture or "en-GB"
|
||||
|
||||
_MESSAGESRS.MSRS:SetGender(Gender)
|
||||
_MESSAGESRS.Gender = Gender
|
||||
_MESSAGESRS.Gender = Gender or "female"
|
||||
|
||||
_MESSAGESRS.MSRS:SetGoogle(PathToCredentials)
|
||||
_MESSAGESRS.google = PathToCredentials
|
||||
|
||||
_MESSAGESRS.MSRS:SetLabel(Label or "MESSAGE")
|
||||
--_MESSAGESRS.MSRS:SetModulations(Modulation)
|
||||
--_MESSAGESRS.MSRS:SetPath(PathToSRS)
|
||||
_MESSAGESRS.MSRS:SetPort(Port)
|
||||
-- _MESSAGESRS.MSRS:SetVolume(Volume)
|
||||
_MESSAGESRS.MSRS:SetVoice(Voice)
|
||||
_MESSAGESRS.Voice = Voice
|
||||
_MESSAGESRS.label = Label or "MESSAGE"
|
||||
|
||||
_MESSAGESRS.MSRS:SetPort(Port or 5002)
|
||||
_MESSAGESRS.port = Port or 5002
|
||||
|
||||
_MESSAGESRS.volume = Volume or 1
|
||||
_MESSAGESRS.MSRS:SetVolume(_MESSAGESRS.volume)
|
||||
|
||||
if Voice then _MESSAGESRS.MSRS:SetVoice(Voice) end
|
||||
|
||||
_MESSAGESRS.voice = Voice --or MSRS.Voices.Microsoft.Hedda
|
||||
--if _MESSAGESRS.google and not Voice then _MESSAGESRS.Voice = MSRS.Voices.Google.Standard.en_GB_Standard_A end
|
||||
--_MESSAGESRS.MSRS:SetVoice(Voice or _MESSAGESRS.voice)
|
||||
|
||||
_MESSAGESRS.SRSQ = MSRSQUEUE:New(Label or "MESSAGE")
|
||||
env.info(_MESSAGESRS.MSRS.provider,false)
|
||||
end
|
||||
|
||||
--- Sends a message via SRS.
|
||||
@@ -505,7 +523,7 @@ end
|
||||
-- @param #number frequency (optional) Frequency in MHz. Can also be given as a #table of frequencies. Only needed if you want to override defaults set with `MESSAGE.SetMSRS()` for this one setting.
|
||||
-- @param #number modulation (optional) Modulation, i.e. radio.modulation.AM or radio.modulation.FM. Can also be given as a #table of modulations. Only needed if you want to override defaults set with `MESSAGE.SetMSRS()` for this one setting.
|
||||
-- @param #string gender (optional) Gender, i.e. "male" or "female". Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`.
|
||||
-- @param #string culture (optional) Culture, e.g. "en-US. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`.
|
||||
-- @param #string culture (optional) Culture, e.g. "en-US". Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`.
|
||||
-- @param #string voice (optional) Voice. Will override gender and culture settings. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`.
|
||||
-- @param #number coalition (optional) Coalition, can be coalition.side.RED, coalition.side.BLUE or coalition.side.NEUTRAL. Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`.
|
||||
-- @param #number volume (optional) Volume, can be between 0.0 and 1.0 (loudest). Only needed if you want to change defaults set with `MESSAGE.SetMSRS()`.
|
||||
@@ -519,13 +537,16 @@ end
|
||||
-- MESSAGE:New("Test message!",15,"SPAWN"):ToSRS()
|
||||
--
|
||||
function MESSAGE:ToSRS(frequency,modulation,gender,culture,voice,coalition,volume,coordinate)
|
||||
local tgender = gender or _MESSAGESRS.Gender
|
||||
if _MESSAGESRS.SRSQ then
|
||||
_MESSAGESRS.MSRS:SetVoice(voice or _MESSAGESRS.Voice)
|
||||
if voice then
|
||||
_MESSAGESRS.MSRS:SetVoice(voice or _MESSAGESRS.voice)
|
||||
end
|
||||
if coordinate then
|
||||
_MESSAGESRS.MSRS:SetCoordinate(coordinate)
|
||||
end
|
||||
local category = string.gsub(self.MessageCategory,":","")
|
||||
_MESSAGESRS.SRSQ:NewTransmission(self.MessageText,nil,_MESSAGESRS.MSRS,nil,nil,nil,nil,nil,frequency,modulation,gender or _MESSAGESRS.Gender,culture or _MESSAGESRS.Culture,voice or _MESSAGESRS.Voice,volume,category,coordinate)
|
||||
_MESSAGESRS.SRSQ:NewTransmission(self.MessageText,nil,_MESSAGESRS.MSRS,nil,nil,nil,nil,nil,frequency or _MESSAGESRS.frequency,modulation or _MESSAGESRS.modulation, gender or _MESSAGESRS.Gender,culture or _MESSAGESRS.Culture,nil,volume or _MESSAGESRS.volume,category,coordinate or _MESSAGESRS.coordinate)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -544,7 +544,7 @@ do -- COORDINATE
|
||||
if ZoneObject then
|
||||
|
||||
-- Get category of scanned object.
|
||||
local ObjectCategory = ZoneObject:getCategory()
|
||||
local ObjectCategory = Object.getCategory(ZoneObject)
|
||||
|
||||
-- Check for unit or static objects
|
||||
if ObjectCategory==Object.Category.UNIT and ZoneObject:isExist() then
|
||||
|
||||
@@ -2425,6 +2425,26 @@ do -- SET_UNIT
|
||||
return CountU
|
||||
end
|
||||
|
||||
--- Gets the alive set.
|
||||
-- @param #SET_UNIT self
|
||||
-- @return #table Table of SET objects
|
||||
-- @return #SET_UNIT AliveSet
|
||||
function SET_UNIT:GetAliveSet()
|
||||
|
||||
local AliveSet = SET_UNIT:New()
|
||||
|
||||
-- Clean the Set before returning with only the alive Groups.
|
||||
for GroupName, GroupObject in pairs(self.Set) do
|
||||
local GroupObject=GroupObject --Wrapper.Client#CLIENT
|
||||
|
||||
if GroupObject and GroupObject:IsAlive() then
|
||||
AliveSet:Add(GroupName, GroupObject)
|
||||
end
|
||||
end
|
||||
|
||||
return AliveSet.Set or {}, AliveSet
|
||||
end
|
||||
|
||||
--- [Internal] Private function for use of continous zone filter
|
||||
-- @param #SET_UNIT self
|
||||
-- @return #SET_UNIT self
|
||||
@@ -2819,51 +2839,58 @@ do -- SET_UNIT
|
||||
-- @param #SET_UNIT self
|
||||
-- @return Core.Point#COORDINATE The center coordinate of all the units in the set, including heading in degrees and speed in mps in case of moving units.
|
||||
function SET_UNIT:GetCoordinate()
|
||||
|
||||
local Coordinate = self:GetRandom():GetCoordinate()
|
||||
--self:F({Coordinate:GetVec3()})
|
||||
|
||||
|
||||
local x1 = Coordinate.x
|
||||
local x2 = Coordinate.x
|
||||
local y1 = Coordinate.y
|
||||
local y2 = Coordinate.y
|
||||
local z1 = Coordinate.z
|
||||
local z2 = Coordinate.z
|
||||
local MaxVelocity = 0
|
||||
local AvgHeading = nil
|
||||
local MovingCount = 0
|
||||
|
||||
for UnitName, UnitData in pairs( self:GetSet() ) do
|
||||
|
||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||
local Coordinate = Unit:GetCoordinate()
|
||||
|
||||
x1 = (Coordinate.x < x1) and Coordinate.x or x1
|
||||
x2 = (Coordinate.x > x2) and Coordinate.x or x2
|
||||
y1 = (Coordinate.y < y1) and Coordinate.y or y1
|
||||
y2 = (Coordinate.y > y2) and Coordinate.y or y2
|
||||
z1 = (Coordinate.y < z1) and Coordinate.z or z1
|
||||
z2 = (Coordinate.y > z2) and Coordinate.z or z2
|
||||
|
||||
local Velocity = Coordinate:GetVelocity()
|
||||
if Velocity ~= 0 then
|
||||
MaxVelocity = (MaxVelocity < Velocity) and Velocity or MaxVelocity
|
||||
local Heading = Coordinate:GetHeading()
|
||||
AvgHeading = AvgHeading and (AvgHeading + Heading) or Heading
|
||||
MovingCount = MovingCount + 1
|
||||
end
|
||||
local Coordinate = nil
|
||||
local unit = self:GetRandom()
|
||||
if self:Count() == 1 and unit then
|
||||
return unit:GetCoordinate()
|
||||
end
|
||||
if unit then
|
||||
local Coordinate = unit:GetCoordinate()
|
||||
--self:F({Coordinate:GetVec3()})
|
||||
|
||||
|
||||
local x1 = Coordinate.x
|
||||
local x2 = Coordinate.x
|
||||
local y1 = Coordinate.y
|
||||
local y2 = Coordinate.y
|
||||
local z1 = Coordinate.z
|
||||
local z2 = Coordinate.z
|
||||
local MaxVelocity = 0
|
||||
local AvgHeading = nil
|
||||
local MovingCount = 0
|
||||
|
||||
for UnitName, UnitData in pairs( self:GetAliveSet() ) do
|
||||
|
||||
local Unit = UnitData -- Wrapper.Unit#UNIT
|
||||
local Coordinate = Unit:GetCoordinate()
|
||||
|
||||
x1 = (Coordinate.x < x1) and Coordinate.x or x1
|
||||
x2 = (Coordinate.x > x2) and Coordinate.x or x2
|
||||
y1 = (Coordinate.y < y1) and Coordinate.y or y1
|
||||
y2 = (Coordinate.y > y2) and Coordinate.y or y2
|
||||
z1 = (Coordinate.y < z1) and Coordinate.z or z1
|
||||
z2 = (Coordinate.y > z2) and Coordinate.z or z2
|
||||
|
||||
local Velocity = Coordinate:GetVelocity()
|
||||
if Velocity ~= 0 then
|
||||
MaxVelocity = (MaxVelocity < Velocity) and Velocity or MaxVelocity
|
||||
local Heading = Coordinate:GetHeading()
|
||||
AvgHeading = AvgHeading and (AvgHeading + Heading) or Heading
|
||||
MovingCount = MovingCount + 1
|
||||
end
|
||||
end
|
||||
|
||||
AvgHeading = AvgHeading and (AvgHeading / MovingCount)
|
||||
|
||||
Coordinate.x = (x2 - x1) / 2 + x1
|
||||
Coordinate.y = (y2 - y1) / 2 + y1
|
||||
Coordinate.z = (z2 - z1) / 2 + z1
|
||||
Coordinate:SetHeading( AvgHeading )
|
||||
Coordinate:SetVelocity( MaxVelocity )
|
||||
|
||||
self:F( { Coordinate = Coordinate } )
|
||||
end
|
||||
|
||||
AvgHeading = AvgHeading and (AvgHeading / MovingCount)
|
||||
|
||||
Coordinate.x = (x2 - x1) / 2 + x1
|
||||
Coordinate.y = (y2 - y1) / 2 + y1
|
||||
Coordinate.z = (z2 - z1) / 2 + z1
|
||||
Coordinate:SetHeading( AvgHeading )
|
||||
Coordinate:SetVelocity( MaxVelocity )
|
||||
|
||||
self:F( { Coordinate = Coordinate } )
|
||||
return Coordinate
|
||||
|
||||
end
|
||||
@@ -4317,6 +4344,8 @@ do -- SET_CLIENT
|
||||
self:UnHandleEvent(EVENTS.Birth)
|
||||
self:UnHandleEvent(EVENTS.Dead)
|
||||
self:UnHandleEvent(EVENTS.Crash)
|
||||
--self:UnHandleEvent(EVENTS.PlayerEnterUnit)
|
||||
--self:UnHandleEvent(EVENTS.PlayerLeaveUnit)
|
||||
|
||||
if self.Filter.Zones and self.ZoneTimer and self.ZoneTimer:IsRunning() then
|
||||
self.ZoneTimer:Stop()
|
||||
@@ -4335,6 +4364,9 @@ do -- SET_CLIENT
|
||||
self:HandleEvent( EVENTS.Birth, self._EventOnBirth )
|
||||
self:HandleEvent( EVENTS.Dead, self._EventOnDeadOrCrash )
|
||||
self:HandleEvent( EVENTS.Crash, self._EventOnDeadOrCrash )
|
||||
--self:HandleEvent( EVENTS.PlayerEnterUnit, self._EventPlayerEnterUnit)
|
||||
--self:HandleEvent( EVENTS.PlayerLeaveUnit, self._EventPlayerLeaveUnit)
|
||||
--self:SetEventPriority(1)
|
||||
if self.Filter.Zones then
|
||||
self.ZoneTimer = TIMER:New(self._ContinousZoneFilter,self)
|
||||
local timing = self.ZoneTimerInterval or 30
|
||||
@@ -4345,6 +4377,43 @@ do -- SET_CLIENT
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Handle CA slots addition
|
||||
-- @param #SET_CLIENT self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
-- @return #SET_CLIENT self
|
||||
function SET_CLIENT:_EventPlayerEnterUnit(Event)
|
||||
self:I( "_EventPlayerEnterUnit" )
|
||||
if Event.IniDCSUnit then
|
||||
if Event.IniObjectCategory == 1 and Event.IniGroup and Event.IniGroup:IsGround() then
|
||||
-- CA Slot entered
|
||||
local ObjectName, Object = self:AddInDatabase( Event )
|
||||
self:I( ObjectName, UTILS.PrintTableToLog(Object) )
|
||||
if Object and self:IsIncludeObject( Object ) then
|
||||
self:Add( ObjectName, Object )
|
||||
end
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Handle CA slots removal
|
||||
-- @param #SET_CLIENT self
|
||||
-- @param Core.Event#EVENTDATA Event
|
||||
-- @return #SET_CLIENT self
|
||||
function SET_CLIENT:_EventPlayerLeaveUnit(Event)
|
||||
self:I( "_EventPlayerLeaveUnit" )
|
||||
if Event.IniDCSUnit then
|
||||
if Event.IniObjectCategory == 1 and Event.IniGroup and Event.IniGroup:IsGround() then
|
||||
-- CA Slot left
|
||||
local ObjectName, Object = self:FindInDatabase( Event )
|
||||
if ObjectName then
|
||||
self:Remove( ObjectName )
|
||||
end
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Handles the Database to check on an event (birth) that the Object was added in the Database.
|
||||
-- This is required, because sometimes the _DATABASE birth event gets called later than the SET_BASE birth event!
|
||||
|
||||
@@ -329,14 +329,14 @@ do
|
||||
if self.Lasing then
|
||||
if self.Target and self.Target:IsAlive() then
|
||||
|
||||
self.SpotIR:setPoint( self.Target:GetPointVec3():AddY(1):AddY(math.random(-100,100)/100):AddX(math.random(-100,100)/100):GetVec3() )
|
||||
self.SpotIR:setPoint( self.Target:GetPointVec3():AddY(1):AddY(math.random(-100,100)/200):AddX(math.random(-100,100)/200):GetVec3() )
|
||||
self.SpotLaser:setPoint( self.Target:GetPointVec3():AddY(1):GetVec3() )
|
||||
|
||||
self:__Lasing(0.2)
|
||||
elseif self.TargetCoord then
|
||||
|
||||
-- Wiggle the IR spot a bit.
|
||||
local irvec3={x=self.TargetCoord.x+math.random(-100,100)/100, y=self.TargetCoord.y+math.random(-100,100)/100, z=self.TargetCoord.z} --#DCS.Vec3
|
||||
local irvec3={x=self.TargetCoord.x+math.random(-100,100)/200, y=self.TargetCoord.y+math.random(-100,100)/200, z=self.TargetCoord.z} --#DCS.Vec3
|
||||
local lsvec3={x=self.TargetCoord.x, y=self.TargetCoord.y, z=self.TargetCoord.z} --#DCS.Vec3
|
||||
|
||||
self.SpotIR:setPoint(irvec3)
|
||||
|
||||
@@ -1093,11 +1093,8 @@ function ZONE_RADIUS:Scan( ObjectCategories, UnitCategories )
|
||||
--if ZoneObject:isExist() then --FF: isExist always returns false for SCENERY objects since DCS 2.2 and still in DCS 2.5
|
||||
if ZoneObject then
|
||||
|
||||
local ObjectCategory = ZoneObject:getCategory()
|
||||
|
||||
--local name=ZoneObject:getName()
|
||||
--env.info(string.format("Zone object %s", tostring(name)))
|
||||
--self:E(ZoneObject)
|
||||
-- Get object category.
|
||||
local ObjectCategory = Object.getCategory(ZoneObject)
|
||||
|
||||
if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then
|
||||
|
||||
@@ -2801,7 +2798,7 @@ function ZONE_POLYGON:Scan( ObjectCategories, UnitCategories )
|
||||
|
||||
if ZoneObject then
|
||||
|
||||
local ObjectCategory = ZoneObject:getCategory()
|
||||
local ObjectCategory = Object.getCategory(ZoneObject)
|
||||
|
||||
if ( ObjectCategory == Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() ) or (ObjectCategory == Object.Category.STATIC and ZoneObject:isExist()) then
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
-- @image Designation.JPG
|
||||
--
|
||||
-- Date: 24 Oct 2021
|
||||
-- Last Update: Aug 2022
|
||||
-- Last Update: Oct 2023
|
||||
--
|
||||
--- Class AUTOLASE
|
||||
-- @type AUTOLASE
|
||||
@@ -84,6 +84,9 @@
|
||||
-- @field #string alias
|
||||
-- @field #boolean debug
|
||||
-- @field #string version
|
||||
-- @field Core.Set#SET_GROUP RecceSet
|
||||
-- @field #table LaserCodes
|
||||
-- @field #table playermenus
|
||||
-- @extends Ops.Intel#INTEL
|
||||
|
||||
---
|
||||
@@ -109,9 +112,10 @@ AUTOLASE = {
|
||||
-- @field #string unittype
|
||||
-- @field Core.Point#COORDINATE coordinate
|
||||
|
||||
|
||||
--- AUTOLASE class version.
|
||||
-- @field #string version
|
||||
AUTOLASE.version = "0.1.21"
|
||||
AUTOLASE.version = "0.1.22"
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Begin Functional.Autolase.lua
|
||||
@@ -196,6 +200,8 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet)
|
||||
self.NoMenus = false
|
||||
self.minthreatlevel = 0
|
||||
self.blacklistattributes = {}
|
||||
self:SetLaserCodes( { 1688, 1130, 4785, 6547, 1465, 4578 } ) -- set self.LaserCodes
|
||||
self.playermenus = {}
|
||||
|
||||
-- Set some string id for output to DCS.log file.
|
||||
self.lid=string.format("AUTOLASE %s (%s) | ", self.alias, self.coalition and UTILS.GetCoalitionName(self.coalition) or "unknown")
|
||||
@@ -214,7 +220,7 @@ function AUTOLASE:New(RecceSet, Coalition, Alias, PilotSet)
|
||||
if PilotSet then
|
||||
self.usepilotset = true
|
||||
self.pilotset = PilotSet
|
||||
self:HandleEvent(EVENTS.PlayerEnterAircraft)
|
||||
self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler)
|
||||
--self:SetPilotMenu()
|
||||
end
|
||||
--self.SetPilotMenu()
|
||||
@@ -298,6 +304,16 @@ end
|
||||
-- Helper Functions
|
||||
-------------------------------------------------------------------
|
||||
|
||||
--- [User] Set a table of possible laser codes.
|
||||
-- Each new RECCE can select a code from this table, default is { 1688, 1130, 4785, 6547, 1465, 4578 } .
|
||||
-- @param #AUTOLASE self
|
||||
-- @param #list<#number> LaserCodes
|
||||
-- @return #AUTOLASE
|
||||
function AUTOLASE:SetLaserCodes( LaserCodes )
|
||||
self.LaserCodes = ( type( LaserCodes ) == "table" ) and LaserCodes or { LaserCodes }
|
||||
return self
|
||||
end
|
||||
|
||||
--- (Internal) Function to set pilot menu.
|
||||
-- @param #AUTOLASE self
|
||||
-- @return #AUTOLASE self
|
||||
@@ -308,8 +324,31 @@ function AUTOLASE:SetPilotMenu()
|
||||
local Unit = _unit -- Wrapper.Unit#UNIT
|
||||
if Unit and Unit:IsAlive() then
|
||||
local Group = Unit:GetGroup()
|
||||
local lasemenu = MENU_GROUP_COMMAND:New(Group,"Autolase Status",nil,self.ShowStatus,self,Group,Unit)
|
||||
lasemenu:Refresh()
|
||||
local unitname = Unit:GetName()
|
||||
if self.playermenus[unitname] then self.playermenus[unitname]:Remove() end
|
||||
local lasetopm = MENU_GROUP:New(Group,"Autolase",nil)
|
||||
self.playermenus[unitname] = lasetopm
|
||||
local lasemenu = MENU_GROUP_COMMAND:New(Group,"Status",lasetopm,self.ShowStatus,self,Group,Unit)
|
||||
local smoke = (self.smoketargets == true) and "off" or "on"
|
||||
local smoketext = string.format("Switch smoke targets to %s",smoke)
|
||||
local smokemenu = MENU_GROUP_COMMAND:New(Group,smoketext,lasetopm,self.SetSmokeTargets,self,(not self.smoketargets))
|
||||
for _,_grp in pairs(self.RecceSet.Set) do
|
||||
local grp = _grp -- Wrapper.Group#GROUP
|
||||
local unit = grp:GetUnit(1)
|
||||
--local name = grp:GetName()
|
||||
if unit and unit:IsAlive() then
|
||||
local name = unit:GetName()
|
||||
local mname = string.gsub(name,".%d+.%d+$","")
|
||||
local code = self:GetLaserCode(name)
|
||||
local unittop = MENU_GROUP:New(Group,"Change laser code for "..mname,lasetopm)
|
||||
for _,_code in pairs(self.LaserCodes) do
|
||||
local text = tostring(_code)
|
||||
if _code == code then text = text.."(*)" end
|
||||
local changemenu = MENU_GROUP_COMMAND:New(Group,text,unittop,self.SetRecceLaserCode,self,name,_code,true)
|
||||
end
|
||||
end
|
||||
end
|
||||
--lasemenu:Refresh()
|
||||
end
|
||||
end
|
||||
else
|
||||
@@ -324,7 +363,7 @@ end
|
||||
-- @param #AUTOLASE self
|
||||
-- @param Core.Event#EVENTDATA EventData
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:OnEventPlayerEnterAircraft(EventData)
|
||||
function AUTOLASE:_EventHandler(EventData)
|
||||
self:SetPilotMenu()
|
||||
return self
|
||||
end
|
||||
@@ -397,7 +436,7 @@ end
|
||||
--- (User) Function enable sending messages via SRS.
|
||||
-- @param #AUTOLASE self
|
||||
-- @param #boolean OnOff Switch usage on and off
|
||||
-- @param #string Path Path to SRS directory, e.g. C:\\Program Files\\DCS-SimpleRadio-Standalon
|
||||
-- @param #string Path Path to SRS directory, e.g. C:\\Program Files\\DCS-SimpleRadio-Standalone
|
||||
-- @param #number Frequency Frequency to send, e.g. 243
|
||||
-- @param #number Modulation Modulation i.e. radio.modulation.AM or radio.modulation.FM
|
||||
-- @param #string Label (Optional) Short label to be used on the SRS Client Overlay
|
||||
@@ -465,10 +504,20 @@ end
|
||||
-- @param #AUTOLASE self
|
||||
-- @param #string RecceName (Unit!) Name of the Recce
|
||||
-- @param #number Code The lase code
|
||||
-- @param #boolean Refresh If true, refresh menu entries
|
||||
-- @return #AUTOLASE self
|
||||
function AUTOLASE:SetRecceLaserCode(RecceName, Code)
|
||||
function AUTOLASE:SetRecceLaserCode(RecceName, Code, Refresh)
|
||||
local code = Code or 1688
|
||||
self.RecceLaserCode[RecceName] = code
|
||||
if Refresh then
|
||||
self:SetPilotMenu()
|
||||
if self.notifypilots then
|
||||
if string.find(RecceName,"#") then
|
||||
RecceName = string.match(RecceName,"^(.*)#")
|
||||
end
|
||||
self:NotifyPilots(string.format("Code for %s set to: %d",RecceName,Code),15)
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -524,6 +573,9 @@ end
|
||||
function AUTOLASE:SetSmokeTargets(OnOff,Color)
|
||||
self.smoketargets = OnOff
|
||||
self.smokecolor = Color or SMOKECOLOR.Red
|
||||
local smktxt = OnOff == true and "on" or "off"
|
||||
local Message = "Smoking targets is now "..smktxt.."!"
|
||||
self:NotifyPilots(Message,10)
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -673,7 +725,7 @@ function AUTOLASE:ShowStatus(Group,Unit)
|
||||
if playername then
|
||||
local settings = _DATABASE:GetPlayerSettings(playername)
|
||||
if settings then
|
||||
--self:I("Get Settings ok!")
|
||||
self:I("Get Settings ok!")
|
||||
if settings:IsA2G_MGRS() then
|
||||
locationstring = entry.coordinate:ToStringMGRS(settings)
|
||||
elseif settings:IsA2G_LL_DMS() then
|
||||
@@ -995,6 +1047,9 @@ end
|
||||
function AUTOLASE:onbeforeRecceKIA(From,Event,To,RecceName)
|
||||
self:T({From, Event, To, RecceName})
|
||||
if self.notifypilots or self.debug then
|
||||
if string.find(RecceName,"#") then
|
||||
RecceName = string.match(RecceName,"^(.*)#")
|
||||
end
|
||||
local text = string.format("Recce %s KIA!",RecceName)
|
||||
self:NotifyPilots(text,self.reporttimeshort)
|
||||
end
|
||||
@@ -1029,6 +1084,9 @@ end
|
||||
function AUTOLASE:onbeforeTargetLost(From,Event,To,UnitName,RecceName)
|
||||
self:T({From, Event, To, UnitName,RecceName})
|
||||
if self.notifypilots or self.debug then
|
||||
if string.find(RecceName,"#") then
|
||||
RecceName = string.match(RecceName,"^(.*)#")
|
||||
end
|
||||
local text = string.format("%s lost sight of unit %s.",RecceName,UnitName)
|
||||
self:NotifyPilots(text,self.reporttimeshort)
|
||||
end
|
||||
@@ -1046,6 +1104,9 @@ end
|
||||
function AUTOLASE:onbeforeLaserTimeout(From,Event,To,UnitName,RecceName)
|
||||
self:T({From, Event, To, UnitName,RecceName})
|
||||
if self.notifypilots or self.debug then
|
||||
if string.find(RecceName,"#") then
|
||||
RecceName = string.match(RecceName,"^(.*)#")
|
||||
end
|
||||
local text = string.format("%s laser timeout on unit %s.",RecceName,UnitName)
|
||||
self:NotifyPilots(text,self.reporttimeshort)
|
||||
end
|
||||
@@ -1063,10 +1124,9 @@ function AUTOLASE:onbeforeLasing(From,Event,To,LaserSpot)
|
||||
self:T({From, Event, To, LaserSpot.unittype})
|
||||
if self.notifypilots or self.debug then
|
||||
local laserspot = LaserSpot -- #AUTOLASE.LaserSpot
|
||||
local name = laserspot.reccename
|
||||
if string.find(name,"#") then
|
||||
local name = laserspot.reccename if string.find(name,"#") then
|
||||
name = string.match(name,"^(.*)#")
|
||||
end
|
||||
end
|
||||
local text = string.format("%s is lasing %s code %d\nat %s",name,laserspot.unittype,laserspot.lasercode,laserspot.location)
|
||||
self:NotifyPilots(text,self.reporttimeshort+5)
|
||||
end
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
-- @module Functional.Mantis
|
||||
-- @image Functional.Mantis.jpg
|
||||
--
|
||||
-- Last Update: Sept 2023
|
||||
-- Last Update: Oct 2023
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
--- **MANTIS** class, extends Core.Base#BASE
|
||||
@@ -103,6 +103,7 @@
|
||||
-- * Roland
|
||||
-- * Silkworm (though strictly speaking this is a surface to ship missile)
|
||||
-- * SA-2, SA-3, SA-5, SA-6, SA-7, SA-8, SA-9, SA-10, SA-11, SA-13, SA-15, SA-19
|
||||
-- * From IDF mod: STUNNER IDFA, TAMIR IDFA (Note all caps!)
|
||||
-- * From HDS (see note on HDS below): SA-2, SA-3, SA-10B, SA-10C, SA-12, SA-17, SA-20A, SA-20B, SA-23, HQ-2
|
||||
--
|
||||
-- * From SMA: RBS98M, RBS70, RBS90, RBS90M, RBS103A, RBS103B, RBS103AM, RBS103BM, Lvkv9040M
|
||||
@@ -373,7 +374,9 @@ MANTIS.SamData = {
|
||||
["SA-20A"] = { Range=150, Blindspot=5, Height=27, Type="Long" , Radar="S-300PMU1"},
|
||||
["SA-20B"] = { Range=200, Blindspot=4, Height=27, Type="Long" , Radar="S-300PMU2"},
|
||||
["HQ-2"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
|
||||
["SHORAD"] = { Range=3, Blindspot=0, Height=3, Type="Short", Radar="Igla" }
|
||||
["SHORAD"] = { Range=3, Blindspot=0, Height=3, Type="Short", Radar="Igla" },
|
||||
["TAMIR IDFA"] = { Range=20, Blindspot=0.6, Height=12.3, Type="Short", Radar="IRON_DOME_LN" },
|
||||
["STUNNER IDFA"] = { Range=250, Blindspot=1, Height=45, Type="Long", Radar="DAVID_SLING_LN" },
|
||||
}
|
||||
|
||||
--- SAM data HDS
|
||||
@@ -487,7 +490,11 @@ do
|
||||
-- mybluemantis:Start()
|
||||
--
|
||||
function MANTIS:New(name,samprefix,ewrprefix,hq,coalition,dynamic,awacs, EmOnOff, Padding, Zones)
|
||||
|
||||
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
local self = BASE:Inherit(self, FSM:New()) -- #MANTIS
|
||||
|
||||
-- DONE: Create some user functions for these
|
||||
-- DONE: Make HQ useful
|
||||
-- DONE: Set SAMs to auto if EWR dies
|
||||
@@ -549,6 +556,10 @@ do
|
||||
self.ShoradGroupSet = SET_GROUP:New() -- Core.Set#SET_GROUP
|
||||
self.FilterZones = Zones
|
||||
|
||||
self.SkateZones = nil
|
||||
self.SkateNumber = 3
|
||||
self.shootandscoot = false
|
||||
|
||||
self.UseEmOnOff = true
|
||||
if EmOnOff == false then
|
||||
self.UseEmOnOff = false
|
||||
@@ -560,9 +571,6 @@ do
|
||||
self.advAwacs = false
|
||||
end
|
||||
|
||||
-- Inherit everything from BASE class.
|
||||
local self = BASE:Inherit(self, FSM:New()) -- #MANTIS
|
||||
|
||||
-- Set the string id for output to DCS.log file.
|
||||
self.lid=string.format("MANTIS %s | ", self.name)
|
||||
|
||||
@@ -623,7 +631,7 @@ do
|
||||
|
||||
-- TODO Version
|
||||
-- @field #string version
|
||||
self.version="0.8.14"
|
||||
self.version="0.8.15"
|
||||
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
|
||||
|
||||
--- FSM Functions ---
|
||||
@@ -787,6 +795,19 @@ do
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a SET_ZONE of zones for Shoot&Scoot - SHORAD units will move around
|
||||
-- @param #MANTIS self
|
||||
-- @param Core.Set#SET_ZONE ZoneSet Set of zones to be used. Units will move around to the next (random) zone between 100m and 3000m away.
|
||||
-- @param #number Number Number of closest zones to be considered, defaults to 3.
|
||||
-- @return #MANTIS self
|
||||
function MANTIS:AddScootZones(ZoneSet, Number)
|
||||
self:T(self.lid .. " AddScootZones")
|
||||
self.SkateZones = ZoneSet
|
||||
self.SkateNumber = Number or 3
|
||||
self.shootandscoot = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Function to set accept and reject zones.
|
||||
-- @param #MANTIS self
|
||||
-- @param #table AcceptZones Table of @{Core.Zone#ZONE} objects
|
||||
@@ -1786,6 +1807,10 @@ do
|
||||
self.Shorad:SetDefenseLimits(80,95)
|
||||
self.ShoradLink = true
|
||||
self.Shorad.Groupset=self.ShoradGroupSet
|
||||
self.Shorad.debug = self.debug
|
||||
end
|
||||
if self.shootandscoot and self.SkateZones then
|
||||
self.Shorad:AddScootZones(self.SkateZones,self.SkateNumber or 3)
|
||||
end
|
||||
self:__Status(-math.random(1,10))
|
||||
return self
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
-- @field Sound.SRS#MSRSQUEUE controlsrsQ SRS queue for range controller.
|
||||
-- @field Sound.SRS#MSRS instructmsrs SRS wrapper for range instructor.
|
||||
-- @field Sound.SRS#MSRSQUEUE instructsrsQ SRS queue for range instructor.
|
||||
-- @field #number Coalition Coalition side for the menu, if any.
|
||||
-- @extends Core.Fsm#FSM
|
||||
|
||||
--- *Don't only practice your art, but force your way into its secrets; art deserves that, for it and knowledge can raise man to the Divine.* - Ludwig van Beethoven
|
||||
@@ -355,7 +356,8 @@ RANGE = {
|
||||
targetsheet = nil,
|
||||
targetpath = nil,
|
||||
targetprefix = nil,
|
||||
}
|
||||
Coalition = nil,
|
||||
}
|
||||
|
||||
--- Default range parameters.
|
||||
-- @type RANGE.Defaults
|
||||
@@ -591,7 +593,7 @@ RANGE.MenuF10Root = nil
|
||||
|
||||
--- Range script version.
|
||||
-- @field #string version
|
||||
RANGE.version = "2.7.1"
|
||||
RANGE.version = "2.7.3"
|
||||
|
||||
-- TODO list:
|
||||
-- TODO: Verbosity level for messages.
|
||||
@@ -613,8 +615,9 @@ RANGE.version = "2.7.1"
|
||||
--- RANGE contructor. Creates a new RANGE object.
|
||||
-- @param #RANGE self
|
||||
-- @param #string RangeName Name of the range. Has to be unique. Will we used to create F10 menu items etc.
|
||||
-- @param #number Coalition (optional) Coalition of the range, if any, e.g. coalition.side.BLUE.
|
||||
-- @return #RANGE RANGE object.
|
||||
function RANGE:New( RangeName )
|
||||
function RANGE:New( RangeName, Coalition )
|
||||
|
||||
-- Inherit BASE.
|
||||
local self = BASE:Inherit( self, FSM:New() ) -- #RANGE
|
||||
@@ -622,7 +625,9 @@ function RANGE:New( RangeName )
|
||||
-- Get range name.
|
||||
-- TODO: make sure that the range name is not given twice. This would lead to problems in the F10 radio menu.
|
||||
self.rangename = RangeName or "Practice Range"
|
||||
|
||||
|
||||
self.Coalition = Coalition
|
||||
|
||||
-- Log id.
|
||||
self.lid = string.format( "RANGE %s | ", self.rangename )
|
||||
|
||||
@@ -1745,10 +1750,16 @@ function RANGE:OnEventBirth( EventData )
|
||||
|
||||
-- Reset current strafe status.
|
||||
self.strafeStatus[_uid] = nil
|
||||
|
||||
-- Add Menu commands after a delay of 0.1 seconds.
|
||||
self:ScheduleOnce( 0.1, self._AddF10Commands, self, _unitName )
|
||||
|
||||
|
||||
if self.Coalition then
|
||||
if EventData.IniCoalition == self.Coalition then
|
||||
self:ScheduleOnce( 0.1, self._AddF10Commands, self, _unitName )
|
||||
end
|
||||
else
|
||||
-- Add Menu commands after a delay of 0.1 seconds.
|
||||
self:ScheduleOnce( 0.1, self._AddF10Commands, self, _unitName )
|
||||
end
|
||||
|
||||
-- By default, some bomb impact points and do not flare each hit on target.
|
||||
self.PlayerSettings[_playername] = {} -- #RANGE.PlayerData
|
||||
self.PlayerSettings[_playername].smokebombimpact = self.defaultsmokebomb
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
--
|
||||
-- ### Authors: **FlightControl**, **applevangelist**
|
||||
--
|
||||
-- Last Update: September 2023
|
||||
-- Last Update: Oct 2023
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -34,7 +34,7 @@
|
||||
--
|
||||
-- This class is very easy to use. Just setup a SEAD object by using @{#SEAD.New}() and SAMs will evade and take defensive action when being fired upon.
|
||||
-- Once a HARM attack is detected, SEAD will shut down the radars of the attacked SAM site and take evasive action by moving the SAM
|
||||
-- vehicles around (*if* they are drivable, that is). There's a component of randomness in detection and evasion, which is based on the
|
||||
-- vehicles around (*if* they are driveable, that is). There's a component of randomness in detection and evasion, which is based on the
|
||||
-- skill set of the SAM set (the higher the skill, the more likely). When a missile is fired from far away, the SAM will stay active for a
|
||||
-- period of time to stay defensive, before it takes evasive actions.
|
||||
--
|
||||
@@ -66,7 +66,6 @@ SEAD = {
|
||||
-- @field Harms
|
||||
SEAD.Harms = {
|
||||
["AGM_88"] = "AGM_88",
|
||||
--["AGM_45"] = "AGM_45",
|
||||
["AGM_122"] = "AGM_122",
|
||||
["AGM_84"] = "AGM_84",
|
||||
["AGM_45"] = "AGM_45",
|
||||
@@ -80,6 +79,7 @@ SEAD = {
|
||||
["BGM_109"] = "BGM_109",
|
||||
["AGM_154"] = "AGM_154",
|
||||
["HY-2"] = "HY-2",
|
||||
["ADM_141A"] = "ADM_141A",
|
||||
}
|
||||
|
||||
--- Missile enumerators - from DCS ME and Wikipedia
|
||||
@@ -100,6 +100,7 @@ SEAD = {
|
||||
["BGM_109"] = {460, 0.705}, --in-game ~465kn
|
||||
["AGM_154"] = {130, 0.61},
|
||||
["HY-2"] = {90,1},
|
||||
["ADM_141A"] = {126,0.6},
|
||||
}
|
||||
|
||||
--- Creates the main object which is handling defensive actions for SA sites or moving SA vehicles.
|
||||
@@ -143,7 +144,7 @@ function SEAD:New( SEADGroupPrefixes, Padding )
|
||||
self:AddTransition("*", "ManageEvasion", "*")
|
||||
self:AddTransition("*", "CalculateHitZone", "*")
|
||||
|
||||
self:I("*** SEAD - Started Version 0.4.4")
|
||||
self:I("*** SEAD - Started Version 0.4.5")
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -348,8 +349,9 @@ end
|
||||
-- @param #string SEADWeaponName
|
||||
-- @param Wrapper.Group#GROUP SEADGroup Attacker Group
|
||||
-- @param #number timeoffset Offset for tti calc
|
||||
-- @param Wrapper.Weapon#WEAPON Weapon
|
||||
-- @return #SEAD self
|
||||
function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,timeoffset)
|
||||
function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,timeoffset,Weapon)
|
||||
local timeoffset = timeoffset or 0
|
||||
if _targetskill == "Random" then -- when skill is random, choose a skill
|
||||
local Skills = { "Average", "Good", "High", "Excellent" }
|
||||
@@ -372,6 +374,10 @@ function SEAD:onafterManageEvasion(From,Event,To,_targetskill,_targetgroup,SEADP
|
||||
reach = wpndata[1] * 1.1
|
||||
local mach = wpndata[2]
|
||||
wpnspeed = math.floor(mach * 340.29)
|
||||
if Weapon then
|
||||
wpnspeed = Weapon:GetSpeed()
|
||||
self:T(string.format("*** SEAD - Weapon Speed from WEAPON: %f m/s",wpnspeed))
|
||||
end
|
||||
end
|
||||
-- time to impact
|
||||
local _tti = math.floor(_distance / wpnspeed) - timeoffset -- estimated impact time
|
||||
@@ -457,6 +463,9 @@ function SEAD:HandleEventShot( EventData )
|
||||
local SEADWeapon = EventData.Weapon -- Identify the weapon fired
|
||||
local SEADWeaponName = EventData.WeaponName -- return weapon type
|
||||
|
||||
local WeaponWrapper = WEAPON:New(EventData.Weapon)
|
||||
--local SEADWeaponSpeed = WeaponWrapper:GetSpeed() -- mps
|
||||
|
||||
self:T( "*** SEAD - Missile Launched = " .. SEADWeaponName)
|
||||
--self:T({ SEADWeapon })
|
||||
|
||||
@@ -475,7 +484,7 @@ function SEAD:HandleEventShot( EventData )
|
||||
end
|
||||
return self
|
||||
end
|
||||
local targetcat = _target:getCategory() -- Identify category
|
||||
local targetcat = Object.getCategory(_target) -- Identify category
|
||||
local _targetUnit = nil -- Wrapper.Unit#UNIT
|
||||
local _targetgroup = nil -- Wrapper.Group#GROUP
|
||||
self:T(string.format("*** Targetcat = %d",targetcat))
|
||||
@@ -513,7 +522,11 @@ function SEAD:HandleEventShot( EventData )
|
||||
end
|
||||
end
|
||||
if SEADGroupFound == true then -- yes we are being attacked
|
||||
self:ManageEvasion(_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup)
|
||||
if string.find(SEADWeaponName,"ADM_141",1,true) then
|
||||
self:__ManageEvasion(2,_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,0,WeaponWrapper)
|
||||
else
|
||||
self:ManageEvasion(_targetskill,_targetgroup,SEADPlanePos,SEADWeaponName,SEADGroup,0,WeaponWrapper)
|
||||
end
|
||||
end
|
||||
end
|
||||
return self
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
-- @image Functional.Shorad.jpg
|
||||
--
|
||||
-- Date: Nov 2021
|
||||
-- Last Update: Nov 2023
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
--- **SHORAD** class, extends Core.Base#BASE
|
||||
@@ -39,8 +40,11 @@
|
||||
-- @field #boolean DefendHarms Default true, intercept incoming HARMS
|
||||
-- @field #boolean DefendMavs Default true, intercept incoming AG-Missiles
|
||||
-- @field #number DefenseLowProb Default 70, minimum detection limit
|
||||
-- @field #number DefenseHighProb Default 90, maximim detection limit
|
||||
-- @field #number DefenseHighProb Default 90, maximum detection limit
|
||||
-- @field #boolean UseEmOnOff Decide if we are using Emission on/off (default) or AlarmState red/green.
|
||||
-- @field #boolean shootandscoot
|
||||
-- @field #number SkateNumber
|
||||
-- @field Core.Set#SET_ZONE SkateZones
|
||||
-- @extends Core.Base#BASE
|
||||
|
||||
|
||||
@@ -99,7 +103,10 @@ SHORAD = {
|
||||
DefendMavs = true,
|
||||
DefenseLowProb = 70,
|
||||
DefenseHighProb = 90,
|
||||
UseEmOnOff = false,
|
||||
UseEmOnOff = true,
|
||||
shootandscoot = false,
|
||||
SkateNumber = 3,
|
||||
SkateZones = nil,
|
||||
}
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
@@ -112,7 +119,6 @@ do
|
||||
-- @field Harms
|
||||
SHORAD.Harms = {
|
||||
["AGM_88"] = "AGM_88",
|
||||
["AGM_45"] = "AGM_45",
|
||||
["AGM_122"] = "AGM_122",
|
||||
["AGM_84"] = "AGM_84",
|
||||
["AGM_45"] = "AGM_45",
|
||||
@@ -123,6 +129,8 @@ do
|
||||
["X_25"] = "X_25",
|
||||
["X_31"] = "X_31",
|
||||
["Kh25"] = "Kh25",
|
||||
["HY-2"] = "HY-2",
|
||||
["ADM_141A"] = "ADM_141A",
|
||||
}
|
||||
|
||||
--- TODO complete list?
|
||||
@@ -134,7 +142,6 @@ do
|
||||
["Kh29"] = "Kh29",
|
||||
["Kh31"] = "Kh31",
|
||||
["Kh66"] = "Kh66",
|
||||
--["BGM_109"] = "BGM_109",
|
||||
}
|
||||
|
||||
--- Instantiates a new SHORAD object
|
||||
@@ -146,7 +153,7 @@ do
|
||||
-- @param #number ActiveTimer Determines how many seconds the systems stay on red alert after wake-up call
|
||||
-- @param #string Coalition Coalition, i.e. "blue", "red", or "neutral"
|
||||
-- @param #boolean UseEmOnOff Use Emissions On/Off rather than Alarm State Red/Green (default: use Emissions switch)
|
||||
-- @retunr #SHORAD self
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:New(Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition, UseEmOnOff)
|
||||
local self = BASE:Inherit( self, FSM:New() )
|
||||
self:T({Name, ShoradPrefix, Samset, Radius, ActiveTimer, Coalition})
|
||||
@@ -165,8 +172,9 @@ do
|
||||
self.DefendMavs = true
|
||||
self.DefenseLowProb = 70 -- probability to detect a missile shot, low margin
|
||||
self.DefenseHighProb = 90 -- probability to detect a missile shot, high margin
|
||||
self.UseEmOnOff = UseEmOnOff or false -- Decide if we are using Emission on/off (default) or AlarmState red/green
|
||||
self:I("*** SHORAD - Started Version 0.3.1")
|
||||
self.UseEmOnOff = true -- Decide if we are using Emission on/off (default) or AlarmState red/green
|
||||
if UseEmOnOff == false then self.UseEmOnOff = UseEmOnOff end
|
||||
self:I("*** SHORAD - Started Version 0.3.2")
|
||||
-- Set the string id for output to DCS.log file.
|
||||
self.lid=string.format("SHORAD %s | ", self.name)
|
||||
self:_InitState()
|
||||
@@ -176,12 +184,14 @@ do
|
||||
self:SetStartState("Running")
|
||||
self:AddTransition("*", "WakeUpShorad", "*")
|
||||
self:AddTransition("*", "CalculateHitZone", "*")
|
||||
self:AddTransition("*", "ShootAndScoot", "*")
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
--- Initially set all groups to alarm state GREEN
|
||||
-- @param #SHORAD self
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:_InitState()
|
||||
self:T(self.lid .. " _InitState")
|
||||
local table = {}
|
||||
@@ -205,21 +215,36 @@ do
|
||||
return self
|
||||
end
|
||||
|
||||
--- Add a SET_ZONE of zones for Shoot&Scoot
|
||||
-- @param #SHORAD self
|
||||
-- @param Core.Set#SET_ZONE ZoneSet Set of zones to be used. Units will move around to the next (random) zone between 100m and 3000m away.
|
||||
-- @param #number Number Number of closest zones to be considered, defaults to 3.
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:AddScootZones(ZoneSet, Number)
|
||||
self:T(self.lid .. " AddScootZones")
|
||||
self.SkateZones = ZoneSet
|
||||
self.SkateNumber = Number or 3
|
||||
self.shootandscoot = true
|
||||
return self
|
||||
end
|
||||
|
||||
--- Switch debug state on
|
||||
-- @param #SHORAD self
|
||||
-- @param #boolean debug Switch debug on (true) or off (false)
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:SwitchDebug(onoff)
|
||||
self:T( { onoff } )
|
||||
if onoff then
|
||||
self:SwitchDebugOn()
|
||||
else
|
||||
self.SwitchDebugOff()
|
||||
self:SwitchDebugOff()
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Switch debug state on
|
||||
-- @param #SHORAD self
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:SwitchDebugOn()
|
||||
self.debug = true
|
||||
--tracing
|
||||
@@ -230,6 +255,7 @@ do
|
||||
|
||||
--- Switch debug state off
|
||||
-- @param #SHORAD self
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:SwitchDebugOff()
|
||||
self.debug = false
|
||||
BASE:TraceOff()
|
||||
@@ -239,6 +265,7 @@ do
|
||||
--- Switch defense for HARMs
|
||||
-- @param #SHORAD self
|
||||
-- @param #boolean onoff
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:SwitchHARMDefense(onoff)
|
||||
self:T( { onoff } )
|
||||
local onoff = onoff or true
|
||||
@@ -249,6 +276,7 @@ do
|
||||
--- Switch defense for AGMs
|
||||
-- @param #SHORAD self
|
||||
-- @param #boolean onoff
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:SwitchAGMDefense(onoff)
|
||||
self:T( { onoff } )
|
||||
local onoff = onoff or true
|
||||
@@ -260,6 +288,7 @@ do
|
||||
-- @param #SHORAD self
|
||||
-- @param #number low Minimum detection limit, integer 1-100
|
||||
-- @param #number high Maximum detection limit integer 1-100
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:SetDefenseLimits(low,high)
|
||||
self:T( { low, high } )
|
||||
local low = low or 70
|
||||
@@ -278,6 +307,7 @@ do
|
||||
--- Set the number of seconds a SHORAD site will stay active
|
||||
-- @param #SHORAD self
|
||||
-- @param #number seconds Number of seconds systems stay active
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:SetActiveTimer(seconds)
|
||||
self:T(self.lid .. " SetActiveTimer")
|
||||
local timer = seconds or 600
|
||||
@@ -291,6 +321,7 @@ do
|
||||
--- Set the number of meters for the SHORAD defense zone
|
||||
-- @param #SHORAD self
|
||||
-- @param #number meters Radius of the defense search zone in meters. #SHORADs in this range around a targeted group will go active
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:SetDefenseRadius(meters)
|
||||
self:T(self.lid .. " SetDefenseRadius")
|
||||
local radius = meters or 20000
|
||||
@@ -304,6 +335,7 @@ do
|
||||
--- Set using Emission on/off instead of changing alarm state
|
||||
-- @param #SHORAD self
|
||||
-- @param #boolean switch Decide if we are changing alarm state or AI state
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:SetUsingEmOnOff(switch)
|
||||
self:T(self.lid .. " SetUsingEmOnOff")
|
||||
self.UseEmOnOff = switch or false
|
||||
@@ -375,11 +407,11 @@ do
|
||||
local shorad = self.Groupset
|
||||
local shoradset = shorad:GetAliveSet() --#table
|
||||
local returnname = false
|
||||
--local TDiff = 1
|
||||
for _,_groups in pairs (shoradset) do
|
||||
local groupname = _groups:GetName()
|
||||
if string.find(groupname, tgtgrp, 1, true) then
|
||||
returnname = true
|
||||
--_groups:RelocateGroundRandomInRadius(7,100,false,false) -- be a bit evasive
|
||||
end
|
||||
end
|
||||
return returnname
|
||||
@@ -426,6 +458,7 @@ do
|
||||
-- @param #number Radius Radius of the #ZONE
|
||||
-- @param #number ActiveTimer Number of seconds to stay active
|
||||
-- @param #number TargetCat (optional) Category, i.e. Object.Category.UNIT or Object.Category.STATIC
|
||||
-- @return #SHORAD self
|
||||
-- @usage Use this function to integrate with other systems, example
|
||||
--
|
||||
-- local SamSet = SET_GROUP:New():FilterPrefixes("Blue SAM"):FilterCoalitions("blue"):FilterStart()
|
||||
@@ -452,28 +485,35 @@ do
|
||||
local targetzone = ZONE_RADIUS:New("Shorad",targetvec2,Radius) -- create a defense zone to check
|
||||
local groupset = self.Groupset --Core.Set#SET_GROUP
|
||||
local shoradset = groupset:GetAliveSet() --#table
|
||||
|
||||
-- local function to switch off shorad again
|
||||
local function SleepShorad(group)
|
||||
local groupname = group:GetName()
|
||||
self.ActiveGroups[groupname] = nil
|
||||
if self.UseEmOnOff then
|
||||
group:EnableEmission(false)
|
||||
--group:SetAIOff()
|
||||
else
|
||||
group:OptionAlarmStateGreen()
|
||||
if group and group:IsAlive() then
|
||||
local groupname = group:GetName()
|
||||
self.ActiveGroups[groupname] = nil
|
||||
if self.UseEmOnOff then
|
||||
group:EnableEmission(false)
|
||||
else
|
||||
group:OptionAlarmStateGreen()
|
||||
end
|
||||
local text = string.format("Sleeping SHORAD %s", group:GetName())
|
||||
self:T(text)
|
||||
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
|
||||
--Shoot and Scoot
|
||||
if self.shootandscoot then
|
||||
self:__ShootAndScoot(1,group)
|
||||
end
|
||||
end
|
||||
local text = string.format("Sleeping SHORAD %s", group:GetName())
|
||||
self:T(text)
|
||||
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
|
||||
end
|
||||
|
||||
-- go through set and find the one(s) to activate
|
||||
local TDiff = 4
|
||||
for _,_group in pairs (shoradset) do
|
||||
if _group:IsAnyInZone(targetzone) then
|
||||
local text = string.format("Waking up SHORAD %s", _group:GetName())
|
||||
self:T(text)
|
||||
local m = MESSAGE:New(text,10,"SHORAD"):ToAllIf(self.debug)
|
||||
if self.UseEmOnOff then
|
||||
--_group:SetAIOn()
|
||||
_group:EnableEmission(true)
|
||||
end
|
||||
_group:OptionAlarmStateRed()
|
||||
@@ -481,91 +521,128 @@ do
|
||||
if self.ActiveGroups[groupname] == nil then -- no timer yet for this group
|
||||
self.ActiveGroups[groupname] = { Timing = ActiveTimer }
|
||||
local endtime = timer.getTime() + (ActiveTimer * math.random(75,100) / 100 ) -- randomize wakeup a bit
|
||||
timer.scheduleFunction(SleepShorad, _group, endtime)
|
||||
self.ActiveGroups[groupname].Timer = TIMER:New(SleepShorad,_group):Start(endtime)
|
||||
--Shoot and Scoot
|
||||
if self.shootandscoot then
|
||||
self:__ShootAndScoot(TDiff,_group)
|
||||
TDiff=TDiff+1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- (Internal) Calculate hit zone of an AGM-88
|
||||
-- @param #SHORAD self
|
||||
-- @param #table SEADWeapon DCS.Weapon object
|
||||
-- @param Core.Point#COORDINATE pos0 Position of the plane when it fired
|
||||
-- @param #number height Height when the missile was fired
|
||||
-- @param Wrapper.Group#GROUP SEADGroup Attacker group
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup)
|
||||
self:T("**** Calculating hit zone")
|
||||
if SEADWeapon and SEADWeapon:isExist() then
|
||||
--local pos = SEADWeapon:getPoint()
|
||||
--- (Internal) Calculate hit zone of an AGM-88
|
||||
-- @param #SHORAD self
|
||||
-- @param #table SEADWeapon DCS.Weapon object
|
||||
-- @param Core.Point#COORDINATE pos0 Position of the plane when it fired
|
||||
-- @param #number height Height when the missile was fired
|
||||
-- @param Wrapper.Group#GROUP SEADGroup Attacker group
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADGroup)
|
||||
self:T("**** Calculating hit zone")
|
||||
if SEADWeapon and SEADWeapon:isExist() then
|
||||
--local pos = SEADWeapon:getPoint()
|
||||
|
||||
-- postion and height
|
||||
local position = SEADWeapon:getPosition()
|
||||
local mheight = height
|
||||
-- heading
|
||||
local wph = math.atan2(position.x.z, position.x.x)
|
||||
if wph < 0 then
|
||||
wph=wph+2*math.pi
|
||||
end
|
||||
wph=math.deg(wph)
|
||||
|
||||
-- velocity
|
||||
local wpndata = SEAD.HarmData["AGM_88"]
|
||||
local mveloc = math.floor(wpndata[2] * 340.29)
|
||||
local c1 = (2*mheight*9.81)/(mveloc^2)
|
||||
local c2 = (mveloc^2) / 9.81
|
||||
local Ropt = c2 * math.sqrt(c1+1)
|
||||
if height <= 5000 then
|
||||
Ropt = Ropt * 0.72
|
||||
elseif height <= 7500 then
|
||||
Ropt = Ropt * 0.82
|
||||
elseif height <= 10000 then
|
||||
Ropt = Ropt * 0.87
|
||||
elseif height <= 12500 then
|
||||
Ropt = Ropt * 0.98
|
||||
end
|
||||
|
||||
-- look at a couple of zones across the trajectory
|
||||
for n=1,3 do
|
||||
local dist = Ropt - ((n-1)*20000)
|
||||
local predpos= pos0:Translate(dist,wph)
|
||||
if predpos then
|
||||
|
||||
-- postion and height
|
||||
local position = SEADWeapon:getPosition()
|
||||
local mheight = height
|
||||
-- heading
|
||||
local wph = math.atan2(position.x.z, position.x.x)
|
||||
if wph < 0 then
|
||||
wph=wph+2*math.pi
|
||||
end
|
||||
wph=math.deg(wph)
|
||||
|
||||
-- velocity
|
||||
local wpndata = SEAD.HarmData["AGM_88"]
|
||||
local mveloc = math.floor(wpndata[2] * 340.29)
|
||||
local c1 = (2*mheight*9.81)/(mveloc^2)
|
||||
local c2 = (mveloc^2) / 9.81
|
||||
local Ropt = c2 * math.sqrt(c1+1)
|
||||
if height <= 5000 then
|
||||
Ropt = Ropt * 0.72
|
||||
elseif height <= 7500 then
|
||||
Ropt = Ropt * 0.82
|
||||
elseif height <= 10000 then
|
||||
Ropt = Ropt * 0.87
|
||||
elseif height <= 12500 then
|
||||
Ropt = Ropt * 0.98
|
||||
local targetzone = ZONE_RADIUS:New("Target Zone",predpos:GetVec2(),20000)
|
||||
|
||||
if self.debug then
|
||||
predpos:MarkToAll(string.format("height=%dm | heading=%d | velocity=%ddeg | Ropt=%dm",mheight,wph,mveloc,Ropt),false)
|
||||
targetzone:DrawZone(coalition.side.BLUE,{0,0,1},0.2,nil,nil,3,true)
|
||||
end
|
||||
|
||||
local seadset = self.Groupset
|
||||
local tgtcoord = targetzone:GetRandomPointVec2()
|
||||
local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord)
|
||||
local _targetgroup = nil
|
||||
local _targetgroupname = "none"
|
||||
local _targetskill = "Random"
|
||||
if tgtgrp and tgtgrp:IsAlive() then
|
||||
_targetgroup = tgtgrp
|
||||
_targetgroupname = tgtgrp:GetName() -- group name
|
||||
_targetskill = tgtgrp:GetUnit(1):GetSkill()
|
||||
self:T("*** Found Target = ".. _targetgroupname)
|
||||
self:WakeUpShorad(_targetgroupname, self.Radius, self.ActiveTimer, Object.Category.UNIT)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- look at a couple of zones across the trajectory
|
||||
for n=1,3 do
|
||||
local dist = Ropt - ((n-1)*20000)
|
||||
local predpos= pos0:Translate(dist,wph)
|
||||
if predpos then
|
||||
return self
|
||||
end
|
||||
|
||||
local targetzone = ZONE_RADIUS:New("Target Zone",predpos:GetVec2(),20000)
|
||||
|
||||
if self.debug then
|
||||
predpos:MarkToAll(string.format("height=%dm | heading=%d | velocity=%ddeg | Ropt=%dm",mheight,wph,mveloc,Ropt),false)
|
||||
targetzone:DrawZone(coalition.side.BLUE,{0,0,1},0.2,nil,nil,3,true)
|
||||
end
|
||||
|
||||
local seadset = self.Groupset
|
||||
local tgtcoord = targetzone:GetRandomPointVec2()
|
||||
local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord)
|
||||
local _targetgroup = nil
|
||||
local _targetgroupname = "none"
|
||||
local _targetskill = "Random"
|
||||
if tgtgrp and tgtgrp:IsAlive() then
|
||||
_targetgroup = tgtgrp
|
||||
_targetgroupname = tgtgrp:GetName() -- group name
|
||||
_targetskill = tgtgrp:GetUnit(1):GetSkill()
|
||||
self:T("*** Found Target = ".. _targetgroupname)
|
||||
self:WakeUpShorad(_targetgroupname, self.Radius, self.ActiveTimer, Object.Category.UNIT)
|
||||
--- (Internal) Shoot and Scoot
|
||||
-- @param #SHORAD self
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Group#GROUP Shorad Shorad group
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:onafterShootAndScoot(From,Event,To,Shorad)
|
||||
self:T( { From,Event,To } )
|
||||
local possibleZones = {}
|
||||
local mindist = 100
|
||||
local maxdist = 3000
|
||||
if Shorad and Shorad:IsAlive() then
|
||||
local NowCoord = Shorad:GetCoordinate()
|
||||
for _,_zone in pairs(self.SkateZones.Set) do
|
||||
local zone = _zone -- Core.Zone#ZONE_RADIUS
|
||||
local dist = NowCoord:Get2DDistance(zone:GetCoordinate())
|
||||
if dist >= mindist and dist <= maxdist then
|
||||
possibleZones[#possibleZones+1] = zone
|
||||
if #possibleZones == self.SkateNumber then break end
|
||||
end
|
||||
end
|
||||
end
|
||||
if #possibleZones > 0 and Shorad:GetVelocityKMH() < 2 then
|
||||
local rand = math.floor(math.random(1,#possibleZones*1000)/1000+0.5)
|
||||
if rand == 0 then rand = 1 end
|
||||
self:T(self.lid .. " ShootAndScoot to zone "..rand)
|
||||
local ToCoordinate = possibleZones[rand]:GetCoordinate()
|
||||
Shorad:RouteGroundTo(ToCoordinate,20,"Cone",1)
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
--- Main function - work on the EventData
|
||||
-- @param #SHORAD self
|
||||
-- @param Core.Event#EVENTDATA EventData The event details table data set
|
||||
-- @return #SHORAD self
|
||||
function SHORAD:HandleEventShot( EventData )
|
||||
self:T( { EventData } )
|
||||
self:T(self.lid .. " HandleEventShot")
|
||||
--local ShootingUnit = EventData.IniDCSUnit
|
||||
--local ShootingUnitName = EventData.IniDCSUnitName
|
||||
local ShootingWeapon = EventData.Weapon -- Identify the weapon fired
|
||||
local ShootingWeaponName = EventData.WeaponName -- return weapon type
|
||||
-- get firing coalition
|
||||
@@ -596,27 +673,18 @@ end
|
||||
return self
|
||||
end
|
||||
|
||||
local targetcat = targetdata:getCategory() -- Identify category
|
||||
local targetcat = Object.getCategory(targetdata) -- Identify category
|
||||
self:T(string.format("Target Category (3=STATIC, 1=UNIT)= %s",tostring(targetcat)))
|
||||
self:T({targetdata})
|
||||
local targetunit = nil
|
||||
if targetcat == Object.Category.UNIT then -- UNIT
|
||||
targetunit = UNIT:Find(targetdata)
|
||||
elseif targetcat == Object.Category.STATIC then -- STATIC
|
||||
--self:T("Static Target Data")
|
||||
--self:T({targetdata:isExist()})
|
||||
--self:T({targetdata:getPoint()})
|
||||
local tgtcoord = COORDINATE:NewFromVec3(targetdata:getPoint())
|
||||
--tgtcoord:MarkToAll("Missile Target",true)
|
||||
|
||||
local tgtgrp1 = self.Samset:FindNearestGroupFromPointVec2(tgtcoord)
|
||||
local tgtgrp1 = self.Samset:FindNearestGroupFromPointVec2(tgtcoord)
|
||||
local tgtcoord1 = tgtgrp1:GetCoordinate()
|
||||
--tgtcoord1:MarkToAll("Close target SAM",true)
|
||||
|
||||
local tgtgrp2 = self.Groupset:FindNearestGroupFromPointVec2(tgtcoord)
|
||||
local tgtcoord2 = tgtgrp2:GetCoordinate()
|
||||
--tgtcoord2:MarkToAll("Close target SHORAD",true)
|
||||
|
||||
local dist1 = tgtcoord:Get2DDistance(tgtcoord1)
|
||||
local dist2 = tgtcoord:Get2DDistance(tgtcoord2)
|
||||
|
||||
@@ -628,10 +696,8 @@ end
|
||||
targetcat = Object.Category.UNIT
|
||||
end
|
||||
end
|
||||
--local targetunitname = Unit.getName(targetdata) -- Unit name
|
||||
if targetunit and targetunit:IsAlive() then
|
||||
local targetunitname = targetunit:GetName()
|
||||
--local targetgroup = Unit.getGroup(Weapon.getTarget(ShootingWeapon)) --targeted group
|
||||
local targetgroup = nil
|
||||
local targetgroupname = "none"
|
||||
if targetcat == Object.Category.UNIT then
|
||||
@@ -649,7 +715,6 @@ end
|
||||
self:T( text )
|
||||
local m = MESSAGE:New(text,10,"Info"):ToAllIf(self.debug)
|
||||
-- check if we or a SAM site are the target
|
||||
--local TargetGroup = EventData.TgtGroup -- Wrapper.Group#GROUP
|
||||
local shotatus = self:_CheckShotAtShorad(targetgroupname) --#boolean
|
||||
local shotatsams = self:_CheckShotAtSams(targetgroupname) --#boolean
|
||||
-- if being shot at, find closest SHORADs to activate
|
||||
|
||||
@@ -2512,7 +2512,7 @@ function ATIS:onafterBroadcast( From, Event, To )
|
||||
|
||||
if not self.ATISforFARPs then
|
||||
-- Active runway.
|
||||
local subtitle
|
||||
local subtitle = ""
|
||||
if runwayLanding then
|
||||
local actrun = self.gettext:GetEntry("ACTIVELANDING",self.locale)
|
||||
--subtitle=string.format("Active runway landing %s", runwayLanding)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **applevangelist**
|
||||
-- @date Last Update July 2023
|
||||
-- @date Last Update Nov 2023
|
||||
-- @module Ops.AWACS
|
||||
-- @image OPS_AWACS.jpg
|
||||
|
||||
@@ -507,7 +507,7 @@ do
|
||||
-- @field #AWACS
|
||||
AWACS = {
|
||||
ClassName = "AWACS", -- #string
|
||||
version = "0.2.58", -- #string
|
||||
version = "0.2.59", -- #string
|
||||
lid = "", -- #string
|
||||
coalition = coalition.side.BLUE, -- #number
|
||||
coalitiontxt = "blue", -- #string
|
||||
@@ -2988,7 +2988,7 @@ function AWACS:_Picture(Group,IsGeneral)
|
||||
|
||||
if clustersAO == 0 and clustersEWR == 0 then
|
||||
-- clean
|
||||
self:_NewRadioEntry(text,textScreen,GID,Outcome,true,true,false)
|
||||
self:_NewRadioEntry(text,text,GID,Outcome,true,true,false)
|
||||
else
|
||||
|
||||
if clustersAO > 0 then
|
||||
|
||||
@@ -329,7 +329,7 @@ FLIGHTCONTROL.FlightStatus={
|
||||
|
||||
--- FlightControl class version.
|
||||
-- @field #string version
|
||||
FLIGHTCONTROL.version="0.7.4"
|
||||
FLIGHTCONTROL.version="0.7.5"
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- TODO list
|
||||
@@ -4441,11 +4441,21 @@ function FLIGHTCONTROL:SpawnParkingGuard(unit)
|
||||
-- Length of the unit + 3 meters.
|
||||
local size, x, y, z=unit:GetObjectSize()
|
||||
|
||||
local xdiff = 3
|
||||
--Fix for hangars, puts the guy out front and not on top.
|
||||
if AIRBASE._CheckTerminalType(spot.TerminalType, AIRBASE.TerminalType.Shelter) then
|
||||
xdiff = 27-(x*0.5)
|
||||
end
|
||||
|
||||
if (AIRBASE._CheckTerminalType(spot.TerminalType, AIRBASE.TerminalType.OpenMed) or AIRBASE._CheckTerminalType(spot.TerminalType, AIRBASE.TerminalType.Shelter)) and self.airbasename == AIRBASE.Sinai.Ramon_Airbase then
|
||||
xdiff = 12
|
||||
end
|
||||
|
||||
-- Debug message.
|
||||
self:T2(self.lid..string.format("Parking guard for %s: heading=%d, distance x=%.1f m", unit:GetName(), heading, x))
|
||||
self:T2(self.lid..string.format("Parking guard for %s: heading=%d, length x=%.1f m, xdiff=%.1f m", unit:GetName(), heading, x, xdiff))
|
||||
|
||||
-- Coordinate for the guard.
|
||||
local Coordinate=coordinate:Translate(0.75*x+3, heading)
|
||||
local Coordinate=coordinate:Translate(x*0.5+xdiff, heading)
|
||||
|
||||
-- Let him face the aircraft.
|
||||
local lookat=heading-180
|
||||
|
||||
@@ -1025,7 +1025,7 @@ function OPSZONE:Scan()
|
||||
if ZoneObject then
|
||||
|
||||
-- Object category.
|
||||
local ObjectCategory=ZoneObject:getCategory()
|
||||
local ObjectCategory=Object.getCategory(ZoneObject)
|
||||
|
||||
if ObjectCategory==Object.Category.UNIT and ZoneObject:isExist() and ZoneObject:isActive() then
|
||||
|
||||
|
||||
@@ -104,7 +104,7 @@ PLAYERRECCE = {
|
||||
ClassName = "PLAYERRECCE",
|
||||
verbose = true,
|
||||
lid = nil,
|
||||
version = "0.0.19",
|
||||
version = "0.0.21",
|
||||
ViewZone = {},
|
||||
ViewZoneVisual = {},
|
||||
ViewZoneLaser = {},
|
||||
@@ -268,6 +268,146 @@ function PLAYERRECCE:New(Name, Coalition, PlayerSet)
|
||||
|
||||
self:I(self.lid.." Started.")
|
||||
|
||||
------------------------
|
||||
--- Pseudo Functions ---
|
||||
------------------------
|
||||
|
||||
--- Triggers the FSM event "Start". Starts the PLAYERRECCE. Note: Start() is called automatically after New().
|
||||
-- @function [parent=#PLAYERRECCE] Start
|
||||
-- @param #PLAYERRECCE self
|
||||
|
||||
--- Triggers the FSM event "Start" after a delay. Starts the PLAYERRECCE. Note: Start() is called automatically after New().
|
||||
-- @function [parent=#PLAYERRECCE] __Start
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
--- Triggers the FSM event "Stop". Stops the PLAYERRECCE and all its event handlers.
|
||||
-- @param #PLAYERRECCE self
|
||||
|
||||
--- Triggers the FSM event "Stop" after a delay. Stops the PLAYERRECCE and all its event handlers.
|
||||
-- @function [parent=#PLAYERRECCE] __Stop
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param #number delay Delay in seconds.
|
||||
|
||||
--- FSM Function OnAfterRecceOnStation. Recce came on station.
|
||||
-- @function [parent=#PLAYERRECCE] OnAfterRecceOnStation
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param #string From State.
|
||||
-- @param #string Event Trigger.
|
||||
-- @param #string To State.
|
||||
-- @param Wrapper.Client#CLIENT Client
|
||||
-- @param #string Playername
|
||||
-- @return #PLAYERRECCE self
|
||||
|
||||
--- FSM Function OnAfterRecceOffStation. Recce went off duty.
|
||||
-- @function [parent=#PLAYERRECCE] OnAfterRecceOffStation
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param #string From State.
|
||||
-- @param #string Event Trigger.
|
||||
-- @param #string To State.
|
||||
-- @param Wrapper.Client#CLIENT Client
|
||||
-- @param #string Playername
|
||||
-- @return #PLAYERRECCE self
|
||||
|
||||
--- FSM Function OnAfterTargetDetected. Targets detected.
|
||||
-- @function [parent=#PLAYERRECCE] OnAfterTargetDetected
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param #string From State.
|
||||
-- @param #string Event Trigger.
|
||||
-- @param #string To State.
|
||||
-- @param #table Targetsbyclock #table with index 1..12 containing a #table of Wrapper.Unit#UNIT objects each.
|
||||
-- @param Wrapper.Client#CLIENT Client
|
||||
-- @param #string Playername
|
||||
-- @return #PLAYERRECCE self
|
||||
|
||||
--- FSM Function OnAfterTargetsSmoked. Smoke grenade shot.
|
||||
-- @function [parent=#PLAYERRECCE] OnAfterTargetsSmoked
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param #string From State.
|
||||
-- @param #string Event Trigger.
|
||||
-- @param #string To State.
|
||||
-- @param Wrapper.Client#CLIENT Client
|
||||
-- @param #string Playername
|
||||
-- @param Core.Set#SET_UNIT TargetSet
|
||||
-- @return #PLAYERRECCE self
|
||||
|
||||
--- FSM Function OnAfterTargetsFlared. Flares shot.
|
||||
-- @function [parent=#PLAYERRECCE] OnAfterTargetsFlared
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param #string From State.
|
||||
-- @param #string Event Trigger.
|
||||
-- @param #string To State.
|
||||
-- @param Wrapper.Client#CLIENT Client
|
||||
-- @param #string Playername
|
||||
-- @param Core.Set#SET_UNIT TargetSet
|
||||
-- @return #PLAYERRECCE self
|
||||
|
||||
--- FSM Function OnAfterIllumination. Illumination rocket shot.
|
||||
-- @function [parent=#PLAYERRECCE] OnAfterIllumination
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param #string From State.
|
||||
-- @param #string Event Trigger.
|
||||
-- @param #string To State.
|
||||
-- @param Wrapper.Client#CLIENT Client
|
||||
-- @param #string Playername
|
||||
-- @param Core.Set#SET_UNIT TargetSet
|
||||
-- @return #PLAYERRECCE self
|
||||
|
||||
--- FSM Function OnAfterTargetLasing. Lasing a new target.
|
||||
-- @function [parent=#PLAYERRECCE] OnAfterTargetLasing
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param #string From State.
|
||||
-- @param #string Event Trigger.
|
||||
-- @param #string To State.
|
||||
-- @param Wrapper.Client#CLIENT Client
|
||||
-- @param Wrapper.Unit#UNIT Target
|
||||
-- @param #number Lasercode
|
||||
-- @param #number Lasingtime
|
||||
-- @return #PLAYERRECCE self
|
||||
|
||||
--- FSM Function OnAfterTargetLOSLost. Lost LOS on lased target.
|
||||
-- @function [parent=#PLAYERRECCE] OnAfterTargetLOSLost
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param #string From State.
|
||||
-- @param #string Event Trigger.
|
||||
-- @param #string To State.
|
||||
-- @param Wrapper.Client#CLIENT Client
|
||||
-- @param Wrapper.Unit#UNIT Target
|
||||
-- @return #PLAYERRECCE self
|
||||
|
||||
--- FSM Function OnAfterTargetReport. Laser target report sent.
|
||||
-- @function [parent=#PLAYERRECCE] OnAfterTargetReport
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param #string From State.
|
||||
-- @param #string Event Trigger.
|
||||
-- @param #string To State.
|
||||
-- @param Wrapper.Client#CLIENT Client
|
||||
-- @param Core.Set#SET_UNIT TargetSet
|
||||
-- @param Wrapper.Unit#UNIT Target Target currently lased
|
||||
-- @param #string Text
|
||||
-- @return #PLAYERRECCE self
|
||||
|
||||
--- FSM Function OnAfterTargetReportSent. All targets report sent.
|
||||
-- @function [parent=#PLAYERRECCE] OnAfterTargetReportSent
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param #string From State.
|
||||
-- @param #string Event Trigger.
|
||||
-- @param #string To State.
|
||||
-- @param Wrapper.Client#CLIENT Client Client sending the report
|
||||
-- @param #string Playername Player name
|
||||
-- @param Core.Set#SET_UNIT TargetSet Set of targets
|
||||
-- @return #PLAYERRECCE self
|
||||
|
||||
--- FSM Function OnAfterShack. Lased target has been destroyed.
|
||||
-- @function [parent=#PLAYERRECCE] OnAfterShack
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param #string From State.
|
||||
-- @param #string Event Trigger.
|
||||
-- @param #string To State.
|
||||
-- @param Wrapper.Client#CLIENT Client
|
||||
-- @param Wrapper.Unit#UNIT Target The destroyed target (if obtainable)
|
||||
-- @return #PLAYERRECCE self
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -346,7 +486,7 @@ function PLAYERRECCE:_GetClockDirection(unit, target)
|
||||
end
|
||||
|
||||
--- [User] Set a table of possible laser codes.
|
||||
-- Each new RECCE can select a code from this table, default is 1688.
|
||||
-- Each new RECCE can select a code from this table, default is { 1688, 1130, 4785, 6547, 1465, 4578 }.
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param #list<#number> LaserCodes
|
||||
-- @return #PLAYERRECCE
|
||||
@@ -399,7 +539,7 @@ end
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param Wrapper.Client#CLIENT client
|
||||
-- @param #string playername
|
||||
-- @return #boolen OnOff
|
||||
-- @return #boolean OnOff
|
||||
function PLAYERRECCE:_CameraOn(client,playername)
|
||||
local camera = true
|
||||
local unit = client -- Wrapper.Unit#UNIT
|
||||
@@ -430,26 +570,31 @@ function PLAYERRECCE:_GetGazelleVivianneSight(Gazelle)
|
||||
local unit = Gazelle -- Wrapper.Unit#UNIT
|
||||
if unit and unit:IsAlive() then
|
||||
local dcsunit = Unit.getByName(Gazelle:GetName())
|
||||
local vivihorizontal = dcsunit:getDrawArgumentValue(215) or 0 -- (not in MiniGun) 1 to -1 -- zero is straight ahead, 1/-1 = 180 deg
|
||||
local vivihorizontal = dcsunit:getDrawArgumentValue(215) or 0 -- (not in MiniGun) 1 to -1 -- zero is straight ahead, 1/-1 = 180 deg,
|
||||
local vivivertical = dcsunit:getDrawArgumentValue(216) or 0 -- L/Mistral/Minigun model has no 216, ca 10deg up (=1) and down (=-1)
|
||||
-- vertical model limits 1.53846, -1.10731
|
||||
local vivioff = false
|
||||
-- -1 = -180, 1 = 180
|
||||
-- Actual view -0,66 to 0,66
|
||||
-- Nick view -0,98 to 0,98 for +/- 30°
|
||||
if vivihorizontal < -0.7 then
|
||||
vivihorizontal = -0.7
|
||||
vivioff = true
|
||||
return 0,0,0,false
|
||||
elseif vivihorizontal > 0.7 then
|
||||
vivihorizontal = 0.7
|
||||
-- Actual model view -0,66 to 0,66
|
||||
-- Nick view 1.53846, -1.10731 for - 30° to +45°
|
||||
if vivihorizontal < -0.67 then -- model end
|
||||
vivihorizontal = -0.67
|
||||
vivioff = false
|
||||
--return 0,0,0,false
|
||||
elseif vivihorizontal > 0.67 then -- vivi off
|
||||
vivihorizontal = 0.67
|
||||
vivioff = true
|
||||
return 0,0,0,false
|
||||
end
|
||||
vivivertical = vivivertical / 1.10731 -- normalize
|
||||
local horizontalview = vivihorizontal * -180
|
||||
local verticalview = vivivertical * -30 -- ca +/- 30°
|
||||
local verticalview = vivivertical * 30 -- ca +/- 30°
|
||||
--self:I(string.format("vivihorizontal=%.5f | vivivertical=%.5f",vivihorizontal,vivivertical))
|
||||
--self:I(string.format("horizontal=%.5f | vertical=%.5f",horizontalview,verticalview))
|
||||
local heading = unit:GetHeading()
|
||||
local viviheading = (heading+horizontalview)%360
|
||||
local maxview = self:_GetActualMaxLOSight(unit,viviheading, verticalview,vivioff)
|
||||
--self:I(string.format("maxview=%.5f",maxview))
|
||||
-- visual skew
|
||||
local factor = 3.15
|
||||
self.GazelleViewFactors = {
|
||||
@@ -469,16 +614,18 @@ function PLAYERRECCE:_GetGazelleVivianneSight(Gazelle)
|
||||
}
|
||||
local lfac = UTILS.Round(maxview,-2)
|
||||
if lfac <= 1300 then
|
||||
factor = self.GazelleViewFactors[lfac/100]
|
||||
--factor = self.GazelleViewFactors[lfac/100]
|
||||
factor = 3.15
|
||||
maxview = math.ceil((maxview*factor)/100)*100
|
||||
end
|
||||
if maxview > 8000 then maxview = 8000 end
|
||||
--self:I(string.format("corrected maxview=%.5f",maxview))
|
||||
return viviheading, verticalview,maxview, not vivioff
|
||||
end
|
||||
return 0,0,0,false
|
||||
end
|
||||
|
||||
--- [Internal] Get the max line of sight based on unit head and camera nod via trigonometrie. Returns 0 if camera is off.
|
||||
--- [Internal] Get the max line of sight based on unit head and camera nod via trigonometry. Returns 0 if camera is off.
|
||||
-- @param #PLAYERRECCE self
|
||||
-- @param Wrapper.Unit#UNIT unit The unit which LOS we want
|
||||
-- @param #number vheading Heading where the unit or camera is looking
|
||||
@@ -488,12 +635,13 @@ end
|
||||
function PLAYERRECCE:_GetActualMaxLOSight(unit,vheading, vnod, vivoff)
|
||||
self:T(self.lid.."_GetActualMaxLOSight")
|
||||
if vivoff then return 0 end
|
||||
--if vnod < -0.03 then vnod = -0.03 end
|
||||
local maxview = 0
|
||||
if unit and unit:IsAlive() then
|
||||
local typename = unit:GetTypeName()
|
||||
maxview = self.MaxViewDistance[typename] or 8000
|
||||
maxview = self.MaxViewDistance[typename] or 8000
|
||||
local CamHeight = self.Cameraheight[typename] or 0
|
||||
if vnod > 0 then
|
||||
if vnod < 0 then
|
||||
-- Looking down
|
||||
-- determine max distance we're looking at
|
||||
local beta = 90
|
||||
@@ -505,7 +653,7 @@ function PLAYERRECCE:_GetActualMaxLOSight(unit,vheading, vnod, vivoff)
|
||||
maxview = c*1.2 -- +20%
|
||||
end
|
||||
end
|
||||
return maxview
|
||||
return math.abs(maxview)
|
||||
end
|
||||
|
||||
--- [User] Set callsign options for TTS output. See @{Wrapper.Group#GROUP.GetCustomCallSign}() on how to set customized callsigns.
|
||||
@@ -768,7 +916,8 @@ function PLAYERRECCE:_LaseTarget(client,targetset)
|
||||
-- still looking at target?
|
||||
local target=self.LaserTarget[playername] -- Ops.Target#TARGET
|
||||
local oldtarget = target:GetObject() --or laser.Target
|
||||
self:T("Targetstate: "..target:GetState())
|
||||
--self:I("Targetstate: "..target:GetState())
|
||||
--self:I("Laser State: "..tostring(laser:IsLasing()))
|
||||
if not oldtarget or targetset:IsNotInSet(oldtarget) or target:IsDead() or target:IsDestroyed() then
|
||||
-- lost LOS or dead
|
||||
laser:LaseOff()
|
||||
@@ -780,12 +929,20 @@ function PLAYERRECCE:_LaseTarget(client,targetset)
|
||||
self.LaserTarget[playername] = nil
|
||||
end
|
||||
end
|
||||
if oldtarget and (not laser:IsLasing()) then
|
||||
--self:I("Switching laser back on ..")
|
||||
local lasercode = self.UnitLaserCodes[playername] or laser.LaserCode or 1688
|
||||
local lasingtime = self.lasingtime or 60
|
||||
--local targettype = target:GetTypeName()
|
||||
laser:LaseOn(oldtarget,lasercode,lasingtime)
|
||||
--self:__TargetLasing(-1,client,oldtarget,lasercode,lasingtime)
|
||||
end
|
||||
elseif not laser:IsLasing() and target then
|
||||
local relativecam = self.LaserRelativePos[client:GetTypeName()]
|
||||
laser:SetRelativeStartPosition(relativecam)
|
||||
local lasercode = self.UnitLaserCodes[playername] or laser.LaserCode or 1688
|
||||
local lasingtime = self.lasingtime or 60
|
||||
local targettype = target:GetTypeName()
|
||||
--local targettype = target:GetTypeName()
|
||||
laser:LaseOn(target,lasercode,lasingtime)
|
||||
self.LaserTarget[playername] = TARGET:New(target)
|
||||
self.LaserTarget[playername].TStatus = 9
|
||||
@@ -953,9 +1110,13 @@ function PLAYERRECCE:_SmokeTargets(client,group,playername)
|
||||
end
|
||||
end
|
||||
|
||||
local coordinate = nil
|
||||
local setthreat = 0
|
||||
-- smoke everything else
|
||||
local coordinate = cameraset:GetCoordinate()
|
||||
local setthreat = cameraset:CalculateThreatLevelA2G()
|
||||
if cameraset:CountAlive() > 1 then
|
||||
local coordinate = cameraset:GetCoordinate()
|
||||
local setthreat = cameraset:CalculateThreatLevelA2G()
|
||||
end
|
||||
|
||||
if coordinate then
|
||||
local color = lowsmoke
|
||||
@@ -1582,7 +1743,7 @@ end
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param #table Targetsbyclock
|
||||
-- @param #table Targetsbyclock. #table with index 1..12 containing a #table of Wrapper.Unit#UNIT objects each.
|
||||
-- @param Wrapper.Client#CLIENT Client
|
||||
-- @param #string Playername
|
||||
-- @return #PLAYERRECCE self
|
||||
@@ -1835,7 +1996,7 @@ function PLAYERRECCE:onafterTargetLasing(From, Event, To, Client, Target, Laserc
|
||||
coordtext = coord:ToStringFromRPShort(self.ReferencePoint,self.RPName,client,Settings)
|
||||
end
|
||||
local coordtext = coord:ToStringA2G(client,Settings)
|
||||
local text = string.format("All stations, %s lasing %s\nat %s!\nCode %d, Duration %d seconds!",callsign, targettype, coordtext, Lasercode, Lasingtime)
|
||||
local text = string.format("All stations, %s lasing %s\nat %s!\nCode %d, Duration %d plus seconds!",callsign, targettype, coordtext, Lasercode, Lasingtime)
|
||||
MESSAGE:New(text,15,self.Name or "FACA"):ToClient(client)
|
||||
end
|
||||
end
|
||||
@@ -1862,9 +2023,8 @@ end
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Client#CLIENT Client
|
||||
-- @param Wrapper.Unit#UNIT Target
|
||||
-- @param #string Targettype
|
||||
-- @return #PLAYERRECCE self
|
||||
function PLAYERRECCE:onafterShack(From, Event, To, Client, Target, Targettype)
|
||||
function PLAYERRECCE:onafterShack(From, Event, To, Client, Target)
|
||||
self:T({From, Event, To})
|
||||
local callsign = Client:GetGroup():GetCustomCallSign(self.ShortCallsign,self.Keepnumber,self.CallsignTranslations)
|
||||
local Settings = ( Client and _DATABASE:GetPlayerSettings( Client:GetPlayerName() ) ) or _SETTINGS
|
||||
@@ -1972,7 +2132,6 @@ function PLAYERRECCE:onafterTargetReport(From, Event, To, Client, TargetSet, Tar
|
||||
end
|
||||
end
|
||||
end
|
||||
--self:__TargetReportSent(-2,Client, TargetSet, Target, Text)
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -1981,12 +2140,11 @@ end
|
||||
-- @param #string From
|
||||
-- @param #string Event
|
||||
-- @param #string To
|
||||
-- @param Wrapper.Client#CLIENT Client
|
||||
-- @param Core.Set#SET_UNIT TargetSet
|
||||
-- @param Wrapper.Unit#UNIT Target
|
||||
-- @param #string Text
|
||||
-- @param Wrapper.Client#CLIENT Client Client sending the report
|
||||
-- @param #string Playername Player name
|
||||
-- @param Core.Set#SET_UNIT TargetSet Set of targets
|
||||
-- @return #PLAYERRECCE self
|
||||
function PLAYERRECCE:onafterTargetReportSent(From, Event, To, Client, TargetSet)
|
||||
function PLAYERRECCE:onafterTargetReportSent(From, Event, To, Client, Playername, TargetSet)
|
||||
self:T({From, Event, To})
|
||||
local text = "Upload completed!"
|
||||
if self.UseSRS then
|
||||
|
||||
@@ -1552,7 +1552,7 @@ PLAYERTASKCONTROLLER.Messages = {
|
||||
|
||||
--- PLAYERTASK class version.
|
||||
-- @field #string version
|
||||
PLAYERTASKCONTROLLER.version="0.1.62"
|
||||
PLAYERTASKCONTROLLER.version="0.1.63"
|
||||
|
||||
--- Create and run a new TASKCONTROLLER instance.
|
||||
-- @param #PLAYERTASKCONTROLLER self
|
||||
@@ -1585,7 +1585,7 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter)
|
||||
self.TaskQueue = FIFO:New() -- Utilities.FiFo#FIFO
|
||||
self.TasksPerPlayer = FIFO:New() -- Utilities.FiFo#FIFO
|
||||
self.PrecisionTasks = FIFO:New() -- Utilities.FiFo#FIFO
|
||||
self.PlayerMenu = {} -- #table
|
||||
--self.PlayerMenu = {} -- #table
|
||||
self.FlashPlayer = {} -- #table
|
||||
self.AllowFlash = false
|
||||
self.lasttaskcount = 0
|
||||
@@ -2175,10 +2175,10 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData)
|
||||
if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then
|
||||
if EventData.IniPlayerName then
|
||||
self:T(self.lid.."Event for player: "..EventData.IniPlayerName)
|
||||
if self.PlayerMenu[EventData.IniPlayerName] then
|
||||
self.PlayerMenu[EventData.IniPlayerName]:Remove()
|
||||
self.PlayerMenu[EventData.IniPlayerName] = nil
|
||||
end
|
||||
--if self.PlayerMenu[EventData.IniPlayerName] then
|
||||
--self.PlayerMenu[EventData.IniPlayerName]:Remove()
|
||||
--self.PlayerMenu[EventData.IniPlayerName] = nil
|
||||
--end
|
||||
local text = ""
|
||||
if self.TasksPerPlayer:HasUniqueID(EventData.IniPlayerName) then
|
||||
local task = self.TasksPerPlayer:PullByID(EventData.IniPlayerName) -- Ops.PlayerTask#PLAYERTASK
|
||||
@@ -2187,6 +2187,8 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData)
|
||||
task:RemoveClient(Client)
|
||||
--text = "Task aborted!"
|
||||
text = self.gettext:GetEntry("TASKABORT",self.locale)
|
||||
self.ActiveTaskMenuTemplate:ResetMenu(Client)
|
||||
self.JoinTaskMenuTemplate:ResetMenu(Client)
|
||||
else
|
||||
task:RemoveClient(nil,EventData.IniPlayerName)
|
||||
--text = "Task aborted!"
|
||||
@@ -2236,8 +2238,8 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData)
|
||||
self.SRSQueue:NewTransmission(text,nil,self.SRS,timer.getAbsTime()+60,2,{EventData.IniGroup},text,30,self.BCFrequency,self.BCModulation)
|
||||
end
|
||||
if EventData.IniPlayerName then
|
||||
self.PlayerMenu[EventData.IniPlayerName] = nil
|
||||
local player = CLIENT:FindByName(EventData.IniUnitName)
|
||||
--self.PlayerMenu[EventData.IniPlayerName] = nil
|
||||
local player = _DATABASE:FindClient( EventData.IniUnitName )
|
||||
self:_SwitchMenuForClient(player,"Info")
|
||||
end
|
||||
end
|
||||
@@ -2949,7 +2951,7 @@ function PLAYERTASKCONTROLLER:_AddTask(Target)
|
||||
task:HandleEvent(EVENTS.Shot)
|
||||
function task:OnEventShot(EventData)
|
||||
local data = EventData -- Core.Event#EVENTDATA EventData
|
||||
local wcat = data.Weapon:getCategory() -- cat 2 or 3
|
||||
local wcat = Object.getCategory(data.Weapon) -- cat 2 or 3
|
||||
local coord = data.IniUnit:GetCoordinate() or data.IniGroup:GetCoordinate()
|
||||
local vec2 = coord:GetVec2() or {x=0, y=0}
|
||||
local coal = data.IniCoalition
|
||||
|
||||
@@ -1243,7 +1243,7 @@ function MSRS:LoadConfigFile(Path,Filename,ConfigLoaded)
|
||||
self.gender = MSRS_Config.Gender or "male"
|
||||
self.google = MSRS_Config.Google
|
||||
self.Label = MSRS_Config.Label or "MSRS"
|
||||
self.voice = MSRS_Config.Voice or MSRS.Voices.Microsoft.Hazel
|
||||
self.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel
|
||||
if MSRS_Config.GRPC then
|
||||
self.provider = MSRS_Config.GRPC.DefaultProvider
|
||||
if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then
|
||||
@@ -1267,7 +1267,7 @@ function MSRS:LoadConfigFile(Path,Filename,ConfigLoaded)
|
||||
MSRS.gender = MSRS_Config.Gender or "male"
|
||||
MSRS.google = MSRS_Config.Google
|
||||
MSRS.Label = MSRS_Config.Label or "MSRS"
|
||||
MSRS.voice = MSRS_Config.Voice or MSRS.Voices.Microsoft.Hazel
|
||||
MSRS.voice = MSRS_Config.Voice --or MSRS.Voices.Microsoft.Hazel
|
||||
if MSRS_Config.GRPC then
|
||||
MSRS.provider = MSRS_Config.GRPC.DefaultProvider
|
||||
if MSRS_Config.GRPC[MSRS_Config.GRPC.DefaultProvider] then
|
||||
@@ -1677,7 +1677,7 @@ end
|
||||
-- @param #MSRSQUEUE self
|
||||
-- @return #MSRSQUEUE self The MSRSQUEUE object.
|
||||
function MSRSQUEUE:Clear()
|
||||
self:I(self.lid.."Clearning MSRSQUEUE")
|
||||
self:I(self.lid.."Clearing MSRSQUEUE")
|
||||
self.queue={}
|
||||
return self
|
||||
end
|
||||
@@ -1778,7 +1778,7 @@ function MSRSQUEUE:NewTransmission(text, duration, msrs, tstart, interval, subgr
|
||||
transmission.gender = gender
|
||||
transmission.culture = culture
|
||||
transmission.voice = voice
|
||||
transmission.gender = volume
|
||||
transmission.volume = volume
|
||||
transmission.label = label
|
||||
transmission.coordinate = coordinate
|
||||
|
||||
@@ -1792,7 +1792,7 @@ end
|
||||
-- @param #MSRSQUEUE self
|
||||
-- @param #MSRSQUEUE.Transmission transmission The transmission.
|
||||
function MSRSQUEUE:Broadcast(transmission)
|
||||
|
||||
|
||||
if transmission.frequency then
|
||||
transmission.msrs:PlayTextExt(transmission.text, nil, transmission.frequency, transmission.modulation, transmission.gender, transmission.culture, transmission.voice, transmission.volume, transmission.label, transmission.coordinate)
|
||||
else
|
||||
|
||||
@@ -929,7 +929,7 @@ end
|
||||
function AIRBASE:GetWarehouse()
|
||||
local warehouse=nil --DCS#Warehouse
|
||||
local airbase=self:GetDCSObject()
|
||||
if airbase then
|
||||
if airbase and Airbase.getWarehouse then
|
||||
warehouse=airbase:getWarehouse()
|
||||
end
|
||||
return warehouse
|
||||
@@ -1773,7 +1773,7 @@ function AIRBASE:_CheckParkingLists(TerminalID)
|
||||
end
|
||||
|
||||
--- Helper function to check for the correct terminal type including "artificial" ones.
|
||||
-- @param #number Term_Type Termial type from getParking routine.
|
||||
-- @param #number Term_Type Terminal type from getParking routine.
|
||||
-- @param #AIRBASE.TerminalType termtype Terminal type from AIRBASE.TerminalType enumerator.
|
||||
-- @return #boolean True if terminal types match.
|
||||
function AIRBASE._CheckTerminalType(Term_Type, termtype)
|
||||
|
||||
@@ -323,7 +323,7 @@ function CLIENT:Alive( CallBackFunction, ... )
|
||||
return self
|
||||
end
|
||||
|
||||
--- @param #CLIENT self
|
||||
-- @param #CLIENT self
|
||||
function CLIENT:_AliveCheckScheduler( SchedulerName )
|
||||
self:F3( { SchedulerName, self.ClientName, self.ClientAlive2, self.ClientBriefingShown, self.ClientCallBack } )
|
||||
|
||||
@@ -531,8 +531,6 @@ function CLIENT:ShowCargo()
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
--- The main message driver for the CLIENT.
|
||||
-- This function displays various messages to the Player logged into the CLIENT through the DCS World Messaging system.
|
||||
-- @param #CLIENT self
|
||||
@@ -578,3 +576,36 @@ function CLIENT:Message( Message, MessageDuration, MessageCategory, MessageInter
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- [Multi-Player Server] Get UCID from a CLIENT.
|
||||
-- @param #CLIENT self
|
||||
-- @return #string UCID
|
||||
function CLIENT:GetUCID()
|
||||
local PID = NET.GetPlayerIDByName(nil,self:GetPlayerName())
|
||||
return net.get_player_info(tonumber(PID), 'ucid')
|
||||
end
|
||||
|
||||
--- [Multi-Player Server] Return a table of attributes from CLIENT. If optional attribute is present, only that value is returned.
|
||||
-- @param #CLIENT self
|
||||
-- @param #string Attribute (Optional) The attribute to obtain. List see below.
|
||||
-- @return #table PlayerInfo or nil if it cannot be found
|
||||
-- @usage
|
||||
-- Returned table holds these attributes:
|
||||
--
|
||||
-- 'id' : player ID
|
||||
-- 'name' : player name
|
||||
-- 'side' : 0 - spectators, 1 - red, 2 - blue
|
||||
-- 'slot' : slot ID 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
|
||||
--
|
||||
function CLIENT:GetPlayerInfo(Attribute)
|
||||
local PID = NET.GetPlayerIDByName(nil,self:GetPlayerName())
|
||||
if PID then
|
||||
return net.get_player_info(tonumber(PID), Attribute)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@
|
||||
-- ### Contributions:
|
||||
--
|
||||
-- * **Entropy**, **Afinegan**: Came up with the requirement for AIOnOff().
|
||||
--
|
||||
-- * **Applevangelist**: various
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
-- @module Wrapper.Group
|
||||
@@ -45,12 +46,16 @@
|
||||
|
||||
--- Wrapper class of the DCS world Group object.
|
||||
--
|
||||
-- ## Finding groups
|
||||
--
|
||||
-- The GROUP class provides the following functions to retrieve quickly the relevant GROUP instance:
|
||||
--
|
||||
-- * @{#GROUP.Find}(): Find a GROUP instance from the global _DATABASE object (an instance of @{Core.Database#DATABASE}) using a DCS Group object.
|
||||
-- * @{#GROUP.FindByName}(): Find a GROUP instance from the global _DATABASE object (an instance of @{Core.Database#DATABASE}) using a DCS Group name.
|
||||
-- * @{#GROUP.FindByMatching}(): Find a GROUP instance from the global _DATABASE object (an instance of @{Core.Database#DATABASE}) using pattern matching.
|
||||
-- * @{#GROUP.FindAllByMatching}(): Find all GROUP instances from the global _DATABASE object (an instance of @{Core.Database#DATABASE}) using pattern matching.
|
||||
--
|
||||
-- # 1. Tasking of groups
|
||||
-- ## Tasking of groups
|
||||
--
|
||||
-- A GROUP is derived from the wrapper class CONTROLLABLE (@{Wrapper.Controllable#CONTROLLABLE}).
|
||||
-- See the @{Wrapper.Controllable} task methods section for a description of the task methods.
|
||||
@@ -285,7 +290,7 @@ function GROUP:Find( DCSGroup )
|
||||
return GroupFound
|
||||
end
|
||||
|
||||
--- Find the created GROUP using the DCS Group Name.
|
||||
--- Find a GROUP using the DCS Group Name.
|
||||
-- @param #GROUP self
|
||||
-- @param #string GroupName The DCS Group Name.
|
||||
-- @return #GROUP The GROUP.
|
||||
@@ -295,6 +300,55 @@ function GROUP:FindByName( GroupName )
|
||||
return GroupFound
|
||||
end
|
||||
|
||||
--- Find the first(!) GROUP matching using patterns. Note that this is **a lot** slower than `:FindByName()`!
|
||||
-- @param #GROUP self
|
||||
-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_(Regular_Expressions)) for regular expressions in LUA.
|
||||
-- @return #GROUP The GROUP.
|
||||
-- @usage
|
||||
-- -- Find a group with a partial group name
|
||||
-- local grp = GROUP:FindByMatching( "Apple" )
|
||||
-- -- will return e.g. a group named "Apple-1-1"
|
||||
--
|
||||
-- -- using a pattern
|
||||
-- local grp = GROUP:FindByMatching( ".%d.%d$" )
|
||||
-- -- will return the first group found ending in "-1-1" to "-9-9", but not e.g. "-10-1"
|
||||
function GROUP:FindByMatching( Pattern )
|
||||
local GroupFound = nil
|
||||
|
||||
for name,group in pairs(_DATABASE.GROUPS) do
|
||||
if string.match(name, Pattern ) then
|
||||
GroupFound = group
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return GroupFound
|
||||
end
|
||||
|
||||
--- Find all GROUP objects matching using patterns. Note that this is **a lot** slower than `:FindByName()`!
|
||||
-- @param #GROUP self
|
||||
-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_(Regular_Expressions)) for regular expressions in LUA.
|
||||
-- @return #table Groups Table of matching #GROUP objects found
|
||||
-- @usage
|
||||
-- -- Find all group with a partial group name
|
||||
-- local grptable = GROUP:FindAllByMatching( "Apple" )
|
||||
-- -- will return all groups with "Apple" in the name
|
||||
--
|
||||
-- -- using a pattern
|
||||
-- local grp = GROUP:FindAllByMatching( ".%d.%d$" )
|
||||
-- -- will return the all groups found ending in "-1-1" to "-9-9", but not e.g. "-10-1" or "-1-10"
|
||||
function GROUP:FindAllByMatching( Pattern )
|
||||
local GroupsFound = {}
|
||||
|
||||
for name,group in pairs(_DATABASE.GROUPS) do
|
||||
if string.match(name, Pattern ) then
|
||||
GroupsFound[#GroupsFound+1] = group
|
||||
end
|
||||
end
|
||||
|
||||
return GroupsFound
|
||||
end
|
||||
|
||||
-- DCS Group methods support.
|
||||
|
||||
--- Returns the DCS Group.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
-- ===
|
||||
--
|
||||
-- ### Author: **Applevangelist**
|
||||
-- # Last Update June 2023
|
||||
-- # Last Update Oct 2023
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -43,7 +43,7 @@ do
|
||||
-- @field #NET
|
||||
NET = {
|
||||
ClassName = "NET",
|
||||
Version = "0.1.2",
|
||||
Version = "0.1.3",
|
||||
BlockTime = 600,
|
||||
BlockedPilots = {},
|
||||
BlockedUCIDs = {},
|
||||
@@ -470,11 +470,9 @@ end
|
||||
-- @return #number PlayerID or nil
|
||||
function NET:GetPlayerIDByName(Name)
|
||||
if not Name then return nil end
|
||||
local playerList = self:GetPlayerList()
|
||||
self:T({playerList})
|
||||
local playerList = net.get_player_list()
|
||||
for i=1,#playerList do
|
||||
local playerName = net.get_name(i)
|
||||
self:T({playerName})
|
||||
if playerName == Name then
|
||||
return playerList[i]
|
||||
end
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ### Contributions:
|
||||
-- ### Contributions: **funkyfranky
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -12,12 +12,13 @@
|
||||
-- @image MOOSE.JPG
|
||||
|
||||
|
||||
--- @type OBJECT
|
||||
--- OBJECT class.
|
||||
-- @type OBJECT
|
||||
-- @extends Core.Base#BASE
|
||||
-- @field #string ObjectName The name of the Object.
|
||||
|
||||
|
||||
--- Wrapper class to hendle the DCS Object objects.
|
||||
--- Wrapper class to handle the DCS Object objects.
|
||||
--
|
||||
-- * Support all DCS Object APIs.
|
||||
-- * Enhance with Object specific APIs not in the DCS Object API set.
|
||||
@@ -43,9 +44,15 @@ OBJECT = {
|
||||
-- @param #OBJECT self
|
||||
-- @param DCS#Object ObjectName The Object name
|
||||
-- @return #OBJECT self
|
||||
function OBJECT:New( ObjectName, Test )
|
||||
function OBJECT:New( ObjectName)
|
||||
|
||||
-- Inherit BASE class.
|
||||
local self = BASE:Inherit( self, BASE:New() )
|
||||
|
||||
-- Debug output.
|
||||
self:F2( ObjectName )
|
||||
|
||||
-- Set object name.
|
||||
self.ObjectName = ObjectName
|
||||
|
||||
return self
|
||||
@@ -64,15 +71,14 @@ function OBJECT:GetID()
|
||||
return ObjectID
|
||||
end
|
||||
|
||||
BASE:E( { "Cannot GetID", Name = self.ObjectName, Class = self:GetClassName() } )
|
||||
self:E( { "Cannot GetID", Name = self.ObjectName, Class = self:GetClassName() } )
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Destroys the OBJECT.
|
||||
-- @param #OBJECT self
|
||||
-- @return #boolean true if the object is destroyed.
|
||||
-- @return #nil The DCS Unit is not existing or alive.
|
||||
-- @return #boolean Returns `true` if the object is destroyed or #nil if the object is nil.
|
||||
function OBJECT:Destroy()
|
||||
|
||||
local DCSObject = self:GetDCSObject()
|
||||
@@ -83,7 +89,7 @@ function OBJECT:Destroy()
|
||||
return true
|
||||
end
|
||||
|
||||
BASE:E( { "Cannot Destroy", Name = self.ObjectName, Class = self:GetClassName() } )
|
||||
self:E( { "Cannot Destroy", Name = self.ObjectName, Class = self:GetClassName() } )
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
@@ -168,8 +168,10 @@ function STORAGE:New(AirbaseName)
|
||||
local self=BASE:Inherit(self, BASE:New()) -- #STORAGE
|
||||
|
||||
self.airbase=Airbase.getByName(AirbaseName)
|
||||
|
||||
self.warehouse=self.airbase:getWarehouse()
|
||||
|
||||
if Airbase.getWarehouse then
|
||||
self.warehouse=self.airbase:getWarehouse()
|
||||
end
|
||||
|
||||
self.lid = string.format("STORAGE %s", AirbaseName)
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
--
|
||||
-- ### Author: **FlightControl**
|
||||
--
|
||||
-- ### Contributions: **funkyfranky**
|
||||
-- ### Contributions: **funkyfranky**, **Applevangelist**
|
||||
--
|
||||
-- ===
|
||||
--
|
||||
@@ -42,6 +42,8 @@
|
||||
--
|
||||
-- * @{#UNIT.Find}(): Find a UNIT instance from the global _DATABASE object (an instance of @{Core.Database#DATABASE}) using a DCS Unit object.
|
||||
-- * @{#UNIT.FindByName}(): Find a UNIT instance from the global _DATABASE object (an instance of @{Core.Database#DATABASE}) using a DCS Unit name.
|
||||
-- * @{#UNIT.FindByMatching}(): Find a UNIT instance from the global _DATABASE object (an instance of @{Core.Database#DATABASE}) using a pattern.
|
||||
-- * @{#UNIT.FindAllByMatching}(): Find all UNIT instances from the global _DATABASE object (an instance of @{Core.Database#DATABASE}) using a pattern.
|
||||
--
|
||||
-- IMPORTANT: ONE SHOULD NEVER SANITIZE these UNIT OBJECT REFERENCES! (make the UNIT object references nil).
|
||||
--
|
||||
@@ -160,6 +162,55 @@ function UNIT:FindByName( UnitName )
|
||||
return UnitFound
|
||||
end
|
||||
|
||||
--- Find the first(!) UNIT matching using patterns. Note that this is **a lot** slower than `:FindByName()`!
|
||||
-- @param #UNIT self
|
||||
-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_(Regular_Expressions)) for regular expressions in LUA.
|
||||
-- @return #UNIT The UNIT.
|
||||
-- @usage
|
||||
-- -- Find a group with a partial group name
|
||||
-- local unit = UNIT:FindByMatching( "Apple" )
|
||||
-- -- will return e.g. a group named "Apple-1-1"
|
||||
--
|
||||
-- -- using a pattern
|
||||
-- local unit = UNIT:FindByMatching( ".%d.%d$" )
|
||||
-- -- will return the first group found ending in "-1-1" to "-9-9", but not e.g. "-10-1"
|
||||
function UNIT:FindByMatching( Pattern )
|
||||
local GroupFound = nil
|
||||
|
||||
for name,group in pairs(_DATABASE.UNITS) do
|
||||
if string.match(name, Pattern ) then
|
||||
GroupFound = group
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
return GroupFound
|
||||
end
|
||||
|
||||
--- Find all UNIT objects matching using patterns. Note that this is **a lot** slower than `:FindByName()`!
|
||||
-- @param #UNIT self
|
||||
-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_(Regular_Expressions)) for regular expressions in LUA.
|
||||
-- @return #table Units Table of matching #UNIT objects found
|
||||
-- @usage
|
||||
-- -- Find all group with a partial group name
|
||||
-- local unittable = UNIT:FindAllByMatching( "Apple" )
|
||||
-- -- will return all units with "Apple" in the name
|
||||
--
|
||||
-- -- using a pattern
|
||||
-- local unittable = UNIT:FindAllByMatching( ".%d.%d$" )
|
||||
-- -- will return the all units found ending in "-1-1" to "-9-9", but not e.g. "-10-1" or "-1-10"
|
||||
function UNIT:FindAllByMatching( Pattern )
|
||||
local GroupsFound = {}
|
||||
|
||||
for name,group in pairs(_DATABASE.UNITS) do
|
||||
if string.match(name, Pattern ) then
|
||||
GroupsFound[#GroupsFound+1] = group
|
||||
end
|
||||
end
|
||||
|
||||
return GroupsFound
|
||||
end
|
||||
|
||||
--- Return the name of the UNIT.
|
||||
-- @param #UNIT self
|
||||
-- @return #string The UNIT name.
|
||||
|
||||
@@ -367,7 +367,7 @@ function WEAPON:GetTarget()
|
||||
if object then
|
||||
|
||||
-- Get object category.
|
||||
local category=object:getCategory()
|
||||
local category=Object.getCategory(object)
|
||||
|
||||
--Target name
|
||||
local name=object:getName()
|
||||
|
||||
Reference in New Issue
Block a user