Compare commits

..

3 Commits

Author SHA1 Message Date
kaltokri
4c66fd7cab Renamed basic.md to concepts.md and added more text 2024-01-22 17:36:32 +01:00
kaltokri
80f76b26c2 Small fixes in advanced guide 2024-01-19 16:57:33 +01:00
kaltokri
4e956c3203 New guides added 2024-01-19 11:41:09 +01:00
78 changed files with 1993 additions and 83493 deletions

View File

@@ -107,7 +107,7 @@ jobs:
- name: Run LuaSrcDiet
run: |
luasrcdiet --basic --opt-emptylines ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua
#########################################################################
# Push to MOOSE_INCLUDE
#########################################################################

View File

@@ -33,7 +33,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v3
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
@@ -43,7 +43,7 @@ jobs:
working-directory: docs/
- name: Setup Pages
id: pages
uses: actions/configure-pages@v4
uses: actions/configure-pages@v3
- name: Build with Jekyll
# Outputs to the './_site' directory by default
run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}"
@@ -52,7 +52,7 @@ jobs:
working-directory: docs/
- name: Upload artifact
# Automatically uploads an artifact from the './_site' directory by default
uses: actions/upload-pages-artifact@v3
uses: actions/upload-pages-artifact@v1
with:
path: docs/_site/
@@ -66,13 +66,13 @@ jobs:
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@v1
check:
runs-on: ubuntu-latest
needs: deploy
steps:
- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@v3
- run: npm install linkinator
- run: npx linkinator https://flightcontrol-master.github.io/MOOSE/ --verbosity error --timeout 5000 --recurse --skip "(java.com)" --retry-errors --retry-errors-count 3 --retry-errors-jitter

7
.gitignore vendored
View File

@@ -28,13 +28,6 @@ local.properties
.buildpath
#####################
## Visual Studio Code
#####################
*.code-workspace
.vscode/
#################
## Visual Studio
#################

View File

@@ -1144,19 +1144,6 @@ function BASE:TraceClassMethod( Class, Method )
self:I( "Tracing method " .. Method .. " of class " .. Class )
end
--- (Internal) Serialize arguments
-- @param #BASE self
-- @param #table Arguments
-- @return #string Text
function BASE:_Serialize(Arguments)
local text = UTILS.PrintTableToLog({Arguments}, 0, true)
text = string.gsub(text,"\n","")
text = string.gsub(text,"%(%(","%(")
text = string.gsub(text,"%)%)","%)")
text = string.gsub(text,"(%s+)","")
return text
end
--- Trace a function call. This function is private.
-- @param #BASE self
-- @param Arguments A #table or any field.
@@ -1181,7 +1168,7 @@ function BASE:_F( Arguments, DebugInfoCurrentParam, DebugInfoFromParam )
if DebugInfoFrom then
LineFrom = DebugInfoFrom.currentline
end
env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "F", self.ClassName, self.ClassID, Function, BASE:_Serialize(Arguments) ) )
env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "F", self.ClassName, self.ClassID, Function, UTILS.BasicSerialize( Arguments ) ) )
end
end
end
@@ -1255,7 +1242,7 @@ function BASE:_T( Arguments, DebugInfoCurrentParam, DebugInfoFromParam )
if DebugInfoFrom then
LineFrom = DebugInfoFrom.currentline
end
env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s", LineCurrent, LineFrom, "T", self.ClassName, self.ClassID, BASE:_Serialize(Arguments) ) )
env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s", LineCurrent, LineFrom, "T", self.ClassName, self.ClassID, UTILS.BasicSerialize( Arguments ) ) )
end
end
end
@@ -1327,7 +1314,7 @@ function BASE:E( Arguments )
env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, UTILS.BasicSerialize( Arguments ) ) )
else
env.info( string.format( "%1s:%30s%05d(%s)", "E", self.ClassName, self.ClassID, BASE:_Serialize(Arguments) ) )
env.info( string.format( "%1s:%30s%05d(%s)", "E", self.ClassName, self.ClassID, UTILS.BasicSerialize( Arguments ) ) )
end
end
@@ -1354,8 +1341,39 @@ function BASE:I( Arguments )
env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "I", self.ClassName, self.ClassID, Function, UTILS.BasicSerialize( Arguments ) ) )
else
env.info( string.format( "%1s:%30s%05d(%s)", "I", self.ClassName, self.ClassID, BASE:_Serialize(Arguments)) )
env.info( string.format( "%1s:%30s%05d(%s)", "I", self.ClassName, self.ClassID, UTILS.BasicSerialize( Arguments ) ) )
end
end
--- old stuff
-- function BASE:_Destructor()
-- --self:E("_Destructor")
--
-- --self:EventRemoveAll()
-- end
-- THIS IS WHY WE NEED LUA 5.2 ...
-- function BASE:_SetDestructor()
--
-- -- TODO: Okay, this is really technical...
-- -- When you set a proxy to a table to catch __gc, weak tables don't behave like weak...
-- -- Therefore, I am parking this logic until I've properly discussed all this with the community.
--
-- local proxy = newproxy(true)
-- local proxyMeta = getmetatable(proxy)
--
-- proxyMeta.__gc = function ()
-- env.info("In __gc for " .. self:GetClassNameAndID() )
-- if self._Destructor then
-- self:_Destructor()
-- end
-- end
--
-- -- keep the userdata from newproxy reachable until the object
-- -- table is about to be garbage-collected - then the __gc hook
-- -- will be invoked and the destructor called
-- rawset( self, '__proxy', proxy )
--
-- end

View File

@@ -8,10 +8,6 @@
--
-- ===
--
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_Demos/tree/master/Core/Beacon)
--
-- ===
--
-- ### Authors: Hugues "Grey_Echo" Bousquet, funkyfranky
--
-- @module Core.Beacon
@@ -290,7 +286,6 @@ end
-- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon
function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
self:F({TACANChannel, Message, Bearing, BeaconDuration})
self:E("This method is DEPRECATED! Please use ActivateTACAN() instead.")
local IsValid = true

View File

@@ -1,898 +0,0 @@
--- **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: Oct 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 #string groupname Group 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.1",
name = nil,
path = nil,
group = nil,
client = nil,
GroupID = nil,
Children = {},
Once = false,
Generic = false,
debug = false,
Controller = nil,
groupname = 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. Leave as nil for a generic entry.
-- @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()
self.groupname = self.group:GetName() or "Unknown Groupname"
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,";"))
local function RemoveFunction()
return missionCommands.removeItemForGroup(self.GroupID , self.path )
end
local status, err = pcall(RemoveFunction)
if not status then
self:I(string.format("**** Error Removing Menu Entry %s for %s!",tostring(self.name),self.groupname))
end
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
-- @field #table PlayerMenu
-- @field #number Coalition
-- @extends Core.Base#BASE
--- *As a child my family's menu consisted of two choices: take it, or leave it.*
--
-- ===
--
-- ## 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.
--
-- ## Conceptual remarks
--
-- There's a couple of things to fully understand:
--
-- 1) **CLIENTMENUMANAGER** manages a set of entries from **CLIENTMENU**, it's main purpose is to administer the *shadow menu tree*, ie. a menu structure which is not
-- (yet) visible to any client
-- 2) The entries are **CLIENTMENU** objects, which are linked in a tree form. There's two ways to create them:
-- A) in the manager with ":NewEntry()" which initially
-- adds it to the shadow menu **only**
-- B) stand-alone directly as `CLIENTMENU:NewEntry()` - here it depends on whether or not you gave a CLIENT object if the entry is created as generic entry or pushed
-- a **specific** client. **Be aware** though that the entries are not managed by the CLIENTMANAGER before the next step!
-- A generic entry can be added to the manager (and the shadow tree) with `:AddEntry()` - this will also push it to all clients(!) if no client is given, or a specific client only.
-- 3) Pushing only works for alive clients.
-- 4) Live and shadow tree entries are managed via the CLIENTMENUMANAGER object.
-- 5) `Propagate()`refreshes the menu tree for all, or a single client.
--
-- ## 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() -- propagate **once** to all clients in the SET_CLIENT
--
-- ## 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()
--
-- ## Set to auto-propagate for CLIENTs joining the SET_CLIENT **after** the script is loaded - handy if you have a single menu tree.
--
-- menumgr:InitAutoPropagation()
--
-- @field #CLIENTMENUMANAGER
CLIENTMENUMANAGER = {
ClassName = "CLIENTMENUMANAGER",
lid = "",
version = "0.1.4",
name = nil,
clientset = nil,
menutree = {},
flattree = {},
playertree = {},
entrycount = 0,
rootentries = {},
debug = true,
PlayerMenu = {},
Coalition = nil,
}
--- Create a new ClientManager instance.
-- @param #CLIENTMENUMANAGER self
-- @param Core.Set#SET_CLIENT ClientSet The set of clients to manage.
-- @param #string Alias The name of this manager.
-- @param #number Coalition (Optional) Coalition of this Manager, defaults to coalition.side.BLUE
-- @return #CLIENTMENUMANAGER self
function CLIENTMENUMANAGER:New(ClientSet, Alias, Coalition)
-- Inherit everything from FSM class.
local self=BASE:Inherit(self, BASE:New()) -- #CLIENTMENUMANAGER
self.clientset = ClientSet
self.PlayerMenu = {}
self.name = Alias or "Nightshift"
self.Coalition = Coalition or coalition.side.BLUE
-- Log id.
self.lid=string.format("CLIENTMENUMANAGER %s | %s | ", self.version, self.name)
if self.debug then
self:I(self.lid.."Created")
end
return self
end
--- [Internal] Event handling
-- @param #CLIENTMENUMANAGER self
-- @param Core.Event#EVENTDATA EventData
-- @return #CLIENTMENUMANAGER self
function CLIENTMENUMANAGER:_EventHandler(EventData)
self:T(self.lid.."_EventHandler: "..EventData.id)
--self:I(self.lid.."_EventHandler: "..tostring(EventData.IniPlayerName))
if EventData.id == EVENTS.PlayerLeaveUnit or EventData.id == EVENTS.Ejection or EventData.id == EVENTS.Crash or EventData.id == EVENTS.PilotDead then
self:T(self.lid.."Leave event for player: "..tostring(EventData.IniPlayerName))
local Client = _DATABASE:FindClient( EventData.IniUnitName )
if Client then
self:ResetMenu(Client)
end
elseif (EventData.id == EVENTS.PlayerEnterAircraft) and EventData.IniCoalition == self.Coalition then
if EventData.IniPlayerName and EventData.IniGroup then
if (not self.clientset:IsIncludeObject(_DATABASE:FindClient( EventData.IniUnitName ))) then
self:T(self.lid.."Client not in SET: "..EventData.IniPlayerName)
return self
end
--self:I(self.lid.."Join event for player: "..EventData.IniPlayerName)
local player = _DATABASE:FindClient( EventData.IniUnitName )
self:Propagate(player)
end
elseif EventData.id == EVENTS.PlayerEnterUnit then
-- special for CA slots
local grp = GROUP:FindByName(EventData.IniGroupName)
if grp:IsGround() then
self:T(string.format("Player %s entered GROUND unit %s!",EventData.IniPlayerName,EventData.IniUnitName))
local IsPlayer = EventData.IniDCSUnit:getPlayerName()
if IsPlayer then
local client=_DATABASE.CLIENTS[EventData.IniDCSUnitName] --Wrapper.Client#CLIENT
-- Add client in case it does not exist already.
if not client then
-- Debug info.
self:I(string.format("Player '%s' joined ground unit '%s' of group '%s'", tostring(EventData.IniPlayerName), tostring(EventData.IniDCSUnitName), tostring(EventData.IniDCSGroupName)))
client=_DATABASE:AddClient(EventData.IniDCSUnitName)
-- Add player.
client:AddPlayer(EventData.IniPlayerName)
-- Add player.
if not _DATABASE.PLAYERS[EventData.IniPlayerName] then
_DATABASE:AddPlayer( EventData.IniUnitName, EventData.IniPlayerName )
end
-- Player settings.
local Settings = SETTINGS:Set( EventData.IniPlayerName )
Settings:SetPlayerMenu(EventData.IniUnit)
end
--local player = _DATABASE:FindClient( EventData.IniPlayerName )
self:Propagate(client)
end
end
end
return self
end
--- Set this Client Manager to auto-propagate menus **once** to newly joined players. Useful if you have **one** menu structure only. Does not automatically push follow-up changes to the client(s).
-- @param #CLIENTMENUMANAGER self
-- @return #CLIENTMENUMANAGER self
function CLIENTMENUMANAGER:InitAutoPropagation()
-- Player Events
self:HandleEvent(EVENTS.PlayerLeaveUnit, self._EventHandler)
self:HandleEvent(EVENTS.Ejection, self._EventHandler)
self:HandleEvent(EVENTS.Crash, self._EventHandler)
self:HandleEvent(EVENTS.PilotDead, self._EventHandler)
self:HandleEvent(EVENTS.PlayerEnterAircraft, self._EventHandler)
self:HandleEvent(EVENTS.PlayerEnterUnit, self._EventHandler)
self:SetEventPriority(5)
return self
end
--- Create a new entry in the **generic** structure.
-- @param #CLIENTMENUMANAGER self
-- @param #string Text Text of the F10 menu entry.
-- @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,1,true) and string.find(Entry.UUID,Parent.UUID,1,true) then
table.insert(matches,_uuid)
table.insert(entries,Entry )
n=n+1
end
else
if Entry and string.find(Entry.name,Text,1,true) 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,1,true) 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:I(UTILS.PrintTableToLog(Client,1))
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() or "none"
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 F10 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.
-- @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,1,true) 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,1,true) 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
--
----------------------------------------------------------------------------------------------------------------

View File

@@ -37,8 +37,6 @@
-- @field #table Templates Templates: Units, Groups, Statics, ClientsByName, ClientsByID.
-- @field #table CLIENTS Clients.
-- @field #table STORAGES DCS warehouse storages.
-- @field #table STNS Used Link16 octal numbers for F16/15/18/AWACS planes.
-- @field #table SADL Used Link16 octal numbers for A10/C-II planes.
-- @extends Core.Base#BASE
--- Contains collections of wrapper objects defined within MOOSE that reflect objects within the simulator.
@@ -95,8 +93,6 @@ DATABASE = {
OPSZONES = {},
PATHLINES = {},
STORAGES = {},
STNS={},
SADL={},
}
local _DATABASECoalition =
@@ -932,7 +928,7 @@ function DATABASE:Spawn( SpawnTemplate )
SpawnTemplate.CountryID = nil
SpawnTemplate.CategoryID = nil
self:_RegisterGroupTemplate( SpawnTemplate, SpawnCoalitionID, SpawnCategoryID, SpawnCountryID, SpawnTemplate.name )
self:_RegisterGroupTemplate( SpawnTemplate, SpawnCoalitionID, SpawnCategoryID, SpawnCountryID )
self:T3( SpawnTemplate )
coalition.addGroup( SpawnCountryID, SpawnCategoryID, SpawnTemplate )
@@ -1033,31 +1029,10 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
self.Templates.ClientsByName[UnitTemplate.name].CountryID = CountryID
self.Templates.ClientsByID[UnitTemplate.unitId] = UnitTemplate
end
if UnitTemplate.AddPropAircraft then
if UnitTemplate.AddPropAircraft.STN_L16 then
local stn = UTILS.OctalToDecimal(UnitTemplate.AddPropAircraft.STN_L16)
if stn == nil or stn < 1 then
self:E("WARNING: Invalid STN "..tostring(UnitTemplate.AddPropAircraft.STN_L16).." for ".. UnitTemplate.name)
else
self.STNS[stn] = UnitTemplate.name
self:I("Register STN "..tostring(UnitTemplate.AddPropAircraft.STN_L16).." for ".. UnitTemplate.name)
end
end
if UnitTemplate.AddPropAircraft.SADL_TN then
local sadl = UTILS.OctalToDecimal(UnitTemplate.AddPropAircraft.SADL_TN)
if sadl == nil or sadl < 1 then
self:E("WARNING: Invalid SADL "..tostring(UnitTemplate.AddPropAircraft.SADL_TN).." for ".. UnitTemplate.name)
else
self.SADL[sadl] = UnitTemplate.name
self:I("Register SADL "..tostring(UnitTemplate.AddPropAircraft.SADL_TN).." for ".. UnitTemplate.name)
end
end
end
UnitNames[#UnitNames+1] = self.Templates.Units[UnitTemplate.name].UnitName
end
-- Debug info.
self:T( { Group = self.Templates.Groups[GroupTemplateName].GroupName,
Coalition = self.Templates.Groups[GroupTemplateName].CoalitionID,
@@ -1068,80 +1043,6 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
)
end
--- Get next (consecutive) free STN as octal number.
-- @param #DATABASE self
-- @param #number octal Starting octal.
-- @param #string unitname Name of the associated unit.
-- @return #number Octal
function DATABASE:GetNextSTN(octal,unitname)
local first = UTILS.OctalToDecimal(octal)
if self.STNS[first] == unitname then return octal end
local nextoctal = 77777
local found = false
if 32767-first < 10 then
first = 0
end
for i=first+1,32767 do
if self.STNS[i] == nil then
found = true
nextoctal = UTILS.DecimalToOctal(i)
self.STNS[i] = unitname
self:T("Register STN "..tostring(nextoctal).." for ".. unitname)
break
end
end
if not found then
self:E(string.format("WARNING: No next free STN past %05d found!",octal))
-- cleanup
local NewSTNS = {}
for _id,_name in pairs(self.STNS) do
if self.UNITS[_name] ~= nil then
NewSTNS[_id] = _name
end
end
self.STNS = nil
self.STNS = NewSTNS
end
return nextoctal
end
--- Get next (consecutive) free SADL as octal number.
-- @param #DATABASE self
-- @param #number octal Starting octal.
-- @param #string unitname Name of the associated unit.
-- @return #number Octal
function DATABASE:GetNextSADL(octal,unitname)
local first = UTILS.OctalToDecimal(octal)
if self.SADL[first] == unitname then return octal end
local nextoctal = 7777
local found = false
if 4095-first < 10 then
first = 0
end
for i=first+1,4095 do
if self.STNS[i] == nil then
found = true
nextoctal = UTILS.DecimalToOctal(i)
self.SADL[i] = unitname
self:T("Register SADL "..tostring(nextoctal).." for ".. unitname)
break
end
end
if not found then
self:E(string.format("WARNING: No next free SADL past %04d found!",octal))
-- cleanup
local NewSTNS = {}
for _id,_name in pairs(self.SADL) do
if self.UNITS[_name] ~= nil then
NewSTNS[_id] = _name
end
end
self.SADL = nil
self.SADL = NewSTNS
end
return nextoctal
end
--- Get group template.
-- @param #DATABASE self
-- @param #string GroupName Group name.

View File

@@ -1378,7 +1378,6 @@ function EVENT:onEvent( Event )
Event.MarkCoordinate=COORDINATE:NewFromVec3(Event.pos)
Event.MarkText=Event.text
Event.MarkCoalition=Event.coalition
Event.IniCoalition=Event.coalition
Event.MarkGroupID = Event.groupID
end

View File

@@ -17,7 +17,7 @@
-- ### Author: **Applevangelist**
--
-- Date: 5 May 2021
-- Last Update: Mar 2023
-- Last Update: Feb 2023
--
-- ===
---
@@ -50,7 +50,7 @@ MARKEROPS_BASE = {
ClassName = "MARKEROPS",
Tag = "mytag",
Keywords = {},
version = "0.1.3",
version = "0.1.1",
debug = false,
Casesensitive = true,
}
@@ -114,8 +114,6 @@ function MARKEROPS_BASE:New(Tagname,Keywords,Casesensitive)
-- @param #string Text The text on the marker
-- @param #table Keywords Table of matching keywords found in the Event text
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
-- @param #number MarkerID Id of this marker
-- @param #number CoalitionNumber Coalition of the marker creator
--- On after "MarkChanged" event. Triggered when a Marker is changed on the F10 map.
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkChanged
@@ -126,8 +124,7 @@ function MARKEROPS_BASE:New(Tagname,Keywords,Casesensitive)
-- @param #string Text The text on the marker
-- @param #table Keywords Table of matching keywords found in the Event text
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
-- @param #number MarkerID Id of this marker
-- @param #number CoalitionNumber Coalition of the marker creator
-- @param #number idx DCS Marker ID
--- On after "MarkDeleted" event. Triggered when a Marker is deleted from the F10 map.
-- @function [parent=#MARKEROPS_BASE] OnAfterMarkDeleted
@@ -136,7 +133,7 @@ function MARKEROPS_BASE:New(Tagname,Keywords,Casesensitive)
-- @param #string Event The Event called
-- @param #string To The To state
--- "Stop" trigger. Used to stop the function an unhandle events
--- "Stop" trigger. Used to stop the function an unhandle events
-- @function [parent=#MARKEROPS_BASE] Stop
end
@@ -158,30 +155,29 @@ function MARKEROPS_BASE:OnEventMark(Event)
local text = tostring(Event.text)
local m = MESSAGE:New(string.format("Mark added at %s with text: %s",coordtext,text),10,"Info",false):ToAll()
end
local coalition = Event.MarkCoalition
-- decision
if Event.id==world.event.S_EVENT_MARK_ADDED then
self:T({event="S_EVENT_MARK_ADDED", carrier=Event.IniGroupName, vec3=Event.pos})
self:T({event="S_EVENT_MARK_ADDED", carrier=self.groupname, vec3=Event.pos})
-- Handle event
local Eventtext = tostring(Event.text)
if Eventtext~=nil then
if self:_MatchTag(Eventtext) then
local matchtable = self:_MatchKeywords(Eventtext)
self:MarkAdded(Eventtext,matchtable,coord,Event.idx,coalition)
self:MarkAdded(Eventtext,matchtable,coord)
end
end
elseif Event.id==world.event.S_EVENT_MARK_CHANGE then
self:T({event="S_EVENT_MARK_CHANGE", carrier=Event.IniGroupName, vec3=Event.pos})
self:T({event="S_EVENT_MARK_CHANGE", carrier=self.groupname, vec3=Event.pos})
-- Handle event.
local Eventtext = tostring(Event.text)
if Eventtext~=nil then
if self:_MatchTag(Eventtext) then
local matchtable = self:_MatchKeywords(Eventtext)
self:MarkChanged(Eventtext,matchtable,coord,Event.idx,coalition)
self:MarkChanged(Eventtext,matchtable,coord,Event.idx)
end
end
elseif Event.id==world.event.S_EVENT_MARK_REMOVED then
self:T({event="S_EVENT_MARK_REMOVED", carrier=Event.IniGroupName, vec3=Event.pos})
self:T({event="S_EVENT_MARK_REMOVED", carrier=self.groupname, vec3=Event.pos})
-- Hande event.
local Eventtext = tostring(Event.text)
if Eventtext~=nil then
@@ -234,10 +230,8 @@ end
-- @param #string To The To state
-- @param #string Text The text on the marker
-- @param #table Keywords Table of matching keywords found in the Event text
-- @param #number MarkerID Id of this marker
-- @param #number CoalitionNumber Coalition of the marker creator
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
function MARKEROPS_BASE:onbeforeMarkAdded(From,Event,To,Text,Keywords,Coord,MarkerID,CoalitionNumber)
function MARKEROPS_BASE:onbeforeMarkAdded(From,Event,To,Text,Keywords,Coord)
self:T({self.lid,From,Event,To,Text,Keywords,Coord:ToStringLLDDM()})
end
@@ -248,10 +242,8 @@ end
-- @param #string To The To state
-- @param #string Text The text on the marker
-- @param #table Keywords Table of matching keywords found in the Event text
-- @param #number MarkerID Id of this marker
-- @param #number CoalitionNumber Coalition of the marker creator
-- @param Core.Point#COORDINATE Coord Coordinate of the marker.
function MARKEROPS_BASE:onbeforeMarkChanged(From,Event,To,Text,Keywords,Coord,MarkerID,CoalitionNumber)
function MARKEROPS_BASE:onbeforeMarkChanged(From,Event,To,Text,Keywords,Coord)
self:T({self.lid,From,Event,To,Text,Keywords,Coord:ToStringLLDDM()})
end

View File

@@ -73,7 +73,7 @@ PATHLINE = {
--- PATHLINE class version.
-- @field #string version
PATHLINE.version="0.1.1"
PATHLINE.version="0.1.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
@@ -237,14 +237,13 @@ end
--- Get COORDINATES of pathline. Note that COORDINATE objects are created when calling this function. That does involve deep copy calls and can have an impact on performance if done too often.
-- @param #PATHLINE self
-- @return <Core.Point#COORDINATE> List of COORDINATES points.
function PATHLINE:GetCoordinates()
function PATHLINE:GetCoordinats()
local vecs={}
for _,_point in pairs(self.points) do
local point=_point --#PATHLINE.Point
local coord=COORDINATE:NewFromVec3(point.vec3)
table.insert(vecs,coord)
end
return vecs
@@ -263,7 +262,7 @@ function PATHLINE:GetPointFromIndex(n)
local point=nil --#PATHLINE.Point
if n>=1 and n<=N then
point=self.points[n]
point=self.point[n]
else
self:E(self.lid..string.format("ERROR: No point in pathline for N=%s", tostring(n)))
end
@@ -368,4 +367,4 @@ end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@@ -702,9 +702,8 @@ do -- COORDINATE
-- @param #COORDINATE PointVec2Reference The reference @{#COORDINATE}.
-- @return DCS#Distance The distance from the reference @{#COORDINATE} in meters.
function COORDINATE:DistanceFromPointVec2( PointVec2Reference )
self:F2( PointVec2Reference )
if not PointVec2Reference then return math.huge end
self:F2( PointVec2Reference )
local Distance = ( ( PointVec2Reference.x - self.x ) ^ 2 + ( PointVec2Reference.z - self.z ) ^2 ) ^ 0.5
self:T2( Distance )
@@ -3148,18 +3147,17 @@ do -- COORDINATE
-- @param #string Northing Meters northing - string in order to allow for leading zeros, e.g. "12340". Should be 5 digits.
-- @return #COORDINATE self
function COORDINATE:NewFromMGRS( UTMZone, MGRSDigraph, Easting, Northing )
if string.len(Easting) < 5 then Easting = tostring(Easting..string.rep("0",5-string.len(Easting) )) end
if string.len(Northing) < 5 then Northing = tostring(Northing..string.rep("0",5-string.len(Northing) )) end
if string.len(Easting) < 5 then Easting = Easting..string.rep("0",5-string.len(Easting) )end
if string.len(Northing) < 5 then Northing = Northing..string.rep("0",5-string.len(Northing) )end
local MGRS = {
UTMZone = UTMZone,
MGRSDigraph = MGRSDigraph,
Easting = tostring(Easting),
Northing = tostring(Northing),
Easting = Easting,
Northing = Northing,
}
local lat, lon = coord.MGRStoLL(MGRS)
local point = coord.LLtoLO(lat, lon, 0)
local coord = COORDINATE:NewFromVec2({x=point.x,y=point.z})
return coord
end
--- Provides a coordinate string of the point, based on a coordinate format system:

View File

@@ -1097,7 +1097,6 @@ do
GroupPrefixes = nil,
Zones = nil,
Functions = nil,
Alive = nil,
},
FilterMeta = {
Coalitions = {
@@ -1205,7 +1204,7 @@ do
if not DontSetCargoBayLimit then
-- I set the default cargo bay weight limit each time a new group is added to the set.
-- TODO Why is this here in the first place?
for UnitID, UnitData in pairs( group:GetUnits() or {} ) do
for UnitID, UnitData in pairs( group:GetUnits() ) do
if UnitData and UnitData:IsAlive() then
UnitData:SetCargoBayWeightLimit()
end
@@ -1471,7 +1470,7 @@ do
end
--- Builds a set of groups that are active, ie in the mission but not yet activated (false) or actived (true).
--- Builds a set of groups that are only active.
-- Only the groups that are active will be included within the set.
-- @param #SET_GROUP self
-- @param #boolean Active (Optional) Include only active groups to the set.
@@ -1496,14 +1495,6 @@ do
self.Filter.Active = Active
return self
end
--- Build a set of groups that are alive.
-- @param #SET_GROUP self
-- @return #SET_GROUP self
function SET_GROUP:FilterAlive()
self.Filter.Alive = true
return self
end
--- Starts the filtering.
-- @param #SET_GROUP self
@@ -2002,16 +1993,7 @@ do
function SET_GROUP:IsIncludeObject( MGroup )
self:F2( MGroup )
local MGroupInclude = true
if self.Filter.Alive == true then
local MGroupAlive = false
self:F( { Active = self.Filter.Active } )
if MGroup and MGroup:IsAlive() then
MGroupAlive = true
end
MGroupInclude = MGroupInclude and MGroupAlive
end
if self.Filter.Active ~= nil then
local MGroupActive = false
self:F( { Active = self.Filter.Active } )
@@ -3015,7 +2997,7 @@ do -- SET_UNIT
local velocity = self:GetVelocity() or 0
Coordinate:SetHeading( heading )
Coordinate:SetVelocity( velocity )
self:T(UTILS.PrintTableToLog(Coordinate))
self:I(UTILS.PrintTableToLog(Coordinate))
end
return Coordinate
@@ -4539,7 +4521,7 @@ do -- SET_CLIENT
if Event.IniObjectCategory == Object.Category.UNIT and Event.IniGroup and Event.IniGroup:IsGround() then
-- CA Slot entered
local ObjectName, Object = self:AddInDatabase( Event )
self:T( ObjectName, UTILS.PrintTableToLog(Object) )
self:I( ObjectName, UTILS.PrintTableToLog(Object) )
if Object and self:IsIncludeObject( Object ) then
self:Add( ObjectName, Object )
end
@@ -8417,7 +8399,7 @@ do -- SET_SCENERY
--- Calculate current relative lifepoints of the SET objects, i.e. Life divided by Life0 as percentage value, eg 75 meaning 75% alive.
-- **CAVEAT**: Some objects change their life value or "hitpoints" **after** the first hit. Hence we will adjust the Life0 value to 120%
-- of the last life value if life exceeds life0 ata any point.
-- Thus we will get a smooth percentage decrease, if you use this e.g. as success criteria for a bombing task.
-- Thus will will get a smooth percentage decrease, if you use this e.g. as success criteria for a bombing task.
-- @param #SET_SCENERY self
-- @return #number LifePoints
function SET_SCENERY:GetRelativeLife()

View File

@@ -199,22 +199,6 @@
--
-- * @{#SPAWN.InitRepeat}() or @{#SPAWN.InitRepeatOnLanding}(): This method is used to re-spawn automatically the same group after it has landed.
-- * @{#SPAWN.InitRepeatOnEngineShutDown}(): This method is used to re-spawn automatically the same group after it has landed and it shuts down the engines at the ramp.
--
-- ### Link-16 Datalink STN and SADL IDs (limited at the moment to F15/16/18/AWACS/Tanker/B1B, but not the F15E for clients, SADL A10CII only)
--
-- *{#SPAWN.InitSTN}(): Set the STN of the first unit in the group. All other units will have consecutive STNs, provided they have not been used yet.
-- *{#SPAWN.InitSADL}(): Set the SADL of the first unit in the group. All other units will have consecutive SADLs, provided they have not been used yet.
--
-- ### Callsigns
--
-- *{#SPAWN.InitRandomizeCallsign}(): Set a random callsign name per spawn.
-- *{#SPAWN.SpawnInitCallSign}(): Set a specific callsign for a spawned group.
--
-- ### Speed
--
-- *{#SPAWN.InitSpeedMps}(): Set the initial speed on spawning in meters per second.
-- *{#SPAWN.InitSpeedKph}(): Set the initial speed on spawning in kilometers per hour.
-- *{#SPAWN.InitSpeedKnots}(): Set the initial speed on spawning in knots.
--
-- ## SPAWN **Spawn** methods
--
@@ -536,7 +520,7 @@ function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPr
end
if SpawnTemplate then
self.SpawnTemplate = UTILS.DeepCopy(SpawnTemplate) -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!!
self.SpawnTemplate = SpawnTemplate -- Contains the template structure for a Group Spawn from the Mission Editor. Note that this group must have lateActivation always on!!!
self.SpawnTemplatePrefix = SpawnTemplatePrefix
self.SpawnAliasPrefix = SpawnAliasPrefix or SpawnTemplatePrefix
self.SpawnTemplate.name = SpawnTemplatePrefix
@@ -740,7 +724,7 @@ end
-- @param #number Country Country id as number or enumerator:
--
-- * @{DCS#country.id.RUSSIA}
-- * @{DCS#country.id.USA}
-- * @{DCS#county.id.USA}
--
-- @return #SPAWN self
function SPAWN:InitCountry( Country )
@@ -796,82 +780,6 @@ function SPAWN:InitSkill( Skill )
return self
end
--- [Airplane - F15/16/18/AWACS/B1B/Tanker only] Set the STN Link16 starting number of the Group; each unit of the spawned group will have a consecutive STN set.
-- @param #SPAWN self
-- @param #number Octal The octal number (digits 1..7, max 5 digits, i.e. 1..77777) to set the STN to. Every STN needs to be unique!
-- @return #SPAWN self
function SPAWN:InitSTN(Octal)
self:F( { Octal = Octal } )
self.SpawnInitSTN = Octal or 77777
local num = UTILS.OctalToDecimal(Octal)
if num == nil or num < 1 then
self:E("WARNING - STN "..tostring(Octal).." is not valid!")
return self
end
if _DATABASE.STNS[num] ~= nil then
self:E("WARNING - STN already assigned: "..tostring(Octal).." is used for ".._DATABASE.STNS[Octal])
end
return self
end
--- [Airplane - A10-C II only] Set the SADL TN starting number of the Group; each unit of the spawned group will have a consecutive SADL set.
-- @param #SPAWN self
-- @param #number Octal The octal number (digits 1..7, max 4 digits, i.e. 1..7777) to set the SADL to. Every SADL needs to be unique!
-- @return #SPAWN self
function SPAWN:InitSADL(Octal)
self:F( { Octal = Octal } )
self.SpawnInitSADL = Octal or 7777
local num = UTILS.OctalToDecimal(Octal)
if num == nil or num < 1 then
self:E("WARNING - SADL "..tostring(Octal).." is not valid!")
return self
end
if _DATABASE.SADL[num] ~= nil then
self:E("WARNING - SADL already assigned: "..tostring(Octal).." is used for ".._DATABASE.SADL[Octal])
end
return self
end
--- [Airplane] Set the initial speed on spawning in meters per second. Useful when spawning in-air only.
-- @param #SPAWN self
-- @param #number MPS The speed in MPS to use.
-- @return #SPAWN self
function SPAWN:InitSpeedMps(MPS)
self:F( { MPS = MPS } )
if MPS == nil or tonumber(MPS)<0 then
MPS=125
end
self.InitSpeed = MPS
return self
end
--- [Airplane] Set the initial speed on spawning in knots. Useful when spawning in-air only.
-- @param #SPAWN self
-- @param #number Knots The speed in knots to use.
-- @return #SPAWN self
function SPAWN:InitSpeedKnots(Knots)
self:F( { Knots = Knots } )
if Knots == nil or tonumber(Knots)<0 then
Knots=300
end
self.InitSpeed = UTILS.KnotsToMps(Knots)
return self
end
--- [Airplane] Set the initial speed on spawning in kilometers per hour. Useful when spawning in-air only.
-- @param #SPAWN self
-- @param #number KPH The speed in KPH to use.
-- @return #SPAWN self
function SPAWN:InitSpeedKph(KPH)
self:F( { KPH = KPH } )
if KPH == nil or tonumber(KPH)<0 then
KPH=UTILS.KnotsToKmph(300)
end
self.InitSpeed = UTILS.KmphToMps(KPH)
return self
end
--- Sets the radio communication on or off. Same as checking/unchecking the COMM box in the mission editor.
-- @param #SPAWN self
-- @param #number switch If true (or nil), enables the radio communication. If false, disables the radio for the spawned group.
@@ -1781,8 +1689,8 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
-- If there is a SpawnFunction hook defined, call it.
if self.SpawnFunctionHook then
-- delay calling this for .3 seconds so that it hopefully comes after the BIRTH event of the group.
self.SpawnHookScheduler:Schedule( nil, self.SpawnFunctionHook, { self.SpawnGroups[self.SpawnIndex].Group, unpack( self.SpawnFunctionArguments ) }, 0.3 )
-- delay calling this for .1 seconds so that it hopefully comes after the BIRTH event of the group.
self.SpawnHookScheduler:Schedule( nil, self.SpawnFunctionHook, { self.SpawnGroups[self.SpawnIndex].Group, unpack( self.SpawnFunctionArguments ) }, 0.1 )
end
-- TODO: Need to fix this by putting an "R" in the name of the group when the group repeats.
-- if self.Repeat then
@@ -1791,7 +1699,6 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
end
self.SpawnGroups[self.SpawnIndex].Spawned = true
self.SpawnGroups[self.SpawnIndex].Group.TemplateDonor = self.SpawnTemplatePrefix
return self.SpawnGroups[self.SpawnIndex].Group
else
-- self:E( { self.SpawnTemplatePrefix, "No more Groups to Spawn:", SpawnIndex, self.SpawnMaxGroups } )
@@ -3396,7 +3303,7 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
end
end
end
if self.SpawnInitKeepUnitNames == false then
for UnitID = 1, #SpawnTemplate.units do
SpawnTemplate.units[UnitID].name = string.format( SpawnTemplate.name .. '-%02d', UnitID )
@@ -3404,17 +3311,9 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
end
else
for UnitID = 1, #SpawnTemplate.units do
local SpawnInitKeepUnitIFF = false
if string.find(SpawnTemplate.units[UnitID].name,"#IFF_",1,true) then --Razbam IFF hack for F15E etc
SpawnInitKeepUnitIFF = true
end
local UnitPrefix, Rest
if SpawnInitKeepUnitIFF == false then
UnitPrefix, Rest = string.match( SpawnTemplate.units[UnitID].name, "^([^#]+)#?" ):gsub( "^%s*(.-)%s*$", "%1" )
self:T( { UnitPrefix, Rest } )
else
UnitPrefix=SpawnTemplate.units[UnitID].name
end
local UnitPrefix, Rest = string.match( SpawnTemplate.units[UnitID].name, "^([^#]+)#?" ):gsub( "^%s*(.-)%s*$", "%1" )
self:T( { UnitPrefix, Rest } )
SpawnTemplate.units[UnitID].name = string.format( '%s#%03d-%02d', UnitPrefix, SpawnIndex, UnitID )
SpawnTemplate.units[UnitID].unitId = nil
end
@@ -3496,58 +3395,34 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
SpawnTemplate.units[UnitID].callsign = Callsign + SpawnIndex
end
end
-- Speed
if self.InitSpeed then
SpawnTemplate.units[UnitID].speed = self.InitSpeed
end
-- Link16
local AddProps = SpawnTemplate.units[UnitID].AddPropAircraft
if AddProps then
if SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 then
if self.SpawnInitSTN then
local octal = self.SpawnInitSTN
if UnitID > 1 then
octal = _DATABASE:GetNextSTN(self.SpawnInitSTN,SpawnTemplate.units[UnitID].name)
end
SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",octal)
else
-- 5 digit octal with leading 0
if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16) ~= nil then
local octal = SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16
local num = UTILS.OctalToDecimal(octal)
if _DATABASE.STNS[num] ~= nil or UnitID > 1 then -- STN taken or next unit
octal = _DATABASE:GetNextSTN(octal,SpawnTemplate.units[UnitID].name)
end
SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",octal)
else -- ED bug - chars in here
local OSTN = _DATABASE:GetNextSTN(1,SpawnTemplate.units[UnitID].name)
SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",OSTN)
end
-- 4 digit octal with leading 0
if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16) ~= nil then
local octal = SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16
local decimal = UTILS.OctalToDecimal(octal)+UnitID-1
SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",UTILS.DecimalToOctal(decimal))
else -- ED bug - chars in here
local STN = math.floor(UTILS.RandomGaussian(4088/2,nil,1000,4088))
STN = STN+UnitID-1
local OSTN = UTILS.DecimalToOctal(STN)
SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",OSTN)
end
end
-- A10CII
if SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN then
-- 4 digit octal with leading 0
if self.SpawnInitSADL then
local octal = self.SpawnInitSADL
if UnitID > 1 then
octal = _DATABASE:GetNextSADL(self.SpawnInitSADL,SpawnTemplate.units[UnitID].name)
end
SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",octal)
else
if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN) ~= nil then
local octal = SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN
local num = UTILS.OctalToDecimal(octal)
self.SpawnInitSADL = num -- we arrived here seeing that self.SpawnInitSADL == nil, but now that we have a SADL (num), we also need to set it to self.SpawnInitSADL in case
-- we need to get the next SADL from _DATABASE, or else UTILS.OctalToDecimal() will fail in GetNextSADL
if _DATABASE.SADL[num] ~= nil or UnitID > 1 then -- SADL taken or next unit
octal = _DATABASE:GetNextSADL(self.SpawnInitSADL,SpawnTemplate.units[UnitID].name)
end
SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",octal)
else -- ED bug - chars in here
local OSTN = _DATABASE:GetNextSADL(1,SpawnTemplate.units[UnitID].name)
SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",OSTN)
end
-- 3 digit octal with leading 0
if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN) ~= nil then
local octal = SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN
local decimal = UTILS.OctalToDecimal(octal)+UnitID-1
SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",UTILS.DecimalToOctal(decimal))
else -- ED bug - chars in here
local STN = math.floor(UTILS.RandomGaussian(504/2,nil,100,504))
STN = STN+UnitID-1
local OSTN = UTILS.DecimalToOctal(STN)
SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",OSTN)
end
end
-- VoiceCallsignNumber

View File

@@ -1,36 +1,36 @@
--- **Core** - Spawn statics.
--
--
-- ===
--
--
-- ## Features:
--
--
-- * Spawn new statics from a static already defined in the mission editor.
-- * Spawn new statics from a given template.
-- * Spawn new statics from a given type.
-- * Spawn with a custom heading and location.
-- * Spawn within a zone.
-- * Spawn statics linked to units, .e.g on aircraft carriers.
--
--
-- ===
--
--
-- # Demo Missions
--
-- ## [SPAWNSTATIC Demo Missions](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master/Core/SpawnStatic)
--
-- ## [SPAWNSTATIC Demo Missions](https://github.com/FlightControl-Master/MOOSE_Demos/tree/master/Core/SpawnStatic)
--
--
--
-- ===
--
--
-- # YouTube Channel
--
--
-- ## No videos yet!
--
--
-- ===
--
--
-- ### Author: **FlightControl**
-- ### Contributions: **funkyfranky**
--
--
-- ===
--
--
-- @module Core.SpawnStatic
-- @image Core_Spawnstatic.JPG
@@ -58,37 +58,37 @@
--- Allows to spawn dynamically new @{Wrapper.Static}s into your mission.
--
-- Through creating a copy of an existing static object template as defined in the Mission Editor (ME), SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc),
--
-- Through creating a copy of an existing static object template as defined in the Mission Editor (ME), SPAWNSTATIC can retireve the properties of the defined static object template (like type, category etc),
-- and "copy" these properties to create a new static object and place it at the desired coordinate.
--
-- New spawned @{Wrapper.Static}s get **the same name** as the name of the template Static, or gets the given name when a new name is provided at the Spawn method.
--
-- New spawned @{Wrapper.Static}s get **the same name** as the name of the template Static, or gets the given name when a new name is provided at the Spawn method.
-- By default, spawned @{Wrapper.Static}s will follow a naming convention at run-time:
--
--
-- * Spawned @{Wrapper.Static}s will have the name _StaticName_#_nnn_, where _StaticName_ is the name of the **Template Static**, and _nnn_ is a **counter from 0 to 99999**.
--
--
-- # SPAWNSTATIC Constructors
--
--
-- Firstly, we need to create a SPAWNSTATIC object that will be used to spawn new statics into the mission. There are three ways to do this.
--
--
-- ## Use another Static
--
--
-- A new SPAWNSTATIC object can be created using another static by the @{#SPAWNSTATIC.NewFromStatic}() function. All parameters such as position, heading, country will be initialized
-- from the static.
--
--
-- ## From a Template
--
--
-- A SPAWNSTATIC object can also be created from a template table using the @{#SPAWNSTATIC.NewFromTemplate}(SpawnTemplate, CountryID) function. All parameters are taken from the template.
--
--
-- ## From a Type
--
--
-- A very basic method is to create a SPAWNSTATIC object by just giving the type of the static. All parameters must be initialized from the InitXYZ functions described below. Otherwise default values
-- are used. For example, if no spawn coordinate is given, the static will be created at the origin of the map.
--
--
-- # Setting Parameters
--
--
-- Parameters such as the spawn position, heading, country etc. can be set via :Init*XYZ* functions. Note that these functions must be given before the actual spawn command!
--
--
-- * @{#SPAWNSTATIC.InitCoordinate}(Coordinate) Sets the coordinate where the static is spawned. Statics are always spawnd on the ground.
-- * @{#SPAWNSTATIC.InitHeading}(Heading) sets the orientation of the static.
-- * @{#SPAWNSTATIC.InitLivery}(LiveryName) sets the livery of the static. Not all statics support this.
@@ -99,17 +99,17 @@
-- * @{#SPAWNSTATIC.InitLinkToUnit}(Unit, OffsetX, OffsetY, OffsetAngle) links the static to a unit, e.g. to an aircraft carrier.
--
-- # Spawning the Statics
--
--
-- Once the SPAWNSTATIC object is created and parameters are initialized, the spawn command can be given. There are different methods where some can be used to directly set parameters
-- such as position and heading.
--
--
-- * @{#SPAWNSTATIC.Spawn}(Heading, NewName) spawns the static with the set parameters. Optionally, heading and name can be given. The name **must be unique**!
-- * @{#SPAWNSTATIC.SpawnFromCoordinate}(Coordinate, Heading, NewName) spawn the static at the given coordinate. Optionally, heading and name can be given. The name **must be unique**!
-- * @{#SPAWNSTATIC.SpawnFromPointVec2}(PointVec2, Heading, NewName) spawns the static at a POINT_VEC2 coordinate. Optionally, heading and name can be given. The name **must be unique**!
-- * @{#SPAWNSTATIC.SpawnFromZone}(Zone, Heading, NewName) spawns the static at the center of a @{Core.Zone}. Optionally, heading and name can be given. The name **must be unique**!
--
--
-- @field #SPAWNSTATIC SPAWNSTATIC
--
--
SPAWNSTATIC = {
ClassName = "SPAWNSTATIC",
SpawnIndex = 0,
@@ -139,9 +139,9 @@ SPAWNSTATIC = {
function SPAWNSTATIC:NewFromStatic(SpawnTemplateName, SpawnCountryID)
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate(SpawnTemplateName)
if TemplateStatic then
self.SpawnTemplatePrefix = SpawnTemplateName
self.TemplateStaticUnit = UTILS.DeepCopy(TemplateStatic.units[1])
@@ -166,11 +166,11 @@ end
function SPAWNSTATIC:NewFromTemplate(SpawnTemplate, CountryID)
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
self.TemplateStaticUnit = UTILS.DeepCopy(SpawnTemplate)
self.SpawnTemplatePrefix = SpawnTemplate.name
self.CountryID = CountryID or country.id.USA
return self
end
@@ -189,7 +189,7 @@ function SPAWNSTATIC:NewFromType(StaticType, StaticCategory, CountryID)
self.InitStaticCategory=StaticCategory
self.CountryID=CountryID or country.id.USA
self.SpawnTemplatePrefix=self.InitStaticType
self.InitStaticCoordinate=COORDINATE:New(0, 0, 0)
self.InitStaticHeading=0
@@ -291,7 +291,7 @@ function SPAWNSTATIC:InitCountry(CountryID)
return self
end
--- Initialize name prefix statics get. This will be appended by "#0001", "#0002" etc.
--- Initialize name prefix statics get. This will be appended by "#0001", "#0002" etc.
-- @param #SPAWNSTATIC self
-- @param #string NamePrefix Name prefix of statics spawned. Will append #0001, etc to the name.
-- @return #SPAWNSTATIC self
@@ -327,13 +327,13 @@ function SPAWNSTATIC:Spawn(Heading, NewName)
if Heading then
self.InitStaticHeading=Heading
end
if NewName then
self.InitStaticName=NewName
end
return self:_SpawnStatic(self.TemplateStaticUnit, self.CountryID)
end
--- Creates a new @{Wrapper.Static} from a POINT_VEC2.
@@ -347,7 +347,7 @@ function SPAWNSTATIC:SpawnFromPointVec2(PointVec2, Heading, NewName)
local vec2={x=PointVec2:GetX(), y=PointVec2:GetY()}
local Coordinate=COORDINATE:NewFromVec2(vec2)
return self:SpawnFromCoordinate(Coordinate, Heading, NewName)
end
@@ -362,11 +362,11 @@ function SPAWNSTATIC:SpawnFromCoordinate(Coordinate, Heading, NewName)
-- Set up coordinate.
self.InitStaticCoordinate=Coordinate
if Heading then
self.InitStaticHeading=Heading
end
if NewName then
self.InitStaticName=NewName
end
@@ -385,7 +385,7 @@ function SPAWNSTATIC:SpawnFromZone(Zone, Heading, NewName)
-- Spawn the new static at the center of the zone.
local Static = self:SpawnFromPointVec2( Zone:GetPointVec2(), Heading, NewName )
return Static
end
@@ -399,45 +399,45 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
Template=Template or {}
local CountryID=CountryID or self.CountryID
if self.InitStaticType then
Template.type=self.InitStaticType
end
if self.InitStaticCategory then
Template.category=self.InitStaticCategory
end
if self.InitStaticCoordinate then
Template.x = self.InitStaticCoordinate.x
if self.InitStaticCoordinate then
Template.x = self.InitStaticCoordinate.x
Template.y = self.InitStaticCoordinate.z
Template.alt = self.InitStaticCoordinate.y
Template.alt = self.InitStaticCoordinate.y
end
if self.InitStaticHeading then
Template.heading = math.rad(self.InitStaticHeading)
Template.heading = math.rad(self.InitStaticHeading)
end
if self.InitStaticShape then
Template.shape_name=self.InitStaticShape
end
if self.InitStaticLivery then
Template.livery_id=self.InitStaticLivery
end
if self.InitStaticDead~=nil then
Template.dead=self.InitStaticDead
end
if self.InitStaticCargo~=nil then
Template.canCargo=self.InitStaticCargo
end
if self.InitStaticCargoMass~=nil then
Template.mass=self.InitStaticCargoMass
end
if self.InitLinkUnit then
Template.linkUnit=self.InitLinkUnit:GetID()
Template.linkOffset=true
@@ -446,45 +446,45 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
Template.offsets.x=self.InitOffsetX
Template.offsets.angle=self.InitOffsetAngle and math.rad(self.InitOffsetAngle) or 0
end
if self.InitFarp then
Template.heliport_callsign_id = self.InitFarpCallsignID
Template.heliport_frequency = self.InitFarpFreq
Template.heliport_modulation = self.InitFarpModu
Template.unitId=nil
end
-- Increase spawn index counter.
self.SpawnIndex = self.SpawnIndex + 1
-- Name of the spawned static.
Template.name = self.InitStaticName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex)
-- Add and register the new static.
local mystatic=_DATABASE:AddStatic(Template.name)
-- Debug output.
self:T(Template)
-- Add static to the game.
local Static=nil --DCS#StaticObject
if self.InitFarp then
local TemplateGroup={}
local TemplateGroup={}
TemplateGroup.units={}
TemplateGroup.units[1]=Template
TemplateGroup.visible=true
TemplateGroup.hidden=false
TemplateGroup.x=Template.x
TemplateGroup.y=Template.y
TemplateGroup.name=Template.name
self:T("Spawning FARP")
self:T("Spawning FARP")
self:T({Template=Template})
self:T({TemplateGroup=TemplateGroup})
-- ED's dirty way to spawn FARPS.
Static=coalition.addGroup(CountryID, -1, TemplateGroup)
@@ -499,10 +499,10 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
world.onEvent(Event)
else
self:T("Spawning Static")
self:T2({Template=Template})
self:T("Spawning Static")
self:T2({Template=Template})
Static=coalition.addStaticObject(CountryID, Template)
end
return mystatic
end

View File

@@ -46,10 +46,6 @@
--
-- ===
--
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_Demos/tree/master/Core/Zone)
--
-- ===
--
-- ### Author: **FlightControl**
-- ### Contributions: **Applevangelist**, **FunkyFranky**, **coconutcockpit**
--
@@ -330,14 +326,14 @@ function ZONE_BASE:GetRandomVec2()
return nil
end
--- Define a random @{Core.Point#POINT_VEC2} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
--- Define a random @{Core.Point#POINT_VEC2} within the zone.
-- @param #ZONE_BASE self
-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates.
function ZONE_BASE:GetRandomPointVec2()
return nil
end
--- Define a random @{Core.Point#POINT_VEC3} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
--- Define a random @{Core.Point#POINT_VEC3} within the zone.
-- @param #ZONE_BASE self
-- @return Core.Point#POINT_VEC3 The PointVec3 coordinates.
function ZONE_BASE:GetRandomPointVec3()
@@ -903,8 +899,7 @@ function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound )
local Point = {}
local Vec2 = self:GetVec2()
local countryID = CountryID or country.id.USA
Points = Points and Points or 360
local Angle
@@ -915,7 +910,7 @@ function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound )
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
Point.y = Vec2.y + math.sin( Radial ) * self:GetRadius()
local CountryName = _DATABASE.COUNTRY_NAME[countryID]
local CountryName = _DATABASE.COUNTRY_NAME[CountryID]
local Tire = {
["country"] = CountryName,
@@ -930,7 +925,7 @@ function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound )
["heading"] = 0,
} -- end of ["group"]
local Group = coalition.addStaticObject( countryID, Tire )
local Group = coalition.addStaticObject( CountryID, Tire )
if UnBound and UnBound == true then
Group:destroy()
end
@@ -1180,7 +1175,7 @@ function ZONE_RADIUS:RemoveJunk()
return n
end
--- Get a table of scanned units.
--- Count the number of different coalitions inside the zone.
-- @param #ZONE_RADIUS self
-- @return #table Table of DCS units and DCS statics inside the zone.
function ZONE_RADIUS:GetScannedUnits()
@@ -1215,7 +1210,7 @@ function ZONE_RADIUS:GetScannedSetUnit()
return SetUnit
end
--- Get a set of scanned groups.
--- Get a set of scanned units.
-- @param #ZONE_RADIUS self
-- @return Core.Set#SET_GROUP Set of groups.
function ZONE_RADIUS:GetScannedSetGroup()
@@ -1515,7 +1510,7 @@ function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes)
return point
end
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
-- @param #ZONE_RADIUS self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
@@ -1546,7 +1541,7 @@ function ZONE_RADIUS:GetRandomVec3( inner, outer )
end
--- Returns a @{Core.Point#POINT_VEC3} object reflecting a random 3D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
--- Returns a @{Core.Point#POINT_VEC3} object reflecting a random 3D location within the zone.
-- @param #ZONE_RADIUS self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
@@ -1990,7 +1985,7 @@ function ZONE_GROUP:GetRandomVec2()
return Point
end
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
-- @param #ZONE_GROUP self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.
@@ -2834,7 +2829,7 @@ function ZONE_POLYGON_BASE:GetRandomVec2()
end
end
--- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
--- Return a @{Core.Point#POINT_VEC2} object representing a random 2D point at landheight within the zone.
-- @param #ZONE_POLYGON_BASE self
-- @return @{Core.Point#POINT_VEC2}
function ZONE_POLYGON_BASE:GetRandomPointVec2()
@@ -2847,7 +2842,7 @@ function ZONE_POLYGON_BASE:GetRandomPointVec2()
return PointVec2
end
--- Return a @{Core.Point#POINT_VEC3} object representing a random 3D point at landheight within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
--- Return a @{Core.Point#POINT_VEC3} object representing a random 3D point at landheight within the zone.
-- @param #ZONE_POLYGON_BASE self
-- @return @{Core.Point#POINT_VEC3}
function ZONE_POLYGON_BASE:GetRandomPointVec3()
@@ -3835,18 +3830,18 @@ function ZONE_OVAL:GetRandomVec2()
return {x=rx, y=ry}
end
--- Define a random @{Core.Point#POINT_VEC2} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
--- Define a random @{Core.Point#POINT_VEC2} within the zone.
-- @param #ZONE_OVAL self
-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates.
function ZONE_OVAL:GetRandomPointVec2()
return POINT_VEC2:NewFromVec2(self:GetRandomVec2())
end
--- Define a random @{Core.Point#POINT_VEC2} within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec3 table.
--- Define a random @{Core.Point#POINT_VEC2} within the zone.
-- @param #ZONE_OVAL self
-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates.
function ZONE_OVAL:GetRandomPointVec3()
return POINT_VEC3:NewFromVec3(self:GetRandomVec2())
return POINT_VEC2:NewFromVec3(self:GetRandomVec2())
end
--- Draw the zone on the F10 map.
@@ -3986,7 +3981,7 @@ do -- ZONE_AIRBASE
return ZoneVec2
end
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone. Note that this is actually a @{Core.Point#COORDINATE} type object, and not a simple Vec2 table.
--- Returns a @{Core.Point#POINT_VEC2} object reflecting a random 2D location within the zone.
-- @param #ZONE_AIRBASE self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0.
-- @param #number outer (optional) Maximal distance from the outer edge of the zone. Default is the radius of the zone.

File diff suppressed because it is too large Load Diff

View File

@@ -1,806 +0,0 @@
--- **Functional** -- Send a truck to supply artillery groups.
--
-- ===
--
-- **AMMOTRUCK** - Send a truck to supply artillery groups.
--
-- ===
--
-- ## Missions:
--
-- Demo missions can be found on [GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Functional/AmmoTruck)
--
-- ===
--
-- ### Author : **applevangelist**
--
-- @module Functional.AmmoTruck
-- @image Artillery.JPG
--
-- Last update: July 2023
-------------------------------------------------------------------------
--- **AMMOTRUCK** class, extends Core.Fsm#FSM
-- @type AMMOTRUCK
-- @field #string ClassName Class Name
-- @field #string lid Lid for log entries
-- @field #string version Version string
-- @field #string alias Alias name
-- @field #boolean debug Debug flag
-- @field #table trucklist List of (alive) #AMMOTRUCK.data trucks
-- @field #table targetlist List of (alive) #AMMOTRUCK.data artillery
-- @field #number coalition Coalition this is for
-- @field Core.Set#SET_GROUP truckset SET of trucks
-- @field Core.Set#SET_GROUP targetset SET of artillery
-- @field #table remunitionqueue List of (alive) #AMMOTRUCK.data artillery to be reloaded
-- @field #table waitingtargets List of (alive) #AMMOTRUCK.data artillery waiting
-- @field #number ammothreshold Threshold (min) ammo before sending a truck
-- @field #number remunidist Max distance trucks will go
-- @field #number monitor Monitor interval in seconds
-- @field #number unloadtime Unload time in seconds
-- @field #number waitingtime Max waiting time in seconds
-- @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
--- *Amateurs talk about tactics, but professionals study logistics.* - General Robert H Barrow, USMC
--
-- Simple Class to re-arm your artillery with trucks.
--
-- #AMMOTRUCK
--
-- * Controls a SET\_GROUP of trucks which will re-arm a SET\_GROUP of artillery groups when they run out of ammunition.
--
-- ## 1 The AMMOTRUCK concept
--
-- A SET\_GROUP of trucks which will re-arm a SET\_GROUP of artillery groups when they run out of ammunition. They will be based on a
-- homebase and drive from there to the artillery groups and then back home.
-- Trucks are the **only known in-game mechanic** to re-arm artillery and other units in DCS. Working units are e.g.: M-939 (blue), Ural-375 and ZIL-135 (both red).
--
-- ## 2 Set-up
--
-- Define a set of trucks and a set of artillery:
--
-- local truckset = SET_GROUP:New():FilterCoalitions("blue"):FilterActive(true):FilterCategoryGround():FilterPrefixes("Ammo Truck"):FilterStart()
-- local ariset = SET_GROUP:New():FilterCoalitions("blue"):FilterActive(true):FilterCategoryGround():FilterPrefixes("Artillery"):FilterStart()
--
-- Create an AMMOTRUCK object to take care of the artillery using the trucks, with a homezone:
--
-- local ammotruck = AMMOTRUCK:New(truckset,ariset,coalition.side.BLUE,"Logistics",ZONE:FindByName("HomeZone")
--
-- ## 2 Options and their default values
--
-- ammotruck.ammothreshold = 5 -- send a truck when down to this many rounds
-- ammotruck.remunidist = 20000 -- 20km - send trucks max this far from home
-- ammotruck.unloadtime = 600 -- 10 minutes - min time to unload ammunition
-- ammotruck.waitingtime = 1800 -- 30 mintes - wait max this long until remunition is done
-- ammotruck.monitor = -60 -- 1 minute - AMMOTRUCK checks run every one minute
-- 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.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
--
-- Truck has been sent off:
--
-- function ammotruck:OnAfterRouteTruck(From, Event, To, Truckdata, Aridata)
-- ...
-- end
--
-- Truck has arrived:
--
-- function ammotruck:OnAfterTruckArrived(From, Event, To, Truckdata)
-- ...
-- end
--
-- Truck is unloading:
--
-- function ammotruck:OnAfterTruckUnloading(From, Event, To, Truckdata)
-- ...
-- end
--
-- Truck is returning home:
--
-- function ammotruck:OnAfterTruckReturning(From, Event, To, Truckdata)
-- ...
-- end
--
-- Truck is arrived at home:
--
-- function ammotruck:OnAfterTruckHome(From, Event, To, Truckdata)
-- ...
-- end
--
-- @field #AMMOTRUCK
AMMOTRUCK = {
ClassName = "AMMOTRUCK",
lid = "",
version = "0.0.12",
alias = "",
debug = false,
trucklist = {},
targetlist = {},
coalition = nil,
truckset = nil,
targetset = nil,
remunitionqueue = {},
waitingtargets = {},
ammothreshold = 5,
remunidist = 20000,
monitor = -60,
unloadtime = 600,
waitingtime = 1800,
routeonroad = true,
reloads = 5,
}
---
-- @type AMMOTRUCK.State
AMMOTRUCK.State = {
IDLE = "idle",
DRIVING = "driving",
ARRIVED = "arrived",
UNLOADING = "unloading",
RETURNING = "returning",
WAITING = "waiting",
RELOADING = "reloading",
OUTOFAMMO = "outofammo",
REQUESTED = "requested",
}
---
--@type AMMOTRUCK.data
--@field Wrapper.Group#GROUP group
--@field #string name
--@field #AMMOTRUCK.State statusquo
--@field #number timestamp
--@field #number ammo
--@field Core.Point#COORDINATE coordinate
--@field #string targetname
--@field Wrapper.Group#GROUP targetgroup
--@field Core.Point#COORDINATE targetcoordinate
--@field #number reloads
---
-- @param #AMMOTRUCK self
-- @param Core.Set#SET_GROUP Truckset Set of truck groups
-- @param Core.Set#SET_GROUP Targetset Set of artillery groups
-- @param #number Coalition Coalition
-- @param #string Alias Alias Name
-- @param Core.Zone#ZONE Homezone Home, return zone for trucks
-- @return #AMMOTRUCK self
-- @usage
-- Define a set of trucks and a set of artillery:
-- local truckset = SET_GROUP:New():FilterCoalitions("blue"):FilterActive(true):FilterCategoryGround():FilterPrefixes("Ammo Truck"):FilterStart()
-- local ariset = SET_GROUP:New():FilterCoalitions("blue"):FilterActive(true):FilterCategoryGround():FilterPrefixes("Artillery"):FilterStart()
--
-- Create an AMMOTRUCK object to take care of the artillery using the trucks, with a homezone:
-- local ammotruck = AMMOTRUCK:New(truckset,ariset,coalition.side.BLUE,"Logistics",ZONE:FindByName("HomeZone")
function AMMOTRUCK:New(Truckset,Targetset,Coalition,Alias,Homezone)
-- Inherit everything from BASE class.
local self=BASE:Inherit(self, FSM:New()) -- #AMMOTRUCK
self.truckset = Truckset -- Core.Set#SET_GROUP
self.targetset = Targetset -- Core.Set#SET_GROUP
self.coalition = Coalition -- #number
self.alias = Alias -- #string
self.debug = false
self.remunitionqueue = {}
self.trucklist = {}
self.targetlist = {}
self.ammothreshold = 5
self.remunidist = 20000
self.homezone = Homezone -- Core.Zone#ZONE
self.waitingtime = 1800
self.usearmygroup = false
self.hasarmygroup = false
-- Log id.
self.lid=string.format("AMMOTRUCK %s | %s | ", self.version, self.alias)
self:SetStartState("Stopped")
self:AddTransition("Stopped", "Start", "Running")
self:AddTransition("*", "Monitor", "*")
self:AddTransition("*", "RouteTruck", "*")
self:AddTransition("*", "TruckArrived", "*")
self:AddTransition("*", "TruckUnloading", "*")
self:AddTransition("*", "TruckReturning", "*")
self:AddTransition("*", "TruckHome", "*")
self:AddTransition("*", "Stop", "Stopped")
self:__Start(math.random(5,10))
self:I(self.lid .. "Started")
------------------------
--- Pseudo Functions ---
------------------------
--- Triggers the FSM event "Stop". Stops the AMMOTRUCK and all its event handlers.
-- @function [parent=#AMMOTRUCK] Stop
-- @param #AMMOTRUCK self
--- Triggers the FSM event "Stop" after a delay. Stops the AMMOTRUCK and all its event handlers.
-- @function [parent=#AMMOTRUCK] __Stop
-- @param #AMMOTRUCK self
-- @param #number delay Delay in seconds.
--- On after "RouteTruck" event.
-- @function [parent=#AMMOTRUCK] OnAfterRouteTruck
-- @param #AMMOTRUCK self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #AMMOTRUCK.data Truck
-- @param #AMMOTRUCK.data Artillery
--- On after "TruckUnloading" event.
-- @function [parent=#AMMOTRUCK] OnAfterTruckUnloading
-- @param #AMMOTRUCK self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #AMMOTRUCK.data Truck
--- On after "TruckReturning" event.
-- @function [parent=#AMMOTRUCK] OnAfterTruckReturning
-- @param #AMMOTRUCK self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #AMMOTRUCK.data Truck
--- On after "RouteTruck" event.
-- @function [parent=#AMMOTRUCK] OnAfterRouteTruck
-- @param #AMMOTRUCK self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #AMMOTRUCK.data Truck
--- On after "TruckHome" event.
-- @function [parent=#AMMOTRUCK] OnAfterTruckHome
-- @param #AMMOTRUCK self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param #AMMOTRUCK.data Truck
return self
end
---
-- @param #AMMOTRUCK self
-- @param #table dataset table of #AMMOTRUCK.data entries
-- @return #AMMOTRUCK self
function AMMOTRUCK:CheckDrivingTrucks(dataset)
self:T(self.lid .. " CheckDrivingTrucks")
local data = dataset
for _,_data in pairs (data) do
local truck = _data -- #AMMOTRUCK.data
-- see if we arrived at destination
local coord = truck.group:GetCoordinate()
local tgtcoord = truck.targetcoordinate
local dist = coord:Get2DDistance(tgtcoord)
if dist <= 150 then
-- arrived
truck.statusquo = AMMOTRUCK.State.ARRIVED
truck.timestamp = timer.getAbsTime()
truck.coordinate = coord
self:__TruckArrived(1,truck)
end
-- still driving?
local Tnow = timer.getAbsTime()
if Tnow - truck.timestamp > 30 then
local group = truck.group
if self.usearmygroup then
group = truck.group:GetGroup()
end
local currspeed = group:GetVelocityKMH()
if truck.lastspeed then
if truck.lastspeed == 0 and currspeed == 0 then
self:T(truck.group:GetName().." Is not moving!")
-- try and move it
truck.timestamp = timer.getAbsTime()
if self.routeonroad then
group:RouteGroundOnRoad(truck.targetcoordinate,30,2,"Vee")
else
group:RouteGroundTo(truck.targetcoordinate,30,"Vee",2)
end
end
truck.lastspeed = currspeed
else
truck.lastspeed = currspeed
truck.timestamp = timer.getAbsTime()
end
self:I({truck=truck.group:GetName(),currspeed=currspeed,lastspeed=truck.lastspeed})
end
end
return self
end
---
-- @param #AMMOTRUCK self
-- @param Wrapper.Group#GROUP Group
-- @return #AMMOTRUCK self
function AMMOTRUCK:GetAmmoStatus(Group)
local ammotot, shells, rockets, bombs, missiles, narti = Group:GetAmmunition()
return rockets+missiles+narti
end
---
-- @param #AMMOTRUCK self
-- @param #table dataset table of #AMMOTRUCK.data entries
-- @return #AMMOTRUCK self
function AMMOTRUCK:CheckWaitingTargets(dataset)
self:T(self.lid .. " CheckWaitingTargets")
local data = dataset
for _,_data in pairs (data) do
local truck = _data -- #AMMOTRUCK.data
-- see how long we're waiting - maybe ammo truck is dead?
local Tnow = timer.getAbsTime()
local Tdiff = Tnow - truck.timestamp
if Tdiff > self.waitingtime then
local hasammo = self:GetAmmoStatus(truck.group)
if hasammo <= self.ammothreshold then
truck.statusquo = AMMOTRUCK.State.OUTOFAMMO
else
truck.statusquo = AMMOTRUCK.State.IDLE
end
end
end
return self
end
---
-- @param #AMMOTRUCK self
-- @param #table dataset table of #AMMOTRUCK.data entries
-- @return #AMMOTRUCK self
function AMMOTRUCK:CheckReturningTrucks(dataset)
self:T(self.lid .. " CheckReturningTrucks")
local data = dataset
local tgtcoord = self.homezone:GetCoordinate()
local radius = self.homezone:GetRadius()
for _,_data in pairs (data) do
local truck = _data -- #AMMOTRUCK.data
-- see if we arrived at destination
local coord = truck.group:GetCoordinate()
local dist = coord:Get2DDistance(tgtcoord)
self:T({name=truck.name,radius=radius,distance=dist})
if dist <= radius then
-- arrived
truck.statusquo = AMMOTRUCK.State.IDLE
truck.timestamp = timer.getAbsTime()
truck.coordinate = coord
truck.reloads = self.reloads or 5
self:__TruckHome(1,truck)
end
end
return self
end
---
-- @param #AMMOTRUCK self
-- @param #string name Artillery group name to find
-- @return #AMMOTRUCK.data Data
function AMMOTRUCK:FindTarget(name)
self:T(self.lid .. " FindTarget")
local data = nil
local dataset = self.targetlist
for _,_entry in pairs(dataset) do
local entry = _entry -- #AMMOTRUCK.data
if entry.name == name then
data = entry
break
end
end
return data
end
---
-- @param #AMMOTRUCK self
-- @param #string name Truck group name to find
-- @return #AMMOTRUCK.data Data
function AMMOTRUCK:FindTruck(name)
self:T(self.lid .. " FindTruck")
local data = nil
local dataset = self.trucklist
for _,_entry in pairs(dataset) do
local entry = _entry -- #AMMOTRUCK.data
if entry.name == name then
data = entry
break
end
end
return data
end
---
-- @param #AMMOTRUCK self
-- @param #table dataset table of #AMMOTRUCK.data entries
-- @return #AMMOTRUCK self
function AMMOTRUCK:CheckArrivedTrucks(dataset)
self:T(self.lid .. " CheckArrivedTrucks")
local data = dataset
for _,_data in pairs (data) do
-- set to unloading
local truck = _data -- #AMMOTRUCK.data
truck.statusquo = AMMOTRUCK.State.UNLOADING
truck.timestamp = timer.getAbsTime()
self:__TruckUnloading(2,truck)
-- set target to reloading
local aridata = self:FindTarget(truck.targetname) -- #AMMOTRUCK.data
if aridata then
aridata.statusquo = AMMOTRUCK.State.RELOADING
aridata.timestamp = timer.getAbsTime()
end
end
return self
end
---
-- @param #AMMOTRUCK self
-- @param #table dataset table of #AMMOTRUCK.data entries
-- @return #AMMOTRUCK self
function AMMOTRUCK:CheckUnloadingTrucks(dataset)
self:T(self.lid .. " CheckUnloadingTrucks")
local data = dataset
for _,_data in pairs (data) do
-- check timestamp
local truck = _data -- #AMMOTRUCK.data
local Tnow = timer.getAbsTime()
local Tpassed = Tnow - truck.timestamp
local hasammo = self:GetAmmoStatus(truck.targetgroup)
if Tpassed > self.unloadtime and hasammo > self.ammothreshold then
truck.statusquo = AMMOTRUCK.State.RETURNING
truck.timestamp = timer.getAbsTime()
self:__TruckReturning(2,truck)
-- set target to reloaded
local aridata = self:FindTarget(truck.targetname) -- #AMMOTRUCK.data
if aridata then
aridata.statusquo = AMMOTRUCK.State.IDLE
aridata.timestamp = timer.getAbsTime()
end
end
end
return self
end
---
-- @param #AMMOTRUCK self
-- @return #AMMOTRUCK self
function AMMOTRUCK:CheckTargetsAlive()
self:T(self.lid .. " CheckTargetsAlive")
local arilist = self.targetlist
for _,_ari in pairs(arilist) do
local ari = _ari -- #AMMOTRUCK.data
if ari.group and ari.group:IsAlive() then
-- everything fine
else
-- ari dead
self.targetlist[ari.name] = nil
end
end
-- new arrivals?
local aritable = self.targetset:GetSetObjects() --#table
for _,_ari in pairs(aritable) do
local ari = _ari -- Wrapper.Group#GROUP
if ari and ari:IsAlive() and not self.targetlist[ari:GetName()] then
local name = ari:GetName()
local newari = {} -- #AMMOTRUCK.data
newari.name = name
newari.group = ari
newari.statusquo = AMMOTRUCK.State.IDLE
newari.timestamp = timer.getAbsTime()
newari.coordinate = ari:GetCoordinate()
local hasammo = self:GetAmmoStatus(ari)
--newari.ammo = ari:GetAmmunition()
newari.ammo = hasammo
self.targetlist[name] = newari
end
end
return self
end
---
-- @param #AMMOTRUCK self
-- @return #AMMOTRUCK self
function AMMOTRUCK:CheckTrucksAlive()
self:T(self.lid .. " CheckTrucksAlive")
local trucklist = self.trucklist
for _,_truck in pairs(trucklist) do
local truck = _truck -- #AMMOTRUCK.data
if truck.group and truck.group:IsAlive() then
-- everything fine
else
-- truck dead
local tgtname = truck.targetname
local targetdata = self:FindTarget(tgtname) -- #AMMOTRUCK.data
if targetdata then
if targetdata.statusquo ~= AMMOTRUCK.State.IDLE then
targetdata.statusquo = AMMOTRUCK.State.IDLE
end
end
self.trucklist[truck.name] = nil
end
end
-- new arrivals?
local trucktable = self.truckset:GetSetObjects() --#table
for _,_truck in pairs(trucktable) do
local truck = _truck -- Wrapper.Group#GROUP
if truck and truck:IsAlive() and not self.trucklist[truck:GetName()] then
local name = truck:GetName()
local newtruck = {} -- #AMMOTRUCK.data
newtruck.name = name
newtruck.group = truck
if self.hasarmygroup then
-- is (not) already ARMYGROUP?
if truck.ClassName and truck.ClassName == "GROUP" then
local trucker = ARMYGROUP:New(truck)
trucker:Activate()
newtruck.group = trucker
end
end
newtruck.statusquo = AMMOTRUCK.State.IDLE
newtruck.timestamp = timer.getAbsTime()
newtruck.coordinate = truck:GetCoordinate()
newtruck.reloads = self.reloads or 5
self.trucklist[name] = newtruck
end
end
return self
end
---
-- @param #AMMOTRUCK self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #AMMOTRUCK self
function AMMOTRUCK:onafterStart(From, Event, To)
self:T({From, Event, To})
if ARMYGROUP and self.usearmygroup then
self.hasarmygroup = true
else
self.hasarmygroup = false
end
if self.debug then
BASE:TraceOn()
BASE:TraceClass("AMMOTRUCK")
end
self:CheckTargetsAlive()
self:CheckTrucksAlive()
self:__Monitor(-30)
return self
end
---
-- @param #AMMOTRUCK self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #AMMOTRUCK self
function AMMOTRUCK:onafterMonitor(From, Event, To)
self:T({From, Event, To})
self:CheckTargetsAlive()
self:CheckTrucksAlive()
-- update ammo state
local remunition = false
local remunitionqueue = {}
local waitingtargets = {}
for _,_ari in pairs(self.targetlist) do
local data = _ari -- #AMMOTRUCK.data
if data.group and data.group:IsAlive() then
data.ammo = self:GetAmmoStatus(data.group)
data.timestamp = timer.getAbsTime()
local text = string.format("Ari %s | Ammo %d | State %s",data.name,data.ammo,data.statusquo)
self:T(text)
if data.ammo <= self.ammothreshold and (data.statusquo == AMMOTRUCK.State.IDLE or data.statusquo == AMMOTRUCK.State.OUTOFAMMO) then
-- add to remu queue
data.statusquo = AMMOTRUCK.State.OUTOFAMMO
remunitionqueue[#remunitionqueue+1] = data
remunition = true
elseif data.statusquo == AMMOTRUCK.State.WAITING then
waitingtargets[#waitingtargets+1] = data
end
else
self.targetlist[data.name] = nil
end
end
-- sort trucks in buckets
local idletrucks = {}
local drivingtrucks = {}
local unloadingtrucks = {}
local arrivedtrucks = {}
local returningtrucks = {}
local found = false
for _,_truckdata in pairs(self.trucklist) do
local data = _truckdata -- #AMMOTRUCK.data
if data.group and data.group:IsAlive() then
-- check state
local text = string.format("Truck %s | State %s",data.name,data.statusquo)
self:T(text)
if data.statusquo == AMMOTRUCK.State.IDLE then
idletrucks[#idletrucks+1] = data
found = true
elseif data.statusquo == AMMOTRUCK.State.DRIVING then
drivingtrucks[#drivingtrucks+1] = data
elseif data.statusquo == AMMOTRUCK.State.ARRIVED then
arrivedtrucks[#arrivedtrucks+1] = data
elseif data.statusquo == AMMOTRUCK.State.UNLOADING then
unloadingtrucks[#unloadingtrucks+1] = data
elseif data.statusquo == AMMOTRUCK.State.RETURNING then
returningtrucks[#returningtrucks+1] = data
if data.reloads > 0 or data.reloads == -1 then
idletrucks[#idletrucks+1] = data
found = true
end
end
else
self.truckset[data.name] = nil
end
end
-- see if we can/need route one
local n=0
if found and remunition then
-- match
--local match = false
for _,_truckdata in pairs(idletrucks) do
local truckdata = _truckdata -- #AMMOTRUCK.data
local truckcoord = truckdata.group:GetCoordinate() -- Core.Point#COORDINATE
for _,_aridata in pairs(remunitionqueue) do
local aridata = _aridata -- #AMMOTRUCK.data
local aricoord = aridata.coordinate
local distance = truckcoord:Get2DDistance(aricoord)
if distance <= self.remunidist and aridata.statusquo == AMMOTRUCK.State.OUTOFAMMO and n <= #idletrucks then
n = n + 1
aridata.statusquo = AMMOTRUCK.State.REQUESTED
self:__RouteTruck(n*5,truckdata,aridata)
break
end
end
end
end
-- check driving trucks
if #drivingtrucks > 0 then
self:CheckDrivingTrucks(drivingtrucks)
end
-- check arrived trucks
if #arrivedtrucks > 0 then
self:CheckArrivedTrucks(arrivedtrucks)
end
-- check unloading trucks
if #unloadingtrucks > 0 then
self:CheckUnloadingTrucks(unloadingtrucks)
end
-- check returningtrucks trucks
if #returningtrucks > 0 then
self:CheckReturningTrucks(returningtrucks)
end
-- check waiting targets
if #waitingtargets > 0 then
self:CheckWaitingTargets(waitingtargets)
end
self:__Monitor(self.monitor)
return self
end
---
-- @param #AMMOTRUCK self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #AMMOTRUCK.data Truckdata
-- @param #AMMOTRUCK.data Aridata
-- @return #AMMOTRUCK self
function AMMOTRUCK:onafterRouteTruck(From, Event, To, Truckdata, Aridata)
self:T({From, Event, To, Truckdata.name, Aridata.name})
local truckdata = Truckdata -- #AMMOTRUCK.data
local aridata = Aridata -- #AMMOTRUCK.data
local tgtgrp = aridata.group
local tgtzone = ZONE_GROUP:New(aridata.name,tgtgrp,30)
local tgtcoord = tgtzone:GetRandomCoordinate(15)
if self.hasarmygroup then
local mission = AUFTRAG:NewONGUARD(tgtcoord)
local oldmission = truckdata.group:GetMissionCurrent()
if oldmission then oldmission:Cancel() end
mission:SetTime(5)
mission:SetTeleport(false)
truckdata.group:AddMission(mission)
elseif self.routeonroad then
truckdata.group:RouteGroundOnRoad(tgtcoord,30)
else
truckdata.group:RouteGroundTo(tgtcoord,30)
end
truckdata.statusquo = AMMOTRUCK.State.DRIVING
truckdata.targetgroup = tgtgrp
truckdata.targetname = aridata.name
truckdata.targetcoordinate = tgtcoord
aridata.statusquo = AMMOTRUCK.State.WAITING
aridata.timestamp = timer.getAbsTime()
return self
end
---
-- @param #AMMOTRUCK self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #AMMOTRUCK.data Truckdata
-- @return #AMMOTRUCK self
function AMMOTRUCK:onafterTruckUnloading(From, Event, To, Truckdata)
local m = MESSAGE:New("Truck "..Truckdata.name.." unloading!",15,"AmmoTruck"):ToCoalitionIf(self.coalition,self.debug)
local truck = Truckdata -- Functional.AmmoTruck#AMMOTRUCK.data
local coord = truck.group:GetCoordinate()
local heading = truck.group:GetHeading()
heading = heading < 180 and (360-heading) or (heading - 180)
local cid = self.coalition == coalition.side.BLUE and country.id.USA or country.id.RUSSIA
cid = self.coalition == coalition.side.NEUTRAL and country.id.UN_PEACEKEEPERS or cid
local ammo = {}
for i=1,5 do
ammo[i] = SPAWNSTATIC:NewFromType("ammo_cargo","Cargos",cid)
:InitCoordinate(coord:Translate((15+((i-1)*4)),heading))
:Spawn(0,"AmmoCrate-"..math.random(1,10000))
end
local function destroyammo(ammo)
for _,_crate in pairs(ammo) do
_crate:Destroy(false)
end
end
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
---
-- @param #AMMOTRUCK self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @param #AMMOTRUCK.data Truck
-- @return #AMMOTRUCK self
function AMMOTRUCK:onafterTruckReturning(From, Event, To, Truck)
self:T({From, Event, To, Truck.name})
-- route home
local truckdata = Truck -- #AMMOTRUCK.data
local tgtzone = self.homezone
local tgtcoord = tgtzone:GetRandomCoordinate()
if self.hasarmygroup then
local mission = AUFTRAG:NewONGUARD(tgtcoord)
local oldmission = truckdata.group:GetMissionCurrent()
if oldmission then oldmission:Cancel() end
mission:SetTime(5)
mission:SetTeleport(false)
truckdata.group:AddMission(mission)
elseif self.routeonroad then
truckdata.group:RouteGroundOnRoad(tgtcoord,30,1,"Cone")
else
truckdata.group:RouteGroundTo(tgtcoord,30,"Cone",1)
end
return self
end
---
-- @param #AMMOTRUCK self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #AMMOTRUCK self
function AMMOTRUCK:onafterStop(From, Event, To)
self:T({From, Event, To})
return self
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -22,7 +22,7 @@
-- @module Functional.Mantis
-- @image Functional.Mantis.jpg
--
-- Last Update: Feb 2024
-- Last Update: Dec 2023
-------------------------------------------------------------------------
--- **MANTIS** class, extends Core.Base#BASE
@@ -347,17 +347,17 @@ MANTIS.SamType = {
-- @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.SamData = {
["Hawk"] = { Range=35, Blindspot=0, Height=12, Type="Medium", Radar="Hawk" }, -- measures in km
["NASAMS"] = { Range=14, Blindspot=0, Height=7, Type="Short", Radar="NSAMS" }, -- AIM 120B
["Patriot"] = { Range=99, Blindspot=0, Height=25, Type="Long", Radar="Patriot" },
["Rapier"] = { Range=10, Blindspot=0, Height=3, Type="Short", Radar="rapier" },
["Hawk"] = { Range=44, Blindspot=0, Height=9, Type="Medium", Radar="Hawk" }, -- measures in km
["NASAMS"] = { Range=14, Blindspot=0, Height=3, Type="Short", Radar="NSAMS" },
["Patriot"] = { Range=99, Blindspot=0, Height=9, Type="Long", Radar="Patriot" },
["Rapier"] = { Range=6, Blindspot=0, Height=3, Type="Short", Radar="rapier" },
["SA-2"] = { Range=40, Blindspot=7, Height=25, Type="Medium", Radar="S_75M_Volhov" },
["SA-3"] = { Range=18, Blindspot=6, Height=18, Type="Short", Radar="5p73 s-125 ln" },
["SA-5"] = { Range=250, Blindspot=7, Height=40, Type="Long", Radar="5N62V" },
["SA-6"] = { Range=25, Blindspot=0, Height=8, Type="Medium", Radar="1S91" },
["SA-10"] = { Range=119, Blindspot=0, Height=18, Type="Long" , Radar="S-300PS 4"},
["SA-11"] = { Range=35, Blindspot=0, Height=20, Type="Medium", Radar="SA-11" },
["Roland"] = { Range=5, Blindspot=0, Height=5, Type="Short", Radar="Roland" },
["Roland"] = { Range=8, Blindspot=0, Height=3, Type="Short", Radar="Roland" },
["HQ-7"] = { Range=12, Blindspot=0, Height=3, Type="Short", Radar="HQ-7" },
["SA-9"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Strela" },
["SA-8"] = { Range=10, Blindspot=0, Height=5, Type="Short", Radar="Osa 9A33" },
@@ -376,7 +376,7 @@ MANTIS.SamData = {
["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" },
["TAMIR IDFA"] = { Range=20, Blindspot=0.6, Height=12.3, Type="Short", Radar="IRON_DOME_LN" },
["STUNNER IDFA"] = { Range=250, Blindspot=1, Height=45, Type="Long", Radar="DAVID_SLING_LN" },
["STUNNER IDFA"] = { Range=250, Blindspot=1, Height=45, Type="Long", Radar="DAVID_SLING_LN" },
}
--- SAM data HDS
@@ -1222,10 +1222,10 @@ do
function MANTIS:_PreFilterHeight(height)
self:T(self.lid.."_PreFilterHeight")
local set = {}
local dlink = self.Detection -- Ops.Intel#INTEL_DLINK
local dlink = self.Detection -- Ops.Intelligence#INTEL_DLINK
local detectedgroups = dlink:GetContactTable()
for _,_contact in pairs(detectedgroups) do
local contact = _contact -- Ops.Intel#INTEL.Contact
local contact = _contact -- Ops.Intelligence#INTEL.Contact
local grp = contact.group -- Wrapper.Group#GROUP
if grp:IsAlive() then
if grp:GetHeight(true) < height then
@@ -1777,7 +1777,7 @@ do
-- @return #MANTIS self
function MANTIS:_CheckDLinkState()
self:T(self.lid .. "_CheckDLinkState")
local dlink = self.Detection -- Ops.Intel#INTEL_DLINK
local dlink = self.Detection -- Ops.Intelligence#INTEL_DLINK
local TS = timer.getAbsTime()
if not dlink:Is("Running") and (TS - self.DLTimeStamp > 29) then
self.DLink = false

View File

@@ -1226,10 +1226,8 @@ function RANGE:SetSRS(PathToSRS, Port, Coalition, Frequency, Modulation, Volume,
self.instructsrsQ = MSRSQUEUE:New("INSTRUCT")
if PathToGoogleKey then
self.controlmsrs:SetProviderOptionsGoogle(PathToGoogleKey,PathToGoogleKey)
self.controlmsrs:SetProvider(MSRS.Provider.GOOGLE)
self.instructmsrs:SetProviderOptionsGoogle(PathToGoogleKey,PathToGoogleKey)
self.instructmsrs:SetProvider(MSRS.Provider.GOOGLE)
self.controlmsrs:SetGoogle(PathToGoogleKey)
self.instructmsrs:SetGoogle(PathToGoogleKey)
end
else

View File

@@ -78,8 +78,7 @@
-- ### Authors: **FlightControl**
--
-- ### Contributions:
--
-- * **Applevangelist**: Additional functionality, fixes.
--
-- * **Wingthor (TAW)**: Testing & Advice.
-- * **Dutch-Baron (TAW)**: Testing & Advice.
-- * **Whisper**: Testing and Advice.
@@ -117,13 +116,11 @@
-- Special targets can be set that will give extra scores to the players when these are destroyed.
-- Use the methods @{#SCORING.AddUnitScore}() and @{#SCORING.RemoveUnitScore}() to specify a special additional score for a specific @{Wrapper.Unit}s.
-- Use the methods @{#SCORING.AddStaticScore}() and @{#SCORING.RemoveStaticScore}() to specify a special additional score for a specific @{Wrapper.Static}s.
-- Use the method @{#SCORING.AddScoreSetGroup}() to specify a special additional score for a specific @{Wrapper.Group}s gathered in a @{Core.Set#SET_GROUP}.
-- Use the method @{#SCORING.SetGroupGroup}() to specify a special additional score for a specific @{Wrapper.Group}s.
--
-- local Scoring = SCORING:New( "Scoring File" )
-- Scoring:AddUnitScore( UNIT:FindByName( "Unit #001" ), 200 )
-- Scoring:AddStaticScore( STATIC:FindByName( "Static #1" ), 100 )
-- local GroupSet = SET_GROUP:New():FilterPrefixes("RAT"):FilterStart()
-- Scoring:AddScoreSetGroup( GroupSet, 100)
--
-- The above grants an additional score of 200 points for Unit #001 and an additional 100 points of Static #1 if these are destroyed.
-- Note that later in the mission, one can remove these scores set, for example, when the a goal achievement time limit is over.
@@ -229,7 +226,7 @@ SCORING = {
ClassID = 0,
Players = {},
AutoSave = true,
version = "1.18.4"
version = "1.17.1"
}
local _SCORINGCoalition = {
@@ -248,15 +245,13 @@ local _SCORINGCategory = {
--- Creates a new SCORING object to administer the scoring achieved by players.
-- @param #SCORING self
-- @param #string GameName The name of the game. This name is also logged in the CSV score file.
-- @param #string SavePath (Optional) Path where to save the CSV file, defaults to your **<User>\\Saved Games\\DCS\\Logs** folder.
-- @param #boolean AutoSave (Optional) If passed as `false`, then swith autosave off.
-- @return #SCORING self
-- @usage
--
-- -- Define a new scoring object for the mission Gori Valley.
-- ScoringObject = SCORING:New( "Gori Valley" )
--
function SCORING:New( GameName, SavePath, AutoSave )
function SCORING:New( GameName )
-- Inherits from BASE
local self = BASE:Inherit( self, BASE:New() ) -- #SCORING
@@ -319,8 +314,7 @@ function SCORING:New( GameName, SavePath, AutoSave )
end )
-- Create the CSV file.
self.AutoSavePath = SavePath
self.AutoSave = AutoSave or true
self.AutoSave = true
self:OpenCSV( GameName )
return self
@@ -434,31 +428,6 @@ function SCORING:AddScoreGroup( ScoreGroup, Score )
return self
end
--- Specify a special additional score for a @{Core.Set#SET_GROUP}.
-- @param #SCORING self
-- @param Core.Set#SET_GROUP Set The @{Core.Set#SET_GROUP} for which each @{Wrapper.Unit} in each Group a Score is given.
-- @param #number Score The Score value.
-- @return #SCORING
function SCORING:AddScoreSetGroup(Set, Score)
local set = Set:GetSetObjects()
for _,_group in pairs (set) do
if _group and _group:IsAlive() then
self:AddScoreGroup(_group,Score)
end
end
local function AddScore(group)
self:AddScoreGroup(group,Score)
end
function Set:OnAfterAdded(From,Event,To,ObjectName,Object)
AddScore(Object)
end
return self
end
--- Add a @{Core.Zone} to define additional scoring when any object is destroyed in that zone.
-- Note that if a @{Core.Zone} with the same name is already within the scoring added, the @{Core.Zone} (type) and Score will be replaced!
-- This allows for a dynamic destruction zone evolution within your mission.
@@ -1061,11 +1030,11 @@ function SCORING:_EventOnHit( Event )
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
-- After an instant kill we can't compute the threat level anymore. To fix this we compute at OnEventBirth
-- After an instant kill we can't compute the thread level anymore. To fix this we compute at OnEventBirth
if PlayerHit.UNIT.ThreatType == nil then
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
-- if this fails for some reason, set a good default value
if PlayerHit.ThreatType == nil or PlayerHit.ThreatType == "" then
if PlayerHit.ThreatType == nil then
PlayerHit.ThreatLevel = 1
PlayerHit.ThreatType = "Unknown"
end
@@ -1172,7 +1141,7 @@ function SCORING:_EventOnHit( Event )
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT
-- After an instant kill we can't compute the threat level anymore. To fix this we compute at OnEventBirth
-- After an instant kill we can't compute the thread level anymore. To fix this we compute at OnEventBirth
if PlayerHit.UNIT.ThreatType == nil then
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
-- if this fails for some reason, set a good default value
@@ -1319,17 +1288,17 @@ function SCORING:_EventOnDeadOrCrash( Event )
TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy + 1
--self:OnKillPvP(PlayerName, TargetPlayerName, true, TargetThreatLevel, Player.ThreatLevel, ThreatPenalty)
self:OnKillPvP(Player, TargetPlayerName, true, TargetThreatLevel, Player.ThreatLevel, ThreatPenalty)
if Player.HitPlayers[TargetPlayerName] then -- A player destroyed another player
self:OnKillPvP(PlayerName, TargetPlayerName, true)
self:OnKillPvP(Player, TargetPlayerName, true)
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Penalty: -" .. ThreatPenalty .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information )
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
else
self:OnKillPvE(PlayerName, TargetUnitName, true, TargetThreatLevel, Player.ThreatLevel, ThreatPenalty)
self:OnKillPvE(Player, TargetUnitName, true, TargetThreatLevel, Player.ThreatLevel, ThreatPenalty)
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Penalty: -" .. ThreatPenalty .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information )
@@ -1357,14 +1326,14 @@ function SCORING:_EventOnDeadOrCrash( Event )
else
Player.PlayerKills = 1
end
self:OnKillPvP(PlayerName, TargetPlayerName, false, TargetThreatLevel, Player.ThreatLevel, ThreatScore)
self:OnKillPvP(Player, TargetPlayerName, false, TargetThreatLevel, Player.ThreatLevel, ThreatScore)
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Score: +" .. ThreatScore .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information )
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
else
self:OnKillPvE(PlayerName, TargetUnitName, false, TargetThreatLevel, Player.ThreatLevel, ThreatScore)
self:OnKillPvE(Player, TargetUnitName, false, TargetThreatLevel, Player.ThreatLevel, ThreatScore)
MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Score: +" .. ThreatScore .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information )
@@ -1842,11 +1811,10 @@ end
function SCORING:OpenCSV( ScoringCSV )
self:F( ScoringCSV )
if lfs and io and os and self.AutoSave == true then
if lfs and io and os and self.AutoSave then
if ScoringCSV then
self.ScoringCSV = ScoringCSV
local path = self.AutoSavePath or lfs.writedir() .. [[Logs\]]
local fdir = path .. self.ScoringCSV .. " " .. os.date( "%Y-%m-%d %H-%M-%S" ) .. ".csv"
local fdir = lfs.writedir() .. [[Logs\]] .. self.ScoringCSV .. " " .. os.date( "%Y-%m-%d %H-%M-%S" ) .. ".csv"
self.CSVFile, self.err = io.open( fdir, "w+" )
if not self.CSVFile then
@@ -1967,23 +1935,23 @@ end
--- Handles the event when one player kill another player
-- @param #SCORING self
-- @param #string PlayerName The attacking player
-- @param #string TargetPlayerName The name of the killed player
-- @param #boolean IsTeamKill true if this kill was a team kill
-- @param #number TargetThreatLevel Threat level of the target
-- @param #number PlayerThreatLevel Threat level of the player
-- @param #PLAYER Player the ataching player
-- @param #string TargetPlayerName the name of the killed player
-- @param #bool IsTeamKill true if this kill was a team kill
-- @param #number TargetThreatLevel Thread level of the target
-- @param #number PlayerThreatLevelThread level of the player
-- @param #number Score The score based on both threat levels
function SCORING:OnKillPvP(PlayerName, TargetPlayerName, IsTeamKill, TargetThreatLevel, PlayerThreatLevel, Score)
function SCORING:OnKillPvP(Player, TargetPlayerName, IsTeamKill, TargetThreatLevel, PlayerThreatLevel, Score)
end
--- Handles the event when one player kill another player
-- @param #SCORING self
-- @param #string PlayerName The attacking player
-- @param #PLAYER Player the ataching player
-- @param #string TargetUnitName the name of the killed unit
-- @param #boolean IsTeamKill true if this kill was a team kill
-- @param #number TargetThreatLevel Threat level of the target
-- @param #number PlayerThreatLevel Threat level of the player
-- @param #bool IsTeamKill true if this kill was a team kill
-- @param #number TargetThreatLevel Thread level of the target
-- @param #number PlayerThreatLevelThread level of the player
-- @param #number Score The score based on both threat levels
function SCORING:OnKillPvE(PlayerName, TargetUnitName, IsTeamKill, TargetThreatLevel, PlayerThreatLevel, Score)
function SCORING:OnKillPvE(Player, TargetUnitName, IsTeamKill, TargetThreatLevel, PlayerThreatLevel, Score)
end
end

View File

@@ -320,6 +320,9 @@ function SEAD:onafterCalculateHitZone(From,Event,To,SEADWeapon,pos0,height,SEADG
end
local seadset = SET_GROUP:New():FilterPrefixes(self.SEADGroupPrefixes):FilterZones({targetzone}):FilterOnce()
local tgtcoord = targetzone:GetRandomPointVec2()
--if tgtcoord and tgtcoord.ClassName == "COORDINATE" then
--local tgtgrp = seadset:FindNearestGroupFromPointVec2(tgtcoord)
local tgtgrp = seadset:GetRandom()
local _targetgroup = nil
local _targetgroupname = "none"

File diff suppressed because it is too large Load Diff

View File

@@ -1,590 +0,0 @@
--- **Functional** - TIRESIAS - manages AI behaviour.
--
-- ===
--
-- The @{#TIRESIAS} class is working in the back to keep your large-scale ground units in check.
--
-- ## Features:
--
-- * Designed to keep CPU and Network usage lower on missions with a lot of ground units.
-- * Does not affect ships to keep the Navy guys happy.
-- * Does not affect OpsGroup type groups.
-- * Distinguishes between SAM groups, AAA groups and other ground groups.
-- * Exceptions can be defined to keep certain actions going.
-- * Works coalition-independent in the back
-- * Easy setup.
--
-- ===
--
-- ## Missions:
--
-- ### [TIRESIAS](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/master)
--
-- ===
--
-- ### Author : **applevangelist **
--
-- @module Functional.Tiresias
-- @image Functional.Tiresias.jpg
--
-- Last Update: Dec 2023
-------------------------------------------------------------------------
--- **TIRESIAS** class, extends Core.Base#BASE
-- @type TIRESIAS
-- @field #string ClassName
-- @field #booelan debug
-- @field #string version
-- @field #number Interval
-- @field Core.Set#SET_GROUP GroundSet
-- @field #number Coalition
-- @field Core.Set#SET_GROUP VehicleSet
-- @field Core.Set#SET_GROUP AAASet
-- @field Core.Set#SET_GROUP SAMSet
-- @field Core.Set#SET_GROUP ExceptionSet
-- @field Core.Set#SET_OPSGROUP OpsGroupSet
-- @field #number AAARange
-- @field #number HeloSwitchRange
-- @field #number PlaneSwitchRange
-- @field Core.Set#SET_GROUP FlightSet
-- @field #boolean SwitchAAA
-- @extends Core.Fsm#FSM
---
-- @type TIRESIAS.Data
-- @field #string type
-- @field #number range
-- @field #boolean invisible
-- @field #boolean AIOff
-- @field #boolean exception
--- *Tiresias, Greek demi-god and shapeshifter, blinded by the Gods, works as oracle for you.* (Wiki)
--
-- ===
--
-- ## TIRESIAS Concept
--
-- * Designed to keep CPU and Network usage lower on missions with a lot of ground units.
-- * Does not affect ships to keep the Navy guys happy.
-- * Does not affect OpsGroup type groups.
-- * Distinguishes between SAM groups, AAA groups and other ground groups.
-- * Exceptions can be defined in SET_GROUP objects to keep certain actions going.
-- * Works coalition-independent in the back
-- * Easy setup.
--
-- ## Setup
--
-- Setup is a one-liner:
--
-- local blinder = TIRESIAS:New()
--
-- Optionally you can set up exceptions, e.g. for convoys driving around
--
-- local exceptionset = SET_GROUP:New():FilterCoalitions("red"):FilterPrefixes("Convoy"):FilterStart()
-- local blinder = TIRESIAS:New()
-- blinder:AddExceptionSet(exceptionset)
--
-- Options
--
-- -- Setup different radius for activation around helo and airplane groups (applies to AI and humans)
-- blinder:SetActivationRanges(10,25) -- defaults are 10, and 25
--
-- -- Setup engagement ranges for AAA (non-advanced SAM units like Flaks etc) and if you want them to be AIOff
-- blinder:SetAAARanges(60,true) -- defaults are 60, and true
--
-- @field #TIRESIAS
TIRESIAS = {
ClassName = "TIRESIAS",
debug = false,
version = "0.0.4",
Interval = 20,
GroundSet = nil,
VehicleSet = nil,
AAASet = nil,
SAMSet = nil,
ExceptionSet = nil,
AAARange = 60, -- 60%
HeloSwitchRange = 10, -- NM
PlaneSwitchRange = 25, -- NM
SwitchAAA = true,
}
--- [USER] Create a new Tiresias object and start it up.
-- @param #TIRESIAS self
-- @return #TIRESIAS self
function TIRESIAS:New()
-- Inherit everything from FSM class.
local self = BASE:Inherit(self, FSM:New()) -- #TIRESIAS
--- FSM Functions ---
-- Start State.
self:SetStartState("Stopped")
-- Add FSM transitions.
-- From State --> Event --> To State
self:AddTransition("Stopped", "Start", "Running") -- Start FSM.
self:AddTransition("*", "Status", "*") -- TIRESIAS status update.
self:AddTransition("*", "Stop", "Stopped") -- Stop FSM.
self.ExceptionSet = SET_GROUP:New():Clear(false)
self:HandleEvent(EVENTS.PlayerEnterAircraft,self._EventHandler)
self.lid = string.format("TIRESIAS %s | ",self.version)
self:I(self.lid.."Managing ground groups!")
--- Triggers the FSM event "Stop". Stops TIRESIAS and all its event handlers.
-- @function [parent=#TIRESIAS] Stop
-- @param #TIRESIAS self
--- Triggers the FSM event "Stop" after a delay. Stops TIRESIAS and all its event handlers.
-- @function [parent=#TIRESIAS] __Stop
-- @param #TIRESIAS self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Start". Starts TIRESIAS and all its event handlers. Note - `:New()` already starts the instance.
-- @function [parent=#TIRESIAS] Start
-- @param #TIRESIAS self
--- Triggers the FSM event "Start" after a delay. Starts TIRESIAS and all its event handlers. Note - `:New()` already starts the instance.
-- @function [parent=#TIRESIAS] __Start
-- @param #TIRESIAS self
-- @param #number delay Delay in seconds.
self:__Start(1)
return self
end
-------------------------------------------------------------------------------------------------------------
--
-- Helper Functions
--
-------------------------------------------------------------------------------------------------------------
---[USER] Set activation radius for Helos and Planes in Nautical Miles.
-- @param #TIRESIAS self
-- @param #number HeloMiles Radius around a Helicopter in which AI ground units will be activated. Defaults to 10NM.
-- @param #number PlaneMiles Radius around an Airplane in which AI ground units will be activated. Defaults to 25NM.
-- @return #TIRESIAS self
function TIRESIAS:SetActivationRanges(HeloMiles,PlaneMiles)
self.HeloSwitchRange = HeloMiles or 10
self.PlaneSwitchRange = PlaneMiles or 25
return self
end
---[USER] Set AAA Ranges - AAA equals non-SAM systems which qualify as AAA in DCS world.
-- @param #TIRESIAS self
-- @param #number FiringRange The engagement range that AAA units will be set to. Can be 0 to 100 (percent). Defaults to 60.
-- @param #boolean SwitchAAA Decide if these system will have their AI switched off, too. Defaults to true.
-- @return #TIRESIAS self
function TIRESIAS:SetAAARanges(FiringRange,SwitchAAA)
self.AAARange = FiringRange or 60
self.SwitchAAA = (SwitchAAA == false) and false or true
return self
end
--- [USER] Add a SET_GROUP of GROUP objects as exceptions. Can be done multiple times.
-- @param #TIRESIAS self
-- @param Core.Set#SET_GROUP Set to add to the exception list.
-- @return #TIRESIAS self
function TIRESIAS:AddExceptionSet(Set)
self:T(self.lid.."AddExceptionSet")
local exceptions = self.ExceptionSet
Set:ForEachGroupAlive(
function(grp)
if not grp.Tiresias then
grp.Tiresias = { -- #TIRESIAS.Data
type = "Exception",
exception = true,
}
exceptions:AddGroup(grp,true)
end
BASE:I("TIRESIAS: Added exception group: "..grp:GetName())
end
)
return self
end
--- [INTERNAL] Filter Function
-- @param Wrapper.Group#GROUP Group
-- @return #boolean isin
function TIRESIAS._FilterNotAAA(Group)
local grp = Group -- Wrapper.Group#GROUP
local isaaa = grp:IsAAA()
if isaaa == true and grp:IsGround() and not grp:IsShip() then
return false -- remove from SET
else
return true -- keep in SET
end
end
--- [INTERNAL] Filter Function
-- @param Wrapper.Group#GROUP Group
-- @return #boolean isin
function TIRESIAS._FilterNotSAM(Group)
local grp = Group -- Wrapper.Group#GROUP
local issam = grp:IsSAM()
if issam == true and grp:IsGround() and not grp:IsShip() then
return false -- remove from SET
else
return true -- keep in SET
end
end
--- [INTERNAL] Filter Function
-- @param Wrapper.Group#GROUP Group
-- @return #boolean isin
function TIRESIAS._FilterAAA(Group)
local grp = Group -- Wrapper.Group#GROUP
local isaaa = grp:IsAAA()
if isaaa == true and grp:IsGround() and not grp:IsShip() then
return true -- remove from SET
else
return false -- keep in SET
end
end
--- [INTERNAL] Filter Function
-- @param Wrapper.Group#GROUP Group
-- @return #boolean isin
function TIRESIAS._FilterSAM(Group)
local grp = Group -- Wrapper.Group#GROUP
local issam = grp:IsSAM()
if issam == true and grp:IsGround() and not grp:IsShip() then
return true -- remove from SET
else
return false -- keep in SET
end
end
--- [INTERNAL] Init Groups
-- @param #TIRESIAS self
-- @return #TIRESIAS self
function TIRESIAS:_InitGroups()
self:T(self.lid.."_InitGroups")
-- Set all groups invisible/motionless
local EngageRange = self.AAARange
local SwitchAAA = self.SwitchAAA
--- AAA
self.AAASet:ForEachGroupAlive(
function(grp)
if not grp.Tiresias then
grp:OptionEngageRange(EngageRange)
grp:SetCommandInvisible(true)
if SwitchAAA then
grp:SetAIOff()
grp:EnableEmission(false)
end
grp.Tiresias = { -- #TIRESIAS.Data
type = "AAA",
invisible = true,
range = EngageRange,
exception = false,
AIOff = SwitchAAA,
}
end
if grp.Tiresias and (not grp.Tiresias.exception == true) then
if grp.Tiresias.invisible and grp.Tiresias.invisible == false then
grp:SetCommandInvisible(true)
grp.Tiresias.invisible = true
if SwitchAAA then
grp:SetAIOff()
grp:EnableEmission(false)
grp.Tiresias.AIOff = true
end
end
end
--BASE:I(string.format("Init/Switch off AAA %s (Exception %s)",grp:GetName(),tostring(grp.Tiresias.exception)))
end
)
--- Vehicles
self.VehicleSet:ForEachGroupAlive(
function(grp)
if not grp.Tiresias then
grp:SetAIOff()
grp:SetCommandInvisible(true)
grp.Tiresias = { -- #TIRESIAS.Data
type = "Vehicle",
invisible = true,
AIOff = true,
exception = false,
}
end
if grp.Tiresias and (not grp.Tiresias.exception == true) then
if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then
grp:SetCommandInvisible(true)
grp:SetAIOff()
grp.Tiresias.invisible = true
end
end
--BASE:I(string.format("Init/Switch off Vehicle %s (Exception %s)",grp:GetName(),tostring(grp.Tiresias.exception)))
end
)
--- SAM
self.SAMSet:ForEachGroupAlive(
function(grp)
if not grp.Tiresias then
grp:SetCommandInvisible(true)
grp.Tiresias = { -- #TIRESIAS.Data
type = "SAM",
invisible = true,
exception = false,
}
end
if grp.Tiresias and (not grp.Tiresias.exception == true) then
if grp.Tiresias and grp.Tiresias.invisible and grp.Tiresias.invisible == false then
grp:SetCommandInvisible(true)
grp.Tiresias.invisible = true
end
end
--BASE:I(string.format("Init/Switch off SAM %s (Exception %s)",grp:GetName(),tostring(grp.Tiresias.exception)))
end
)
return self
end
--- [INTERNAL] Event handler function
-- @param #TIRESIAS self
-- @param Core.Event#EVENTDATA EventData
-- @return #TIRESIAS self
function TIRESIAS:_EventHandler(EventData)
self:T(string.format("%s Event = %d",self.lid, EventData.id))
local event = EventData -- Core.Event#EVENTDATA
if event.id == EVENTS.PlayerEnterAircraft or event.id == EVENTS.PlayerEnterUnit then
--local _coalition = event.IniCoalition
--if _coalition ~= self.Coalition then
-- return --ignore!
--end
local unitname = event.IniUnitName or "none"
local _unit = event.IniUnit
local _group = event.IniGroup
if _group and _group:IsAlive() then
local radius = self.PlaneSwitchRange
if _group:IsHelicopter() then
radius = self.HeloSwitchRange
end
self:_SwitchOnGroups(_group,radius)
end
end
return self
end
--- [INTERNAL] Switch Groups Behaviour
-- @param #TIRESIAS self
-- @param Wrapper.Group#GROUP group
-- @param #number radius Radius in NM
-- @return #TIRESIAS self
function TIRESIAS:_SwitchOnGroups(group,radius)
self:T(self.lid.."_SwitchOnGroups "..group:GetName().." Radius "..radius.." NM")
local zone = ZONE_GROUP:New("Zone-"..group:GetName(),group,UTILS.NMToMeters(radius))
local ground = SET_GROUP:New():FilterCategoryGround():FilterZones({zone}):FilterOnce()
local count = ground:CountAlive()
if self.debug then
local text = string.format("There are %d groups around this plane or helo!",count)
self:I(text)
end
local SwitchAAA = self.SwitchAAA
if ground:CountAlive() > 0 then
ground:ForEachGroupAlive(
function(grp)
if grp.Tiresias and grp.Tiresias.type and (not grp.Tiresias.exception == true ) then
if grp.Tiresias.invisible == true then
grp:SetCommandInvisible(false)
grp.Tiresias.invisible = false
end
if grp.Tiresias.type == "Vehicle" and grp.Tiresias.AIOff and grp.Tiresias.AIOff == true then
grp:SetAIOn()
grp.Tiresias.AIOff = false
end
if SwitchAAA and grp.Tiresias.type == "AAA" and grp.Tiresias.AIOff and grp.Tiresias.AIOff == true then
grp:SetAIOn()
grp:EnableEmission(true)
grp.Tiresias.AIOff = false
end
--BASE:I(string.format("TIRESIAS - Switch on %s %s (Exception %s)",tostring(grp.Tiresias.type),grp:GetName(),tostring(grp.Tiresias.exception)))
else
BASE:E("TIRESIAS - This group has not been initialized or is an exception!")
end
end
)
end
return self
end
-------------------------------------------------------------------------------------------------------------
--
-- FSM Functions
--
-------------------------------------------------------------------------------------------------------------
--- [INTERNAL] FSM Function
-- @param #TIRESIAS self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #TIRESIAS self
function TIRESIAS:onafterStart(From, Event, To)
self:T({From, Event, To})
local VehicleSet = SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterNotAAA):FilterFunction(TIRESIAS._FilterNotSAM):FilterStart()
local AAASet = SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterAAA):FilterStart()
local SAMSet = SET_GROUP:New():FilterCategoryGround():FilterFunction(TIRESIAS._FilterSAM):FilterStart()
local OpsGroupSet = SET_OPSGROUP:New():FilterActive(true):FilterStart()
self.FlightSet = SET_GROUP:New():FilterCategories({"plane","helicopter"}):FilterStart()
local EngageRange = self.AAARange
local ExceptionSet = self.ExceptionSet
if self.ExceptionSet then
function ExceptionSet:OnAfterAdded(From,Event,To,ObjectName,Object)
BASE:I("TIRESIAS: EXCEPTION Object Added: "..Object:GetName())
if Object and Object:IsAlive() then
Object.Tiresias = { -- #TIRESIAS.Data
type = "Exception",
exception = true,
}
Object:SetAIOn()
Object:SetCommandInvisible(false)
Object:EnableEmission(true)
end
end
local OGS = OpsGroupSet:GetAliveSet()
for _,_OG in pairs(OGS or {}) do
local OG = _OG -- Ops.OpsGroup#OPSGROUP
local grp = OG:GetGroup()
ExceptionSet:AddGroup(grp,true)
end
function OpsGroupSet:OnAfterAdded(From,Event,To,ObjectName,Object)
local grp = Object:GetGroup()
ExceptionSet:AddGroup(grp,true)
end
end
function VehicleSet:OnAfterAdded(From,Event,To,ObjectName,Object)
BASE:I("TIRESIAS: VEHCILE Object Added: "..Object:GetName())
if Object and Object:IsAlive() then
Object:SetAIOff()
Object:SetCommandInvisible(true)
Object.Tiresias = { -- #TIRESIAS.Data
type = "Vehicle",
invisible = true,
AIOff = true,
exception = false,
}
end
end
local SwitchAAA = self.SwitchAAA
function AAASet:OnAfterAdded(From,Event,To,ObjectName,Object)
if Object and Object:IsAlive() then
BASE:I("TIRESIAS: AAA Object Added: "..Object:GetName())
Object:OptionEngageRange(EngageRange)
Object:SetCommandInvisible(true)
if SwitchAAA then
Object:SetAIOff()
Object:EnableEmission(false)
end
Object.Tiresias = { -- #TIRESIAS.Data
type = "AAA",
invisible = true,
range = EngageRange,
exception = false,
AIOff = SwitchAAA,
}
end
end
function SAMSet:OnAfterAdded(From,Event,To,ObjectName,Object)
if Object and Object:IsAlive() then
BASE:I("TIRESIAS: SAM Object Added: "..Object:GetName())
Object:SetCommandInvisible(true)
Object.Tiresias = { -- #TIRESIAS.Data
type = "SAM",
invisible = true,
exception = false,
}
end
end
self.VehicleSet = VehicleSet
self.AAASet = AAASet
self.SAMSet = SAMSet
self.OpsGroupSet = OpsGroupSet
self:_InitGroups()
self:__Status(1)
return self
end
--- [INTERNAL] FSM Function
-- @param #TIRESIAS self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #TIRESIAS self
function TIRESIAS:onbeforeStatus(From, Event, To)
self:T({From, Event, To})
if self:GetState() == "Stopped" then
return false
end
return self
end
--- [INTERNAL] FSM Function
-- @param #TIRESIAS self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #TIRESIAS self
function TIRESIAS:onafterStatus(From, Event, To)
self:T({From, Event, To})
if self.debug then
local count = self.VehicleSet:CountAlive()
local AAAcount = self.AAASet:CountAlive()
local SAMcount = self.SAMSet:CountAlive()
local text = string.format("Overall: %d | Vehicles: %d | AAA: %d | SAM: %d",count+AAAcount+SAMcount,count,AAAcount,SAMcount)
self:I(text)
end
self:_InitGroups()
if self.FlightSet:CountAlive() > 0 then
local Set = self.FlightSet:GetAliveSet()
for _,_plane in pairs(Set) do
local plane = _plane -- Wrapper.Group#GROUP
local radius = self.PlaneSwitchRange
if plane:IsHelicopter() then
radius = self.HeloSwitchRange
end
self:_SwitchOnGroups(_plane,radius)
end
end
if self:GetState() ~= "Stopped" then
self:__Status(self.Interval)
end
return self
end
--- [INTERNAL] FSM Function
-- @param #TIRESIAS self
-- @param #string From
-- @param #string Event
-- @param #string To
-- @return #TIRESIAS self
function TIRESIAS:onafterStop(From, Event, To)
self:T({From, Event, To})
self:UnHandleEvent(EVENTS.PlayerEnterAircraft)
return self
end
-------------------------------------------------------------------------------------------------------------
--
-- End
--
-------------------------------------------------------------------------------------------------------------

View File

@@ -1629,7 +1629,7 @@ WAREHOUSE = {
-- @field #boolean arrived If true, asset arrived at its destination.
--
-- @field #number damage Damage of asset group in percent.
-- @field Ops.Airwing#AIRWING.Payload payload The payload of the asset.
-- @field Ops.AirWing#AIRWING.Payload payload The payload of the asset.
-- @field Ops.OpsGroup#OPSGROUP flightgroup The flightgroup object.
-- @field Ops.Cohort#COHORT cohort The cohort this asset belongs to.
-- @field Ops.Legion#LEGION legion The legion this asset belonts to.
@@ -3414,7 +3414,7 @@ end
-- FSM states
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- On after Start event. Starts the warehouse. Adds event handlers and schedules status updates of reqests and queue.
--- On after Start event. Starts the warehouse. Addes event handlers and schedules status updates of reqests and queue.
-- @param #WAREHOUSE self
-- @param #string From From state.
-- @param #string Event Event.
@@ -3595,7 +3595,6 @@ function WAREHOUSE:onafterStatus(From, Event, To)
local Trepair=self:GetRunwayRepairtime()
self:I(self.lid..string.format("Runway destroyed! Will be repaired in %d sec", Trepair))
if Trepair==0 then
self.runwaydestroyed = nil
self:RunwayRepaired()
end
end
@@ -5393,8 +5392,7 @@ function WAREHOUSE:onafterRunwayDestroyed(From, Event, To)
self:_InfoMessage(text)
self.runwaydestroyed=timer.getAbsTime()
return self
end
--- On after "RunwayRepaired" event.
@@ -5409,8 +5407,7 @@ function WAREHOUSE:onafterRunwayRepaired(From, Event, To)
self:_InfoMessage(text)
self.runwaydestroyed=nil
return self
end

View File

@@ -1,4 +1,4 @@
--- **Functional (WIP)** - Base class modeling processes to achieve goals involving coalition zones.
--- **Functional** - Base class that models processes to achieve goals involving a Zone for a Coalition.
--
-- ===
--

View File

@@ -33,7 +33,6 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/Spot.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/MarkerOps_Base.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/TextAndSound.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/Pathline.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/ClientMenu.lua')
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Object.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Identifiable.lua' )
@@ -78,12 +77,6 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Warehouse.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Fox.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Mantis.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Shorad.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/AICSAR.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/AmmoTruck.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Autolase.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/ZoneGoalCargo.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Tiresias.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Stratego.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Airboss.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/RecoveryTanker.lua' )
@@ -91,31 +84,6 @@ __Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/RescueHelo.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/ATIS.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/CTLD.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/CSAR.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/AirWing.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/ArmyGroup.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Auftrag.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Awacs.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Brigade.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Chief.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Cohort.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Commander.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Fleet.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/FlightControl.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/FlightGroup.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Flotilla.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Intelligence.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Legion.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/NavyGroup.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Operation.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/OpsGroup.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/OpsTransport.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/OpsZone.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Platoon.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/PlayerTask.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/PlayerRecce.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Squadron.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/Target.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/EasyGCICAP.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Balancer.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Air.lua' )

View File

@@ -1,172 +0,0 @@
__Moose.Include( 'Utilities\\Enums.lua' )
__Moose.Include( 'Utilities\\Routines.lua' )
__Moose.Include( 'Utilities\\Utils.lua' )
__Moose.Include( 'Utilities\\Profiler.lua' )
__Moose.Include( 'Utilities\\Templates.lua' )
__Moose.Include( 'Utilities\\STTS.lua' )
__Moose.Include( 'Utilities\\FiFo.lua' )
__Moose.Include( 'Utilities\\Socket.lua' )
__Moose.Include( 'Core\\Base.lua' )
__Moose.Include( 'Core\\Beacon.lua' )
__Moose.Include( 'Core\\UserFlag.lua' )
__Moose.Include( 'Core\\Report.lua' )
__Moose.Include( 'Core\\Scheduler.lua' )
__Moose.Include( 'Core\\ScheduleDispatcher.lua' )
__Moose.Include( 'Core\\Event.lua' )
__Moose.Include( 'Core\\Settings.lua' )
__Moose.Include( 'Core\\Menu.lua' )
__Moose.Include( 'Core\\Zone.lua' )
__Moose.Include( 'Core\\Zone_Detection.lua' )
__Moose.Include( 'Core\\Database.lua' )
__Moose.Include( 'Core\\Set.lua' )
__Moose.Include( 'Core\\Point.lua' )
__Moose.Include( 'Core\\Velocity.lua' )
__Moose.Include( 'Core\\Message.lua' )
__Moose.Include( 'Core\\Fsm.lua' )
__Moose.Include( 'Core\\Spawn.lua' )
__Moose.Include( 'Core\\SpawnStatic.lua' )
__Moose.Include( 'Core\\Timer.lua' )
__Moose.Include( 'Core\\Goal.lua' )
__Moose.Include( 'Core\\Spot.lua' )
__Moose.Include( 'Core\\Astar.lua' )
__Moose.Include( 'Core\\MarkerOps_Base.lua' )
__Moose.Include( 'Core\\TextAndSound.lua' )
__Moose.Include( 'Core\\Condition.lua' )
__Moose.Include( 'Core\\ClientMenu.lua' )
__Moose.Include( 'Wrapper\\Object.lua' )
__Moose.Include( 'Wrapper\\Identifiable.lua' )
__Moose.Include( 'Wrapper\\Positionable.lua' )
__Moose.Include( 'Wrapper\\Controllable.lua' )
__Moose.Include( 'Wrapper\\Group.lua' )
__Moose.Include( 'Wrapper\\Unit.lua' )
__Moose.Include( 'Wrapper\\Client.lua' )
__Moose.Include( 'Wrapper\\Static.lua' )
__Moose.Include( 'Wrapper\\Airbase.lua' )
__Moose.Include( 'Wrapper\\Scenery.lua' )
__Moose.Include( 'Wrapper\\Marker.lua' )
__Moose.Include( 'Cargo\\Cargo.lua' )
__Moose.Include( 'Cargo\\CargoUnit.lua' )
__Moose.Include( 'Cargo\\CargoSlingload.lua' )
__Moose.Include( 'Cargo\\CargoCrate.lua' )
__Moose.Include( 'Cargo\\CargoGroup.lua' )
__Moose.Include( 'Functional\\Scoring.lua' )
__Moose.Include( 'Functional\\CleanUp.lua' )
__Moose.Include( 'Functional\\Movement.lua' )
__Moose.Include( 'Functional\\Sead.lua' )
__Moose.Include( 'Functional\\Escort.lua' )
__Moose.Include( 'Functional\\MissileTrainer.lua' )
__Moose.Include( 'Functional\\ATC_Ground.lua' )
__Moose.Include( 'Functional\\Detection.lua' )
__Moose.Include( 'Functional\\DetectionZones.lua' )
__Moose.Include( 'Functional\\Designate.lua' )
__Moose.Include( 'Functional\\RAT.lua' )
__Moose.Include( 'Functional\\Range.lua' )
__Moose.Include( 'Functional\\ZoneGoal.lua' )
__Moose.Include( 'Functional\\ZoneGoalCoalition.lua' )
__Moose.Include( 'Functional\\ZoneCaptureCoalition.lua' )
__Moose.Include( 'Functional\\Artillery.lua' )
__Moose.Include( 'Functional\\Suppression.lua' )
__Moose.Include( 'Functional\\PseudoATC.lua' )
__Moose.Include( 'Functional\\Warehouse.lua' )
__Moose.Include( 'Functional\\Fox.lua' )
__Moose.Include( 'Functional\\Mantis.lua' )
__Moose.Include( 'Functional\\Shorad.lua' )
__Moose.Include( 'Functional\\Autolase.lua' )
__Moose.Include( 'Functional\\AICSAR.lua' )
__Moose.Include( 'Ops\\Airboss.lua' )
__Moose.Include( 'Ops\\RecoveryTanker.lua' )
__Moose.Include( 'Ops\\RescueHelo.lua' )
__Moose.Include( 'Ops\\ATIS.lua' )
__Moose.Include( 'Ops\\Auftrag.lua' )
__Moose.Include( 'Ops\\Target.lua' )
__Moose.Include( 'Ops\\OpsGroup.lua' )
__Moose.Include( 'Ops\\FlightGroup.lua' )
__Moose.Include( 'Ops\\NavyGroup.lua' )
__Moose.Include( 'Ops\\ArmyGroup.lua' )
__Moose.Include( 'Ops\\Cohort.lua' )
__Moose.Include( 'Ops\\Squadron.lua' )
__Moose.Include( 'Ops\\Platoon.lua' )
__Moose.Include( 'Ops\\Legion.lua' )
__Moose.Include( 'Ops\\AirWing.lua' )
__Moose.Include( 'Ops\\Brigade.lua' )
__Moose.Include( 'Ops\\Intelligence.lua' )
__Moose.Include( 'Ops\\Commander.lua' )
__Moose.Include( 'Ops\\OpsTransport.lua' )
__Moose.Include( 'Ops\\CSAR.lua' )
__Moose.Include( 'Ops\\CTLD.lua' )
__Moose.Include( 'Ops\\OpsZone.lua' )
__Moose.Include( 'Ops\\Chief.lua' )
__Moose.Include( 'Ops\\Flotilla.lua' )
__Moose.Include( 'Ops\\Fleet.lua' )
__Moose.Include( 'Ops\\Awacs.lua' )
__Moose.Include( 'Ops\\PlayerTask.lua' )
__Moose.Include( 'Ops\\Operation.lua' )
__Moose.Include( 'Ops\\FlightControl.lua' )
__Moose.Include( 'AI\\AI_Balancer.lua' )
__Moose.Include( 'AI\\AI_Air.lua' )
__Moose.Include( 'AI\\AI_Air_Patrol.lua' )
__Moose.Include( 'AI\\AI_Air_Engage.lua' )
__Moose.Include( 'AI\\AI_A2A_Patrol.lua' )
__Moose.Include( 'AI\\AI_A2A_Cap.lua' )
__Moose.Include( 'AI\\AI_A2A_Gci.lua' )
__Moose.Include( 'AI\\AI_A2A_Dispatcher.lua' )
__Moose.Include( 'AI\\AI_A2G_BAI.lua' )
__Moose.Include( 'AI\\AI_A2G_CAS.lua' )
__Moose.Include( 'AI\\AI_A2G_SEAD.lua' )
__Moose.Include( 'AI\\AI_A2G_Dispatcher.lua' )
__Moose.Include( 'AI\\AI_Patrol.lua' )
__Moose.Include( 'AI\\AI_Cap.lua' )
__Moose.Include( 'AI\\AI_Cas.lua' )
__Moose.Include( 'AI\\AI_Bai.lua' )
__Moose.Include( 'AI\\AI_Formation.lua' )
__Moose.Include( 'AI\\AI_Escort.lua' )
__Moose.Include( 'AI\\AI_Escort_Request.lua' )
__Moose.Include( 'AI\\AI_Escort_Dispatcher.lua' )
__Moose.Include( 'AI\\AI_Escort_Dispatcher_Request.lua' )
__Moose.Include( 'AI\\AI_Cargo.lua' )
__Moose.Include( 'AI\\AI_Cargo_APC.lua' )
__Moose.Include( 'AI\\AI_Cargo_Helicopter.lua' )
__Moose.Include( 'AI\\AI_Cargo_Airplane.lua' )
__Moose.Include( 'AI\\AI_Cargo_Ship.lua' )
__Moose.Include( 'AI\\AI_Cargo_Dispatcher.lua' )
__Moose.Include( 'AI\\AI_Cargo_Dispatcher_APC.lua' )
__Moose.Include( 'AI\\AI_Cargo_Dispatcher_Helicopter.lua' )
__Moose.Include( 'AI\\AI_Cargo_Dispatcher_Airplane.lua' )
__Moose.Include( 'AI\\AI_Cargo_Dispatcher_Ship.lua' )
__Moose.Include( 'Actions\\Act_Assign.lua' )
__Moose.Include( 'Actions\\Act_Route.lua' )
__Moose.Include( 'Actions\\Act_Account.lua' )
__Moose.Include( 'Actions\\Act_Assist.lua' )
__Moose.Include( 'Sound\\UserSound.lua' )
__Moose.Include( 'Sound\\SoundOutput.lua' )
__Moose.Include( 'Sound\\Radio.lua' )
__Moose.Include( 'Sound\\RadioQueue.lua' )
__Moose.Include( 'Sound\\RadioSpeech.lua' )
__Moose.Include( 'Sound\\SRS.lua' )
__Moose.Include( 'Tasking\\CommandCenter.lua' )
__Moose.Include( 'Tasking\\Mission.lua' )
__Moose.Include( 'Tasking\\Task.lua' )
__Moose.Include( 'Tasking\\TaskInfo.lua' )
__Moose.Include( 'Tasking\\Task_Manager.lua' )
__Moose.Include( 'Tasking\\DetectionManager.lua' )
__Moose.Include( 'Tasking\\Task_A2G_Dispatcher.lua' )
__Moose.Include( 'Tasking\\Task_A2G.lua' )
__Moose.Include( 'Tasking\\Task_A2A_Dispatcher.lua' )
__Moose.Include( 'Tasking\\Task_A2A.lua' )
__Moose.Include( 'Tasking\\Task_Cargo.lua' )
__Moose.Include( 'Tasking\\Task_Cargo_Transport.lua' )
__Moose.Include( 'Tasking\\Task_Cargo_CSAR.lua' )
__Moose.Include( 'Tasking\\Task_Cargo_Dispatcher.lua' )
__Moose.Include( 'Tasking\\Task_Capture_Zone.lua' )
__Moose.Include( 'Tasking\\Task_Capture_Dispatcher.lua' )
__Moose.Include( 'Globals.lua' )

File diff suppressed because it is too large Load Diff

View File

@@ -3075,8 +3075,7 @@ function AIRBOSS:EnableSRS(PathToSRS,Port,Culture,Gender,Voice,GoogleCreds,Volum
self.SRS:SetVolume(Volume or 1)
--self.SRS:SetModulations(Modulations)
if GoogleCreds then
self.SRS:SetProviderOptionsGoogle(GoogleCreds,GoogleCreds)
self.SRS:SetProvider(MSRS.Provider.GOOGLE)
self.SRS:SetGoogle(GoogleCreds)
end
if Voice then
self.SRS:SetVoice(Voice)
@@ -9755,7 +9754,7 @@ function AIRBOSS:_Groove( playerData )
local glideslopeError = groovedata.GSE
local AoA = groovedata.AoA
if rho <= RXX and playerData.step == AIRBOSS.PatternStep.GROOVE_XX and (math.abs( groovedata.Roll ) <= 4.0 and playerData.unit:IsInZone( self:_GetZoneLineup() )) then
if rho <= RXX and playerData.step == AIRBOSS.PatternStep.GROOVE_XX and (math.abs( groovedata.Roll ) <= 4.0 or playerData.unit:IsInZone( self:_GetZoneLineup() )) then
-- Start time in groove
playerData.TIG0 = timer.getTime()
@@ -12123,18 +12122,16 @@ function AIRBOSS:_LSOgrade( playerData )
local GIC, nIC = self:_Flightdata2Text( playerData, AIRBOSS.GroovePos.IC )
local GAR, nAR = self:_Flightdata2Text( playerData, AIRBOSS.GroovePos.AR )
-- VTOL approach, which is graded differently (currently only Harrier).
local vtol=playerData.actype==AIRBOSS.AircraftCarrier.AV8B
-- Put everything together.
local G = GXX .. " " .. GIM .. " " .. " " .. GIC .. " " .. GAR
-- Count number of minor/small nS, normal nN and major/large deviations nL.
-- Count number of minor, normal and major deviations.
local N=nXX+nIM+nIC+nAR
local Nv=nXX+nIM
local nL=count(G, '_')/2
local nS=count(G, '%(')
local nN=N-nS-nL
local nNv=Nv-nS-nL
-- Groove time 15-18.99 sec for a unicorn. Or 60-65 for V/STOL unicorn.
local Tgroove=playerData.Tgroove
@@ -12150,64 +12147,34 @@ function AIRBOSS:_LSOgrade( playerData )
G = "Unicorn"
else
if vtol then
-- Add AV-8B Harrier devation allowances due to lower groundspeed and 3x conventional groove time, this allows to maintain LSO tolerances while respecting the deviations are not unsafe.--Pene testing
-- Add AV-8B Harrier devation allowances due to lower groundspeed and 3x conventional groove time, this allows to maintain LSO tolerances while respecting the deviations are not unsafe.--Pene testing
-- Large devaitions still result in a No Grade, A Unicorn still requires a clean pass with no deviation.
if nL > 1 and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-- Larger deviations ==> "No grade" 2.0 points.
grade="--"
points=2.0
elseif nNv >= 1 and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-- Only average deviations ==> "Fair Pass" Pass with average deviations and corrections.
grade="(OK)"
points=3.0
elseif nNv < 1 and playerData.actype==AIRBOSS.AircraftCarrier.AV8B then
-- Only minor average deviations ==> "OK" Pass with minor deviations and corrections. (test nNv<=1 and)
grade="OK"
points=4.0
elseif nL > 0 then
-- Larger deviations ==> "No grade" 2.0 points.
grade="--"
points=2.0
elseif nN> 0 then
-- No larger but average deviations ==> "Fair Pass" Pass with average deviations and corrections.
grade="(OK)"
points=3.0
else
-- Only minor corrections
grade="OK"
points=4.0
end
-- Normal laning part at the beginning
local Gb = GXX .. " " .. GIM
-- Number of deviations that occurred at the the beginning of the landing (XX or IM). These are graded like in non-VTOL landings, i.e. on deviations is
local N=nXX+nIM
local nL=count(Gb, '_')/2
local nS=count(Gb, '%(')
local nN=N-nS-nL
-- VTOL part of the landing
local Gv = GIC .. " " .. GAR
-- Number of deviations that occurred at the the end (VTOL part) of the landing (IC or AR).
local Nv=nIC+nAR
local nLv=count(Gv, '_')/2
local nSv=count(Gv, '%(')
local nNv=Nv-nSv-nLv
if nL>0 or nLv>1 then
-- Larger deviations at XX or IM or at least one larger deviation IC or AR==> "No grade" 2.0 points.
-- In other words, we allow one larger deviation at IC+AR
grade="--"
points=2.0
elseif nN>0 or nNv>1 or nLv==1 then
-- Average deviations at XX+IM or more than one normal deviation IC or AR ==> "Fair Pass" Pass with average deviations and corrections.
grade="(OK)"
points=3.0
else
-- Only minor corrections
grade="OK"
points=4.0
end
else
-- This is a normal (non-VTOL) landing.
if nL > 0 then
-- Larger deviations ==> "No grade" 2.0 points.
grade="--"
points=2.0
elseif nN> 0 then
-- No larger but average/normal deviations ==> "Fair Pass" Pass with average deviations and corrections.
grade="(OK)"
points=3.0
else
-- Only minor corrections ==> "Okay pass" 4.0 points.
grade="OK"
points=4.0
end
end
end
-- Replace" )"( and "__"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,621 +0,0 @@
--- **Ops** - Brigade Warehouse.
--
-- **Main Features:**
--
-- * Manage platoons
-- * Carry out ARTY and PATROLZONE missions (AUFTRAG)
-- * Define rearming zones
--
-- ===
--
-- ## Example Missions:
--
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Ops/Brigade).
--
-- ===
--
-- ### Author: **funkyfranky**
--
-- ===
-- @module Ops.Brigade
-- @image OPS_Brigade_.png
--- BRIGADE class.
-- @type BRIGADE
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity of output.
-- @field #table rearmingZones Rearming zones. Each element is of type `#BRIGADE.SupplyZone`.
-- @field #table refuellingZones Refuelling zones. Each element is of type `#BRIGADE.SupplyZone`.
-- @field Core.Set#SET_ZONE retreatZones Retreat zone set.
-- @extends Ops.Legion#LEGION
--- *I am not afraid of an Army of lions lead by a sheep; I am afraid of sheep lead by a lion* -- Alexander the Great
--
-- ===
--
-- # The BRIGADE Concept
--
-- A BRIGADE consists of one or multiple PLATOONs. These platoons "live" in a WAREHOUSE that has a phyiscal struction (STATIC or UNIT) and can be captured or destroyed.
--
--
-- @field #BRIGADE
BRIGADE = {
ClassName = "BRIGADE",
verbose = 0,
rearmingZones = {},
refuellingZones = {},
}
--- Supply Zone.
-- @type BRIGADE.SupplyZone
-- @field Core.Zone#ZONE zone The zone.
-- @field Ops.Auftrag#AUFTRAG mission Mission assigned to supply ammo or fuel.
-- @field #boolean markerOn If `true`, marker is on.
-- @field Wrapper.Marker#MARKER marker F10 marker.
--- BRIGADE class version.
-- @field #string version
BRIGADE.version="0.1.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Spawn when hosting warehouse is a ship or oil rig or gas platform.
-- TODO: Rearming zones.
-- TODO: Retreat zones.
-- DONE: Add weapon range.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new BRIGADE class object.
-- @param #BRIGADE self
-- @param #string WarehouseName Name of the warehouse STATIC or UNIT object representing the warehouse.
-- @param #string BrigadeName Name of the brigade.
-- @return #BRIGADE self
function BRIGADE:New(WarehouseName, BrigadeName)
-- Inherit everything from LEGION class.
local self=BASE:Inherit(self, LEGION:New(WarehouseName, BrigadeName)) -- #BRIGADE
-- Nil check.
if not self then
BASE:E(string.format("ERROR: Could not find warehouse %s!", WarehouseName))
return nil
end
-- Set some string id for output to DCS.log file.
self.lid=string.format("BRIGADE %s | ", self.alias)
-- Defaults
self:SetRetreatZones()
-- Turn ship into NAVYGROUP.
if self:IsShip() then
local wh=self.warehouse --Wrapper.Unit#UNIT
local group=wh:GetGroup()
self.warehouseOpsGroup=NAVYGROUP:New(group) --Ops.NavyGroup#NAVYGROUP
self.warehouseOpsElement=self.warehouseOpsGroup:GetElementByName(wh:GetName())
end
-- Add FSM transitions.
-- From State --> Event --> To State
self:AddTransition("*", "ArmyOnMission", "*") -- An ARMYGROUP was send on a Mission (AUFTRAG).
------------------------
--- Pseudo Functions ---
------------------------
--- Triggers the FSM event "Start". Starts the BRIGADE. Initializes parameters and starts event handlers.
-- @function [parent=#BRIGADE] Start
-- @param #BRIGADE self
--- Triggers the FSM event "Start" after a delay. Starts the BRIGADE. Initializes parameters and starts event handlers.
-- @function [parent=#BRIGADE] __Start
-- @param #BRIGADE self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Stop". Stops the BRIGADE and all its event handlers.
-- @param #BRIGADE self
--- Triggers the FSM event "Stop" after a delay. Stops the BRIGADE and all its event handlers.
-- @function [parent=#BRIGADE] __Stop
-- @param #BRIGADE self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "ArmyOnMission".
-- @function [parent=#BRIGADE] ArmyOnMission
-- @param #BRIGADE self
-- @param Ops.ArmyGroup#ARMYGROUP ArmyGroup The ARMYGROUP on mission.
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
--- Triggers the FSM event "ArmyOnMission" after a delay.
-- @function [parent=#BRIGADE] __ArmyOnMission
-- @param #BRIGADE self
-- @param #number delay Delay in seconds.
-- @param Ops.ArmyGroup#ARMYGROUP ArmyGroup The ARMYGROUP on mission.
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
--- On after "ArmyOnMission" event.
-- @function [parent=#BRIGADE] OnAfterArmyOnMission
-- @param #BRIGADE self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.ArmyGroup#ARMYGROUP ArmyGroup The ARMYGROUP on mission.
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Add a platoon to the brigade.
-- @param #BRIGADE self
-- @param Ops.Platoon#PLATOON Platoon The platoon object.
-- @return #BRIGADE self
function BRIGADE:AddPlatoon(Platoon)
-- Add platoon to brigade.
table.insert(self.cohorts, Platoon)
-- Add assets to platoon.
self:AddAssetToPlatoon(Platoon, Platoon.Ngroups)
-- Set brigade of platoon.
Platoon:SetBrigade(self)
-- Start platoon.
if Platoon:IsStopped() then
Platoon:Start()
end
return self
end
--- Add asset group(s) to platoon.
-- @param #BRIGADE self
-- @param Ops.Platoon#PLATOON Platoon The platoon object.
-- @param #number Nassets Number of asset groups to add.
-- @return #BRIGADE self
function BRIGADE:AddAssetToPlatoon(Platoon, Nassets)
if Platoon then
-- Get the template group of the platoon.
local Group=GROUP:FindByName(Platoon.templatename)
if Group then
-- Debug text.
local text=string.format("Adding asset %s to platoon %s", Group:GetName(), Platoon.name)
self:T(self.lid..text)
-- Add assets to airwing warehouse.
self:AddAsset(Group, Nassets, nil, nil, nil, nil, Platoon.skill, Platoon.livery, Platoon.name)
else
self:E(self.lid.."ERROR: Group does not exist!")
end
else
self:E(self.lid.."ERROR: Platoon does not exit!")
end
return self
end
--- Define a set of retreat zones.
-- @param #BRIGADE self
-- @param Core.Set#SET_ZONE RetreatZoneSet Set of retreat zones.
-- @return #BRIGADE self
function BRIGADE:SetRetreatZones(RetreatZoneSet)
self.retreatZones=RetreatZoneSet or SET_ZONE:New()
return self
end
--- Add a retreat zone.
-- @param #BRIGADE self
-- @param Core.Zone#ZONE RetreatZone Retreat zone.
-- @return #BRIGADE self
function BRIGADE:AddRetreatZone(RetreatZone)
self.retreatZones:AddZone(RetreatZone)
return self
end
--- Get retreat zones.
-- @param #BRIGADE self
-- @return Core.Set#SET_ZONE Set of retreat zones.
function BRIGADE:GetRetreatZones()
return self.retreatZones
end
--- Add a rearming zone.
-- @param #BRIGADE self
-- @param Core.Zone#ZONE RearmingZone Rearming zone.
-- @return #BRIGADE.SupplyZone The rearming zone data.
function BRIGADE:AddRearmingZone(RearmingZone)
local rearmingzone={} --#BRIGADE.SupplyZone
rearmingzone.zone=RearmingZone
rearmingzone.mission=nil
rearmingzone.marker=MARKER:New(rearmingzone.zone:GetCoordinate(), "Rearming Zone"):ToCoalition(self:GetCoalition())
table.insert(self.rearmingZones, rearmingzone)
return rearmingzone
end
--- Add a refuelling zone.
-- @param #BRIGADE self
-- @param Core.Zone#ZONE RefuellingZone Refuelling zone.
-- @return #BRIGADE.SupplyZone The refuelling zone data.
function BRIGADE:AddRefuellingZone(RefuellingZone)
local supplyzone={} --#BRIGADE.SupplyZone
supplyzone.zone=RefuellingZone
supplyzone.mission=nil
supplyzone.marker=MARKER:New(supplyzone.zone:GetCoordinate(), "Refuelling Zone"):ToCoalition(self:GetCoalition())
table.insert(self.refuellingZones, supplyzone)
return supplyzone
end
--- Get platoon by name.
-- @param #BRIGADE self
-- @param #string PlatoonName Name of the platoon.
-- @return Ops.Platoon#PLATOON The Platoon object.
function BRIGADE:GetPlatoon(PlatoonName)
local platoon=self:_GetCohort(PlatoonName)
return platoon
end
--- Get platoon of an asset.
-- @param #BRIGADE self
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The platoon asset.
-- @return Ops.Platoon#PLATOON The platoon object.
function BRIGADE:GetPlatoonOfAsset(Asset)
local platoon=self:GetPlatoon(Asset.squadname)
return platoon
end
--- Remove asset from platoon.
-- @param #BRIGADE self
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The platoon asset.
function BRIGADE:RemoveAssetFromPlatoon(Asset)
local platoon=self:GetPlatoonOfAsset(Asset)
if platoon then
platoon:DelAsset(Asset)
end
end
--- [ GROUND ] Function to load back an asset in the field that has been filed before.
-- @param #BRIGADE self
-- @param #string Templatename e.g."1 PzDv LogRg I\_AID-976" - that's the alias (name) of an platoon spawned as `"platoon - alias"_AID-"asset-ID"`
-- @param Core.Point#COORDINATE Position where to spawn the platoon
-- @return #BRIGADE self
-- @usage
-- Prerequisites:
-- Save the assets spawned by BRIGADE/CHIEF regularly (~every 5 mins) into a file, e.g. like this:
--
-- local Path = FilePath or "C:\\Users\\<yourname>\\Saved Games\\DCS\\Missions\\" -- example path
-- local BlueOpsFilename = BlueFileName or "ExamplePlatoonSave.csv" -- example filename
-- local BlueSaveOps = SET_OPSGROUP:New():FilterCoalitions("blue"):FilterCategoryGround():FilterOnce()
-- UTILS.SaveSetOfOpsGroups(BlueSaveOps,Path,BlueOpsFilename)
--
-- where Path and Filename are strings, as chosen by you.
-- You can then load back the assets at the start of your next mission run. Be aware that it takes a couple of seconds for the
-- platoon data to arrive in brigade, so make this an action after ~20 seconds, e.g. like so:
--
-- function LoadBackAssets()
-- local Path = FilePath or "C:\\Users\\<yourname>\\Saved Games\\DCS\\Missions\\" -- example path
-- local BlueOpsFilename = BlueFileName or "ExamplePlatoonSave.csv" -- example filename
-- if UTILS.CheckFileExists(Path,BlueOpsFilename) then
-- local loadback = UTILS.LoadSetOfOpsGroups(Path,BlueOpsFilename,false)
-- for _,_platoondata in pairs (loadback) do
-- local groupname = _platoondata.groupname -- #string
-- local coordinate = _platoondata.coordinate -- Core.Point#COORDINATE
-- Your_Brigade:LoadBackAssetInPosition(groupname,coordinate)
-- end
-- end
-- end
--
-- local AssetLoader = TIMER:New(LoadBackAssets)
-- AssetLoader:Start(20)
--
-- The assets loaded back into the mission will be considered for AUFTRAG type missions from CHIEF and BRIGADE.
function BRIGADE:LoadBackAssetInPosition(Templatename,Position)
self:T(self.lid .. "LoadBackAssetInPosition: " .. tostring(Templatename))
-- get Platoon alias from Templatename
local nametbl = UTILS.Split(Templatename,"_")
local name = nametbl[1]
self:T(string.format("*** Target Platoon = %s ***",name))
-- find a matching asset table from BRIGADE
local cohorts = self.cohorts or {}
local thisasset = nil --Functional.Warehouse#WAREHOUSE.Assetitem
local found = false
for _,_cohort in pairs(cohorts) do
local asset = _cohort:GetName()
self:T(string.format("*** Looking at Platoon = %s ***",asset))
if asset == name then
self:T("**** Found Platoon ****")
local cohassets = _cohort.assets or {}
for _,_zug in pairs (cohassets) do
local zug = _zug -- Functional.Warehouse#WAREHOUSE.Assetitem
if zug.assignment == name and zug.requested == false then
self:T("**** Found Asset ****")
found = true
thisasset = zug --Functional.Warehouse#WAREHOUSE.Assetitem
break
end
end
end
end
if found then
-- prep asset
thisasset.rid = thisasset.uid
thisasset.requested = false
thisasset.score=100
thisasset.missionTask="CAS"
thisasset.spawned = true
local template = thisasset.templatename
local alias = thisasset.spawngroupname
-- Spawn group
local spawnasset = SPAWN:NewWithAlias(template,alias)
:InitDelayOff()
:SpawnFromCoordinate(Position)
-- build a new self request
local request = {} --Functional.Warehouse#WAREHOUSE.Pendingitem
request.assignment = name
request.warehouse = self
request.assets = {thisasset}
request.ntransporthome = 0
request.ndelivered = 0
request.ntransport = 0
request.cargoattribute = thisasset.attribute
request.category = thisasset.category
request.cargoassets = {thisasset}
request.assetdesc = WAREHOUSE.Descriptor.ASSETLIST
request.cargocategory = thisasset.category
request.toself = true
request.transporttype = WAREHOUSE.TransportType.SELFPROPELLED
request.assetproblem = {}
request.born = true
request.prio = 50
request.uid = thisasset.uid
request.airbase = nil
request.timestamp = timer.getAbsTime()
request.assetdescval = {thisasset}
request.nasset = 1
request.cargogroupset = SET_GROUP:New()
request.cargogroupset:AddGroup(spawnasset)
request.iscargo = true
-- Call Brigade self
self:__AssetSpawned(2, spawnasset, thisasset, request)
end
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Start BRIGADE FSM.
-- @param #BRIGADE self
function BRIGADE:onafterStart(From, Event, To)
-- Start parent Warehouse.
self:GetParent(self, BRIGADE).onafterStart(self, From, Event, To)
-- Info.
self:I(self.lid..string.format("Starting BRIGADE v%s", BRIGADE.version))
end
--- Update status.
-- @param #BRIGADE self
function BRIGADE:onafterStatus(From, Event, To)
-- Status of parent Warehouse.
self:GetParent(self).onafterStatus(self, From, Event, To)
-- FSM state.
local fsmstate=self:GetState()
----------------
-- Transport ---
----------------
self:CheckTransportQueue()
--------------
-- Mission ---
--------------
-- Check if any missions should be cancelled.
self:CheckMissionQueue()
---------------------
-- Rearming Zones ---
---------------------
for _,_rearmingzone in pairs(self.rearmingZones) do
local rearmingzone=_rearmingzone --#BRIGADE.SupplyZone
if (not rearmingzone.mission) or rearmingzone.mission:IsOver() then
rearmingzone.mission=AUFTRAG:NewAMMOSUPPLY(rearmingzone.zone)
self:AddMission(rearmingzone.mission)
end
end
-----------------------
-- Refuelling Zones ---
-----------------------
-- Check refuelling zones.
for _,_supplyzone in pairs(self.refuellingZones) do
local supplyzone=_supplyzone --#BRIGADE.SupplyZone
-- Check if mission is nil or over.
if (not supplyzone.mission) or supplyzone.mission:IsOver() then
supplyzone.mission=AUFTRAG:NewFUELSUPPLY(supplyzone.zone)
self:AddMission(supplyzone.mission)
end
end
-----------
-- Info ---
-----------
-- General info:
if self.verbose>=1 then
-- Count missions not over yet.
local Nmissions=self:CountMissionsInQueue()
-- Asset count.
local Npq, Np, Nq=self:CountAssetsOnMission()
-- Asset string.
local assets=string.format("%d [OnMission: Total=%d, Active=%d, Queued=%d]", self:CountAssets(), Npq, Np, Nq)
-- Output.
local text=string.format("%s: Missions=%d, Platoons=%d, Assets=%s", fsmstate, Nmissions, #self.cohorts, assets)
self:I(self.lid..text)
end
------------------
-- Mission Info --
------------------
if self.verbose>=2 then
local text=string.format("Missions Total=%d:", #self.missionqueue)
for i,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
local prio=string.format("%d/%s", mission.prio, tostring(mission.importance)) ; if mission.urgent then prio=prio.." (!)" end
local assets=string.format("%d/%d", mission:CountOpsGroups(), mission.Nassets or 0)
local target=string.format("%d/%d Damage=%.1f", mission:CountMissionTargets(), mission:GetTargetInitialNumber(), mission:GetTargetDamage())
text=text..string.format("\n[%d] %s %s: Status=%s, Prio=%s, Assets=%s, Targets=%s", i, mission.name, mission.type, mission.status, prio, assets, target)
end
self:I(self.lid..text)
end
--------------------
-- Transport Info --
--------------------
if self.verbose>=2 then
local text=string.format("Transports Total=%d:", #self.transportqueue)
for i,_transport in pairs(self.transportqueue) do
local transport=_transport --Ops.OpsTransport#OPSTRANSPORT
local prio=string.format("%d/%s", transport.prio, tostring(transport.importance)) ; if transport.urgent then prio=prio.." (!)" end
local carriers=string.format("Ncargo=%d/%d, Ncarriers=%d", transport.Ncargo, transport.Ndelivered, transport.Ncarrier)
text=text..string.format("\n[%d] UID=%d: Status=%s, Prio=%s, Cargo: %s", i, transport.uid, transport:GetState(), prio, carriers)
end
self:I(self.lid..text)
end
-------------------
-- Platoon Info --
-------------------
if self.verbose>=3 then
local text="Platoons:"
for i,_platoon in pairs(self.cohorts) do
local platoon=_platoon --Ops.Platoon#PLATOON
local callsign=platoon.callsignName and UTILS.GetCallsignName(platoon.callsignName) or "N/A"
local modex=platoon.modex and platoon.modex or -1
local skill=platoon.skill and tostring(platoon.skill) or "N/A"
-- Platoon text.
text=text..string.format("\n* %s %s: %s*%d/%d, Callsign=%s, Modex=%d, Skill=%s", platoon.name, platoon:GetState(), platoon.aircrafttype, platoon:CountAssets(true), #platoon.assets, callsign, modex, skill)
end
self:I(self.lid..text)
end
-------------------
-- Rearming Info --
-------------------
if self.verbose>=4 then
local text="Rearming Zones:"
for i,_rearmingzone in pairs(self.rearmingZones) do
local rearmingzone=_rearmingzone --#BRIGADE.SupplyZone
-- Info text.
text=text..string.format("\n* %s: Mission status=%s, suppliers=%d", rearmingzone.zone:GetName(), rearmingzone.mission:GetState(), rearmingzone.mission:CountOpsGroups())
end
self:I(self.lid..text)
end
---------------------
-- Refuelling Info --
---------------------
if self.verbose>=4 then
local text="Refuelling Zones:"
for i,_refuellingzone in pairs(self.refuellingZones) do
local refuellingzone=_refuellingzone --#BRIGADE.SupplyZone
-- Info text.
text=text..string.format("\n* %s: Mission status=%s, suppliers=%d", refuellingzone.zone:GetName(), refuellingzone.mission:GetState(), refuellingzone.mission:CountOpsGroups())
end
self:I(self.lid..text)
end
----------------
-- Asset Info --
----------------
if self.verbose>=5 then
local text="Assets in stock:"
for i,_asset in pairs(self.stock) do
local asset=_asset --Functional.Warehouse#WAREHOUSE.Assetitem
-- Info text.
text=text..string.format("\n* %s: spawned=%s", asset.spawngroupname, tostring(asset.spawned))
end
self:I(self.lid..text)
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- On after "ArmyOnMission".
-- @param #BRIGADE self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.ArmyGroup#ARMYGROUP ArmyGroup Ops army group on mission.
-- @param Ops.Auftrag#AUFTRAG Mission The requested mission.
function BRIGADE:onafterArmyOnMission(From, Event, To, ArmyGroup, Mission)
-- Debug info.
self:T(self.lid..string.format("Group %s on %s mission %s", ArmyGroup:GetName(), Mission:GetType(), Mission:GetName()))
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

@@ -290,11 +290,10 @@ CSAR.AircraftType["Bell-47"] = 2
CSAR.AircraftType["UH-60L"] = 10
CSAR.AircraftType["AH-64D_BLK_II"] = 2
CSAR.AircraftType["Bronco-OV-10A"] = 2
CSAR.AircraftType["MH-60R"] = 10
--- CSAR class version.
-- @field #string version
CSAR.version="1.0.20"
CSAR.version="1.0.19"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list

View File

@@ -24,7 +24,7 @@
-- @module Ops.CTLD
-- @image OPS_CTLD.jpg
-- Last Update March 2024
-- Last Update December 2023
do
@@ -44,8 +44,6 @@ do
-- @field #number PerCrateMass Mass in kg.
-- @field #number Stock Number of builds available, -1 for unlimited.
-- @field #string Subcategory Sub-category name.
-- @field #boolean DontShowInMenu Show this item in menu or not.
-- @field Core.Zone#ZONE Location Location (if set) where to get this cargo item.
-- @extends Core.Base#BASE
---
@@ -64,8 +62,6 @@ CTLD_CARGO = {
PerCrateMass = 0,
Stock = nil,
Mark = nil,
DontShowInMenu = false,
Location = nil,
}
--- Define cargo types.
@@ -101,10 +97,8 @@ CTLD_CARGO = {
-- @param #number PerCrateMass Mass in kg
-- @param #number Stock Number of builds available, nil for unlimited
-- @param #string Subcategory Name of subcategory, handy if using > 10 types to load.
-- @param #boolean DontShowInMenu Show this item in menu or not (default: false == show it).
-- @param Core.Zone#ZONE Location (optional) Where the cargo is available (one location only).
-- @return #CTLD_CARGO self
function CTLD_CARGO:New(ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped, PerCrateMass, Stock, Subcategory, DontShowInMenu, Location)
function CTLD_CARGO:New(ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped, PerCrateMass, Stock, Subcategory)
-- Inherit everything from BASE class.
local self=BASE:Inherit(self, BASE:New()) -- #CTLD_CARGO
self:T({ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped})
@@ -121,20 +115,8 @@ CTLD_CARGO = {
self.Stock = Stock or nil --#number
self.Mark = nil
self.Subcategory = Subcategory or "Other"
self.DontShowInMenu = DontShowInMenu or false
if type(Location) == "string" then
Location = ZONE:New(Location)
end
self.Location = Location
return self
end
--- Query Location.
-- @param #CTLD_CARGO self
-- @return Core.Zone#ZONE location or `nil` if not set
function CTLD_CARGO:GetLocation()
return self.Location
end
--- Query ID.
-- @param #CTLD_CARGO self
@@ -674,8 +656,6 @@ do
-- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775)
-- -- if you want to limit your stock, add a number (here: 10) as parameter after weight. No parameter / nil means unlimited stock.
-- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775,10)
-- -- additionally, you can limit **where** the stock is available (one location only!) - this one is available in a zone called "Vehicle Store".
-- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775,10,nil,nil,"Vehicle Store")
--
-- -- add infantry unit called "Forward Ops Base" using template "FOB", of type FOB, size 4, i.e. needs four crates to be build:
-- my_ctld:AddCratesCargo("Forward Ops Base",{"FOB"},CTLD_CARGO.Enum.FOB,4)
@@ -777,9 +757,6 @@ do
-- ["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25, cargoweightlimit = 19000},
-- ["UH-60L"] = {type="UH-60L", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500},
-- ["AH-64D_BLK_II"] = {type="AH-64D_BLK_II", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 17, cargoweightlimit = 200},
-- ["MH-60R"] = {type="MH-60R", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
-- ["SH-60B"] = {type="SH-60B", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
-- ["Bronco-OV-10A"] = {type="Bronco-OV-10A", crates= false, troops=true, cratelimit = 0, trooplimit = 5, length = 13, cargoweightlimit = 1450},
--
-- ### 2.1.2 Activate and deactivate zones
--
@@ -1245,15 +1222,13 @@ CTLD.UnitTypeCapabilities = {
["Hercules"] = {type="Hercules", crates=true, troops=true, cratelimit = 7, trooplimit = 64, length = 25, cargoweightlimit = 19000}, -- 19t cargo, 64 paratroopers.
--Actually it's longer, but the center coord is off-center of the model.
["UH-60L"] = {type="UH-60L", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
["MH-60R"] = {type="MH-60R", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
["SH-60B"] = {type="SH-60B", crates=true, troops=true, cratelimit = 2, trooplimit = 20, length = 16, cargoweightlimit = 3500}, -- 4t cargo, 20 (unsec) seats
["AH-64D_BLK_II"] = {type="AH-64D_BLK_II", crates=false, troops=true, cratelimit = 0, trooplimit = 2, length = 17, cargoweightlimit = 200}, -- 2 ppl **outside** the helo
["Bronco-OV-10A"] = {type="Bronco-OV-10A", crates= false, troops=true, cratelimit = 0, trooplimit = 5, length = 13, cargoweightlimit = 1450},
}
--- CTLD class version.
-- @field #string version
CTLD.version="1.0.50"
CTLD.version="1.0.45"
--- Instantiate a new CTLD.
-- @param #CTLD self
@@ -1458,7 +1433,7 @@ function CTLD:New(Coalition, Prefixes, Alias)
--- Pseudo Functions ---
------------------------
--- Triggers the FSM event "Start". Starts the CTLD. Initializes parameters and starts event handlers.
--- Triggers the FSM event "Start". Starts the CTLD. Initializes parameters and starts event handlers.
-- @function [parent=#CTLD] Start
-- @param #CTLD self
@@ -2260,7 +2235,7 @@ end
local secondarygroups = {}
for i=1,#distancekeys do
local nearestGroup = nearestList[distancekeys[i]] -- Wrapper.Group#GROUP
local nearestGroup = nearestList[distancekeys[i]]
-- find matching cargo type
local groupType = string.match(nearestGroup:GetName(), "(.+)-(.+)$")
local Cargotype = nil
@@ -2296,31 +2271,20 @@ end
self.CargoCounter = self.CargoCounter + 1
local loadcargotype = CTLD_CARGO:New(self.CargoCounter, Cargotype.Name, Cargotype.Templates, Cargotype.CargoType, true, true, Cargotype.CratesNeeded,nil,nil,Cargotype.PerCrateMass)
self:T({cargotype=loadcargotype})
local running = math.floor(nearestDistance / 4)+10 -- time run to helo plus boarding
loaded.Troopsloaded = loaded.Troopsloaded + troopsize
table.insert(loaded.Cargo,loadcargotype)
self.Loaded_Cargo[unitname] = loaded
self:ScheduleOnce(running,self._SendMessage,self,"Troops boarded!", 10, false, Group)
self:_SendMessage("Troops boarding!", 10, false, Group)
self:_SendMessage("Troops boarded!", 10, false, Group)
self:_UpdateUnitCargoMass(Unit)
self:__TroopsExtracted(running,Group, Unit, nearestGroup)
local coord = Unit:GetCoordinate() or Group:GetCoordinate() -- Core.Point#COORDINATE
local Point
if coord then
local heading = unit:GetHeading() or 0
local Angle = math.floor((heading+160)%360)
Point = coord:Translate(8,Angle):GetVec2()
if Point then
nearestGroup:RouteToVec2(Point,4)
end
end
self:__TroopsExtracted(1,Group, Unit, nearestGroup)
-- clean up:
if type(Cargotype.Templates) == "table" and Cargotype.Templates[2] then
for _,_key in pairs (Cargotype.Templates) do
table.insert(secondarygroups,_key)
end
end
nearestGroup:Destroy(false,running)
nearestGroup:Destroy(false)
end
end
end
@@ -2330,7 +2294,7 @@ end
if _group and _group:IsAlive() then
local groupname = string.match(_group:GetName(), "(.+)-(.+)$")
if _name == groupname then
_group:Destroy(false,15)
_group:Destroy(false)
end
end
end
@@ -2386,21 +2350,7 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop, pack)
self:_SendMessage("You are not close enough to a logistics zone!", 10, false, Group)
if not self.debug then return self end
end
-- Check cargo location if available
local location = Cargo:GetLocation()
if location then
local unitcoord = Unit:GetCoordinate() or Group:GetCoordinate()
if unitcoord then
if not location:IsCoordinateInZone(unitcoord) then
-- no we're not at the right spot
self:_SendMessage("The requested cargo is not available in this zone!", 10, false, Group)
if not self.debug then return self end
end
end
end
-- avoid crate spam
local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitTypeCapabilities
local canloadcratesno = capabilities.cratelimit
@@ -3074,10 +3024,9 @@ end
function CTLD:_GetUnitPositions(Coordinate,Radius,Heading,Template)
local Positions = {}
local template = _DATABASE:GetGroupTemplate(Template)
--UTILS.PrintTableToLog(template)
UTILS.PrintTableToLog(template)
local numbertroops = #template.units
local slightshift = math.abs(math.random(0,200)/100)
local newcenter = Coordinate:Translate(Radius+slightshift,((Heading+270)%360))
local newcenter = Coordinate:Translate(Radius,((Heading+270)%360))
for i=1,360,math.floor(360/numbertroops) do
local phead = ((Heading+270+i)%360)
local post = newcenter:Translate(Radius,phead)
@@ -3089,7 +3038,7 @@ function CTLD:_GetUnitPositions(Coordinate,Radius,Heading,Template)
}
table.insert(Positions,p1t)
end
--UTILS.PrintTableToLog(Positions)
UTILS.PrintTableToLog(Positions)
return Positions
end
@@ -3751,20 +3700,14 @@ function CTLD:_RefreshF10Menus()
for _,_entry in pairs(self.Cargo_Troops) do
local entry = _entry -- #CTLD_CARGO
local subcat = entry.Subcategory
local noshow = entry.DontShowInMenu
if not noshow then
menucount = menucount + 1
menus[menucount] = MENU_GROUP_COMMAND:New(_group,entry.Name,subcatmenus[subcat],self._LoadTroops, self, _group, _unit, entry)
end
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
local entry = _entry -- #CTLD_CARGO
local noshow = entry.DontShowInMenu
if not noshow then
menucount = menucount + 1
menus[menucount] = MENU_GROUP_COMMAND:New(_group,entry.Name,troopsmenu,self._LoadTroops, self, _group, _unit, entry)
end
menucount = menucount + 1
menus[menucount] = MENU_GROUP_COMMAND:New(_group,entry.Name,troopsmenu,self._LoadTroops, self, _group, _unit, entry)
end
end
local unloadmenu1 = MENU_GROUP_COMMAND:New(_group,"Drop troops",toptroops, self._UnloadTroops, self, _group, _unit):Refresh()
@@ -3785,61 +3728,33 @@ function CTLD:_RefreshF10Menus()
for _,_entry in pairs(self.Cargo_Crates) do
local entry = _entry -- #CTLD_CARGO
local subcat = entry.Subcategory
local noshow = entry.DontShowInMenu
local zone = entry.Location
if not noshow then
menucount = menucount + 1
local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
if zone then
menutext = string.format("Crate %s (%dkg)[R]",entry.Name,entry.PerCrateMass or 0)
end
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates, self, _group, _unit, entry)
end
menucount = menucount + 1
local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates, self, _group, _unit, entry)
end
for _,_entry in pairs(self.Cargo_Statics) do
local entry = _entry -- #CTLD_CARGO
local subcat = entry.Subcategory
local noshow = entry.DontShowInMenu
local zone = entry.Location
if not noshow then
menucount = menucount + 1
local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
if zone then
menutext = string.format("Crate %s (%dkg)[R]",entry.Name,entry.PerCrateMass or 0)
end
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates, self, _group, _unit, entry)
end
menucount = menucount + 1
local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates, self, _group, _unit, entry)
end
else
for _,_entry in pairs(self.Cargo_Crates) do
local entry = _entry -- #CTLD_CARGO
local noshow = entry.DontShowInMenu
local zone = entry.Location
if not noshow then
menucount = menucount + 1
local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
if zone then
menutext = string.format("Crate %s (%dkg)[R]",entry.Name,entry.PerCrateMass or 0)
end
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry)
end
menucount = menucount + 1
local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry)
end
for _,_entry in pairs(self.Cargo_Statics) do
local entry = _entry -- #CTLD_CARGO
local noshow = entry.DontShowInMenu
local zone = entry.Location
if not noshow then
menucount = menucount + 1
local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
if zone then
menutext = string.format("Crate %s (%dkg)[R]",entry.Name,entry.PerCrateMass or 0)
end
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry)
end
menucount = menucount + 1
local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry)
end
end
listmenu = MENU_GROUP_COMMAND:New(_group,"List crates nearby",topcrates, self._ListCratesNearby, self, _group, _unit)
local removecrates = MENU_GROUP_COMMAND:New(_group,"Remove crates nearby",removecratesmenu, self._RemoveCratesNearby, self, _group, _unit)
removecrates = MENU_GROUP_COMMAND:New(_group,"Remove crates nearby",removecratesmenu, self._RemoveCratesNearby, self, _group, _unit)
local unloadmenu = MENU_GROUP_COMMAND:New(_group,"Drop crates",topcrates, self._UnloadCrates, self, _group, _unit)
if not self.nobuildmenu then
local buildmenu = MENU_GROUP_COMMAND:New(_group,"Build crates",topcrates, self._BuildCrates, self, _group, _unit)
@@ -3914,9 +3829,7 @@ end
-- @param #number PerCrateMass Mass in kg of each crate
-- @param #number Stock Number of buildable groups in stock. Nil for unlimited.
-- @param #string SubCategory Name of sub-category (optional).
-- @param #boolean DontShowInMenu (optional) If set to "true" this won't show up in the menu.
-- @param Core.Zone#ZONE Location (optional) If set, the cargo item is **only** available here. Can be a #ZONE object or the name of a zone as #string.
function CTLD:AddCratesCargo(Name,Templates,Type,NoCrates,PerCrateMass,Stock,SubCategory,DontShowInMenu,Location)
function CTLD:AddCratesCargo(Name,Templates,Type,NoCrates,PerCrateMass,Stock,SubCategory)
self:T(self.lid .. " AddCratesCargo")
if not self:_CheckTemplates(Templates) then
self:E(self.lid .. "Crates Cargo for " .. Name .. " has missing template(s)!" )
@@ -3924,7 +3837,7 @@ function CTLD:AddCratesCargo(Name,Templates,Type,NoCrates,PerCrateMass,Stock,Sub
end
self.CargoCounter = self.CargoCounter + 1
-- Crates are not directly loadable
local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Templates,Type,false,false,NoCrates,nil,nil,PerCrateMass,Stock,SubCategory,DontShowInMenu,Location)
local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Templates,Type,false,false,NoCrates,nil,nil,PerCrateMass,Stock,SubCategory)
table.insert(self.Cargo_Crates,cargo)
return self
end
@@ -3935,15 +3848,13 @@ end
-- @param #number Mass Mass in kg of each static in kg, e.g. 100.
-- @param #number Stock Number of groups in stock. Nil for unlimited.
-- @param #string SubCategory Name of sub-category (optional).
-- @param #boolean DontShowInMenu (optional) If set to "true" this won't show up in the menu.
-- @param Core.Zone#ZONE Location (optional) If set, the cargo item is **only** available here. Can be a #ZONE object or the name of a zone as #string.
function CTLD:AddStaticsCargo(Name,Mass,Stock,SubCategory,DontShowInMenu,Location)
function CTLD:AddStaticsCargo(Name,Mass,Stock,SubCategory)
self:T(self.lid .. " AddStaticsCargo")
self.CargoCounter = self.CargoCounter + 1
local type = CTLD_CARGO.Enum.STATIC
local template = STATIC:FindByName(Name,true):GetTypeName()
-- Crates are not directly loadable
local cargo = CTLD_CARGO:New(self.CargoCounter,Name,template,type,false,false,1,nil,nil,Mass,Stock,SubCategory,DontShowInMenu,Location)
local cargo = CTLD_CARGO:New(self.CargoCounter,Name,template,type,false,false,1,nil,nil,Mass,Stock,SubCategory)
table.insert(self.Cargo_Statics,cargo)
return self
end
@@ -3973,9 +3884,7 @@ end
-- @param #number PerCrateMass Mass in kg of each crate
-- @param #number Stock Number of groups in stock. Nil for unlimited.
-- @param #string SubCategory Name of the sub-category (optional).
-- @param #boolean DontShowInMenu (optional) If set to "true" this won't show up in the menu.
-- @param Core.Zone#ZONE Location (optional) If set, the cargo item is **only** available here. Can be a #ZONE object or the name of a zone as #string.
function CTLD:AddCratesRepair(Name,Template,Type,NoCrates, PerCrateMass,Stock,SubCategory,DontShowInMenu,Location)
function CTLD:AddCratesRepair(Name,Template,Type,NoCrates, PerCrateMass,Stock,SubCategory)
self:T(self.lid .. " AddCratesRepair")
if not self:_CheckTemplates(Template) then
self:E(self.lid .. "Repair Cargo for " .. Name .. " has a missing template!" )
@@ -3983,7 +3892,7 @@ function CTLD:AddCratesRepair(Name,Template,Type,NoCrates, PerCrateMass,Stock,Su
end
self.CargoCounter = self.CargoCounter + 1
-- Crates are not directly loadable
local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Template,Type,false,false,NoCrates,nil,nil,PerCrateMass,Stock,SubCategory,DontShowInMenu,Location)
local cargo = CTLD_CARGO:New(self.CargoCounter,Name,Template,Type,false,false,NoCrates,nil,nil,PerCrateMass,Stock,SubCategory)
table.insert(self.Cargo_Crates,cargo)
return self
end
@@ -5448,19 +5357,19 @@ end
return self
end
--- (Internal) FSM Function onbeforeTroopsExtracted.
-- @param #CTLD self
-- @param #string From State.
-- @param #string Event Trigger.
-- @param #string To State.
-- @param Wrapper.Group#GROUP Group Group Object.
-- @param Wrapper.Unit#UNIT Unit Unit Object.
-- @param Wrapper.Group#GROUP Troops Troops #GROUP Object.
-- @return #CTLD self
function CTLD:onbeforeTroopsExtracted(From, Event, To, Group, Unit, Troops)
self:T({From, Event, To})
return self
end
--- (Internal) FSM Function onbeforeTroopsExtracted.
-- @param #CTLD self
-- @param #string From State.
-- @param #string Event Trigger.
-- @param #string To State.
-- @param Wrapper.Group#GROUP Group Group Object.
-- @param Wrapper.Unit#UNIT Unit Unit Object.
-- @param Wrapper.Group#GROUP Troops Troops #GROUP Object.
-- @return #CTLD self
function CTLD:onbeforeTroopsExtracted(From, Event, To, Group, Unit, Troops)
self:T({From, Event, To})
return self
end
--- (Internal) FSM Function onbeforeTroopsDeployed.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,425 +0,0 @@
--- **Ops** - Fleet Warehouse.
--
-- **Main Features:**
--
-- * Manage flotillas
-- * Carry out ARTY and PATROLZONE missions (AUFTRAG)
--
-- ===
--
-- ## Example Missions:
--
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Ops/Fleet).
--
-- ===
--
-- ### Author: **funkyfranky**
--
-- ===
-- @module Ops.Fleet
-- @image OPS_Fleet.png
--- FLEET class.
-- @type FLEET
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity of output.
-- @field Core.Set#SET_ZONE retreatZones Retreat zone set.
-- @field #boolean pathfinding Set pathfinding on for all spawned navy groups.
-- @extends Ops.Legion#LEGION
--- *A fleet of British ships at war are the best negotiators.* -- Horatio Nelson
--
-- ===
--
-- # The FLEET Concept
--
-- A FLEET consists of one or multiple FLOTILLAs. These flotillas "live" in a WAREHOUSE that has a phyiscal struction (STATIC or UNIT) and can be captured or destroyed.
--
-- # Basic Setup
--
-- A new `FLEET` object can be created with the @{#FLEET.New}(`WarehouseName`, `FleetName`) function, where `WarehouseName` is the name of the static or unit object hosting the fleet
-- and `FleetName` is the name you want to give the fleet. This must be *unique*!
--
-- myFleet=FLEET:New("myWarehouseName", "1st Fleet")
-- myFleet:SetPortZone(ZonePort1stFleet)
-- myFleet:Start()
--
-- A fleet needs a *port zone*, which is set via the @{#FLEET.SetPortZone}(`PortZone`) function. This is the zone where the naval assets are spawned and return to.
--
-- Finally, the fleet needs to be started using the @{#FLEET.Start}() function. If the fleet is not started, it will not process any requests.
--
-- ## Adding Flotillas
--
-- Flotillas can be added via the @{#FLEET.AddFlotilla}(`Flotilla`) function. See @{Ops.Flotilla#FLOTILLA} for how to create a flotilla.
--
-- myFleet:AddFlotilla(FlotillaTiconderoga)
-- myFleet:AddFlotilla(FlotillaPerry)
--
--
--
-- @field #FLEET
FLEET = {
ClassName = "FLEET",
verbose = 0,
pathfinding = false,
}
--- Supply Zone.
-- @type FLEET.SupplyZone
-- @field Core.Zone#ZONE zone The zone.
-- @field Ops.Auftrag#AUFTRAG mission Mission assigned to supply ammo or fuel.
-- @field #boolean markerOn If `true`, marker is on.
-- @field Wrapper.Marker#MARKER marker F10 marker.
--- FLEET class version.
-- @field #string version
FLEET.version="0.0.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Add routes?
-- DONE: Add weapon range.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new FLEET class object.
-- @param #FLEET self
-- @param #string WarehouseName Name of the warehouse STATIC or UNIT object representing the warehouse.
-- @param #string FleetName Name of the fleet.
-- @return #FLEET self
function FLEET:New(WarehouseName, FleetName)
-- Inherit everything from LEGION class.
local self=BASE:Inherit(self, LEGION:New(WarehouseName, FleetName)) -- #FLEET
-- Nil check.
if not self then
BASE:E(string.format("ERROR: Could not find warehouse %s!", WarehouseName))
return nil
end
-- Set some string id for output to DCS.log file.
self.lid=string.format("FLEET %s | ", self.alias)
-- Defaults
self:SetRetreatZones()
-- Turn ship into NAVYGROUP.
if self:IsShip() then
local wh=self.warehouse --Wrapper.Unit#UNIT
local group=wh:GetGroup()
self.warehouseOpsGroup=NAVYGROUP:New(group) --Ops.NavyGroup#NAVYGROUP
self.warehouseOpsElement=self.warehouseOpsGroup:GetElementByName(wh:GetName())
end
-- Add FSM transitions.
-- From State --> Event --> To State
self:AddTransition("*", "NavyOnMission", "*") -- An NAVYGROUP was send on a Mission (AUFTRAG).
------------------------
--- Pseudo Functions ---
------------------------
--- Triggers the FSM event "Start". Starts the FLEET. Initializes parameters and starts event handlers.
-- @function [parent=#FLEET] Start
-- @param #FLEET self
--- Triggers the FSM event "Start" after a delay. Starts the FLEET. Initializes parameters and starts event handlers.
-- @function [parent=#FLEET] __Start
-- @param #FLEET self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Stop". Stops the FLEET and all its event handlers.
-- @param #FLEET self
--- Triggers the FSM event "Stop" after a delay. Stops the FLEET and all its event handlers.
-- @function [parent=#FLEET] __Stop
-- @param #FLEET self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "NavyOnMission".
-- @function [parent=#FLEET] NavyOnMission
-- @param #FLEET self
-- @param Ops.NavyGroup#NAVYGROUP ArmyGroup The NAVYGROUP on mission.
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
--- Triggers the FSM event "NavyOnMission" after a delay.
-- @function [parent=#FLEET] __NavyOnMission
-- @param #FLEET self
-- @param #number delay Delay in seconds.
-- @param Ops.NavyGroup#NAVYGROUP ArmyGroup The NAVYGROUP on mission.
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
--- On after "NavyOnMission" event.
-- @function [parent=#FLEET] OnAfterNavyOnMission
-- @param #FLEET self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.NavyGroup#NAVYGROUP NavyGroup The NAVYGROUP on mission.
-- @param Ops.Auftrag#AUFTRAG Mission The mission.
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Add a flotilla to the fleet.
-- @param #FLEET self
-- @param Ops.Flotilla#FLOTILLA Flotilla The flotilla object.
-- @return #FLEET self
function FLEET:AddFlotilla(Flotilla)
-- Add flotilla to fleet.
table.insert(self.cohorts, Flotilla)
-- Add assets to flotilla.
self:AddAssetToFlotilla(Flotilla, Flotilla.Ngroups)
-- Set fleet of flotilla.
Flotilla:SetFleet(self)
-- Start flotilla.
if Flotilla:IsStopped() then
Flotilla:Start()
end
return self
end
--- Add asset group(s) to flotilla.
-- @param #FLEET self
-- @param Ops.Flotilla#FLOTILLA Flotilla The flotilla object.
-- @param #number Nassets Number of asset groups to add.
-- @return #FLEET self
function FLEET:AddAssetToFlotilla(Flotilla, Nassets)
if Flotilla then
-- Get the template group of the flotilla.
local Group=GROUP:FindByName(Flotilla.templatename)
if Group then
-- Debug text.
local text=string.format("Adding asset %s to flotilla %s", Group:GetName(), Flotilla.name)
self:T(self.lid..text)
-- Add assets to airwing warehouse.
self:AddAsset(Group, Nassets, nil, nil, nil, nil, Flotilla.skill, Flotilla.livery, Flotilla.name)
else
self:E(self.lid.."ERROR: Group does not exist!")
end
else
self:E(self.lid.."ERROR: Flotilla does not exit!")
end
return self
end
--- Set pathfinding for all spawned naval groups.
-- @param #FLEET self
-- @param #boolean Switch If `true`, pathfinding is used.
-- @return #FLEET self
function FLEET:SetPathfinding(Switch)
self.pathfinding=Switch
return self
end
--- Define a set of retreat zones.
-- @param #FLEET self
-- @param Core.Set#SET_ZONE RetreatZoneSet Set of retreat zones.
-- @return #FLEET self
function FLEET:SetRetreatZones(RetreatZoneSet)
self.retreatZones=RetreatZoneSet or SET_ZONE:New()
return self
end
--- Add a retreat zone.
-- @param #FLEET self
-- @param Core.Zone#ZONE RetreatZone Retreat zone.
-- @return #FLEET self
function FLEET:AddRetreatZone(RetreatZone)
self.retreatZones:AddZone(RetreatZone)
return self
end
--- Get retreat zones.
-- @param #FLEET self
-- @return Core.Set#SET_ZONE Set of retreat zones.
function FLEET:GetRetreatZones()
return self.retreatZones
end
--- Get flotilla by name.
-- @param #FLEET self
-- @param #string FlotillaName Name of the flotilla.
-- @return Ops.Flotilla#FLOTILLA The Flotilla object.
function FLEET:GetFlotilla(FlotillaName)
local flotilla=self:_GetCohort(FlotillaName)
return flotilla
end
--- Get flotilla of an asset.
-- @param #FLEET self
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The flotilla asset.
-- @return Ops.Flotilla#FLOTILLA The flotilla object.
function FLEET:GetFlotillaOfAsset(Asset)
local flotilla=self:GetFlotilla(Asset.squadname)
return flotilla
end
--- Remove asset from flotilla.
-- @param #FLEET self
-- @param Functional.Warehouse#WAREHOUSE.Assetitem Asset The flotilla asset.
function FLEET:RemoveAssetFromFlotilla(Asset)
local flotilla=self:GetFlotillaOfAsset(Asset)
if flotilla then
flotilla:DelAsset(Asset)
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Start FLEET FSM.
-- @param #FLEET self
function FLEET:onafterStart(From, Event, To)
-- Start parent Warehouse.
self:GetParent(self, FLEET).onafterStart(self, From, Event, To)
-- Info.
self:I(self.lid..string.format("Starting FLEET v%s", FLEET.version))
end
--- Update status.
-- @param #FLEET self
function FLEET:onafterStatus(From, Event, To)
-- Status of parent Warehouse.
self:GetParent(self).onafterStatus(self, From, Event, To)
-- FSM state.
local fsmstate=self:GetState()
----------------
-- Transport ---
----------------
self:CheckTransportQueue()
--------------
-- Mission ---
--------------
-- Check if any missions should be cancelled.
self:CheckMissionQueue()
-----------
-- Info ---
-----------
-- General info:
if self.verbose>=1 then
-- Count missions not over yet.
local Nmissions=self:CountMissionsInQueue()
-- Asset count.
local Npq, Np, Nq=self:CountAssetsOnMission()
-- Asset string.
local assets=string.format("%d [OnMission: Total=%d, Active=%d, Queued=%d]", self:CountAssets(), Npq, Np, Nq)
-- Output.
local text=string.format("%s: Missions=%d, Flotillas=%d, Assets=%s", fsmstate, Nmissions, #self.cohorts, assets)
self:I(self.lid..text)
end
------------------
-- Mission Info --
------------------
if self.verbose>=2 then
local text=string.format("Missions Total=%d:", #self.missionqueue)
for i,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG
local prio=string.format("%d/%s", mission.prio, tostring(mission.importance)) ; if mission.urgent then prio=prio.." (!)" end
local assets=string.format("%d/%d", mission:CountOpsGroups(), mission.Nassets or 0)
local target=string.format("%d/%d Damage=%.1f", mission:CountMissionTargets(), mission:GetTargetInitialNumber(), mission:GetTargetDamage())
text=text..string.format("\n[%d] %s %s: Status=%s, Prio=%s, Assets=%s, Targets=%s", i, mission.name, mission.type, mission.status, prio, assets, target)
end
self:I(self.lid..text)
end
--------------------
-- Transport Info --
--------------------
if self.verbose>=2 then
local text=string.format("Transports Total=%d:", #self.transportqueue)
for i,_transport in pairs(self.transportqueue) do
local transport=_transport --Ops.OpsTransport#OPSTRANSPORT
local prio=string.format("%d/%s", transport.prio, tostring(transport.importance)) ; if transport.urgent then prio=prio.." (!)" end
local carriers=string.format("Ncargo=%d/%d, Ncarriers=%d", transport.Ncargo, transport.Ndelivered, transport.Ncarrier)
text=text..string.format("\n[%d] UID=%d: Status=%s, Prio=%s, Cargo: %s", i, transport.uid, transport:GetState(), prio, carriers)
end
self:I(self.lid..text)
end
-------------------
-- Flotilla Info --
-------------------
if self.verbose>=3 then
local text="Flotillas:"
for i,_flotilla in pairs(self.cohorts) do
local flotilla=_flotilla --Ops.Flotilla#FLOTILLA
local callsign=flotilla.callsignName and UTILS.GetCallsignName(flotilla.callsignName) or "N/A"
local modex=flotilla.modex and flotilla.modex or -1
local skill=flotilla.skill and tostring(flotilla.skill) or "N/A"
-- Flotilla text.
text=text..string.format("\n* %s %s: %s*%d/%d, Callsign=%s, Modex=%d, Skill=%s", flotilla.name, flotilla:GetState(), flotilla.aircrafttype, flotilla:CountAssets(true), #flotilla.assets, callsign, modex, skill)
end
self:I(self.lid..text)
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- FSM Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- On after "NavyOnMission".
-- @param #FLEET self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
-- @param Ops.ArmyGroup#ARMYGROUP ArmyGroup Ops army group on mission.
-- @param Ops.Auftrag#AUFTRAG Mission The requested mission.
function FLEET:onafterNavyOnMission(From, Event, To, NavyGroup, Mission)
-- Debug info.
self:T(self.lid..string.format("Group %s on %s mission %s", NavyGroup:GetName(), Mission:GetType(), Mission:GetName()))
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,175 +0,0 @@
--- **Ops** - Flotilla is a small naval group belonging to a fleet.
--
-- **Main Features:**
--
-- * Set parameters like livery, skill valid for all flotilla members.
-- * Define mission types, this flotilla can perform (see Ops.Auftrag#AUFTRAG).
-- * Pause/unpause flotilla operations.
--
-- ===
--
-- ### Author: **funkyfranky**
--
-- ===
-- @module Ops.Flotilla
-- @image OPS_Flotilla.png
--- FLOTILLA class.
-- @type FLOTILLA
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity level.
-- @field Ops.OpsGroup#OPSGROUP.WeaponData weaponData Weapon data table with key=BitType.
-- @extends Ops.Cohort#COHORT
--- *No captain can do very wrong if he places his ship alongside that of an enemy.* -- Horation Nelson
--
-- ===
--
-- # The FLOTILLA Concept
--
-- A FLOTILLA is an essential part of a FLEET.
--
--
--
-- @field #FLOTILLA
FLOTILLA = {
ClassName = "FLOTILLA",
verbose = 0,
weaponData = {},
}
--- FLOTILLA class version.
-- @field #string version
FLOTILLA.version="0.1.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new FLOTILLA object and start the FSM.
-- @param #FLOTILLA self
-- @param #string TemplateGroupName Name of the template group.
-- @param #number Ngroups Number of asset groups of this flotilla. Default 3.
-- @param #string FlotillaName Name of the flotilla. Must be **unique**!
-- @return #FLOTILLA self
function FLOTILLA:New(TemplateGroupName, Ngroups, FlotillaName)
-- Inherit everything from COHORT class.
local self=BASE:Inherit(self, COHORT:New(TemplateGroupName, Ngroups, FlotillaName)) -- #FLOTILLA
-- All flotillas get mission type Nothing.
self:AddMissionCapability(AUFTRAG.Type.NOTHING, 50)
-- Is naval.
self.isNaval=true
-- Get initial ammo.
self.ammo=self:_CheckAmmo()
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Flotilla specific user functions.
--- Set fleet of this flotilla.
-- @param #FLOTILLA self
-- @param Ops.Fleet#FLEET Fleet The fleet.
-- @return #FLOTILLA self
function FLOTILLA:SetFleet(Fleet)
self.legion=Fleet
return self
end
--- Get fleet of this flotilla.
-- @param #FLOTILLA self
-- @return Ops.Fleet#FLEET The fleet.
function FLOTILLA:GetFleet()
return self.legion
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Start & Status
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- On after Start event. Starts the FLIGHTGROUP FSM and event handlers.
-- @param #FLOTILLA self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function FLOTILLA:onafterStart(From, Event, To)
-- Short info.
local text=string.format("Starting %s v%s %s", self.ClassName, self.version, self.name)
self:I(self.lid..text)
-- Start the status monitoring.
self:__Status(-1)
end
--- On after "Status" event.
-- @param #FLOTILLA self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function FLOTILLA:onafterStatus(From, Event, To)
if self.verbose>=1 then
-- FSM state.
local fsmstate=self:GetState()
local callsign=self.callsignName and UTILS.GetCallsignName(self.callsignName) or "N/A"
local skill=self.skill and tostring(self.skill) or "N/A"
local NassetsTot=#self.assets
local NassetsInS=self:CountAssets(true)
local NassetsQP=0 ; local NassetsP=0 ; local NassetsQ=0
if self.legion then
NassetsQP, NassetsP, NassetsQ=self.legion:CountAssetsOnMission(nil, self)
end
-- Short info.
local text=string.format("%s [Type=%s, Call=%s, Skill=%s]: Assets Total=%d, Stock=%d, Mission=%d [Active=%d, Queue=%d]",
fsmstate, self.aircrafttype, callsign, skill, NassetsTot, NassetsInS, NassetsQP, NassetsP, NassetsQ)
self:T(self.lid..text)
-- Weapon data info.
if self.verbose>=3 and self.weaponData then
local text="Weapon Data:"
for bit,_weapondata in pairs(self.weaponData) do
local weapondata=_weapondata --Ops.OpsGroup#OPSGROUP.WeaponData
text=text..string.format("\n- Bit=%s: Rmin=%.1f km, Rmax=%.1f km", bit, weapondata.RangeMin/1000, weapondata.RangeMax/1000)
end
self:I(self.lid..text)
end
-- Check if group has detected any units.
self:_CheckAssetStatus()
end
if not self:IsStopped() then
self:__Status(-60)
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Misc Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,175 +0,0 @@
--- **Ops** - Brigade Platoon.
--
-- **Main Features:**
--
-- * Set parameters like livery, skill valid for all platoon members.
-- * Define modex and callsigns.
-- * Define mission types, this platoon can perform (see Ops.Auftrag#AUFTRAG).
-- * Pause/unpause platoon operations.
--
-- ===
--
-- ### Author: **funkyfranky**
-- @module Ops.Platoon
-- @image OPS_Platoon.png
--- PLATOON class.
-- @type PLATOON
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity level.
-- @field Ops.OpsGroup#OPSGROUP.WeaponData weaponData Weapon data table with key=BitType.
-- @extends Ops.Cohort#COHORT
--- *Some cool cohort quote* -- Known Author
--
-- ===
--
-- # The PLATOON Concept
--
-- A PLATOON is essential part of an BRIGADE.
--
--
--
-- @field #PLATOON
PLATOON = {
ClassName = "PLATOON",
verbose = 0,
weaponData = {},
}
--- PLATOON class version.
-- @field #string version
PLATOON.version="0.1.0"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: A lot.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new PLATOON object and start the FSM.
-- @param #PLATOON self
-- @param #string TemplateGroupName Name of the template group.
-- @param #number Ngroups Number of asset groups of this platoon. Default 3.
-- @param #string PlatoonName Name of the platoon. Must be **unique**!
-- @return #PLATOON self
function PLATOON:New(TemplateGroupName, Ngroups, PlatoonName)
-- Inherit everything from COHORT class.
local self=BASE:Inherit(self, COHORT:New(TemplateGroupName, Ngroups, PlatoonName)) -- #PLATOON
-- All platoons get mission type Nothing.
self:AddMissionCapability(AUFTRAG.Type.NOTHING, 50)
-- Is ground.
self.isGround=true
-- Get ammo.
self.ammo=self:_CheckAmmo()
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Platoon specific user functions.
--- Set brigade of this platoon.
-- @param #PLATOON self
-- @param Ops.Brigade#BRIGADE Brigade The brigade.
-- @return #PLATOON self
function PLATOON:SetBrigade(Brigade)
self.legion=Brigade
return self
end
--- Get brigade of this platoon.
-- @param #PLATOON self
-- @return Ops.Brigade#BRIGADE The brigade.
function PLATOON:GetBrigade()
return self.legion
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Start & Status
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--[[
--- On after Start event. Starts the FLIGHTGROUP FSM and event handlers.
-- @param #PLATOON self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function PLATOON:onafterStart(From, Event, To)
-- Short info.
local text=string.format("Starting %s v%s %s", self.ClassName, self.version, self.name)
self:I(self.lid..text)
-- Start the status monitoring.
self:__Status(-1)
end
]]
--- On after "Status" event.
-- @param #PLATOON self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function PLATOON:onafterStatus(From, Event, To)
if self.verbose>=1 then
-- FSM state.
local fsmstate=self:GetState()
local callsign=self.callsignName and UTILS.GetCallsignName(self.callsignName) or "N/A"
local skill=self.skill and tostring(self.skill) or "N/A"
local NassetsTot=#self.assets
local NassetsInS=self:CountAssets(true)
local NassetsQP=0 ; local NassetsP=0 ; local NassetsQ=0
if self.legion then
NassetsQP, NassetsP, NassetsQ=self.legion:CountAssetsOnMission(nil, self)
end
-- Short info.
local text=string.format("%s [Type=%s, Call=%s, Skill=%s]: Assets Total=%d, Stock=%d, Mission=%d [Active=%d, Queue=%d]",
fsmstate, self.aircrafttype, callsign, skill, NassetsTot, NassetsInS, NassetsQP, NassetsP, NassetsQ)
self:T(self.lid..text)
-- Weapon data info.
if self.verbose>=3 and self.weaponData then
local text="Weapon Data:"
for bit,_weapondata in pairs(self.weaponData) do
local weapondata=_weapondata --Ops.OpsGroup#OPSGROUP.WeaponData
text=text..string.format("\n- Bit=%s: Rmin=%.1f km, Rmax=%.1f km", bit, weapondata.RangeMin/1000, weapondata.RangeMax/1000)
end
self:I(self.lid..text)
end
-- Check if group has detected any units.
self:_CheckAssetStatus()
end
if not self:IsStopped() then
self:__Status(-60)
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Misc Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Misc functions.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,324 +0,0 @@
--- **Ops** - Airwing Squadron.
--
-- **Main Features:**
--
-- * Set parameters like livery, skill valid for all squadron members.
-- * Define modex and callsigns.
-- * Define mission types, this squadron can perform (see Ops.Auftrag#AUFTRAG).
-- * Pause/unpause squadron operations.
--
-- ===
--
-- ### Author: **funkyfranky**
-- @module Ops.Squadron
-- @image OPS_Squadron.png
--- SQUADRON class.
-- @type SQUADRON
-- @field #string ClassName Name of the class.
-- @field #number verbose Verbosity level.
-- @field #string lid Class id string for output to DCS log file.
-- @field #string name Name of the squadron.
-- @field #string templatename Name of the template group.
-- @field #string aircrafttype Type of the airframe the squadron is using.
-- @field Wrapper.Group#GROUP templategroup Template group.
-- @field #number ngrouping User defined number of units in the asset group.
-- @field #table assets Squadron assets.
-- @field #table missiontypes Capabilities (mission types and performances) of the squadron.
-- @field #number fuellow Low fuel threshold.
-- @field #boolean fuellowRefuel If `true`, flight tries to refuel at the nearest tanker.
-- @field #number maintenancetime Time in seconds needed for maintenance of a returned flight.
-- @field #number repairtime Time in seconds for each
-- @field #string livery Livery of the squadron.
-- @field #number skill Skill of squadron members.
-- @field #number modex Modex.
-- @field #number modexcounter Counter to incease modex number for assets.
-- @field #string callsignName Callsign name.
-- @field #number callsigncounter Counter to increase callsign names for new assets.
-- @field #number Ngroups Number of asset flight groups this squadron has.
-- @field #number engageRange Mission range in meters.
-- @field #string attribute Generalized attribute of the squadron template group.
-- @field #number tankerSystem For tanker squads, the refuel system used (boom=0 or probpe=1). Default nil.
-- @field #number refuelSystem For refuelable squads, the refuel system used (boom=0 or probe=1). Default nil.
-- @field #table tacanChannel List of TACAN channels available to the squadron.
-- @field #number radioFreq Radio frequency in MHz the squad uses.
-- @field #number radioModu Radio modulation the squad uses.
-- @field #string takeoffType Take of type.
-- @field #table parkingIDs Parking IDs for this squadron.
-- @field #boolean despawnAfterLanding Aircraft are despawned after landing.
-- @field #boolean despawnAfterHolding Aircraft are despawned after holding.
-- @extends Ops.Cohort#COHORT
--- *It is unbelievable what a squadron of twelve aircraft did to tip the balance* -- Adolf Galland
--
-- ===
--
-- # The SQUADRON Concept
--
-- A SQUADRON is essential part of an @{Ops.Airwing#AIRWING} and consists of **one** type of aircraft.
--
--
--
-- @field #SQUADRON
SQUADRON = {
ClassName = "SQUADRON",
verbose = 0,
modex = nil,
modexcounter = 0,
callsignName = nil,
callsigncounter= 11,
tankerSystem = nil,
refuelSystem = nil,
}
--- SQUADRON class version.
-- @field #string version
SQUADRON.version="0.8.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- DONE: Parking spots for squadrons?
-- DONE: Engage radius.
-- DONE: Modex.
-- DONE: Call signs.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new SQUADRON object and start the FSM.
-- @param #SQUADRON self
-- @param #string TemplateGroupName Name of the template group.
-- @param #number Ngroups Number of asset groups of this squadron. Default 3.
-- @param #string SquadronName Name of the squadron, e.g. "VFA-37". Must be **unique**!
-- @return #SQUADRON self
function SQUADRON:New(TemplateGroupName, Ngroups, SquadronName)
-- Inherit everything from FSM class.
local self=BASE:Inherit(self, COHORT:New(TemplateGroupName, Ngroups, SquadronName)) -- #SQUADRON
-- Everyone can ORBIT.
self:AddMissionCapability(AUFTRAG.Type.ORBIT)
-- Is air.
self.isAir=true
-- Refueling system.
self.refuelSystem=select(2, self.templategroup:GetUnit(1):IsRefuelable())
self.tankerSystem=select(2, self.templategroup:GetUnit(1):IsTanker())
------------------------
--- Pseudo Functions ---
------------------------
-- See COHORT class
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Set number of units in groups.
-- @param #SQUADRON self
-- @param #number nunits Number of units. Must be >=1 and <=4. Default 2.
-- @return #SQUADRON self
function SQUADRON:SetGrouping(nunits)
self.ngrouping=nunits or 2
if self.ngrouping<1 then self.ngrouping=1 end
if self.ngrouping>4 then self.ngrouping=4 end
return self
end
--- Set valid parking spot IDs. Assets of this squad are only allowed to be spawned at these parking spots. **Note** that the IDs are different from the ones displayed in the mission editor!
-- @param #SQUADRON self
-- @param #table ParkingIDs Table of parking ID numbers or a single `#number`.
-- @return #SQUADRON self
function SQUADRON:SetParkingIDs(ParkingIDs)
if type(ParkingIDs)~="table" then
ParkingIDs={ParkingIDs}
end
self.parkingIDs=ParkingIDs
return self
end
--- Set takeoff type. All assets of this squadron will be spawned with cold (default) or hot engines.
-- Spawning on runways is not supported.
-- @param #SQUADRON self
-- @param #string TakeoffType Take off type: "Cold" (default) or "Hot" with engines on or "Air" for spawning in air.
-- @return #SQUADRON self
function SQUADRON:SetTakeoffType(TakeoffType)
TakeoffType=TakeoffType or "Cold"
if TakeoffType:lower()=="hot" then
self.takeoffType=COORDINATE.WaypointType.TakeOffParkingHot
elseif TakeoffType:lower()=="cold" then
self.takeoffType=COORDINATE.WaypointType.TakeOffParking
elseif TakeoffType:lower()=="air" then
self.takeoffType=COORDINATE.WaypointType.TurningPoint
else
self.takeoffType=COORDINATE.WaypointType.TakeOffParking
end
return self
end
--- Set takeoff type cold (default). All assets of this squadron will be spawned with engines off (cold).
-- @param #SQUADRON self
-- @return #SQUADRON self
function SQUADRON:SetTakeoffCold()
self:SetTakeoffType("Cold")
return self
end
--- Set takeoff type hot. All assets of this squadron will be spawned with engines on (hot).
-- @param #SQUADRON self
-- @return #SQUADRON self
function SQUADRON:SetTakeoffHot()
self:SetTakeoffType("Hot")
return self
end
--- Set takeoff type air. All assets of this squadron will be spawned in air above the airbase.
-- @param #SQUADRON self
-- @return #SQUADRON self
function SQUADRON:SetTakeoffAir()
self:SetTakeoffType("Air")
return self
end
--- Set despawn after landing. Aircraft will be despawned after the landing event.
-- Can help to avoid DCS AI taxiing issues.
-- @param #SQUADRON self
-- @param #boolean Switch If `true` (default), activate despawn after landing.
-- @return #SQUADRON self
function SQUADRON:SetDespawnAfterLanding(Switch)
if Switch then
self.despawnAfterLanding=Switch
else
self.despawnAfterLanding=true
end
return self
end
--- Set despawn after holding. Aircraft will be despawned when they arrive at their holding position at the airbase.
-- Can help to avoid DCS AI taxiing issues.
-- @param #SQUADRON self
-- @param #boolean Switch If `true` (default), activate despawn after holding.
-- @return #SQUADRON self
function SQUADRON:SetDespawnAfterHolding(Switch)
if Switch then
self.despawnAfterHolding=Switch
else
self.despawnAfterHolding=true
end
return self
end
--- Set low fuel threshold.
-- @param #SQUADRON self
-- @param #number LowFuel Low fuel threshold in percent. Default 25.
-- @return #SQUADRON self
function SQUADRON:SetFuelLowThreshold(LowFuel)
self.fuellow=LowFuel or 25
return self
end
--- Set if low fuel threshold is reached, flight tries to refuel at the neares tanker.
-- @param #SQUADRON self
-- @param #boolean switch If true or nil, flight goes for refuelling. If false, turn this off.
-- @return #SQUADRON self
function SQUADRON:SetFuelLowRefuel(switch)
if switch==false then
self.fuellowRefuel=false
else
self.fuellowRefuel=true
end
return self
end
--- Set airwing.
-- @param #SQUADRON self
-- @param Ops.Airwing#AIRWING Airwing The airwing.
-- @return #SQUADRON self
function SQUADRON:SetAirwing(Airwing)
self.legion=Airwing
return self
end
--- Get airwing.
-- @param #SQUADRON self
-- @return Ops.Airwing#AIRWING The airwing.
function SQUADRON:GetAirwing(Airwing)
return self.legion
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Start & Status
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- On after Start event. Starts the FLIGHTGROUP FSM and event handlers.
-- @param #SQUADRON self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function SQUADRON:onafterStart(From, Event, To)
-- Short info.
local text=string.format("Starting SQUADRON", self.name)
self:T(self.lid..text)
-- Start the status monitoring.
self:__Status(-1)
end
--- On after "Status" event.
-- @param #SQUADRON self
-- @param #string From From state.
-- @param #string Event Event.
-- @param #string To To state.
function SQUADRON:onafterStatus(From, Event, To)
if self.verbose>=1 then
-- FSM state.
local fsmstate=self:GetState()
local callsign=self.callsignName and UTILS.GetCallsignName(self.callsignName) or "N/A"
local modex=self.modex and self.modex or -1
local skill=self.skill and tostring(self.skill) or "N/A"
local NassetsTot=#self.assets
local NassetsInS=self:CountAssets(true)
local NassetsQP=0 ; local NassetsP=0 ; local NassetsQ=0
if self.legion then
NassetsQP, NassetsP, NassetsQ=self.legion:CountAssetsOnMission(nil, self)
end
-- Short info.
local text=string.format("%s [Type=%s, Call=%s, Modex=%d, Skill=%s]: Assets Total=%d, Stock=%d, Mission=%d [Active=%d, Queue=%d]",
fsmstate, self.aircrafttype, callsign, modex, skill, NassetsTot, NassetsInS, NassetsQP, NassetsP, NassetsQ)
self:I(self.lid..text)
-- Check if group has detected any units.
self:_CheckAssetStatus()
end
if not self:IsStopped() then
self:__Status(-60)
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Misc Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

File diff suppressed because it is too large Load Diff

View File

@@ -30,10 +30,6 @@
--
-- ===
--
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_Demos/tree/master/Sound/Radio)
--
-- ===
--
-- ### Authors: Hugues "Grey_Echo" Bousquet, funkyfranky
--
-- @module Sound.Radio

View File

@@ -14,7 +14,7 @@
--
-- ===
--
-- ## Example Missions: [GitHub](https://github.com/FlightControl-Master/MOOSE_Demos/tree/master/Sound/MSRS).
-- ## Example Missions: [GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Sound/MSRS).
--
-- ===
--
@@ -824,7 +824,7 @@ function MSRS:SetVoiceProvider(Voice, Provider)
self:F( {Voice=Voice, Provider=Provider} )
self.poptions=self.poptions or {}
self.poptions[Provider or self:GetProvider()].voice=Voice
self.poptions[Provider or self:GetProvider()]=Voice
return self
end

View File

@@ -444,11 +444,10 @@ end
--- Print a table to log in a nice format
-- @param #table table The table to print
-- @param #number indent Number of indents
-- @param #boolean noprint Don't log but return text
-- @return #string text Text created on the fly of the log output
function UTILS.PrintTableToLog(table, indent, noprint)
function UTILS.PrintTableToLog(table, indent)
local text = "\n"
if not table or type(table) ~= "table" then
if not table then
env.warning("No table passed!")
return nil
end
@@ -456,16 +455,11 @@ function UTILS.PrintTableToLog(table, indent, noprint)
for k, v in pairs(table) do
if string.find(k," ") then k='"'..k..'"'end
if type(v) == "table" then
if not noprint then
env.info(string.rep(" ", indent) .. tostring(k) .. " = {")
end
env.info(string.rep(" ", indent) .. tostring(k) .. " = {")
text = text ..string.rep(" ", indent) .. tostring(k) .. " = {\n"
text = text .. tostring(UTILS.PrintTableToLog(v, indent + 1)).."\n"
if not noprint then
env.info(string.rep(" ", indent) .. "},")
end
env.info(string.rep(" ", indent) .. "},")
text = text .. string.rep(" ", indent) .. "},\n"
elseif type(v) == "function" then
else
local value
if tostring(v) == "true" or tostring(v) == "false" or tonumber(v) ~= nil then
@@ -473,9 +467,7 @@ function UTILS.PrintTableToLog(table, indent, noprint)
else
value = '"'..tostring(v)..'"'
end
if not noprint then
env.info(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n")
end
env.info(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n")
text = text .. string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n"
end
end
@@ -833,64 +825,6 @@ UTILS.tostringLL = function( lat, lon, acc, DMS)
end
end
--[[acc:
in DM: decimal point of minutes.
In DMS: decimal point of seconds.
position after the decimal of the least significant digit:
So:
42.32 - acc of 2.
]]
UTILS.tostringLLM2KData = function( lat, lon, acc)
local latHemi, lonHemi
if lat > 0 then
latHemi = 'N'
else
latHemi = 'S'
end
if lon > 0 then
lonHemi = 'E'
else
lonHemi = 'W'
end
lat = math.abs(lat)
lon = math.abs(lon)
local latDeg = math.floor(lat)
local latMin = (lat - latDeg)*60
local lonDeg = math.floor(lon)
local lonMin = (lon - lonDeg)*60
-- degrees, decimal minutes.
latMin = UTILS.Round(latMin, acc)
lonMin = UTILS.Round(lonMin, acc)
if latMin == 60 then
latMin = 0
latDeg = latDeg + 1
end
if lonMin == 60 then
lonMin = 0
lonDeg = lonDeg + 1
end
local minFrmtStr -- create the formatting string for the minutes place
if acc <= 0 then -- no decimal place.
minFrmtStr = '%02d'
else
local width = 3 + acc -- 01.310 - that's a width of 6, for example.
minFrmtStr = '%0' .. width .. '.' .. acc .. 'f'
end
-- 024 23'N or 024 23.123'N
return latHemi..string.format('%02d:', latDeg) .. string.format(minFrmtStr, latMin), lonHemi..string.format('%02d:', lonDeg) .. string.format(minFrmtStr, lonMin)
end
-- acc- the accuracy of each easting/northing. 0, 1, 2, 3, 4, or 5.
UTILS.tostringMGRS = function(MGRS, acc) --R2.1
@@ -2237,11 +2171,6 @@ function UTILS.IsLoadingDoorOpen( unit_name )
return true -- no doors on this one ;)
end
if type_name == "MH-60R" and (unit:getDrawArgumentValue(403) > 0 or unit:getDrawArgumentValue(403) == -1) then
BASE:T(unit_name .. " cargo door is open")
return true
end
return false
end -- nil
@@ -3730,116 +3659,3 @@ end
function UTILS.OctalToDecimal(Number)
return tonumber(Number,8)
end
--- Function to save the position of a set of #OPSGROUP (ARMYGROUP) objects.
-- @param Core.Set#SET_OPSGROUP Set of ops objects to save
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
-- @param #string Filename The name of the file.
-- @param #boolean Structured Append the data with a list of typenames in the group plus their count.
-- @return #boolean outcome True if saving is successful, else false.
function UTILS.SaveSetOfOpsGroups(Set,Path,Filename,Structured)
local filename = Filename or "SetOfGroups"
local data = "--Save SET of groups: (name,legion,template,alttemplate,units,position.x,position.y,position.z,strucdata) "..Filename .."\n"
local List = Set:GetSetObjects()
for _,_group in pairs (List) do
local group = _group:GetGroup() -- Wrapper.Group#GROUP
if group and group:IsAlive() then
local name = group:GetName()
local template = string.gsub(name,"(.AID.%d+$","")
if string.find(template,"#") then
template = string.gsub(name,"#(%d+)$","")
end
local alttemplate = _group.templatename or "none"
local legiono = _group.legion -- Ops.Legion#LEGION
local legion = "none"
if legiono and type(legiono) == "table" and legiono.ClassName then
legion = legiono:GetName()
local asset = legiono:GetAssetByName(name) -- Functional.Warehouse#WAREHOUSE.Assetitem
alttemplate=asset.templatename
end
local units = group:CountAliveUnits()
local position = group:GetVec3()
if Structured then
local structure = UTILS.GetCountPerTypeName(group)
local strucdata = ""
for typen,anzahl in pairs (structure) do
strucdata = strucdata .. typen .. "=="..anzahl..";"
end
data = string.format("%s%s,%s,%s,%s,%d,%d,%d,%d,%s\n",data,name,legion,template,alttemplate,units,position.x,position.y,position.z,strucdata)
else
data = string.format("%s%s,%s,%s,%s,%d,%d,%d,%d\n",data,name,legion,template,alttemplate,units,position.x,position.y,position.z)
end
end
end
-- save the data
local outcome = UTILS.SaveToFile(Path,Filename,data)
return outcome
end
--- Load back a #OPSGROUP (ARMYGROUP) data from file for use with @{Ops.Brigade#BRIGADE.LoadBackAssetInPosition}()
-- @param #string Path The path to use. Use double backslashes \\\\ on Windows filesystems.
-- @param #string Filename The name of the file.
-- @return #table Returns a table of data entries: `{ groupname=groupname, size=size, coordinate=coordinate, template=template, structure=structure, legion=legion, alttemplate=alttemplate }`
-- Returns nil when the file cannot be read.
function UTILS.LoadSetOfOpsGroups(Path,Filename)
local filename = Filename or "SetOfGroups"
local datatable = {}
if UTILS.CheckFileExists(Path,filename) then
local outcome,loadeddata = UTILS.LoadFromFile(Path,Filename)
-- remove header
table.remove(loadeddata, 1)
for _id,_entry in pairs (loadeddata) do
local dataset = UTILS.Split(_entry,",")
-- 1name,2legion,3template,4alttemplate,5units,6position.x,7position.y,8position.z,9strucdata
local groupname = dataset[1]
local legion = dataset[2]
local template = dataset[3]
local alttemplate = dataset[4]
local size = tonumber(dataset[5])
local posx = tonumber(dataset[6])
local posy = tonumber(dataset[7])
local posz = tonumber(dataset[8])
local structure = dataset[9]
local coordinate = COORDINATE:NewFromVec3({x=posx, y=posy, z=posz})
if size > 0 then
local data = { groupname=groupname, size=size, coordinate=coordinate, template=template, structure=structure, legion=legion, alttemplate=alttemplate }
table.insert(datatable,data)
end
end
else
return nil
end
return datatable
end
--- Get the clock position from a relative heading
-- @param #number refHdg The heading of the reference object (such as a Wrapper.UNIT) in 0-360
-- @param #number tgtHdg The absolute heading from the reference object to the target object/point in 0-360
-- @return #string text Text in clock heading such as "4 O'CLOCK"
-- @usage Display the range and clock distance of a BTR in relation to REAPER 1-1's heading:
--
-- myUnit = UNIT:FindByName( "REAPER 1-1" )
-- myTarget = GROUP:FindByName( "BTR-1" )
--
-- coordUnit = myUnit:GetCoordinate()
-- coordTarget = myTarget:GetCoordinate()
--
-- hdgUnit = myUnit:GetHeading()
-- hdgTarget = coordUnit:HeadingTo( coordTarget )
-- distTarget = coordUnit:Get3DDistance( coordTarget )
--
-- clockString = UTILS.ClockHeadingString( hdgUnit, hdgTarget )
--
-- -- Will show this message to REAPER 1-1 in-game: Contact BTR at 3 o'clock for 1134m!
-- MESSAGE:New("Contact BTR at " .. clockString .. " for " .. distTarget .. "m!):ToUnit( myUnit )
function UTILS.ClockHeadingString(refHdg,tgtHdg)
local relativeAngle = tgtHdg - refHdg
if relativeAngle < 0 then
relativeAngle = relativeAngle + 360
end
local clockPos = math.ceil((relativeAngle % 360) / 30)
return clockPos.." o'clock"
end

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -123,38 +123,18 @@ end
--- Check if SCENERY Object is alive.
--@param #SCENERY self
--@param #number Threshold (Optional) If given, SCENERY counts as alive above this relative life in percent (1..100).
--@return #number life
function SCENERY:IsAlive(Threshold)
if not Threshold then
return self:GetLife() >= 1 and true or false
else
return self:GetRelativeLife() > Threshold and true or false
end
function SCENERY:IsAlive()
return self:GetLife() >= 1 and true or false
end
--- Check if SCENERY Object is dead.
--@param #SCENERY self
--@param #number Threshold (Optional) If given, SCENERY counts as dead below this relative life in percent (1..100).
--@return #number life
function SCENERY:IsDead(Threshold)
if not Threshold then
return self:GetLife() < 1 and true or false
else
return self:GetRelativeLife() <= Threshold and true or false
end
function SCENERY:IsDead()
return self:GetLife() < 1 and true or false
end
--- Get SCENERY relative life in percent, e.g. 75.
--@param #SCENERY self
--@return #number rlife
function SCENERY:GetRelativeLife()
local life = self:GetLife()
local life0 = self:GetLife0()
local rlife = math.floor((life/life0)*100)
return rlife
end
--- Get the threat level of a SCENERY object. Always 0 as scenery does not pose a threat to anyone.
--@param #SCENERY self
--@return #number Threat level 0.

View File

@@ -12,8 +12,7 @@
-- @image Wrapper_Static.JPG
---
-- @type STATIC
--- @type STATIC
-- @extends Wrapper.Positionable#POSITIONABLE
--- Wrapper class to handle Static objects.
@@ -237,7 +236,7 @@ function STATIC:SpawnAt(Coordinate, Heading, Delay)
end
--- Respawn the @{Wrapper.Static} at the same location with the same properties.
--- Respawn the @{Wrapper.Unit} at the same location with the same properties.
-- This is useful to respawn a cargo after it has been destroyed.
-- @param #STATIC self
-- @param DCS#country.id CountryID (Optional) The country ID used for spawning the new static. Default is same as currently.
@@ -249,7 +248,7 @@ function STATIC:ReSpawn(CountryID, Delay)
else
CountryID=CountryID or self:GetCountry()
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, CountryID)
SpawnStatic:Spawn(nil, self.StaticName)
@@ -271,8 +270,8 @@ function STATIC:ReSpawnAt(Coordinate, Heading, Delay)
if Delay and Delay>0 then
SCHEDULER:New(nil, self.ReSpawnAt, {self, Coordinate, Heading}, Delay)
else
else
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, self:GetCountry())
SpawnStatic:SpawnFromCoordinate(Coordinate, Heading, self.StaticName)
@@ -281,52 +280,3 @@ function STATIC:ReSpawnAt(Coordinate, Heading, Delay)
return self
end
--- Find the first(!) STATIC matching using patterns. Note that this is **a lot** slower than `:FindByName()`!
-- @param #STATIC self
-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_\(Regular_Expressions\)) for regular expressions in LUA.
-- @return #STATIC The STATIC.
-- @usage
-- -- Find a static with a partial static name
-- local grp = STATIC:FindByMatching( "Apple" )
-- -- will return e.g. a static named "Apple-1-1"
--
-- -- using a pattern
-- local grp = STATIC:FindByMatching( ".%d.%d$" )
-- -- will return the first static found ending in "-1-1" to "-9-9", but not e.g. "-10-1"
function STATIC:FindByMatching( Pattern )
local GroupFound = nil
for name,static in pairs(_DATABASE.STATICS) do
if string.match(name, Pattern ) then
GroupFound = static
break
end
end
return GroupFound
end
--- Find all STATIC objects matching using patterns. Note that this is **a lot** slower than `:FindByName()`!
-- @param #STATIC self
-- @param #string Pattern The pattern to look for. Refer to [LUA patterns](http://www.easyuo.com/openeuo/wiki/index.php/Lua_Patterns_and_Captures_\(Regular_Expressions\)) for regular expressions in LUA.
-- @return #table Groups Table of matching #STATIC objects found
-- @usage
-- -- Find all static with a partial static name
-- local grptable = STATIC:FindAllByMatching( "Apple" )
-- -- will return all statics with "Apple" in the name
--
-- -- using a pattern
-- local grp = STATIC:FindAllByMatching( ".%d.%d$" )
-- -- will return the all statics found ending in "-1-1" to "-9-9", but not e.g. "-10-1" or "-1-10"
function STATIC:FindAllByMatching( Pattern )
local GroupsFound = {}
for name,static in pairs(_DATABASE.STATICS) do
if string.match(name, Pattern ) then
GroupsFound[#GroupsFound+1] = static
end
end
return GroupsFound
end

View File

@@ -8,7 +8,7 @@
--
-- ## Example Missions:
--
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_Demos/tree/master/Wrapper/Storage).
-- Demo missions can be found on [github](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Wrapper/Storage).
--
-- ===
--
@@ -33,93 +33,93 @@
-- ===
--
-- # The STORAGE Concept
--
-- The STORAGE class offers an easy-to-use wrapper interface to all DCS API functions of DCS warehouses.
--
-- The STORAGE class offers an easy-to-use wrapper interface to all DCS API functions of DCS warehouses.
-- We named the class STORAGE, because the name WAREHOUSE is already taken by another MOOSE class.
--
--
-- This class allows you to add and remove items to a DCS warehouse, such as aircraft, liquids, weapons and other equipment.
--
--
-- # Constructor
--
--
-- A DCS warehouse is associated with an airbase. Therefore, a `STORAGE` instance is automatically created, once an airbase is registered and added to the MOOSE database.
--
-- You can get the `STORAGE` object from the
--
-- -- Create a STORAGE instance of the Batumi warehouse
--
-- You can get the `STORAGE` object from the
--
-- -- Create a STORAGE instance of the Batumi warehouse
-- local storage=STORAGE:FindByName("Batumi")
--
--
-- An other way to get the `STORAGE` object is to retrieve it from the AIRBASE function `AIRBASE:GetStorage()`
--
--
-- -- Get storage instance of Batumi airbase
-- local Batumi=AIRBASE:FindByName("Batumi")
-- local storage=Batumi:GetStorage()
--
--
-- # Aircraft, Weapons and Equipment
--
--
-- ## Adding Items
--
--
-- To add aircraft, weapons and/or othe equipment, you can use the @{#STORAGE.AddItem}() function
--
--
-- storage:AddItem("A-10C", 3)
-- storage:AddItem("weapons.missiles.AIM_120C", 10)
--
--
-- This will add three A-10Cs and ten AIM-120C missiles to the warehouse inventory.
--
--
-- ## Setting Items
--
--
-- You can also explicitly set, how many items are in the inventory with the @{#STORAGE.SetItem}() function.
--
--
-- ## Removing Items
--
--
-- Items can be removed from the inventory with the @{#STORAGE.RemoveItem}() function.
--
--
-- ## Getting Amount
--
--
-- The number of items currently in the inventory can be obtained with the @{#STORAGE.GetItemAmount}() function
--
--
-- local N=storage:GetItemAmount("A-10C")
-- env.info(string.format("We currently have %d A-10Cs available", N))
--
--
-- # Liquids
--
--
-- Liquids can be added and removed by slightly different functions as described below. Currently there are four types of liquids
--
--
-- * Jet fuel `STORAGE.Liquid.JETFUEL`
-- * Aircraft gasoline `STORAGE.Liquid.GASOLINE`
-- * MW 50 `STORAGE.Liquid.MW50`
-- * Diesel `STORAGE.Liquid.DIESEL`
--
--
-- ## Adding Liquids
--
--
-- To add a certain type of liquid, you can use the @{#STORAGE.AddItem}(Type, Amount) function
--
--
-- storage:AddLiquid(STORAGE.Liquid.JETFUEL, 10000)
-- storage:AddLiquid(STORAGE.Liquid.DIESEL, 20000)
--
--
-- This will add 10,000 kg of jet fuel and 20,000 kg of diesel to the inventory.
--
--
-- ## Setting Liquids
--
--
-- You can also explicitly set the amount of liquid with the @{#STORAGE.SetLiquid}(Type, Amount) function.
--
--
-- ## Removing Liquids
--
--
-- Liquids can be removed with @{#STORAGE.RemoveLiquid}(Type, Amount) function.
--
--
-- ## Getting Amount
--
--
-- The current amount of a certain liquid can be obtained with the @{#STORAGE.GetLiquidAmount}(Type) function
--
--
-- local N=storage:GetLiquidAmount(STORAGE.Liquid.DIESEL)
-- env.info(string.format("We currently have %d kg of Diesel available", N))
--
--
--
--
-- # Inventory
--
--
-- The current inventory of the warehouse can be obtained with the @{#STORAGE.GetInventory}() function. This returns three tables with the aircraft, liquids and weapons:
--
--
-- local aircraft, liquids, weapons=storage:GetInventory()
--
--
-- UTILS.PrintTableToLog(aircraft)
-- UTILS.PrintTableToLog(liquids)
-- UTILS.PrintTableToLog(weapons)
@@ -168,7 +168,7 @@ function STORAGE:New(AirbaseName)
local self=BASE:Inherit(self, BASE:New()) -- #STORAGE
self.airbase=Airbase.getByName(AirbaseName)
if Airbase.getWarehouse then
self.warehouse=self.airbase:getWarehouse()
end
@@ -322,7 +322,7 @@ end
function STORAGE:GetLiquidName(Type)
local name="Unknown"
if Type==STORAGE.Liquid.JETFUEL then
name = "Jet fuel"
elseif Type==STORAGE.Liquid.GASOLINE then
@@ -411,25 +411,25 @@ function STORAGE:IsUnlimited(Type)
-- Get current amount of type.
local N=self:GetAmount(Type)
local unlimited=false
if N>0 then
-- Remove one item.
self:RemoveAmount(Type, 1)
-- Get amount.
local n=self:GetAmount(Type)
-- If amount did not change, it is unlimited.
unlimited=n==N
-- Add item back.
if not unlimited then
self:AddAmount(Type, 1)
end
-- Debug info.
self:I(self.lid..string.format("Type=%s: unlimited=%s (N=%d n=%d)", tostring(Type), tostring(unlimited), N, n))
end
@@ -523,7 +523,7 @@ end
function STORAGE:GetInventory(Item)
local inventory=self.warehouse:getInventory(Item)
return inventory.aircraft, inventory.liquids, inventory.weapon
end

View File

@@ -1244,9 +1244,7 @@ function UNIT:GetThreatLevel()
if Attributes["Fighters"] then ThreatLevel = 10
elseif Attributes["Multirole fighters"] then ThreatLevel = 9
elseif Attributes["Interceptors"] then ThreatLevel = 9
elseif Attributes["Battleplanes"] then ThreatLevel = 8
elseif Attributes["Battle airplanes"] then ThreatLevel = 8
elseif Attributes["Attack helicopters"] then ThreatLevel = 7
elseif Attributes["Strategic bombers"] then ThreatLevel = 6
elseif Attributes["Bombers"] then ThreatLevel = 5

View File

@@ -14,7 +14,7 @@
--
-- ## Additional Material:
--
-- * **Demo Missions:** [GitHub](https://github.com/FlightControl-Master/MOOSE_Demos/tree/master/Wrapper/Weapon)
-- * **Demo Missions:** [GitHub](https://github.com/FlightControl-Master/MOOSE_MISSIONS/tree/develop/Wrapper/Weapon)
-- * **YouTube videos:** None
-- * **Guides:** None
--
@@ -69,77 +69,77 @@
-- ===
--
-- # The WEAPON Concept
--
--
-- The WEAPON class offers an easy-to-use wrapper interface to all DCS API functions.
--
--
-- Probably, the most striking highlight is that the position of the weapon can be tracked and its impact position can be determined, which is not
-- possible with the native DCS scripting engine functions.
--
-- **Note** that this wrapper class is different from most others as weapon objects cannot be found with a DCS API function like `getByName()`.
-- They can only be found in DCS events like the "Shot" event, where the weapon object is contained in the event data.
--
--
-- # Tracking
--
--
-- The status of the weapon can be tracked with the @{#WEAPON.StartTrack} function. This function will try to determin the position of the weapon in (normally) relatively
-- small time steps. The time step can be set via the @{#WEAPON.SetTimeStepTrack} function and is by default set to 0.01 seconds.
--
--
-- Once the position cannot be retrieved any more, the weapon has impacted (or was destroyed otherwise) and the last known position is safed as the impact point.
-- The impact point can be accessed with the @{#WEAPON.GetImpactVec3} or @{#WEAPON.GetImpactCoordinate} functions.
--
--
-- ## Impact Point Marking
--
--
-- You can mark the impact point on the F10 map with @{#WEAPON.SetMarkImpact}.
--
--
-- You can also trigger coloured smoke at the impact point via @{#WEAPON.SetSmokeImpact}.
--
--
-- ## Callback functions
--
--
-- It is possible to define functions that are called during the tracking of the weapon and upon impact, which help you to customize further actions.
--
--
-- ### Callback on Impact
--
--
-- The function called on impact can be set with @{#WEAPON.SetFuncImpact}
--
--
-- ### Callback when Tracking
--
--
-- The function called each time the weapon status is tracked can be set with @{#WEAPON.SetFuncTrack}
--
--
-- # Target
--
-- If the weapon has a specific target, you can get it with the @{#WEAPON.GetTarget} function. Note that the object, which is returned can vary. Normally, it is a UNIT
--
-- If the weapon has a specific target, you can get it with the @{#WEAPON.GetTarget} function. Note that the object, which is returned can vary. Normally, it is a UNIT
-- but it could also be a STATIC object.
--
--
-- Also note that the weapon does not always have a target, it can loose a target and re-aquire it and the target might change to another unit.
--
--
-- You can get the target name with the @{#WEAPON.GetTargetName} function.
--
--
-- The distance to the target is returned by the @{#WEAPON.GetTargetDistance} function.
--
--
-- # Category
--
--
-- The category (bomb, rocket, missile, shell, torpedo) of the weapon can be retrieved with the @{#WEAPON.GetCategory} function.
--
-- You can check if the weapon is a
--
--
-- You can check if the weapon is a
--
-- * bomb with @{#WEAPON.IsBomb}
-- * rocket with @{#WEAPON.IsRocket}
-- * missile with @{#WEAPON.IsMissile}
-- * shell with @{#WEAPON.IsShell}
-- * torpedo with @{#WEAPON.IsTorpedo}
--
--
-- # Parameters
--
--
-- You can get various parameters of the weapon, *e.g.*
--
--
-- * position: @{#WEAPON.GetVec3}, @{#WEAPON.GetVec2 }, @{#WEAPON.GetCoordinate}
-- * speed: @{#WEAPON.GetSpeed}
-- * coalition: @{#WEAPON.GetCoalition}
-- * country: @{#WEAPON.GetCountry}
--
--
-- # Dependencies
--
--
-- This class is used (at least) in the MOOSE classes:
--
--
-- * RANGE (to determine the impact points of bombs and missiles)
-- * ARTY (to destroy and replace shells with smoke or illumination)
-- * FOX (to destroy the missile before it hits the target)
@@ -181,48 +181,48 @@ function WEAPON:New(WeaponObject)
-- Inherit everything from FSM class.
local self=BASE:Inherit(self, POSITIONABLE:New("Weapon")) -- #WEAPON
-- Set DCS weapon object.
self.weapon=WeaponObject
-- Descriptors containing a lot of info.
self.desc=WeaponObject:getDesc()
-- This gives the object category which is always Object.Category.WEAPON!
--self.category=WeaponObject:getCategory()
-- Weapon category: 0=SHELL, 1=MISSILE, 2=ROCKET, 3=BOMB (Weapon.Category.X)
self.category = self.desc.category
if self:IsMissile() and self.desc.missileCategory then
if self:IsMissile() and self.desc.missileCategory then
self.categoryMissile=self.desc.missileCategory
end
-- Get type name.
self.typeName=WeaponObject:getTypeName() or "Unknown Type"
-- Get name of object. Usually a number like "1234567".
self.name=WeaponObject:getName()
-- Get coaliton of weapon.
self.coalition=WeaponObject:getCoalition()
-- Get country of weapon.
self.country=WeaponObject:getCountry()
-- Get DCS unit of the launcher.
self.launcher=WeaponObject:getLauncher()
-- Get launcher of weapon.
self.launcherName="Unknown Launcher"
if self.launcher then
self.launcherName=self.launcher:getName()
self.launcherUnit=UNIT:Find(self.launcher)
end
-- Init the coordinate of the weapon from that of the launcher.
self.coordinate=COORDINATE:NewFromVec3(self.launcher:getPoint())
-- Set log ID.
self.lid=string.format("[%s] %s | ", self.typeName, self.name)
@@ -237,12 +237,12 @@ function WEAPON:New(WeaponObject)
-- Set default parameters
self:SetTimeStepTrack()
self:SetDistanceInterceptPoint()
-- Debug info.
local text=string.format("Weapon v%s\nName=%s, TypeName=%s, Category=%s, Coalition=%d, Country=%d, Launcher=%s",
local text=string.format("Weapon v%s\nName=%s, TypeName=%s, Category=%s, Coalition=%d, Country=%d, Launcher=%s",
self.version, self.name, self.typeName, self.category, self.coalition, self.country, self.launcherName)
self:T(self.lid..text)
-- Descriptors.
self:T2(self.desc)
@@ -312,13 +312,13 @@ function WEAPON:SetSmokeImpact(Switch, SmokeColor)
else
self.impactSmoke=true
end
self.impactSmokeColor=SmokeColor or SMOKECOLOR.Red
return self
end
--- Set callback function when weapon is tracked and still alive. The first argument will be the WEAPON object.
--- Set callback function when weapon is tracked and still alive. The first argument will be the WEAPON object.
-- Note that this can be called many times per second. So be careful for performance reasons.
-- @param #WEAPON self
-- @param #function FuncTrack Function called during tracking.
@@ -335,19 +335,19 @@ end
-- @param #function FuncImpact Function called once the weapon impacted.
-- @param ... Optional function arguments.
-- @return #WEAPON self
--
--
-- @usage
-- -- Function called on impact.
-- local function OnImpact(Weapon)
-- Weapon:GetImpactCoordinate():MarkToAll("Impact Coordinate of weapon")
-- end
--
--
-- -- Set which function to call.
-- myweapon:SetFuncImpact(OnImpact)
--
--
-- -- Start tracking.
-- myweapon:Track()
--
--
function WEAPON:SetFuncImpact(FuncImpact, ...)
self.impactFunc=FuncImpact
self.impactArg=arg or {}
@@ -368,37 +368,37 @@ end
function WEAPON:GetTarget()
local target=nil --Wrapper.Object#OBJECT
if self.weapon then
-- Get the DCS target object, which can be a Unit, Weapon, Static, Scenery, Airbase.
local object=self.weapon:getTarget()
if object then
-- Get object category.
local category=Object.getCategory(object)
--Target name
local name=object:getName()
-- Debug info.
self:T(self.lid..string.format("Got Target Object %s, category=%d", object:getName(), category))
self:T(self.lid..string.format("Got Target Object %s, category=%d", object:getName(), category))
if category==Object.Category.UNIT then
target=UNIT:FindByName(name)
elseif category==Object.Category.STATIC then
target=STATIC:FindByName(name, false)
elseif category==Object.Category.SCENERY then
self:E(self.lid..string.format("ERROR: Scenery target not implemented yet!"))
else
self:E(self.lid..string.format("ERROR: Object category=%d is not implemented yet!", category))
end
end
end
@@ -413,25 +413,25 @@ function WEAPON:GetTargetDistance(ConversionFunction)
-- Get the target of the weapon.
local target=self:GetTarget() --Wrapper.Unit#UNIT
local distance=nil
if target then
-- Current position of target.
local tv3=target:GetVec3()
-- Current position of weapon.
local wv3=self:GetVec3()
if tv3 and wv3 then
distance=UTILS.VecDist3D(tv3, wv3)
if ConversionFunction then
distance=ConversionFunction(distance)
end
end
end
return distance
@@ -445,10 +445,10 @@ function WEAPON:GetTargetName()
-- Get the target of the weapon.
local target=self:GetTarget() --Wrapper.Unit#UNIT
local name="None"
if target then
name=target:GetName()
name=target:GetName()
end
return name
@@ -476,13 +476,13 @@ function WEAPON:GetSpeed(ConversionFunction)
if self.weapon then
local v=self:GetVelocityVec3()
speed=UTILS.VecNorm(v)
if ConversionFunction then
speed=ConversionFunction(speed)
end
end
return speed
@@ -508,11 +508,11 @@ end
function WEAPON:GetVec2()
local vec3=self:GetVec3()
if vec3 then
local vec2={x=vec3.x, y=vec3.z}
return vec2
end
@@ -521,28 +521,28 @@ end
--- Get type name.
-- @param #WEAPON self
-- @return #string The type name.
-- @return #string The type name.
function WEAPON:GetTypeName()
return self.typeName
end
--- Get coalition.
-- @param #WEAPON self
-- @return #number Coalition ID.
-- @return #number Coalition ID.
function WEAPON:GetCoalition()
return self.coalition
end
--- Get country.
-- @param #WEAPON self
-- @return #number Country ID.
-- @return #number Country ID.
function WEAPON:GetCountry()
return self.country
end
--- Get DCS object.
-- @param #WEAPON self
-- @return DCS#Weapon The weapon object.
-- @return DCS#Weapon The weapon object.
function WEAPON:GetDCSObject()
-- This polymorphic function is used in Wrapper.Identifiable#IDENTIFIABLE
return self.weapon
@@ -675,23 +675,23 @@ end
function WEAPON:Destroy(Delay)
if Delay and Delay>0 then
self:ScheduleOnce(Delay, WEAPON.Destroy, self, 0)
self:ScheduleOnce(Delay, WEAPON.Destroy, self, 0)
else
if self.weapon then
self:T(self.lid.."Destroying Weapon NOW!")
self:StopTrack()
self.weapon:destroy()
end
end
end
return self
end
--- Start tracking the weapon until it impacts or is destroyed otherwise.
-- The position of the weapon is monitored in small time steps. Once the position cannot be determined anymore, the monitoring is stopped and the last known position is
-- the (approximate) impact point. Of course, the smaller the time step, the better the position can be determined. However, this can hit the performance as many
-- The position of the weapon is monitored in small time steps. Once the position cannot be determined anymore, the monitoring is stopped and the last known position is
-- the (approximate) impact point. Of course, the smaller the time step, the better the position can be determined. However, this can hit the performance as many
-- calculations per second need to be carried out.
-- @param #WEAPON self
-- @param #WEAPON self
-- @param #number Delay Delay in seconds before the tracking starts. Default 0.001 sec.
-- @return #WEAPON self
function WEAPON:StartTrack(Delay)
@@ -700,8 +700,8 @@ function WEAPON:StartTrack(Delay)
Delay=math.max(Delay or 0.001, 0.001)
-- Debug info.
self:T(self.lid..string.format("Start tracking weapon in %.4f sec", Delay))
self:T(self.lid..string.format("Start tracking weapon in %.4f sec", Delay))
-- Weapon is not yet "alife" just yet. Start timer in 0.001 seconds.
self.trackScheduleID=timer.scheduleFunction(WEAPON._TrackWeapon, self, timer.getTime() + Delay)
@@ -710,7 +710,7 @@ end
--- Stop tracking the weapon by removing the scheduler function.
-- @param #WEAPON self
-- @param #WEAPON self
-- @param #number Delay (Optional) Delay in seconds before the tracking is stopped.
-- @return #WEAPON self
function WEAPON:StopTrack(Delay)
@@ -719,13 +719,13 @@ function WEAPON:StopTrack(Delay)
-- Delayed call.
self:ScheduleOnce(Delay, WEAPON.StopTrack, self, 0)
else
if self.trackScheduleID then
timer.removeFunction(self.trackScheduleID)
end
end
return self
@@ -762,10 +762,10 @@ function WEAPON:_TrackWeapon(time)
-- Update last known position.
self.pos3 = pos3
-- Update last known vec3.
self.vec3 = UTILS.DeepCopy(self.pos3.p)
-- Update coordinate.
self.coordinate:UpdateFromVec3(self.vec3)
@@ -774,70 +774,70 @@ function WEAPON:_TrackWeapon(time)
-- Keep on tracking by returning the next time below.
self.tracking=true
-- Callback function.
if self.trackFunc then
self.trackFunc(self, unpack(self.trackArg))
end
-- Verbose output.
if self.verbose>=5 then
-- Get vec2 of current position.
local vec2={x=self.vec3.x, y=self.vec3.z}
-- Land hight.
local height=land.getHeight(vec2)
-- Current height above ground level.
-- Current height above ground level.
local agl=self.vec3.y-height
-- Estimated IP (if any)
local ip=self:_GetIP(self.distIP)
-- Distance between positon and estimated impact.
local d=0
if ip then
d=UTILS.VecDist3D(self.vec3, ip)
end
-- Output.
self:I(self.lid..string.format("T=%.3f: Height=%.3f m AGL=%.3f m, dIP=%.3f", time, height, agl, d))
end
else
---------------------------
-- Weapon does NOT exist --
---------------------------
---------------------------
-- Get intercept point from position (p) and direction (x) in 50 meters.
local ip = self:_GetIP(self.distIP)
if self.verbose>=10 and ip then
-- Output.
self:I(self.lid.."Got intercept point!")
-- Coordinate of the impact point.
local coord=COORDINATE:NewFromVec3(ip)
-- Mark coordinate.
coord:MarkToAll("Intercept point")
coord:SmokeBlue()
-- Distance to last known pos.
local d=UTILS.VecDist3D(ip, self.vec3)
-- Output.
self:I(self.lid..string.format("FF d(ip, vec3)=%.3f meters", d))
end
-- Safe impact vec3.
self.impactVec3=ip or self.vec3
-- Safe impact coordinate.
self.impactCoord=COORDINATE:NewFromVec3(self.vec3)
@@ -848,22 +848,22 @@ function WEAPON:_TrackWeapon(time)
if self.impactMark then
self.impactCoord:MarkToAll(string.format("Impact point of weapon %s\ntype=%s\nlauncher=%s", self.name, self.typeName, self.launcherName))
end
-- Smoke on impact point.
if self.impactSmoke then
self.impactCoord:Smoke(self.impactSmokeColor)
end
-- Call callback function.
if self.impactFunc then
self.impactFunc(self, unpack(self.impactArg or {}))
end
-- Stop tracking by returning nil below.
self.tracking=false
end
-- Return next time the function is called or nil to stop the scheduler.
if self.tracking then
if self.dtTrack and self.dtTrack>=0.00001 then
@@ -885,12 +885,12 @@ function WEAPON:_GetIP(Distance)
Distance=Distance or 50
local ip=nil --DCS#Vec3
if Distance>0 and self.pos3 then
-- Get intercept point from position (p) and direction (x) in 20 meters.
ip = land.getIP(self.pos3.p, self.pos3.x, Distance or 20) --DCS#Vec3
end
return ip

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramLaunchConfigurationType">
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/lua54/lua54.exe}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="&quot;Moose Setup/Moose_Create.lua&quot; &quot;D&quot; &quot;LOCAL&quot; &quot;${workspace_loc:/Moose_Framework/Moose Development/Moose}&quot; &quot;${workspace_loc:/Moose_Framework/Moose Setup}&quot;&#13;&#10;&quot;${workspace_loc:/Moose_Framework/MOOSE_INCLUDE\Moose_Include_Dynamic}&quot; 1"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="C:\ProgramData\chocolatey\lib\lua51\tools\lua5.1.exe"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="&quot;Moose Setup\Moose_Create.lua&quot; &quot;D&quot; &quot;LOCAL&quot; &quot;${resource_loc:/MOOSE/Moose Development/Moose}&quot; &quot;${resource_loc:/MOOSE/Moose Setup}&quot;&#13;&#10;${resource_loc:/MOOSE_INCLUDE/Moose_Include_Dynamic}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/MOOSE}"/>
</launchConfiguration>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramLaunchConfigurationType">
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/Moose_Framework/lua54/lua54.exe}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="&quot;Moose Setup\Moose_Create.lua&quot; &quot;S&quot; &quot;LOCAL&quot; &quot;${workspace_loc:/Moose_Framework/Moose Development/Moose}&quot; &quot;${workspace_loc:/Moose_Framework/Moose Setup}&quot;&#13;&#10;&quot;${workspace_loc:/Moose_Framework/MOOSE_INCLUDE\Moose_Include_Static}&quot; 1"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/Moose_Framework}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="C:\ProgramData\chocolatey\lib\lua51\tools\lua5.1.exe"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_TOOL_ARGUMENTS" value="&quot;Moose Setup\Moose_Create.lua&quot; &quot;S&quot; &quot;LOCAL&quot; &quot;${resource_loc:/MOOSE/Moose Development/Moose}&quot; &quot;${resource_loc:/MOOSE/Moose Setup}&quot;&#13;&#10;${resource_loc:/MOOSE_INCLUDE/Moose_Include_Static}"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/MOOSE}"/>
</launchConfiguration>

View File

@@ -29,12 +29,11 @@ Core/SpawnStatic.lua
Core/Timer.lua
Core/Goal.lua
Core/Spot.lua
Core/TextAndSound.lua
Core/Condition.lua
Core/Pathline.lua
Core/ClientMenu.lua
Core/Astar.lua
Core/MarkerOps_Base.lua
Core/Astar.lua
Core/Condition.lua
Core/TextAndSound.lua
Core/Pathline.lua
Wrapper/Object.lua
Wrapper/Identifiable.lua
@@ -79,44 +78,13 @@ Functional/Warehouse.lua
Functional/Fox.lua
Functional/Mantis.lua
Functional/Shorad.lua
Functional/Autolase.lua
Functional/AICSAR.lua
Functional/AmmoTruck.lua
Functional/ZoneGoalCargo.lua
Functional/Tiresias.lua
Functional/Stratego.lua
Ops/Airboss.lua
Ops/RecoveryTanker.lua
Ops/RescueHelo.lua
Ops/ATIS.lua
Ops/Auftrag.lua
Ops/OpsGroup.lua
Ops/FlightGroup.lua
Ops/NavyGroup.lua
Ops/Cohort.lua
Ops/Squadron.lua
Ops/Platoon.lua
Ops/Flotilla.lua
Ops/Legion.lua
Ops/AirWing.lua
Ops/Brigade.lua
Ops/Fleet.lua
Ops/Intelligence.lua
Ops/Commander.lua
Ops/Chief.lua
Ops/CSAR.lua
Ops/CTLD.lua
Ops/Awacs.lua
Ops/Operation.lua
Ops/FlightControl.lua
Ops/PlayerTask.lua
Ops/PlayerRecce.lua
Ops/EasyGCICAP.lua
Ops/OpsZone.lua
Ops/ArmyGroup.lua
Ops/OpsTransport.lua
Ops/Target.lua
Ops/CSAR.lua
AI/AI_Balancer.lua
AI/AI_Air.lua

View File

@@ -4,12 +4,6 @@ parent: Advanced
nav_order: 01
---
# Concepts
{: .no_toc }
1. Table of contents
{:toc}
If you want to get deeper into Moose, you will encounter a few terms and
concepts that we will explain here. You will need them for the later pages.
@@ -26,21 +20,21 @@ files on [GitHub] with a browser. But using [Git] will ease up the steps to keep
the Moose version on your hard disk up to date.
You will need to interact with [GitHub]. At least to download the Moose files.
For non-developers the page can be confusing. Take your time and read this
For non developers the page can be confusing. Take your time and read this
documentation. We are not able to explain every single detail on using [GitHub]
and [Git]. Especially because it is changing really quick and this documentation
and [Git]. Especially because it is changing really quick and this documentaion
will not. So try to use the help system of [GitHub] or find some videos on
[YouTube]. If you get stuck ask for help in the [Moose Discord].
Moose uses more than one repository on [GitHub] which doesn't exactly make it
Moose uses more then one repository on [GitHub] which doesn't exactly make it
any clearer. A list can be found on the [reposities] page.
# Branches: master & develop
As already explained in the [overview] two branches are used:
- Branch [master]: Stable release branch.
- Branch [develop]: Newest development with more OPS classes.
- [master]: Stable release branch.
- [develop]: Newest development with more OPS classes.
As a starter it is okay to begin your journey with the `master` branch.
If you are interested in some newer classes you need to use the `develop`
@@ -48,8 +42,8 @@ branch. The later one is also very stable, but it's missing more detailed
documentation and example missions for some of the new OPS classes.
You can switch between these branches with a drop down in the upper left corner
of the [GitHub] repository page. The list of branches is long. So it is a best
practice to save a bookmark in your browser with the links above.
of th [GitHub] repository page. The list of branches is long. So it is a best
practise to save a bookmark in your browser with the links above.
Both branches are available on most of the different repositories. But because
of a limitation of [GitHub pages], we had to split the documentation in two
different repositories:
@@ -61,7 +55,7 @@ different repositories:
Moose consists of more than 140 individual files with the file extension `.lua`.
They are places in a [directory tree], which makes it more organized and its
semantic is pre-defined for [IntelliSense] to work.
semantic is pre-defined for IntelliSense to work.
On every change which is pushed to [GitHub] a build job will combine all of
these files to a single file called `Moose.lua`. In a second step all
@@ -69,12 +63,12 @@ comments will be removed to decrease the file size and the result will be saved
as `Moose_.lua`. These both files are created for users of Moose to include in
your missions.
The individual `.lua` files are used by the Moose developers and power users.
The individual `.lua` files are used by the Mosse developers and power users.
It is complicated to use them, but in combination with an IDE and a debugger it
is very useful to analyze even complex problems or write new additions to the
is very usefull to analyse even complex problems or write new additions to the
Moose framework.
# Static loading
# Static loading vs. dynamic loading
If you add a script file with a `DO SCRIPT FILE` trigger, like we described in
[Create your own Hello world], the script file will be copied into the mission
@@ -84,159 +78,25 @@ with another file ending.
If you change the script file after adding it to the mission, the changes are
not available on mission start. You have to re-add the script after each change.
This can be very annoying and often leads to forgetting to add the change again.
Then you wonder why the mission does not deliver the desired result.
Then you wonder why the script does not deliver the desired result.
But when the mission is finished you can upload it to your dedicated DCS server
or give it to a friend and it should run without problems. This way of embedding
the scripts do we call `static loading` and the resulting mission is very
portable.
# Dynamic loading of mission scripts
The other way of loading scripts is by using `DO SCRIPT`. This time the mission
The other way on loading scripts is by using `DO SCRIPT`. This time the mission
editor don't show a file browse button. Instead you see a (very small) text
field to enter the code directly into it. It is only useful for very small
script snippets. But we can use it to load a file from your hard drive like
this:
field to enter the code directly into it. It is only usefull for very small
script snippets. But we can use it to load a file from our hard drive like this:
```lua
dofile('C:/MyScripts/hello-world.lua')
dofile('C:\\MyScripts\\hello-world.lua')
dofile([[C:\MyScripts\hello-world.lua]])
```
So all lines above do the same. In [Lua] you need to specify the path with
slashes, escape backslashes or use double square brackets around the string.
Double square brackets are usefull, because you can copy paste the path
without any modification.
If you upload a mission with this code, you need to create the folder
`C:\MyScripts\` on the server file system and upload the newest version of
`hello-world.lua`, too. The same applies, if you give the mission to a friend.
This makes the mission less portable, but on the other hand the mission uses the
file on the hard disk, without the need to add it to the mission again.
All you need to do is save the file and restart the mission.
The following can be used to increase portability:
```lua
dofile(lfs.writedir() .. '/Missions/hello-world.lua')
```
The function `lfs.writedir()` will return your [Saved Games folder].
So you place the scripts in the subfolder Missions. This way the folder
structure is already available on all target systems. But you need to ensure
mission and script are both in sync to avoid problems. If you changed both and
upload only one of them to your server, you may get trouble.
There is another method you may find useful to dynamically load scripts:
```lua
assert(loadfile('C:/MyScripts/hello-world.lua'))()
assert(loadfile('C:\\MyScripts\\hello-world.lua'))()
assert(loadfile([[C:\MyScripts\hello-world.lua]]))()
```
It is a little bit harder to read and write because of all these different
brackets. Especially the one on line 3. But it is a little safer than `dofile`.
Because of readability I prefer to use `dofile`.
# Dynamic loading of Moose
Of course you can use the same method to load Moose. This way you can place one
Moose file in your [Saved Games folder], which is used by multiple missions.
If you want to update Moose you just need to replace the file and all missions
will use the new version. But I prefer to add Moose by a `DO SCRIPT FILE`
trigger so I can add and test the new version for each mission step by step.
But we added two different ways to load the Moose source files automatically.
This is useful for Moose developers and it is a requirement to use a debugger.
This will be explained later in the [Debugger Guide].
# Automatic dynamic loading
With the code below you can have the advantages of both approaches.
- Copy the code into your mission script at the beginning.
- Save the mission script into the folder Missions in your [Saved Games folder].
- Change script filename in line 2 to match to your script.
- [De-Sanitize] your `MissionScripting.lua`.
Now the mission will use the script on your hard drive instead of the script
embedded in th MIZ file, as long as it is available. So you can chnge the
script, save it and restart the mission, without the need to readd it after each
change.
If you reach a stable state in your script development and want to upload the
mission to your server or give it to a friend, then just add the script again
like in the static method and save the mission.
{: .important }
> Do not forget to readd the script, prior uploading or sharing the mission,
> or it will run with an outdated version of your script and may fail if the
> objects in the mission don't match to this old version.
```lua
-- Use script file from hard disk instead of the one included in the .miz file
if lfs and io then
MissionScript = lfs.writedir() .. '/Missions/hello-world-autodyn.lua'
-- Check if the running skript is from temp directory to avoid an endless loop
if string.find( debug.getinfo(1).source, lfs.tempdir() ) then
local f=io.open(MissionScript,"r")
if f~=nil then
io.close(f)
env.info( '*** LOAD MISSION SCRIPT FROM HARD DISK *** ' )
dofile(MissionScript)
do return end
end
end
else
env.error( '*** LOAD MISSION SCRIPT FROM HARD DISK FAILED (Desanitize lfs and io)*** ' )
end
--
-- Simple example mission to show the very basics of MOOSE
--
MESSAGE:New( "Hello World! This messages is printed by MOOSE!", 35, "INFO" ):ToAll():ToLog()
aaa
```
# IDE vs. Notepad++
As a beginner you should start with a good text editor, which supports syntax
highlighting of [Lua] code. This must not be [Notepad++]. It can be any other
powerful editor of your choice. Do yourself a favor and don't use the Windows
editor.
If you are a developer of [Lua] or another programming language, then your are
most likely familiar with an IDE (Integrated Develop Environment).
Otherwise you should know, that an IDE may help you with code completion,
Refactoring, Autocorrection, Formatting Source Code, showing documentation
as popup on mouse hover over keywords and Debugging.
There are different IDEs available. And not all IDEs support all features.
The three most important for Moose are:
- [Eclipse LDT]
- [Visual Studio Code]
- [PyCharm] (or [IntelliJ IDEA])
Eclipse has the best support for hover documentation and [IntelliSense] with
Moose. The Inventor of Moose (FlightControl) did an amazing job by adding an
integration to Eclipse LDT (a special version for Lua).
Unfortunately Eclipse LDT is not maintained any longer (last release 2018).
And the debugger doesn't work anymore, since an update of DCS.
In Visual Studio Code the support of Lua can be added by an addon.
The debugger works with Moose and DCS, but showing the LuaDoc and [IntelliSense]
is very limited.
PyCharm supports Lua also with an addon. The debugger works with Moose and DCS,
but showing the LuaDoc and [IntelliSense] is very limited.
It is up to you to choose the IDE according to your taste. Guides on how to
setup Moose with different IDEs and Debuggers are provided later in this
documentation.
# What is a debugger (good for)
[Git]: https://en.wikipedia.org/wiki/Git
[GitHub]: https://github.com/
@@ -250,14 +110,3 @@ documentation.
[MOOSE_DOCS]: https://flightcontrol-master.github.io/MOOSE_DOCS/
[MOOSE_DOCS_DEVELOP]: https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/
[directory tree]: https://github.com/FlightControl-Master/MOOSE/tree/master/Moose%20Development/Moose
[Saved Games folder]: ../beginner/tipps-and-tricks.md#find-the-saved-games-folder
[Lua]: https://www.lua.org/
[Create your own Hello world]: ../beginner/hello-world-build.md
[Debugger Guide]: debugger.md
[IntelliSense]: https://en.wikipedia.org/wiki/IntelliSense
[De-Sanitize]: desanitize-dcs.md
[Notepad++]: https://notepad-plus-plus.org/downloads/
[Eclipse LDT]: https://projects.eclipse.org/projects/tools.ldt
[Visual Studio Code]: https://code.visualstudio.com/
[PyCharm]: https://www.jetbrains.com/pycharm/
[IntelliJ IDEA]: https://www.jetbrains.com/idea/

View File

@@ -1,8 +0,0 @@
---
title: Debugger
parent: Advanced
nav_order: 100
---
{: .warning }
> THIS DOCUMENT IS STILL WORK IN PROGRESS!

View File

@@ -35,24 +35,16 @@ Please remember when posting a question:
- Before posting anything follow the [troubleshooting steps].
- **Read your logs**.
### Formulate a good description
A post should contain the following:
- A description what you expected to happen and what actually happened.
- Do not use vague words this stuff is hard to help with! Be specific.
1. A describtion what you expected to happen and what actually happened.
- Do not use vague words this stuff is hard to help with! Be specific.
- Describe what happens instead.
- The less detail you offer, the less chance you can be helped.
- Don't say it doesn't work. Or is it broken. Say what it actually does.
2. Describe what happens instead.
- The less detail you offer, the less chance you can be helped.
- Dont say it doesnt work. Or is it broken. Say what it actually does.
### Format your code
The easier your code is to read, the more likely you are to get a helpful answer. If your code is hard to read, some
people who could help you may not even bother to read your code. Syntax Highlighting makes the code much clearer and
easier to understand. Therefore:
- Post your code in Discord as formatted code:
3. Post your code in Discord as formatted code:
- Wrap a single line of code in backticks \` like this:
@@ -62,31 +54,14 @@ easier to understand. Therefore:
![discord-multi-line-code.png](../images/beginner/discord-multi-line-code.png)
### Do not post a screenshot of your code
- Post your log lines with the error or warning messages. Format them like this:
Your code is easy to read on a screenshot if you are using a good text editor or IDE, but if someone discovers an error
in your code and wants to post a corrected version, they will have to type out the entire code. This could lead to them
not helping you because it's too much work for them.
### Post your log
If the error message in the `dcs.log` does not tell you anything, then post it in the Discord.
- Post the important log lines with the error or warning messages. Format them like this:
![discord-format-logs.png](../images/beginner/discord-format-logs.png)
### Send your mission when requested
Please don't just send your mission file. You have to manually extract the script from the file.
It is better to send your script code and log lines beforehand.
If this does not help, you may be asked to send your mission.
![discord-fomat-logs.png](../images/beginner/discord-fomat-logs.png)
- Some complex problems need the mission (.miz file) also.
- But post your mission only when requested.
- Try to simplify your mission if it is complex!
- Try to avoid or delete MODs, because could prevent people from helping you.
There are people in the Discord and in the forum, who spend their free time to
help you. <br />

View File

@@ -11,14 +11,10 @@ nav_order: 05
## Something went wrong
If the mission shows not the expected behavior do the following steps:
If the mission shows not the expected behaviour do the following steps:
1. Double check if you added the changed mission script to the mission again!
1. Check if the triggers are configured as requested in the last sections:
- To load MOOSE: `4 MISSION START`, nothing on `CONDITIONS`, `DO SCRIPT FILE` to load `Moose_.lua`.
- To load mission script(s): `1 ONCE`, in `CONDITIONS` add `TIME MORE` = 1, `DO SCRIPT FILE` to load `yourscript.lua`.
1. Double check if you have the right version of MOOSE (some classes need the develop branch).
1. Try the newest version of MOOSE.
1. Check if the triggers are configured as requested in the last sections.
## Read the logs
@@ -26,7 +22,8 @@ The DCS log is a super important and useful log for the entire of DCS World.
All scripting and other errors are recorded here. It is the one stop shop for
things that occurred in your mission. It will tell you if there was a mistake.
1. Open the file `dcs.log` in the `Logs` subfolder in your DCS [Saved Games folder].
1. Open the file `dcs.log` in the `Logs` subfolder in your DCS
[Saved Games folder].
1. Search for the following line: `*** MOOSE INCLUDE END ***`
- If it is included in the log, Moose was loaded.

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Binary file not shown.