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:
commit
a7857670d3
785
Moose Development/Moose/Core/ClientMenu.lua
Normal file
785
Moose Development/Moose/Core/ClientMenu.lua
Normal file
@ -0,0 +1,785 @@
|
|||||||
|
--- **Core** - Client Menu Management.
|
||||||
|
--
|
||||||
|
-- **Main Features:**
|
||||||
|
--
|
||||||
|
-- * For complex, non-static menu structures
|
||||||
|
-- * Lightweigt implementation as alternative to MENU
|
||||||
|
-- * Separation of menu tree creation from menu on the clients's side
|
||||||
|
-- * Works with a SET_CLIENT set of clients
|
||||||
|
-- * Allow manipulation of the shadow tree in various ways
|
||||||
|
-- * Push to all or only one client
|
||||||
|
-- * Change entries' menu text
|
||||||
|
-- * Option to make an entry usable once only across all clients
|
||||||
|
-- * Auto appends GROUP and CLIENT objects to menu calls
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ### Author: **applevangelist**
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- @module Core.ClientMenu
|
||||||
|
-- @image Core_Menu.JPG
|
||||||
|
-- last change: July 2023
|
||||||
|
|
||||||
|
-- TODO
|
||||||
|
----------------------------------------------------------------------------------------------------------------
|
||||||
|
--
|
||||||
|
-- CLIENTMENU
|
||||||
|
--
|
||||||
|
----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @type CLIENTMENU
|
||||||
|
-- @field #string ClassName Class Name
|
||||||
|
-- @field #string lid Lid for log entries
|
||||||
|
-- @field #string version Version string
|
||||||
|
-- @field #string name Name
|
||||||
|
-- @field #table path
|
||||||
|
-- @field #table parentpath
|
||||||
|
-- @field #CLIENTMENU Parent
|
||||||
|
-- @field Wrapper.Client#CLIENT client
|
||||||
|
-- @field #number GroupID Group ID
|
||||||
|
-- @field #number ID Entry ID
|
||||||
|
-- @field Wrapper.Group#GROUP group
|
||||||
|
-- @field #string UUID Unique ID based on path+name
|
||||||
|
-- @field #string Function
|
||||||
|
-- @field #table Functionargs
|
||||||
|
-- @field #table Children
|
||||||
|
-- @field #boolean Once
|
||||||
|
-- @field #boolean Generic
|
||||||
|
-- @field #boolean debug
|
||||||
|
-- @field #CLIENTMENUMANAGER Controller
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @field #CLIENTMENU
|
||||||
|
CLIENTMENU = {
|
||||||
|
ClassName = "CLIENTMENUE",
|
||||||
|
lid = "",
|
||||||
|
version = "0.1.0",
|
||||||
|
name = nil,
|
||||||
|
path = nil,
|
||||||
|
group = nil,
|
||||||
|
client = nil,
|
||||||
|
GroupID = nil,
|
||||||
|
Children = {},
|
||||||
|
Once = false,
|
||||||
|
Generic = false,
|
||||||
|
debug = false,
|
||||||
|
Controller = nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @field #CLIENTMENU_ID
|
||||||
|
CLIENTMENU_ID = 0
|
||||||
|
|
||||||
|
--- Create an new CLIENTMENU object.
|
||||||
|
-- @param #CLIENTMENU self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client for whom this entry is.
|
||||||
|
-- @param #string Text Text of the F10 menu entry.
|
||||||
|
-- @param #CLIENTMENU Parent The parent menu entry.
|
||||||
|
-- @param #string Function (optional) Function to call when the entry is used.
|
||||||
|
-- @param ... (optional) Arguments for the Function, comma separated
|
||||||
|
-- @return #CLIENTMENU self
|
||||||
|
function CLIENTMENU:NewEntry(Client,Text,Parent,Function,...)
|
||||||
|
-- Inherit everything from BASE class.
|
||||||
|
local self=BASE:Inherit(self, BASE:New()) -- #CLIENTMENU
|
||||||
|
CLIENTMENU_ID = CLIENTMENU_ID + 1
|
||||||
|
self.ID = CLIENTMENU_ID
|
||||||
|
if Client then
|
||||||
|
self.group = Client:GetGroup()
|
||||||
|
self.client = Client
|
||||||
|
self.GroupID = self.group:GetID()
|
||||||
|
else
|
||||||
|
self.Generic = true
|
||||||
|
end
|
||||||
|
self.name = Text or "unknown entry"
|
||||||
|
if Parent then
|
||||||
|
if Parent:IsInstanceOf("MENU_BASE") then
|
||||||
|
self.parentpath = Parent.MenuPath
|
||||||
|
else
|
||||||
|
self.parentpath = Parent:GetPath()
|
||||||
|
Parent:AddChild(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.Parent = Parent
|
||||||
|
self.Function = Function
|
||||||
|
self.Functionargs = arg or {}
|
||||||
|
table.insert(self.Functionargs,self.group)
|
||||||
|
table.insert(self.Functionargs,self.client)
|
||||||
|
if self.Functionargs and self.debug then
|
||||||
|
self:T({"Functionargs",self.Functionargs})
|
||||||
|
end
|
||||||
|
if not self.Generic then
|
||||||
|
if Function ~= nil then
|
||||||
|
local ErrorHandler = function( errmsg )
|
||||||
|
env.info( "MOOSE Error in CLIENTMENU COMMAND function: " .. errmsg )
|
||||||
|
if BASE.Debug ~= nil then
|
||||||
|
env.info( BASE.Debug.traceback() )
|
||||||
|
end
|
||||||
|
return errmsg
|
||||||
|
end
|
||||||
|
self.CallHandler = function()
|
||||||
|
local function MenuFunction()
|
||||||
|
return self.Function( unpack( self.Functionargs ) )
|
||||||
|
end
|
||||||
|
local Status, Result = xpcall( MenuFunction, ErrorHandler)
|
||||||
|
if self.Once == true then
|
||||||
|
self:Clear()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.path = missionCommands.addCommandForGroup(self.GroupID,Text,self.parentpath, self.CallHandler)
|
||||||
|
else
|
||||||
|
self.path = missionCommands.addSubMenuForGroup(self.GroupID,Text,self.parentpath)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if self.parentpath then
|
||||||
|
self.path = UTILS.DeepCopy(self.parentpath)
|
||||||
|
else
|
||||||
|
self.path = {}
|
||||||
|
end
|
||||||
|
self.path[#self.path+1] = Text
|
||||||
|
end
|
||||||
|
self.UUID = table.concat(self.path,";")
|
||||||
|
self:T({self.UUID})
|
||||||
|
self.Once = false
|
||||||
|
-- Log id.
|
||||||
|
self.lid=string.format("CLIENTMENU %s | %s | ", self.ID, self.name)
|
||||||
|
self:T(self.lid.."Created")
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a UUID
|
||||||
|
-- @param #CLIENTMENU self
|
||||||
|
-- @param #CLIENTMENU Parent The parent object if any
|
||||||
|
-- @param #string Text The menu entry text
|
||||||
|
-- @return #string UUID
|
||||||
|
function CLIENTMENU:CreateUUID(Parent,Text)
|
||||||
|
local path = {}
|
||||||
|
if Parent and Parent.path then
|
||||||
|
path = Parent.path
|
||||||
|
end
|
||||||
|
path[#path+1] = Text
|
||||||
|
local UUID = table.concat(path,";")
|
||||||
|
return UUID
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Set the CLIENTMENUMANAGER for this entry.
|
||||||
|
-- @param #CLIENTMENU self
|
||||||
|
-- @param #CLIENTMENUMANAGER Controller The controlling object.
|
||||||
|
-- @return #CLIENTMENU self
|
||||||
|
function CLIENTMENU:SetController(Controller)
|
||||||
|
self.Controller = Controller
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- The entry will be deleted after being used used - for menu entries with functions only.
|
||||||
|
-- @param #CLIENTMENU self
|
||||||
|
-- @return #CLIENTMENU self
|
||||||
|
function CLIENTMENU:SetOnce()
|
||||||
|
self:T(self.lid.."SetOnce")
|
||||||
|
self.Once = true
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove the entry from the F10 menu.
|
||||||
|
-- @param #CLIENTMENU self
|
||||||
|
-- @return #CLIENTMENU self
|
||||||
|
function CLIENTMENU:RemoveF10()
|
||||||
|
self:T(self.lid.."RemoveF10")
|
||||||
|
if self.GroupID then
|
||||||
|
--self:I(self.lid.."Removing "..table.concat(self.path,";"))
|
||||||
|
missionCommands.removeItemForGroup(self.GroupID , self.path )
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the menu path table.
|
||||||
|
-- @param #CLIENTMENU self
|
||||||
|
-- @return #table Path
|
||||||
|
function CLIENTMENU:GetPath()
|
||||||
|
self:T(self.lid.."GetPath")
|
||||||
|
return self.path
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Get the UUID.
|
||||||
|
-- @param #CLIENTMENU self
|
||||||
|
-- @return #string UUID
|
||||||
|
function CLIENTMENU:GetUUID()
|
||||||
|
self:T(self.lid.."GetUUID")
|
||||||
|
return self.UUID
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Link a child entry.
|
||||||
|
-- @param #CLIENTMENU self
|
||||||
|
-- @param #CLIENTMENU Child The entry to link as a child.
|
||||||
|
-- @return #CLIENTMENU self
|
||||||
|
function CLIENTMENU:AddChild(Child)
|
||||||
|
self:T(self.lid.."AddChild "..Child.ID)
|
||||||
|
table.insert(self.Children,Child.ID,Child)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove a child entry.
|
||||||
|
-- @param #CLIENTMENU self
|
||||||
|
-- @param #CLIENTMENU Child The entry to remove from the children.
|
||||||
|
-- @return #CLIENTMENU self
|
||||||
|
function CLIENTMENU:RemoveChild(Child)
|
||||||
|
self:T(self.lid.."RemoveChild "..Child.ID)
|
||||||
|
table.remove(self.Children,Child.ID)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove all subentries (children) from this entry.
|
||||||
|
-- @param #CLIENTMENU self
|
||||||
|
-- @return #CLIENTMENU self
|
||||||
|
function CLIENTMENU:RemoveSubEntries()
|
||||||
|
self:T(self.lid.."RemoveSubEntries")
|
||||||
|
self:T({self.Children})
|
||||||
|
for _id,_entry in pairs(self.Children) do
|
||||||
|
self:T("Removing ".._id)
|
||||||
|
if _entry then
|
||||||
|
_entry:RemoveSubEntries()
|
||||||
|
_entry:RemoveF10()
|
||||||
|
if _entry.Parent then
|
||||||
|
_entry.Parent:RemoveChild(self)
|
||||||
|
end
|
||||||
|
--if self.Controller then
|
||||||
|
--self.Controller:_RemoveByID(_entry.ID)
|
||||||
|
--end
|
||||||
|
--_entry = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove this entry and all subentries (children) from this entry.
|
||||||
|
-- @param #CLIENTMENU self
|
||||||
|
-- @return #CLIENTMENU self
|
||||||
|
function CLIENTMENU:Clear()
|
||||||
|
self:T(self.lid.."Clear")
|
||||||
|
for _id,_entry in pairs(self.Children) do
|
||||||
|
if _entry then
|
||||||
|
_entry:RemoveSubEntries()
|
||||||
|
_entry = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self:RemoveF10()
|
||||||
|
if self.Parent then
|
||||||
|
self.Parent:RemoveChild(self)
|
||||||
|
end
|
||||||
|
--if self.Controller then
|
||||||
|
--self.Controller:_RemoveByID(self.ID)
|
||||||
|
--end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO
|
||||||
|
----------------------------------------------------------------------------------------------------------------
|
||||||
|
--
|
||||||
|
-- CLIENTMENUMANAGER
|
||||||
|
--
|
||||||
|
----------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
--- Class CLIENTMENUMANAGER
|
||||||
|
-- @type CLIENTMENUMANAGER
|
||||||
|
-- @field #string ClassName Class Name
|
||||||
|
-- @field #string lid Lid for log entries
|
||||||
|
-- @field #string version Version string
|
||||||
|
-- @field #string name Name
|
||||||
|
-- @field Core.Set#SET_CLIENT clientset The set of clients this menu manager is for
|
||||||
|
-- @field #table flattree
|
||||||
|
-- @field #table rootentries
|
||||||
|
-- @field #table menutree
|
||||||
|
-- @field #number entrycount
|
||||||
|
-- @field #boolean debug
|
||||||
|
-- @extends Core.Base#BASE
|
||||||
|
|
||||||
|
--- *As a child my family's menu consisted of two choices: take it, or leave it.*
|
||||||
|
--
|
||||||
|
-- ===
|
||||||
|
--
|
||||||
|
-- ## CLIENTMENU and CLIENTMENUMANAGER
|
||||||
|
--
|
||||||
|
-- Manage menu structures for a SET_CLIENT of clients.
|
||||||
|
--
|
||||||
|
-- ## Concept
|
||||||
|
--
|
||||||
|
-- Separate creation of a menu tree structure from pushing it to each client. Create a shadow "reference" menu structure tree for your client pilot's in a mission.
|
||||||
|
-- This can then be propagated to all clients. Manipulate the entries in the structure with removing, clearing or changing single entries, create replacement sub-structures
|
||||||
|
-- for entries etc, push to one or all clients.
|
||||||
|
--
|
||||||
|
-- Many functions can either change the tree for one client or for all clients.
|
||||||
|
--
|
||||||
|
-- ## Create a base reference tree and send to all clients
|
||||||
|
--
|
||||||
|
-- local clientset = SET_CLIENT:New():FilterStart()
|
||||||
|
--
|
||||||
|
-- local menumgr = CLIENTMENUMANAGER:New(clientset,"Dayshift")
|
||||||
|
-- local mymenu = menumgr:NewEntry("Top")
|
||||||
|
-- local mymenu_lv1a = menumgr:NewEntry("Level 1 a",mymenu)
|
||||||
|
-- local mymenu_lv1b = menumgr:NewEntry("Level 1 b",mymenu)
|
||||||
|
-- -- next one is a command menu entry, which can only be used once
|
||||||
|
-- local mymenu_lv1c = menumgr:NewEntry("Action Level 1 c",mymenu, testfunction, "testtext"):SetOnce()
|
||||||
|
--
|
||||||
|
-- local mymenu_lv2a = menumgr:NewEntry("Go here",mymenu_lv1a)
|
||||||
|
-- local mymenu_lv2b = menumgr:NewEntry("Level 2 ab",mymenu_lv1a)
|
||||||
|
-- local mymenu_lv2c = menumgr:NewEntry("Level 2 ac",mymenu_lv1a)
|
||||||
|
--
|
||||||
|
-- local mymenu_lv2ba = menumgr:NewEntry("Level 2 ba",mymenu_lv1b)
|
||||||
|
-- local mymenu_lv2bb = menumgr:NewEntry("Level 2 bb",mymenu_lv1b)
|
||||||
|
-- local mymenu_lv2bc = menumgr:NewEntry("Level 2 bc",mymenu_lv1b)
|
||||||
|
--
|
||||||
|
-- local mymenu_lv3a = menumgr:NewEntry("Level 3 aaa",mymenu_lv2a)
|
||||||
|
-- local mymenu_lv3b = menumgr:NewEntry("Level 3 aab",mymenu_lv2a)
|
||||||
|
-- local mymenu_lv3c = menumgr:NewEntry("Level 3 aac",mymenu_lv2a)
|
||||||
|
--
|
||||||
|
-- menumgr:Propagate()
|
||||||
|
--
|
||||||
|
-- ## Remove a single entry's subtree
|
||||||
|
--
|
||||||
|
-- menumgr:RemoveSubEntries(mymenu_lv3a)
|
||||||
|
--
|
||||||
|
-- ## Remove a single entry and also it's subtree
|
||||||
|
--
|
||||||
|
-- menumgr:DeleteEntry(mymenu_lv3a)
|
||||||
|
--
|
||||||
|
-- ## Add a single entry
|
||||||
|
--
|
||||||
|
-- local baimenu = menumgr:NewEntry("BAI",mymenu_lv1b)
|
||||||
|
--
|
||||||
|
-- menumgr:AddEntry(baimenu)
|
||||||
|
--
|
||||||
|
-- ## Add an entry with a function
|
||||||
|
--
|
||||||
|
-- local baimenu = menumgr:NewEntry("Task Action", mymenu_lv1b, TestFunction, Argument1, Argument1)
|
||||||
|
--
|
||||||
|
-- Now, the class will **automatically append the call with GROUP and CLIENT objects**, as this is can only be done when pushing the entry to the clients. So, the actual function implementation needs to look like this:
|
||||||
|
--
|
||||||
|
-- function TestFunction( Argument1, Argument2, Group, Client)
|
||||||
|
--
|
||||||
|
-- **Caveat is**, that you need to ensure your arguments are not **nil** or **false**, as LUA will optimize those away. You would end up having Group and Client in wrong places in the function call. Hence,
|
||||||
|
-- if you need/ want to send **nil** or **false**, send a place holder instead and ensure your function can handle this, e.g.
|
||||||
|
--
|
||||||
|
-- local baimenu = menumgr:NewEntry("Task Action", mymenu_lv1b, TestFunction, "nil", Argument1)
|
||||||
|
--
|
||||||
|
-- ## Change the text of a leaf entry in the menu tree
|
||||||
|
--
|
||||||
|
-- menumgr:ChangeEntryTextForAll(mymenu_lv1b,"Attack")
|
||||||
|
--
|
||||||
|
-- ## Reset a single clients menu tree
|
||||||
|
--
|
||||||
|
-- menumgr:ResetMenu(client)
|
||||||
|
--
|
||||||
|
-- ## Reset all and clear the reference tree
|
||||||
|
--
|
||||||
|
-- menumgr:ResetMenuComplete()
|
||||||
|
--
|
||||||
|
-- @field #CLIENTMENUMANAGER
|
||||||
|
CLIENTMENUMANAGER = {
|
||||||
|
ClassName = "CLIENTMENUMANAGER",
|
||||||
|
lid = "",
|
||||||
|
version = "0.1.0",
|
||||||
|
name = nil,
|
||||||
|
clientset = nil,
|
||||||
|
menutree = {},
|
||||||
|
flattree = {},
|
||||||
|
playertree = {},
|
||||||
|
entrycount = 0,
|
||||||
|
rootentries = {},
|
||||||
|
debug = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
--- 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.
|
||||||
|
-- @return #CLIENTMENUMANAGER self
|
||||||
|
function CLIENTMENUMANAGER:New(ClientSet, Alias)
|
||||||
|
-- Inherit everything from FSM class.
|
||||||
|
local self=BASE:Inherit(self, BASE:New()) -- #CLIENTMENUMANAGER
|
||||||
|
self.clientset = ClientSet
|
||||||
|
self.name = Alias or "Nightshift"
|
||||||
|
-- Log id.
|
||||||
|
self.lid=string.format("CLIENTMENUMANAGER %s | %s | ", self.version, self.name)
|
||||||
|
if self.debug then
|
||||||
|
self:T(self.lid.."Created")
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Create a new entry in the generic structure.
|
||||||
|
-- @param #CLIENTMENUMANAGER self
|
||||||
|
-- @param #string Text Text of the F10 menu entry.
|
||||||
|
-- @param #CLIENTMENU Parent The parent menu entry.
|
||||||
|
-- @param #string Function (optional) Function to call when the entry is used.
|
||||||
|
-- @param ... (optional) Arguments for the Function, comma separated.
|
||||||
|
-- @return #CLIENTMENU Entry
|
||||||
|
function CLIENTMENUMANAGER:NewEntry(Text,Parent,Function,...)
|
||||||
|
self:T(self.lid.."NewEntry "..Text or "None")
|
||||||
|
self.entrycount = self.entrycount + 1
|
||||||
|
local entry = CLIENTMENU:NewEntry(nil,Text,Parent,Function,unpack(arg))
|
||||||
|
if not Parent then
|
||||||
|
self.rootentries[self.entrycount] = entry
|
||||||
|
end
|
||||||
|
local depth = #entry.path
|
||||||
|
if not self.menutree[depth] then self.menutree[depth] = {} end
|
||||||
|
table.insert(self.menutree[depth],entry.UUID)
|
||||||
|
self.flattree[entry.UUID] = entry
|
||||||
|
return entry
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Check matching entry in the generic structure by UUID.
|
||||||
|
-- @param #CLIENTMENUMANAGER self
|
||||||
|
-- @param #string UUID UUID of the menu entry.
|
||||||
|
-- @return #boolean Exists
|
||||||
|
function CLIENTMENUMANAGER:EntryUUIDExists(UUID)
|
||||||
|
local exists = self.flattree[UUID] and true or false
|
||||||
|
return exists
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find matching entry in the generic structure by UUID.
|
||||||
|
-- @param #CLIENTMENUMANAGER self
|
||||||
|
-- @param #string UUID UUID of the menu entry.
|
||||||
|
-- @return #CLIENTMENU Entry The #CLIENTMENU object found or nil.
|
||||||
|
function CLIENTMENUMANAGER:FindEntryByUUID(UUID)
|
||||||
|
self:T(self.lid.."FindEntryByUUID "..UUID or "None")
|
||||||
|
local entry = nil
|
||||||
|
for _gid,_entry in pairs(self.flattree) do
|
||||||
|
local Entry = _entry -- #CLIENTMENU
|
||||||
|
if Entry and Entry.UUID == UUID then
|
||||||
|
entry = Entry
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return entry
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find matching entries by text in the generic structure by UUID.
|
||||||
|
-- @param #CLIENTMENUMANAGER self
|
||||||
|
-- @param #string Text Text or partial text of the menu entry to find.
|
||||||
|
-- @param #CLIENTMENU Parent (Optional) Only find entries under this parent entry.
|
||||||
|
-- @return #table Table of matching UUIDs of #CLIENTMENU objects
|
||||||
|
-- @return #table Table of matching #CLIENTMENU objects
|
||||||
|
-- @return #number Number of matches
|
||||||
|
function CLIENTMENUMANAGER:FindUUIDsByText(Text,Parent)
|
||||||
|
self:T(self.lid.."FindUUIDsByText "..Text or "None")
|
||||||
|
local matches = {}
|
||||||
|
local entries = {}
|
||||||
|
local n = 0
|
||||||
|
for _uuid,_entry in pairs(self.flattree) do
|
||||||
|
local Entry = _entry -- #CLIENTMENU
|
||||||
|
if Parent then
|
||||||
|
if Entry and string.find(Entry.name,Text) and string.find(Entry.UUID,Parent.UUID) then
|
||||||
|
table.insert(matches,_uuid)
|
||||||
|
table.insert(entries,Entry )
|
||||||
|
n=n+1
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if Entry and string.find(Entry.name,Text) then
|
||||||
|
table.insert(matches,_uuid)
|
||||||
|
table.insert(entries,Entry )
|
||||||
|
n=n+1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return matches, entries, n
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find matching entries in the generic structure by the menu text.
|
||||||
|
-- @param #CLIENTMENUMANAGER self
|
||||||
|
-- @param #string Text Text or partial text of the F10 menu entry.
|
||||||
|
-- @param #CLIENTMENU Parent (Optional) Only find entries under this parent entry.
|
||||||
|
-- @return #table Table of matching #CLIENTMENU objects.
|
||||||
|
-- @return #number Number of matches
|
||||||
|
function CLIENTMENUMANAGER:FindEntriesByText(Text,Parent)
|
||||||
|
self:T(self.lid.."FindEntriesByText "..Text or "None")
|
||||||
|
local matches, objects, number = self:FindUUIDsByText(Text, Parent)
|
||||||
|
return objects, number
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find matching entries under a parent in the generic structure by UUID.
|
||||||
|
-- @param #CLIENTMENUMANAGER self
|
||||||
|
-- @param #CLIENTMENU Parent Find entries under this parent entry.
|
||||||
|
-- @return #table Table of matching UUIDs of #CLIENTMENU objects
|
||||||
|
-- @return #table Table of matching #CLIENTMENU objects
|
||||||
|
-- @return #number Number of matches
|
||||||
|
function CLIENTMENUMANAGER:FindUUIDsByParent(Parent)
|
||||||
|
self:T(self.lid.."FindUUIDsByParent")
|
||||||
|
local matches = {}
|
||||||
|
local entries = {}
|
||||||
|
local n = 0
|
||||||
|
for _uuid,_entry in pairs(self.flattree) do
|
||||||
|
local Entry = _entry -- #CLIENTMENU
|
||||||
|
if Parent then
|
||||||
|
if Entry and string.find(Entry.UUID,Parent.UUID) then
|
||||||
|
table.insert(matches,_uuid)
|
||||||
|
table.insert(entries,Entry )
|
||||||
|
n=n+1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return matches, entries, n
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Find matching entries in the generic structure under a parent.
|
||||||
|
-- @param #CLIENTMENUMANAGER self
|
||||||
|
-- @param #CLIENTMENU Parent Find entries under this parent entry.
|
||||||
|
-- @return #table Table of matching #CLIENTMENU objects.
|
||||||
|
-- @return #number Number of matches
|
||||||
|
function CLIENTMENUMANAGER:FindEntriesByParent(Parent)
|
||||||
|
self:T(self.lid.."FindEntriesByParent")
|
||||||
|
local matches, objects, number = self:FindUUIDsByParent(Parent)
|
||||||
|
return objects, number
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Alter the text of a leaf entry in the generic structure and push to one specific client's F10 menu.
|
||||||
|
-- @param #CLIENTMENUMANAGER self
|
||||||
|
-- @param #CLIENTMENU Entry The menu entry.
|
||||||
|
-- @param #string Text New Text of the F10 menu entry.
|
||||||
|
-- @param Wrapper.Client#CLIENT Client (optional) The client for whom to alter the entry, if nil done for all clients.
|
||||||
|
-- @return #CLIENTMENUMANAGER self
|
||||||
|
function CLIENTMENUMANAGER:ChangeEntryText(Entry, Text, Client)
|
||||||
|
self:T(self.lid.."ChangeEntryText "..Text or "None")
|
||||||
|
local newentry = CLIENTMENU:NewEntry(nil,Text,Entry.Parent,Entry.Function,unpack(Entry.Functionargs))
|
||||||
|
self:DeleteF10Entry(Entry,Client)
|
||||||
|
self:DeleteGenericEntry(Entry)
|
||||||
|
if not Entry.Parent then
|
||||||
|
self.rootentries[self.entrycount] = newentry
|
||||||
|
end
|
||||||
|
local depth = #newentry.path
|
||||||
|
if not self.menutree[depth] then self.menutree[depth] = {} end
|
||||||
|
table.insert(self.menutree[depth],newentry.UUID)
|
||||||
|
self.flattree[newentry.UUID] = newentry
|
||||||
|
self:AddEntry(newentry,Client)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Push the complete menu structure to each of the clients in the set - refresh the menu tree of the clients.
|
||||||
|
-- @param #CLIENTMENUMANAGER self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client (optional) If given, propagate only for this client.
|
||||||
|
-- @return #CLIENTMENU Entry
|
||||||
|
function CLIENTMENUMANAGER:Propagate(Client)
|
||||||
|
self:T(self.lid.."Propagate")
|
||||||
|
self:T(Client)
|
||||||
|
local Set = self.clientset.Set
|
||||||
|
if Client then
|
||||||
|
Set = {Client}
|
||||||
|
end
|
||||||
|
self:ResetMenu(Client)
|
||||||
|
for _,_client in pairs(Set) do
|
||||||
|
local client = _client -- Wrapper.Client#CLIENT
|
||||||
|
if client and client:IsAlive() then
|
||||||
|
local playername = client:GetPlayerName()
|
||||||
|
if not self.playertree[playername] then
|
||||||
|
self.playertree[playername] = {}
|
||||||
|
end
|
||||||
|
for level,branch in pairs (self.menutree) do
|
||||||
|
self:T("Building branch:" .. level)
|
||||||
|
for _,leaf in pairs(branch) do
|
||||||
|
self:T("Building leaf:" .. leaf)
|
||||||
|
local entry = self:FindEntryByUUID(leaf)
|
||||||
|
if entry then
|
||||||
|
self:T("Found generic entry:" .. entry.UUID)
|
||||||
|
local parent = nil
|
||||||
|
if entry.Parent and entry.Parent.UUID then
|
||||||
|
parent = self.playertree[playername][entry.Parent.UUID] or self:FindEntryByUUID(entry.Parent.UUID)
|
||||||
|
end
|
||||||
|
self.playertree[playername][entry.UUID] = CLIENTMENU:NewEntry(client,entry.name,parent,entry.Function,unpack(entry.Functionargs))
|
||||||
|
self.playertree[playername][entry.UUID].Once = entry.Once
|
||||||
|
else
|
||||||
|
self:T("NO generic entry for:" .. leaf)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Push a single previously created entry into the menu structure of all clients.
|
||||||
|
-- @param #CLIENTMENUMANAGER self
|
||||||
|
-- @param #CLIENTMENU Entry The entry to add.
|
||||||
|
-- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client.
|
||||||
|
-- @return #CLIENTMENUMANAGER self
|
||||||
|
function CLIENTMENUMANAGER:AddEntry(Entry,Client)
|
||||||
|
self:T(self.lid.."AddEntry")
|
||||||
|
local Set = self.clientset.Set
|
||||||
|
if Client then
|
||||||
|
Set = {Client}
|
||||||
|
end
|
||||||
|
for _,_client in pairs(Set) do
|
||||||
|
local client = _client -- Wrapper.Client#CLIENT
|
||||||
|
if client and client:IsAlive() then
|
||||||
|
local playername = client:GetPlayerName()
|
||||||
|
if Entry then
|
||||||
|
self:T("Adding generic entry:" .. Entry.UUID)
|
||||||
|
local parent = nil
|
||||||
|
if not self.playertree[playername] then
|
||||||
|
self.playertree[playername] = {}
|
||||||
|
end
|
||||||
|
if Entry.Parent and Entry.Parent.UUID then
|
||||||
|
parent = self.playertree[playername][Entry.Parent.UUID] or self:FindEntryByUUID(Entry.Parent.UUID)
|
||||||
|
end
|
||||||
|
self.playertree[playername][Entry.UUID] = CLIENTMENU:NewEntry(client,Entry.name,parent,Entry.Function,unpack(Entry.Functionargs))
|
||||||
|
self.playertree[playername][Entry.UUID].Once = Entry.Once
|
||||||
|
else
|
||||||
|
self:T("NO generic entry given")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Blank out the menu - remove **all root entries** and all entries below from the client's F10 menus, leaving the generic structure untouched.
|
||||||
|
-- @param #CLIENTMENUMANAGER self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client (optional) If given, remove only for this client.
|
||||||
|
-- @return #CLIENTMENUMANAGER self
|
||||||
|
function CLIENTMENUMANAGER:ResetMenu(Client)
|
||||||
|
self:T(self.lid.."ResetMenu")
|
||||||
|
for _,_entry in pairs(self.rootentries) do
|
||||||
|
--local RootEntry = self.structure.generic[_entry]
|
||||||
|
if _entry then
|
||||||
|
self:DeleteF10Entry(_entry,Client)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Blank out the menu - remove **all root entries** and all entries below from all clients' F10 menus, and **delete** the generic structure.
|
||||||
|
-- @param #CLIENTMENUMANAGER self
|
||||||
|
-- @return #CLIENTMENUMANAGER self
|
||||||
|
function CLIENTMENUMANAGER:ResetMenuComplete()
|
||||||
|
self:T(self.lid.."ResetMenuComplete")
|
||||||
|
for _,_entry in pairs(self.rootentries) do
|
||||||
|
--local RootEntry = self.structure.generic[_entry]
|
||||||
|
if _entry then
|
||||||
|
self:DeleteF10Entry(_entry)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
self.playertree = nil
|
||||||
|
self.playertree = {}
|
||||||
|
self.rootentries = nil
|
||||||
|
self.rootentries = {}
|
||||||
|
self.menutree = nil
|
||||||
|
self.menutree = {}
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove the entry and all entries below the given entry from the client's F10 menus.
|
||||||
|
-- @param #CLIENTMENUMANAGER self
|
||||||
|
-- @param #CLIENTMENU Entry The entry to remove
|
||||||
|
-- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client. In this case the generic structure will not be touched.
|
||||||
|
-- @return #CLIENTMENUMANAGER self
|
||||||
|
function CLIENTMENUMANAGER:DeleteF10Entry(Entry,Client)
|
||||||
|
self:T(self.lid.."DeleteF10Entry")
|
||||||
|
local Set = self.clientset.Set
|
||||||
|
if Client then
|
||||||
|
Set = {Client}
|
||||||
|
end
|
||||||
|
for _,_client in pairs(Set) do
|
||||||
|
if _client and _client:IsAlive() then
|
||||||
|
local playername = _client:GetPlayerName()
|
||||||
|
if self.playertree[playername] then
|
||||||
|
local centry = self.playertree[playername][Entry.UUID] -- #CLIENTMENU
|
||||||
|
if centry then
|
||||||
|
--self:I("Match for "..Entry.UUID)
|
||||||
|
centry:Clear()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove the entry and all entries below the given entry from the generic tree.
|
||||||
|
-- @param #CLIENTMENUMANAGER self
|
||||||
|
-- @param #CLIENTMENU Entry The entry to remove
|
||||||
|
-- @return #CLIENTMENUMANAGER self
|
||||||
|
function CLIENTMENUMANAGER:DeleteGenericEntry(Entry)
|
||||||
|
self:T(self.lid.."DeleteGenericEntry")
|
||||||
|
|
||||||
|
if Entry.Children and #Entry.Children > 0 then
|
||||||
|
self:RemoveGenericSubEntries(Entry)
|
||||||
|
end
|
||||||
|
|
||||||
|
local depth = #Entry.path
|
||||||
|
local uuid = Entry.UUID
|
||||||
|
|
||||||
|
local tbl = UTILS.DeepCopy(self.menutree)
|
||||||
|
|
||||||
|
if tbl[depth] then
|
||||||
|
for i=depth,#tbl do
|
||||||
|
--self:I("Level = "..i)
|
||||||
|
for _id,_uuid in pairs(tbl[i]) do
|
||||||
|
self:T(_uuid)
|
||||||
|
if string.find(_uuid,uuid) or _uuid == uuid then
|
||||||
|
--self:I("Match for ".._uuid)
|
||||||
|
self.menutree[i][_id] = nil
|
||||||
|
self.flattree[_uuid] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Remove all entries below the given entry from the generic tree.
|
||||||
|
-- @param #CLIENTMENUMANAGER self
|
||||||
|
-- @param #CLIENTMENU Entry The entry where to start. This entry stays.
|
||||||
|
-- @return #CLIENTMENUMANAGER self
|
||||||
|
function CLIENTMENUMANAGER:RemoveGenericSubEntries(Entry)
|
||||||
|
self:T(self.lid.."RemoveGenericSubEntries")
|
||||||
|
|
||||||
|
local depth = #Entry.path + 1
|
||||||
|
local uuid = Entry.UUID
|
||||||
|
|
||||||
|
local tbl = UTILS.DeepCopy(self.menutree)
|
||||||
|
|
||||||
|
if tbl[depth] then
|
||||||
|
for i=depth,#tbl do
|
||||||
|
self:T("Level = "..i)
|
||||||
|
for _id,_uuid in pairs(tbl[i]) do
|
||||||
|
self:T(_uuid)
|
||||||
|
if string.find(_uuid,uuid) then
|
||||||
|
self:T("Match for ".._uuid)
|
||||||
|
self.menutree[i][_id] = nil
|
||||||
|
self.flattree[_uuid] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--- Remove all entries below the given entry from the client's F10 menus.
|
||||||
|
-- @param #CLIENTMENUMANAGER self
|
||||||
|
-- @param #CLIENTMENU Entry The entry where to start. This entry stays.
|
||||||
|
-- @param Wrapper.Client#CLIENT Client (optional) If given, make this change only for this client. In this case the generic structure will not be touched.
|
||||||
|
-- @return #CLIENTMENUMANAGER self
|
||||||
|
function CLIENTMENUMANAGER:RemoveF10SubEntries(Entry,Client)
|
||||||
|
self:T(self.lid.."RemoveSubEntries")
|
||||||
|
local Set = self.clientset.Set
|
||||||
|
if Client then
|
||||||
|
Set = {Client}
|
||||||
|
end
|
||||||
|
for _,_client in pairs(Set) do
|
||||||
|
if _client and _client:IsAlive() then
|
||||||
|
local playername = _client:GetPlayerName()
|
||||||
|
if self.playertree[playername] then
|
||||||
|
local centry = self.playertree[playername][Entry.UUID] -- #CLIENTMENU
|
||||||
|
centry:RemoveSubEntries()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------------------------------------------
|
||||||
|
--
|
||||||
|
-- End ClientMenu
|
||||||
|
--
|
||||||
|
----------------------------------------------------------------------------------------------------------------
|
||||||
@ -2945,7 +2945,12 @@ do -- COORDINATE
|
|||||||
alttext = "very low"
|
alttext = "very low"
|
||||||
end
|
end
|
||||||
|
|
||||||
local track = UTILS.BearingToCardinal(bearing) or "North"
|
-- corrected Track to be direction of travel of bogey (self in this case)
|
||||||
|
local track = "Maneuver"
|
||||||
|
|
||||||
|
if self.Heading then
|
||||||
|
track = UTILS.BearingToCardinal(self.Heading) or "North"
|
||||||
|
end
|
||||||
|
|
||||||
if rangeNM > 3 then
|
if rangeNM > 3 then
|
||||||
if SSML then -- google says "oh" instead of zero, be aware
|
if SSML then -- google says "oh" instead of zero, be aware
|
||||||
|
|||||||
@ -163,6 +163,16 @@
|
|||||||
--
|
--
|
||||||
-- * @{#SPAWN.InitArray}(): Make groups visible before they are actually activated, and order these groups like a battalion in an array.
|
-- * @{#SPAWN.InitArray}(): Make groups visible before they are actually activated, and order these groups like a battalion in an array.
|
||||||
--
|
--
|
||||||
|
-- ### Group initial position - if wanted different from template position, for use with e.g. @{#SPAWN.SpawnScheduled}().
|
||||||
|
--
|
||||||
|
-- * @{#SPAWN.InitPositionCoordinate}(): Set initial position of group via a COORDINATE.
|
||||||
|
-- * @{#SPAWN.InitPositionVec2}(): Set initial position of group via a VEC2.
|
||||||
|
--
|
||||||
|
-- ### Set the positions of a group's units to absolute positions, or relative positions to unit No. 1
|
||||||
|
--
|
||||||
|
-- * @{#SPAWN.InitSetUnitRelativePositions}(): Spawn the UNITs of this group with individual relative positions to unit #1 and individual headings.
|
||||||
|
-- * @{#SPAWN.InitSetUnitAbsolutePositions}(): Spawn the UNITs of this group with individual absolute positions and individual headings.
|
||||||
|
--
|
||||||
-- ### Position randomization
|
-- ### Position randomization
|
||||||
--
|
--
|
||||||
-- * @{#SPAWN.InitRandomizePosition}(): Randomizes the position of @{Wrapper.Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens.
|
-- * @{#SPAWN.InitRandomizePosition}(): Randomizes the position of @{Wrapper.Group}s that are spawned within a **radius band**, given an Outer and Inner radius, from the point that the spawn happens.
|
||||||
@ -268,7 +278,7 @@ SPAWN = {
|
|||||||
-- @type SPAWN.Takeoff
|
-- @type SPAWN.Takeoff
|
||||||
-- @extends Wrapper.Group#GROUP.Takeoff
|
-- @extends Wrapper.Group#GROUP.Takeoff
|
||||||
|
|
||||||
--- @field #SPAWN.Takeoff Takeoff
|
-- @field #SPAWN.Takeoff Takeoff
|
||||||
SPAWN.Takeoff = {
|
SPAWN.Takeoff = {
|
||||||
Air = 1,
|
Air = 1,
|
||||||
Runway = 2,
|
Runway = 2,
|
||||||
@ -276,7 +286,7 @@ SPAWN.Takeoff = {
|
|||||||
Cold = 4,
|
Cold = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
--- @type SPAWN.SpawnZoneTable
|
-- @type SPAWN.SpawnZoneTable
|
||||||
-- @list <Core.Zone#ZONE_BASE> SpawnZone
|
-- @list <Core.Zone#ZONE_BASE> SpawnZone
|
||||||
|
|
||||||
--- Creates the main object to spawn a @{Wrapper.Group} defined in the DCS ME.
|
--- Creates the main object to spawn a @{Wrapper.Group} defined in the DCS ME.
|
||||||
@ -1047,7 +1057,7 @@ end
|
|||||||
--- This method provides the functionality to randomize the spawning of the Groups at a given list of zones of different types.
|
--- This method provides the functionality to randomize the spawning of the Groups at a given list of zones of different types.
|
||||||
-- @param #SPAWN self
|
-- @param #SPAWN self
|
||||||
-- @param #table SpawnZoneTable A table with @{Core.Zone} objects. If this table is given, then each spawn will be executed within the given list of @{Core.Zone}s objects.
|
-- @param #table SpawnZoneTable A table with @{Core.Zone} objects. If this table is given, then each spawn will be executed within the given list of @{Core.Zone}s objects.
|
||||||
-- @return #SPAWN
|
-- @return #SPAWN self
|
||||||
-- @usage
|
-- @usage
|
||||||
--
|
--
|
||||||
-- -- Create a zone table of the 2 zones.
|
-- -- Create a zone table of the 2 zones.
|
||||||
@ -1077,6 +1087,31 @@ function SPAWN:InitRandomizeZones( SpawnZoneTable )
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- This method sets a spawn position for the group that is different from the location of the template.
|
||||||
|
-- @param #SPAWN self
|
||||||
|
-- @param Core.Point#COORDINATE Coordinate The position to spawn from
|
||||||
|
-- @return #SPAWN self
|
||||||
|
function SPAWN:InitPositionCoordinate(Coordinate)
|
||||||
|
self:T( { self.SpawnTemplatePrefix, Coordinate:GetVec2()} )
|
||||||
|
self:InitPositionVec2(Coordinate:GetVec2())
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- This method sets a spawn position for the group that is different from the location of the template.
|
||||||
|
-- @param #SPAWN self
|
||||||
|
-- @param DCS#Vec2 Vec2 The position to spawn from
|
||||||
|
-- @return #SPAWN self
|
||||||
|
function SPAWN:InitPositionVec2(Vec2)
|
||||||
|
self:T( { self.SpawnTemplatePrefix, Vec2} )
|
||||||
|
self.SpawnInitPosition = Vec2
|
||||||
|
self.SpawnFromNewPosition = true
|
||||||
|
self:I("MaxGroups:"..self.SpawnMaxGroups)
|
||||||
|
for SpawnGroupID = 1, self.SpawnMaxGroups do
|
||||||
|
self:_SetInitialPosition( SpawnGroupID )
|
||||||
|
end
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- For planes and helicopters, when these groups go home and land on their home airbases and FARPs, they normally would taxi to the parking spot, shut-down their engines and wait forever until the Group is removed by the runtime environment.
|
--- For planes and helicopters, when these groups go home and land on their home airbases and FARPs, they normally would taxi to the parking spot, shut-down their engines and wait forever until the Group is removed by the runtime environment.
|
||||||
-- This method is used to re-spawn automatically (so no extra call is needed anymore) the same group after it has landed.
|
-- This method is used to re-spawn automatically (so no extra call is needed anymore) the same group after it has landed.
|
||||||
-- This will enable a spawned group to be re-spawned after it lands, until it is destroyed...
|
-- This will enable a spawned group to be re-spawned after it lands, until it is destroyed...
|
||||||
@ -1377,6 +1412,11 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
|
|||||||
|
|
||||||
if self:_GetSpawnIndex( SpawnIndex ) then
|
if self:_GetSpawnIndex( SpawnIndex ) then
|
||||||
|
|
||||||
|
if self.SpawnFromNewPosition then
|
||||||
|
self:_SetInitialPosition( SpawnIndex )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
if self.SpawnGroups[self.SpawnIndex].Visible then
|
if self.SpawnGroups[self.SpawnIndex].Visible then
|
||||||
self.SpawnGroups[self.SpawnIndex].Group:Activate()
|
self.SpawnGroups[self.SpawnIndex].Group:Activate()
|
||||||
else
|
else
|
||||||
@ -1614,7 +1654,7 @@ end
|
|||||||
-- @param #number SpawnTime The time interval defined in seconds between each new spawn of new groups.
|
-- @param #number SpawnTime The time interval defined in seconds between each new spawn of new groups.
|
||||||
-- @param #number SpawnTimeVariation The variation to be applied on the defined time interval between each new spawn.
|
-- @param #number SpawnTimeVariation The variation to be applied on the defined time interval between each new spawn.
|
||||||
-- The variation is a number between 0 and 1, representing the % of variation to be applied on the time interval.
|
-- The variation is a number between 0 and 1, representing the % of variation to be applied on the time interval.
|
||||||
-- @param #boolen WithDelay Do not spawn the **first** group immediately, but delay the spawn as per the calculation below.
|
-- @param #boolean WithDelay Do not spawn the **first** group immediately, but delay the spawn as per the calculation below.
|
||||||
-- Effectively the same as @{InitDelayOn}().
|
-- Effectively the same as @{InitDelayOn}().
|
||||||
-- @return #SPAWN self
|
-- @return #SPAWN self
|
||||||
-- @usage
|
-- @usage
|
||||||
@ -3128,6 +3168,10 @@ function SPAWN:_GetTemplate( SpawnTemplatePrefix )
|
|||||||
|
|
||||||
local SpawnTemplate = nil
|
local SpawnTemplate = nil
|
||||||
|
|
||||||
|
if _DATABASE.Templates.Groups[SpawnTemplatePrefix] == nil then
|
||||||
|
error( 'No Template exists for SpawnTemplatePrefix = ' .. SpawnTemplatePrefix )
|
||||||
|
end
|
||||||
|
|
||||||
local Template = _DATABASE.Templates.Groups[SpawnTemplatePrefix].Template
|
local Template = _DATABASE.Templates.Groups[SpawnTemplatePrefix].Template
|
||||||
self:F( { Template = Template } )
|
self:F( { Template = Template } )
|
||||||
|
|
||||||
@ -3296,6 +3340,57 @@ function SPAWN:_RandomizeTemplate( SpawnIndex )
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Private method that sets the DCS#Vec2 where the Group will be spawned.
|
||||||
|
-- @param #SPAWN self
|
||||||
|
-- @param #number SpawnIndex
|
||||||
|
-- @return #SPAWN self
|
||||||
|
function SPAWN:_SetInitialPosition( SpawnIndex )
|
||||||
|
self:T( { self.SpawnTemplatePrefix, SpawnIndex, self.SpawnRandomizeZones } )
|
||||||
|
|
||||||
|
if self.SpawnFromNewPosition then
|
||||||
|
|
||||||
|
self:T( "Preparing Spawn at Vec2 ", self.SpawnInitPosition )
|
||||||
|
|
||||||
|
local SpawnVec2 = self.SpawnInitPosition
|
||||||
|
|
||||||
|
self:T( { SpawnVec2 = SpawnVec2 } )
|
||||||
|
|
||||||
|
local SpawnTemplate = self.SpawnGroups[SpawnIndex].SpawnTemplate
|
||||||
|
|
||||||
|
SpawnTemplate.route = SpawnTemplate.route or {}
|
||||||
|
SpawnTemplate.route.points = SpawnTemplate.route.points or {}
|
||||||
|
SpawnTemplate.route.points[1] = SpawnTemplate.route.points[1] or {}
|
||||||
|
SpawnTemplate.route.points[1].x = SpawnTemplate.route.points[1].x or 0
|
||||||
|
SpawnTemplate.route.points[1].y = SpawnTemplate.route.points[1].y or 0
|
||||||
|
|
||||||
|
self:T( { Route = SpawnTemplate.route } )
|
||||||
|
|
||||||
|
for UnitID = 1, #SpawnTemplate.units do
|
||||||
|
local UnitTemplate = SpawnTemplate.units[UnitID]
|
||||||
|
self:T( 'Before Translation SpawnTemplate.units[' .. UnitID .. '].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units[' .. UnitID .. '].y = ' .. UnitTemplate.y )
|
||||||
|
local SX = UnitTemplate.x
|
||||||
|
local SY = UnitTemplate.y
|
||||||
|
local BX = SpawnTemplate.route.points[1].x
|
||||||
|
local BY = SpawnTemplate.route.points[1].y
|
||||||
|
local TX = SpawnVec2.x + (SX - BX)
|
||||||
|
local TY = SpawnVec2.y + (SY - BY)
|
||||||
|
UnitTemplate.x = TX
|
||||||
|
UnitTemplate.y = TY
|
||||||
|
-- TODO: Manage altitude based on landheight...
|
||||||
|
-- SpawnTemplate.units[UnitID].alt = SpawnVec2:
|
||||||
|
self:T( 'After Translation SpawnTemplate.units[' .. UnitID .. '].x = ' .. UnitTemplate.x .. ', SpawnTemplate.units[' .. UnitID .. '].y = ' .. UnitTemplate.y )
|
||||||
|
end
|
||||||
|
|
||||||
|
SpawnTemplate.route.points[1].x = SpawnVec2.x
|
||||||
|
SpawnTemplate.route.points[1].y = SpawnVec2.y
|
||||||
|
SpawnTemplate.x = SpawnVec2.x
|
||||||
|
SpawnTemplate.y = SpawnVec2.y
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- Private method that randomizes the @{Core.Zone}s where the Group will be spawned.
|
--- Private method that randomizes the @{Core.Zone}s where the Group will be spawned.
|
||||||
-- @param #SPAWN self
|
-- @param #SPAWN self
|
||||||
-- @param #number SpawnIndex
|
-- @param #number SpawnIndex
|
||||||
@ -3415,7 +3510,7 @@ end
|
|||||||
|
|
||||||
-- TODO Need to delete this... _DATABASE does this now ...
|
-- TODO Need to delete this... _DATABASE does this now ...
|
||||||
|
|
||||||
--- @param #SPAWN self
|
-- @param #SPAWN self
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
function SPAWN:_OnBirth( EventData )
|
function SPAWN:_OnBirth( EventData )
|
||||||
self:F( self.SpawnTemplatePrefix )
|
self:F( self.SpawnTemplatePrefix )
|
||||||
@ -3435,7 +3530,7 @@ function SPAWN:_OnBirth( EventData )
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #SPAWN self
|
-- @param #SPAWN self
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
function SPAWN:_OnDeadOrCrash( EventData )
|
function SPAWN:_OnDeadOrCrash( EventData )
|
||||||
self:F( self.SpawnTemplatePrefix )
|
self:F( self.SpawnTemplatePrefix )
|
||||||
|
|||||||
@ -33,7 +33,8 @@
|
|||||||
|
|
||||||
do
|
do
|
||||||
|
|
||||||
--- @type SPOT
|
---
|
||||||
|
-- @type SPOT
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
|
|
||||||
@ -228,7 +229,8 @@ do
|
|||||||
-- @param #number LaserCode Laser code.
|
-- @param #number LaserCode Laser code.
|
||||||
-- @param #number Duration Duration of lasing in seconds.
|
-- @param #number Duration Duration of lasing in seconds.
|
||||||
function SPOT:onafterLaseOn( From, Event, To, Target, LaserCode, Duration )
|
function SPOT:onafterLaseOn( From, Event, To, Target, LaserCode, Duration )
|
||||||
self:F( { "LaseOn", Target, LaserCode, Duration } )
|
self:T({From, Event, To})
|
||||||
|
self:T2( { "LaseOn", Target, LaserCode, Duration } )
|
||||||
|
|
||||||
local function StopLase( self )
|
local function StopLase( self )
|
||||||
self:LaseOff()
|
self:LaseOff()
|
||||||
@ -256,6 +258,8 @@ do
|
|||||||
self:HandleEvent( EVENTS.Dead )
|
self:HandleEvent( EVENTS.Dead )
|
||||||
|
|
||||||
self:__Lasing( -1 )
|
self:__Lasing( -1 )
|
||||||
|
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -268,7 +272,7 @@ do
|
|||||||
-- @param #number LaserCode Laser code.
|
-- @param #number LaserCode Laser code.
|
||||||
-- @param #number Duration Duration of lasing in seconds.
|
-- @param #number Duration Duration of lasing in seconds.
|
||||||
function SPOT:onafterLaseOnCoordinate(From, Event, To, Coordinate, LaserCode, Duration)
|
function SPOT:onafterLaseOnCoordinate(From, Event, To, Coordinate, LaserCode, Duration)
|
||||||
self:F( { "LaseOnCoordinate", Coordinate, LaserCode, Duration } )
|
self:T2( { "LaseOnCoordinate", Coordinate, LaserCode, Duration } )
|
||||||
|
|
||||||
local function StopLase( self )
|
local function StopLase( self )
|
||||||
self:LaseOff()
|
self:LaseOff()
|
||||||
@ -290,12 +294,14 @@ do
|
|||||||
end
|
end
|
||||||
|
|
||||||
self:__Lasing(-1)
|
self:__Lasing(-1)
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #SPOT self
|
---
|
||||||
|
-- @param #SPOT self
|
||||||
-- @param Core.Event#EVENTDATA EventData
|
-- @param Core.Event#EVENTDATA EventData
|
||||||
function SPOT:OnEventDead(EventData)
|
function SPOT:OnEventDead(EventData)
|
||||||
self:F( { Dead = EventData.IniDCSUnitName, Target = self.Target } )
|
self:T2( { Dead = EventData.IniDCSUnitName, Target = self.Target } )
|
||||||
if self.Target then
|
if self.Target then
|
||||||
if EventData.IniDCSUnitName == self.TargetName then
|
if EventData.IniDCSUnitName == self.TargetName then
|
||||||
self:F( {"Target dead ", self.TargetName } )
|
self:F( {"Target dead ", self.TargetName } )
|
||||||
@ -309,18 +315,24 @@ do
|
|||||||
self:LaseOff()
|
self:LaseOff()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #SPOT self
|
---
|
||||||
|
-- @param #SPOT self
|
||||||
-- @param From
|
-- @param From
|
||||||
-- @param Event
|
-- @param Event
|
||||||
-- @param To
|
-- @param To
|
||||||
function SPOT:onafterLasing( From, Event, To )
|
function SPOT:onafterLasing( From, Event, To )
|
||||||
|
self:T({From, Event, To})
|
||||||
|
|
||||||
|
if self.Lasing then
|
||||||
if self.Target and self.Target:IsAlive() 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)/100):AddX(math.random(-100,100)/100):GetVec3() )
|
||||||
self.SpotLaser:setPoint( self.Target:GetPointVec3():AddY(1):GetVec3() )
|
self.SpotLaser:setPoint( self.Target:GetPointVec3():AddY(1):GetVec3() )
|
||||||
self:__Lasing( -0.2 )
|
|
||||||
|
self:__Lasing(0.2)
|
||||||
elseif self.TargetCoord then
|
elseif self.TargetCoord then
|
||||||
|
|
||||||
-- Wiggle the IR spot a bit.
|
-- Wiggle the IR spot a bit.
|
||||||
@ -330,21 +342,24 @@ do
|
|||||||
self.SpotIR:setPoint(irvec3)
|
self.SpotIR:setPoint(irvec3)
|
||||||
self.SpotLaser:setPoint(lsvec3)
|
self.SpotLaser:setPoint(lsvec3)
|
||||||
|
|
||||||
self:__Lasing(-0.25)
|
self:__Lasing(0.2)
|
||||||
else
|
else
|
||||||
self:F( { "Target is not alive", self.Target:IsAlive() } )
|
self:F( { "Target is not alive", self.Target:IsAlive() } )
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- @param #SPOT self
|
---
|
||||||
|
-- @param #SPOT self
|
||||||
-- @param From
|
-- @param From
|
||||||
-- @param Event
|
-- @param Event
|
||||||
-- @param To
|
-- @param To
|
||||||
-- @return #SPOT
|
-- @return #SPOT
|
||||||
function SPOT:onafterLaseOff( From, Event, To )
|
function SPOT:onafterLaseOff( From, Event, To )
|
||||||
|
self:T({From, Event, To})
|
||||||
|
|
||||||
self:F( {"Stopped lasing for ", self.Target and self.Target:GetName() or "coord", SpotIR = self.SportIR, SpotLaser = self.SpotLaser } )
|
self:T2( {"Stopped lasing for ", self.Target and self.Target:GetName() or "coord", SpotIR = self.SportIR, SpotLaser = self.SpotLaser } )
|
||||||
|
|
||||||
self.Lasing = false
|
self.Lasing = false
|
||||||
|
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
-- ===
|
-- ===
|
||||||
--
|
--
|
||||||
-- ### Author: **Applevangelist**
|
-- ### Author: **Applevangelist**
|
||||||
-- Last Update February 2022
|
-- Last Update July 2023
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
-- @module Functional.AICSAR
|
-- @module Functional.AICSAR
|
||||||
@ -191,7 +191,7 @@
|
|||||||
-- @field #AICSAR
|
-- @field #AICSAR
|
||||||
AICSAR = {
|
AICSAR = {
|
||||||
ClassName = "AICSAR",
|
ClassName = "AICSAR",
|
||||||
version = "0.1.14",
|
version = "0.1.15",
|
||||||
lid = "",
|
lid = "",
|
||||||
coalition = coalition.side.BLUE,
|
coalition = coalition.side.BLUE,
|
||||||
template = "",
|
template = "",
|
||||||
@ -397,6 +397,7 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone)
|
|||||||
self:AddTransition("*", "PilotRescued", "*") -- Pilot Rescued
|
self:AddTransition("*", "PilotRescued", "*") -- Pilot Rescued
|
||||||
self:AddTransition("*", "PilotKIA", "*") -- Pilot dead
|
self:AddTransition("*", "PilotKIA", "*") -- Pilot dead
|
||||||
self:AddTransition("*", "HeloDown", "*") -- Helo dead
|
self:AddTransition("*", "HeloDown", "*") -- Helo dead
|
||||||
|
self:AddTransition("*", "HeloOnDuty", "*") -- Helo spawnd
|
||||||
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
|
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
|
||||||
|
|
||||||
self:HandleEvent(EVENTS.LandingAfterEjection,self._EventHandler)
|
self:HandleEvent(EVENTS.LandingAfterEjection,self._EventHandler)
|
||||||
@ -473,6 +474,14 @@ function AICSAR:New(Alias,Coalition,Pilottemplate,Helotemplate,FARP,MASHZone)
|
|||||||
-- @param #string Event Event.
|
-- @param #string Event Event.
|
||||||
-- @param #string To To state.
|
-- @param #string To To state.
|
||||||
|
|
||||||
|
--- On after "HeloOnDuty" event.
|
||||||
|
-- @function [parent=#AICSAR] OnAfterHeloOnDuty
|
||||||
|
-- @param #AICSAR self
|
||||||
|
-- @param #string From From state.
|
||||||
|
-- @param #string Event Event.
|
||||||
|
-- @param #string To To state.
|
||||||
|
-- @param Wrapper.Group#GROUP Helo Helo group object
|
||||||
|
|
||||||
--- On after "HeloDown" event.
|
--- On after "HeloDown" event.
|
||||||
-- @function [parent=#AICSAR] OnAfterHeloDown
|
-- @function [parent=#AICSAR] OnAfterHeloDown
|
||||||
-- @param #AICSAR self
|
-- @param #AICSAR self
|
||||||
@ -853,6 +862,11 @@ function AICSAR:_GetFlight()
|
|||||||
local newhelo = SPAWN:NewWithAlias(self.helotemplate,self.helotemplate..math.random(1,10000))
|
local newhelo = SPAWN:NewWithAlias(self.helotemplate,self.helotemplate..math.random(1,10000))
|
||||||
:InitDelayOff()
|
:InitDelayOff()
|
||||||
:InitUnControlled(true)
|
:InitUnControlled(true)
|
||||||
|
:OnSpawnGroup(
|
||||||
|
function(Group)
|
||||||
|
self:__HeloOnDuty(1,Group)
|
||||||
|
end
|
||||||
|
)
|
||||||
:Spawn()
|
:Spawn()
|
||||||
|
|
||||||
local nhelo=FLIGHTGROUP:New(newhelo)
|
local nhelo=FLIGHTGROUP:New(newhelo)
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
-- @module Functional.AmmoTruck
|
-- @module Functional.AmmoTruck
|
||||||
-- @image Artillery.JPG
|
-- @image Artillery.JPG
|
||||||
--
|
--
|
||||||
-- Date: Nov 2022
|
-- Last update: July 2023
|
||||||
|
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
--- **AMMOTRUCK** class, extends Core.FSM#FSM
|
--- **AMMOTRUCK** class, extends Core.FSM#FSM
|
||||||
@ -40,6 +40,7 @@
|
|||||||
-- @field #number unloadtime Unload time in seconds
|
-- @field #number unloadtime Unload time in seconds
|
||||||
-- @field #number waitingtime Max waiting time in seconds
|
-- @field #number waitingtime Max waiting time in seconds
|
||||||
-- @field #boolean routeonroad Route truck on road if true (default)
|
-- @field #boolean routeonroad Route truck on road if true (default)
|
||||||
|
-- @field #number reloads Number of reloads a single truck can do before he must return home
|
||||||
-- @extends Core.FSM#FSM
|
-- @extends Core.FSM#FSM
|
||||||
|
|
||||||
--- *Amateurs talk about tactics, but professionals study logistics.* - General Robert H Barrow, USMC
|
--- *Amateurs talk about tactics, but professionals study logistics.* - General Robert H Barrow, USMC
|
||||||
@ -73,9 +74,10 @@
|
|||||||
-- ammotruck.remunidist = 20000 -- 20km - send trucks max this far from home
|
-- ammotruck.remunidist = 20000 -- 20km - send trucks max this far from home
|
||||||
-- ammotruck.unloadtime = 600 -- 10 minutes - min time to unload ammunition
|
-- ammotruck.unloadtime = 600 -- 10 minutes - min time to unload ammunition
|
||||||
-- ammotruck.waitingtime = 1800 -- 30 mintes - wait max this long until remunition is done
|
-- ammotruck.waitingtime = 1800 -- 30 mintes - wait max this long until remunition is done
|
||||||
-- ammotruck.monitor = -60 - 1 minute - AMMOTRUCK checks on things every 1 minute
|
-- ammotruck.monitor = -60 -- 1 minute - AMMOTRUCK checks run every one minute
|
||||||
-- ammotruck.routeonroad = true - Trucks will **try** to drive on roads
|
-- ammotruck.routeonroad = true -- Trucks will **try** to drive on roads
|
||||||
-- ammotruck.usearmygroup = false - if true, will make use of ARMYGROUP in the background (if used in DEV branch)
|
-- ammotruck.usearmygroup = false -- If true, will make use of ARMYGROUP in the background (if used in DEV branch)
|
||||||
|
-- ammotruck.reloads = 5 -- Maxn re-arms a truck can do before he needs to go home and restock. Set to -1 for unlimited
|
||||||
--
|
--
|
||||||
-- ## 3 FSM Events to shape mission
|
-- ## 3 FSM Events to shape mission
|
||||||
--
|
--
|
||||||
@ -113,7 +115,7 @@
|
|||||||
AMMOTRUCK = {
|
AMMOTRUCK = {
|
||||||
ClassName = "AMMOTRUCK",
|
ClassName = "AMMOTRUCK",
|
||||||
lid = "",
|
lid = "",
|
||||||
version = "0.0.10",
|
version = "0.0.12",
|
||||||
alias = "",
|
alias = "",
|
||||||
debug = false,
|
debug = false,
|
||||||
trucklist = {},
|
trucklist = {},
|
||||||
@ -128,7 +130,8 @@ AMMOTRUCK = {
|
|||||||
monitor = -60,
|
monitor = -60,
|
||||||
unloadtime = 600,
|
unloadtime = 600,
|
||||||
waitingtime = 1800,
|
waitingtime = 1800,
|
||||||
routeonroad = true
|
routeonroad = true,
|
||||||
|
reloads = 5,
|
||||||
}
|
}
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -156,6 +159,7 @@ AMMOTRUCK.State = {
|
|||||||
--@field #string targetname
|
--@field #string targetname
|
||||||
--@field Wrapper.Group#GROUP targetgroup
|
--@field Wrapper.Group#GROUP targetgroup
|
||||||
--@field Core.Point#COORDINATE targetcoordinate
|
--@field Core.Point#COORDINATE targetcoordinate
|
||||||
|
--@field #number reloads
|
||||||
|
|
||||||
---
|
---
|
||||||
-- @param #AMMOTRUCK self
|
-- @param #AMMOTRUCK self
|
||||||
@ -369,6 +373,7 @@ function AMMOTRUCK:CheckReturningTrucks(dataset)
|
|||||||
truck.statusquo = AMMOTRUCK.State.IDLE
|
truck.statusquo = AMMOTRUCK.State.IDLE
|
||||||
truck.timestamp = timer.getAbsTime()
|
truck.timestamp = timer.getAbsTime()
|
||||||
truck.coordinate = coord
|
truck.coordinate = coord
|
||||||
|
truck.reloads = self.reloads or 5
|
||||||
self:__TruckHome(1,truck)
|
self:__TruckHome(1,truck)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -540,6 +545,7 @@ function AMMOTRUCK:CheckTrucksAlive()
|
|||||||
newtruck.statusquo = AMMOTRUCK.State.IDLE
|
newtruck.statusquo = AMMOTRUCK.State.IDLE
|
||||||
newtruck.timestamp = timer.getAbsTime()
|
newtruck.timestamp = timer.getAbsTime()
|
||||||
newtruck.coordinate = truck:GetCoordinate()
|
newtruck.coordinate = truck:GetCoordinate()
|
||||||
|
newtruck.reloads = self.reloads or 5
|
||||||
self.trucklist[name] = newtruck
|
self.trucklist[name] = newtruck
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -626,9 +632,11 @@ function AMMOTRUCK:onafterMonitor(From, Event, To)
|
|||||||
unloadingtrucks[#unloadingtrucks+1] = data
|
unloadingtrucks[#unloadingtrucks+1] = data
|
||||||
elseif data.statusquo == AMMOTRUCK.State.RETURNING then
|
elseif data.statusquo == AMMOTRUCK.State.RETURNING then
|
||||||
returningtrucks[#returningtrucks+1] = data
|
returningtrucks[#returningtrucks+1] = data
|
||||||
|
if data.reloads > 0 or data.reloads == -1 then
|
||||||
idletrucks[#idletrucks+1] = data
|
idletrucks[#idletrucks+1] = data
|
||||||
found = true
|
found = true
|
||||||
end
|
end
|
||||||
|
end
|
||||||
else
|
else
|
||||||
self.truckset[data.name] = nil
|
self.truckset[data.name] = nil
|
||||||
end
|
end
|
||||||
@ -637,7 +645,7 @@ function AMMOTRUCK:onafterMonitor(From, Event, To)
|
|||||||
local n=0
|
local n=0
|
||||||
if found and remunition then
|
if found and remunition then
|
||||||
-- match
|
-- match
|
||||||
local match = false
|
--local match = false
|
||||||
for _,_truckdata in pairs(idletrucks) do
|
for _,_truckdata in pairs(idletrucks) do
|
||||||
local truckdata = _truckdata -- #AMMOTRUCK.data
|
local truckdata = _truckdata -- #AMMOTRUCK.data
|
||||||
local truckcoord = truckdata.group:GetCoordinate() -- Core.Point#COORDINATE
|
local truckcoord = truckdata.group:GetCoordinate() -- Core.Point#COORDINATE
|
||||||
@ -750,6 +758,12 @@ end
|
|||||||
end
|
end
|
||||||
|
|
||||||
local scheduler = SCHEDULER:New(nil,destroyammo,{ammo},self.waitingtime)
|
local scheduler = SCHEDULER:New(nil,destroyammo,{ammo},self.waitingtime)
|
||||||
|
|
||||||
|
-- one reload less
|
||||||
|
if truck.reloads ~= -1 then
|
||||||
|
truck.reloads = truck.reloads - 1
|
||||||
|
end
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@ -2354,6 +2354,7 @@ do -- DETECTION_TYPES
|
|||||||
if not DetectedItem then
|
if not DetectedItem then
|
||||||
DetectedItem = self:AddDetectedItem( "TYPE", DetectedTypeName )
|
DetectedItem = self:AddDetectedItem( "TYPE", DetectedTypeName )
|
||||||
DetectedItem.TypeName = DetectedTypeName
|
DetectedItem.TypeName = DetectedTypeName
|
||||||
|
DetectedItem.Name = DetectedUnitName -- fix by @Nocke
|
||||||
end
|
end
|
||||||
|
|
||||||
DetectedItem.Set:AddUnit( DetectedUnit )
|
DetectedItem.Set:AddUnit( DetectedUnit )
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
-- @module Functional.Mantis
|
-- @module Functional.Mantis
|
||||||
-- @image Functional.Mantis.jpg
|
-- @image Functional.Mantis.jpg
|
||||||
--
|
--
|
||||||
-- Last Update: Oct 2022
|
-- Last Update: July 2023
|
||||||
|
|
||||||
-------------------------------------------------------------------------
|
-------------------------------------------------------------------------
|
||||||
--- **MANTIS** class, extends Core.Base#BASE
|
--- **MANTIS** class, extends Core.Base#BASE
|
||||||
@ -104,9 +104,13 @@
|
|||||||
-- * Silkworm (though strictly speaking this is a surface to ship missile)
|
-- * 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
|
-- * 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 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 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
|
-- * From SMA: RBS98M, RBS70, RBS90, RBS90M, RBS103A, RBS103B, RBS103AM, RBS103BM, Lvkv9040M
|
||||||
-- **NOTE** If you are using the Swedish Military Assets (SMA), please note that the **group name** for RBS-SAM types also needs to contain the keyword "SMA"
|
-- **NOTE** If you are using the Swedish Military Assets (SMA), please note that the **group name** for RBS-SAM types also needs to contain the keyword "SMA"
|
||||||
--
|
--
|
||||||
|
-- * From CH: 2S38, PantsirS1, PantsirS2, PGL-625, HQ-17A, M903PAC2, M903PAC3, TorM2, TorM2K, TorM2M, NASAMS3-AMRAAMER, NASAMS3-AIM9X2, C-RAM, PGZ-09, S350-9M100, S350-9M96D
|
||||||
|
-- **NOTE** If you are using the Military Assets by Currenthill (CH), please note that the **group name** for CH-SAM types also needs to contain the keyword "CHM"
|
||||||
|
--
|
||||||
-- Following the example started above, an SA-6 site group name should start with "Red SAM SA-6" then, or a blue Patriot installation with e.g. "Blue SAM Patriot".
|
-- Following the example started above, an SA-6 site group name should start with "Red SAM SA-6" then, or a blue Patriot installation with e.g. "Blue SAM Patriot".
|
||||||
-- **NOTE** If you are using the High-Digit-Sam Mod, please note that the **group name** for the following SAM types also needs to contain the keyword "HDS":
|
-- **NOTE** If you are using the High-Digit-Sam Mod, please note that the **group name** for the following SAM types also needs to contain the keyword "HDS":
|
||||||
--
|
--
|
||||||
@ -369,6 +373,7 @@ MANTIS.SamData = {
|
|||||||
["SA-20A"] = { Range=150, Blindspot=5, Height=27, Type="Long" , Radar="S-300PMU1"},
|
["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"},
|
["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" },
|
["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" }
|
||||||
}
|
}
|
||||||
|
|
||||||
--- SAM data HDS
|
--- SAM data HDS
|
||||||
@ -415,6 +420,35 @@ MANTIS.SamDataSMA = {
|
|||||||
["Lvkv9040M SMA"] = { Range=4, Blindspot=0, Height=2.5, Type="Short", Radar="LvKv9040" },
|
["Lvkv9040M SMA"] = { Range=4, Blindspot=0, Height=2.5, Type="Short", Radar="LvKv9040" },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--- SAM data CH
|
||||||
|
-- @type MANTIS.SamDataCH
|
||||||
|
-- @field #number Range Max firing range in km
|
||||||
|
-- @field #number Blindspot no-firing range (green circle)
|
||||||
|
-- @field #number Height Max firing height in km
|
||||||
|
-- @field #string Type #MANTIS.SamType of SAM, i.e. SHORT, MEDIUM or LONG (range)
|
||||||
|
-- @field #string Radar Radar typename on unit level (used as key)
|
||||||
|
MANTIS.SamDataCH = {
|
||||||
|
-- units from CH (Military Assets by Currenthill)
|
||||||
|
-- https://www.currenthill.com/
|
||||||
|
-- group name MUST contain CHM to ID launcher type correctly!
|
||||||
|
["2S38 CH"] = { Range=8, Blindspot=0.5, Height=6, Type="Short", Radar="2S38" },
|
||||||
|
["PantsirS1 CH"] = { Range=20, Blindspot=1.2, Height=15, Type="Short", Radar="PantsirS1" },
|
||||||
|
["PantsirS2 CH"] = { Range=30, Blindspot=1.2, Height=18, Type="Medium", Radar="PantsirS2" },
|
||||||
|
["PGL-625 CH"] = { Range=10, Blindspot=0.5, Height=5, Type="Short", Radar="PGL_625" },
|
||||||
|
["HQ-17A CH"] = { Range=20, Blindspot=1.5, Height=10, Type="Short", Radar="HQ17A" },
|
||||||
|
["M903PAC2 CH"] = { Range=160, Blindspot=3, Height=24.5, Type="Long", Radar="MIM104_M903_PAC2" },
|
||||||
|
["M903PAC3 CH"] = { Range=120, Blindspot=1, Height=40, Type="Long", Radar="MIM104_M903_PAC3" },
|
||||||
|
["TorM2 CH"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2" },
|
||||||
|
["TorM2K CH"] = { Range=12, Blindspot=1, Height=10, Type="Short", Radar="TorM2K" },
|
||||||
|
["TorM2M CH"] = { Range=16, Blindspot=1, Height=10, Type="Short", Radar="TorM2M" },
|
||||||
|
["NASAMS3-AMRAAMER CH"] = { Range=50, Blindspot=2, Height=35.7, Type="Medium", Radar="CH_NASAMS3_LN_AMRAAM_ER" },
|
||||||
|
["NASAMS3-AIM9X2 CH"] = { Range=20, Blindspot=0.2, Height=18, Type="Short", Radar="CH_NASAMS3_LN_AIM9X2" },
|
||||||
|
["C-RAM CH"] = { Range=2, Blindspot=0, Height=2, Type="Short", Radar="CH_Centurion_C_RAM" },
|
||||||
|
["PGZ-09 CH"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="CH_PGZ09" },
|
||||||
|
["S350-9M100 CH"] = { Range=15, Blindspot=1.5, Height=8, Type="Short", Radar="CH_S350_50P6_9M100" },
|
||||||
|
["S350-9M96D CH"] = { Range=150, Blindspot=2.5, Height=30, Type="Long", Radar="CH_S350_50P6_9M96D" },
|
||||||
|
}
|
||||||
|
|
||||||
-----------------------------------------------------------------------
|
-----------------------------------------------------------------------
|
||||||
-- MANTIS System
|
-- MANTIS System
|
||||||
-----------------------------------------------------------------------
|
-----------------------------------------------------------------------
|
||||||
@ -578,7 +612,7 @@ do
|
|||||||
|
|
||||||
-- TODO Version
|
-- TODO Version
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
self.version="0.8.9"
|
self.version="0.8.11"
|
||||||
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
|
self:I(string.format("***** Starting MANTIS Version %s *****", self.version))
|
||||||
|
|
||||||
--- FSM Functions ---
|
--- FSM Functions ---
|
||||||
@ -1299,11 +1333,12 @@ do
|
|||||||
-- @param #string grpname Name of the group
|
-- @param #string grpname Name of the group
|
||||||
-- @param #boolean mod HDS mod flag
|
-- @param #boolean mod HDS mod flag
|
||||||
-- @param #boolean sma SMA mod flag
|
-- @param #boolean sma SMA mod flag
|
||||||
|
-- @param #boolean chm CH mod flag
|
||||||
-- @return #number range Max firing range
|
-- @return #number range Max firing range
|
||||||
-- @return #number height Max firing height
|
-- @return #number height Max firing height
|
||||||
-- @return #string type Long, medium or short range
|
-- @return #string type Long, medium or short range
|
||||||
-- @return #number blind "blind" spot
|
-- @return #number blind "blind" spot
|
||||||
function MANTIS:_GetSAMDataFromUnits(grpname,mod,sma)
|
function MANTIS:_GetSAMDataFromUnits(grpname,mod,sma,chm)
|
||||||
self:T(self.lid.."_GetSAMRangeFromUnits")
|
self:T(self.lid.."_GetSAMRangeFromUnits")
|
||||||
local found = false
|
local found = false
|
||||||
local range = self.checkradius
|
local range = self.checkradius
|
||||||
@ -1318,8 +1353,10 @@ do
|
|||||||
SAMData = self.SamDataHDS
|
SAMData = self.SamDataHDS
|
||||||
elseif sma then
|
elseif sma then
|
||||||
SAMData = self.SamDataSMA
|
SAMData = self.SamDataSMA
|
||||||
|
elseif chm then
|
||||||
|
SAMData = self.SamDataCH
|
||||||
end
|
end
|
||||||
--self:I("Looking to auto-match for "..grpname)
|
--self:T("Looking to auto-match for "..grpname)
|
||||||
for _,_unit in pairs(units) do
|
for _,_unit in pairs(units) do
|
||||||
local unit = _unit -- Wrapper.Unit#UNIT
|
local unit = _unit -- Wrapper.Unit#UNIT
|
||||||
local type = string.lower(unit:GetTypeName())
|
local type = string.lower(unit:GetTypeName())
|
||||||
@ -1364,10 +1401,13 @@ do
|
|||||||
local found = false
|
local found = false
|
||||||
local HDSmod = false
|
local HDSmod = false
|
||||||
local SMAMod = false
|
local SMAMod = false
|
||||||
|
local CHMod = false
|
||||||
if string.find(grpname,"HDS",1,true) then
|
if string.find(grpname,"HDS",1,true) then
|
||||||
HDSmod = true
|
HDSmod = true
|
||||||
elseif string.find(grpname,"SMA",1,true) then
|
elseif string.find(grpname,"SMA",1,true) then
|
||||||
SMAMod = true
|
SMAMod = true
|
||||||
|
elseif string.find(grpname,"CHM",1,true) then
|
||||||
|
CHMod = true
|
||||||
end
|
end
|
||||||
if self.automode then
|
if self.automode then
|
||||||
for idx,entry in pairs(self.SamData) do
|
for idx,entry in pairs(self.SamData) do
|
||||||
@ -1386,8 +1426,8 @@ do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- secondary filter if not found
|
-- secondary filter if not found
|
||||||
if (not found and self.automode) or HDSmod or SMAMod then
|
if (not found and self.automode) or HDSmod or SMAMod or CHMod then
|
||||||
range, height, type = self:_GetSAMDataFromUnits(grpname,HDSmod,SMAMod)
|
range, height, type = self:_GetSAMDataFromUnits(grpname,HDSmod,SMAMod,CHMod)
|
||||||
elseif not found then
|
elseif not found then
|
||||||
self:E(self.lid .. string.format("*****Could not match radar data for %s! Will default to midrange values!",grpname))
|
self:E(self.lid .. string.format("*****Could not match radar data for %s! Will default to midrange values!",grpname))
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1767,9 +1767,9 @@ function SCORING:SecondsToClock( sSeconds )
|
|||||||
-- return nil;
|
-- return nil;
|
||||||
return "00:00:00";
|
return "00:00:00";
|
||||||
else
|
else
|
||||||
nHours = string.format( "%02.f", math.floor( nSeconds / 3600 ) );
|
local nHours = string.format( "%02.f", math.floor( nSeconds / 3600 ) );
|
||||||
nMins = string.format( "%02.f", math.floor( nSeconds / 60 - (nHours * 60) ) );
|
local nMins = string.format( "%02.f", math.floor( nSeconds / 60 - (nHours * 60) ) );
|
||||||
nSecs = string.format( "%02.f", math.floor( nSeconds - nHours * 3600 - nMins * 60 ) );
|
local nSecs = string.format( "%02.f", math.floor( nSeconds - nHours * 3600 - nMins * 60 ) );
|
||||||
return nHours .. ":" .. nMins .. ":" .. nSecs
|
return nHours .. ":" .. nMins .. ":" .. nSecs
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -12,6 +12,7 @@ __Moose.Include( 'Scripts/Moose/Core/Base.lua' )
|
|||||||
__Moose.Include( 'Scripts/Moose/Core/Astar.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Astar.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Beacon.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Beacon.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Condition.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Condition.lua' )
|
||||||
|
__Moose.Include( 'Scripts/Moose/Core/ClientMenu.lua')
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Database.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Database.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Event.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Event.lua' )
|
||||||
__Moose.Include( 'Scripts/Moose/Core/Fsm.lua' )
|
__Moose.Include( 'Scripts/Moose/Core/Fsm.lua' )
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
---@diagnostic disable: cast-local-type
|
||||||
--- **Ops** - Automatic Terminal Information Service (ATIS).
|
--- **Ops** - Automatic Terminal Information Service (ATIS).
|
||||||
--
|
--
|
||||||
-- ===
|
-- ===
|
||||||
@ -608,15 +609,16 @@ _ATIS = {}
|
|||||||
|
|
||||||
--- ATIS class version.
|
--- ATIS class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
ATIS.version = "0.9.14"
|
ATIS.version = "0.9.15"
|
||||||
|
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
-- TODO list
|
-- TODO list
|
||||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
-- TODO: Add new Normandy airfields.
|
|
||||||
-- TODO: Zulu time --> Zulu in output.
|
|
||||||
-- TODO: Correct fog for elevation.
|
-- TODO: Correct fog for elevation.
|
||||||
|
-- DONE: Zulu time --> Zulu in output.
|
||||||
|
-- DONE: Fix for AB not having a runway - Helopost like Naqoura
|
||||||
|
-- DONE: Add new Normandy airfields.
|
||||||
-- DONE: Use new AIRBASE system to set start/landing runway
|
-- DONE: Use new AIRBASE system to set start/landing runway
|
||||||
-- DONE: SetILS doesn't work
|
-- DONE: SetILS doesn't work
|
||||||
-- DONE: Visibility reported twice over SRS
|
-- DONE: Visibility reported twice over SRS
|
||||||
@ -2140,15 +2142,19 @@ function ATIS:onafterBroadcast( From, Event, To )
|
|||||||
end
|
end
|
||||||
alltext = alltext .. ";\n" .. subtitle
|
alltext = alltext .. ";\n" .. subtitle
|
||||||
|
|
||||||
|
local _RUNACT
|
||||||
|
|
||||||
if not self.ATISforFARPs then
|
if not self.ATISforFARPs then
|
||||||
-- Active runway.
|
-- Active runway.
|
||||||
|
if runwayLanding then
|
||||||
local subtitle=string.format("Active runway %s", runwayLanding)
|
local subtitle=string.format("Active runway %s", runwayLanding)
|
||||||
if rwyLandingLeft==true then
|
if rwyLandingLeft==true then
|
||||||
subtitle=subtitle.." Left"
|
subtitle=subtitle.." Left"
|
||||||
elseif rwyLandingLeft==false then
|
elseif rwyLandingLeft==false then
|
||||||
subtitle=subtitle.." Right"
|
subtitle=subtitle.." Right"
|
||||||
end
|
end
|
||||||
local _RUNACT = subtitle
|
end
|
||||||
|
_RUNACT = subtitle
|
||||||
if not self.useSRS then
|
if not self.useSRS then
|
||||||
self:Transmission(ATIS.Sound.ActiveRunway, 1.0, subtitle)
|
self:Transmission(ATIS.Sound.ActiveRunway, 1.0, subtitle)
|
||||||
self.radioqueue:Number2Transmission(runwayLanding)
|
self.radioqueue:Number2Transmission(runwayLanding)
|
||||||
@ -2509,8 +2515,11 @@ function ATIS:GetActiveRunway(Takeoff)
|
|||||||
else
|
else
|
||||||
runway=self.airbase:GetActiveRunwayLanding()
|
runway=self.airbase:GetActiveRunwayLanding()
|
||||||
end
|
end
|
||||||
|
if runway then -- some ABs have NO runways, e.g. Syria Naqoura
|
||||||
return runway.name, runway.isLeft
|
return runway.name, runway.isLeft
|
||||||
|
else
|
||||||
|
return nil, nil
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Get runway from user supplied magnetic heading.
|
--- Get runway from user supplied magnetic heading.
|
||||||
|
|||||||
@ -11585,7 +11585,13 @@ function AIRBOSS:GetHeadingIntoWind( magnetic, coord )
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Get direction the wind is blowing from. This is where we want to go.
|
-- Get direction the wind is blowing from. This is where we want to go.
|
||||||
local windfrom, vwind = self:GetWind( nil, nil, coord ) + adjustDegreesForWindSpeed(vwind)
|
local windfrom, vwind = self:GetWind( nil, nil, coord )
|
||||||
|
|
||||||
|
--self:I("windfrom="..windfrom.." vwind="..vwind)
|
||||||
|
|
||||||
|
vwind = vwind + adjustDegreesForWindSpeed(vwind)
|
||||||
|
|
||||||
|
--self:I("windfrom="..windfrom.." (c)vwind="..vwind)
|
||||||
|
|
||||||
-- Actually, we want the runway in the wind.
|
-- Actually, we want the runway in the wind.
|
||||||
local intowind = windfrom - self.carrierparam.rwyangle
|
local intowind = windfrom - self.carrierparam.rwyangle
|
||||||
@ -17348,7 +17354,7 @@ function AIRBOSS:_DisplayCarrierInfo( _unitname )
|
|||||||
state = "Deck closed"
|
state = "Deck closed"
|
||||||
end
|
end
|
||||||
if self.turning then
|
if self.turning then
|
||||||
state = state .. " (turning currently)"
|
state = state .. " (currently turning)"
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Message text.
|
-- Message text.
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
---@diagnostic disable: undefined-global
|
||||||
--- **Ops** - Auftrag (mission) for Ops.
|
--- **Ops** - Auftrag (mission) for Ops.
|
||||||
--
|
--
|
||||||
-- ## Main Features:
|
-- ## Main Features:
|
||||||
@ -259,6 +260,10 @@
|
|||||||
--
|
--
|
||||||
-- Not implemented yet.
|
-- Not implemented yet.
|
||||||
--
|
--
|
||||||
|
-- ## Ground Escort
|
||||||
|
--
|
||||||
|
-- An escort mission can be created with the @{#AUFTRAG.NewGROUNDESCORT}() function.
|
||||||
|
--
|
||||||
-- ## Intercept
|
-- ## Intercept
|
||||||
--
|
--
|
||||||
-- An intercept mission can be created with the @{#AUFTRAG.NewINTERCEPT}() function.
|
-- An intercept mission can be created with the @{#AUFTRAG.NewINTERCEPT}() function.
|
||||||
@ -406,6 +411,7 @@ _AUFTRAGSNR=0
|
|||||||
-- @field #string FAC Forward AirController mission.
|
-- @field #string FAC Forward AirController mission.
|
||||||
-- @field #string FACA Forward AirController airborne mission.
|
-- @field #string FACA Forward AirController airborne mission.
|
||||||
-- @field #string FERRY Ferry mission.
|
-- @field #string FERRY Ferry mission.
|
||||||
|
-- @field #string GROUNDESCORT Ground escort mission.
|
||||||
-- @field #string INTERCEPT Intercept mission.
|
-- @field #string INTERCEPT Intercept mission.
|
||||||
-- @field #string ORBIT Orbit mission.
|
-- @field #string ORBIT Orbit mission.
|
||||||
-- @field #string GCICAP Similar to CAP but no auto engage targets.
|
-- @field #string GCICAP Similar to CAP but no auto engage targets.
|
||||||
@ -450,6 +456,7 @@ AUFTRAG.Type={
|
|||||||
FAC="FAC",
|
FAC="FAC",
|
||||||
FACA="FAC-A",
|
FACA="FAC-A",
|
||||||
FERRY="Ferry Flight",
|
FERRY="Ferry Flight",
|
||||||
|
GROUNDESCORT="Ground Escort",
|
||||||
INTERCEPT="Intercept",
|
INTERCEPT="Intercept",
|
||||||
ORBIT="Orbit",
|
ORBIT="Orbit",
|
||||||
GCICAP="Ground Controlled CAP",
|
GCICAP="Ground Controlled CAP",
|
||||||
@ -477,7 +484,7 @@ AUFTRAG.Type={
|
|||||||
RELOCATECOHORT="Relocate Cohort",
|
RELOCATECOHORT="Relocate Cohort",
|
||||||
AIRDEFENSE="Air Defence",
|
AIRDEFENSE="Air Defence",
|
||||||
EWR="Early Warning Radar",
|
EWR="Early Warning Radar",
|
||||||
RECOVERYTANKER="Recovery Tanker",
|
--RECOVERYTANKER="Recovery Tanker",
|
||||||
REARMING="Rearming",
|
REARMING="Rearming",
|
||||||
CAPTUREZONE="Capture Zone",
|
CAPTUREZONE="Capture Zone",
|
||||||
NOTHING="Nothing",
|
NOTHING="Nothing",
|
||||||
@ -1740,6 +1747,43 @@ function AUFTRAG:NewBOMBCARPET(Target, Altitude, CarpetLength)
|
|||||||
return mission
|
return mission
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- **[AIR/HELO]** Create a GROUNDESCORT (or FOLLOW) mission. Helo will escort a **ground** group and automatically engage certain target types.
|
||||||
|
-- @param #AUFTRAG self
|
||||||
|
-- @param Wrapper.Group#GROUP EscortGroup The ground group to escort.
|
||||||
|
-- @param #number OrbitDistance Orbit to/from the lead unit this many NM. Defaults to 1.5 NM.
|
||||||
|
-- @param #table TargetTypes Types of targets to engage automatically. Default is {"Ground vehicles"}, i.e. all enemy ground units. Use an empty set {} for a simple "FOLLOW" mission.
|
||||||
|
-- @return #AUFTRAG self
|
||||||
|
function AUFTRAG:NewGROUNDESCORT(EscortGroup, OrbitDistance, TargetTypes)
|
||||||
|
|
||||||
|
local mission=AUFTRAG:New(AUFTRAG.Type.GROUNDESCORT)
|
||||||
|
|
||||||
|
-- If only a string is passed we set a variable and check later if the group exists.
|
||||||
|
if type(EscortGroup)=="string" then
|
||||||
|
mission.escortGroupName=EscortGroup
|
||||||
|
mission:_TargetFromObject()
|
||||||
|
else
|
||||||
|
mission:_TargetFromObject(EscortGroup)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- DCS task parameters:
|
||||||
|
mission.orbitDistance=OrbitDistance and UTILS.NMToMeters(OrbitDistance) or UTILS.NMToMeters(1.5)
|
||||||
|
--mission.engageMaxDistance=EngageMaxDistance and UTILS.NMToMeters(EngageMaxDistance) or UTILS.NMToMeters(5)
|
||||||
|
mission.engageTargetTypes=TargetTypes or {"Ground vehicles"}
|
||||||
|
|
||||||
|
-- Mission options:
|
||||||
|
mission.missionTask=ENUMS.MissionTask.GROUNDESCORT
|
||||||
|
mission.missionFraction=0.1
|
||||||
|
mission.missionAltitude=100
|
||||||
|
mission.optionROE=ENUMS.ROE.OpenFire -- TODO: what's the best ROE here? Make dependent on ESCORT or FOLLOW!
|
||||||
|
mission.optionROT=ENUMS.ROT.EvadeFire
|
||||||
|
|
||||||
|
mission.categories={AUFTRAG.Category.HELICOPTER}
|
||||||
|
|
||||||
|
mission.DCStask=mission:GetDCSMissionTask()
|
||||||
|
|
||||||
|
return mission
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
--- **[AIR]** Create an ESCORT (or FOLLOW) mission. Flight will escort another group and automatically engage certain target types.
|
--- **[AIR]** Create an ESCORT (or FOLLOW) mission. Flight will escort another group and automatically engage certain target types.
|
||||||
-- @param #AUFTRAG self
|
-- @param #AUFTRAG self
|
||||||
@ -5843,10 +5887,20 @@ function AUFTRAG:GetDCSMissionTask()
|
|||||||
-- ESCORT Mission --
|
-- ESCORT Mission --
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
local DCStask=CONTROLLABLE.TaskEscort(nil, self.engageTarget:GetObject(), self.escortVec3, LastWaypointIndex, self.engageMaxDistance, self.engageTargetTypes)
|
local DCStask=CONTROLLABLE.TaskEscort(nil, self.engageTarget:GetObject(), self.escortVec3, nil, self.engageMaxDistance, self.engageTargetTypes)
|
||||||
|
|
||||||
table.insert(DCStasks, DCStask)
|
table.insert(DCStasks, DCStask)
|
||||||
|
|
||||||
|
elseif self.type==AUFTRAG.Type.GROUNDESCORT then
|
||||||
|
|
||||||
|
--------------------
|
||||||
|
-- GROUNDESCORT Mission --
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
local DCSTask=CONTROLLABLE.TaskGroundEscort(nil,self.engageTarget:GetObject(),nil,self.orbitDistance,self.engageTargetTypes)
|
||||||
|
|
||||||
|
table.insert(DCStasks, DCSTask)
|
||||||
|
|
||||||
elseif self.type==AUFTRAG.Type.FACA then
|
elseif self.type==AUFTRAG.Type.FACA then
|
||||||
|
|
||||||
------------------
|
------------------
|
||||||
@ -6528,6 +6582,8 @@ function AUFTRAG:GetMissionTaskforMissionType(MissionType)
|
|||||||
mtask=ENUMS.MissionTask.AFAC
|
mtask=ENUMS.MissionTask.AFAC
|
||||||
elseif MissionType==AUFTRAG.Type.FERRY then
|
elseif MissionType==AUFTRAG.Type.FERRY then
|
||||||
mtask=ENUMS.MissionTask.NOTHING
|
mtask=ENUMS.MissionTask.NOTHING
|
||||||
|
elseif MissionType==AUFTRAG.Type.GROUNDESCORT then
|
||||||
|
mtask=ENUMS.MissionTask.GROUNDESCORT
|
||||||
elseif MissionType==AUFTRAG.Type.INTERCEPT then
|
elseif MissionType==AUFTRAG.Type.INTERCEPT then
|
||||||
mtask=ENUMS.MissionTask.INTERCEPT
|
mtask=ENUMS.MissionTask.INTERCEPT
|
||||||
elseif MissionType==AUFTRAG.Type.RECON then
|
elseif MissionType==AUFTRAG.Type.RECON then
|
||||||
|
|||||||
@ -499,7 +499,7 @@ do
|
|||||||
-- @field #AWACS
|
-- @field #AWACS
|
||||||
AWACS = {
|
AWACS = {
|
||||||
ClassName = "AWACS", -- #string
|
ClassName = "AWACS", -- #string
|
||||||
version = "0.2.54", -- #string
|
version = "0.2.55", -- #string
|
||||||
lid = "", -- #string
|
lid = "", -- #string
|
||||||
coalition = coalition.side.BLUE, -- #number
|
coalition = coalition.side.BLUE, -- #number
|
||||||
coalitiontxt = "blue", -- #string
|
coalitiontxt = "blue", -- #string
|
||||||
@ -783,8 +783,8 @@ AWACS.Messages = {
|
|||||||
-- @field #string AwacsStateMission
|
-- @field #string AwacsStateMission
|
||||||
-- @field #string AwacsStateFG
|
-- @field #string AwacsStateFG
|
||||||
-- @field #boolean AwacsShiftChange
|
-- @field #boolean AwacsShiftChange
|
||||||
-- @field #string EscortsStateMission
|
-- @field #table EscortsStateMission
|
||||||
-- @field #string EscortsStateFG
|
-- @field #table EscortsStateFG
|
||||||
-- @field #boolean EscortsShiftChange
|
-- @field #boolean EscortsShiftChange
|
||||||
-- @field #number AICAPMax
|
-- @field #number AICAPMax
|
||||||
-- @field #number AICAPCurrent
|
-- @field #number AICAPCurrent
|
||||||
@ -1162,8 +1162,8 @@ function AWACS:New(Name,AirWing,Coalition,AirbaseName,AwacsOrbit,OpsZone,Station
|
|||||||
MonitoringData.AwacsStateFG = "unknown"
|
MonitoringData.AwacsStateFG = "unknown"
|
||||||
MonitoringData.AwacsStateMission = "unknown"
|
MonitoringData.AwacsStateMission = "unknown"
|
||||||
MonitoringData.EscortsShiftChange = false
|
MonitoringData.EscortsShiftChange = false
|
||||||
MonitoringData.EscortsStateFG= "unknown"
|
MonitoringData.EscortsStateFG = {}
|
||||||
MonitoringData.EscortsStateMission = "unknown"
|
MonitoringData.EscortsStateMission = {}
|
||||||
self.MonitoringOn = false -- #boolean
|
self.MonitoringOn = false -- #boolean
|
||||||
self.MonitoringData = MonitoringData
|
self.MonitoringData = MonitoringData
|
||||||
|
|
||||||
@ -2027,9 +2027,9 @@ function AWACS:_StartEscorts(Shiftchange)
|
|||||||
self.CatchAllMissions[#self.CatchAllMissions+1] = escort
|
self.CatchAllMissions[#self.CatchAllMissions+1] = escort
|
||||||
|
|
||||||
if Shiftchange then
|
if Shiftchange then
|
||||||
self.EscortMissionReplacement[i] = mission
|
self.EscortMissionReplacement[i] = escort
|
||||||
else
|
else
|
||||||
self.EscortMission[i] = mission
|
self.EscortMission[i] = escort
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -3597,10 +3597,13 @@ function AWACS:_SetClientMenus()
|
|||||||
local tasking = MENU_GROUP:New(cgrp,"Tasking",basemenu)
|
local tasking = MENU_GROUP:New(cgrp,"Tasking",basemenu)
|
||||||
local showtask = MENU_GROUP_COMMAND:New(cgrp,"Showtask",tasking,self._Showtask,self,cgrp)
|
local showtask = MENU_GROUP_COMMAND:New(cgrp,"Showtask",tasking,self._Showtask,self,cgrp)
|
||||||
|
|
||||||
|
local commit
|
||||||
|
local unable
|
||||||
|
local abort
|
||||||
if self.PlayerCapAssignment then
|
if self.PlayerCapAssignment then
|
||||||
local commit = MENU_GROUP_COMMAND:New(cgrp,"Commit",tasking,self._Commit,self,cgrp)
|
commit = MENU_GROUP_COMMAND:New(cgrp,"Commit",tasking,self._Commit,self,cgrp)
|
||||||
local unable = MENU_GROUP_COMMAND:New(cgrp,"Unable",tasking,self._Unable,self,cgrp)
|
unable = MENU_GROUP_COMMAND:New(cgrp,"Unable",tasking,self._Unable,self,cgrp)
|
||||||
local abort = MENU_GROUP_COMMAND:New(cgrp,"Abort",tasking,self._TaskAbort,self,cgrp)
|
abort = MENU_GROUP_COMMAND:New(cgrp,"Abort",tasking,self._TaskAbort,self,cgrp)
|
||||||
--local judy = MENU_GROUP_COMMAND:New(cgrp,"Judy",tasking,self._Judy,self,cgrp)
|
--local judy = MENU_GROUP_COMMAND:New(cgrp,"Judy",tasking,self._Judy,self,cgrp)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -4933,8 +4936,8 @@ function AWACS:_AnnounceContact(Contact,IsNew,Group,IsBogeyDope,Tag,IsPopup,Repo
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
string.gsub(BRAText,"BRAA","brah")
|
BRAText = string.gsub(BRAText,"BRAA","brah")
|
||||||
string.gsub(BRAText,"BRA","brah")
|
BRAText = string.gsub(BRAText,"BRA","brah")
|
||||||
|
|
||||||
local prio = IsNew or IsBogeyDope
|
local prio = IsNew or IsBogeyDope
|
||||||
self:_NewRadioEntry(BRAText,TextScreen,GID,isGroup,true,IsNew,false,prio)
|
self:_NewRadioEntry(BRAText,TextScreen,GID,isGroup,true,IsNew,false,prio)
|
||||||
@ -5547,7 +5550,7 @@ end
|
|||||||
-- @param #string Event
|
-- @param #string Event
|
||||||
-- @param #string To
|
-- @param #string To
|
||||||
-- @return #AWACS self
|
-- @return #AWACS self
|
||||||
function AWACS:onbeforeStart(From,Event,to)
|
function AWACS:onbeforeStart(From,Event,To)
|
||||||
self:T({From, Event, To})
|
self:T({From, Event, To})
|
||||||
if self.IncludeHelicopters then
|
if self.IncludeHelicopters then
|
||||||
self.clientset:FilterCategories("helicopter")
|
self.clientset:FilterCategories("helicopter")
|
||||||
|
|||||||
@ -204,7 +204,7 @@ CTLD_CARGO = {
|
|||||||
-- @param #CTLD_CARGO self
|
-- @param #CTLD_CARGO self
|
||||||
-- @param #boolean loaded
|
-- @param #boolean loaded
|
||||||
function CTLD_CARGO:Isloaded()
|
function CTLD_CARGO:Isloaded()
|
||||||
if self.HasBeenMoved and not self.WasDropped() then
|
if self.HasBeenMoved and not self:WasDropped() then
|
||||||
return true
|
return true
|
||||||
else
|
else
|
||||||
return false
|
return false
|
||||||
@ -1221,7 +1221,7 @@ CTLD.UnitTypes = {
|
|||||||
|
|
||||||
--- CTLD class version.
|
--- CTLD class version.
|
||||||
-- @field #string version
|
-- @field #string version
|
||||||
CTLD.version="1.0.39"
|
CTLD.version="1.0.40"
|
||||||
|
|
||||||
--- Instantiate a new CTLD.
|
--- Instantiate a new CTLD.
|
||||||
-- @param #CTLD self
|
-- @param #CTLD self
|
||||||
@ -1390,6 +1390,7 @@ function CTLD:New(Coalition, Prefixes, Alias)
|
|||||||
-- sub categories
|
-- sub categories
|
||||||
self.usesubcats = false
|
self.usesubcats = false
|
||||||
self.subcats = {}
|
self.subcats = {}
|
||||||
|
self.subcatsTroop = {}
|
||||||
|
|
||||||
-- disallow building in loadzones
|
-- disallow building in loadzones
|
||||||
self.nobuildinloadzones = true
|
self.nobuildinloadzones = true
|
||||||
@ -2279,6 +2280,7 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop)
|
|||||||
if not drop then
|
if not drop then
|
||||||
inzone = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD)
|
inzone = self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD)
|
||||||
if not inzone then
|
if not inzone then
|
||||||
|
---@diagnostic disable-next-line: cast-local-type
|
||||||
inzone, ship, zone, distance, width = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
|
inzone, ship, zone, distance, width = self:IsUnitInZone(Unit,CTLD.CargoZoneType.SHIP)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@ -3468,6 +3470,12 @@ function CTLD:_RefreshF10Menus()
|
|||||||
self.subcats[entry.Subcategory] = entry.Subcategory
|
self.subcats[entry.Subcategory] = entry.Subcategory
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
for _id,_cargo in pairs(self.Cargo_Troops) do
|
||||||
|
local entry = _cargo -- #CTLD_CARGO
|
||||||
|
if not self.subcatsTroop[entry.Subcategory] then
|
||||||
|
self.subcatsTroop[entry.Subcategory] = entry.Subcategory
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- build unit menus
|
-- build unit menus
|
||||||
@ -3506,11 +3514,24 @@ function CTLD:_RefreshF10Menus()
|
|||||||
-- sub menu troops management
|
-- sub menu troops management
|
||||||
if cantroops then
|
if cantroops then
|
||||||
local troopsmenu = MENU_GROUP:New(_group,"Load troops",toptroops)
|
local troopsmenu = MENU_GROUP:New(_group,"Load troops",toptroops)
|
||||||
|
if self.usesubcats then
|
||||||
|
local subcatmenus = {}
|
||||||
|
for _name,_entry in pairs(self.subcatsTroop) do
|
||||||
|
subcatmenus[_name] = MENU_GROUP:New(_group,_name,troopsmenu)
|
||||||
|
end
|
||||||
|
for _,_entry in pairs(self.Cargo_Troops) do
|
||||||
|
local entry = _entry -- #CTLD_CARGO
|
||||||
|
local subcat = entry.Subcategory
|
||||||
|
menucount = menucount + 1
|
||||||
|
menus[menucount] = MENU_GROUP_COMMAND:New(_group,entry.Name,subcatmenus[subcat],self._LoadTroops, self, _group, _unit, entry)
|
||||||
|
end
|
||||||
|
else
|
||||||
for _,_entry in pairs(self.Cargo_Troops) do
|
for _,_entry in pairs(self.Cargo_Troops) do
|
||||||
local entry = _entry -- #CTLD_CARGO
|
local entry = _entry -- #CTLD_CARGO
|
||||||
menucount = menucount + 1
|
menucount = menucount + 1
|
||||||
menus[menucount] = MENU_GROUP_COMMAND:New(_group,entry.Name,troopsmenu,self._LoadTroops, self, _group, _unit, entry)
|
menus[menucount] = MENU_GROUP_COMMAND:New(_group,entry.Name,troopsmenu,self._LoadTroops, self, _group, _unit, entry)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
local unloadmenu1 = MENU_GROUP_COMMAND:New(_group,"Drop troops",toptroops, self._UnloadTroops, self, _group, _unit):Refresh()
|
local unloadmenu1 = MENU_GROUP_COMMAND:New(_group,"Drop troops",toptroops, self._UnloadTroops, self, _group, _unit):Refresh()
|
||||||
local extractMenu1 = MENU_GROUP_COMMAND:New(_group, "Extract troops", toptroops, self._ExtractTroops, self, _group, _unit):Refresh()
|
local extractMenu1 = MENU_GROUP_COMMAND:New(_group, "Extract troops", toptroops, self._ExtractTroops, self, _group, _unit):Refresh()
|
||||||
end
|
end
|
||||||
@ -3603,7 +3624,8 @@ end
|
|||||||
-- @param #number NoTroops Size of the group in number of Units across combined templates (for loading).
|
-- @param #number NoTroops Size of the group in number of Units across combined templates (for loading).
|
||||||
-- @param #number PerTroopMass Mass in kg of each soldier
|
-- @param #number PerTroopMass Mass in kg of each soldier
|
||||||
-- @param #number Stock Number of groups in stock. Nil for unlimited.
|
-- @param #number Stock Number of groups in stock. Nil for unlimited.
|
||||||
function CTLD:AddTroopsCargo(Name,Templates,Type,NoTroops,PerTroopMass,Stock)
|
-- @param #string SubCategory Name of sub-category (optional).
|
||||||
|
function CTLD:AddTroopsCargo(Name,Templates,Type,NoTroops,PerTroopMass,Stock,SubCategory)
|
||||||
self:T(self.lid .. " AddTroopsCargo")
|
self:T(self.lid .. " AddTroopsCargo")
|
||||||
self:T({Name,Templates,Type,NoTroops,PerTroopMass,Stock})
|
self:T({Name,Templates,Type,NoTroops,PerTroopMass,Stock})
|
||||||
if not self:_CheckTemplates(Templates) then
|
if not self:_CheckTemplates(Templates) then
|
||||||
@ -3612,7 +3634,7 @@ function CTLD:AddTroopsCargo(Name,Templates,Type,NoTroops,PerTroopMass,Stock)
|
|||||||
end
|
end
|
||||||
self.CargoCounter = self.CargoCounter + 1
|
self.CargoCounter = self.CargoCounter + 1
|
||||||
-- Troops are directly loadable
|
-- Troops are directly loadable
|
||||||
local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Templates,Type,false,true,NoTroops,nil,nil,PerTroopMass,Stock)
|
local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Templates,Type,false,true,NoTroops,nil,nil,PerTroopMass,Stock, SubCategory)
|
||||||
table.insert(self.Cargo_Troops,cargo)
|
table.insert(self.Cargo_Troops,cargo)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|||||||
@ -522,7 +522,7 @@ function PLAYERTASK:MarkTargetOnF10Map(Text,Coalition,ReadOnly)
|
|||||||
-- Marker exists, delete one first
|
-- Marker exists, delete one first
|
||||||
self.TargetMarker:Remove()
|
self.TargetMarker:Remove()
|
||||||
end
|
end
|
||||||
local text = Text or "Target of "..self.lid
|
local text = Text or ("Target of "..self.lid)
|
||||||
self.TargetMarker = MARKER:New(coordinate,text)
|
self.TargetMarker = MARKER:New(coordinate,text)
|
||||||
if ReadOnly then
|
if ReadOnly then
|
||||||
self.TargetMarker:ReadOnly()
|
self.TargetMarker:ReadOnly()
|
||||||
@ -954,6 +954,7 @@ do
|
|||||||
-- @field Utilities.FiFo#FIFO TasksPerPlayer
|
-- @field Utilities.FiFo#FIFO TasksPerPlayer
|
||||||
-- @field Utilities.FiFo#FIFO PrecisionTasks
|
-- @field Utilities.FiFo#FIFO PrecisionTasks
|
||||||
-- @field Core.Set#SET_CLIENT ClientSet
|
-- @field Core.Set#SET_CLIENT ClientSet
|
||||||
|
-- @field Core.Set#SET_CLIENT ActiveClientSet
|
||||||
-- @field #string ClientFilter
|
-- @field #string ClientFilter
|
||||||
-- @field #string Name
|
-- @field #string Name
|
||||||
-- @field #string Type
|
-- @field #string Type
|
||||||
@ -988,6 +989,7 @@ do
|
|||||||
-- @field #table PlayerJoinMenu
|
-- @field #table PlayerJoinMenu
|
||||||
-- @field #table PlayerInfoMenu
|
-- @field #table PlayerInfoMenu
|
||||||
-- @field #boolean noflaresmokemenu
|
-- @field #boolean noflaresmokemenu
|
||||||
|
-- @field #boolean illumenu
|
||||||
-- @field #boolean TransmitOnlyWithPlayers
|
-- @field #boolean TransmitOnlyWithPlayers
|
||||||
-- @field #boolean buddylasing
|
-- @field #boolean buddylasing
|
||||||
-- @field Ops.PlayerRecce#PLAYERRECCE PlayerRecce
|
-- @field Ops.PlayerRecce#PLAYERRECCE PlayerRecce
|
||||||
@ -999,6 +1001,13 @@ do
|
|||||||
-- @field #table PlayerMenuTag
|
-- @field #table PlayerMenuTag
|
||||||
-- @field #boolean UseTypeNames
|
-- @field #boolean UseTypeNames
|
||||||
-- @field Functional.Scoring#SCORING Scoring
|
-- @field Functional.Scoring#SCORING Scoring
|
||||||
|
-- @field Core.ClientMenu#CLIENTMENUMANAGER JoinTaskMenuTemplate
|
||||||
|
-- @field Core.ClientMenu#CLIENTMENU JoinMenu
|
||||||
|
-- @field Core.ClientMenu#CLIENTMENU JoinTopMenu
|
||||||
|
-- @field Core.ClientMenu#CLIENTMENU JoinInfoMenu
|
||||||
|
-- @field Core.ClientMenu#CLIENTMENUMANAGER ActiveTaskMenuTemplate
|
||||||
|
-- @field Core.ClientMenu#CLIENTMENU ActiveTopMenu
|
||||||
|
-- @field Core.ClientMenu#CLIENTMENU ActiveInfoMenu
|
||||||
-- @extends Core.Fsm#FSM
|
-- @extends Core.Fsm#FSM
|
||||||
|
|
||||||
---
|
---
|
||||||
@ -1155,6 +1164,7 @@ do
|
|||||||
-- MENUMARK = "Mark on map",
|
-- MENUMARK = "Mark on map",
|
||||||
-- MENUSMOKE = "Smoke",
|
-- MENUSMOKE = "Smoke",
|
||||||
-- MENUFLARE = "Flare",
|
-- MENUFLARE = "Flare",
|
||||||
|
-- MENUILLU = "Illuminate",
|
||||||
-- MENUABORT = "Abort",
|
-- MENUABORT = "Abort",
|
||||||
-- MENUJOIN = "Join Task",
|
-- MENUJOIN = "Join Task",
|
||||||
-- MENUTASKINFO = Task Info",
|
-- MENUTASKINFO = Task Info",
|
||||||
@ -1315,6 +1325,7 @@ PLAYERTASKCONTROLLER = {
|
|||||||
PlayerInfoMenu = {},
|
PlayerInfoMenu = {},
|
||||||
PlayerMenuTag = {},
|
PlayerMenuTag = {},
|
||||||
noflaresmokemenu = false,
|
noflaresmokemenu = false,
|
||||||
|
illumenu = false,
|
||||||
TransmitOnlyWithPlayers = true,
|
TransmitOnlyWithPlayers = true,
|
||||||
buddylasing = false,
|
buddylasing = false,
|
||||||
PlayerRecce = nil,
|
PlayerRecce = nil,
|
||||||
@ -1408,6 +1419,7 @@ PLAYERTASKCONTROLLER.Messages = {
|
|||||||
MENUMARK = "Mark on map",
|
MENUMARK = "Mark on map",
|
||||||
MENUSMOKE = "Smoke",
|
MENUSMOKE = "Smoke",
|
||||||
MENUFLARE = "Flare",
|
MENUFLARE = "Flare",
|
||||||
|
MENUILLU = "Illuminate",
|
||||||
MENUABORT = "Abort",
|
MENUABORT = "Abort",
|
||||||
MENUJOIN = "Join Task",
|
MENUJOIN = "Join Task",
|
||||||
MENUTASKINFO = "Task Info",
|
MENUTASKINFO = "Task Info",
|
||||||
@ -1487,6 +1499,7 @@ PLAYERTASKCONTROLLER.Messages = {
|
|||||||
MENUMARK = "Kartenmarkierung",
|
MENUMARK = "Kartenmarkierung",
|
||||||
MENUSMOKE = "Rauchgranate",
|
MENUSMOKE = "Rauchgranate",
|
||||||
MENUFLARE = "Leuchtgranate",
|
MENUFLARE = "Leuchtgranate",
|
||||||
|
MENUILLU = "Feldbeleuchtung",
|
||||||
MENUABORT = "Abbrechen",
|
MENUABORT = "Abbrechen",
|
||||||
MENUJOIN = "Auftrag annehmen",
|
MENUJOIN = "Auftrag annehmen",
|
||||||
MENUTASKINFO = "Auftrag Briefing",
|
MENUTASKINFO = "Auftrag Briefing",
|
||||||
@ -1592,25 +1605,28 @@ function PLAYERTASKCONTROLLER:New(Name, Coalition, Type, ClientFilter)
|
|||||||
self.CallsignTranslations = nil
|
self.CallsignTranslations = nil
|
||||||
|
|
||||||
self.noflaresmokemenu = false
|
self.noflaresmokemenu = false
|
||||||
|
self.illumenu = false
|
||||||
|
|
||||||
self.ShowMagnetic = true
|
self.ShowMagnetic = true
|
||||||
|
|
||||||
self.UseTypeNames = false
|
self.UseTypeNames = false
|
||||||
|
|
||||||
local IsClientSet = false
|
self.IsClientSet = false
|
||||||
|
|
||||||
if ClientFilter and type(ClientFilter) == "table" and ClientFilter.ClassName and ClientFilter.ClassName == "SET_CLIENT" then
|
if ClientFilter and type(ClientFilter) == "table" and ClientFilter.ClassName and ClientFilter.ClassName == "SET_CLIENT" then
|
||||||
-- we have a predefined SET_CLIENT
|
-- we have a predefined SET_CLIENT
|
||||||
self.ClientSet = ClientFilter
|
self.ClientSet = ClientFilter
|
||||||
IsClientSet = true
|
self.IsClientSet = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if ClientFilter and not IsClientSet then
|
if ClientFilter and not self.IsClientSet then
|
||||||
self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterPrefixes(ClientFilter):FilterStart()
|
self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterPrefixes(ClientFilter):FilterStart()
|
||||||
elseif not IsClientSet then
|
elseif not self.IsClientSet then
|
||||||
self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterStart()
|
self.ClientSet = SET_CLIENT:New():FilterCoalitions(string.lower(self.CoalitionName)):FilterActive(true):FilterStart()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
self.ActiveClientSet = SET_CLIENT:New()
|
||||||
|
|
||||||
self.lid=string.format("PlayerTaskController %s %s | ", self.Name, tostring(self.Type))
|
self.lid=string.format("PlayerTaskController %s %s | ", self.Name, tostring(self.Type))
|
||||||
|
|
||||||
self:_InitLocalization()
|
self:_InitLocalization()
|
||||||
@ -1849,6 +1865,24 @@ function PLAYERTASKCONTROLLER:SetEnableSmokeFlareTask()
|
|||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- [User] Show menu entries to illuminate targets. Needs smoke/flare enabled.
|
||||||
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
|
function PLAYERTASKCONTROLLER:SetEnableIlluminateTask()
|
||||||
|
self:T(self.lid.."SetEnableSmokeFlareTask")
|
||||||
|
self.illumenu = true
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [User] Do not show menu entries to illuminate targets.
|
||||||
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
|
function PLAYERTASKCONTROLLER:SetDisableIlluminateTask()
|
||||||
|
self:T(self.lid.."SetDisableIlluminateTask")
|
||||||
|
self.illumenu = false
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- [User] Show info text on screen with a coordinate info in any case (OFF by default)
|
--- [User] Show info text on screen with a coordinate info in any case (OFF by default)
|
||||||
-- @param #PLAYERTASKCONTROLLER self
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
-- @param #boolean OnOff Switch on = true or off = false
|
-- @param #boolean OnOff Switch on = true or off = false
|
||||||
@ -2141,6 +2175,7 @@ end
|
|||||||
-- @return #PLAYERTASKCONTROLLER self
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
function PLAYERTASKCONTROLLER:_EventHandler(EventData)
|
function PLAYERTASKCONTROLLER:_EventHandler(EventData)
|
||||||
self:T(self.lid.."_EventHandler: "..EventData.id)
|
self:T(self.lid.."_EventHandler: "..EventData.id)
|
||||||
|
self:T(self.lid.."_EventHandler: "..EventData.IniPlayerName)
|
||||||
if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then
|
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
|
if EventData.IniPlayerName then
|
||||||
self:T(self.lid.."Event for player: "..EventData.IniPlayerName)
|
self:T(self.lid.."Event for player: "..EventData.IniPlayerName)
|
||||||
@ -2164,11 +2199,14 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData)
|
|||||||
self:T(self.lid..text)
|
self:T(self.lid..text)
|
||||||
end
|
end
|
||||||
elseif EventData.id == EVENTS.PlayerEnterAircraft and EventData.IniCoalition == self.Coalition then
|
elseif EventData.id == EVENTS.PlayerEnterAircraft and EventData.IniCoalition == self.Coalition then
|
||||||
if EventData.IniPlayerName and EventData.IniGroup and self.UseSRS then
|
if EventData.IniPlayerName and EventData.IniGroup then
|
||||||
if self.ClientSet:IsNotInSet(CLIENT:FindByName(EventData.IniUnitName)) then
|
if self.IsClientSet and self.ClientSet:IsNotInSet(CLIENT:FindByName(EventData.IniUnitName)) then
|
||||||
|
self:T(self.lid.."Client not in SET: "..EventData.IniPlayerName)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
self:T(self.lid.."Event for player: "..EventData.IniPlayerName)
|
self:T(self.lid.."Event for player: "..EventData.IniPlayerName)
|
||||||
|
|
||||||
|
if self.UseSRS then
|
||||||
local frequency = self.Frequency
|
local frequency = self.Frequency
|
||||||
local freqtext = ""
|
local freqtext = ""
|
||||||
if type(frequency) == "table" then
|
if type(frequency) == "table" then
|
||||||
@ -2195,10 +2233,11 @@ function PLAYERTASKCONTROLLER:_EventHandler(EventData)
|
|||||||
--local text = string.format("%s, %s, switch to %s for task assignment!",EventData.IniPlayerName,self.MenuName or self.Name,freqtext)
|
--local text = string.format("%s, %s, switch to %s for task assignment!",EventData.IniPlayerName,self.MenuName or self.Name,freqtext)
|
||||||
local text = string.format(switchtext,playername,self.MenuName or self.Name,freqtext)
|
local text = string.format(switchtext,playername,self.MenuName or self.Name,freqtext)
|
||||||
self.SRSQueue:NewTransmission(text,nil,self.SRS,timer.getAbsTime()+60,2,{EventData.IniGroup},text,30,self.BCFrequency,self.BCModulation)
|
self.SRSQueue:NewTransmission(text,nil,self.SRS,timer.getAbsTime()+60,2,{EventData.IniGroup},text,30,self.BCFrequency,self.BCModulation)
|
||||||
|
end
|
||||||
if EventData.IniPlayerName then
|
if EventData.IniPlayerName then
|
||||||
self.PlayerMenu[EventData.IniPlayerName] = nil
|
self.PlayerMenu[EventData.IniPlayerName] = nil
|
||||||
--self:_BuildMenus(CLIENT:FindByName(EventData.IniUnitName))
|
local player = CLIENT:FindByName(EventData.IniUnitName)
|
||||||
self:_BuildMenus(CLIENT:FindByPlayerName(EventData.IniPlayerName))
|
self:_SwitchMenuForClient(player,"Info")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -2308,7 +2347,7 @@ function PLAYERTASKCONTROLLER:_GetTasksPerType()
|
|||||||
self:T(self.lid.."_GetTasksPerType")
|
self:T(self.lid.."_GetTasksPerType")
|
||||||
local tasktypes = self:_GetAvailableTaskTypes()
|
local tasktypes = self:_GetAvailableTaskTypes()
|
||||||
|
|
||||||
--self:T({tasktypes})
|
--self:I({tasktypes})
|
||||||
|
|
||||||
-- Sort tasks per threat level first
|
-- Sort tasks per threat level first
|
||||||
local datatable = self.TaskQueue:GetDataTable()
|
local datatable = self.TaskQueue:GetDataTable()
|
||||||
@ -2327,11 +2366,23 @@ function PLAYERTASKCONTROLLER:_GetTasksPerType()
|
|||||||
local threat=_data.threat
|
local threat=_data.threat
|
||||||
local task = _data.task -- Ops.PlayerTask#PLAYERTASK
|
local task = _data.task -- Ops.PlayerTask#PLAYERTASK
|
||||||
local type = task.Type
|
local type = task.Type
|
||||||
|
local name = task.Target:GetName()
|
||||||
|
--self:I(name)
|
||||||
if not task:IsDone() then
|
if not task:IsDone() then
|
||||||
|
--self:I(name)
|
||||||
table.insert(tasktypes[type],task)
|
table.insert(tasktypes[type],task)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[[
|
||||||
|
for _type,_data in pairs(tasktypes) do
|
||||||
|
self:I("Task Type: ".._type)
|
||||||
|
for _id,_task in pairs(_data) do
|
||||||
|
self:I("Task Name: ".._task.Target:GetName())
|
||||||
|
end
|
||||||
|
end
|
||||||
|
--]]
|
||||||
|
|
||||||
return tasktypes
|
return tasktypes
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -2443,7 +2494,7 @@ function PLAYERTASKCONTROLLER:_CheckTaskQueue()
|
|||||||
local client = _client --Wrapper.Client#CLIENT
|
local client = _client --Wrapper.Client#CLIENT
|
||||||
local group = client:GetGroup()
|
local group = client:GetGroup()
|
||||||
for _,task in pairs(nexttasks) do
|
for _,task in pairs(nexttasks) do
|
||||||
self:_JoinTask(group,client,task,true)
|
self:_JoinTask(task,true,group,client)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -2462,7 +2513,7 @@ end
|
|||||||
-- @param #PLAYERTASKCONTROLLER self
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
-- @return #PLAYERTASKCONTROLLER self
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
function PLAYERTASKCONTROLLER:_CheckPrecisionTasks()
|
function PLAYERTASKCONTROLLER:_CheckPrecisionTasks()
|
||||||
self:T(self.lid.."_CheckTaskQueue")
|
self:T(self.lid.."_CheckPrecisionTasks")
|
||||||
if self.PrecisionTasks:Count() > 0 and self.precisionbombing then
|
if self.PrecisionTasks:Count() > 0 and self.precisionbombing then
|
||||||
if not self.LasingDrone or self.LasingDrone:IsDead() then
|
if not self.LasingDrone or self.LasingDrone:IsDead() then
|
||||||
-- we need a new drone
|
-- we need a new drone
|
||||||
@ -2965,15 +3016,20 @@ end
|
|||||||
|
|
||||||
--- [Internal] Join a player to a task
|
--- [Internal] Join a player to a task
|
||||||
-- @param #PLAYERTASKCONTROLLER self
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
-- @param Wrapper.Group#GROUP Group
|
|
||||||
-- @param Wrapper.Client#CLIENT Client
|
|
||||||
-- @param Ops.PlayerTask#PLAYERTASK Task
|
-- @param Ops.PlayerTask#PLAYERTASK Task
|
||||||
-- @param #boolean Force Assign task even if client already has one
|
-- @param #boolean Force Assign task even if client already has one
|
||||||
|
-- @param Wrapper.Group#GROUP Group
|
||||||
|
-- @param Wrapper.Client#CLIENT Client
|
||||||
-- @return #PLAYERTASKCONTROLLER self
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task, Force)
|
function PLAYERTASKCONTROLLER:_JoinTask(Task, Force, Group, Client)
|
||||||
|
self:T({Force, Group, Client})
|
||||||
self:T(self.lid.."_JoinTask")
|
self:T(self.lid.."_JoinTask")
|
||||||
|
local force = false
|
||||||
|
if type(Force) == "boolean" then
|
||||||
|
force = Force
|
||||||
|
end
|
||||||
local playername, ttsplayername = self:_GetPlayerName(Client)
|
local playername, ttsplayername = self:_GetPlayerName(Client)
|
||||||
if self.TasksPerPlayer:HasUniqueID(playername) and not Force then
|
if self.TasksPerPlayer:HasUniqueID(playername) and not force then
|
||||||
-- Player already has a task
|
-- Player already has a task
|
||||||
if not self.NoScreenOutput then
|
if not self.NoScreenOutput then
|
||||||
local text = self.gettext:GetEntry("HAVEACTIVETASK",self.locale)
|
local text = self.gettext:GetEntry("HAVEACTIVETASK",self.locale)
|
||||||
@ -3004,7 +3060,7 @@ function PLAYERTASKCONTROLLER:_JoinTask(Group, Client, Task, Force)
|
|||||||
self.TasksPerPlayer:Push(Task,playername)
|
self.TasksPerPlayer:Push(Task,playername)
|
||||||
self:__PlayerJoinedTask(1, Group, Client, Task)
|
self:__PlayerJoinedTask(1, Group, Client, Task)
|
||||||
-- clear menu
|
-- clear menu
|
||||||
self:_BuildMenus(Client,true)
|
self:_SwitchMenuForClient(Client,"Active",1)
|
||||||
end
|
end
|
||||||
if Task.Type == AUFTRAG.Type.PRECISIONBOMBING then
|
if Task.Type == AUFTRAG.Type.PRECISIONBOMBING then
|
||||||
if not self.PrecisionTasks:HasUniqueID(Task.PlayerTaskNr) then
|
if not self.PrecisionTasks:HasUniqueID(Task.PlayerTaskNr) then
|
||||||
@ -3065,19 +3121,23 @@ end
|
|||||||
|
|
||||||
--- [Internal] Show active task info
|
--- [Internal] Show active task info
|
||||||
-- @param #PLAYERTASKCONTROLLER self
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
|
-- @param Ops.PlayerTask#PLAYERTASK Task
|
||||||
-- @param Wrapper.Group#GROUP Group
|
-- @param Wrapper.Group#GROUP Group
|
||||||
-- @param Wrapper.Client#CLIENT Client
|
-- @param Wrapper.Client#CLIENT Client
|
||||||
-- @param Ops.PlayerTask#PLAYERTASK Task
|
|
||||||
-- @return #PLAYERTASKCONTROLLER self
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Group, Client, Task)
|
function PLAYERTASKCONTROLLER:_ActiveTaskInfo(Task, Group, Client)
|
||||||
self:T(self.lid.."_ActiveTaskInfo")
|
self:T(self.lid.."_ActiveTaskInfo")
|
||||||
local playername, ttsplayername = self:_GetPlayerName(Client)
|
local playername, ttsplayername = self:_GetPlayerName(Client)
|
||||||
local text = ""
|
local text = ""
|
||||||
local textTTS = ""
|
local textTTS = ""
|
||||||
if self.TasksPerPlayer:HasUniqueID(playername) or Task then
|
local task = nil
|
||||||
|
if type(Task) ~= "string" then
|
||||||
|
task = Task
|
||||||
|
end
|
||||||
|
if self.TasksPerPlayer:HasUniqueID(playername) or task then
|
||||||
-- NODO: Show multiple?
|
-- NODO: Show multiple?
|
||||||
-- Details
|
-- Details
|
||||||
local task = Task or self.TasksPerPlayer:ReadByID(playername) -- Ops.PlayerTask#PLAYERTASK
|
local task = task or self.TasksPerPlayer:ReadByID(playername) -- Ops.PlayerTask#PLAYERTASK
|
||||||
local tname = self.gettext:GetEntry("TASKNAME",self.locale)
|
local tname = self.gettext:GetEntry("TASKNAME",self.locale)
|
||||||
local ttsname = self.gettext:GetEntry("TASKNAMETTS",self.locale)
|
local ttsname = self.gettext:GetEntry("TASKNAMETTS",self.locale)
|
||||||
local taskname = string.format(tname,task.Type,task.PlayerTaskNr)
|
local taskname = string.format(tname,task.Type,task.PlayerTaskNr)
|
||||||
@ -3389,280 +3449,287 @@ function PLAYERTASKCONTROLLER:_AbortTask(Group, Client)
|
|||||||
if not self.NoScreenOutput then
|
if not self.NoScreenOutput then
|
||||||
local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client)
|
local m=MESSAGE:New(text,15,"Tasking"):ToClient(Client)
|
||||||
end
|
end
|
||||||
self:_BuildMenus(Client,true)
|
self:_SwitchMenuForClient(Client,"Info",1)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--- [Internal] Build Task Info Menu
|
|
||||||
-- @param #PLAYERTASKCONTROLLER self
|
|
||||||
-- @param Wrapper.Group#GROUP group
|
|
||||||
-- @param Wrapper.Client#CLIENT client
|
|
||||||
-- @param #string playername
|
|
||||||
-- @param Core.Menu#MENU_BASE topmenu
|
|
||||||
-- @param #table tasktypes
|
|
||||||
-- @param #table taskpertype
|
|
||||||
-- @param #string newtag
|
|
||||||
-- @return #table taskinfomenu
|
|
||||||
function PLAYERTASKCONTROLLER:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype,newtag)
|
|
||||||
self:T(self.lid.."_BuildTaskInfoMenu")
|
|
||||||
local taskinfomenu = nil
|
|
||||||
if self.taskinfomenu then
|
|
||||||
local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale)
|
|
||||||
local taskinfomenu = MENU_GROUP:New(group,menutaskinfo,topmenu):SetTag(newtag)
|
|
||||||
local ittypes = {}
|
|
||||||
local itaskmenu = {}
|
|
||||||
local tnow = timer.getTime()
|
|
||||||
|
|
||||||
for _tasktype,_data in pairs(tasktypes) do
|
-- TODO - New Menu Manager
|
||||||
ittypes[_tasktype] = MENU_GROUP:New(group,_tasktype,taskinfomenu):SetTag(newtag)
|
--- [Internal] _UpdateJoinMenuTemplate
|
||||||
local tasks = taskpertype[_tasktype] or {}
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
local n = 0
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
for _,_task in pairs(tasks) do
|
function PLAYERTASKCONTROLLER:_UpdateJoinMenuTemplate()
|
||||||
_task = _task -- Ops.PlayerTask#PLAYERTASK
|
self:T("_UpdateJoinMenuTemplate")
|
||||||
local pilotcount = _task:CountClients()
|
if self.TaskQueue:Count() > 0 then
|
||||||
local newtext = "]"
|
local taskpertype = self:_GetTasksPerType()
|
||||||
-- marker for new tasks
|
local JoinMenu = self.JoinMenu -- Core.ClientMenu#CLIENTMENU
|
||||||
if tnow - _task.timestamp < 60 then
|
--self:I(JoinMenu.UUID)
|
||||||
newtext = "*]"
|
local controller = self.JoinTaskMenuTemplate -- Core.ClientMenu#CLIENTMENUMANAGER
|
||||||
|
local actcontroller = self.ActiveTaskMenuTemplate -- Core.ClientMenu#CLIENTMENUMANAGER
|
||||||
|
local actinfomenu = self.ActiveInfoMenu
|
||||||
|
--local entrynumbers = {}
|
||||||
|
--local existingentries = {}
|
||||||
|
local maxn = self.menuitemlimit
|
||||||
|
-- Generate task type menu items
|
||||||
|
for _type,_ in pairs(taskpertype) do
|
||||||
|
local found = controller:FindEntriesByText(_type)
|
||||||
|
--self:I({found})
|
||||||
|
if #found == 0 then
|
||||||
|
local newentry = controller:NewEntry(_type,JoinMenu)
|
||||||
|
controller:AddEntry(newentry)
|
||||||
|
if self.JoinInfoMenu then
|
||||||
|
local newentry = controller:NewEntry(_type,self.JoinInfoMenu)
|
||||||
|
controller:AddEntry(newentry)
|
||||||
end
|
end
|
||||||
|
if actinfomenu then
|
||||||
|
local newentry = actcontroller:NewEntry(_type,self.ActiveInfoMenu)
|
||||||
|
actcontroller:AddEntry(newentry)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local typelist = self:_GetAvailableTaskTypes()
|
||||||
|
-- Slot in Tasks
|
||||||
|
for _tasktype,_data in pairs(typelist) do
|
||||||
|
self:T("**** Building for TaskType: ".._tasktype)
|
||||||
|
--local tasks = taskpertype[_tasktype] or {}
|
||||||
|
for _,_task in pairs(taskpertype[_tasktype]) do
|
||||||
|
_task = _task -- Ops.PlayerTask#PLAYERTASK
|
||||||
|
self:T("**** Building for Task: ".._task.Target:GetName())
|
||||||
|
if _task.InMenu then
|
||||||
|
self:T("**** Task already in Menu ".._task.Target:GetName())
|
||||||
|
else
|
||||||
|
--local pilotcount = _task:CountClients()
|
||||||
|
--local newtext = "]"
|
||||||
|
--local tnow = timer.getTime()
|
||||||
|
-- marker for new tasks
|
||||||
|
--if tnow - _task.timestamp < 60 then
|
||||||
|
--newtext = "*]"
|
||||||
|
--end
|
||||||
local menutaskno = self.gettext:GetEntry("MENUTASKNO",self.locale)
|
local menutaskno = self.gettext:GetEntry("MENUTASKNO",self.locale)
|
||||||
local text = string.format("%s %03d [%d%s",menutaskno,_task.PlayerTaskNr,pilotcount,newtext)
|
--local text = string.format("%s %03d [%d%s",menutaskno,_task.PlayerTaskNr,pilotcount,newtext)
|
||||||
|
local text = string.format("%s %03d",menutaskno,_task.PlayerTaskNr)
|
||||||
if self.UseGroupNames then
|
if self.UseGroupNames then
|
||||||
local name = _task.Target:GetName()
|
local name = _task.Target:GetName()
|
||||||
if name ~= "Unknown" then
|
if name ~= "Unknown" then
|
||||||
text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext)
|
--text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext)
|
||||||
|
text = string.format("%s (%03d)",name,_task.PlayerTaskNr)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if self.UseTypeNames then
|
--local taskentry = MENU_GROUP_COMMAND:New(group,text,ttypes[_tasktype],self._JoinTask,self,group,client,_task):SetTag(newtag)
|
||||||
if _task.TypeName then
|
local parenttable, number = controller:FindEntriesByText(_tasktype,JoinMenu)
|
||||||
--local name = self.gettext:GetEntry(_task.TypeName,self.locale)
|
if number > 0 then
|
||||||
text = string.format("%s (%03d) [%d%s",_task.TypeName,_task.PlayerTaskNr,pilotcount,newtext)
|
local Parent = parenttable[1]
|
||||||
--self:T(self.lid.."Menu text = "..text)
|
local matches, count = controller:FindEntriesByParent(Parent)
|
||||||
|
self:T("***** Join Menu ".._tasktype.. " # of entries: "..count)
|
||||||
|
if count < self.menuitemlimit then
|
||||||
|
local taskentry = controller:NewEntry(text,Parent,self._JoinTask,self,_task,"false")
|
||||||
|
controller:AddEntry(taskentry)
|
||||||
|
_task.InMenu = true
|
||||||
|
if not _task.UUIDS then _task.UUIDS = {} end
|
||||||
|
table.insert(_task.UUIDS,taskentry.UUID)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if self.JoinInfoMenu then
|
||||||
|
local parenttable, number = controller:FindEntriesByText(_tasktype,self.JoinInfoMenu)
|
||||||
|
if number > 0 then
|
||||||
|
local Parent = parenttable[1]
|
||||||
|
local matches, count = controller:FindEntriesByParent(Parent)
|
||||||
|
self:T("***** Join Info Menu ".._tasktype.. " # of entries: "..count)
|
||||||
|
if count < self.menuitemlimit then
|
||||||
|
local taskentry = controller:NewEntry(text,Parent,self._ActiveTaskInfo,self,_task)
|
||||||
|
controller:AddEntry(taskentry)
|
||||||
|
_task.InMenu = true
|
||||||
|
if not _task.UUIDS then _task.UUIDS = {} end
|
||||||
|
table.insert(_task.UUIDS,taskentry.UUID)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if actinfomenu then
|
||||||
|
local parenttable, number = actcontroller:FindEntriesByText(_tasktype,self.ActiveInfoMenu)
|
||||||
|
if number > 0 then
|
||||||
|
local Parent = parenttable[1]
|
||||||
|
local matches, count = actcontroller:FindEntriesByParent(Parent)
|
||||||
|
self:T("***** Active Info Menu ".._tasktype.. " # of entries: "..count)
|
||||||
|
if count < self.menuitemlimit then
|
||||||
|
local taskentry = actcontroller:NewEntry(text,Parent,self._ActiveTaskInfo,self,_task)
|
||||||
|
actcontroller:AddEntry(taskentry)
|
||||||
|
_task.InMenu = true
|
||||||
|
if not _task.AUUIDS then _task.AUUIDS = {} end
|
||||||
|
table.insert(_task.AUUIDS,taskentry.UUID)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
local taskentry = MENU_GROUP_COMMAND:New(group,text,ittypes[_tasktype],self._ActiveTaskInfo,self,group,client,_task):SetTag(newtag)
|
|
||||||
--taskentry:SetTag(playername)
|
|
||||||
itaskmenu[#itaskmenu+1] = taskentry
|
|
||||||
-- keep max items limit
|
|
||||||
n = n + 1
|
|
||||||
if n >= self.menuitemlimit then
|
|
||||||
break
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return taskinfomenu
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--- [Internal] Build client menus
|
|
||||||
-- @param #PLAYERTASKCONTROLLER self
|
|
||||||
-- @param Wrapper.Client#CLIENT Client (optional) build for this client name only
|
|
||||||
-- @param #boolean enforced
|
|
||||||
-- @param #boolean fromsuccess
|
|
||||||
-- @return #PLAYERTASKCONTROLLER self
|
|
||||||
function PLAYERTASKCONTROLLER:_BuildMenus(Client,enforced,fromsuccess)
|
|
||||||
self:T(self.lid.."_BuildMenus")
|
|
||||||
|
|
||||||
if self.MenuBuildLocked and (timer.getAbsTime() - self.MenuBuildLocked < 2) then
|
|
||||||
self:ScheduleOnce(2,self._BuildMenus,self,Client,enforced,fromsuccess)
|
|
||||||
return self
|
return self
|
||||||
else
|
|
||||||
self.MenuBuildLocked = timer.getAbsTime()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local clients = self.ClientSet:GetAliveSet()
|
--- [Internal] _RemoveMenuEntriesForTask
|
||||||
local joinorabort = false
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
local timedbuild = false
|
-- @param #PLAYERTASK Task
|
||||||
|
-- @param Wrapper.Client#CLIENT Client
|
||||||
if Client then
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
-- client + enforced -- join task or abort
|
function PLAYERTASKCONTROLLER:_RemoveMenuEntriesForTask(Task,Client)
|
||||||
clients = {Client}
|
self:T("_RemoveMenuEntriesForTask")
|
||||||
enforced = true
|
--self:I("Task name: "..Task.Target:GetName())
|
||||||
joinorabort = true
|
--self:I("Client: "..Client:GetPlayerName())
|
||||||
|
if Task then
|
||||||
|
if Task.UUIDS and self.JoinTaskMenuTemplate then
|
||||||
|
--self:I("***** JoinTaskMenuTemplate")
|
||||||
|
UTILS.PrintTableToLog(Task.UUIDS)
|
||||||
|
local controller = self.JoinTaskMenuTemplate
|
||||||
|
for _,_uuid in pairs(Task.UUIDS) do
|
||||||
|
local Entry = controller:FindEntryByUUID(_uuid)
|
||||||
|
if Entry then
|
||||||
|
controller:DeleteF10Entry(Entry,Client)
|
||||||
|
controller:DeleteGenericEntry(Entry)
|
||||||
|
UTILS.PrintTableToLog(controller.menutree)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local tasktypes = self:_GetAvailableTaskTypes()
|
if Task.AUUIDS and self.ActiveTaskMenuTemplate then
|
||||||
local taskpertype = self:_GetTasksPerType()
|
--self:I("***** ActiveTaskMenuTemplate")
|
||||||
|
UTILS.PrintTableToLog(Task.AUUIDS)
|
||||||
|
for _,_uuid in pairs(Task.AUUIDS) do
|
||||||
|
local controller = self.ActiveTaskMenuTemplate
|
||||||
|
local Entry = controller:FindEntryByUUID(_uuid)
|
||||||
|
if Entry then
|
||||||
|
controller:DeleteF10Entry(Entry,Client)
|
||||||
|
controller:DeleteGenericEntry(Entry)
|
||||||
|
UTILS.PrintTableToLog(controller.menutree)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
for _,_client in pairs(clients) do
|
Task.UUIDS = nil
|
||||||
if _client and _client:IsAlive() then
|
Task.AUUIDS = nil
|
||||||
local client = _client -- Wrapper.Client#CLIENT
|
end
|
||||||
local group = client:GetGroup()
|
return self
|
||||||
local unknown = self.gettext:GetEntry("UNKNOWN",self.locale)
|
end
|
||||||
local playername = client:GetPlayerName() or unknown
|
|
||||||
|
|
||||||
local oldtag = self.PlayerMenuTag[playername]
|
--- [Internal] _CreateJoinMenuTemplate
|
||||||
local newtag = playername..timer.getAbsTime()
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
self.PlayerMenuTag[playername] = newtag
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
|
function PLAYERTASKCONTROLLER:_CreateJoinMenuTemplate()
|
||||||
|
self:T("_CreateActiveTaskMenuTemplate")
|
||||||
|
|
||||||
if group and client then
|
local menujoin = self.gettext:GetEntry("MENUJOIN",self.locale)
|
||||||
---
|
local menunotasks = self.gettext:GetEntry("MENUNOTASKS",self.locale)
|
||||||
-- TOPMENU
|
local flashtext = self.gettext:GetEntry("FLASHMENU",self.locale)
|
||||||
---
|
|
||||||
|
local JoinTaskMenuTemplate = CLIENTMENUMANAGER:New(self.ClientSet,"JoinTask")
|
||||||
|
|
||||||
|
if not self.JoinTopMenu then
|
||||||
local taskings = self.gettext:GetEntry("MENUTASKING",self.locale)
|
local taskings = self.gettext:GetEntry("MENUTASKING",self.locale)
|
||||||
local longname = self.Name..taskings..self.Type
|
local longname = self.Name..taskings..self.Type
|
||||||
local menuname = self.MenuName or longname
|
local menuname = self.MenuName or longname
|
||||||
local playerhastask = false
|
self.JoinTopMenu = JoinTaskMenuTemplate:NewEntry(menuname,self.MenuParent)
|
||||||
|
|
||||||
if self:_CheckPlayerHasTask(playername) and not fromsuccess then playerhastask = true end
|
|
||||||
local topmenu = nil
|
|
||||||
--local oldmenu = nil
|
|
||||||
local rebuilddone = false
|
|
||||||
|
|
||||||
self:T("Playerhastask = "..tostring(playerhastask).." Enforced = "..tostring(enforced).." Join or Abort = "..tostring(joinorabort))
|
|
||||||
|
|
||||||
-- Cases to rebuild menu
|
|
||||||
-- 1) new player
|
|
||||||
-- 2) player joined a task, joinorabort = true
|
|
||||||
-- 3) player left a task, joinorabort = true
|
|
||||||
-- 4) player has no task, but number of tasks changed, and last build > 30 secs ago
|
|
||||||
if self.PlayerMenu[playername] then
|
|
||||||
-- NOT a new player
|
|
||||||
-- 2)+3) Join or abort?
|
|
||||||
if joinorabort then
|
|
||||||
self.PlayerMenu[playername]:RemoveSubMenus()
|
|
||||||
self.PlayerMenu[playername] = MENU_GROUP:New(group,menuname,self.MenuParent)
|
|
||||||
self.PlayerMenu[playername]:SetTag(newtag)
|
|
||||||
topmenu = self.PlayerMenu[playername]
|
|
||||||
elseif (not playerhastask) or enforced then
|
|
||||||
-- 4) last build > 30 secs?
|
|
||||||
local T0 = timer.getAbsTime()
|
|
||||||
local TDiff = T0-self.PlayerMenu[playername].PTTimeStamp
|
|
||||||
self:T("TDiff = "..string.format("%.2d",TDiff))
|
|
||||||
if TDiff >= self.holdmenutime then
|
|
||||||
--self.PlayerMenu[playername]:RemoveSubMenus()
|
|
||||||
--oldmenu = self.PlayerMenu[playername]
|
|
||||||
--self.PlayerMenu[playername] = nil
|
|
||||||
|
|
||||||
--self.PlayerMenu[playername]:RemoveSubMenus()
|
|
||||||
--self.PlayerMenu[playername] = MENU_GROUP:New(group,menuname,self.MenuParent)
|
|
||||||
--self.PlayerMenu[playername]:SetTag(newtag)
|
|
||||||
--self.PlayerMenu[playername].PTTimeStamp = timer.getAbsTime()
|
|
||||||
--timedbuild = true
|
|
||||||
end
|
|
||||||
topmenu = self.PlayerMenu[playername]
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- 1) new player#
|
|
||||||
topmenu = MENU_GROUP:New(group,menuname,self.MenuParent)
|
|
||||||
self.PlayerMenu[playername] = topmenu
|
|
||||||
self.PlayerMenu[playername]:SetTag(newtag)
|
|
||||||
self.PlayerMenu[playername].PTTimeStamp = timer.getAbsTime()
|
|
||||||
enforced = true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
---
|
if self.AllowFlash then
|
||||||
-- ACTIVE TASK MENU
|
JoinTaskMenuTemplate:NewEntry(flashtext,self.JoinTopMenu,self._SwitchFlashing,self)
|
||||||
---
|
end
|
||||||
if playerhastask and enforced then
|
|
||||||
self:T("Building Active Task Menus for "..playername)
|
self.JoinMenu = JoinTaskMenuTemplate:NewEntry(menujoin,self.JoinTopMenu)
|
||||||
rebuilddone = true
|
|
||||||
|
if self.taskinfomenu then
|
||||||
|
local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale)
|
||||||
|
self.JoinInfoMenu = JoinTaskMenuTemplate:NewEntry(menutaskinfo,self.JoinTopMenu)
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.TaskQueue:Count() == 0 then
|
||||||
|
JoinTaskMenuTemplate:NewEntry(menunotasks,self.JoinMenu)
|
||||||
|
end
|
||||||
|
|
||||||
|
self.JoinTaskMenuTemplate = JoinTaskMenuTemplate
|
||||||
|
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
|
--- [Internal] _CreateActiveTaskMenuTemplate
|
||||||
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
|
function PLAYERTASKCONTROLLER:_CreateActiveTaskMenuTemplate()
|
||||||
|
self:T("_CreateActiveTaskMenuTemplate")
|
||||||
|
|
||||||
local menuactive = self.gettext:GetEntry("MENUACTIVE",self.locale)
|
local menuactive = self.gettext:GetEntry("MENUACTIVE",self.locale)
|
||||||
local menuinfo = self.gettext:GetEntry("MENUINFO",self.locale)
|
local menuinfo = self.gettext:GetEntry("MENUINFO",self.locale)
|
||||||
local menumark = self.gettext:GetEntry("MENUMARK",self.locale)
|
local menumark = self.gettext:GetEntry("MENUMARK",self.locale)
|
||||||
local menusmoke = self.gettext:GetEntry("MENUSMOKE",self.locale)
|
local menusmoke = self.gettext:GetEntry("MENUSMOKE",self.locale)
|
||||||
local menuflare = self.gettext:GetEntry("MENUFLARE",self.locale)
|
local menuflare = self.gettext:GetEntry("MENUFLARE",self.locale)
|
||||||
|
local menuillu = self.gettext:GetEntry("MENUILLU",self.locale)
|
||||||
local menuabort = self.gettext:GetEntry("MENUABORT",self.locale)
|
local menuabort = self.gettext:GetEntry("MENUABORT",self.locale)
|
||||||
|
|
||||||
local active = MENU_GROUP:New(group,menuactive,topmenu):SetTag(newtag)
|
local ActiveTaskMenuTemplate = CLIENTMENUMANAGER:New(self.ActiveClientSet,"ActiveTask")
|
||||||
local info = MENU_GROUP_COMMAND:New(group,menuinfo,active,self._ActiveTaskInfo,self,group,client):SetTag(newtag)
|
|
||||||
local mark = MENU_GROUP_COMMAND:New(group,menumark,active,self._MarkTask,self,group,client):SetTag(newtag)
|
|
||||||
if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A then
|
|
||||||
if self.noflaresmokemenu ~= true then
|
|
||||||
-- no smoking/flaring here if A2A or designer has set noflaresmokemenu to true
|
|
||||||
local smoke = MENU_GROUP_COMMAND:New(group,menusmoke,active,self._SmokeTask,self,group,client):SetTag(newtag)
|
|
||||||
local flare = MENU_GROUP_COMMAND:New(group,menuflare,active,self._FlareTask,self,group,client):SetTag(newtag)
|
|
||||||
local IsNight = client:GetCoordinate():IsNight()
|
|
||||||
if IsNight then
|
|
||||||
local light = MENU_GROUP_COMMAND:New(group,menuflare,active,self._IlluminateTask,self,group,client):SetTag(newtag)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local abort = MENU_GROUP_COMMAND:New(group,menuabort,active,self._AbortTask,self,group,client):SetTag(newtag)
|
|
||||||
if self.activehasinfomenu and self.taskinfomenu then
|
|
||||||
self:T("Building Active-Info Menus for "..playername)
|
|
||||||
if self.PlayerInfoMenu[playername] then
|
|
||||||
self.PlayerInfoMenu[playername]:RemoveSubMenus(nil,oldtag)
|
|
||||||
end
|
|
||||||
self.PlayerInfoMenu[playername] = self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype,newtag)
|
|
||||||
end
|
|
||||||
elseif (self.TaskQueue:Count() > 0 and enforced) or (not playerhastask and (timedbuild or joinorabort)) then
|
|
||||||
self:T("Building Join Menus for "..playername)
|
|
||||||
rebuilddone = true
|
|
||||||
---
|
|
||||||
-- JOIN TASK MENU
|
|
||||||
---
|
|
||||||
local menujoin = self.gettext:GetEntry("MENUJOIN",self.locale)
|
|
||||||
|
|
||||||
if self.PlayerJoinMenu[playername] then
|
if not self.ActiveTopMenu then
|
||||||
self.PlayerJoinMenu[playername]:RemoveSubMenus(nil,oldtag)
|
local taskings = self.gettext:GetEntry("MENUTASKING",self.locale)
|
||||||
|
local longname = self.Name..taskings..self.Type
|
||||||
|
local menuname = self.MenuName or longname
|
||||||
|
self.ActiveTopMenu = ActiveTaskMenuTemplate:NewEntry(menuname,self.MenuParent)
|
||||||
end
|
end
|
||||||
|
|
||||||
local joinmenu = MENU_GROUP:New(group,menujoin,topmenu):SetTag(newtag)
|
if self.AllowFlash then
|
||||||
self.PlayerJoinMenu[playername] = joinmenu
|
|
||||||
|
|
||||||
local ttypes = {}
|
|
||||||
local taskmenu = {}
|
|
||||||
for _tasktype,_data in pairs(tasktypes) do
|
|
||||||
ttypes[_tasktype] = MENU_GROUP:New(group,_tasktype,joinmenu):SetTag(newtag)
|
|
||||||
local tasks = taskpertype[_tasktype] or {}
|
|
||||||
local n = 0
|
|
||||||
for _,_task in pairs(tasks) do
|
|
||||||
_task = _task -- Ops.PlayerTask#PLAYERTASK
|
|
||||||
local pilotcount = _task:CountClients()
|
|
||||||
local newtext = "]"
|
|
||||||
local tnow = timer.getTime()
|
|
||||||
-- marker for new tasks
|
|
||||||
if tnow - _task.timestamp < 60 then
|
|
||||||
newtext = "*]"
|
|
||||||
end
|
|
||||||
local menutaskno = self.gettext:GetEntry("MENUTASKNO",self.locale)
|
|
||||||
local text = string.format("%s %03d [%d%s",menutaskno,_task.PlayerTaskNr,pilotcount,newtext)
|
|
||||||
if self.UseGroupNames then
|
|
||||||
local name = _task.Target:GetName()
|
|
||||||
if name ~= "Unknown" then
|
|
||||||
text = string.format("%s (%03d) [%d%s",name,_task.PlayerTaskNr,pilotcount,newtext)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
local taskentry = MENU_GROUP_COMMAND:New(group,text,ttypes[_tasktype],self._JoinTask,self,group,client,_task):SetTag(newtag)
|
|
||||||
--taskentry:SetTag(playername)
|
|
||||||
taskmenu[#taskmenu+1] = taskentry
|
|
||||||
n = n + 1
|
|
||||||
if n >= self.menuitemlimit then
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if self.taskinfomenu then
|
|
||||||
self:T("Building Join-Info Menus for "..playername)
|
|
||||||
if self.PlayerInfoMenu[playername] then
|
|
||||||
self.PlayerInfoMenu[playername]:RemoveSubMenus(nil,oldtag)
|
|
||||||
end
|
|
||||||
self.PlayerInfoMenu[playername] = self:_BuildTaskInfoMenu(group,client,playername,topmenu,tasktypes,taskpertype,newtag)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if self.AllowFlash and topmenu ~= nil then
|
|
||||||
local flashtext = self.gettext:GetEntry("FLASHMENU",self.locale)
|
local flashtext = self.gettext:GetEntry("FLASHMENU",self.locale)
|
||||||
local flashmenu = MENU_GROUP_COMMAND:New(group,flashtext,topmenu,self._SwitchFlashing,self,group,client):SetTag(newtag)
|
ActiveTaskMenuTemplate:NewEntry(flashtext,self.ActiveTopMenu,self._SwitchFlashing,self)
|
||||||
end
|
end
|
||||||
if self.TaskQueue:Count() == 0 then
|
|
||||||
self:T("No open tasks info")
|
local active = ActiveTaskMenuTemplate:NewEntry(menuactive,self.ActiveTopMenu)
|
||||||
local menunotasks = self.gettext:GetEntry("MENUNOTASKS",self.locale)
|
ActiveTaskMenuTemplate:NewEntry(menuinfo,active,self._ActiveTaskInfo,self,"NONE")
|
||||||
local joinmenu = MENU_GROUP:New(group,menunotasks,self.PlayerMenu[playername]):SetTag(newtag)
|
ActiveTaskMenuTemplate:NewEntry(menumark,active,self._MarkTask,self)
|
||||||
rebuilddone = true
|
|
||||||
|
if self.Type ~= PLAYERTASKCONTROLLER.Type.A2A and self.noflaresmokemenu ~= true then
|
||||||
|
ActiveTaskMenuTemplate:NewEntry(menusmoke,active,self._SmokeTask,self)
|
||||||
|
ActiveTaskMenuTemplate:NewEntry(menuflare,active,self._FlareTask,self)
|
||||||
|
|
||||||
|
if self.illumenu then
|
||||||
|
ActiveTaskMenuTemplate:NewEntry(menuillu,active,self._IlluminateTask,self)
|
||||||
end
|
end
|
||||||
---
|
|
||||||
-- REFRESH MENU
|
|
||||||
---
|
|
||||||
if rebuilddone then
|
|
||||||
--self.PlayerMenu[playername]:RemoveSubMenus(nil,oldtag)
|
|
||||||
--self.PlayerMenu[playername]:Set()
|
|
||||||
self.PlayerMenu[playername]:Refresh()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
ActiveTaskMenuTemplate:NewEntry(menuabort,active,self._AbortTask,self)
|
||||||
|
self.ActiveTaskMenuTemplate = ActiveTaskMenuTemplate
|
||||||
|
|
||||||
|
if self.taskinfomenu and self.activehasinfomenu then
|
||||||
|
local menutaskinfo = self.gettext:GetEntry("MENUTASKINFO",self.locale)
|
||||||
|
self.ActiveInfoMenu = ActiveTaskMenuTemplate:NewEntry(menutaskinfo,self.ActiveTopMenu)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- [Internal] _SwitchMenuForClient
|
||||||
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
|
-- @param Wrapper.Client#CLIENT Client The client
|
||||||
|
-- @param #string MenuType
|
||||||
|
-- @param #number Delay
|
||||||
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
|
function PLAYERTASKCONTROLLER:_SwitchMenuForClient(Client,MenuType,Delay)
|
||||||
|
self:T(self.lid.."_SwitchMenuForClient")
|
||||||
|
if Delay then
|
||||||
|
self:ScheduleOnce(Delay,self._SwitchMenuForClient,self,Client,MenuType)
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
if MenuType == "Info" then
|
||||||
|
self.ClientSet:AddClientsByName(Client:GetName())
|
||||||
|
self.ActiveClientSet:Remove(Client:GetName(),true)
|
||||||
|
self.ActiveTaskMenuTemplate:ResetMenu(Client)
|
||||||
|
self.JoinTaskMenuTemplate:ResetMenu(Client)
|
||||||
|
self.JoinTaskMenuTemplate:Propagate(Client)
|
||||||
|
elseif MenuType == "Active" then
|
||||||
|
self.ActiveClientSet:AddClientsByName(Client:GetName())
|
||||||
|
self.ClientSet:Remove(Client:GetName(),true)
|
||||||
|
self.ActiveTaskMenuTemplate:ResetMenu(Client)
|
||||||
|
self.JoinTaskMenuTemplate:ResetMenu(Client)
|
||||||
|
self.ActiveTaskMenuTemplate:Propagate(Client)
|
||||||
|
else
|
||||||
|
self:E(self.lid .."Unknown menu type in _SwitchMenuForClient:"..tostring(MenuType))
|
||||||
end
|
end
|
||||||
self.MenuBuildLocked = false
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -3810,8 +3877,8 @@ end
|
|||||||
-- @param Core.Menu#MENU_MISSION Menu
|
-- @param Core.Menu#MENU_MISSION Menu
|
||||||
-- @return #PLAYERTASKCONTROLLER self
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
function PLAYERTASKCONTROLLER:SetParentMenu(Menu)
|
function PLAYERTASKCONTROLLER:SetParentMenu(Menu)
|
||||||
self:T(self.lid.."SetParentName")
|
self:T(self.lid.."SetParentMenu")
|
||||||
self.MenuParent = Menu
|
--self.MenuParent = Menu
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -3965,6 +4032,20 @@ end
|
|||||||
-- TODO: FSM Functions PLAYERTASKCONTROLLER
|
-- TODO: FSM Functions PLAYERTASKCONTROLLER
|
||||||
-------------------------------------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
--- [Internal] On after start call
|
||||||
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
|
-- @param #string From
|
||||||
|
-- @param #string Event
|
||||||
|
-- @param #string To
|
||||||
|
-- @return #PLAYERTASKCONTROLLER self
|
||||||
|
function PLAYERTASKCONTROLLER:onafterStart(From, Event, To)
|
||||||
|
self:T({From, Event, To})
|
||||||
|
self:T(self.lid.."onafterStart")
|
||||||
|
self:_CreateJoinMenuTemplate()
|
||||||
|
self:_CreateActiveTaskMenuTemplate()
|
||||||
|
return self
|
||||||
|
end
|
||||||
|
|
||||||
--- [Internal] On after Status call
|
--- [Internal] On after Status call
|
||||||
-- @param #PLAYERTASKCONTROLLER self
|
-- @param #PLAYERTASKCONTROLLER self
|
||||||
-- @param #string From
|
-- @param #string From
|
||||||
@ -3994,7 +4075,7 @@ function PLAYERTASKCONTROLLER:onafterStatus(From, Event, To)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
self:_BuildMenus(nil,enforcedmenu)
|
self:_UpdateJoinMenuTemplate()
|
||||||
|
|
||||||
if self.verbose then
|
if self.verbose then
|
||||||
local text = string.format("%s | New Targets: %02d | Active Tasks: %02d | Active Players: %02d | Assigned Tasks: %02d",self.MenuName, targetcount,taskcount,playercount,assignedtasks)
|
local text = string.format("%s | New Targets: %02d | Active Tasks: %02d | Active Players: %02d | Assigned Tasks: %02d",self.MenuName, targetcount,taskcount,playercount,assignedtasks)
|
||||||
@ -4041,6 +4122,16 @@ function PLAYERTASKCONTROLLER:onafterTaskCancelled(From, Event, To, Task)
|
|||||||
taskname = string.format(canceltxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType))
|
taskname = string.format(canceltxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType))
|
||||||
self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2)
|
self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local clients=Task:GetClientObjects()
|
||||||
|
for _,client in pairs(clients) do
|
||||||
|
self:_RemoveMenuEntriesForTask(Task,client)
|
||||||
|
--self:_SwitchMenuForClient(client,"Info")
|
||||||
|
end
|
||||||
|
for _,client in pairs(clients) do
|
||||||
|
--self:_RemoveMenuEntriesForTask(Task,client)
|
||||||
|
self:_SwitchMenuForClient(client,"Info",5)
|
||||||
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -4065,9 +4156,15 @@ function PLAYERTASKCONTROLLER:onafterTaskSuccess(From, Event, To, Task)
|
|||||||
taskname = string.format(succtxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType))
|
taskname = string.format(succtxttts, self.MenuName or self.Name, Task.PlayerTaskNr, tostring(Task.TTSType))
|
||||||
self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2)
|
self.SRSQueue:NewTransmission(taskname,nil,self.SRS,nil,2)
|
||||||
end
|
end
|
||||||
|
|
||||||
local clients=Task:GetClientObjects()
|
local clients=Task:GetClientObjects()
|
||||||
for _,client in pairs(clients) do
|
for _,client in pairs(clients) do
|
||||||
self:_BuildMenus(client,true,true)
|
self:_RemoveMenuEntriesForTask(Task,client)
|
||||||
|
--self:_SwitchMenuForClient(client,"Info")
|
||||||
|
end
|
||||||
|
for _,client in pairs(clients) do
|
||||||
|
-- self:_RemoveMenuEntriesForTask(Task,client)
|
||||||
|
self:_SwitchMenuForClient(client,"Info",5)
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|||||||
@ -236,6 +236,7 @@ ENUMS.WeaponType.Any={
|
|||||||
-- @field #string ESCORT Escort another group.
|
-- @field #string ESCORT Escort another group.
|
||||||
-- @field #string FIGHTERSWEEP Fighter sweep.
|
-- @field #string FIGHTERSWEEP Fighter sweep.
|
||||||
-- @field #string GROUNDATTACK Ground attack.
|
-- @field #string GROUNDATTACK Ground attack.
|
||||||
|
-- @field #string GROUNDESCORT Ground escort another group.
|
||||||
-- @field #string INTERCEPT Intercept.
|
-- @field #string INTERCEPT Intercept.
|
||||||
-- @field #string PINPOINTSTRIKE Pinpoint strike.
|
-- @field #string PINPOINTSTRIKE Pinpoint strike.
|
||||||
-- @field #string RECONNAISSANCE Reconnaissance mission.
|
-- @field #string RECONNAISSANCE Reconnaissance mission.
|
||||||
@ -251,6 +252,7 @@ ENUMS.MissionTask={
|
|||||||
CAP="CAP",
|
CAP="CAP",
|
||||||
CAS="CAS",
|
CAS="CAS",
|
||||||
ESCORT="Escort",
|
ESCORT="Escort",
|
||||||
|
GROUNDESCORT="Ground escort",
|
||||||
FIGHTERSWEEP="Fighter Sweep",
|
FIGHTERSWEEP="Fighter Sweep",
|
||||||
GROUNDATTACK="Ground Attack",
|
GROUNDATTACK="Ground Attack",
|
||||||
INTERCEPT="Intercept",
|
INTERCEPT="Intercept",
|
||||||
|
|||||||
@ -299,14 +299,14 @@ end
|
|||||||
-- @param #table tbl Input table.
|
-- @param #table tbl Input table.
|
||||||
UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function
|
UTILS.OneLineSerialize = function( tbl ) -- serialization of a table all on a single line, no comments, made to replace old get_table_string function
|
||||||
|
|
||||||
lookup_table = {}
|
local lookup_table = {}
|
||||||
|
|
||||||
local function _Serialize( tbl )
|
local function _Serialize( tbl )
|
||||||
|
|
||||||
if type(tbl) == 'table' then --function only works for tables!
|
if type(tbl) == 'table' then --function only works for tables!
|
||||||
|
|
||||||
if lookup_table[tbl] then
|
if lookup_table[tbl] then
|
||||||
return lookup_table[object]
|
return lookup_table[tbl]
|
||||||
end
|
end
|
||||||
|
|
||||||
local tbl_str = {}
|
local tbl_str = {}
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
-- @image Wrapper_Airbase.JPG
|
-- @image Wrapper_Airbase.JPG
|
||||||
|
|
||||||
|
|
||||||
--- @type AIRBASE
|
-- @type AIRBASE
|
||||||
-- @field #string ClassName Name of the class, i.e. "AIRBASE".
|
-- @field #string ClassName Name of the class, i.e. "AIRBASE".
|
||||||
-- @field #table CategoryName Names of airbase categories.
|
-- @field #table CategoryName Names of airbase categories.
|
||||||
-- @field #string AirbaseName Name of the airbase.
|
-- @field #string AirbaseName Name of the airbase.
|
||||||
@ -1501,16 +1501,16 @@ function AIRBASE:FindFreeParkingSpotForAircraft(group, terminaltype, scanradius,
|
|||||||
|
|
||||||
-- Get the aircraft size, i.e. it's longest side of x,z.
|
-- Get the aircraft size, i.e. it's longest side of x,z.
|
||||||
local aircraft = nil -- fix local problem below
|
local aircraft = nil -- fix local problem below
|
||||||
local _aircraftsize, ax,ay,az
|
-- SU27 dimensions as default
|
||||||
|
local _aircraftsize = 23
|
||||||
|
local ax = 23 -- l
|
||||||
|
local ay = 7 -- h
|
||||||
|
local az = 17 -- w
|
||||||
if group and group.ClassName == "GROUP" then
|
if group and group.ClassName == "GROUP" then
|
||||||
aircraft=group:GetUnit(1)
|
aircraft=group:GetUnit(1)
|
||||||
|
if aircraft then
|
||||||
_aircraftsize, ax,ay,az=aircraft:GetObjectSize()
|
_aircraftsize, ax,ay,az=aircraft:GetObjectSize()
|
||||||
else
|
end
|
||||||
-- SU27 dimensions
|
|
||||||
_aircraftsize = 23
|
|
||||||
ax = 23 -- length
|
|
||||||
ay = 7 -- height
|
|
||||||
az = 17 -- width
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -52,6 +52,7 @@
|
|||||||
-- * @{#CONTROLLABLE.TaskEmbarking}: (AIR) Move the controllable to a Vec2 Point, wait for a defined duration and embark a controllable.
|
-- * @{#CONTROLLABLE.TaskEmbarking}: (AIR) Move the controllable to a Vec2 Point, wait for a defined duration and embark a controllable.
|
||||||
-- * @{#CONTROLLABLE.TaskEmbarkToTransport}: (GROUND) Embark to a Transport landed at a location.
|
-- * @{#CONTROLLABLE.TaskEmbarkToTransport}: (GROUND) Embark to a Transport landed at a location.
|
||||||
-- * @{#CONTROLLABLE.TaskEscort}: (AIR) Escort another airborne controllable.
|
-- * @{#CONTROLLABLE.TaskEscort}: (AIR) Escort another airborne controllable.
|
||||||
|
-- * @{#CONTROLLABLE.TaskGroundEscort}: (AIR/HELO) Escort a ground controllable.
|
||||||
-- * @{#CONTROLLABLE.TaskFAC_AttackGroup}: (AIR + GROUND) The task makes the controllable/unit a FAC and orders the FAC to control the target (enemy ground controllable) destruction.
|
-- * @{#CONTROLLABLE.TaskFAC_AttackGroup}: (AIR + GROUND) The task makes the controllable/unit a FAC and orders the FAC to control the target (enemy ground controllable) destruction.
|
||||||
-- * @{#CONTROLLABLE.TaskFireAtPoint}: (GROUND) Fire some or all ammunition at a VEC2 point.
|
-- * @{#CONTROLLABLE.TaskFireAtPoint}: (GROUND) Fire some or all ammunition at a VEC2 point.
|
||||||
-- * @{#CONTROLLABLE.TaskFollow}: (AIR) Following another airborne controllable.
|
-- * @{#CONTROLLABLE.TaskFollow}: (AIR) Following another airborne controllable.
|
||||||
@ -1480,15 +1481,53 @@ function CONTROLLABLE:TaskFollow( FollowControllable, Vec3, LastWaypointIndex )
|
|||||||
return DCSTask
|
return DCSTask
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- (AIR/HELO) Escort a ground controllable.
|
||||||
|
-- The unit / controllable will follow lead unit of the other controllable, additional units of both controllables will continue following their leaders.
|
||||||
|
-- The unit / controllable will also protect that controllable from threats of specified types.
|
||||||
|
-- @param #CONTROLLABLE self
|
||||||
|
-- @param #CONTROLLABLE FollowControllable The controllable to be escorted.
|
||||||
|
-- @param #number LastWaypointIndex (optional) Detach waypoint of another controllable. Once reached the unit / controllable Escort task is finished.
|
||||||
|
-- @param #number OrbitDistance (optional) Maximum distance helo will orbit around the ground unit in meters. Defaults to 2000 meters.
|
||||||
|
-- @param DCS#AttributeNameArray TargetTypes (optional) Array of AttributeName that is contains threat categories allowed to engage. Default {"Ground vehicles"}. See [https://wiki.hoggit.us/view/DCS_enum_attributes](https://wiki.hoggit.us/view/DCS_enum_attributes)
|
||||||
|
-- @return DCS#Task The DCS task structure.
|
||||||
|
function CONTROLLABLE:TaskGroundEscort( FollowControllable, LastWaypointIndex, OrbitDistance, TargetTypes )
|
||||||
|
|
||||||
|
-- Escort = {
|
||||||
|
-- id = 'GroundEscort',
|
||||||
|
-- params = {
|
||||||
|
-- groupId = Group.ID, -- must
|
||||||
|
-- engagementDistMax = Distance, -- Must. With his task it does not appear to actually define the range AI are allowed to attack at, rather it defines the size length of the orbit. The helicopters will fly up to this set distance before returning to the escorted group.
|
||||||
|
-- lastWptIndexFlag = boolean, -- optional
|
||||||
|
-- lastWptIndex = number, -- optional
|
||||||
|
-- targetTypes = array of AttributeName, -- must
|
||||||
|
-- lastWptIndexFlagChangedManually = boolean, -- must be true
|
||||||
|
-- }
|
||||||
|
-- }
|
||||||
|
|
||||||
|
local DCSTask = {
|
||||||
|
id = 'GroundEscort',
|
||||||
|
params = {
|
||||||
|
groupId = FollowControllable and FollowControllable:GetID() or nil,
|
||||||
|
engagementDistMax = OrbitDistance or 2000,
|
||||||
|
lastWptIndexFlag = LastWaypointIndex and true or false,
|
||||||
|
lastWptIndex = LastWaypointIndex,
|
||||||
|
targetTypes = TargetTypes or {"Ground vehicles"},
|
||||||
|
lastWptIndexFlagChangedManually = true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return DCSTask
|
||||||
|
end
|
||||||
|
|
||||||
--- (AIR) Escort another airborne controllable.
|
--- (AIR) Escort another airborne controllable.
|
||||||
-- The unit / controllable will follow lead unit of another controllable, wingmens of both controllables will continue following their leaders.
|
-- The unit / controllable will follow lead unit of another controllable, wingmens of both controllables will continue following their leaders.
|
||||||
-- The unit / controllable will also protect that controllable from threats of specified types.
|
-- The unit / controllable will also protect that controllable from threats of specified types.
|
||||||
-- @param #CONTROLLABLE self
|
-- @param #CONTROLLABLE self
|
||||||
-- @param #CONTROLLABLE FollowControllable The controllable to be escorted.
|
-- @param #CONTROLLABLE FollowControllable The controllable to be escorted.
|
||||||
-- @param DCS#Vec3 Vec3 Position of the unit / lead unit of the controllable relative lead unit of another controllable in frame reference oriented by course of lead unit of another controllable. If another controllable is on land the unit / controllable will orbit around.
|
-- @param DCS#Vec3 Vec3 Position of the unit / lead unit of the controllable relative lead unit of another controllable in frame reference oriented by course of lead unit of another controllable. If another controllable is on land the unit / controllable will orbit around.
|
||||||
-- @param #number LastWaypointIndex Detach waypoint of another controllable. Once reached the unit / controllable Follow task is finished.
|
-- @param #number LastWaypointIndex Detach waypoint of another controllable. Once reached the unit / controllable Escort task is finished.
|
||||||
-- @param #number EngagementDistance Maximal distance from escorted controllable to threat. If the threat is already engaged by escort escort will disengage if the distance becomes greater than 1.5 * engagementDistMax.
|
-- @param #number EngagementDistance Maximal distance from escorted controllable to threat in meters. If the threat is already engaged by escort escort will disengage if the distance becomes greater than 1.5 * engagementDistMax.
|
||||||
-- @param DCS#AttributeNameArray TargetTypes Array of AttributeName that is contains threat categories allowed to engage. Default {"Air"}.
|
-- @param DCS#AttributeNameArray TargetTypes Array of AttributeName that is contains threat categories allowed to engage. Default {"Air"}. See https://wiki.hoggit.us/view/DCS_enum_attributes
|
||||||
-- @return DCS#Task The DCS task structure.
|
-- @return DCS#Task The DCS task structure.
|
||||||
function CONTROLLABLE:TaskEscort( FollowControllable, Vec3, LastWaypointIndex, EngagementDistance, TargetTypes )
|
function CONTROLLABLE:TaskEscort( FollowControllable, Vec3, LastWaypointIndex, EngagementDistance, TargetTypes )
|
||||||
|
|
||||||
@ -1504,8 +1543,7 @@ function CONTROLLABLE:TaskEscort( FollowControllable, Vec3, LastWaypointIndex, E
|
|||||||
-- }
|
-- }
|
||||||
-- }
|
-- }
|
||||||
|
|
||||||
local DCSTask
|
local DCSTask = {
|
||||||
DCSTask = {
|
|
||||||
id = 'Escort',
|
id = 'Escort',
|
||||||
params = {
|
params = {
|
||||||
groupId = FollowControllable and FollowControllable:GetID() or nil,
|
groupId = FollowControllable and FollowControllable:GetID() or nil,
|
||||||
|
|||||||
@ -32,6 +32,7 @@ Core/Spot.lua
|
|||||||
Core/TextAndSound.lua
|
Core/TextAndSound.lua
|
||||||
Core/Condition.lua
|
Core/Condition.lua
|
||||||
Core/Pathline.lua
|
Core/Pathline.lua
|
||||||
|
Core/ClientMenu.lua
|
||||||
|
|
||||||
Wrapper/Object.lua
|
Wrapper/Object.lua
|
||||||
Wrapper/Identifiable.lua
|
Wrapper/Identifiable.lua
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user