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 - name: Run LuaSrcDiet
run: | run: |
luasrcdiet --basic --opt-emptylines ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua luasrcdiet --basic --opt-emptylines ./build/result/Moose_Include_Static/Moose.lua -o ./build/result/Moose_Include_Static/Moose_.lua
######################################################################### #########################################################################
# Push to MOOSE_INCLUDE # Push to MOOSE_INCLUDE
######################################################################### #########################################################################

View File

@@ -33,7 +33,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Setup Ruby - name: Setup Ruby
uses: ruby/setup-ruby@v1 uses: ruby/setup-ruby@v1
with: with:
@@ -43,7 +43,7 @@ jobs:
working-directory: docs/ working-directory: docs/
- name: Setup Pages - name: Setup Pages
id: pages id: pages
uses: actions/configure-pages@v4 uses: actions/configure-pages@v3
- name: Build with Jekyll - name: Build with Jekyll
# Outputs to the './_site' directory by default # Outputs to the './_site' directory by default
run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}" run: bundle exec jekyll build --baseurl "${{ steps.pages.outputs.base_path }}"
@@ -52,7 +52,7 @@ jobs:
working-directory: docs/ working-directory: docs/
- name: Upload artifact - name: Upload artifact
# Automatically uploads an artifact from the './_site' directory by default # Automatically uploads an artifact from the './_site' directory by default
uses: actions/upload-pages-artifact@v3 uses: actions/upload-pages-artifact@v1
with: with:
path: docs/_site/ path: docs/_site/
@@ -66,13 +66,13 @@ jobs:
steps: steps:
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
id: deployment id: deployment
uses: actions/deploy-pages@v4 uses: actions/deploy-pages@v1
check: check:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: deploy needs: deploy
steps: steps:
- name: Setup Node - name: Setup Node
uses: actions/setup-node@v4 uses: actions/setup-node@v3
- run: npm install linkinator - 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 - 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 .buildpath
#####################
## Visual Studio Code
#####################
*.code-workspace
.vscode/
################# #################
## Visual Studio ## Visual Studio
################# #################

View File

@@ -1144,19 +1144,6 @@ function BASE:TraceClassMethod( Class, Method )
self:I( "Tracing method " .. Method .. " of class " .. Class ) self:I( "Tracing method " .. Method .. " of class " .. Class )
end 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. --- Trace a function call. This function is private.
-- @param #BASE self -- @param #BASE self
-- @param Arguments A #table or any field. -- @param Arguments A #table or any field.
@@ -1181,7 +1168,7 @@ function BASE:_F( Arguments, DebugInfoCurrentParam, DebugInfoFromParam )
if DebugInfoFrom then if DebugInfoFrom then
LineFrom = DebugInfoFrom.currentline LineFrom = DebugInfoFrom.currentline
end 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 end
end end
@@ -1255,7 +1242,7 @@ function BASE:_T( Arguments, DebugInfoCurrentParam, DebugInfoFromParam )
if DebugInfoFrom then if DebugInfoFrom then
LineFrom = DebugInfoFrom.currentline LineFrom = DebugInfoFrom.currentline
end 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 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 ) ) ) env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "E", self.ClassName, self.ClassID, Function, UTILS.BasicSerialize( Arguments ) ) )
else 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
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 ) ) ) env.info( string.format( "%6d(%6d)/%1s:%30s%05d.%s(%s)", LineCurrent, LineFrom, "I", self.ClassName, self.ClassID, Function, UTILS.BasicSerialize( Arguments ) ) )
else 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
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 -- ### Authors: Hugues "Grey_Echo" Bousquet, funkyfranky
-- --
-- @module Core.Beacon -- @module Core.Beacon
@@ -290,7 +286,6 @@ end
-- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon -- myBeacon:AATACAN(20, "TEXACO", true) -- Activate the beacon
function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration) function BEACON:AATACAN(TACANChannel, Message, Bearing, BeaconDuration)
self:F({TACANChannel, Message, Bearing, BeaconDuration}) self:F({TACANChannel, Message, Bearing, BeaconDuration})
self:E("This method is DEPRECATED! Please use ActivateTACAN() instead.")
local IsValid = true 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 Templates Templates: Units, Groups, Statics, ClientsByName, ClientsByID.
-- @field #table CLIENTS Clients. -- @field #table CLIENTS Clients.
-- @field #table STORAGES DCS warehouse storages. -- @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 -- @extends Core.Base#BASE
--- Contains collections of wrapper objects defined within MOOSE that reflect objects within the simulator. --- Contains collections of wrapper objects defined within MOOSE that reflect objects within the simulator.
@@ -95,8 +93,6 @@ DATABASE = {
OPSZONES = {}, OPSZONES = {},
PATHLINES = {}, PATHLINES = {},
STORAGES = {}, STORAGES = {},
STNS={},
SADL={},
} }
local _DATABASECoalition = local _DATABASECoalition =
@@ -932,7 +928,7 @@ function DATABASE:Spawn( SpawnTemplate )
SpawnTemplate.CountryID = nil SpawnTemplate.CountryID = nil
SpawnTemplate.CategoryID = nil SpawnTemplate.CategoryID = nil
self:_RegisterGroupTemplate( SpawnTemplate, SpawnCoalitionID, SpawnCategoryID, SpawnCountryID, SpawnTemplate.name ) self:_RegisterGroupTemplate( SpawnTemplate, SpawnCoalitionID, SpawnCategoryID, SpawnCountryID )
self:T3( SpawnTemplate ) self:T3( SpawnTemplate )
coalition.addGroup( SpawnCountryID, SpawnCategoryID, 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.ClientsByName[UnitTemplate.name].CountryID = CountryID
self.Templates.ClientsByID[UnitTemplate.unitId] = UnitTemplate self.Templates.ClientsByID[UnitTemplate.unitId] = UnitTemplate
end 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 UnitNames[#UnitNames+1] = self.Templates.Units[UnitTemplate.name].UnitName
end end
-- Debug info. -- Debug info.
self:T( { Group = self.Templates.Groups[GroupTemplateName].GroupName, self:T( { Group = self.Templates.Groups[GroupTemplateName].GroupName,
Coalition = self.Templates.Groups[GroupTemplateName].CoalitionID, Coalition = self.Templates.Groups[GroupTemplateName].CoalitionID,
@@ -1068,80 +1043,6 @@ function DATABASE:_RegisterGroupTemplate( GroupTemplate, CoalitionSide, Category
) )
end 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. --- Get group template.
-- @param #DATABASE self -- @param #DATABASE self
-- @param #string GroupName Group name. -- @param #string GroupName Group name.

View File

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

View File

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

View File

@@ -73,7 +73,7 @@ PATHLINE = {
--- PATHLINE class version. --- PATHLINE class version.
-- @field #string version -- @field #string version
PATHLINE.version="0.1.1" PATHLINE.version="0.1.0"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list -- 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. --- 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 -- @param #PATHLINE self
-- @return <Core.Point#COORDINATE> List of COORDINATES points. -- @return <Core.Point#COORDINATE> List of COORDINATES points.
function PATHLINE:GetCoordinates() function PATHLINE:GetCoordinats()
local vecs={} local vecs={}
for _,_point in pairs(self.points) do for _,_point in pairs(self.points) do
local point=_point --#PATHLINE.Point local point=_point --#PATHLINE.Point
local coord=COORDINATE:NewFromVec3(point.vec3) local coord=COORDINATE:NewFromVec3(point.vec3)
table.insert(vecs,coord)
end end
return vecs return vecs
@@ -263,7 +262,7 @@ function PATHLINE:GetPointFromIndex(n)
local point=nil --#PATHLINE.Point local point=nil --#PATHLINE.Point
if n>=1 and n<=N then if n>=1 and n<=N then
point=self.points[n] point=self.point[n]
else else
self:E(self.lid..string.format("ERROR: No point in pathline for N=%s", tostring(n))) self:E(self.lid..string.format("ERROR: No point in pathline for N=%s", tostring(n)))
end end
@@ -368,4 +367,4 @@ end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

View File

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

View File

@@ -1097,7 +1097,6 @@ do
GroupPrefixes = nil, GroupPrefixes = nil,
Zones = nil, Zones = nil,
Functions = nil, Functions = nil,
Alive = nil,
}, },
FilterMeta = { FilterMeta = {
Coalitions = { Coalitions = {
@@ -1205,7 +1204,7 @@ do
if not DontSetCargoBayLimit then if not DontSetCargoBayLimit then
-- I set the default cargo bay weight limit each time a new group is added to the set. -- 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? -- 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 if UnitData and UnitData:IsAlive() then
UnitData:SetCargoBayWeightLimit() UnitData:SetCargoBayWeightLimit()
end end
@@ -1471,7 +1470,7 @@ do
end 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. -- Only the groups that are active will be included within the set.
-- @param #SET_GROUP self -- @param #SET_GROUP self
-- @param #boolean Active (Optional) Include only active groups to the set. -- @param #boolean Active (Optional) Include only active groups to the set.
@@ -1496,14 +1495,6 @@ do
self.Filter.Active = Active self.Filter.Active = Active
return self return self
end 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. --- Starts the filtering.
-- @param #SET_GROUP self -- @param #SET_GROUP self
@@ -2002,16 +1993,7 @@ do
function SET_GROUP:IsIncludeObject( MGroup ) function SET_GROUP:IsIncludeObject( MGroup )
self:F2( MGroup ) self:F2( MGroup )
local MGroupInclude = true 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 if self.Filter.Active ~= nil then
local MGroupActive = false local MGroupActive = false
self:F( { Active = self.Filter.Active } ) self:F( { Active = self.Filter.Active } )
@@ -3015,7 +2997,7 @@ do -- SET_UNIT
local velocity = self:GetVelocity() or 0 local velocity = self:GetVelocity() or 0
Coordinate:SetHeading( heading ) Coordinate:SetHeading( heading )
Coordinate:SetVelocity( velocity ) Coordinate:SetVelocity( velocity )
self:T(UTILS.PrintTableToLog(Coordinate)) self:I(UTILS.PrintTableToLog(Coordinate))
end end
return Coordinate return Coordinate
@@ -4539,7 +4521,7 @@ do -- SET_CLIENT
if Event.IniObjectCategory == Object.Category.UNIT and Event.IniGroup and Event.IniGroup:IsGround() then if Event.IniObjectCategory == Object.Category.UNIT and Event.IniGroup and Event.IniGroup:IsGround() then
-- CA Slot entered -- CA Slot entered
local ObjectName, Object = self:AddInDatabase( Event ) 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 if Object and self:IsIncludeObject( Object ) then
self:Add( ObjectName, Object ) self:Add( ObjectName, Object )
end 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. --- 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% -- **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. -- 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 -- @param #SET_SCENERY self
-- @return #number LifePoints -- @return #number LifePoints
function SET_SCENERY:GetRelativeLife() 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.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. -- * @{#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 -- ## SPAWN **Spawn** methods
-- --
@@ -536,7 +520,7 @@ function SPAWN:NewFromTemplate( SpawnTemplate, SpawnTemplatePrefix, SpawnAliasPr
end end
if SpawnTemplate then 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.SpawnTemplatePrefix = SpawnTemplatePrefix
self.SpawnAliasPrefix = SpawnAliasPrefix or SpawnTemplatePrefix self.SpawnAliasPrefix = SpawnAliasPrefix or SpawnTemplatePrefix
self.SpawnTemplate.name = SpawnTemplatePrefix self.SpawnTemplate.name = SpawnTemplatePrefix
@@ -740,7 +724,7 @@ end
-- @param #number Country Country id as number or enumerator: -- @param #number Country Country id as number or enumerator:
-- --
-- * @{DCS#country.id.RUSSIA} -- * @{DCS#country.id.RUSSIA}
-- * @{DCS#country.id.USA} -- * @{DCS#county.id.USA}
-- --
-- @return #SPAWN self -- @return #SPAWN self
function SPAWN:InitCountry( Country ) function SPAWN:InitCountry( Country )
@@ -796,82 +780,6 @@ function SPAWN:InitSkill( Skill )
return self return self
end 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. --- Sets the radio communication on or off. Same as checking/unchecking the COMM box in the mission editor.
-- @param #SPAWN self -- @param #SPAWN self
-- @param #number switch If true (or nil), enables the radio communication. If false, disables the radio for the spawned group. -- @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 there is a SpawnFunction hook defined, call it.
if self.SpawnFunctionHook then if self.SpawnFunctionHook then
-- delay calling this for .3 seconds so that it hopefully comes after the BIRTH event of the group. -- 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.3 ) self.SpawnHookScheduler:Schedule( nil, self.SpawnFunctionHook, { self.SpawnGroups[self.SpawnIndex].Group, unpack( self.SpawnFunctionArguments ) }, 0.1 )
end end
-- TODO: Need to fix this by putting an "R" in the name of the group when the group repeats. -- TODO: Need to fix this by putting an "R" in the name of the group when the group repeats.
-- if self.Repeat then -- if self.Repeat then
@@ -1791,7 +1699,6 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
end end
self.SpawnGroups[self.SpawnIndex].Spawned = true self.SpawnGroups[self.SpawnIndex].Spawned = true
self.SpawnGroups[self.SpawnIndex].Group.TemplateDonor = self.SpawnTemplatePrefix
return self.SpawnGroups[self.SpawnIndex].Group return self.SpawnGroups[self.SpawnIndex].Group
else else
-- self:E( { self.SpawnTemplatePrefix, "No more Groups to Spawn:", SpawnIndex, self.SpawnMaxGroups } ) -- 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 end
end end
if self.SpawnInitKeepUnitNames == false then if self.SpawnInitKeepUnitNames == false then
for UnitID = 1, #SpawnTemplate.units do for UnitID = 1, #SpawnTemplate.units do
SpawnTemplate.units[UnitID].name = string.format( SpawnTemplate.name .. '-%02d', UnitID ) SpawnTemplate.units[UnitID].name = string.format( SpawnTemplate.name .. '-%02d', UnitID )
@@ -3404,17 +3311,9 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
end end
else else
for UnitID = 1, #SpawnTemplate.units do for UnitID = 1, #SpawnTemplate.units do
local SpawnInitKeepUnitIFF = false local UnitPrefix, Rest = string.match( SpawnTemplate.units[UnitID].name, "^([^#]+)#?" ):gsub( "^%s*(.-)%s*$", "%1" )
if string.find(SpawnTemplate.units[UnitID].name,"#IFF_",1,true) then --Razbam IFF hack for F15E etc self:T( { UnitPrefix, Rest } )
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
SpawnTemplate.units[UnitID].name = string.format( '%s#%03d-%02d', UnitPrefix, SpawnIndex, UnitID ) SpawnTemplate.units[UnitID].name = string.format( '%s#%03d-%02d', UnitPrefix, SpawnIndex, UnitID )
SpawnTemplate.units[UnitID].unitId = nil SpawnTemplate.units[UnitID].unitId = nil
end end
@@ -3496,58 +3395,34 @@ function SPAWN:_Prepare( SpawnTemplatePrefix, SpawnIndex ) -- R2.2
SpawnTemplate.units[UnitID].callsign = Callsign + SpawnIndex SpawnTemplate.units[UnitID].callsign = Callsign + SpawnIndex
end end
end end
-- Speed
if self.InitSpeed then
SpawnTemplate.units[UnitID].speed = self.InitSpeed
end
-- Link16 -- Link16
local AddProps = SpawnTemplate.units[UnitID].AddPropAircraft local AddProps = SpawnTemplate.units[UnitID].AddPropAircraft
if AddProps then if AddProps then
if SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 then if SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 then
if self.SpawnInitSTN then -- 4 digit octal with leading 0
local octal = self.SpawnInitSTN if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16) ~= nil then
if UnitID > 1 then local octal = SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16
octal = _DATABASE:GetNextSTN(self.SpawnInitSTN,SpawnTemplate.units[UnitID].name) local decimal = UTILS.OctalToDecimal(octal)+UnitID-1
end SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",UTILS.DecimalToOctal(decimal))
SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",octal) else -- ED bug - chars in here
else local STN = math.floor(UTILS.RandomGaussian(4088/2,nil,1000,4088))
-- 5 digit octal with leading 0 STN = STN+UnitID-1
if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16) ~= nil then local OSTN = UTILS.DecimalToOctal(STN)
local octal = SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 SpawnTemplate.units[UnitID].AddPropAircraft.STN_L16 = string.format("%05d",OSTN)
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
end end
end end
-- A10CII -- A10CII
if SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN then if SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN then
-- 4 digit octal with leading 0 -- 3 digit octal with leading 0
if self.SpawnInitSADL then if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN) ~= nil then
local octal = self.SpawnInitSADL local octal = SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN
if UnitID > 1 then local decimal = UTILS.OctalToDecimal(octal)+UnitID-1
octal = _DATABASE:GetNextSADL(self.SpawnInitSADL,SpawnTemplate.units[UnitID].name) SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",UTILS.DecimalToOctal(decimal))
end else -- ED bug - chars in here
SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",octal) local STN = math.floor(UTILS.RandomGaussian(504/2,nil,100,504))
else STN = STN+UnitID-1
if tonumber(SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN) ~= nil then local OSTN = UTILS.DecimalToOctal(STN)
local octal = SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN SpawnTemplate.units[UnitID].AddPropAircraft.SADL_TN = string.format("%04d",OSTN)
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
end end
end end
-- VoiceCallsignNumber -- VoiceCallsignNumber

View File

@@ -1,36 +1,36 @@
--- **Core** - Spawn statics. --- **Core** - Spawn statics.
-- --
-- === -- ===
-- --
-- ## Features: -- ## Features:
-- --
-- * Spawn new statics from a static already defined in the mission editor. -- * 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 template.
-- * Spawn new statics from a given type. -- * Spawn new statics from a given type.
-- * Spawn with a custom heading and location. -- * Spawn with a custom heading and location.
-- * Spawn within a zone. -- * Spawn within a zone.
-- * Spawn statics linked to units, .e.g on aircraft carriers. -- * Spawn statics linked to units, .e.g on aircraft carriers.
-- --
-- === -- ===
-- --
-- # Demo Missions -- # 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 -- # YouTube Channel
-- --
-- ## No videos yet! -- ## No videos yet!
-- --
-- === -- ===
-- --
-- ### Author: **FlightControl** -- ### Author: **FlightControl**
-- ### Contributions: **funkyfranky** -- ### Contributions: **funkyfranky**
-- --
-- === -- ===
-- --
-- @module Core.SpawnStatic -- @module Core.SpawnStatic
-- @image Core_Spawnstatic.JPG -- @image Core_Spawnstatic.JPG
@@ -58,37 +58,37 @@
--- Allows to spawn dynamically new @{Wrapper.Static}s into your mission. --- 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. -- 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: -- 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**. -- * 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 -- # 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. -- 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 -- ## 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 -- 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 the static.
-- --
-- ## From a Template -- ## 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. -- 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 -- ## 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 -- 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. -- are used. For example, if no spawn coordinate is given, the static will be created at the origin of the map.
-- --
-- # Setting Parameters -- # 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! -- 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.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.InitHeading}(Heading) sets the orientation of the static.
-- * @{#SPAWNSTATIC.InitLivery}(LiveryName) sets the livery of the static. Not all statics support this. -- * @{#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. -- * @{#SPAWNSTATIC.InitLinkToUnit}(Unit, OffsetX, OffsetY, OffsetAngle) links the static to a unit, e.g. to an aircraft carrier.
-- --
-- # Spawning the Statics -- # 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 -- 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. -- 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.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.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.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**! -- * @{#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 -- @field #SPAWNSTATIC SPAWNSTATIC
-- --
SPAWNSTATIC = { SPAWNSTATIC = {
ClassName = "SPAWNSTATIC", ClassName = "SPAWNSTATIC",
SpawnIndex = 0, SpawnIndex = 0,
@@ -139,9 +139,9 @@ SPAWNSTATIC = {
function SPAWNSTATIC:NewFromStatic(SpawnTemplateName, SpawnCountryID) function SPAWNSTATIC:NewFromStatic(SpawnTemplateName, SpawnCountryID)
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate(SpawnTemplateName) local TemplateStatic, CoalitionID, CategoryID, CountryID = _DATABASE:GetStaticGroupTemplate(SpawnTemplateName)
if TemplateStatic then if TemplateStatic then
self.SpawnTemplatePrefix = SpawnTemplateName self.SpawnTemplatePrefix = SpawnTemplateName
self.TemplateStaticUnit = UTILS.DeepCopy(TemplateStatic.units[1]) self.TemplateStaticUnit = UTILS.DeepCopy(TemplateStatic.units[1])
@@ -166,11 +166,11 @@ end
function SPAWNSTATIC:NewFromTemplate(SpawnTemplate, CountryID) function SPAWNSTATIC:NewFromTemplate(SpawnTemplate, CountryID)
local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC local self = BASE:Inherit( self, BASE:New() ) -- #SPAWNSTATIC
self.TemplateStaticUnit = UTILS.DeepCopy(SpawnTemplate) self.TemplateStaticUnit = UTILS.DeepCopy(SpawnTemplate)
self.SpawnTemplatePrefix = SpawnTemplate.name self.SpawnTemplatePrefix = SpawnTemplate.name
self.CountryID = CountryID or country.id.USA self.CountryID = CountryID or country.id.USA
return self return self
end end
@@ -189,7 +189,7 @@ function SPAWNSTATIC:NewFromType(StaticType, StaticCategory, CountryID)
self.InitStaticCategory=StaticCategory self.InitStaticCategory=StaticCategory
self.CountryID=CountryID or country.id.USA self.CountryID=CountryID or country.id.USA
self.SpawnTemplatePrefix=self.InitStaticType self.SpawnTemplatePrefix=self.InitStaticType
self.InitStaticCoordinate=COORDINATE:New(0, 0, 0) self.InitStaticCoordinate=COORDINATE:New(0, 0, 0)
self.InitStaticHeading=0 self.InitStaticHeading=0
@@ -291,7 +291,7 @@ function SPAWNSTATIC:InitCountry(CountryID)
return self return self
end 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 #SPAWNSTATIC self
-- @param #string NamePrefix Name prefix of statics spawned. Will append #0001, etc to the name. -- @param #string NamePrefix Name prefix of statics spawned. Will append #0001, etc to the name.
-- @return #SPAWNSTATIC self -- @return #SPAWNSTATIC self
@@ -327,13 +327,13 @@ function SPAWNSTATIC:Spawn(Heading, NewName)
if Heading then if Heading then
self.InitStaticHeading=Heading self.InitStaticHeading=Heading
end end
if NewName then if NewName then
self.InitStaticName=NewName self.InitStaticName=NewName
end end
return self:_SpawnStatic(self.TemplateStaticUnit, self.CountryID) return self:_SpawnStatic(self.TemplateStaticUnit, self.CountryID)
end end
--- Creates a new @{Wrapper.Static} from a POINT_VEC2. --- 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 vec2={x=PointVec2:GetX(), y=PointVec2:GetY()}
local Coordinate=COORDINATE:NewFromVec2(vec2) local Coordinate=COORDINATE:NewFromVec2(vec2)
return self:SpawnFromCoordinate(Coordinate, Heading, NewName) return self:SpawnFromCoordinate(Coordinate, Heading, NewName)
end end
@@ -362,11 +362,11 @@ function SPAWNSTATIC:SpawnFromCoordinate(Coordinate, Heading, NewName)
-- Set up coordinate. -- Set up coordinate.
self.InitStaticCoordinate=Coordinate self.InitStaticCoordinate=Coordinate
if Heading then if Heading then
self.InitStaticHeading=Heading self.InitStaticHeading=Heading
end end
if NewName then if NewName then
self.InitStaticName=NewName self.InitStaticName=NewName
end end
@@ -385,7 +385,7 @@ function SPAWNSTATIC:SpawnFromZone(Zone, Heading, NewName)
-- Spawn the new static at the center of the zone. -- Spawn the new static at the center of the zone.
local Static = self:SpawnFromPointVec2( Zone:GetPointVec2(), Heading, NewName ) local Static = self:SpawnFromPointVec2( Zone:GetPointVec2(), Heading, NewName )
return Static return Static
end end
@@ -399,45 +399,45 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
Template=Template or {} Template=Template or {}
local CountryID=CountryID or self.CountryID local CountryID=CountryID or self.CountryID
if self.InitStaticType then if self.InitStaticType then
Template.type=self.InitStaticType Template.type=self.InitStaticType
end end
if self.InitStaticCategory then if self.InitStaticCategory then
Template.category=self.InitStaticCategory Template.category=self.InitStaticCategory
end end
if self.InitStaticCoordinate then if self.InitStaticCoordinate then
Template.x = self.InitStaticCoordinate.x Template.x = self.InitStaticCoordinate.x
Template.y = self.InitStaticCoordinate.z Template.y = self.InitStaticCoordinate.z
Template.alt = self.InitStaticCoordinate.y Template.alt = self.InitStaticCoordinate.y
end end
if self.InitStaticHeading then if self.InitStaticHeading then
Template.heading = math.rad(self.InitStaticHeading) Template.heading = math.rad(self.InitStaticHeading)
end end
if self.InitStaticShape then if self.InitStaticShape then
Template.shape_name=self.InitStaticShape Template.shape_name=self.InitStaticShape
end end
if self.InitStaticLivery then if self.InitStaticLivery then
Template.livery_id=self.InitStaticLivery Template.livery_id=self.InitStaticLivery
end end
if self.InitStaticDead~=nil then if self.InitStaticDead~=nil then
Template.dead=self.InitStaticDead Template.dead=self.InitStaticDead
end end
if self.InitStaticCargo~=nil then if self.InitStaticCargo~=nil then
Template.canCargo=self.InitStaticCargo Template.canCargo=self.InitStaticCargo
end end
if self.InitStaticCargoMass~=nil then if self.InitStaticCargoMass~=nil then
Template.mass=self.InitStaticCargoMass Template.mass=self.InitStaticCargoMass
end end
if self.InitLinkUnit then if self.InitLinkUnit then
Template.linkUnit=self.InitLinkUnit:GetID() Template.linkUnit=self.InitLinkUnit:GetID()
Template.linkOffset=true Template.linkOffset=true
@@ -446,45 +446,45 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
Template.offsets.x=self.InitOffsetX Template.offsets.x=self.InitOffsetX
Template.offsets.angle=self.InitOffsetAngle and math.rad(self.InitOffsetAngle) or 0 Template.offsets.angle=self.InitOffsetAngle and math.rad(self.InitOffsetAngle) or 0
end end
if self.InitFarp then if self.InitFarp then
Template.heliport_callsign_id = self.InitFarpCallsignID Template.heliport_callsign_id = self.InitFarpCallsignID
Template.heliport_frequency = self.InitFarpFreq Template.heliport_frequency = self.InitFarpFreq
Template.heliport_modulation = self.InitFarpModu Template.heliport_modulation = self.InitFarpModu
Template.unitId=nil Template.unitId=nil
end end
-- Increase spawn index counter. -- Increase spawn index counter.
self.SpawnIndex = self.SpawnIndex + 1 self.SpawnIndex = self.SpawnIndex + 1
-- Name of the spawned static. -- Name of the spawned static.
Template.name = self.InitStaticName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex) Template.name = self.InitStaticName or string.format("%s#%05d", self.SpawnTemplatePrefix, self.SpawnIndex)
-- Add and register the new static. -- Add and register the new static.
local mystatic=_DATABASE:AddStatic(Template.name) local mystatic=_DATABASE:AddStatic(Template.name)
-- Debug output. -- Debug output.
self:T(Template) self:T(Template)
-- Add static to the game. -- Add static to the game.
local Static=nil --DCS#StaticObject local Static=nil --DCS#StaticObject
if self.InitFarp then if self.InitFarp then
local TemplateGroup={} local TemplateGroup={}
TemplateGroup.units={} TemplateGroup.units={}
TemplateGroup.units[1]=Template TemplateGroup.units[1]=Template
TemplateGroup.visible=true TemplateGroup.visible=true
TemplateGroup.hidden=false TemplateGroup.hidden=false
TemplateGroup.x=Template.x TemplateGroup.x=Template.x
TemplateGroup.y=Template.y TemplateGroup.y=Template.y
TemplateGroup.name=Template.name TemplateGroup.name=Template.name
self:T("Spawning FARP") self:T("Spawning FARP")
self:T({Template=Template}) self:T({Template=Template})
self:T({TemplateGroup=TemplateGroup}) self:T({TemplateGroup=TemplateGroup})
-- ED's dirty way to spawn FARPS. -- ED's dirty way to spawn FARPS.
Static=coalition.addGroup(CountryID, -1, TemplateGroup) Static=coalition.addGroup(CountryID, -1, TemplateGroup)
@@ -499,10 +499,10 @@ function SPAWNSTATIC:_SpawnStatic(Template, CountryID)
world.onEvent(Event) world.onEvent(Event)
else else
self:T("Spawning Static") self:T("Spawning Static")
self:T2({Template=Template}) self:T2({Template=Template})
Static=coalition.addStaticObject(CountryID, Template) Static=coalition.addStaticObject(CountryID, Template)
end end
return mystatic return mystatic
end end

View File

@@ -46,10 +46,6 @@
-- --
-- === -- ===
-- --
-- ### [Demo Missions](https://github.com/FlightControl-Master/MOOSE_Demos/tree/master/Core/Zone)
--
-- ===
--
-- ### Author: **FlightControl** -- ### Author: **FlightControl**
-- ### Contributions: **Applevangelist**, **FunkyFranky**, **coconutcockpit** -- ### Contributions: **Applevangelist**, **FunkyFranky**, **coconutcockpit**
-- --
@@ -330,14 +326,14 @@ function ZONE_BASE:GetRandomVec2()
return nil return nil
end 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 -- @param #ZONE_BASE self
-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. -- @return Core.Point#POINT_VEC2 The PointVec2 coordinates.
function ZONE_BASE:GetRandomPointVec2() function ZONE_BASE:GetRandomPointVec2()
return nil return nil
end 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 -- @param #ZONE_BASE self
-- @return Core.Point#POINT_VEC3 The PointVec3 coordinates. -- @return Core.Point#POINT_VEC3 The PointVec3 coordinates.
function ZONE_BASE:GetRandomPointVec3() function ZONE_BASE:GetRandomPointVec3()
@@ -903,8 +899,7 @@ function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound )
local Point = {} local Point = {}
local Vec2 = self:GetVec2() local Vec2 = self:GetVec2()
local countryID = CountryID or country.id.USA
Points = Points and Points or 360 Points = Points and Points or 360
local Angle local Angle
@@ -915,7 +910,7 @@ function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound )
Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius() Point.x = Vec2.x + math.cos( Radial ) * self:GetRadius()
Point.y = Vec2.y + math.sin( 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 = { local Tire = {
["country"] = CountryName, ["country"] = CountryName,
@@ -930,7 +925,7 @@ function ZONE_RADIUS:BoundZone( Points, CountryID, UnBound )
["heading"] = 0, ["heading"] = 0,
} -- end of ["group"] } -- end of ["group"]
local Group = coalition.addStaticObject( countryID, Tire ) local Group = coalition.addStaticObject( CountryID, Tire )
if UnBound and UnBound == true then if UnBound and UnBound == true then
Group:destroy() Group:destroy()
end end
@@ -1180,7 +1175,7 @@ function ZONE_RADIUS:RemoveJunk()
return n return n
end end
--- Get a table of scanned units. --- Count the number of different coalitions inside the zone.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @return #table Table of DCS units and DCS statics inside the zone. -- @return #table Table of DCS units and DCS statics inside the zone.
function ZONE_RADIUS:GetScannedUnits() function ZONE_RADIUS:GetScannedUnits()
@@ -1215,7 +1210,7 @@ function ZONE_RADIUS:GetScannedSetUnit()
return SetUnit return SetUnit
end end
--- Get a set of scanned groups. --- Get a set of scanned units.
-- @param #ZONE_RADIUS self -- @param #ZONE_RADIUS self
-- @return Core.Set#SET_GROUP Set of groups. -- @return Core.Set#SET_GROUP Set of groups.
function ZONE_RADIUS:GetScannedSetGroup() function ZONE_RADIUS:GetScannedSetGroup()
@@ -1515,7 +1510,7 @@ function ZONE_RADIUS:GetRandomVec2(inner, outer, surfacetypes)
return point return point
end 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 #ZONE_RADIUS self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @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. -- @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 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 #ZONE_RADIUS self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @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. -- @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 return Point
end 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 #ZONE_GROUP self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @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. -- @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
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 -- @param #ZONE_POLYGON_BASE self
-- @return @{Core.Point#POINT_VEC2} -- @return @{Core.Point#POINT_VEC2}
function ZONE_POLYGON_BASE:GetRandomPointVec2() function ZONE_POLYGON_BASE:GetRandomPointVec2()
@@ -2847,7 +2842,7 @@ function ZONE_POLYGON_BASE:GetRandomPointVec2()
return PointVec2 return PointVec2
end 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 -- @param #ZONE_POLYGON_BASE self
-- @return @{Core.Point#POINT_VEC3} -- @return @{Core.Point#POINT_VEC3}
function ZONE_POLYGON_BASE:GetRandomPointVec3() function ZONE_POLYGON_BASE:GetRandomPointVec3()
@@ -3835,18 +3830,18 @@ function ZONE_OVAL:GetRandomVec2()
return {x=rx, y=ry} return {x=rx, y=ry}
end 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 -- @param #ZONE_OVAL self
-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. -- @return Core.Point#POINT_VEC2 The PointVec2 coordinates.
function ZONE_OVAL:GetRandomPointVec2() function ZONE_OVAL:GetRandomPointVec2()
return POINT_VEC2:NewFromVec2(self:GetRandomVec2()) return POINT_VEC2:NewFromVec2(self:GetRandomVec2())
end 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 -- @param #ZONE_OVAL self
-- @return Core.Point#POINT_VEC2 The PointVec2 coordinates. -- @return Core.Point#POINT_VEC2 The PointVec2 coordinates.
function ZONE_OVAL:GetRandomPointVec3() function ZONE_OVAL:GetRandomPointVec3()
return POINT_VEC3:NewFromVec3(self:GetRandomVec2()) return POINT_VEC2:NewFromVec3(self:GetRandomVec2())
end end
--- Draw the zone on the F10 map. --- Draw the zone on the F10 map.
@@ -3986,7 +3981,7 @@ do -- ZONE_AIRBASE
return ZoneVec2 return ZoneVec2
end 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 #ZONE_AIRBASE self
-- @param #number inner (optional) Minimal distance from the center of the zone. Default is 0. -- @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. -- @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 -- @module Functional.Mantis
-- @image Functional.Mantis.jpg -- @image Functional.Mantis.jpg
-- --
-- Last Update: Feb 2024 -- Last Update: Dec 2023
------------------------------------------------------------------------- -------------------------------------------------------------------------
--- **MANTIS** class, extends Core.Base#BASE --- **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 Type #MANTIS.SamType of SAM, i.e. SHORT, MEDIUM or LONG (range)
-- @field #string Radar Radar typename on unit level (used as key) -- @field #string Radar Radar typename on unit level (used as key)
MANTIS.SamData = { MANTIS.SamData = {
["Hawk"] = { Range=35, Blindspot=0, Height=12, Type="Medium", Radar="Hawk" }, -- measures in km ["Hawk"] = { Range=44, Blindspot=0, Height=9, Type="Medium", Radar="Hawk" }, -- measures in km
["NASAMS"] = { Range=14, Blindspot=0, Height=7, Type="Short", Radar="NSAMS" }, -- AIM 120B ["NASAMS"] = { Range=14, Blindspot=0, Height=3, Type="Short", Radar="NSAMS" },
["Patriot"] = { Range=99, Blindspot=0, Height=25, Type="Long", Radar="Patriot" }, ["Patriot"] = { Range=99, Blindspot=0, Height=9, Type="Long", Radar="Patriot" },
["Rapier"] = { Range=10, Blindspot=0, Height=3, Type="Short", Radar="rapier" }, ["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-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-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-5"] = { Range=250, Blindspot=7, Height=40, Type="Long", Radar="5N62V" },
["SA-6"] = { Range=25, Blindspot=0, Height=8, Type="Medium", Radar="1S91" }, ["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-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" }, ["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" }, ["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-9"] = { Range=4, Blindspot=0, Height=3, Type="Short", Radar="Strela" },
["SA-8"] = { Range=10, Blindspot=0, Height=5, Type="Short", Radar="Osa 9A33" }, ["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" }, ["HQ-2"] = { Range=50, Blindspot=6, Height=35, Type="Medium", Radar="HQ_2_Guideline_LN" },
["SHORAD"] = { Range=3, Blindspot=0, Height=3, Type="Short", Radar="Igla" }, ["SHORAD"] = { Range=3, Blindspot=0, Height=3, Type="Short", Radar="Igla" },
["TAMIR IDFA"] = { Range=20, Blindspot=0.6, Height=12.3, Type="Short", Radar="IRON_DOME_LN" }, ["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 --- SAM data HDS
@@ -1222,10 +1222,10 @@ do
function MANTIS:_PreFilterHeight(height) function MANTIS:_PreFilterHeight(height)
self:T(self.lid.."_PreFilterHeight") self:T(self.lid.."_PreFilterHeight")
local set = {} local set = {}
local dlink = self.Detection -- Ops.Intel#INTEL_DLINK local dlink = self.Detection -- Ops.Intelligence#INTEL_DLINK
local detectedgroups = dlink:GetContactTable() local detectedgroups = dlink:GetContactTable()
for _,_contact in pairs(detectedgroups) do 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 local grp = contact.group -- Wrapper.Group#GROUP
if grp:IsAlive() then if grp:IsAlive() then
if grp:GetHeight(true) < height then if grp:GetHeight(true) < height then
@@ -1777,7 +1777,7 @@ do
-- @return #MANTIS self -- @return #MANTIS self
function MANTIS:_CheckDLinkState() function MANTIS:_CheckDLinkState()
self:T(self.lid .. "_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() local TS = timer.getAbsTime()
if not dlink:Is("Running") and (TS - self.DLTimeStamp > 29) then if not dlink:Is("Running") and (TS - self.DLTimeStamp > 29) then
self.DLink = false self.DLink = false

View File

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

View File

@@ -78,8 +78,7 @@
-- ### Authors: **FlightControl** -- ### Authors: **FlightControl**
-- --
-- ### Contributions: -- ### Contributions:
-- --
-- * **Applevangelist**: Additional functionality, fixes.
-- * **Wingthor (TAW)**: Testing & Advice. -- * **Wingthor (TAW)**: Testing & Advice.
-- * **Dutch-Baron (TAW)**: Testing & Advice. -- * **Dutch-Baron (TAW)**: Testing & Advice.
-- * **Whisper**: Testing and 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. -- 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.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 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" ) -- local Scoring = SCORING:New( "Scoring File" )
-- Scoring:AddUnitScore( UNIT:FindByName( "Unit #001" ), 200 ) -- Scoring:AddUnitScore( UNIT:FindByName( "Unit #001" ), 200 )
-- Scoring:AddStaticScore( STATIC:FindByName( "Static #1" ), 100 ) -- 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. -- 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. -- 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, ClassID = 0,
Players = {}, Players = {},
AutoSave = true, AutoSave = true,
version = "1.18.4" version = "1.17.1"
} }
local _SCORINGCoalition = { local _SCORINGCoalition = {
@@ -248,15 +245,13 @@ local _SCORINGCategory = {
--- Creates a new SCORING object to administer the scoring achieved by players. --- Creates a new SCORING object to administer the scoring achieved by players.
-- @param #SCORING self -- @param #SCORING self
-- @param #string GameName The name of the game. This name is also logged in the CSV score file. -- @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 -- @return #SCORING self
-- @usage -- @usage
-- --
-- -- Define a new scoring object for the mission Gori Valley. -- -- Define a new scoring object for the mission Gori Valley.
-- ScoringObject = SCORING:New( "Gori Valley" ) -- ScoringObject = SCORING:New( "Gori Valley" )
-- --
function SCORING:New( GameName, SavePath, AutoSave ) function SCORING:New( GameName )
-- Inherits from BASE -- Inherits from BASE
local self = BASE:Inherit( self, BASE:New() ) -- #SCORING local self = BASE:Inherit( self, BASE:New() ) -- #SCORING
@@ -319,8 +314,7 @@ function SCORING:New( GameName, SavePath, AutoSave )
end ) end )
-- Create the CSV file. -- Create the CSV file.
self.AutoSavePath = SavePath self.AutoSave = true
self.AutoSave = AutoSave or true
self:OpenCSV( GameName ) self:OpenCSV( GameName )
return self return self
@@ -434,31 +428,6 @@ function SCORING:AddScoreGroup( ScoreGroup, Score )
return self return self
end 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. --- 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! -- 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. -- 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.PenaltyHit = PlayerHit.PenaltyHit or 0
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0 PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT 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 if PlayerHit.UNIT.ThreatType == nil then
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel() PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
-- if this fails for some reason, set a good default value -- 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.ThreatLevel = 1
PlayerHit.ThreatType = "Unknown" PlayerHit.ThreatType = "Unknown"
end end
@@ -1172,7 +1141,7 @@ function SCORING:_EventOnHit( Event )
PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0 PlayerHit.PenaltyHit = PlayerHit.PenaltyHit or 0
PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0 PlayerHit.TimeStamp = PlayerHit.TimeStamp or 0
PlayerHit.UNIT = PlayerHit.UNIT or TargetUNIT 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 if PlayerHit.UNIT.ThreatType == nil then
PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel() PlayerHit.ThreatLevel, PlayerHit.ThreatType = PlayerHit.UNIT:GetThreatLevel()
-- if this fails for some reason, set a good default value -- if this fails for some reason, set a good default value
@@ -1319,17 +1288,17 @@ function SCORING:_EventOnDeadOrCrash( Event )
TargetDestroy.PenaltyDestroy = TargetDestroy.PenaltyDestroy + 1 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 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 .. " ) " .. MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Penalty: -" .. ThreatPenalty .. " = " .. Player.Score - Player.Penalty, "Penalty: -" .. ThreatPenalty .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information ) MESSAGE.Type.Information )
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
else 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 .. " ) " .. MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed friendly target " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Penalty: -" .. ThreatPenalty .. " = " .. Player.Score - Player.Penalty, "Penalty: -" .. ThreatPenalty .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information ) MESSAGE.Type.Information )
@@ -1357,14 +1326,14 @@ function SCORING:_EventOnDeadOrCrash( Event )
else else
Player.PlayerKills = 1 Player.PlayerKills = 1
end 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 .. " ) " .. MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy player '" .. TargetPlayerName .. "' " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Score: +" .. ThreatScore .. " = " .. Player.Score - Player.Penalty, "Score: +" .. ThreatScore .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information ) MESSAGE.Type.Information )
:ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() ) :ToAllIf( self:IfMessagesDestroy() and self:IfMessagesToAll() )
:ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() ) :ToCoalitionIf( InitCoalition, self:IfMessagesDestroy() and self:IfMessagesToCoalition() )
else 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 .. " ) " .. MESSAGE:NewType( self.DisplayMessagePrefix .. "Player '" .. PlayerName .. "' destroyed enemy " .. TargetUnitCategory .. " ( " .. ThreatTypeTarget .. " ) " ..
"Score: +" .. ThreatScore .. " = " .. Player.Score - Player.Penalty, "Score: +" .. ThreatScore .. " = " .. Player.Score - Player.Penalty,
MESSAGE.Type.Information ) MESSAGE.Type.Information )
@@ -1842,11 +1811,10 @@ end
function SCORING:OpenCSV( ScoringCSV ) function SCORING:OpenCSV( ScoringCSV )
self:F( 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 if ScoringCSV then
self.ScoringCSV = ScoringCSV self.ScoringCSV = ScoringCSV
local path = self.AutoSavePath or lfs.writedir() .. [[Logs\]] local fdir = lfs.writedir() .. [[Logs\]] .. self.ScoringCSV .. " " .. os.date( "%Y-%m-%d %H-%M-%S" ) .. ".csv"
local fdir = path .. self.ScoringCSV .. " " .. os.date( "%Y-%m-%d %H-%M-%S" ) .. ".csv"
self.CSVFile, self.err = io.open( fdir, "w+" ) self.CSVFile, self.err = io.open( fdir, "w+" )
if not self.CSVFile then if not self.CSVFile then
@@ -1967,23 +1935,23 @@ end
--- Handles the event when one player kill another player --- Handles the event when one player kill another player
-- @param #SCORING self -- @param #SCORING self
-- @param #string PlayerName The attacking player -- @param #PLAYER Player the ataching player
-- @param #string TargetPlayerName The name of the killed player -- @param #string TargetPlayerName the name of the killed player
-- @param #boolean IsTeamKill true if this kill was a team kill -- @param #bool IsTeamKill true if this kill was a team kill
-- @param #number TargetThreatLevel Threat level of the target -- @param #number TargetThreatLevel Thread level of the target
-- @param #number PlayerThreatLevel Threat level of the player -- @param #number PlayerThreatLevelThread level of the player
-- @param #number Score The score based on both threat levels -- @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 end
--- Handles the event when one player kill another player --- Handles the event when one player kill another player
-- @param #SCORING self -- @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 #string TargetUnitName the name of the killed unit
-- @param #boolean IsTeamKill true if this kill was a team kill -- @param #bool IsTeamKill true if this kill was a team kill
-- @param #number TargetThreatLevel Threat level of the target -- @param #number TargetThreatLevel Thread level of the target
-- @param #number PlayerThreatLevel Threat level of the player -- @param #number PlayerThreatLevelThread level of the player
-- @param #number Score The score based on both threat levels -- @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 end
local seadset = SET_GROUP:New():FilterPrefixes(self.SEADGroupPrefixes):FilterZones({targetzone}):FilterOnce() 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 tgtgrp = seadset:GetRandom()
local _targetgroup = nil local _targetgroup = nil
local _targetgroupname = "none" 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 #boolean arrived If true, asset arrived at its destination.
-- --
-- @field #number damage Damage of asset group in percent. -- @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.OpsGroup#OPSGROUP flightgroup The flightgroup object.
-- @field Ops.Cohort#COHORT cohort The cohort this asset belongs to. -- @field Ops.Cohort#COHORT cohort The cohort this asset belongs to.
-- @field Ops.Legion#LEGION legion The legion this asset belonts to. -- @field Ops.Legion#LEGION legion The legion this asset belonts to.
@@ -3414,7 +3414,7 @@ end
-- FSM states -- 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 #WAREHOUSE self
-- @param #string From From state. -- @param #string From From state.
-- @param #string Event Event. -- @param #string Event Event.
@@ -3595,7 +3595,6 @@ function WAREHOUSE:onafterStatus(From, Event, To)
local Trepair=self:GetRunwayRepairtime() local Trepair=self:GetRunwayRepairtime()
self:I(self.lid..string.format("Runway destroyed! Will be repaired in %d sec", Trepair)) self:I(self.lid..string.format("Runway destroyed! Will be repaired in %d sec", Trepair))
if Trepair==0 then if Trepair==0 then
self.runwaydestroyed = nil
self:RunwayRepaired() self:RunwayRepaired()
end end
end end
@@ -5393,8 +5392,7 @@ function WAREHOUSE:onafterRunwayDestroyed(From, Event, To)
self:_InfoMessage(text) self:_InfoMessage(text)
self.runwaydestroyed=timer.getAbsTime() self.runwaydestroyed=timer.getAbsTime()
return self
end end
--- On after "RunwayRepaired" event. --- On after "RunwayRepaired" event.
@@ -5409,8 +5407,7 @@ function WAREHOUSE:onafterRunwayRepaired(From, Event, To)
self:_InfoMessage(text) self:_InfoMessage(text)
self.runwaydestroyed=nil self.runwaydestroyed=nil
return self
end 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/MarkerOps_Base.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Core/TextAndSound.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/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/Object.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Wrapper/Identifiable.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/Fox.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Functional/Mantis.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/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/Airboss.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/RecoveryTanker.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/ATIS.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/Ops/CTLD.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/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_Balancer.lua' )
__Moose.Include( MOOSE_DEVELOPMENT_FOLDER..'/Moose/AI/AI_Air.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:SetVolume(Volume or 1)
--self.SRS:SetModulations(Modulations) --self.SRS:SetModulations(Modulations)
if GoogleCreds then if GoogleCreds then
self.SRS:SetProviderOptionsGoogle(GoogleCreds,GoogleCreds) self.SRS:SetGoogle(GoogleCreds)
self.SRS:SetProvider(MSRS.Provider.GOOGLE)
end end
if Voice then if Voice then
self.SRS:SetVoice(Voice) self.SRS:SetVoice(Voice)
@@ -9755,7 +9754,7 @@ function AIRBOSS:_Groove( playerData )
local glideslopeError = groovedata.GSE local glideslopeError = groovedata.GSE
local AoA = groovedata.AoA 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 -- Start time in groove
playerData.TIG0 = timer.getTime() playerData.TIG0 = timer.getTime()
@@ -12123,18 +12122,16 @@ function AIRBOSS:_LSOgrade( playerData )
local GIC, nIC = self:_Flightdata2Text( playerData, AIRBOSS.GroovePos.IC ) local GIC, nIC = self:_Flightdata2Text( playerData, AIRBOSS.GroovePos.IC )
local GAR, nAR = self:_Flightdata2Text( playerData, AIRBOSS.GroovePos.AR ) 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. -- Put everything together.
local G = GXX .. " " .. GIM .. " " .. " " .. GIC .. " " .. GAR 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 N=nXX+nIM+nIC+nAR
local Nv=nXX+nIM
local nL=count(G, '_')/2 local nL=count(G, '_')/2
local nS=count(G, '%(') local nS=count(G, '%(')
local nN=N-nS-nL 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. -- Groove time 15-18.99 sec for a unicorn. Or 60-65 for V/STOL unicorn.
local Tgroove=playerData.Tgroove local Tgroove=playerData.Tgroove
@@ -12150,64 +12147,34 @@ function AIRBOSS:_LSOgrade( playerData )
G = "Unicorn" G = "Unicorn"
else 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. -- 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 end
-- Replace" )"( and "__" -- 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["UH-60L"] = 10
CSAR.AircraftType["AH-64D_BLK_II"] = 2 CSAR.AircraftType["AH-64D_BLK_II"] = 2
CSAR.AircraftType["Bronco-OV-10A"] = 2 CSAR.AircraftType["Bronco-OV-10A"] = 2
CSAR.AircraftType["MH-60R"] = 10
--- CSAR class version. --- CSAR class version.
-- @field #string version -- @field #string version
CSAR.version="1.0.20" CSAR.version="1.0.19"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list -- ToDo list

View File

@@ -24,7 +24,7 @@
-- @module Ops.CTLD -- @module Ops.CTLD
-- @image OPS_CTLD.jpg -- @image OPS_CTLD.jpg
-- Last Update March 2024 -- Last Update December 2023
do do
@@ -44,8 +44,6 @@ do
-- @field #number PerCrateMass Mass in kg. -- @field #number PerCrateMass Mass in kg.
-- @field #number Stock Number of builds available, -1 for unlimited. -- @field #number Stock Number of builds available, -1 for unlimited.
-- @field #string Subcategory Sub-category name. -- @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 -- @extends Core.Base#BASE
--- ---
@@ -64,8 +62,6 @@ CTLD_CARGO = {
PerCrateMass = 0, PerCrateMass = 0,
Stock = nil, Stock = nil,
Mark = nil, Mark = nil,
DontShowInMenu = false,
Location = nil,
} }
--- Define cargo types. --- Define cargo types.
@@ -101,10 +97,8 @@ CTLD_CARGO = {
-- @param #number PerCrateMass Mass in kg -- @param #number PerCrateMass Mass in kg
-- @param #number Stock Number of builds available, nil for unlimited -- @param #number Stock Number of builds available, nil for unlimited
-- @param #string Subcategory Name of subcategory, handy if using > 10 types to load. -- @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 -- @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. -- Inherit everything from BASE class.
local self=BASE:Inherit(self, BASE:New()) -- #CTLD_CARGO local self=BASE:Inherit(self, BASE:New()) -- #CTLD_CARGO
self:T({ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped}) self:T({ID, Name, Templates, Sorte, HasBeenMoved, LoadDirectly, CratesNeeded, Positionable, Dropped})
@@ -121,20 +115,8 @@ CTLD_CARGO = {
self.Stock = Stock or nil --#number self.Stock = Stock or nil --#number
self.Mark = nil self.Mark = nil
self.Subcategory = Subcategory or "Other" 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 return self
end 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. --- Query ID.
-- @param #CTLD_CARGO self -- @param #CTLD_CARGO self
@@ -674,8 +656,6 @@ do
-- my_ctld:AddCratesCargo("Humvee",{"Humvee"},CTLD_CARGO.Enum.VEHICLE,2,2775) -- 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. -- -- 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) -- 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: -- -- 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) -- 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}, -- ["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}, -- ["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}, -- ["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 -- ### 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. ["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. --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 ["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 ["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}, ["Bronco-OV-10A"] = {type="Bronco-OV-10A", crates= false, troops=true, cratelimit = 0, trooplimit = 5, length = 13, cargoweightlimit = 1450},
} }
--- CTLD class version. --- CTLD class version.
-- @field #string version -- @field #string version
CTLD.version="1.0.50" CTLD.version="1.0.45"
--- Instantiate a new CTLD. --- Instantiate a new CTLD.
-- @param #CTLD self -- @param #CTLD self
@@ -1458,7 +1433,7 @@ function CTLD:New(Coalition, Prefixes, Alias)
--- Pseudo Functions --- --- 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 -- @function [parent=#CTLD] Start
-- @param #CTLD self -- @param #CTLD self
@@ -2260,7 +2235,7 @@ end
local secondarygroups = {} local secondarygroups = {}
for i=1,#distancekeys do for i=1,#distancekeys do
local nearestGroup = nearestList[distancekeys[i]] -- Wrapper.Group#GROUP local nearestGroup = nearestList[distancekeys[i]]
-- find matching cargo type -- find matching cargo type
local groupType = string.match(nearestGroup:GetName(), "(.+)-(.+)$") local groupType = string.match(nearestGroup:GetName(), "(.+)-(.+)$")
local Cargotype = nil local Cargotype = nil
@@ -2296,31 +2271,20 @@ end
self.CargoCounter = self.CargoCounter + 1 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) 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}) self:T({cargotype=loadcargotype})
local running = math.floor(nearestDistance / 4)+10 -- time run to helo plus boarding
loaded.Troopsloaded = loaded.Troopsloaded + troopsize loaded.Troopsloaded = loaded.Troopsloaded + troopsize
table.insert(loaded.Cargo,loadcargotype) table.insert(loaded.Cargo,loadcargotype)
self.Loaded_Cargo[unitname] = loaded self.Loaded_Cargo[unitname] = loaded
self:ScheduleOnce(running,self._SendMessage,self,"Troops boarded!", 10, false, Group) self:_SendMessage("Troops boarded!", 10, false, Group)
self:_SendMessage("Troops boarding!", 10, false, Group)
self:_UpdateUnitCargoMass(Unit) self:_UpdateUnitCargoMass(Unit)
self:__TroopsExtracted(running,Group, Unit, nearestGroup) self:__TroopsExtracted(1,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
-- clean up: -- clean up:
if type(Cargotype.Templates) == "table" and Cargotype.Templates[2] then if type(Cargotype.Templates) == "table" and Cargotype.Templates[2] then
for _,_key in pairs (Cargotype.Templates) do for _,_key in pairs (Cargotype.Templates) do
table.insert(secondarygroups,_key) table.insert(secondarygroups,_key)
end end
end end
nearestGroup:Destroy(false,running) nearestGroup:Destroy(false)
end end
end end
end end
@@ -2330,7 +2294,7 @@ end
if _group and _group:IsAlive() then if _group and _group:IsAlive() then
local groupname = string.match(_group:GetName(), "(.+)-(.+)$") local groupname = string.match(_group:GetName(), "(.+)-(.+)$")
if _name == groupname then if _name == groupname then
_group:Destroy(false,15) _group:Destroy(false)
end end
end 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) self:_SendMessage("You are not close enough to a logistics zone!", 10, false, Group)
if not self.debug then return self end if not self.debug then return self end
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 -- avoid crate spam
local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitTypeCapabilities local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitTypeCapabilities
local canloadcratesno = capabilities.cratelimit local canloadcratesno = capabilities.cratelimit
@@ -3074,10 +3024,9 @@ end
function CTLD:_GetUnitPositions(Coordinate,Radius,Heading,Template) function CTLD:_GetUnitPositions(Coordinate,Radius,Heading,Template)
local Positions = {} local Positions = {}
local template = _DATABASE:GetGroupTemplate(Template) local template = _DATABASE:GetGroupTemplate(Template)
--UTILS.PrintTableToLog(template) UTILS.PrintTableToLog(template)
local numbertroops = #template.units local numbertroops = #template.units
local slightshift = math.abs(math.random(0,200)/100) local newcenter = Coordinate:Translate(Radius,((Heading+270)%360))
local newcenter = Coordinate:Translate(Radius+slightshift,((Heading+270)%360))
for i=1,360,math.floor(360/numbertroops) do for i=1,360,math.floor(360/numbertroops) do
local phead = ((Heading+270+i)%360) local phead = ((Heading+270+i)%360)
local post = newcenter:Translate(Radius,phead) local post = newcenter:Translate(Radius,phead)
@@ -3089,7 +3038,7 @@ function CTLD:_GetUnitPositions(Coordinate,Radius,Heading,Template)
} }
table.insert(Positions,p1t) table.insert(Positions,p1t)
end end
--UTILS.PrintTableToLog(Positions) UTILS.PrintTableToLog(Positions)
return Positions return Positions
end end
@@ -3751,20 +3700,14 @@ function CTLD:_RefreshF10Menus()
for _,_entry in pairs(self.Cargo_Troops) do for _,_entry in pairs(self.Cargo_Troops) do
local entry = _entry -- #CTLD_CARGO local entry = _entry -- #CTLD_CARGO
local subcat = entry.Subcategory local subcat = entry.Subcategory
local noshow = entry.DontShowInMenu menucount = menucount + 1
if not noshow then menus[menucount] = MENU_GROUP_COMMAND:New(_group,entry.Name,subcatmenus[subcat],self._LoadTroops, self, _group, _unit, entry)
menucount = menucount + 1
menus[menucount] = MENU_GROUP_COMMAND:New(_group,entry.Name,subcatmenus[subcat],self._LoadTroops, self, _group, _unit, entry)
end
end end
else else
for _,_entry in pairs(self.Cargo_Troops) do for _,_entry in pairs(self.Cargo_Troops) do
local entry = _entry -- #CTLD_CARGO local entry = _entry -- #CTLD_CARGO
local noshow = entry.DontShowInMenu menucount = menucount + 1
if not noshow then menus[menucount] = MENU_GROUP_COMMAND:New(_group,entry.Name,troopsmenu,self._LoadTroops, self, _group, _unit, entry)
menucount = menucount + 1
menus[menucount] = MENU_GROUP_COMMAND:New(_group,entry.Name,troopsmenu,self._LoadTroops, self, _group, _unit, entry)
end
end end
end end
local unloadmenu1 = MENU_GROUP_COMMAND:New(_group,"Drop troops",toptroops, self._UnloadTroops, self, _group, _unit):Refresh() local unloadmenu1 = MENU_GROUP_COMMAND:New(_group,"Drop troops",toptroops, self._UnloadTroops, self, _group, _unit):Refresh()
@@ -3785,61 +3728,33 @@ function CTLD:_RefreshF10Menus()
for _,_entry in pairs(self.Cargo_Crates) do for _,_entry in pairs(self.Cargo_Crates) do
local entry = _entry -- #CTLD_CARGO local entry = _entry -- #CTLD_CARGO
local subcat = entry.Subcategory local subcat = entry.Subcategory
local noshow = entry.DontShowInMenu menucount = menucount + 1
local zone = entry.Location local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
if not noshow then menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates, self, _group, _unit, entry)
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
end end
for _,_entry in pairs(self.Cargo_Statics) do for _,_entry in pairs(self.Cargo_Statics) do
local entry = _entry -- #CTLD_CARGO local entry = _entry -- #CTLD_CARGO
local subcat = entry.Subcategory local subcat = entry.Subcategory
local noshow = entry.DontShowInMenu menucount = menucount + 1
local zone = entry.Location local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
if not noshow then menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates, self, _group, _unit, entry)
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
end end
else else
for _,_entry in pairs(self.Cargo_Crates) do for _,_entry in pairs(self.Cargo_Crates) do
local entry = _entry -- #CTLD_CARGO local entry = _entry -- #CTLD_CARGO
local noshow = entry.DontShowInMenu menucount = menucount + 1
local zone = entry.Location local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
if not noshow then menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry)
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
end end
for _,_entry in pairs(self.Cargo_Statics) do for _,_entry in pairs(self.Cargo_Statics) do
local entry = _entry -- #CTLD_CARGO local entry = _entry -- #CTLD_CARGO
local noshow = entry.DontShowInMenu menucount = menucount + 1
local zone = entry.Location local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
if not noshow then menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry)
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
end end
end end
listmenu = MENU_GROUP_COMMAND:New(_group,"List crates nearby",topcrates, self._ListCratesNearby, self, _group, _unit) 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) local unloadmenu = MENU_GROUP_COMMAND:New(_group,"Drop crates",topcrates, self._UnloadCrates, self, _group, _unit)
if not self.nobuildmenu then if not self.nobuildmenu then
local buildmenu = MENU_GROUP_COMMAND:New(_group,"Build crates",topcrates, self._BuildCrates, self, _group, _unit) 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 PerCrateMass Mass in kg of each crate
-- @param #number Stock Number of buildable groups in stock. Nil for unlimited. -- @param #number Stock Number of buildable groups in stock. Nil for unlimited.
-- @param #string SubCategory Name of sub-category (optional). -- @param #string SubCategory Name of sub-category (optional).
-- @param #boolean DontShowInMenu (optional) If set to "true" this won't show up in the menu. function CTLD:AddCratesCargo(Name,Templates,Type,NoCrates,PerCrateMass,Stock,SubCategory)
-- @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)
self:T(self.lid .. " AddCratesCargo") self:T(self.lid .. " AddCratesCargo")
if not self:_CheckTemplates(Templates) then if not self:_CheckTemplates(Templates) then
self:E(self.lid .. "Crates Cargo for " .. Name .. " has missing template(s)!" ) 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 end
self.CargoCounter = self.CargoCounter + 1 self.CargoCounter = self.CargoCounter + 1
-- Crates are not directly loadable -- 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) table.insert(self.Cargo_Crates,cargo)
return self return self
end end
@@ -3935,15 +3848,13 @@ end
-- @param #number Mass Mass in kg of each static in kg, e.g. 100. -- @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 #number Stock Number of groups in stock. Nil for unlimited.
-- @param #string SubCategory Name of sub-category (optional). -- @param #string SubCategory Name of sub-category (optional).
-- @param #boolean DontShowInMenu (optional) If set to "true" this won't show up in the menu. function CTLD:AddStaticsCargo(Name,Mass,Stock,SubCategory)
-- @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)
self:T(self.lid .. " AddStaticsCargo") self:T(self.lid .. " AddStaticsCargo")
self.CargoCounter = self.CargoCounter + 1 self.CargoCounter = self.CargoCounter + 1
local type = CTLD_CARGO.Enum.STATIC local type = CTLD_CARGO.Enum.STATIC
local template = STATIC:FindByName(Name,true):GetTypeName() local template = STATIC:FindByName(Name,true):GetTypeName()
-- Crates are not directly loadable -- 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) table.insert(self.Cargo_Statics,cargo)
return self return self
end end
@@ -3973,9 +3884,7 @@ end
-- @param #number PerCrateMass Mass in kg of each crate -- @param #number PerCrateMass Mass in kg of each crate
-- @param #number Stock Number of groups in stock. Nil for unlimited. -- @param #number Stock Number of groups in stock. Nil for unlimited.
-- @param #string SubCategory Name of the sub-category (optional). -- @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. function CTLD:AddCratesRepair(Name,Template,Type,NoCrates, PerCrateMass,Stock,SubCategory)
-- @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)
self:T(self.lid .. " AddCratesRepair") self:T(self.lid .. " AddCratesRepair")
if not self:_CheckTemplates(Template) then if not self:_CheckTemplates(Template) then
self:E(self.lid .. "Repair Cargo for " .. Name .. " has a missing template!" ) 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 end
self.CargoCounter = self.CargoCounter + 1 self.CargoCounter = self.CargoCounter + 1
-- Crates are not directly loadable -- 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) table.insert(self.Cargo_Crates,cargo)
return self return self
end end
@@ -5448,19 +5357,19 @@ end
return self return self
end end
--- (Internal) FSM Function onbeforeTroopsExtracted. --- (Internal) FSM Function onbeforeTroopsExtracted.
-- @param #CTLD self -- @param #CTLD self
-- @param #string From State. -- @param #string From State.
-- @param #string Event Trigger. -- @param #string Event Trigger.
-- @param #string To State. -- @param #string To State.
-- @param Wrapper.Group#GROUP Group Group Object. -- @param Wrapper.Group#GROUP Group Group Object.
-- @param Wrapper.Unit#UNIT Unit Unit Object. -- @param Wrapper.Unit#UNIT Unit Unit Object.
-- @param Wrapper.Group#GROUP Troops Troops #GROUP Object. -- @param Wrapper.Group#GROUP Troops Troops #GROUP Object.
-- @return #CTLD self -- @return #CTLD self
function CTLD:onbeforeTroopsExtracted(From, Event, To, Group, Unit, Troops) function CTLD:onbeforeTroopsExtracted(From, Event, To, Group, Unit, Troops)
self:T({From, Event, To}) self:T({From, Event, To})
return self return self
end end
--- (Internal) FSM Function onbeforeTroopsDeployed. --- (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 -- ### Authors: Hugues "Grey_Echo" Bousquet, funkyfranky
-- --
-- @module Sound.Radio -- @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:F( {Voice=Voice, Provider=Provider} )
self.poptions=self.poptions or {} self.poptions=self.poptions or {}
self.poptions[Provider or self:GetProvider()].voice=Voice self.poptions[Provider or self:GetProvider()]=Voice
return self return self
end end

View File

@@ -444,11 +444,10 @@ end
--- Print a table to log in a nice format --- Print a table to log in a nice format
-- @param #table table The table to print -- @param #table table The table to print
-- @param #number indent Number of indents -- @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 -- @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" local text = "\n"
if not table or type(table) ~= "table" then if not table then
env.warning("No table passed!") env.warning("No table passed!")
return nil return nil
end end
@@ -456,16 +455,11 @@ function UTILS.PrintTableToLog(table, indent, noprint)
for k, v in pairs(table) do for k, v in pairs(table) do
if string.find(k," ") then k='"'..k..'"'end if string.find(k," ") then k='"'..k..'"'end
if type(v) == "table" then if type(v) == "table" then
if not noprint then env.info(string.rep(" ", indent) .. tostring(k) .. " = {")
env.info(string.rep(" ", indent) .. tostring(k) .. " = {")
end
text = text ..string.rep(" ", indent) .. tostring(k) .. " = {\n" text = text ..string.rep(" ", indent) .. tostring(k) .. " = {\n"
text = text .. tostring(UTILS.PrintTableToLog(v, indent + 1)).."\n" text = text .. tostring(UTILS.PrintTableToLog(v, indent + 1)).."\n"
if not noprint then env.info(string.rep(" ", indent) .. "},")
env.info(string.rep(" ", indent) .. "},")
end
text = text .. string.rep(" ", indent) .. "},\n" text = text .. string.rep(" ", indent) .. "},\n"
elseif type(v) == "function" then
else else
local value local value
if tostring(v) == "true" or tostring(v) == "false" or tonumber(v) ~= nil then if tostring(v) == "true" or tostring(v) == "false" or tonumber(v) ~= nil then
@@ -473,9 +467,7 @@ function UTILS.PrintTableToLog(table, indent, noprint)
else else
value = '"'..tostring(v)..'"' value = '"'..tostring(v)..'"'
end end
if not noprint then env.info(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n")
env.info(string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n")
end
text = text .. string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n" text = text .. string.rep(" ", indent) .. tostring(k) .. " = " .. tostring(value)..",\n"
end end
end end
@@ -833,64 +825,6 @@ UTILS.tostringLL = function( lat, lon, acc, DMS)
end end
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. -- acc- the accuracy of each easting/northing. 0, 1, 2, 3, 4, or 5.
UTILS.tostringMGRS = function(MGRS, acc) --R2.1 UTILS.tostringMGRS = function(MGRS, acc) --R2.1
@@ -2237,11 +2171,6 @@ function UTILS.IsLoadingDoorOpen( unit_name )
return true -- no doors on this one ;) return true -- no doors on this one ;)
end 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 return false
end -- nil end -- nil
@@ -3730,116 +3659,3 @@ end
function UTILS.OctalToDecimal(Number) function UTILS.OctalToDecimal(Number)
return tonumber(Number,8) return tonumber(Number,8)
end 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. --- Check if SCENERY Object is alive.
--@param #SCENERY self --@param #SCENERY self
--@param #number Threshold (Optional) If given, SCENERY counts as alive above this relative life in percent (1..100).
--@return #number life --@return #number life
function SCENERY:IsAlive(Threshold) function SCENERY:IsAlive()
if not Threshold then return self:GetLife() >= 1 and true or false
return self:GetLife() >= 1 and true or false
else
return self:GetRelativeLife() > Threshold and true or false
end
end end
--- Check if SCENERY Object is dead. --- Check if SCENERY Object is dead.
--@param #SCENERY self --@param #SCENERY self
--@param #number Threshold (Optional) If given, SCENERY counts as dead below this relative life in percent (1..100).
--@return #number life --@return #number life
function SCENERY:IsDead(Threshold) function SCENERY:IsDead()
if not Threshold then return self:GetLife() < 1 and true or false
return self:GetLife() < 1 and true or false
else
return self:GetRelativeLife() <= Threshold and true or false
end
end 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. --- Get the threat level of a SCENERY object. Always 0 as scenery does not pose a threat to anyone.
--@param #SCENERY self --@param #SCENERY self
--@return #number Threat level 0. --@return #number Threat level 0.

View File

@@ -12,8 +12,7 @@
-- @image Wrapper_Static.JPG -- @image Wrapper_Static.JPG
--- --- @type STATIC
-- @type STATIC
-- @extends Wrapper.Positionable#POSITIONABLE -- @extends Wrapper.Positionable#POSITIONABLE
--- Wrapper class to handle Static objects. --- Wrapper class to handle Static objects.
@@ -237,7 +236,7 @@ function STATIC:SpawnAt(Coordinate, Heading, Delay)
end 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. -- This is useful to respawn a cargo after it has been destroyed.
-- @param #STATIC self -- @param #STATIC self
-- @param DCS#country.id CountryID (Optional) The country ID used for spawning the new static. Default is same as currently. -- @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 else
CountryID=CountryID or self:GetCountry() CountryID=CountryID or self:GetCountry()
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, CountryID) local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, CountryID)
SpawnStatic:Spawn(nil, self.StaticName) SpawnStatic:Spawn(nil, self.StaticName)
@@ -271,8 +270,8 @@ function STATIC:ReSpawnAt(Coordinate, Heading, Delay)
if Delay and Delay>0 then if Delay and Delay>0 then
SCHEDULER:New(nil, self.ReSpawnAt, {self, Coordinate, Heading}, Delay) SCHEDULER:New(nil, self.ReSpawnAt, {self, Coordinate, Heading}, Delay)
else else
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, self:GetCountry()) local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName, self:GetCountry())
SpawnStatic:SpawnFromCoordinate(Coordinate, Heading, self.StaticName) SpawnStatic:SpawnFromCoordinate(Coordinate, Heading, self.StaticName)
@@ -281,52 +280,3 @@ function STATIC:ReSpawnAt(Coordinate, Heading, Delay)
return self return self
end 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: -- ## 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 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. -- 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. -- This class allows you to add and remove items to a DCS warehouse, such as aircraft, liquids, weapons and other equipment.
-- --
-- # Constructor -- # 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. -- 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 -- You can get the `STORAGE` object from the
-- --
-- -- Create a STORAGE instance of the Batumi warehouse -- -- Create a STORAGE instance of the Batumi warehouse
-- local storage=STORAGE:FindByName("Batumi") -- local storage=STORAGE:FindByName("Batumi")
-- --
-- An other way to get the `STORAGE` object is to retrieve it from the AIRBASE function `AIRBASE:GetStorage()` -- An other way to get the `STORAGE` object is to retrieve it from the AIRBASE function `AIRBASE:GetStorage()`
-- --
-- -- Get storage instance of Batumi airbase -- -- Get storage instance of Batumi airbase
-- local Batumi=AIRBASE:FindByName("Batumi") -- local Batumi=AIRBASE:FindByName("Batumi")
-- local storage=Batumi:GetStorage() -- local storage=Batumi:GetStorage()
-- --
-- # Aircraft, Weapons and Equipment -- # Aircraft, Weapons and Equipment
-- --
-- ## Adding Items -- ## Adding Items
-- --
-- To add aircraft, weapons and/or othe equipment, you can use the @{#STORAGE.AddItem}() function -- To add aircraft, weapons and/or othe equipment, you can use the @{#STORAGE.AddItem}() function
-- --
-- storage:AddItem("A-10C", 3) -- storage:AddItem("A-10C", 3)
-- storage:AddItem("weapons.missiles.AIM_120C", 10) -- storage:AddItem("weapons.missiles.AIM_120C", 10)
-- --
-- This will add three A-10Cs and ten AIM-120C missiles to the warehouse inventory. -- This will add three A-10Cs and ten AIM-120C missiles to the warehouse inventory.
-- --
-- ## Setting Items -- ## Setting Items
-- --
-- You can also explicitly set, how many items are in the inventory with the @{#STORAGE.SetItem}() function. -- You can also explicitly set, how many items are in the inventory with the @{#STORAGE.SetItem}() function.
-- --
-- ## Removing Items -- ## Removing Items
-- --
-- Items can be removed from the inventory with the @{#STORAGE.RemoveItem}() function. -- Items can be removed from the inventory with the @{#STORAGE.RemoveItem}() function.
-- --
-- ## Getting Amount -- ## Getting Amount
-- --
-- The number of items currently in the inventory can be obtained with the @{#STORAGE.GetItemAmount}() function -- The number of items currently in the inventory can be obtained with the @{#STORAGE.GetItemAmount}() function
-- --
-- local N=storage:GetItemAmount("A-10C") -- local N=storage:GetItemAmount("A-10C")
-- env.info(string.format("We currently have %d A-10Cs available", N)) -- env.info(string.format("We currently have %d A-10Cs available", N))
-- --
-- # Liquids -- # Liquids
-- --
-- Liquids can be added and removed by slightly different functions as described below. Currently there are four types of 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` -- * Jet fuel `STORAGE.Liquid.JETFUEL`
-- * Aircraft gasoline `STORAGE.Liquid.GASOLINE` -- * Aircraft gasoline `STORAGE.Liquid.GASOLINE`
-- * MW 50 `STORAGE.Liquid.MW50` -- * MW 50 `STORAGE.Liquid.MW50`
-- * Diesel `STORAGE.Liquid.DIESEL` -- * Diesel `STORAGE.Liquid.DIESEL`
-- --
-- ## Adding Liquids -- ## Adding Liquids
-- --
-- To add a certain type of liquid, you can use the @{#STORAGE.AddItem}(Type, Amount) function -- 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.JETFUEL, 10000)
-- storage:AddLiquid(STORAGE.Liquid.DIESEL, 20000) -- storage:AddLiquid(STORAGE.Liquid.DIESEL, 20000)
-- --
-- This will add 10,000 kg of jet fuel and 20,000 kg of diesel to the inventory. -- This will add 10,000 kg of jet fuel and 20,000 kg of diesel to the inventory.
-- --
-- ## Setting Liquids -- ## Setting Liquids
-- --
-- You can also explicitly set the amount of liquid with the @{#STORAGE.SetLiquid}(Type, Amount) function. -- You can also explicitly set the amount of liquid with the @{#STORAGE.SetLiquid}(Type, Amount) function.
-- --
-- ## Removing Liquids -- ## Removing Liquids
-- --
-- Liquids can be removed with @{#STORAGE.RemoveLiquid}(Type, Amount) function. -- Liquids can be removed with @{#STORAGE.RemoveLiquid}(Type, Amount) function.
-- --
-- ## Getting Amount -- ## Getting Amount
-- --
-- The current amount of a certain liquid can be obtained with the @{#STORAGE.GetLiquidAmount}(Type) function -- The current amount of a certain liquid can be obtained with the @{#STORAGE.GetLiquidAmount}(Type) function
-- --
-- local N=storage:GetLiquidAmount(STORAGE.Liquid.DIESEL) -- local N=storage:GetLiquidAmount(STORAGE.Liquid.DIESEL)
-- env.info(string.format("We currently have %d kg of Diesel available", N)) -- env.info(string.format("We currently have %d kg of Diesel available", N))
-- --
-- --
-- # Inventory -- # 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: -- 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() -- local aircraft, liquids, weapons=storage:GetInventory()
-- --
-- UTILS.PrintTableToLog(aircraft) -- UTILS.PrintTableToLog(aircraft)
-- UTILS.PrintTableToLog(liquids) -- UTILS.PrintTableToLog(liquids)
-- UTILS.PrintTableToLog(weapons) -- UTILS.PrintTableToLog(weapons)
@@ -168,7 +168,7 @@ function STORAGE:New(AirbaseName)
local self=BASE:Inherit(self, BASE:New()) -- #STORAGE local self=BASE:Inherit(self, BASE:New()) -- #STORAGE
self.airbase=Airbase.getByName(AirbaseName) self.airbase=Airbase.getByName(AirbaseName)
if Airbase.getWarehouse then if Airbase.getWarehouse then
self.warehouse=self.airbase:getWarehouse() self.warehouse=self.airbase:getWarehouse()
end end
@@ -322,7 +322,7 @@ end
function STORAGE:GetLiquidName(Type) function STORAGE:GetLiquidName(Type)
local name="Unknown" local name="Unknown"
if Type==STORAGE.Liquid.JETFUEL then if Type==STORAGE.Liquid.JETFUEL then
name = "Jet fuel" name = "Jet fuel"
elseif Type==STORAGE.Liquid.GASOLINE then elseif Type==STORAGE.Liquid.GASOLINE then
@@ -411,25 +411,25 @@ function STORAGE:IsUnlimited(Type)
-- Get current amount of type. -- Get current amount of type.
local N=self:GetAmount(Type) local N=self:GetAmount(Type)
local unlimited=false local unlimited=false
if N>0 then if N>0 then
-- Remove one item. -- Remove one item.
self:RemoveAmount(Type, 1) self:RemoveAmount(Type, 1)
-- Get amount. -- Get amount.
local n=self:GetAmount(Type) local n=self:GetAmount(Type)
-- If amount did not change, it is unlimited. -- If amount did not change, it is unlimited.
unlimited=n==N unlimited=n==N
-- Add item back. -- Add item back.
if not unlimited then if not unlimited then
self:AddAmount(Type, 1) self:AddAmount(Type, 1)
end end
-- Debug info. -- Debug info.
self:I(self.lid..string.format("Type=%s: unlimited=%s (N=%d n=%d)", tostring(Type), tostring(unlimited), N, n)) self:I(self.lid..string.format("Type=%s: unlimited=%s (N=%d n=%d)", tostring(Type), tostring(unlimited), N, n))
end end
@@ -523,7 +523,7 @@ end
function STORAGE:GetInventory(Item) function STORAGE:GetInventory(Item)
local inventory=self.warehouse:getInventory(Item) local inventory=self.warehouse:getInventory(Item)
return inventory.aircraft, inventory.liquids, inventory.weapon return inventory.aircraft, inventory.liquids, inventory.weapon
end end

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramLaunchConfigurationType"> <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_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;${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_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_Framework}"/> <stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/MOOSE}"/>
</launchConfiguration> </launchConfiguration>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ui.externaltools.ProgramLaunchConfigurationType"> <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_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;${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_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_Framework}"/> <stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${workspace_loc:/MOOSE}"/>
</launchConfiguration> </launchConfiguration>

View File

@@ -29,12 +29,11 @@ Core/SpawnStatic.lua
Core/Timer.lua Core/Timer.lua
Core/Goal.lua Core/Goal.lua
Core/Spot.lua Core/Spot.lua
Core/TextAndSound.lua
Core/Condition.lua
Core/Pathline.lua
Core/ClientMenu.lua
Core/Astar.lua
Core/MarkerOps_Base.lua Core/MarkerOps_Base.lua
Core/Astar.lua
Core/Condition.lua
Core/TextAndSound.lua
Core/Pathline.lua
Wrapper/Object.lua Wrapper/Object.lua
Wrapper/Identifiable.lua Wrapper/Identifiable.lua
@@ -79,44 +78,13 @@ Functional/Warehouse.lua
Functional/Fox.lua Functional/Fox.lua
Functional/Mantis.lua Functional/Mantis.lua
Functional/Shorad.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/Airboss.lua
Ops/RecoveryTanker.lua Ops/RecoveryTanker.lua
Ops/RescueHelo.lua Ops/RescueHelo.lua
Ops/ATIS.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/CTLD.lua
Ops/Awacs.lua Ops/CSAR.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
AI/AI_Balancer.lua AI/AI_Balancer.lua
AI/AI_Air.lua AI/AI_Air.lua

View File

@@ -4,12 +4,6 @@ parent: Advanced
nav_order: 01 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 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. 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. the Moose version on your hard disk up to date.
You will need to interact with [GitHub]. At least to download the Moose files. 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] 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 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]. [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. any clearer. A list can be found on the [reposities] page.
# Branches: master & develop # Branches: master & develop
As already explained in the [overview] two branches are used: As already explained in the [overview] two branches are used:
- Branch [master]: Stable release branch. - [master]: Stable release branch.
- Branch [develop]: Newest development with more OPS classes. - [develop]: Newest development with more OPS classes.
As a starter it is okay to begin your journey with the `master` branch. 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` 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. 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 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 of th [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. practise to save a bookmark in your browser with the links above.
Both branches are available on most of the different repositories. But because 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 of a limitation of [GitHub pages], we had to split the documentation in two
different repositories: different repositories:
@@ -61,7 +55,7 @@ different repositories:
Moose consists of more than 140 individual files with the file extension `.lua`. 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 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 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 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 as `Moose_.lua`. These both files are created for users of Moose to include in
your missions. 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 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. 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 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 [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 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. 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. 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 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 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 the scripts do we call `static loading` and the resulting mission is very
portable. portable.
# Dynamic loading of mission scripts The other way on loading scripts is by using `DO SCRIPT`. This time the mission
The other way of 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 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 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 your hard drive like script snippets. But we can use it to load a file from our hard drive like this:
this:
```lua ```lua
dofile('C:/MyScripts/hello-world.lua') aaa
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()
``` ```
# IDE vs. Notepad++ # IDE vs. Notepad++
As a beginner you should start with a good text editor, which supports syntax # What is a debugger (good for)
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.
[Git]: https://en.wikipedia.org/wiki/Git [Git]: https://en.wikipedia.org/wiki/Git
[GitHub]: https://github.com/ [GitHub]: https://github.com/
@@ -250,14 +110,3 @@ documentation.
[MOOSE_DOCS]: https://flightcontrol-master.github.io/MOOSE_DOCS/ [MOOSE_DOCS]: https://flightcontrol-master.github.io/MOOSE_DOCS/
[MOOSE_DOCS_DEVELOP]: https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/ [MOOSE_DOCS_DEVELOP]: https://flightcontrol-master.github.io/MOOSE_DOCS_DEVELOP/
[directory tree]: https://github.com/FlightControl-Master/MOOSE/tree/master/Moose%20Development/Moose [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]. - Before posting anything follow the [troubleshooting steps].
- **Read your logs**. - **Read your logs**.
### Formulate a good description
A post should contain the following: A post should contain the following:
- A description what you expected to happen and what actually happened. 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. - Do not use vague words this stuff is hard to help with! Be specific.
- Describe what happens instead. 2. Describe what happens instead.
- The less detail you offer, the less chance you can be helped. - 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. - Don’t say it doesn’t work. Or is it broken. Say what it actually does.
### Format your code 3. Post your code in Discord as formatted 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:
- Wrap a single line of code in backticks \` like this: - 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) ![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 ![discord-fomat-logs.png](../images/beginner/discord-fomat-logs.png)
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.
- Some complex problems need the mission (.miz file) also. - Some complex problems need the mission (.miz file) also.
- But post your mission only when requested. - But post your mission only when requested.
- Try to simplify your mission if it is complex! - 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 There are people in the Discord and in the forum, who spend their free time to
help you. <br /> help you. <br />

View File

@@ -11,14 +11,10 @@ nav_order: 05
## Something went wrong ## 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. 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: 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.
## Read the logs ## 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 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. 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 ***` 1. Search for the following line: `*** MOOSE INCLUDE END ***`
- If it is included in the log, Moose was loaded. - 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.