This commit is contained in:
Frank
2020-04-09 18:07:41 +02:00
parent f554642505
commit d52867c541
12 changed files with 3454 additions and 852 deletions
@@ -1,730 +0,0 @@
--- **Functional** - (R2.5) - Replace client aircraft by statics until a player enters.
--
-- Make the DCS world a bit more lively!
--
-- **Main Features:**
--
-- * Easy!
--
-- ## Known (DCS) Issues
--
-- * Does not support clients on ships.
-- * Does not support Harriers and helicopters on parking spots below a shelter.
--
-- ===
--
-- ### Author: **Hardcard** (aka Goreuncle on the MOOSE discord)
-- ### Contributions: funkyfranky
--
-- @module Functional.Swapr
-- @image Functional_SWAPR.png
--- SWAPR class.
-- @type SWAPR
-- @field #string ClassName Name of the class.
-- @field #boolean Debug Debug mode on/off.
-- @field #string lid Log debug id text.
-- @field Core.Set#SET_CLIENT clientset Set of clients to be replaced.
-- @field #table statics Table of static objects.
-- @field #table statictemplate Table of static template
-- @extends Core.Fsm#FSM
--- Swap clients and statics
--
-- ===
--
-- ![Banner Image](..\Presentations\SWAPR\SWAPR_Main.png)
--
-- # SWAPR Concept
--
-- SWAPR will enable you to easily spawn static aircraft on client slots. When a player enters a client slot, the static object is removed and the player aircraft spawned.
-- This makes the airbases look a lot more alive.
--
-- # Simple Script
--
-- The basic script is very simple and consists of only two lines:
--
-- local clientset=SET_CLIENT:New():FilterActive(false):FilterOnce()
-- swapr=SWAPR:New(clientset)
--
-- The first line defines a set of clients (here all) that will be replaced by statics.
-- The second lines initiates the SWAPR script. That's all.
--
-- **Note** that Harrier and helicopter clients are automatically removed from the client set if they are placed on a sheltered parking spot. Otherwise the statics would be spawned
-- on top of the shelter roof.
--
-- Similarly, clients on ships are removed as these would be spawned at sea level and not on the ship itself.
--
-- All these are *DCS side restriction* when spawning statics.
--
-- # Debugging
--
-- In case you have problems, it is always a good idea to have a look at your DCS log file. You find it in your "Saved Games" folder, so for example in
-- C:\Users\<yourname>\Saved Games\DCS\Logs\dcs.log
-- All output concerning the @{#SWAPR} class should have the string "SWAPR" in the corresponding line.
-- Searching for lines that contain the string "error" or "nil" can also give you a hint what's wrong.
--
-- The verbosity of the output can be increased by adding the following lines to your script:
--
-- BASE:TraceOnOff(true)
-- BASE:TraceLevel(1)
-- BASE:TraceClass("SWAPR")
--
-- To get even more output you can increase the trace level to 2 or even 3, c.f. @{Core.Base#BASE} for more details.
--
-- ## Debug Mode
--
-- You have the option to enable the debug mode for this class via the @{#SWAPR.SetDebugModeON} function.
-- If enabled, text messages about the helo status will be displayed on screen and marks of the pattern created on the F10 map.
--
--
-- @field #SWAPR
SWAPR = {
ClassName = "SWAPR",
Debug = false,
lid = nil,
clientset = nil,
statics = {},
statictemplate = {},
}
--- Class version.
-- @field #string version
SWAPR.version="0.0.2"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- DONE: Check for clients on ships ==> get airdrome id from first route point.
-- DONE: Check that harrier and helo clients are not spawned in shelters ==> get parking spot type for these units in _Prepare()
-- TODO: Check what happens if statics are destroyed.
-- TODO: Check what happens if clients eject, crash or are shot down.
-- TODO: Check that parking spot is not blocked by other aircraft or statics when spawning a static replacement.
-- TODO: Add FSM events, e.g. static spawned, static destroyed etc.
-- TODO: Add user functions, e.g. for defining the static FARP offset.
-- TODO: Safe/load static templates to/from disk.
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new SWAPR object.
-- @param #SWAPR self
-- @param Core.Set#SET_CLIENT clientset (Optional) Set of clients to be replaced. Default all.
-- @return #SWAPR SWAPR object.
function SWAPR:New(clientset)
-- Inherit everthing from FSM class.
local self = BASE:Inherit(self, FSM:New()) -- #SWAPR
-- Carrier type.
self.clientset=clientset or SET_CLIENT:New():FilterActive(false):FilterOnce()
-- Log ID.
self.lid=string.format("SWAPR | ")
-- Debug trace.
if false then
self.Debug=true
BASE:TraceOnOff(true)
BASE:TraceClass(self.ClassName)
BASE:TraceLevel(1)
end
-- Events are handled directly by DCS.
self:T(self.lid.."Events are handled directly by DCS.")
world.addEventHandler(self)
self:HandleEvent(EVENTS.RemoveUnit)
-- Prepare stuff by temporarity spawning aircraft to determine the heading.
self:_Prepare()
-----------------------
--- FSM Transitions ---
-----------------------
-- Start State.
self:SetStartState("Stopped")
-- Add FSM transitions.
-- From State --> Event --> To State
self:AddTransition("Stopped", "Start", "Running")
self:AddTransition("*", "Status", "*")
self:AddTransition("*", "Stop", "Stopped")
--- Triggers the FSM event "Start" that starts the rescue helo. Initializes parameters and starts event handlers.
-- @function [parent=#SWAPR] Start
-- @param #SWAPR self
--- Triggers the FSM event "Start" that starts the rescue helo after a delay. Initializes parameters and starts event handlers.
-- @function [parent=#SWAPR] __Start
-- @param #SWAPR self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Status" that updates the helo status.
-- @function [parent=#SWAPR] Status
-- @param #SWAPR self
--- Triggers the delayed FSM event "Status" that updates the helo status.
-- @function [parent=#SWAPR] __Status
-- @param #SWAPR self
-- @param #number delay Delay in seconds.
--- Triggers the FSM event "Stop" that stops the rescue helo. Event handlers are stopped.
-- @function [parent=#SWAPR] Stop
-- @param #SWAPR self
--- Triggers the FSM event "Stop" that stops the rescue helo after a delay. Event handlers are stopped.
-- @function [parent=#SWAPR] __Stop
-- @param #SWAPR self
-- @param #number delay Delay in seconds.
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Event handler
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- General event handler.
-- @param #SWAPR self
-- @param #table Event DCS event table.
function SWAPR:onEvent(Event)
self:F3(Event)
if Event == nil or Event.initiator == nil then
self:T3("Skipping onEvent. Event or Event.initiator unknown.")
return true
end
if Unit.getByName(Event.initiator:getName()) == nil then
self:T3("Skipping onEvent. Initiator unit name unknown.")
return true
end
-- Get unit name and category.
local IniUnitName = Event.initiator:getName()
local IniCategory = Event.initiator:getCategory()
-- Get client.
local client=self.clientset:FindClient(IniUnitName)
-- This is not an event involving a client in the defined set.
if not client then
self:T3(self.lid.."Event not associated with client aircraft!")
return
end
if Event.id==EVENTS.Birth then
-----------------
-- BIRTH EVENT --
-----------------
if IniCategory==1 then
---------------
-- UNIT BORN --
---------------
local IniDCSGroup = Event.initiator:getGroup()
local IniGroupName = Event.initiator:getGroup():getName()
-- Debug info.
self:T(self.lid..string.format("Event birth of unit %s of group %s", tostring(IniUnitName), tostring(IniGroupName)))
-- Get unit.
local unit=UNIT:FindByName(IniUnitName)
if unit then
--unit:SmokeGreen()
-- Group and name.
local group=unit:GetGroup()
local groupname=group:GetName()
-- Check if this is prepare step to determine the heading.
if string.find(groupname, "_SWAPR") then
--unit:SmokeBlue()
-- Get info necessary for the static template.
local heading=unit:GetHeading()
local coord=unit:GetCoordinate()
local actype=unit:GetTypeName()
local livery=self:_GetLiveryFromTemplate(IniUnitName)
local airbase=self:_GetAirbaseFromTemplate(IniUnitName)
-- FARPS suck!
if airbase:GetAirbaseCategory()==Airbase.Category.HELIPAD then
local parkingid=self:_GetParkingFromTemplate(IniUnitName)
self:T2(self.lid..string.format("FARP parking id=%s", tostring(parkingid)))
if parkingid then
local spot=airbase:GetParkingSpotData(parkingid)
coord=spot.Coordinate
coord.z=coord.z+5
coord.x=coord.x+5
end
end
-- Add static template to table.
local statictemplate=self:_AddStaticTemplate(IniUnitName, actype, coord.x, coord.z, heading, unit:GetCountry(), livery)
-- Destroy unit ==> triggers a remove unit event.
unit:Destroy()
-- Replace aircraft by static.
--self:_Aircraft2Static(unit)
else
self:I(self.lid..string.format("Client %s spawned!", IniUnitName))
-- Get static that is in place of the spawned client.
local static=self.statics[IniUnitName] --Wrapper.Static#STATIC
-- Remove static.
if static then
self:I(self.lid..string.format("Destroying static %s!", IniUnitName))
-- Looks like the MOOSE Destroy function is not fast enough!
static:destroy()
self.statics[IniUnitName]=nil
else
self:E(self.lid..string.format("WARNING: No static %s to destroy!", IniUnitName))
end
end
end
elseif IniCategory==3 then
-----------------
-- STATIC BORN --
-----------------
self:I(self.lid..string.format("Event birth of static %s", tostring(IniUnitName)))
-- WORKS!
local static=STATIC:FindByName(IniUnitName, true)
-- Add spawned static to table.
--self.statics[IniUnitName]=static
self.statics[IniUnitName]=Event.initiator
end
elseif Event.id==EVENTS.PlayerLeaveUnit then
-----------------
-- PLAYER LEFT --
-----------------
self:I(self.lid..string.format("Event player leave unit %s", IniUnitName))
-- Spawn static. Needs to be delayed a tad or DCS crashes to desktop.
local statictemplate=self.statictemplate[IniUnitName]
if statictemplate then
self:ScheduleOnce(0.1, SWAPR._SpawnStaticAircraft, self, statictemplate)
end
end
end
--- General event handler.
-- @param #SWAPR self
-- @param Core.Event#EVENTDATA EventData Event data table.
function SWAPR:OnEventRemoveUnit(EventData)
self:I(EventData)
if EventData and EventData.IniUnitName then
-- Debug info.
self:I(self.lid..string.format("Event removed unit %s!", EventData.IniUnitName))
-- Spawn static aircraft.
local statictemplate=self.statictemplate[EventData.IniUnitName]
if statictemplate then
self:ScheduleOnce(0.1, SWAPR._SpawnStaticAircraft, self, statictemplate)
end
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Spawn functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Add template to static table.
-- @param #SWAPR self
-- @param #string name Name of the static.
-- @param #string actype Type of the aircraft.
-- @param #number x X coordinate of spawn place.
-- @param #number y Y coordinate of spawn place.
-- @param #number heading Heading of static.
-- @param #number country Country ID of static.
-- @param #string livery Livery ID of the static.
-- @return #table Static template.
function SWAPR:_AddStaticTemplate(name, actype, x, y, heading, country, livery)
-- Heading is in rad not degrees!
local headingrad=0
if heading then
headingrad=math.rad(heading)
end
-- Static template table.
local static={
livery_id=livery,
heading=headingrad,
type=actype,
name=name,
y=y ,
x=x ,
CountryID=country,
}
-- Debug info.
self:T2({statictemplate=static})
self.statictemplate[name]=static
return static
end
--- General event handler.
-- @param #SWAPR self
-- @param #table template The static template.
function SWAPR:_SpawnStaticAircraft(template)
self:I({statictemplate=template})
if template and not self.statics[template.name] then
-- Spawn static.
local static=coalition.addStaticObject(template.CountryID, template)
-- Debug info.
self:T2({spawnedstatic=static})
self:T(self.lid..string.format("Spawned static %s", template.name))
else
self:T3(self.lid.."WARNING: Static template is nil!")
end
end
--- Replace a whole aircraft group by statics.
-- @param #SWAPR self
-- @param Wrapper.Group#GROUP group
function SWAPR:_AircraftGroup2Statics(group)
-- Get the group template.
local grouptemplate=group:GetTemplate()
-- Debug info.
self:T3({grouptemplate=grouptemplate})
for i,_unit in pairs(group:GetUnits()) do
local unit=_unit --Wrapper.Unit#UNIT
-- Get unit name.
local unitname=unit:GetName()
local statictemplate=self.statictemplate[unitname]
local static=self.statics[unitname]
if statictemplate and not static then
-- Destroy the unit.
unit:Destroy()
-- Spawn static aircraft instead.
self:_SpawnStaticAircraft(statictemplate)
end
end
end
--- Replace a single aircraft unit by static.
-- @param #SWAPR self
-- @param Wrapper.Unit#UNIT unit The unit to be replaced.
function SWAPR:_Aircraft2Static(unit)
if unit and unit:IsAlive() then
-- Get the group template.
local grouptemplate=unit:GetGroup():GetTemplate()
-- Debug info.
self:T3({grouptemplate=grouptemplate})
-- Get unit name.
local unitname=unit:GetName()
-- Get the static template.
local statictemplate=self.statictemplate[unitname]
-- Get the static to check if there already is one.
local static=self.statics[unitname]
if statictemplate and not static then
-- Destroy the unit ==> triggers a RemoveUnit event.
unit:Destroy()
-- Spawn static aircraft instead.
self:_SpawnStaticAircraft(statictemplate)
end
end
end
--- Temporarily spawn uncontrolled aircraft at all client spots to get the correct headings.
-- @param #SWAPR self
function SWAPR:_Prepare()
local remove={}
for _,_client in pairs(self.clientset:GetSet()) do
local client=_client --Wrapper.Client#CLIENT
-- Unit name
local unitname=client.ClientName
-- Get airbase if any.
local airbase=self:_GetAirbaseFromTemplate(unitname)
-- First check that this is not a cliened spawned in air or on a ship.
if airbase==nil then
-- Spawned in air ==> remove!
self:I(self.lid..string.format("Removing client %s because of air start.", unitname))
table.insert(remove, client)
elseif airbase:GetAirbaseCategory()==Airbase.Category.SHIP then
self:I(self.lid..string.format("Removing client %s because spawned on ship.", unitname))
table.insert(remove, client)
else
if true then
---
-- Spawn a group to get parameters in particular the heading on the parking spot as this is not correct in the template.
---
-- Check that harriers are not spawned in shelters because they would appear on top of them.
-- TODO: Need to do the same of helos?
local _continue=true
if self:_GetTypeFromTemplate(unitname)=="AV8BNA" then
local parkingid=self:_GetParkingFromTemplate(unitname)
if parkingid then
env.info(string.format("Harrier parking spot id %d", parkingid))
local spot=airbase:GetParkingSpotData(parkingid)
if spot and spot.TerminalType==AIRBASE.TerminalType.Shelter then
_continue=false
table.insert(remove, client)
end
end
end
if _continue then
-- Client group name.
local groupname=_DATABASE.Templates.Units[unitname].GroupName
-- Client group template copy.
local grouptemplate=UTILS.DeepCopy(_DATABASE:GetGroupTemplate(groupname))
-- Nillify the group ID.
grouptemplate.groupId=nil
-- Set skill.
for i=1,#grouptemplate.units do
local unit=grouptemplate.units[i]
unit.skill="Good"
-- Nillify the unit ID.
unit.unitId=nil
end
-- Uncontrolled
grouptemplate.uncontrolled=true
-- Add _SWAPR to the group name so that we find it in birth event.
grouptemplate.name=string.format("%s_SWAPR", groupname)
-- Debug info.
self:I({grouptemplate=grouptemplate})
-- Spawn group.
local group=_DATABASE:Spawn(grouptemplate)
end
else
---
-- Get all info from template. Unfortunately, heading is always 0 in the template, i.e. all statics would face due North!
---
local livery=self:_GetLiveryFromTemplate(unitname)
local x,y=self:_GetPositionFromTemplate(unitname)
local actype=self:_GetTypeFromTemplate(unitname)
local heading=self:_GetHeadingFromTemplate(unitname)
-- TODO: country!
local template=self:_AddStaticTemplate(unitname, actype, x, y, heading, 1, livery)
self:_SpawnStaticAircraft(template)
end
end
end
for _,_client in pairs(remove) do
local client=_client --Wrapper.Client#CLIENT
self.clientset:RemoveClientsByName(client.ClientName)
end
self:I(self.lid..string.format("Number of clients left after prepare = %d", self.clientset:Count()))
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Misc functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Get livery from unit.
-- @param #SWAPR self
-- @param #string unitname Name of the unit.
-- @return #string Livery ID.
function SWAPR:_GetLiveryFromTemplate(unitname)
local grouptemplate=_DATABASE:GetGroupTemplateFromUnitName(unitname)
for _,unit in pairs(grouptemplate.units) do
if unit.name==unitname then
return tostring(unit.livery_id)
end
end
return nil
end
--- Get livery from unit.
-- @param #SWAPR self
-- @param #string unitname Name of the unit.
-- @return #number X coordinate.
-- @return #number Y coordinate.
function SWAPR:_GetPositionFromTemplate(unitname)
local grouptemplate=_DATABASE:GetGroupTemplateFromUnitName(unitname)
for _,unit in pairs(grouptemplate.units) do
if unit.name==unitname then
return tonumber(unit.x), tonumber(unit.y)
end
end
return nil, nil
end
--- Get livery from unit.
-- @param #SWAPR self
-- @param #string unitname Name of the unit.
-- @return #string Aircraft type.
function SWAPR:_GetTypeFromTemplate(unitname)
local grouptemplate=_DATABASE:GetGroupTemplateFromUnitName(unitname)
for _,unit in pairs(grouptemplate.units) do
if unit.name==unitname then
return tostring(unit.type)
end
end
return nil
end
--- Get livery from unit.
-- @param #SWAPR self
-- @param #string unitname Name of the unit.
-- @return #number Heading in degrees.
function SWAPR:_GetHeadingFromTemplate(unitname)
local grouptemplate=_DATABASE:GetGroupTemplateFromUnitName(unitname)
for _,unit in pairs(grouptemplate.units) do
if unit.name==unitname then
return tonumber(unit.heading)
end
end
return nil
end
--- Get airbase from template.
-- @param #SWAPR self
-- @param #string unitname Name of the unit.
-- @return Wrapper.Airbase#AIRBASE The airbase object or nil.
function SWAPR:_GetAirbaseFromTemplate(unitname)
local grouptemplate=_DATABASE:GetGroupTemplateFromUnitName(unitname)
-- First waypoint.
local wp=grouptemplate.route.points[1]
local airbase=nil --Wrapper.Airbase#AIRBASE
local id=-666
if wp.airdromeId then
id=tonumber(wp.airdromeId)
elseif wp.helipadId then
id=tonumber(wp.helipadId)
end
-- Find airbase by its id.
airbase=AIRBASE:FindByID(id)
-- Debug info.
if airbase then
self:T3(self.lid..string.format("Found airbase %s for unit %s, id=%d", airbase:GetName(), unitname, id))
else
self:T3(self.lid..string.format("Found NO airbase for unit %s, id=%d", unitname,id))
end
return airbase
end
--- Get parking id from template.
-- @param #SWAPR self
-- @param #string unitname Name of the unit.
-- @return #number Parking id or nil.
function SWAPR:_GetParkingFromTemplate(unitname)
local grouptemplate=_DATABASE:GetGroupTemplateFromUnitName(unitname)
for _,unit in pairs(grouptemplate.units) do
if unit.name==unitname then
return tonumber(unit.parking)
end
end
return nil
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+2 -1
View File
@@ -37,6 +37,8 @@ __Moose.Include( 'Scripts/Moose/Wrapper/Client.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Static.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Static.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Airbase.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Airbase.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Scenery.lua' ) __Moose.Include( 'Scripts/Moose/Wrapper/Scenery.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/Mark.lua' )
__Moose.Include( 'Scripts/Moose/Wrapper/DCSTask.lua' )
__Moose.Include( 'Scripts/Moose/Cargo/Cargo.lua' ) __Moose.Include( 'Scripts/Moose/Cargo/Cargo.lua' )
__Moose.Include( 'Scripts/Moose/Cargo/CargoUnit.lua' ) __Moose.Include( 'Scripts/Moose/Cargo/CargoUnit.lua' )
@@ -67,7 +69,6 @@ __Moose.Include( 'Scripts/Moose/Functional/Fox.lua' )
__Moose.Include( 'Scripts/Moose/Functional/RAT2.lua' ) __Moose.Include( 'Scripts/Moose/Functional/RAT2.lua' )
__Moose.Include( 'Scripts/Moose/Functional/RatCraft.lua' ) __Moose.Include( 'Scripts/Moose/Functional/RatCraft.lua' )
__Moose.Include( 'Scripts/Moose/Functional/FlightModelData.lua' ) __Moose.Include( 'Scripts/Moose/Functional/FlightModelData.lua' )
__Moose.Include( 'Scripts/Moose/Functional/SWAPR.lua' )
__Moose.Include( 'Scripts/Moose/Ops/Airboss.lua' ) __Moose.Include( 'Scripts/Moose/Ops/Airboss.lua' )
__Moose.Include( 'Scripts/Moose/Ops/RecoveryTanker.lua' ) __Moose.Include( 'Scripts/Moose/Ops/RecoveryTanker.lua' )
+65 -16
View File
@@ -32,6 +32,10 @@
-- @field #table pointsTANKER Table of Tanker points. -- @field #table pointsTANKER Table of Tanker points.
-- @field #table pointsAWACS Table of AWACS points. -- @field #table pointsAWACS Table of AWACS points.
-- @field Ops.WingCommander#WINGCOMMANDER wingcommander The wing commander responsible for this airwing. -- @field Ops.WingCommander#WINGCOMMANDER wingcommander The wing commander responsible for this airwing.
--
-- @field Ops.RescueHelo#RESCUEHELO rescuehelo The rescue helo.
-- @field Ops.RecoveryTanker#RECOVERYTANKER recoverytanker The recoverytanker.
--
-- @extends Functional.Warehouse#WAREHOUSE -- @extends Functional.Warehouse#WAREHOUSE
--- Be surprised! --- Be surprised!
@@ -79,11 +83,10 @@ AIRWING = {
-- @type AIRWING.Payload -- @type AIRWING.Payload
-- @field #string unitname Name of the unit this pylon was extracted from. -- @field #string unitname Name of the unit this pylon was extracted from.
-- @field #string aircrafttype Type of aircraft, which can use this payload. -- @field #string aircrafttype Type of aircraft, which can use this payload.
-- @field #table missiontypes Mission types for which this payload can be used. -- @field #table capabilities Mission types and performances for which this payload can be used.
-- @field #table pylons Pylon data extracted for the unit template. -- @field #table pylons Pylon data extracted for the unit template.
-- @field #number navail Number of available payloads of this type. -- @field #number navail Number of available payloads of this type.
-- @field #boolean unlimited If true, this payload is unlimited and does not get consumed. -- @field #boolean unlimited If true, this payload is unlimited and does not get consumed.
-- @field #number priority Priority of the payload.
--- Patrol data. --- Patrol data.
-- @type AIRWING.PatrolData -- @type AIRWING.PatrolData
@@ -95,7 +98,7 @@ AIRWING = {
--- AIRWING class version. --- AIRWING class version.
-- @field #string version -- @field #string version
AIRWING.version="0.1.5" AIRWING.version="0.1.6"
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ToDo list -- ToDo list
@@ -103,7 +106,6 @@ AIRWING.version="0.1.5"
-- TODO: Spawn in air or hot. -- TODO: Spawn in air or hot.
-- TODO: Make special request to transfer squadrons to anther airwing (or warehouse). -- TODO: Make special request to transfer squadrons to anther airwing (or warehouse).
-- TODO: Border zone or even multiple zones.
-- TODO: Check that airbase has enough parking spots if a request is BIG. Alternatively, split requests. -- TODO: Check that airbase has enough parking spots if a request is BIG. Alternatively, split requests.
-- DONE: Add squadrons to warehouse. -- DONE: Add squadrons to warehouse.
-- DONE: Build mission queue. -- DONE: Build mission queue.
@@ -146,6 +148,9 @@ function AIRWING:New(warehousename, airwingname)
self.nflightsTANKERboom=0 self.nflightsTANKERboom=0
self.nflightsTANKERprobe=0 self.nflightsTANKERprobe=0
self.nflightsRecoveryTanker=0
self.nflightsRescuehelo=0
------------------------ ------------------------
--- Pseudo Functions --- --- Pseudo Functions ---
------------------------ ------------------------
@@ -240,32 +245,36 @@ function AIRWING:NewPayload(Unit, MissionTypes, Npayloads, Unlimited, Priority)
MissionTypes={MissionTypes} MissionTypes={MissionTypes}
end end
-- Add ORBIT for all.
if not self:CheckMissionType(AUFTRAG.Type.ORBIT, MissionTypes) then
table.insert(MissionTypes, AUFTRAG.Type.ORBIT)
end
if Unit then if Unit then
local payload={} --#AIRWING.Payload local payload={} --#AIRWING.Payload
--TODO: capability
payload.unitname=Unit:GetName() payload.unitname=Unit:GetName()
payload.aircrafttype=Unit:GetTypeName() payload.aircrafttype=Unit:GetTypeName()
payload.missiontypes=MissionTypes or {} payload.capabilities=MissionTypes or {}
payload.pylons=Unit:GetTemplatePayload() payload.pylons=Unit:GetTemplatePayload()
payload.navail=Npayloads or 99 payload.navail=Npayloads or 99
payload.unlimited=Unlimited payload.unlimited=Unlimited
if Unlimited then if Unlimited then
payload.navail=1 payload.navail=1
end end
payload.priority=Priority or 50
-- Add payload -- Add payload
table.insert(self.payloads, payload) table.insert(self.payloads, payload)
-- Add ORBIT for all.
if not self:CheckMissionType(AUFTRAG.Type.ORBIT, MissionTypes) then
local capability={} --Ops.Auftrag#AUFTRAG.Capability
capability.MissionType=AUFTRAG.Type.ORBIT
capability.Performance=50
--table.insert(MissionTypes, capability)
end
-- Info -- Info
self:I(self.lid..string.format("Adding new payload from unit %s for aircraft type %s: N=%d (unlimited=%s), prio=%d, missions: %s", self:I(self.lid..string.format("Adding new payload from unit %s for aircraft type %s: N=%d (unlimited=%s), prio=%d, missions: %s",
payload.unitname, payload.aircrafttype, payload.navail, tostring(payload.unlimited), payload.priority, table.concat(payload.missiontypes, ", "))) payload.unitname, payload.aircrafttype, payload.navail, tostring(payload.unlimited), payload.priority, table.concat(MissionTypes, ", ")))
return payload return payload
end end
@@ -763,7 +772,7 @@ function AIRWING:CheckTANKER()
for _,_mission in pairs(self.missionqueue) do for _,_mission in pairs(self.missionqueue) do
local mission=_mission --Ops.Auftrag#AUFTRAG local mission=_mission --Ops.Auftrag#AUFTRAG
if mission:IsNotOver() and self:CheckMissionType(mission.type, AUFTRAG.Type.TANKER) then if mission:IsNotOver() and mission.type==AUFTRAG.Type.TANKER then
if mission.refuelSystem==0 then if mission.refuelSystem==0 then
Nboom=Nboom+1 Nboom=Nboom+1
elseif mission.refuelSystem==1 then elseif mission.refuelSystem==1 then
@@ -833,6 +842,13 @@ function AIRWING:CheckAWACS()
return self return self
end end
--- Check how many AWACS missions are assigned and add number of missing missions.
-- @param #AIRWING self
-- @return #AIRWING self
function AIRWING:CheckRecoveryTanker()
end
--- Check how many AWACS missions are assigned and add number of missing missions. --- Check how many AWACS missions are assigned and add number of missing missions.
-- @param #AIRWING self -- @param #AIRWING self
-- @param Ops.FlightGroup#FLIGHTGROUP flightgroup The flightgroup. -- @param Ops.FlightGroup#FLIGHTGROUP flightgroup The flightgroup.
@@ -912,8 +928,7 @@ function AIRWING:_GetNextMission()
if can then if can then
-- Optimize the asset selection. Most useful assets will come first. -- Optimize the asset selection. Most useful assets will come first.
-- TODO: This could be moved to AUFTRAG, right? self:_OptimizeAssetSelection(assets, mission)
--self:_OptimizeAssetSelection(assets, mission)
-- Check that mission.assets table is clean. -- Check that mission.assets table is clean.
if mission.assets and #mission.assets>0 then if mission.assets and #mission.assets>0 then
@@ -968,6 +983,23 @@ function AIRWING:_OptimizeAssetSelection(assets, Mission)
end end
local function score(Asset)
local asset=Asset --#AIRWING.SquadronAsset
score=0
-- Prefer highly skilled assets.
if asset.skill==AI.Skill.GOOD then
score=score+10
elseif asset.skill==AI.Skill.HIGH then
score=score+20
elseif asset.skill==AI.Skill.EXCELLENT then
score=score+30
end
end
-- Sort results table wrt distacance. -- Sort results table wrt distacance.
local function optimize(a, b) local function optimize(a, b)
local assetA=a --#AIRWING.SquadronAsset local assetA=a --#AIRWING.SquadronAsset
@@ -1563,7 +1595,7 @@ function AIRWING:CanMission(Mission)
end end
--- Returns the mission for a given mission ID (Autragsnummer). --- Check if a mission type is contained in a list of possible types.
-- @param #AIRWING self -- @param #AIRWING self
-- @param #string MissionType The requested mission type. -- @param #string MissionType The requested mission type.
-- @param #table PossibleTypes A table with possible mission types. -- @param #table PossibleTypes A table with possible mission types.
@@ -1583,6 +1615,23 @@ function AIRWING:CheckMissionType(MissionType, PossibleTypes)
return false return false
end end
--- Check if a mission type is contained in a list of possible capabilities.
-- @param #AIRWING self
-- @param #string MissionType The requested mission type.
-- @param #table PossibleTypes A table with possible capabilities.
-- @return #boolean If true, the requested mission type is part of the possible mission types.
function AIRWING:CheckMissionCapability(MissionType, Capabilities)
for _,cap in pairs(Capabilities) do
local capability=cap --Ops.Auftrag#AUFTRAG.Capability
if capability.MissionType==MissionType then
return true
end
end
return false
end
--- Returns the mission for a given mission ID (Autragsnummer). --- Returns the mission for a given mission ID (Autragsnummer).
-- @param #AIRWING self -- @param #AIRWING self
-- @param #number mid Mission ID (Auftragsnummer). -- @param #number mid Mission ID (Auftragsnummer).
+29 -1
View File
@@ -273,13 +273,18 @@ AUFTRAG.TargetType={
AIRBASE="Airbase", AIRBASE="Airbase",
} }
--- --- Target data.
-- @type AUFTRAG.TargetData -- @type AUFTRAG.TargetData
-- @field Wrapper.Positionable#POSITIONABLE Target Target Object. -- @field Wrapper.Positionable#POSITIONABLE Target Target Object.
-- @field #string Type Target type: "Group", "Unit", "Static", "Coordinate", "Airbase. -- @field #string Type Target type: "Group", "Unit", "Static", "Coordinate", "Airbase.
-- @field #number Ninital Number of initial targets. -- @field #number Ninital Number of initial targets.
-- @field #number Lifepoints Total life points. -- @field #number Lifepoints Total life points.
--- Mission capability.
-- @type AUFTRAG.Capability
-- @field #string MissionType Type of mission.
-- @field #number Performance Number describing the performance level. The higher the better.
--- Mission success. --- Mission success.
-- @type AUFTRAG.Success -- @type AUFTRAG.Success
-- @field #string ENGAGED Target was engaged. -- @field #string ENGAGED Target was engaged.
@@ -2297,6 +2302,29 @@ function AUFTRAG:GetDCSMissionTask()
local DCStask=CONTROLLABLE.TaskEmbarking(self, Vec2, self.transportGroupSet, Duration, DistributionGroupSet) local DCStask=CONTROLLABLE.TaskEmbarking(self, Vec2, self.transportGroupSet, Duration, DistributionGroupSet)
table.insert(DCStasks, DCStask)
elseif self.type==AUFTRAG.Type.RESCUEHELO then
-------------------------
-- RESCUE HELO Mission --
-------------------------
local DCStask={}
DCStask.id="Formation"
local param={}
param.unitname=""
param.offsetX=20
param.offsetY=20
param.offsetZ=20
param.alitude=70
DCStask.params=param
table.insert(DCStasks, DCStask)
else else
self:E(self.lid..string.format("ERROR: Unknown mission task!")) self:E(self.lid..string.format("ERROR: Unknown mission task!"))
return nil return nil
+59 -63
View File
@@ -2628,7 +2628,6 @@ function FLIGHTGROUP:onafterPassingWaypoint(From, Event, To, n, N)
-- Execute waypoint tasks. -- Execute waypoint tasks.
if #taskswp>0 then if #taskswp>0 then
--self:PushTask(self.group:TaskCombo(taskswp))
self:SetTask(self.group:TaskCombo(taskswp)) self:SetTask(self.group:TaskCombo(taskswp))
end end
@@ -3390,37 +3389,63 @@ function FLIGHTGROUP:onafterTaskExecute(From, Event, To, Task)
-- Task status executing. -- Task status executing.
Task.status=FLIGHTGROUP.TaskStatus.EXECUTING Task.status=FLIGHTGROUP.TaskStatus.EXECUTING
-- If task is scheduled (not waypoint) set task. if Task.dcstask.id=="Formation" then
if Task.type==FLIGHTGROUP.TaskType.SCHEDULED then
local DCStasks={} -- Set of group(s) to follow Mother.
if Task.dcstask.id=='ComboTask' then local followset=SET_GROUP:New():AddGroup(self.group)
-- Loop over all combo tasks.
for TaskID, Task in ipairs(Task.dcstask.params.tasks) do local param=Task.dcstask.params
table.insert(DCStasks, Task)
-- Define AI Formation object.
Task.formation=AI_FORMATION:New(param.carrier, followset, "Formation", "Follow X at given parameters.")
-- Formation parameters.
Task.formation:FormationCenterWing(-param.offsetX, 50, math.abs(param.altitude), 50, param.offsetZ, 50)
-- Set follow time interval.
Task.formation:SetFollowTimeInterval(param.dtFollow)
-- Formation mode.
Task.formation:SetFlightModeFormation(self.group)
-- Start formation FSM.
Task.formation:Start()
else
-- If task is scheduled (not waypoint) set task.
if Task.type==FLIGHTGROUP.TaskType.SCHEDULED then
local DCStasks={}
if Task.dcstask.id=='ComboTask' then
-- Loop over all combo tasks.
for TaskID, Task in ipairs(Task.dcstask.params.tasks) do
table.insert(DCStasks, Task)
end
else
table.insert(DCStasks, Task.dcstask)
end end
else
table.insert(DCStasks, Task.dcstask) -- Combo task.
local TaskCombo=self.group:TaskCombo(DCStasks)
-- Stop condition!
local TaskCondition=self.group:TaskCondition(nil, Task.stopflag:GetName(), 1, nil, Task.duration)
-- Controlled task.
local TaskControlled=self.group:TaskControlled(TaskCombo, TaskCondition)
-- Task done.
local TaskDone=self.group:TaskFunction("FLIGHTGROUP._TaskDone", self, Task)
-- Final task.
local TaskFinal=self.group:TaskCombo({TaskControlled, TaskDone})
-- Set task for group.
self:SetTask(TaskFinal, 1)
end end
-- Combo task.
local TaskCombo=self.group:TaskCombo(DCStasks)
-- Stop condition!
local TaskCondition=self.group:TaskCondition(nil, Task.stopflag:GetName(), 1, nil, Task.duration)
-- Controlled task.
local TaskControlled=self.group:TaskControlled(TaskCombo, TaskCondition)
-- Task done.
local TaskDone=self.group:TaskFunction("FLIGHTGROUP._TaskDone", self, Task)
-- Final task.
local TaskFinal=self.group:TaskCombo({TaskControlled, TaskDone})
-- Set task for group.
self:SetTask(TaskFinal, 1)
end end
-- Get mission of this task (if any). -- Get mission of this task (if any).
@@ -3490,6 +3515,11 @@ function FLIGHTGROUP:onafterTaskCancel(From, Event, To, Task)
-- Set stop flag. When the flag is true, the _TaskDone function is executed and calls :TaskDone() -- Set stop flag. When the flag is true, the _TaskDone function is executed and calls :TaskDone()
Task.stopflag:Set(1) Task.stopflag:Set(1)
if Task.dcstask.id=="Formation" then
Task.formation:Stop()
self:TaskDone(Task)
end
else else
-- Debug info. -- Debug info.
@@ -3577,10 +3607,6 @@ function FLIGHTGROUP:onafterTaskDone(From, Event, To, Task)
self:_CheckFlightDone(1) self:_CheckFlightDone(1)
end end
-- Update route. This is necessary because of the route task being overwritten. But we want to fly to the remaining waypoints.
-- TODO: Since TaskExecute does use PushTask now, it should not be necessary to update the route, right?
--self:__UpdateRoute(-1)
end end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -3739,10 +3765,8 @@ function FLIGHTGROUP:onafterMissionCancel(From, Event, To, Mission)
-- Note that two things can happen. -- Note that two things can happen.
-- 1.) Flight is still on the way to the waypoint (status should be STARTED). In this case there would not be a current task! -- 1.) Flight is still on the way to the waypoint (status should be STARTED). In this case there would not be a current task!
-- 2.) Flight already passed the mission waypoint (status should be EXECUTING). -- 2.) Flight already passed the mission waypoint (status should be EXECUTING).
self:TaskCancel(Task)
-- Set current mission to nil. self:TaskCancel(Task)
--self.currentmission=nil
else else
@@ -4452,34 +4476,6 @@ function FLIGHTGROUP:_UpdateWaypointTasks()
local TaskPassingWaypoint=self.group:TaskFunction("FLIGHTGROUP._PassingWaypoint", self, i) local TaskPassingWaypoint=self.group:TaskFunction("FLIGHTGROUP._PassingWaypoint", self, i)
table.insert(taskswp, TaskPassingWaypoint) table.insert(taskswp, TaskPassingWaypoint)
-- For some reason THIS DOES NOT WORK if executed at the last waypoint if it is an AIR WAYPOINT.
-- I have moved it to the onafterpassingwaypoint function instead.
if false then
-- Get taks
local tasks=self:GetTasksWaypoint(i)
for _,task in pairs(tasks) do
local Task=task --#FLIGHTGROUP.Task
-- Task execute.
table.insert(taskswp, self.group:TaskFunction("FLIGHTGROUP._TaskExecute", self, Task))
-- Stop condition if userflag is set to 1.
local TaskCondition=self.group:TaskCondition(nil, Task.stopflag:GetName(), 1, nil, Task.duration)
-- Controlled task.
table.insert(taskswp, self.group:TaskControlled(Task.dcstask, TaskCondition))
-- Task done.
table.insert(taskswp, self.group:TaskFunction("FLIGHTGROUP._TaskDone", self, Task))
end
end
-- Waypoint task combo. -- Waypoint task combo.
wp.task=self.group:TaskCombo(taskswp) wp.task=self.group:TaskCombo(taskswp)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+33 -12
View File
@@ -223,8 +223,9 @@ end
--- Set mission types this squadron is able to perform. --- Set mission types this squadron is able to perform.
-- @param #SQUADRON self -- @param #SQUADRON self
-- @param #table MissionTypes Table of mission types. Can also be passed as a #string if only one type. -- @param #table MissionTypes Table of mission types. Can also be passed as a #string if only one type.
-- @param #number Performance Performance describing how good this mission can be performed. Higher is better. Default 50. Max 100.
-- @return #SQUADRON self -- @return #SQUADRON self
function SQUADRON:SetMissonTypes(MissionTypes) function SQUADRON:AddMissonCapability(MissionTypes, Performance)
-- Ensure Missiontypes is a table. -- Ensure Missiontypes is a table.
if MissionTypes and type(MissionTypes)~="table" then if MissionTypes and type(MissionTypes)~="table" then
@@ -233,11 +234,18 @@ function SQUADRON:SetMissonTypes(MissionTypes)
-- Add ORBIT for all. -- Add ORBIT for all.
if not self:CheckMissionType(AUFTRAG.Type.ORBIT, MissionTypes) then if not self:CheckMissionType(AUFTRAG.Type.ORBIT, MissionTypes) then
table.insert(MissionTypes, AUFTRAG.Type.ORBIT) --table.insert(MissionTypes, AUFTRAG.Type.ORBIT)
end end
-- Set table. -- Set table.
self.missiontypes=MissionTypes self.missiontypes=self.missiontypes or {} --MissionTypes
for _,missiontype in pairs(MissionTypes) do
local capability={} --Ops.Auftrag#AUFTRAG.Capability
capability.MissionType=missiontype
capability.Performance=Performance
table.insert(self.missiontypes, capability)
end
self:I(self.missiontypes) self:I(self.missiontypes)
@@ -293,6 +301,23 @@ function SQUADRON:SetAirwing(Airwing)
return self return self
end end
--- This squadron can do recue helo operations for boat ops.
-- @param #SQUADRON self
-- @return #SQUADRON self
function SQUADRON:SetCanRescueHelo()
self.canrescuehelo=true
return self
end
--- This squadron can do recovery tanker operations for boat ops.
-- @param #SQUADRON self
-- @return #SQUADRON self
function SQUADRON:SetCanRescueHelo()
self.canrecoverytanker=true
return self
end
--- Add airwing asset to squadron. --- Add airwing asset to squadron.
-- @param #SQUADRON self -- @param #SQUADRON self
-- @param Ops.AirWing#AIRWING.SquadronAsset Asset The airwing asset. -- @param Ops.AirWing#AIRWING.SquadronAsset Asset The airwing asset.
@@ -475,9 +500,7 @@ end
function SQUADRON:_CheckAssetStatus() function SQUADRON:_CheckAssetStatus()
for _,_asset in pairs(self.assets) do for _,_asset in pairs(self.assets) do
local asset=_asset --#SQUADRON.Flight local asset=_asset
flight.flightgroup:IsSpawned()
end end
@@ -516,7 +539,7 @@ function SQUADRON:CanMission(Mission)
-- On duty?= -- On duty?=
if not self:IsOnDuty() then if not self:IsOnDuty() then
self:I(self.lid..string.format("Sqaud in not OnDuty but in state %s", self:GetState())) self:I(self.lid..string.format("Squad in not OnDuty but in state %s", self:GetState()))
return false, assets return false, assets
end end
@@ -560,10 +583,8 @@ function SQUADRON:CanMission(Mission)
-- Check if the payload of this asset is compatible with the mission. -- Check if the payload of this asset is compatible with the mission.
-- Note: we do not check the payload as an asset that is on a PATROL mission should be able to do an intercept as well! -- Note: we do not check the payload as an asset that is on a PATROL mission should be able to do an intercept as well!
--if self:CheckMissionType(Mission.type, asset.payload.missiontypes) then -- TODO: Check if asset actually has weapons left. Difficult!
-- TODO: Check if asset actually has weapons left. Difficult! table.insert(assets, asset)
table.insert(assets, asset)
--end
end end
@@ -600,7 +621,7 @@ function SQUADRON:CanMission(Mission)
-- Asset is still in STOCK -- Asset is still in STOCK
--- ---
-- Check that asset is not already requeseted for another mission. -- Check that asset is not already requested for another mission.
if not asset.requested then if not asset.requested then
-- Check if we got a payload and reserve it for this asset. -- Check if we got a payload and reserve it for this asset.
@@ -392,32 +392,6 @@ function WINGCOMMANDER:onafterStatus(From, Event, To)
end end
-- Create missions for all new contacts.
--[[
for _,_contact in pairs(self.ContactsUnknown) do
local contact=_contact --#WINGCOMMANDER.Contact
local group=contact.group --Wrapper.Group#GROUP
-- Create a mission based on group category.
local mission=AUFTRAG:NewAUTO(group)
-- Add mission to queue.
if mission then
--TODO: Better amount of necessary assets. Count units in asset and in contact. Might need nassetMin/Max.
mission.nassets=1
-- Set mission contact.
contact.mission=mission
-- Add mission to queue.
self:AddMission(mission)
end
end
]]
-- Create missions for all new contacts. -- Create missions for all new contacts.
local Nred=0 local Nred=0
local Nyellow=0 local Nyellow=0
+345
View File
@@ -0,0 +1,345 @@
--- **Wrapper** - Markers On the F10 map.
--
--
--
-- **Main Features:**
--
-- * Manage aircraft recovery.
--
-- ===
--
-- ### Author: **funkyfranky**
-- @module Wrapper.Marker
-- @image Wrapper_Marker.png
--- Marker class.
-- @type MARKER
-- @field #string ClassName Name of the class.
-- @field #boolean Debug Debug mode. Messages to all about status.
-- @field #string lid Class id string for output to DCS log file.
-- @field #number mid Marker ID.
-- @field Core.Point#COORDINATE coordinate Coordinate of the mark.
-- @field #string text Text displayed in the mark panel.
-- @field #string message Message dispayed when the mark is added.
-- @field #boolean readonly Marker is read-only.
-- @field #number coalition Coalition to which the marker is displayed.
-- @extends Core.Fsm#FSM
--- **Ground Control**: Airliner X, Good news, you are clear to taxi to the active.
-- **Pilot**: Roger, What's the bad news?
-- **Ground Control**: No bad news at the moment, but you probably want to get gone before I find any.
--
-- ===
--
-- ![Banner Image](..\Presentations\MARKER\Marker_Main.jpg)
--
-- # The MARKER Concept
--
--
--
-- @field #MARKER
MARKER = {
ClassName = "MARKER",
Debug = false,
lid = nil,
mid = nil,
coordinate = nil,
text = nil,
message = nil,
readonly = nil,
coalition = nil,
}
--- Holding point
-- @type MARKER.HoldingPoint
-- @field Core.Point#COORDINATE pos0 First poosition of racetrack holding point.
-- @field Core.Point#COORDINATE pos1 Second position of racetrack holding point.
-- @field #number angelsmin Smallest holding altitude in angels.
-- @field #number angelsmax Largest holding alitude in angels.
--- Marker class version.
-- @field #string version
MARKER.version="0.0.1"
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO list
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- TODO: Handle events.
-- TODO: Some more...
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Constructor
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Create a new MARKER class object.
-- @param #MARKER self
-- @param Core.Point#COORDINATE Coordinate Coordinate where to place the marker.
-- @param #string Text Text displayed on the mark panel.
-- @return #MARKER self
function MARKER:New(Coordinate, Text)
-- Inherit everything from FSM class.
local self=BASE:Inherit(self, FSM:New()) -- #MARKER
self.coordinate=Coordinate
self.text=Text
-- Defaults
self.readonly=false
self.message=""
-- Get ID.
self.mid=UTILS.GetMarkID()
-- Start State.
self:SetStartState("Stopped")
-- Add FSM transitions.
-- From State --> Event --> To State
self:AddTransition("*", "Added", "Shown") -- Marker was added.
self:AddTransition("*", "Removed", "Shown") -- Marker was added.
self:AddTransition("*", "Change", "*") -- Update status.
-- Handle events.
self:HandleEvent(EVENTS.MarkAdded)
self:HandleEvent(EVENTS.MarkRemoved)
self:HandleEvent(EVENTS.MarkChange)
return self
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- User API Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Marker is readonly.
-- @param #MARKER self
-- @return #MARKER self
function MARKER:ReadOnly()
self.readonly=true
return self
end
--- Marker is readonly.
-- @param #MARKER self
-- @param #string Text Message displayed when the marker is added.
-- @return #MARKER self
function MARKER:Message(Text)
self.message=Text or ""
return self
end
--- Place marker visible for everyone.
-- @param #MARKER self
-- @return #MARKER self
function MARKER:ToAll()
self.toall=true
-- First remove an existing mark.
if self.shown then
self:Remove()
end
-- Call DCS function.
trigger.action.markToAll(self.mid, self.text, self.coordinate:GetVec3(), self.readonly, self.message)
return self
end
--- Place marker visible for a specific coalition only.
-- @param #MARKER self
-- @param #number Coalition Coalition 1=Red, 2=Blue, 0=Neutral. See `coaliton.side.RED`.
-- @return #MARKER self
function MARKER:ToCoalition(Coalition)
self.coalition=Coalition
self.tocoaliton=true
-- First remove an existing mark.
if self.shown then
self:Remove()
end
-- Call DCS function.
trigger.action.markToCoalition(self.mid, self.text, self.coordinate:GetVec3(), self.coalition, self.readonly, self.message)
return self
end
--- Place marker visible for the blue coalition only.
-- @param #MARKER self
-- @return #MARKER self
function MARKER:ToBlue()
self:ToCoalition(coalition.side.BLUE)
return self
end
--- Place marker visible for the blue coalition only.
-- @param #MARKER self
-- @return #MARKER self
function MARKER:ToRed()
self:ToCoalition(coalition.side.RED)
return self
end
--- Place marker visible for the neutral coalition only.
-- @param #MARKER self
-- @return #MARKER self
function MARKER:ToNeutral()
self:ToCoalition(coalition.side.NEUTRAL)
return self
end
--- Place marker visible for a specific group only.
-- @param #MARKER self
-- @param Wrapper.Group#GROUP Group The group to which te
-- @return #MARKER self
function MARKER:ToGroup(Group)
-- Check if group exists.
if Group and Group:IsAlive()~=nil then
self.groupid=Group:GetID()
if self.groupid then
self.groupname=Group:GetName()
self.togroup=true
-- First remove an existing mark.
if self.shown then
self:Remove()
end
-- Call DCS function.
trigger.action.markToGroup(self.mid, self.text, self.coordinate:GetVec3(), self.groupid, self.readonly, self.message)
end
else
--TODO: Warning!
end
return self
end
--- Update the text displayed on the mark panel.
-- @param #MARKER self
-- @param #string Text Updated text.
-- @return #MARKER self
function MARKER:UpdateText(Text)
self.text=Text
self:Refresh()
end
--- Update the coordinate where the marker is displayed.
-- @param #MARKER self
-- @param Core.Point#COORDINATE Coordinate The new coordinate.
-- @return #MARKER self
function MARKER:UpdateCoordinate(Coordinate)
self.coordinate=Coordinate
self:Refresh()
end
--- Refresh the marker.
-- @param #MARKER self
-- @return #MARKER self
function MARKER:Refresh()
if self.toall then
self:ToAll()
elseif self.tocoaliton then
self:ToCoalition(self.coalition)
elseif self.togroup then
local group=GROUP:FindByName(self.groupname)
self:ToGroup(group)
end
end
--- Remove a marker.
-- @param #MARKER self
-- @return #MARKER self
function MARKER:Remove()
-- Call DCS function.
trigger.action.removeMark(self.mid)
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- Event Functions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--- Event function when a MARKER is added.
-- @param #MARKER self
-- @param Core.Event#EVENTDATA EventData
function MARKER:OnEventMarkAdded(EventData)
local MarkID=EventData.MarkID
if MarkID==self.mid then
self.shown=true
end
end
--- Event function when a MARKER is removed.
-- @param #MARKER self
-- @param Core.Event#EVENTDATA EventData
function MARKER:OnEventMarkRemoved(EventData)
local MarkID=EventData.MarkID
if MarkID==self.mid then
self.shown=false
end
end
--- Event function when a MARKER changed.
-- @param #MARKER self
-- @param Core.Event#EVENTDATA EventData
function MARKER:OnEventMarkChange(EventData)
local MarkID=EventData.MarkID
if MarkID==self.mid then
end
end
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------